Interpreter, a Google Translate Bot for Slack


by
1703w

Screenshot of the translating bot in action.

Setting it up

  1. Install the Beep Boop bot, which will create a forked GitHub repo for you.

  2. Register an account and follow the instructions at https://cloud.google.com/translate/v2/pricing. The pricing is as follows:

    Translation:

    $20 per 1 M characters of text, where the charges are adjusted in proportion to the number of characters actually provided. For example, if you were to translate 500K characters, you would be billed $10.

    Language Detection:

    $20 per 1 M characters of text, where the charges are adjusted in proportion to the number of characters actually provided.

    Usage limits:

    Google Translate API has a default limit of 2 M chars/day. You can increase this limit up to 50 M chars/day in the Cloud Platform Console by following the instructions below.

    If you need to translate more than 50 M chars/day, please contact us.

  3. Since there is currently a free trial of 60 days with a $300 voucher, you shouldn’t be paying anything at all for the next two months.

  4. Create a project on Google Cloud for our interpreter.

  5. Enable the Google Translate API—and billing—for your Google Cloud project.

  6. Create the API key for your Google Cloud project. You’ll need this later.

package.json

First, you need to install the npm package for Google Translate, the aptly named google-translate.

From the terminal, you can type npm install google-translate --save, which will automatically add the package to your list of dependencies.

You can also just add it manually to the file using GitHub’s online interface:

"dependencies": {
  "botkit": "0.0.5",
  "google-translate": "^1.0.6"
}

In diff terms:

"dependencies": {
- "botkit": "0.0.5"
+ "botkit": "0.0.5",
+ "google-translate": "^1.0.6"
}

You are free to edit the rest of the information in the file, but it has no bearing on how the bot operates. The mandatory requirement is adding the google-translate dependency.

index.js

This is what the file will look out, if we delete some of the superfluous boilerplate code and hide the interpreter functionality:

var Botkit = require("botkit");

// Expect a SLACK_TOKEN environment variable
var slackToken = process.env.SLACK_TOKEN;
if (!slackToken) {
    console.error("SLACK_TOKEN is required!");
    process.exit(1);
}

var controller = Botkit.slackbot();
var bot = controller.spawn({
    token: slackToken
});

bot.startRTM(function (err, bot, payload) {
    if (err) {
        throw new Error("Could not connect to Slack");
    }
});

//=> Interpreter: translates Danish to English
// ...
// <=

// This goes by the end of the file; it works as an "else" function for listener events.
controller.hears(".*", ["direct_message", "direct_mention"], function (bot, message) {
    bot.reply(message, "Sorry <@" + message.user + ">, I don\'t understand. \n");
});

This is what our interpreter code looks like—the // ... in the snippet above:

//=> Interpreter: translates Danish to English
// Expect a GOOGLE_TOKEN environment variable
var googleToken = process.env.GOOGLE_TOKEN;
if (!googleToken) {
    console.error("GOOGLE_TOKEN is required!");
    process.exit(1);
}

var googleTranslate = require("google-translate")(googleToken);

controller.hears(["^translate (.*)$"], ["direct_message", "direct_mention"], function (bot, message) {
    googleTranslate.translate(message.match[1], "da", "en" function(err, translation) {
        try {
            bot.reply(message, ":flag-dk: => " + translation.translatedText);
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});
// <=

Pay attention to this part:

googleTranslate.translate(message.match[1], "da", "en", function(err, translation) {
    try {
        bot.reply(message, ":flag-dk: => " + translation.translatedText);
    }

Because I’m from Denmark, I created this bot to assist the poor people on my Slack team who had to deal with the horrors of the Danish language. This has two implications:

  1. I’ve included "da" (Danish) as the “source language” parameter. What this means is that the Google Translate API won’t have to detect the language—which, if you recall, incurs additional fees:

    Language Detection:

    $20 per 1 M characters of text, where the charges are adjusted in proportion to the number of characters actually provided.

    You can either leave out this parameter for all-purpose translation or choose a different source language. (Or create a separate keyword for each type of query.)

  2. The translation output is ":flag-dk: => " + translation.translatedText

    Which turns into “🇩🇰 => [translation]”.

    You can change it to whatever you want.

This is what the full code for index.js looks like:

var Botkit = require("botkit");

// Expect a SLACK_TOKEN environment variable
var slackToken = process.env.SLACK_TOKEN;
if (!slackToken) {
    console.error("SLACK_TOKEN is required!");
    process.exit(1);
}

var controller = Botkit.slackbot();
var bot = controller.spawn({
    token: slackToken
});

bot.startRTM(function (err, bot, payload) {
    if (err) {
        throw new Error("Could not connect to Slack");
    }
});


//=> Interpreter: translates Danish to English
// Expect a GOOGLE_TOKEN environment variable
var googleToken = process.env.GOOGLE_TOKEN;
if (!googleToken) {
    console.error("GOOGLE_TOKEN is required!");
    process.exit(1);
}

var googleTranslate = require("google-translate")(googleToken);

controller.hears(["^translate (.*)$"], ["direct_message", "direct_mention"], function (bot, message) {
    googleTranslate.translate(message.match[1], "da", "en" function(err, translation) {
        try {
            bot.reply(message, ":flag-dk: => " + translation.translatedText);
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});
// <=


// This goes by the end of the file; it works as an "else" function for listener events.
controller.hears(".*", ["direct_message", "direct_mention"], function (bot, message) {
    bot.reply(message, "Sorry <@" + message.user + ">, I don\'t understand. \n");
});

bot.yml

As you might recall in the beginning, I asked you to create an API key to use later. bot.yml is where you tell your bot to look for an environment variable named GOOGLE_TOKEN.

name: interpreter
description: A bot that translates your queries
avatar: resources/avatar.png
config:
  - name: GOOGLE_TOKEN
    friendly_name: Google API key
    info: API key used for Google Translate
    type: secret
    global: true

After this is done, go to your bot settings on Beep Boop and enter your Google token, so the bot can use the Google Translate API.

Testing and deploying

Every time your save and commit your bot repo, the bot is updated and rebooted. If you don’t want to test by means of trial and error, you can set up the bot locally by cloning the repo and running

npm install # Installs required packages listed in `package.json`
npm start

This will set up a, separate, local bot to run alongside the online version. This means that there are now to versions of the bot listening to what you type in Slack, which is why you will get two responses instead of one.

Keep in mind that you will have restart the bot after each change, so be sure to stop it and type npm start in your terminal again

When you are happy with the results, save and commit your changes to the repo, so the online version of the bot is updated.

This isn’t as hard as it may sound; I’ve just tried walking explaining the deails as painstakinly as possible.

Now go forth and add some features of your own!