From c424efd746693de05edcc5d3b53ca40caa685961 Mon Sep 17 00:00:00 2001 From: Tobias Wochinger Date: Wed, 17 Oct 2018 14:40:16 +0200 Subject: [PATCH 1/3] **Motivation**: The telegram library parses the user id as `int`. However, rasa_core expects a `str`. When a user queries the conversations for a specific user id, his input is processed as `str`. Rasa returns an empty conversation list, as the tracker only contains entries for the user id as `int` but not as `str`. **Proposed Changes**:: - By casting the user id to a `str`, we force all user ids to be handled as `str`. Note, that we do not have to parse back the user id to a `int` when sending message back to Telegram, as the library accepts user ids as `str` and `int`. - `MongoTrackerStore` is the only `TrackerStore` which might be affected by this as it would not be able to find conversations which have been saved using `str` user ids. Therefore, we try querying with an `int` user_id and in doing so update the sender_id to the now used `str` version. --- CHANGELOG.rst | 3 ++ rasa_core/channels/channel.py | 2 +- rasa_core/tracker_store.py | 8 ++++++ tests/test_channels.py | 54 +++++++++++++++++++++++++++++++++-- 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 377e02799a6..6b26de0cb3d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -41,6 +41,9 @@ Fixed - argument ``--connector`` on run script accepts custom channel module names - properly handle non ascii categorical slot values, e.g. ``大于100亿元`` - fixed HTTP server attempting to authenticate based on incorrect path to the correct JWT data field +- all sender ids from channels are now handled as `str`. + Sender ids from old messages with an `int` id are converted to `str`. + [0.11.12] - 2018-10-11 ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/rasa_core/channels/channel.py b/rasa_core/channels/channel.py index d822cffc4be..16acb94fd27 100644 --- a/rasa_core/channels/channel.py +++ b/rasa_core/channels/channel.py @@ -44,7 +44,7 @@ def __init__(self, self.output_channel = CollectingOutputChannel() if sender_id is not None: - self.sender_id = sender_id + self.sender_id = str(sender_id) else: self.sender_id = self.DEFAULT_SENDER_ID diff --git a/rasa_core/tracker_store.py b/rasa_core/tracker_store.py index ceebbdc0e9e..c78106df3de 100644 --- a/rasa_core/tracker_store.py +++ b/rasa_core/tracker_store.py @@ -192,6 +192,14 @@ def save(self, tracker, timeout=None): def retrieve(self, sender_id): stored = self.conversations.find_one({"sender_id": sender_id}) + + # look for conversations which have used an `int` sender_id in the past and update them. + if stored is None and sender_id.isdigit(): + from pymongo import ReturnDocument + stored = self.conversations.find_one_and_update({"sender_id": int(sender_id)}, + {"$set": {"sender_id": str(sender_id)}}, + return_document=ReturnDocument.AFTER) + if stored is not None: if self.domain: return DialogueStateTracker.from_dict(sender_id, diff --git a/tests/test_channels.py b/tests/test_channels.py index 3e702d13261..d914bc69452 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -3,8 +3,6 @@ from __future__ import print_function from __future__ import unicode_literals -import requests - import json from httpretty import httpretty @@ -256,6 +254,58 @@ def test_telegram_channel(): httpretty.disable() +def test_handling_of_telegram_user_id(): + # telegram channel will try to set a webhook, so we need to mock the api + + httpretty.register_uri( + httpretty.POST, + 'https://api.telegram.org/bot123:YOUR_ACCESS_TOKEN/setWebhook', + body='{"ok": true, "result": {}}') + + # telegram will try to verify the user, so we need to mock the api + httpretty.register_uri( + httpretty.GET, + 'https://api.telegram.org/bot123:YOUR_ACCESS_TOKEN/getMe', + body='{"result": {"id": 0, "first_name": "Test", "is_bot": true, "username": "YOUR_TELEGRAM_BOT"}}') + + httpretty.enable() + + from rasa_core.channels.telegram import TelegramInput + from rasa_core.agent import Agent + from rasa_core.interpreter import RegexInterpreter + + # load your trained agent + agent = Agent.load(MODEL_PATH, interpreter=RegexInterpreter()) + + input_channel = TelegramInput( + # you get this when setting up a bot + access_token="123:YOUR_ACCESS_TOKEN", + # this is your bots username + verify="YOUR_TELEGRAM_BOT", + # the url your bot should listen for messages + webhook_url="YOUR_WEBHOOK_URL" + ) + + from flask import Flask + import rasa_core + app = Flask(__name__) + rasa_core.channels.channel.register([input_channel], + app, + agent.handle_message, + route="/webhooks/") + + data = {"message": {"chat": {"id": 1234, "type": "private"}, + "text": "Hello", "message_id": 0, "date": 0}, + "update_id": 0, +} + test_client = app.test_client() + test_client.post("http://localhost:5004/webhooks/telegram/webhook", data=json.dumps(data), + content_type='application/json') + + assert agent.tracker_store.retrieve("1234") is not None + httpretty.disable() + + # USED FOR DOCS - don't rename without changing in the docs def test_twilio_channel(): from rasa_core.channels.twilio import TwilioInput From 24469a643e2610457db6db576612501d08134a7d Mon Sep 17 00:00:00 2001 From: Tobias Wochinger Date: Wed, 17 Oct 2018 15:12:14 +0200 Subject: [PATCH 2/3] #1176 - wrap lines after 80 chars --- rasa_core/tracker_store.py | 10 ++++++---- tests/test_channels.py | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/rasa_core/tracker_store.py b/rasa_core/tracker_store.py index c78106df3de..1d1821c9b01 100644 --- a/rasa_core/tracker_store.py +++ b/rasa_core/tracker_store.py @@ -193,12 +193,14 @@ def save(self, tracker, timeout=None): def retrieve(self, sender_id): stored = self.conversations.find_one({"sender_id": sender_id}) - # look for conversations which have used an `int` sender_id in the past and update them. + # look for conversations which have used an `int` sender_id in the past + # and update them. if stored is None and sender_id.isdigit(): from pymongo import ReturnDocument - stored = self.conversations.find_one_and_update({"sender_id": int(sender_id)}, - {"$set": {"sender_id": str(sender_id)}}, - return_document=ReturnDocument.AFTER) + stored = self.conversations.find_one_and_update( + {"sender_id": int(sender_id)}, + {"$set": {"sender_id": str(sender_id)}}, + return_document=ReturnDocument.AFTER) if stored is not None: if self.domain: diff --git a/tests/test_channels.py b/tests/test_channels.py index d914bc69452..fa6a43e5de1 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -266,7 +266,8 @@ def test_handling_of_telegram_user_id(): httpretty.register_uri( httpretty.GET, 'https://api.telegram.org/bot123:YOUR_ACCESS_TOKEN/getMe', - body='{"result": {"id": 0, "first_name": "Test", "is_bot": true, "username": "YOUR_TELEGRAM_BOT"}}') + body='{"result": {"id": 0, "first_name": "Test", "is_bot": true, ' + '"username": "YOUR_TELEGRAM_BOT"}}') httpretty.enable() @@ -296,10 +297,10 @@ def test_handling_of_telegram_user_id(): data = {"message": {"chat": {"id": 1234, "type": "private"}, "text": "Hello", "message_id": 0, "date": 0}, - "update_id": 0, -} + "update_id": 0} test_client = app.test_client() - test_client.post("http://localhost:5004/webhooks/telegram/webhook", data=json.dumps(data), + test_client.post("http://localhost:5004/webhooks/telegram/webhook", + data=json.dumps(data), content_type='application/json') assert agent.tracker_store.retrieve("1234") is not None From 113b0cc2de0455c5c587d6376ad2aa5e42641d3c Mon Sep 17 00:00:00 2001 From: Tobias Wochinger Date: Wed, 17 Oct 2018 19:59:36 +0200 Subject: [PATCH 3/3] #1176 - fixed test case by mocking additional telegram api request --- tests/test_channels.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_channels.py b/tests/test_channels.py index fa6a43e5de1..f4b45bb90c4 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -269,6 +269,12 @@ def test_handling_of_telegram_user_id(): body='{"result": {"id": 0, "first_name": "Test", "is_bot": true, ' '"username": "YOUR_TELEGRAM_BOT"}}') + # The channel will try to send a message back to telegram, so mock it. + httpretty.register_uri( + httpretty.POST, + 'https://api.telegram.org/bot123:YOUR_ACCESS_TOKEN/sendMessage', + body='{"ok": true, "result": {}}') + httpretty.enable() from rasa_core.channels.telegram import TelegramInput