interactive bots: Create googletranslate bot.
This commit is contained in:
		
							parent
							
								
									7a963916f2
								
							
						
					
					
						commit
						1b16b54780
					
				
					 11 changed files with 424 additions and 0 deletions
				
			
		
							
								
								
									
										0
									
								
								zulip_bots/zulip_bots/bots/googletranslate/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								zulip_bots/zulip_bots/bots/googletranslate/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
								
								
									
										26
									
								
								zulip_bots/zulip_bots/bots/googletranslate/doc.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								zulip_bots/zulip_bots/bots/googletranslate/doc.md
									
										
									
									
									
										Normal 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. | ||||
|  | @ -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" | ||||
|   } | ||||
| } | ||||
|  | @ -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": { | ||||
|   } | ||||
| } | ||||
|  | @ -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" | ||||
|   } | ||||
| } | ||||
|  | @ -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" | ||||
|     } | ||||
| } | ||||
|  | @ -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" | ||||
|   } | ||||
| } | ||||
|  | @ -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" | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,2 @@ | |||
| [googletranslate] | ||||
| key=your_api_key_here | ||||
							
								
								
									
										103
									
								
								zulip_bots/zulip_bots/bots/googletranslate/googletranslate.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								zulip_bots/zulip_bots/bots/googletranslate/googletranslate.py
									
										
									
									
									
										Normal 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 | ||||
|  | @ -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' | ||||
|                     ) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 fredfishgames
						fredfishgames