These chatbots are created in the context of WeNet: the Internet of us.
These Telegram chatbots are the first WeNet applications, allowing users to communicate with each other to organize shared meals with potential unknown people, or to ask questions and generic help, in a strong privacy-oriented manner.
The eat-together bot allows users to create a new task, representing a shared meal. The WeNet platform will select some users potentially interested in the newly created task, and the bot will ask them to candidate to participate to it. Subsequently, the task creator will be notified of a candidature, being asked whether to accept or reject it. Candidates will acknowledge the owner's decision. Eventually, the task is closed by the owner using the chatbot, providing also an outcome, either successfully or failed.
The ask-for-help bot allows to create tasks that are questions, to which other WeNet users will answer.
The bots expose an HTTP endpoint to receive messages and notifications from the WeNet platform.
The bots are deterministic finite automatas (DFA), that are graphs where each node is a possible state in which the conversation can be, and the edges are the transactions between two states. For example:
- The bot is in its initial state
S0, which means that the user has not used it yet. - The user uses the
/startcommand: this is a transaction, going from the initial stateS0to the next stateS1. - State
S1proposes a message with 3 buttons:B1,B2andB3; each button makes the state change to a different state. - So we can have 3 transactions exiting from
S1: the one triggered byB1, the one triggered byB2and the one triggered byB3. - Each transaction makes the user land into a state, either
S2,S3orS4.
So the next state S_(i + 1) depends from the current state S_(i) and from the input received by the bot.
The WenetEventHandler is an abstract class containing the common things used by the two real handlers of the two bots, including the management of the DFA.
The current state is saved in the user context, using as key the constant self.CONTEXT_CURRENT_STATE, while the value represents the state itself. Of course the value must be unique for each possible state of the bot, so it can be useful to define each possible state as constant in the handler class.
Every exchange of message between the user and the bot implies a change of state, so every time the context must be updated. Any possible additional piece of information needed to continue the conversation flow must be saved in the context. For example, to create a new taks the user must specify several information (a date, a title, a description, etc): all these data points are collected in several steps, each one represented by a state, and saving every time the user's inputs in the context. Only in the final state, the task is created and saved.
Assuming that we are in a state where we are expecting that the user types the name of the task, we know that the next input coming from the user will be the name of the task, and so we can use the intent manager to route the flow of the bot.
For example, the following piece of code calls the function self.organize_q2 passing to it the intent self.ORGANIZE_Q2, and calling it every time an input is received and the current state is equal to self.ORGANIZE_Q1.
self.intent_manager.with_fulfiller(
IntentFulfillerV3(self.ORGANIZE_Q2, self.organize_q2).with_rule(
static_context=(self.CONTEXT_CURRENT_STATE, self.ORGANIZE_Q1))
)The bots can receive messages from the Wenet platform (notifications, textual messages, etc). Some methods are already available in the wenet handler to manage these situations:
handle_wenet_textual_message()is triggered every time aTextualMessageis sent to the bot;handle_wenet_authentication_result()is triggered every time an user performs the authentication in the Wenet Hub;handle_wenet_message()is triggered in all the remaining cases, so when the message from Wenet is neither aTextualMessagenor aWeNetAuthenticationEvent; each application implements its own custom messages, and this is the place to handle them.
In these cases the messages are sent directly to the bot, without passing through the chatbot interface. So the user context must be explicitly fetched, together with the user information:
- Get the user account related with the receiver id of the message:
user_accounts = self.get_user_accounts(message.receiver_id)
if len(user_accounts) != 1:
raise Exception(f"No context associated with Wenet user {message.receiver_id}")
user_account = user_accounts[0]- Get the context (it comes for free):
context = user_account.context- Instanciate the service API:
service_api = self._get_service_api_interface_connector_from_context(context)The service API handler manages the OAuth tokens, so every time it is invoked is safe to call the following instruction to save the updated token:
context = self._save_updated_token(context, service_api.client)- At the end, a
NotificationEventmust be returned:
return NotificationEvent(user_account.social_details, response_list, context)where response_list is a list of ResponseMessage objects.
The chatbot required Python version 3.7 or higher.
All required Python packages can be installed using the command:
pip install -r requirements.txtA dedicated Docker image for this component can be build by taking advantage of the repository Docker support. The command:
./build_docker_image.shwill:
- run the tests on the checked-out version of the service APIs;
- build the docker image for the chatbot (the naming is the following
registry.u-hopper.com/)
In order to run the eat-together chatbot, run the following command:
python -m eat_together_bot.mainIn order to run the ask-for-help chatbot, run the following command:
python -m ask_for_help_bot.mainIn order to run the endpoint, run the following command:
python -m messages.mainBoth the chatbots use the following environment variables:
TELEGRAM_KEY: secret key provided by Telegram to use the bot APIMQTT_HOST: MQTT hostMQTT_SUBSCRIBER_ID: MQTT subscriber id for the clientMQTT_USER: MQTT userMQTT_PASSWORD: MQTT passwordMQTT_TOPIC: MQTT topic to listen onINTERFACE_APIKEY: bot interface api keyINSTANCE_NAMESPACE: bot instance namespaceWENET_INSTANCE_URL: the url of the WeNet instanceWENET_APP_ID: WeNet App ID used by the botWENET_HUB_URL: url of the WeNet hubTASK_TYPE_ID: the type ID of the tasksCOMMUNITY_ID: the community IDCLIENT_SECRET: the secret key of the WeNet applicationWENET_AUTHENTICATION_URL: the URL that manages authentication in WeNetWENET_AUTHENTICATION_MANAGEMENT_URL: the URL that manages OAuth in WeNetREDIRECT_URL: the redirection URL associated with the WeNet applicationPROJECT_NAME(optional): a string that will be used as name of the log file (with the format<PROJECT_NAME>.log). The default value iswenet-ask-for-help-chatbotLOCALE_TTL(optional): the time to live of the Redis key in which the user locale is saved, in seconds. By default, it is 86400 (24h).SENTRY_DSN: (Optional) The data source name for sentry, if not set the project will not create any eventSENTRY_RELEASE: (Optional) If set, sentry will associate the events to the given releaseSENTRY_ENVIRONMENT: (Optional) If set, sentry will associate the events to the given environment (ex.production,staging)
The ask4help bot also needs the following environment variables:
MAX_USERS: the maximum number of users that should receive the question in the ask4help bot (default is 5)MAX_ANSWERS: the maximum number of answers that should receive a question in the ask4help bot before expiring (default is 15)EXPIRATION_DURATION: the maximum amount of time before a question in the ask4help bot expires (default is 86400)NEARBY_EXPIRATION_DURATION: the maximum amount of time before a nearby question in the ask4help bot expires (default is 7200)SURVEY_URL: the url of the surveyPILOT_HELPER_URL: (Optional) the url of the helper page specific for a pilotCHANNEL_ID: (Optional) the id of the channel where to publish questions and best answersPUBLICATION_LANGUAGE: (Optional) the language in which to publish messages on the channel. The default isen
For the translations of the badges messages the following environment variables are needed:
FIRST_QUESTION_BADGE_ID: the id of the first question badgeCURIOUS_LEVEL_1_BADGE_ID: the id of the curious level 1 badgeCURIOUS_LEVEL_2_BADGE_ID: the id of the curious level 2 badgeFIRST_ANSWER_BADGE_ID: the id of the first answer badgeHELPER_LEVEL_1_BADGE_ID: the id of the helper level 1 badgeHELPER_LEVEL_2_BADGE_ID: the id of the helper level 2 badgeFIRST_GOOD_ANSWER_BADGE_ID: the id of the first good answer badgeGOOD_ANSWERS_LEVEL_1_BADGE_ID: the id of the good answerer level 1 badgeGOOD_ANSWERS_LEVEL_2_BADGE_ID: the id of the good answerer level 2 badgeFIRST_LONG_ANSWER_BADGE_ID: the id of the first long answer badgeEXPLAINER_LEVEL_1_BADGE_ID: the id of the explainer level 1 badgeEXPLAINER_LEVEL_2_BADGE_ID: the id of the explainer level 2 badge
Optional variables (to setup Redis):
REDIS_HOST(default islocalhost)REDIS_PORT(default is 6379)REDIS_DB(default is 0)
The ask for help bot has the following optional environment variable:
TRANSLATION_FOLDER_PATH, indicating the path of the folder in which translations are stored (default is../../translations).
The endpoint uses the following environment variables:
MESSAGES_HOST: host running the APIs (default to0.0.0.0)MESSAGES_PORT: port of the host (default to12345)MQTT_HOST: MQTT hostMQTT_PUBLISHER_ID: MQTT publisher id for the clientMQTT_USER: MQTT userMQTT_PASSWORD: MQTT passwordMQTT_TOPIC: MQTT topic to write onINSTANCE_NAMESPACE: bot instance namespaceWENET_APP_ID: WeNet App ID used by the botWENET_HUB_URL: url of the WeNet hubBOT_ID: the bot ID associated with the EventHandler used by the bot itself.PROJECT_NAME(optional): a string that will be used as name of the log file (with the format<PROJECT_NAME>-messages.log). The default value iswenet-ask-for-help-chatbot.
Contributions to this project are more than welcome. Contributions regarding the supported languages is particularly appreciated. Details about the contribution guidelines can be found in CONTRIBUTING.md.
The production instance of the ask4help chatbot is available here https://t.me/wenet_ask_for_help_bot.