interactive bots: Create googletranslate bot.

This commit is contained in:
fredfishgames 2017-12-02 18:00:35 +00:00 committed by showell
parent 7a963916f2
commit 1b16b54780
11 changed files with 424 additions and 0 deletions

View file

@ -0,0 +1,26 @@
# 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.

View file

@ -0,0 +1,31 @@
{
"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"
}
}

View file

@ -0,0 +1,16 @@
{
"request": {
"method": "POST",
"api_url": "https://translation.googleapis.com/language/translate/v2",
"params": {
"q": "Hello",
"key": "abcdefg",
"target": "de",
"source": "en"
}
},
"response": {
},
"response-headers": {
}
}

View file

@ -0,0 +1,29 @@
{
"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"
}
}

View file

@ -0,0 +1,32 @@
{
"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"
}
}

View file

@ -0,0 +1,25 @@
{
"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"
}
}

View file

@ -0,0 +1,25 @@
{
"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"
}
}

View file

@ -0,0 +1,2 @@
[googletranslate]
key=your_api_key_here

View file

@ -0,0 +1,103 @@
# 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

View file

@ -0,0 +1,135 @@
#!/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'
)