interactive bots: Create DialogFlow bot.
This commit is contained in:
parent
277b384379
commit
657c6d7b9c
|
@ -69,6 +69,8 @@ force_include = [
|
|||
"zulip_bots/zulip_bots/bots/wikipedia/test_wikipedia.py",
|
||||
"zulip_bots/zulip_bots/bots/yoda/yoda.py",
|
||||
"zulip_bots/zulip_bots/bots/yoda/test_yoda.py",
|
||||
"zulip_bots/zulip_bots/bots/dialogflow/dialogflow.py",
|
||||
"zulip_bots/zulip_bots/bots/dialogflow/test_dialogflow.py"
|
||||
]
|
||||
|
||||
parser = argparse.ArgumentParser(description="Run mypy on files tracked by git.")
|
||||
|
|
|
@ -53,7 +53,8 @@ setuptools_info = dict(
|
|||
'BeautifulSoup4', # for bots/googlesearch
|
||||
'lxml', # for bots/googlesearch
|
||||
'requests', # for bots/link_shortener
|
||||
'python-chess[engine,gaviota]' # for bots/chess
|
||||
'python-chess[engine,gaviota]', # for bots/chess
|
||||
'apiai' # for bots/dialogflow
|
||||
],
|
||||
)
|
||||
|
||||
|
|
0
zulip_bots/zulip_bots/bots/dialogflow/__init__.py
Normal file
0
zulip_bots/zulip_bots/bots/dialogflow/__init__.py
Normal file
3
zulip_bots/zulip_bots/bots/dialogflow/dialogflow.conf
Normal file
3
zulip_bots/zulip_bots/bots/dialogflow/dialogflow.conf
Normal file
|
@ -0,0 +1,3 @@
|
|||
[dialogflow]
|
||||
key=YOUR_PUBLIC_API_KEY_HERE
|
||||
bot_info=BOT_INFO_HERE
|
57
zulip_bots/zulip_bots/bots/dialogflow/dialogflow.py
Normal file
57
zulip_bots/zulip_bots/bots/dialogflow/dialogflow.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# See readme.md for instructions on running this code.
|
||||
from __future__ import print_function
|
||||
import logging
|
||||
from six.moves.urllib import parse
|
||||
import json
|
||||
|
||||
import apiai
|
||||
|
||||
from typing import Dict, Any, List
|
||||
|
||||
help_message = '''DialogFlow bot
|
||||
This bot will interact with dialogflow bots.
|
||||
Simply send this bot a message, and it will respond depending on the configured bot's behaviour.
|
||||
'''
|
||||
|
||||
def get_bot_result(message_content: str, config: Dict[str, str], sender_id: str) -> str:
|
||||
if message_content.strip() == '' or message_content.strip() == 'help':
|
||||
return config['bot_info']
|
||||
ai = apiai.ApiAI(config['key'])
|
||||
try:
|
||||
request = ai.text_request()
|
||||
request.session_id = sender_id
|
||||
request.query = message_content
|
||||
response = request.getresponse()
|
||||
res_str = response.read().decode('utf8', 'ignore')
|
||||
res_json = json.loads(res_str)
|
||||
if res_json['status']['errorType'] != 'success':
|
||||
return 'Error {}: {}.'.format(res_json['status']['code'], res_json['status']['errorDetails'])
|
||||
if res_json['result']['fulfillment']['speech'] == '':
|
||||
if res_json['alternateResult']['fulfillment']['speech'] == '':
|
||||
return 'Error. No result.'
|
||||
return res_json['alternateResult']['fulfillment']['speech']
|
||||
return res_json['result']['fulfillment']['speech']
|
||||
except Exception as e:
|
||||
logging.exception(str(e))
|
||||
return 'Error. {}.'.format(str(e))
|
||||
|
||||
class DialogFlowHandler(object):
|
||||
'''
|
||||
This plugin allows users to easily add their own
|
||||
DialogFlow bots to zulip
|
||||
'''
|
||||
|
||||
def initialize(self, bot_handler: Any) -> None:
|
||||
self.config_info = bot_handler.get_config_info('dialogflow')
|
||||
|
||||
def usage(self) -> str:
|
||||
return '''
|
||||
This plugin will allow users to easily add their own
|
||||
DialogFlow bots to zulip
|
||||
'''
|
||||
|
||||
def handle_message(self, message: Dict[str, str], bot_handler: Any) -> None:
|
||||
result = get_bot_result(message['content'], self.config_info, message['sender_id'])
|
||||
bot_handler.send_reply(message, result)
|
||||
|
||||
handler_class = DialogFlowHandler
|
24
zulip_bots/zulip_bots/bots/dialogflow/doc.md
Normal file
24
zulip_bots/zulip_bots/bots/dialogflow/doc.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# DialogFlow bot
|
||||
This bot allows users to easily add their own DialogFlow bots to zulip.
|
||||
|
||||
## Setup
|
||||
To add your DialogFlow bot:
|
||||
Add the V1 Client access token from your agent's settings in the DialogFlow console to
|
||||
`dialogflow.conf`, and write a short sentence describing what your bot does in the same file
|
||||
as `bot_info`.
|
||||
|
||||
## Usage
|
||||
|
||||
Run this bot as described
|
||||
[here](https://zulipchat.com/api/running-bots#running-a-bot).
|
||||
|
||||
Mention the bot in order to say things to it.
|
||||
|
||||
For example: `@weather What is the weather today?`
|
||||
|
||||
|
||||
## Limitations
|
||||
When creating your DialogFlow bot, please consider these things:
|
||||
|
||||
- Empty input will not be sent to the bot.
|
||||
- Only text can be sent to, and recieved from the bot.
|
12
zulip_bots/zulip_bots/bots/dialogflow/fixtures/test_403.json
Normal file
12
zulip_bots/zulip_bots/bots/dialogflow/fixtures/test_403.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"response": {
|
||||
"status": {
|
||||
"errorType": "fail",
|
||||
"code": "403",
|
||||
"errorDetails": "Access Denied"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"query": "hello"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"response": {
|
||||
"result": {
|
||||
"fulfillment": {
|
||||
"speech": ""
|
||||
}
|
||||
},
|
||||
"alternateResult": {
|
||||
"fulfillment": {
|
||||
"speech": "alternate result"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"errorType": "success"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"query": "hello"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"response": {
|
||||
"result": {
|
||||
"fulfillment": {
|
||||
"speech": ""
|
||||
}
|
||||
},
|
||||
"alternateResult": {
|
||||
"fulfillment": {
|
||||
"speech": ""
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"errorType": "success"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"query": "hello"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"response": {
|
||||
"foo": {
|
||||
"fulfillment": {
|
||||
"speech": "how are you?"
|
||||
}
|
||||
},
|
||||
"bar": {
|
||||
"errorType": "success"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"query": "hello"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"response": {
|
||||
"result": {
|
||||
"fulfillment": {
|
||||
"speech": "how are you?"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
"errorType": "success"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"query": "hello"
|
||||
}
|
||||
}
|
71
zulip_bots/zulip_bots/bots/dialogflow/test_dialogflow.py
Normal file
71
zulip_bots/zulip_bots/bots/dialogflow/test_dialogflow.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from zulip_bots.test_lib import StubBotTestCase, read_bot_fixture_data
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from typing import Any, ByteString
|
||||
|
||||
import json
|
||||
|
||||
class MockTextRequest():
|
||||
def __init__(self) -> None:
|
||||
self.session_id = ""
|
||||
self.query = ""
|
||||
self.response = ""
|
||||
|
||||
def getresponse(self) -> Any:
|
||||
return MockHttplibRequest(self.response)
|
||||
|
||||
class MockHttplibRequest():
|
||||
def __init__(self, response: str) -> None:
|
||||
self.response = response
|
||||
|
||||
def read(self) -> ByteString:
|
||||
return json.dumps(self.response).encode()
|
||||
|
||||
@contextmanager
|
||||
def mock_dialogflow(test_name: str, bot_name: str) -> Any:
|
||||
response_data = read_bot_fixture_data(bot_name, test_name)
|
||||
df_request = response_data.get('request')
|
||||
df_response = response_data.get('response')
|
||||
|
||||
with patch('apiai.ApiAI.text_request') as mock_text_request:
|
||||
request = MockTextRequest()
|
||||
request.response = df_response
|
||||
mock_text_request.return_value = request
|
||||
yield
|
||||
|
||||
class TestDialogFlowBot(StubBotTestCase):
|
||||
bot_name = 'dialogflow'
|
||||
|
||||
def _test(self, test_name: str, message: str, response: str) -> None:
|
||||
with self.mock_config_info({'key': 'abcdefg', 'bot_info': 'bot info foo bar'}), \
|
||||
mock_dialogflow(test_name, 'dialogflow'):
|
||||
self.verify_reply(message, response)
|
||||
|
||||
def test_normal(self) -> None:
|
||||
self._test('test_normal', 'hello', 'how are you?')
|
||||
|
||||
def test_403(self) -> None:
|
||||
self._test('test_403', 'hello', 'Error 403: Access Denied.')
|
||||
|
||||
def test_empty_response(self) -> None:
|
||||
self._test('test_empty_response', 'hello', 'Error. No result.')
|
||||
|
||||
def test_exception(self) -> None:
|
||||
with patch('logging.exception'):
|
||||
self._test('test_exception', 'hello', 'Error. \'status\'.')
|
||||
|
||||
def test_help(self) -> None:
|
||||
self._test('test_normal', 'help', 'bot info foo bar')
|
||||
self._test('test_normal', '', 'bot info foo bar')
|
||||
|
||||
def test_alternate_response(self) -> None:
|
||||
self._test('test_alternate_result', 'hello', 'alternate result')
|
||||
|
||||
def test_bot_responds_to_empty_message(self) -> None:
|
||||
with self.mock_config_info({'key': 'abcdefg', 'bot_info': 'bot info foo bar'}):
|
||||
pass
|
|
@ -114,6 +114,7 @@ class StubBotTestCase(TestCase):
|
|||
display_recipient='foo_stream',
|
||||
sender_email='foo@example.com',
|
||||
sender_full_name='Foo Test User',
|
||||
sender_id='123',
|
||||
content=content,
|
||||
)
|
||||
return message
|
||||
|
|
Loading…
Reference in a new issue