Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions examples/conversationbot2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Simple Bot to reply to Telegram messages
# This program is dedicated to the public domain under the CC0 license.
"""
This Bot uses the Updater class to handle the bot.

First, a few callback functions are defined. Then, those functions are passed to
the Dispatcher and registered at their respective places.
Then, the bot is started and runs until we press Ctrl-C on the command line.

Usage:
Example of a bot-user conversation using ConversationHandler.
Send /start to initiate the conversation.
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""

from telegram import ReplyKeyboardMarkup
from telegram.ext import (Updater, CommandHandler, MessageHandler, Filters, RegexHandler,
ConversationHandler)

import logging

# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)

logger = logging.getLogger(__name__)

CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3)

reply_keyboard = [['Age', 'Favourite colour'],
['Number of siblings', 'Something else...'],
['Done']]
markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True)


def facts_to_str(user_data):
facts = list()

for key, value in user_data.items():
facts.append('%s - %s' % (key, value))

return "\n".join(facts).join(['\n', '\n'])


def start(bot, update):
update.message.reply_text(
"Hi! My name is Doctor Botter. I will hold a more complex conversation with you. "
"Why don't you tell me something about yourself?",
reply_markup=markup)

return CHOOSING


def regular_choice(bot, update, user_data):
text = update.message.text
user_data['choice'] = text
update.message.reply_text('Your %s? Yes, I would love to hear about that!' % text.lower())

return TYPING_REPLY


def custom_choice(bot, update):
update.message.reply_text('Alright, please send me the category first, '
'for example "Most impressive skill"')

return TYPING_CHOICE


def received_information(bot, update, user_data):
text = update.message.text
category = user_data['choice']
user_data[category] = text
del user_data['choice']

update.message.reply_text("Neat! Just so you know, this is what you already told me:"
"%s"
"You can tell me more, or change your opinion on something."
% facts_to_str(user_data),
reply_markup=markup)

return CHOOSING


def done(bot, update, user_data):
if 'choice' in user_data:
del user_data['choice']

update.message.reply_text("I learned these facts about you:"
"%s"
"Until next time!" % facts_to_str(user_data))

user_data.clear()
return ConversationHandler.END


def error(bot, update, error):
logger.warn('Update "%s" caused error "%s"' % (update, error))


def main():
# Create the Updater and pass it your bot's token.
updater = Updater("TOKEN")

# Get the dispatcher to register handlers
dp = updater.dispatcher

# Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],

states={
CHOOSING: [RegexHandler('^(Age|Favourite colour|Number of siblings)$',
regular_choice,
pass_user_data=True),
RegexHandler('^Something else...$',
custom_choice),
],

TYPING_CHOICE: [MessageHandler([Filters.text],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the bitwise-filters PR this will raise a warning. Just so you're aware.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this was already merged, right ^^

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a second thought, I'll wait with that till the release

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, should probably change throughout our examples at that point then :)

regular_choice,
pass_user_data=True),
],

TYPING_REPLY: [MessageHandler([Filters.text],
received_information,
pass_user_data=True),
],
},

fallbacks=[RegexHandler('^Done$', done, pass_user_data=True)]
)

dp.add_handler(conv_handler)

# log all errors
dp.add_error_handler(error)

# Start the Bot
updater.start_polling()

# Run the bot until the you presses Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()


if __name__ == '__main__':
main()
20 changes: 17 additions & 3 deletions telegram/ext/callbackqueryhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ class CallbackQueryHandler(Handler):
pass_groupdict (optional[bool]): If the callback should be passed the
result of ``re.match(pattern, data).groupdict()`` as a keyword
argument called ``groupdict``. Default is ``False``
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""

def __init__(self,
Expand All @@ -60,9 +68,15 @@ def __init__(self,
pass_job_queue=False,
pattern=None,
pass_groups=False,
pass_groupdict=False):
pass_groupdict=False,
pass_user_data=False,
pass_chat_data=False):
super(CallbackQueryHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)

if isinstance(pattern, string_types):
pattern = re.compile(pattern)
Expand All @@ -81,7 +95,7 @@ def check_update(self, update):
return True

def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)
if self.pattern:
match = re.match(self.pattern, update.callback_query.data)

Expand Down
23 changes: 20 additions & 3 deletions telegram/ext/choseninlineresulthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,34 @@ class ChosenInlineResultHandler(Handler):
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""

def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(ChosenInlineResultHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)

def check_update(self, update):
return isinstance(update, Update) and update.chosen_inline_result

def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)

return self.callback(dispatcher.bot, update, **optional_args)

Expand Down
20 changes: 17 additions & 3 deletions telegram/ext/commandhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ class CommandHandler(Handler):
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""

def __init__(self,
Expand All @@ -57,9 +65,15 @@ def __init__(self,
allow_edited=False,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False):
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
super(CommandHandler, self).__init__(
callback, pass_update_queue=pass_update_queue, pass_job_queue=pass_job_queue)
callback,
pass_update_queue=pass_update_queue,
pass_job_queue=pass_job_queue,
pass_user_data=pass_user_data,
pass_chat_data=pass_chat_data)
self.command = command
self.allow_edited = allow_edited
self.pass_args = pass_args
Expand All @@ -76,7 +90,7 @@ def check_update(self, update):
return False

def handle_update(self, update, dispatcher):
optional_args = self.collect_optional_args(dispatcher)
optional_args = self.collect_optional_args(dispatcher, update)

message = update.message or update.edited_message

Expand Down
25 changes: 2 additions & 23 deletions telegram/ext/conversationhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from telegram import Update
from telegram.ext import Handler
from telegram.utils.helpers import extract_chat_and_user
from telegram.utils.promise import Promise


Expand Down Expand Up @@ -115,29 +116,7 @@ def check_update(self, update):
if not isinstance(update, Update):
return False

user = None
chat = None

if update.message:
user = update.message.from_user
chat = update.message.chat

elif update.edited_message:
user = update.edited_message.from_user
chat = update.edited_message.chat

elif update.inline_query:
user = update.inline_query.from_user

elif update.chosen_inline_result:
user = update.chosen_inline_result.from_user

elif update.callback_query:
user = update.callback_query.from_user
chat = update.callback_query.message.chat if update.callback_query.message else None

else:
return False
chat, user = extract_chat_and_user(update)

key = (chat.id, user.id) if chat else (None, user.id)
state = self.conversations.get(key)
Expand Down
6 changes: 6 additions & 0 deletions telegram/ext/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from threading import Thread, Lock, Event, current_thread, BoundedSemaphore
from time import sleep
from uuid import uuid4
from collections import defaultdict

from queue import Queue, Empty

Expand Down Expand Up @@ -86,6 +87,11 @@ def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue
self.job_queue = job_queue
self.workers = workers

self.user_data = defaultdict(dict)
""":type: dict[int, dict]"""
self.chat_data = defaultdict(dict)
""":type: dict[int, dict]"""

self.handlers = {}
""":type: dict[int, list[Handler]"""
self.groups = []
Expand Down
29 changes: 27 additions & 2 deletions telegram/ext/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
Dispatcher """

from telegram.utils.deprecate import deprecate
from telegram.utils.helpers import extract_chat_and_user


class Handler(object):
Expand All @@ -39,12 +40,27 @@ class Handler(object):
``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
instance created by the ``Updater`` which can be used to schedule new jobs.
Default is ``False``.
pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
``user_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the user that sent the update. For each update of
the same user, it will be the same ``dict``. Default is ``False``.
pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
``chat_data`` will be passed to the callback function. It will be a ``dict`` you
can use to keep any data related to the chat that the update was sent in.
For each update in the same chat, it will be the same ``dict``. Default is ``False``.
"""

def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
def __init__(self,
callback,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):
self.callback = callback
self.pass_update_queue = pass_update_queue
self.pass_job_queue = pass_job_queue
self.pass_user_data = pass_user_data
self.pass_chat_data = pass_chat_data

def check_update(self, update):
"""
Expand Down Expand Up @@ -74,7 +90,7 @@ def handle_update(self, update, dispatcher):
"""
raise NotImplementedError

def collect_optional_args(self, dispatcher):
def collect_optional_args(self, dispatcher, update=None):
"""
Prepares the optional arguments that are the same for all types of
handlers
Expand All @@ -83,10 +99,19 @@ def collect_optional_args(self, dispatcher):
dispatcher (Dispatcher):
"""
optional_args = dict()

if self.pass_update_queue:
optional_args['update_queue'] = dispatcher.update_queue
if self.pass_job_queue:
optional_args['job_queue'] = dispatcher.job_queue
if self.pass_user_data or self.pass_chat_data:
chat, user = extract_chat_and_user(update)

if self.pass_user_data:
optional_args['user_data'] = dispatcher.user_data[user.id]

if self.pass_chat_data:
optional_args['chat_data'] = dispatcher.chat_data[chat.id if chat else None]

return optional_args

Expand Down
Loading