interactive bots: Create googletranslate bot.
ADD
zulip_bots/zulip_bots/bots/googletranslate/__init__.py
zulip_bots/zulip_bots/bots/googletranslate/__init__.py
ADD
zulip_bots/zulip_bots/bots/googletranslate/doc.md
+# Google Translate bot
+
+The Google Translate bot uses Google Translate to translate
+any text sent to it.
+
+## Setup
+
+This bot requires a google cloud API key. Create one
+[here](https://support.google.com/cloud/answer/6158862?hl=en)
+
+You should add this key to `googletranslate.conf`.
+
+To run this bot, use:
+`zulip-run-bots googletranslate -c <zuliprc file>
+--bot-config-file <path to googletranslate.conf>`
+
+## Usage
+
+To use this bot, @-mention it like this:
+
+`@-mention "<text>" <target language> <source language(Optional)>`
+
+`text` must be in quotation marks, and `source language`
+is optional.
+
+If `source language` is not given, it will automatically detect your language.

ADD
zulip_bots/zulip_bots/bots/googletranslate/fixtures/test_403.json
+{
+ "request": {
+ "method": "POST",
+ "api_url": "https://translation.googleapis.com/language/translate/v2",
+ "params": {
+ "q": "hello",
+ "key": "abcdefg",
+ "target": "de",
+ "source": "en"
+ }
+ },
+ "response": {
+ "error": {
+ "code": 403,
+ "message": "Invalid API Key.",
+ "errors": [
+ {
+ "message": "Invalid API Key",
+ "domain": "usageLimits",
+ "reason": "accessNotConfigured",
+ "extendedHelp": "https://console.developers.google.com"
+ }
+ ],
+ "status": "PERMISSION_DENIED"
+ }
+ },
+ "response-headers": {
+ "status": 403,
+ "content-type": "application/json; charset=utf-8"
+ }
+}

ADD
zulip_bots/zulip_bots/bots/googletranslate/fixtures/test_conn_error.json
+{
+ "request": {
+ "method": "POST",
+ "api_url": "https://translation.googleapis.com/language/translate/v2",
+ "params": {
+ "q": "Hello",
+ "key": "abcdefg",
+ "target": "de",
+ "source": "en"
+ }
+ },
+ "response": {
+ },
+ "response-headers": {
+ }
+}

ADD
zulip_bots/zulip_bots/bots/googletranslate/fixtures/test_language_403.json
+{
+ "request": {
+ "method": "GET",
+ "api_url": "https://translation.googleapis.com/language/translate/v2/languages",
+ "params": {
+ "key": "abcdefg",
+ "target": "en"
+ }
+ },
+ "response": {
+ "error": {
+ "code": 403,
+ "message": "Invalid API Key.",
+ "errors": [
+ {
+ "message": "Invalid API Key",
+ "domain": "usageLimits",
+ "reason": "accessNotConfigured",
+ "extendedHelp": "https://console.developers.google.com"
+ }
+ ],
+ "status": "PERMISSION_DENIED"
+ }
+ },
+ "response-headers": {
+ "status": 403,
+ "content-type": "application/json; charset=utf-8"
+ }
+}

ADD
zulip_bots/zulip_bots/bots/googletranslate/fixtures/test_languages.json
+{
+ "request": {
+ "method": "GET",
+ "api_url": "https://translation.googleapis.com/language/translate/v2/languages",
+ "params": {
+ "key": "abcdefg",
+ "target": "en"
+ }
+ },
+ "response": {
+ "data": {
+ "languages": [
+ {
+ "language": "en",
+ "name": "English"
+ },
+ {
+ "language": "fr",
+ "name": "French"
+ },
+ {
+ "language": "de",
+ "name": "German"
+ }
+ ]
+ }
+ },
+ "response-headers": {
+ "status": 200,
+ "content-type": "application/json; charset=utf-8"
+ }
+}

ADD
zulip_bots/zulip_bots/bots/googletranslate/fixtures/test_normal.json
+{
+ "request": {
+ "method": "POST",
+ "api_url": "https://translation.googleapis.com/language/translate/v2",
+ "params": {
+ "q": "hello",
+ "key": "abcdefg",
+ "target": "de"
+ }
+ },
+ "response": {
+ "data": {
+ "translations": [
+ {
+ "translatedText": "Hallo",
+ "detectedSourceLanguage": "en"
+ }
+ ]
+ }
+ },
+ "response-headers": {
+ "status": 200,
+ "content-type": "application/json; charset=utf-8"
+ }
+}

ADD
zulip_bots/zulip_bots/bots/googletranslate/fixtures/test_quotation.json
+{
+ "request": {
+ "method": "POST",
+ "api_url": "https://translation.googleapis.com/language/translate/v2",
+ "params": {
+ "q": "this has \"quotation\" marks in",
+ "key": "abcdefg",
+ "target": "en"
+ }
+ },
+ "response": {
+ "data": {
+ "translations": [
+ {
+ "translatedText": "this has \"quotation\" marks in",
+ "detectedSourceLanguage": "en"
+ }
+ ]
+ }
+ },
+ "response-headers": {
+ "status": 200,
+ "content-type": "application/json; charset=utf-8"
+ }
+}

ADD
zulip_bots/zulip_bots/bots/googletranslate/googletranslate.conf
+[googletranslate]
+key=your_api_key_here

ADD
zulip_bots/zulip_bots/bots/googletranslate/googletranslate.py
+# To use this plugin, you need to set up the Google Cloud API key for this bot in
+# googletranslate.conf in this (zulip_bots/bots/googletranslate/) directory.
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import requests
+
+class GoogleTranslateHandler(object):
+ '''
+ This bot will translate any messages sent to it using google translate.
+ Before using it, make sure you set up google api keys, and enable google
+ cloud translate from the google cloud console.
+ '''
+ def usage(self):
+ return '''
+ This plugin allows users translate messages
+ Users should @-mention the bot with the format
+ @-mention "<text_to_translate>" <target-language> <source-language(optional)>
+ '''
+
+ def initialize(self, bot_handler):
+ self.config_info = bot_handler.get_config_info('googletranslate')
+ self.supported_languages = get_supported_languages(self.config_info['key'])
+
+ def handle_message(self, message, bot_handler):
+ bot_response = get_translate_bot_response(message['content'],
+ self.config_info,
+ message['sender_full_name'],
+ self.supported_languages)
+ bot_handler.send_reply(message, bot_response)
+
+api_url = 'https://translation.googleapis.com/language/translate/v2'
+
+help_text = '''
+Google translate bot
+Please format your message like:
+`@-mention "<text_to_translate>" <target-language> <source-language(optional)>`
+Visit [here](https://cloud.google.com/translate/docs/languages) for all languages
+'''
+
+language_not_found_text = '{} language not found. Visit [here](https://cloud.google.com/translate/docs/languages) for all languages'
+
+def get_supported_languages(key):
+ parameters = {'key': key, 'target': 'en'}
+ response = requests.get(api_url + '/languages', params = parameters)
+ if response.status_code == requests.codes.ok:
+ languages = response.json()['data']['languages']
+ return {lang['name'].lower(): lang['language'].lower() for lang in languages}
+ raise TranslateError(response.json()['error']['message'])
+
+class TranslateError(Exception):
+ pass
+
+def translate(text_to_translate, key, dest, src):
+ parameters = {'q': text_to_translate, 'target': dest, 'key': key}
+ if src != '':
+ parameters.update({'source': src})
+ response = requests.post(api_url, params=parameters)
+ if response.status_code == requests.codes.ok:
+ return response.json()['data']['translations'][0]['translatedText']
+ raise TranslateError(response.json()['error']['message'])
+
+def get_code_for_language(language, all_languages):
+ if language.lower() not in all_languages.values():
+ if language.lower() not in all_languages.keys():
+ return ''
+ language = all_languages[language.lower()]
+ return language
+
+def get_translate_bot_response(message_content, config_file, author, all_languages):
+ message_content = message_content.strip()
+ if message_content == 'help' or message_content is None or not message_content.startswith('"'):
+ return help_text
+ split_text = message_content.rsplit('" ', 1)
+ if len(split_text) == 1:
+ return help_text
+ split_text += split_text.pop(1).split(' ')
+ if len(split_text) == 2:
+ # There is no source language
+ split_text.append("")
+ if len(split_text) != 3:
+ return help_text
+ (text_to_translate, target_language, source_language) = split_text
+ text_to_translate = text_to_translate[1:]
+ target_language = get_code_for_language(target_language, all_languages)
+ if target_language == '':
+ return language_not_found_text.format("Target")
+ if source_language != '':
+ source_language = get_code_for_language(source_language, all_languages)
+ if source_language == '':
+ return language_not_found_text.format("Source")
+ try:
+ translated_text = translate(text_to_translate, config_file['key'], target_language, source_language)
+ except requests.exceptions.ConnectionError as conn_err:
+ return "Could not connect to Google Translate. {}.".format(conn_err)
+ except TranslateError as tr_err:
+ return "Translate Error. {}.".format(tr_err)
+ except Exception as err:
+ return "Error. {}.".format(err)
+ return "{} (from {})".format(translated_text, author)
+
+handler_class = GoogleTranslateHandler

ADD
zulip_bots/zulip_bots/bots/googletranslate/test_googletranslate.py
+#!/usr/bin/env python
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import json
+
+from unittest.mock import patch
+from requests.exceptions import HTTPError, ConnectionError
+
+from zulip_bots.test_lib import BotTestCase
+from zulip_bots.bots.googletranslate.googletranslate import TranslateError
+
+help_text = '''
+Google translate bot
+Please format your message like:
+`@-mention "<text_to_translate>" <target-language> <source-language(optional)>`
+Visit [here](https://cloud.google.com/translate/docs/languages) for all languages
+'''
+
+class TestGoogleTranslateBot(BotTestCase):
+ bot_name = "googletranslate"
+
+ def test_normal(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_normal'):
+ with self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"hello" de', 'sender_full_name': 'tester'},
+ response = {'content': 'Hallo (from tester)'},
+ expected_method = 'send_reply'
+ )
+
+ def test_source_language_not_found(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"hello" german foo', 'sender_full_name': 'tester'},
+ response = {'content': 'Source language not found. Visit [here](https://cloud.google.com/translate/docs/languages) for all languages'},
+ expected_method = 'send_reply'
+ )
+
+ def test_target_language_not_found(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"hello" bar english', 'sender_full_name': 'tester'},
+ response = {'content': 'Target language not found. Visit [here](https://cloud.google.com/translate/docs/languages) for all languages'},
+ expected_method = 'send_reply'
+ )
+
+ def test_403(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_403'):
+ with self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"hello" german english', 'sender_full_name': 'tester'},
+ response = {'content': 'Translate Error. Invalid API Key..'},
+ expected_method = 'send_reply'
+ )
+
+ def test_help_empty(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '', 'sender_full_name': 'tester'},
+ response = {'content': help_text},
+ expected_method = 'send_reply'
+ )
+
+ def test_help_command(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': 'help', 'sender_full_name': 'tester'},
+ response = {'content': help_text},
+ expected_method = 'send_reply'
+ )
+
+ def test_help_no_langs(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"hello"', 'sender_full_name': 'tester'},
+ response = {'content': help_text},
+ expected_method = 'send_reply'
+ )
+
+ def test_quotation_in_text(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_quotation'):
+ with self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"this has "quotation" marks in" english', 'sender_full_name': 'tester'},
+ response = {'content': 'this has "quotation" marks in (from tester)'},
+ expected_method = 'send_reply'
+ )
+
+ def test_exception(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ patch('zulip_bots.bots.googletranslate.googletranslate.translate', side_effect=Exception):
+ with self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assertRaises(Exception)
+ self.assert_bot_response(
+ message = {'content': '"hello" de', 'sender_full_name': 'tester'},
+ response = {'content': 'Error. .'},
+ expected_method = 'send_reply'
+ )
+
+ def test_get_language_403(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ self.mock_http_conversation('test_language_403'), \
+ self.assertRaises(TranslateError):
+ self.initialize_bot()
+
+ def test_connection_error(self):
+ with self.mock_config_info({'key': 'abcdefg'}), \
+ patch('requests.post', side_effect=ConnectionError()), \
+ patch('logging.warning'):
+ with self.mock_http_conversation('test_languages'):
+ self.initialize_bot()
+ self.assert_bot_response(
+ message = {'content': '"test" en', 'sender_full_name': 'tester'},
+ response = {'content': 'Could not connect to Google Translate. .'},
+ expected_method = 'send_reply'
+ )

interactive bots: Create googletranslate bot.