Skip to content

Customize context #2262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Jun 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d2e768d
Document CC constructors
Bibo-Joshi Dec 20, 2020
4f8014f
Add custom_context parameter and try to sort out annotations
Bibo-Joshi Dec 22, 2020
680ef7a
Add tests
Bibo-Joshi Dec 22, 2020
e1eb585
POC for customizing *_data
Bibo-Joshi Dec 22, 2020
7de4a33
merge master
Bibo-Joshi Jan 14, 2021
9189994
Get typing to work to reasonable accuracy
Bibo-Joshi Jan 15, 2021
254fe2d
Improve type hints some more
Bibo-Joshi Jan 16, 2021
f24cdff
Test Dispatcher integration
Bibo-Joshi Jan 16, 2021
c221944
Pass CC to PP
Bibo-Joshi Jan 17, 2021
932b86e
Merge branch 'master' into customize-context
Bibo-Joshi Jan 21, 2021
6eed0ea
Fix tests
Bibo-Joshi Jan 21, 2021
c93eb28
Add tests for PicklePersistence
Bibo-Joshi Jan 21, 2021
c72559f
Increase coverage
Bibo-Joshi Jan 21, 2021
a058cf4
Merge branch 'master' into customize-context
Bibo-Joshi Apr 30, 2021
1ff8503
Start refactoring to introduce BasePersistence.refresh_*_data
Bibo-Joshi Apr 30, 2021
524ce0d
Fix existing tests
Bibo-Joshi Apr 30, 2021
a0a65dc
Add new tests
Bibo-Joshi Apr 30, 2021
86f3a13
Update docs & add some versioning directives
Bibo-Joshi May 1, 2021
ab99075
Merge branch 'master' into customize-context
Bibo-Joshi May 5, 2021
5dc7e18
Annotation fix
Bibo-Joshi May 5, 2021
39d1e78
Only specify major version of GH Actions
Bibo-Joshi May 5, 2021
6753bcf
Slight typing improvements
Bibo-Joshi May 10, 2021
b20cd09
Address review
Bibo-Joshi May 14, 2021
e184443
More review
Bibo-Joshi May 14, 2021
ce4738c
Shame on me
Bibo-Joshi May 14, 2021
39a9f83
Drop double copying from persistence & adjust tests
Bibo-Joshi May 15, 2021
700878d
Merge branch 'master' into customize-context
Bibo-Joshi May 27, 2021
c16a647
Add example to examples directory
Bibo-Joshi May 27, 2021
cb17b85
Deepsource
Bibo-Joshi May 27, 2021
e4a05d9
review
Bibo-Joshi May 28, 2021
45eae20
Make BP.refresh_* methods non-breaking
Bibo-Joshi May 28, 2021
5779f8e
Merge branch 'master' into customize-context
Bibo-Joshi May 28, 2021
86bec4a
Merge branch 'master' into customize-context
Bibo-Joshi May 30, 2021
ae7b6e5
Fix slots for Updater on py3.6
Bibo-Joshi May 30, 2021
2ca7f17
Fix slot tests
Bibo-Joshi May 30, 2021
7e6e39e
Merge branch 'slot-fixes' into customize-context
Bibo-Joshi May 30, 2021
5f5b4f0
Merge branch 'master' into customize-context
Bibo-Joshi May 30, 2021
23c4577
More slot fixes 🥳
Bibo-Joshi May 30, 2021
871f334
Try fixing pre-commit
Bibo-Joshi May 30, 2021
d6b04ea
Some more versioning directives
Bibo-Joshi Jun 3, 2021
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
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
shell: bash --noprofile --norc {0}

- name: Submit coverage
uses: codecov/codecov-action@v1.0.13
uses: codecov/codecov-action@v1
with:
env_vars: OS,PYTHON
name: ${{ matrix.os }}-${{ matrix.python-version }}
Expand All @@ -79,7 +79,7 @@ jobs:
run:
git submodule update --init --recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
run:
git submodule update --init --recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
8 changes: 8 additions & 0 deletions docs/source/telegram.ext.contexttypes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/contexttypes.py

telegram.ext.ContextTypes
=========================

.. autoclass:: telegram.ext.ContextTypes
:members:
:show-inheritance:
6 changes: 4 additions & 2 deletions docs/source/telegram.ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ telegram.ext package
telegram.ext.dispatcher
telegram.ext.dispatcherhandlerstop
telegram.ext.callbackcontext
telegram.ext.defaults
telegram.ext.job
telegram.ext.jobqueue
telegram.ext.messagequeue
telegram.ext.delayqueue
telegram.ext.contexttypes
telegram.ext.defaults

Handlers
--------
Expand Down Expand Up @@ -51,4 +52,5 @@ utils

.. toctree::

telegram.ext.utils.promise
telegram.ext.utils.promise
telegram.ext.utils.types
8 changes: 8 additions & 0 deletions docs/source/telegram.ext.utils.types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/utils/types.py

telegram.ext.utils.types Module
================================

.. automodule:: telegram.ext.utils.types
:members:
:show-inheritance:
5 changes: 4 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ A basic example of a bot that can accept payments. Don't forget to enable and co
A basic example on how to set up a custom error handler.

### [`chatmemberbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/chatmemberbot.py)
A basic example on how `(my_)chat_member` updates can be used.
A basic example on how `(my_)chat_member` updates can be used.

### [`contexttypesbot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/contexttypesbot.py)
This example showcases how `telegram.ext.ContextTypes` can be used to customize the `context` argument of handler and job callbacks.

## Pure API
The [`rawapibot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/rawapibot.py) example uses only the pure, "bare-metal" API wrapper.
129 changes: 129 additions & 0 deletions examples/contexttypesbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env python
# pylint: disable=C0116,W0613
# This program is dedicated to the public domain under the CC0 license.

"""
Simple Bot to showcase `telegram.ext.ContextTypes`.

Usage:
Press Ctrl-C on the command line or send a signal to the process to stop the
bot.
"""

from collections import defaultdict
from typing import DefaultDict, Optional, Set

from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, ParseMode
from telegram.ext import (
Updater,
CommandHandler,
CallbackContext,
ContextTypes,
CallbackQueryHandler,
TypeHandler,
Dispatcher,
)


class ChatData:
"""Custom class for chat_data. Here we store data per message."""

def __init__(self) -> None:
self.clicks_per_message: DefaultDict[int, int] = defaultdict(int)


# The [dict, ChatData, dict] is for type checkers like mypy
class CustomContext(CallbackContext[dict, ChatData, dict]):
"""Custom class for context."""

def __init__(self, dispatcher: Dispatcher):
super().__init__(dispatcher=dispatcher)
self._message_id: Optional[int] = None

@property
def bot_user_ids(self) -> Set[int]:
"""Custom shortcut to access a value stored in the bot_data dict"""
return self.bot_data.setdefault('user_ids', set())

@property
def message_clicks(self) -> Optional[int]:
"""Access the number of clicks for the message this context object was built for."""
if self._message_id:
return self.chat_data.clicks_per_message[self._message_id]
return None

@message_clicks.setter
def message_clicks(self, value: int) -> None:
"""Allow to change the count"""
if not self._message_id:
raise RuntimeError('There is no message associated with this context obejct.')
self.chat_data.clicks_per_message[self._message_id] = value

@classmethod
def from_update(cls, update: object, dispatcher: 'Dispatcher') -> 'CustomContext':
"""Override from_update to set _message_id."""
# Make sure to call super()
context = super().from_update(update, dispatcher)

if context.chat_data and isinstance(update, Update) and update.effective_message:
context._message_id = update.effective_message.message_id # pylint: disable=W0212

# Remember to return the object
return context


def start(update: Update, context: CustomContext) -> None:
"""Display a message with a button."""
update.message.reply_html(
'This button was clicked <i>0</i> times.',
reply_markup=InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text='Click me!', callback_data='button')
),
)


def count_click(update: Update, context: CustomContext) -> None:
"""Update the click count for the message."""
context.message_clicks += 1
update.callback_query.answer()
update.effective_message.edit_text(
f'This button was clicked <i>{context.message_clicks}</i> times.',
reply_markup=InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text='Click me!', callback_data='button')
),
parse_mode=ParseMode.HTML,
)


def print_users(update: Update, context: CustomContext) -> None:
"""Show which users have been using this bot."""
update.message.reply_text(
'The following user IDs have used this bot: '
f'{", ".join(map(str, context.bot_user_ids))}'
)


def track_users(update: Update, context: CustomContext) -> None:
"""Store the user id of the incoming update, if any."""
if update.effective_user:
context.bot_user_ids.add(update.effective_user.id)


def main() -> None:
"""Run the bot."""
context_types = ContextTypes(context=CustomContext, chat_data=ChatData)
updater = Updater("TOKEN", context_types=context_types)

dispatcher = updater.dispatcher
# run track_users in its own group to not interfere with the user handlers
dispatcher.add_handler(TypeHandler(Update, track_users), group=-1)
dispatcher.add_handler(CommandHandler("start", start))
dispatcher.add_handler(CallbackQueryHandler(count_click))
dispatcher.add_handler(CommandHandler("print_users", print_users))

updater.start_polling()
updater.idle()


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ omit =
[coverage:report]
exclude_lines =
if TYPE_CHECKING:
...

[mypy]
warn_unused_ignores = True
Expand Down
61 changes: 38 additions & 23 deletions telegram/ext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
# pylint: disable=C0413
"""Extensions over the Telegram Bot API to facilitate bot making"""

from .basepersistence import BasePersistence
from .picklepersistence import PicklePersistence
from .dictpersistence import DictPersistence
from .handler import Handler
from .callbackcontext import CallbackContext
from .contexttypes import ContextTypes
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async

# https://bugs.python.org/issue41451, fixed on 3.7+, doesn't actually remove slots
# try-except is just here in case the __init__ is called twice (like in the tests)
# this block is also the reason for the pylint-ignore at the top of the file
try:
del Dispatcher.__slots__ # type: ignore[has-type]
except AttributeError as exc:
if str(exc) == '__slots__':
pass
else:
raise exc

from .jobqueue import JobQueue, Job
from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler
Expand All @@ -47,38 +61,39 @@
from .defaults import Defaults

__all__ = (
'Dispatcher',
'JobQueue',
'Job',
'Updater',
'BaseFilter',
'BasePersistence',
'CallbackContext',
'CallbackQueryHandler',
'ChatMemberHandler',
'ChosenInlineResultHandler',
'CommandHandler',
'ContextTypes',
'ConversationHandler',
'Defaults',
'DelayQueue',
'DictPersistence',
'Dispatcher',
'DispatcherHandlerStop',
'Filters',
'Handler',
'InlineQueryHandler',
'MessageHandler',
'BaseFilter',
'Job',
'JobQueue',
'MessageFilter',
'UpdateFilter',
'Filters',
'MessageHandler',
'MessageQueue',
'PicklePersistence',
'PollAnswerHandler',
'PollHandler',
'PreCheckoutQueryHandler',
'PrefixHandler',
'RegexHandler',
'ShippingQueryHandler',
'StringCommandHandler',
'StringRegexHandler',
'TypeHandler',
'ConversationHandler',
'PreCheckoutQueryHandler',
'ShippingQueryHandler',
'MessageQueue',
'DelayQueue',
'DispatcherHandlerStop',
'UpdateFilter',
'Updater',
'run_async',
'CallbackContext',
'BasePersistence',
'PicklePersistence',
'DictPersistence',
'PrefixHandler',
'PollAnswerHandler',
'PollHandler',
'ChatMemberHandler',
'Defaults',
)
Loading