app.js 7.52 KB
const TelegramBot = require('node-telegram-bot-api');

const config = require('./config');

// Create a bot that uses 'polling' to fetch new updates
const bot = new TelegramBot(config.telegram.token, { polling: true });

var request = require('request');

// Translation api url
const translate_api_url = 'https://openapi.naver.com/v1/papago/n2mt';

// Language detection api url
const languagedetect_api_url = 'https://openapi.naver.com/v1/papago/detectLangs'

// Initialize database
const db = require('./db');
db.init();

// /echo [whatever]
bot.onText(/\/echo (.+)/, (msg, match) => {
  // 'msg' is the received Message from Telegram
  // 'match' is the result of executing the regexp above on the text content
  // of the message

  const chatId = msg.chat.id;
  const resp = match[1]; // the captured "whatever"

  // send back the matched "whatever" to the chat
  bot.sendMessage(chatId, resp);
});

/**
 * Translate given message and send it to the specified chatroom.
 *
 * @param {*} userId  Id of the user that wants to translate
 * @param {*} message Message to translate
 * @param {*} chatId  Id of the chatroom to send translated message to
 */
function translate(userId, message, chatId) {
  // Language detection options
  var lang_detect_options = {
    url: languagedetect_api_url,
    form: { 'query': message },
    headers: {
      'X-Naver-Client-Id': config.papago.client_id,
      'X-Naver-Client-Secret': config.papago.client_secret
    }
  };

  // Papago language detection
  request.post(lang_detect_options, function (error, response, body) {
    console.log(response.statusCode);
    if (!error && response.statusCode == 200) {
      var detect_body = JSON.parse(response.body);
      var source = '';
      var target = '';
      var result = { type: 'text', text: '' };

      // Check if detection was successful
      console.log('Language detected: ' + detect_body.langCode);

      // Translate using papago
      // Try to grab source lang and target lang from user's preference first.
      // If preference is not found, source default to detected language, and
      // target defaults to English for Korean source, or Korean for all other langs.
      if (detect_body.langCode != 'unk') {
        db.findPref(userId, "pref", (dbResult) => {
          // Choosing source language not implemented as of now
          if (dbResult != undefined) {
            source = dbResult.source != undefined ? dbResult.source : detect_body.langCode;
            target = dbResult.target != undefined ? dbResult.target : (source == 'ko' ? 'en' : 'ko');
          } else {
            source = detect_body.langCode;
            target = source == 'ko' ? 'en' : 'ko';
          }
          console.log(source + ' => ' + target);

          // Papago translation options
          var translate_options = {
            url: translate_api_url,
            form: {
              'source': source, // Before translation
              'target': target, // After translation
              'text': message // Message to translate
            },
            headers: {
              'X-Naver-Client-Id': config.papago.client_id,
              'X-Naver-Client-Secret': config.papago.client_secret
            }
          };

          // Send translatation request
          request.post(translate_options, function (error, response, body) {
            var objBody = JSON.parse(response.body);
            if (!error && response.statusCode == 200) {
              // On success
              result.text = objBody.message.result.translatedText;

              console.log('Before: ' + message);
              console.log('After: ' + result.text);
            } else {
              // On failure
              result.text = '[' + objBody.errorCode + '] ' + objBody.errorMessage;
            }
            // Send translated message
            bot.sendMessage(chatId, result.text);
          });
        });
      }
      // Language not detected
      else {
        result.text = '언어를 감지할 수 없습니다.';
        bot.sendMessage(chatId, result.text);
      }
    }
  });
}

// [Any normal message which is not a command (not starting with '/')]
bot.onText(/^(?!\/)((.|\n)+)/, (msg, match) => {
  const userId = msg.from.id;
  const chatId = msg.chat.id;
  const chatType = msg.chat.type;
  const received_msg = match[1];

  // Ignore if we are not on a private chat,
  // since direct translation is to be used only on private chats.
  if (chatType != 'private') {
    return;
  }

  translate(userId, received_msg, chatId);
});

// /t(ranslate) [Whatever]
// Translate the 'whatever' and show the result in any kind of chatroom.
// Also, if the given '/t' message is a reply to another message,
// translate the reply target message as well.
bot.onText(/^\/(t|translate)($| ((.|\n)+))/, (msg, match) => {
  const userId = msg.from.id;
  const chatId = msg.chat.id;
  const chatType = msg.chat.type;
  const received_msg = match[3];
  // Whether the given '/t' message is a reply to another message
  const isReply = msg.reply_to_message != undefined;
  // Whether a message has been given after '/t' command
  const msgExists = received_msg != undefined;

  // Translate the reply's target message
  if (isReply) {
    const replyMsg = msg.reply_to_message.text;
    translate(userId, replyMsg, chatId);
  }

  // Translate the message after '/t' if exists
  if (msgExists) {
    translate(userId, received_msg, chatId);
  }
});

// /l(anguage)
// Let user select the language he wants his message to translate to.
// When triggered, bot will send an inline keyboard message with a list
// of available langauges. For an example of an inline keyboard message,
// see https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating.
bot.onText(/^\/(l|anguage)/, (msg, match) => {
  const chatId = msg.chat.id;
  const msgId = msg.message_id;

  const inlineKeyboard = {
    inline_keyboard: [
      // Languages supported by papago language detection
      // One array per line
      [
        { text: '한국어', callback_data: 'ko' },
        { text: '영어', callback_data: 'en' },
        { text: '일본어', callback_data: 'ja' }
      ],
      [
        { text: '중국어 간체', callback_data: 'zh-cn' },
        { text: '중국어 번체', callback_data: 'zh-tw' }
      ],
      [
        { text: '힌디어', callback_data: 'hi' },
        { text: '스페인어', callback_data: 'es' },
        { text: '프랑스어', callback_data: 'fr' }
      ],
      [
        { text: '독일어', callback_data: 'de' },
        { text: '포루트갈어', callback_data: 'pt' },
        { text: '베트남어', callback_data: 'vi' }
      ],
      [
        { text: '인도네시아어', callback_data: 'id' },
        { text: '페르시아어', callback_data: 'fa' }
      ],
      [
        { text: '아랍어', callback_data: 'ar' },
        { text: '미얀마어', callback_data: 'mm' },
        { text: '태국어', callback_data: 'th' }
      ],
      [
        { text: '러시아어', callback_data: 'ru' },
        { text: '이탈리아어', callback_data: 'it' }
      ],
    ]
  }
  const options = {
    reply_to_message_id: msgId,
    reply_markup: inlineKeyboard
  }

  bot.sendMessage(chatId, '무슨 언어로 번역할까요? 선택은 기억됩니다.', options);
});

bot.on('callback_query', (query) => {
  const queryId = query.id;
  const userId = query.from.id;
  const langCode = query.data;

  db.updatePref(userId, { "pref": { "target": langCode } }, (result) => {
    const options = {
      text: 'From now on, your messages will be translated into ' + langCode
    }

    bot.answerCallbackQuery(queryId, options);
  })
});