From d9b22ca861e27c6dc058aada419b0dcddfc2da30 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Mon, 18 Apr 2022 22:28:51 +0530
Subject: [PATCH 01/14] spelling
---
Introduction-to-the-API.md | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/Introduction-to-the-API.md b/Introduction-to-the-API.md
index 040f263..93af936 100644
--- a/Introduction-to-the-API.md
+++ b/Introduction-to-the-API.md
@@ -12,7 +12,7 @@ For full details see the official Telegram documentation at [Bots: An introducti
#### Hello, Telegram!
-To get a feeling for the API and how to use it with `python-telegram-bot`, please a new Python file.
+To get a feeling for the API and how to use it with `python-telegram-bot`, please create a new Python file.
We first want to create an instance of the `telegram.Bot` and check that the credentials are correct.
Please paste the following code into your file.
@@ -68,9 +68,7 @@ The output should now look something like this (we abbreviated the output a bit)
We copy the chat id, here `1234567890`.
Note that you can access it also as `updates[0].message.from_user.id`, because `updates[0]` is an instance of the `Update` class.
-Now that we have the chat ID, we can send a message by again adjust the `main`:
-
-```python
+Now that we have the chat ID, we can send a message by again adjusting the `main()`:
```python
async def main():
From 2556c2e579a7b0e1bf345a272c2307df619b77f8 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Mon, 18 Apr 2022 23:09:41 +0530
Subject: [PATCH 02/14] adapt to asyncio changes
---
Extensions-–-Your-first-Bot.md | 95 +++++++++++++++-------------------
1 file changed, 41 insertions(+), 54 deletions(-)
diff --git a/Extensions-–-Your-first-Bot.md b/Extensions-–-Your-first-Bot.md
index 56332da..c434887 100644
--- a/Extensions-–-Your-first-Bot.md
+++ b/Extensions-–-Your-first-Bot.md
@@ -1,11 +1,11 @@
## Introduction
The `telegram.ext` submodule is built on top of the pure API implementation. It provides an easy-to-use interface and takes some work off the programmer, so you [don't have to repeat yourself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
-It consists of several classes, but the two most important ones are [`telegram.ext.Updater`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.Updater) and [`telegram.ext.Dispatcher`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dispatcher.html#telegram.ext.Dispatcher).
+It consists of several classes, but the most important one is [`telegram.ext.Application`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.application.html#telegram-ext-application).
-The `Updater` class continuously fetches new updates from telegram and passes them on to the `Dispatcher` class.
-If you create an `Updater` object, it will create a `Dispatcher` for you and link them together with a `Queue`.
-You can then register handlers of different types in the `Dispatcher`, which will sort the updates fetched by the `Updater` according to the handlers you registered, and deliver them to a callback function that you defined.
+The `Application` class is responsible for fetching updates from the `update_queue`, which is where the [`Updater`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram-ext-updater) class continuously fetches new updates from Telegram and adds them to this queue.
+If you create an `Application` object, using [`ApplicationBuilder`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.applicationbuilder.html#telegram-ext-applicationbuilder), it will automatically create a `Updater` for you and link them together with an [`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html#asyncio.Queue).
+You can then register handlers of different types in the `Application`, which will sort the updates fetched by the `Updater` according to the handlers you registered, and deliver them to a callback function that you defined.
Every handler is an instance of any subclass of the [`telegram.ext.Handler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.handler.html#telegram.ext.Handler) class. The library provides [[handler classes for almost all use cases|Types-of-Handlers]], but if you need something very specific, you can also subclass `Handler` yourself.
@@ -13,23 +13,15 @@ To begin, you'll need an Access Token. If you have already read and followed [[I
## Your first Bot, step-by-step
-So, *let's get started!* Again, please fire up a Python command line if you want to follow this tutorial.
+So, *let's get started!* Again, please create a new file if you want to follow this tutorial.
-First, you have to create an `Updater` object. Replace `'TOKEN'` with your Bot's API token.
+First, you have to create an `Application` object. Replace `'TOKEN'` with your Bot's API token.
```python
-from telegram.ext import Updater
-updater = Updater(token='TOKEN', use_context=True)
-```
-**Related docs:** [`telegram.ext.Updater`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.updater.Updater)
-
-**Note**: The `use_context=True` is a special argument only needed for version 12 of the library. The default value is `False`. It allows for better backwards compatibility with older versions of the library, and to give users some time to upgrade. From version 13 `use_context=True` it is the default.
-
-For quicker access to the `Dispatcher` used by your `Updater`, you can introduce it locally:
-
-```python
-dispatcher = updater.dispatcher
+from telegram.ext import ApplicationBuilder
+app = ApplicationBuilder().token('TOKEN').build()
```
+**Related docs:** [`telegram.ext.ApplicationBuilder`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.applicationbuilder.html#telegram-ext-applicationbuilder), [`telegram.ext.Application`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.application.html#telegram.ext.Application)
This is a good time to set up the `logging` module, so you will know when (and why) things don't work as expected:
@@ -47,60 +39,60 @@ Now, you can define a function that should process a specific type of update:
from telegram import Update
from telegram.ext import CallbackContext
-def start(update: Update, context: CallbackContext):
- context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!")
+async def start(update: Update, context: CallbackContext):
+ await context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!")
```
-**Related docs:** [`send_message`](https://core.telegram.org/bots/api#sendmessage), [`telegram.ext.CallbackContext` (the type of the context argument)](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html), [`telegram.Update` (the type of update argument)](https://python-telegram-bot.readthedocs.io/en/latest/telegram.update.html)
+**Related docs:** [`send_message`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html#telegram.Bot.send_message), [`telegram.ext.CallbackContext` (the type of the context argument)](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html), [`telegram.Update` (the type of update argument)](https://python-telegram-bot.readthedocs.io/en/latest/telegram.update.html)
-The goal is to have this function called every time the Bot receives a Telegram message that contains the `/start` command. To accomplish that, you can use a `CommandHandler` (one of the provided `Handler` subclasses) and register it in the dispatcher:
+The goal is to have this function called every time the Bot receives a Telegram message that contains the `/start` command. To accomplish that, you can use a `CommandHandler` (one of the provided `Handler` subclasses) and register it in the application:
```python
from telegram.ext import CommandHandler
start_handler = CommandHandler('start', start)
-dispatcher.add_handler(start_handler)
+app.add_handler(start_handler)
```
-**Related docs:** [`telegram.ext.CommandHandler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.commandhandler.html), [`telegram.ext.Dispatcher.add_handler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dispatcher.html#telegram.ext.dispatcher.Dispatcher.add_handler)
+**Related docs:** [`telegram.ext.CommandHandler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.commandhandler.html), [`telegram.ext.Application.add_handler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.application.html#telegram.ext.Application.add_handler)
And that's all you need. To start the bot, run:
```python
-updater.start_polling()
+app.run_polling()
```
-**Related docs:** [`telegram.ext.Updater.start_polling`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.updater.Updater.start_polling)
+**Related docs:** [`telegram.ext.Application.run_polling`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.application.html#telegram.ext.Application.run_polling)
Give it a try! Start a chat with your bot and issue the `/start` command - if all went right, it will reply.
But our Bot can now only answer to the `/start` command. Let's add another handler that listens for regular messages. Use the `MessageHandler`, another `Handler` subclass, to echo all text messages:
```python
-from telegram.ext import MessageHandler, Filters
+from telegram.ext import filters, MessageHandler
-def echo(update: Update, context: CallbackContext):
- context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)
+async def echo(update: Update, context: CallbackContext):
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)
-echo_handler = MessageHandler(Filters.text & (~Filters.command), echo)
-dispatcher.add_handler(echo_handler)
+echo_handler = MessageHandler(filters.TEXT & (~filters.COMMAND), echo)
+app.add_handler(echo_handler)
```
**Related docs:** [`telegram.ext.MessageHandler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagehandler.html), [`telegram.ext.filters`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html)
From now on, your bot should echo all non-command messages it receives.
-**Note:** As soon as you add new handlers to `dispatcher`, they are in effect.
+**Note:** As soon as you add new handlers to `Application`, they are in effect.
-**Note:** The `Filters` class contains a number of so called filters that filter incoming messages for text, images, status updates and more. Any message that returns `True` for at least one of the filters passed to `MessageHandler` will be accepted. You can also write your own filters if you want. See more in [[Advanced Filters|Extensions-–-Advanced-Filters]].
+**Note:** The `filters` module contains a number of so called filters that filter incoming messages for text, images, status updates and more. Any message that returns `True` for at least one of the filters passed to `MessageHandler` will be accepted. You can also write your own filters if you want. See more in [[Advanced Filters|Extensions-–-Advanced-Filters]].
Let's add some actual functionality to your bot. We want to implement a `/caps` command that will take some text as an argument and reply to it in CAPS. To make things easy, you can receive the arguments (as a `list`, split on spaces) that were passed to a command in the callback function:
```python
-def caps(update: Update, context: CallbackContext):
+async def caps(update: Update, context: CallbackContext):
text_caps = ' '.join(context.args).upper()
- context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps)
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps)
caps_handler = CommandHandler('caps', caps)
-dispatcher.add_handler(caps_handler)
+app.add_handler(caps_handler)
```
-**Note:** Take a look at the usage of `context.args`. The [`CallbackContext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html) will have many different attributes, depending on which handler is used.
+**Note:** Take a look at the usage of [`context.args`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html#telegram.ext.CallbackContext.args). The `CallbackContext` will have many different attributes, depending on which handler is used.
Another cool feature of the Telegram Bot API is the [inline mode](https://core.telegram.org/bots/inline). If you want to implement inline functionality for your bot, please first talk to [@BotFather](https://telegram.me/botfather) and enable inline mode using `/setinline`. It sometimes takes a while until your Bot registers as an inline bot on your client. You might be able to speed up the process by restarting your Telegram App (or sometimes, you just have to wait for a while).
@@ -108,7 +100,9 @@ As your bot is obviously a very loud one, let's continue with this theme for inl
```python
from telegram import InlineQueryResultArticle, InputTextMessageContent
-def inline_caps(update: Update, context: CallbackContext):
+from telegram.ext import InlineQueryHandler
+
+async def inline_caps(update: Update, context: CallbackContext):
query = update.inline_query.query
if not query:
return
@@ -120,35 +114,28 @@ def inline_caps(update: Update, context: CallbackContext):
input_message_content=InputTextMessageContent(query.upper())
)
)
- context.bot.answer_inline_query(update.inline_query.id, results)
+ await context.bot.answer_inline_query(update.inline_query.id, results)
-from telegram.ext import InlineQueryHandler
inline_caps_handler = InlineQueryHandler(inline_caps)
-dispatcher.add_handler(inline_caps_handler)
+app.add_handler(inline_caps_handler)
```
-**Related docs:** [telegram.ext.InlineQueryHandler](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.inlinequeryhandler.html), [answerInlineQuery](https://core.telegram.org/bots/api#answerinlinequery)
+**Related docs:** [telegram.ext.InlineQueryHandler](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.inlinequeryhandler.html), [answer_inline_query](https://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html#telegram.Bot.answer_inline_query)
Not bad! Your Bot can now yell on command (ha!) and via inline mode.
-Some confused users might try to send commands to the bot that it doesn't understand, so you can use a `MessageHandler` with a `command` filter to reply to all commands that were not recognized by the previous handlers.
+Some confused users might try to send commands to the bot that it doesn't understand, so you can use a `MessageHandler` with a `COMMAND` filter to reply to all commands that were not recognized by the previous handlers.
```python
-def unknown(update: Update, context: CallbackContext):
- context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.")
+async def unknown(update: Update, context: CallbackContext):
+ await context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.")
-unknown_handler = MessageHandler(Filters.command, unknown)
-dispatcher.add_handler(unknown_handler)
+unknown_handler = MessageHandler(filters.COMMAND, unknown)
+app.add_handler(unknown_handler)
```
-**Note:** This handler *must* be added last. If you added it sooner, it would be triggered before the `CommandHandlers` had a chance to look at the update. Once an update is handled, all further handlers are ignored. To circumvent this, you can pass the keyword argument `group (int)` to `add_handler` with a value other than 0. See [`telegram.ext.Dispatcher.add_handler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dispatcher.html#telegram.ext.dispatcher.Dispatcher.add_handler) for details.
+**Note:** This handler *must* be added last. If you added it sooner, it would be triggered before the `CommandHandlers` had a chance to look at the update. Once an update is handled, all further handlers are ignored. To circumvent this, you can pass the keyword argument `group (int)` to `add_handler` with a value other than 0. See [`telegram.ext.Application.add_handler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.application.html#telegram.ext.Application.add_handler) for details.
-If you're done playing around, stop the bot with:
-
-```python
-updater.stop()
-```
-
-**Note:** As you have read earlier, the `Updater` runs in a separate thread. That is very nice for this tutorial, but if you are writing a script, you probably want to stop the Bot by pressing Ctrl+C or sending a signal to the Bot process. To do that, use `updater.idle()`. It blocks execution until one of those two things occur, then calls `updater.stop()` and then continues execution of the script.
+If you're done playing around, stop the bot by pressing `Ctrl + C`.
#### What to read next?
Have a look at the ready-to-run [examples](https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples).
From ed56108923113a8b5863a52995935a1fd7bfedc4 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Mon, 18 Apr 2022 23:10:46 +0530
Subject: [PATCH 03/14] done with tutorial your first bot
---
Home.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Home.md b/Home.md
index f674e38..1b3ee5e 100644
--- a/Home.md
+++ b/Home.md
@@ -1,7 +1,7 @@
# Pages to update
- [ ] `JobQueue`
-- [ ] Tutorial your first bot
+- [x] Tutorial your first bot
- [ ] inline keyboard example
- [ ] performance optimization - extract the asyncio related things to a standalone page
- [ ] Type checking
From 54ba68efeb35960105a4d13edd99e15357ea56d5 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Tue, 19 Apr 2022 00:37:02 +0530
Subject: [PATCH 04/14] add another point
---
Transition-guide-to-Version-14.0.md | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/Transition-guide-to-Version-14.0.md b/Transition-guide-to-Version-14.0.md
index 4e8b689..682734a 100644
--- a/Transition-guide-to-Version-14.0.md
+++ b/Transition-guide-to-Version-14.0.md
@@ -126,7 +126,7 @@ More detailed information on this can be found in the documentation of `{Base, P
#### `store_*_data`
-The parameters & attributes `store_{user,chat,bot}_data` were removed. Instead, these settings were combined into the argument/attribute `store_data`, which accepts an instance of the new helper class `telegram.ext.PersistenceInput`.
+The parameters & attributes `store_{user,chat,bot}_data` were removed. Instead, these settings were combined into the argument/attribute `store_data`, which accepts an instance of the new helper class [`telegram.ext.PersistenceInput`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.persistenceinput.html#telegram-ext-persistenceinput).
Note that `callback_data` is now persisted by default.
@@ -161,6 +161,12 @@ Moreover, `context.{char, user}_data` will be available. This has some subtile a
Unfortunately, the `day_is_strict` argument was not working correctly (see [#2627](../issues/2627)) and was therefore removed. Instead, you cann now pass `day='last'` to make the job run on the last day of the month.
+### `Job`
+
+#### Removed the attribute `job_queue`
+
+This was removed because if you have access to a job, then you also have access to either the `JobQueue` directly or at least a `CallbackContext` instance, which already contains the `job_queue`.
+
### `PicklePersistence`
* The argument `filename` was renamed to `filepath` and now also accepts a `pathlib.Path` object
@@ -169,3 +175,9 @@ Unfortunately, the `day_is_strict` argument was not working correctly (see [#262
### `Updater`
The sole purpose of this class now is to fetch updates from Telegram. It now only accepts the arguments `bot` and `update_queue` and only has those attributes.
+
+### `Application`/`Dispatcher`
+
+#### `user/chat_data`
+
+If you were modifying the `user/chat_data` of `Dispatcher` directly e.g. by doing `context.dispatcher.chat_data[id] = ...`, then this will now not work. [`Application.user/chat_data`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.application.html#telegram.ext.Application.chat_data) is now read only. Note that `context.chat/user_data[key] = ...` will still work.
From d8b2e08dbf528a3f7d2f483211b529792a72e50b Mon Sep 17 00:00:00 2001
From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Date: Tue, 19 Apr 2022 12:36:36 +0200
Subject: [PATCH 05/14] Updated Transition guide to Version 14.0 (markdown)
---
Transition-guide-to-Version-14.0.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Transition-guide-to-Version-14.0.md b/Transition-guide-to-Version-14.0.md
index 682734a..6fb348e 100644
--- a/Transition-guide-to-Version-14.0.md
+++ b/Transition-guide-to-Version-14.0.md
@@ -70,6 +70,12 @@ This module was rewritten from scratch. The constants are now grouped with the h
The argument `hash` is now the second positional argument as specified by the Bot API.
+### `telegram.error`
+
+`telegram.error.Unauthorized` was replaced by `telegram.error.Forbidden`.
+Moreover, `telegram.error.Forbidden` is now only raised if your bot tries to perform actions that it doesn't have enough rights for.
+In case your bot token is invalid, `telegram.error.InvalidToken` is raised.
+
### `telegram.File`
* The `custom_path` parameter now also accepts `pathlib.Path` objects.
From 0b7a9dc66fde9061594c2ef116e194e34bf759d7 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 00:09:43 +0530
Subject: [PATCH 06/14] adapt FAQ to v14 and asyncio
---
Frequently-Asked-Questions.md | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/Frequently-Asked-Questions.md b/Frequently-Asked-Questions.md
index e97fb15..0c3cf7a 100644
--- a/Frequently-Asked-Questions.md
+++ b/Frequently-Asked-Questions.md
@@ -90,7 +90,7 @@ If your handlers callback returns `None` instead of the next state, you will sta
Receiving updates from an external service, e.g. updates about your GitHub repo, is a common use case.
How exactly you get them sadly is beyond the scope of PTB, as that depends on the service. For many cases a simple approach is to check for updates every x seconds. You can use the [`JobQueue`](Extensions-–-JobQueue) for that.
-If you have a setup for getting the updates, you can put them in your bots update queue via `updater.update_queue.put(your_update)`. The `update_queue` is also available as `dispatcher.update_queue` and `context.update_queue`.
+If you have a setup for getting the updates, you can put them in your bots update queue via `await application.update_queue.put(your_update)`. The `update_queue` is also available as `context.update_queue`.
Note that `your_update` does *not* need to be an instance of `telegram.Update` - on the contrary! You can e.g. write your own custom class to represent an update from your external service.
To actually do something with the update, you can register a [`TypeHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.typehandler.html). [`StringCommandHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.stringcommandhandler.html) and [`StringRegexHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.stringregexhandler.html) might also be interesting for some use cases.
@@ -130,7 +130,13 @@ There is no API method for that (see [here](#can-you-add-feature-to-ptb-can-i-do
### Why am I getting an error `The following arguments have not been supplied`?
-The `callback` method you pass to `JobQueue.run_*` takes exactly *one* argument, which is of type `CallbackContext`. This is, because jobs are triggered by a schedule and not by an update from Telegram. If you want to access data in the callback that changes at runtime (e.g. because you schedule jobs on demand), you can either access `context.bot_data` or pass the data to `run_*` as `run_*(…, context=additional_data)`. It can then be accessed within the `callback` as `context.job.context`. Note that `context.{user, chat}_data` will be `None`, as those can only be present, when the `context` object is related to an update, which is not the case for jobs.
+The `callback` method you pass to `JobQueue.run_*` takes exactly *one* argument, which is of type [`CallbackContext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html#telegram-ext-callbackcontext). This is, because jobs are triggered by a schedule and not by an update from Telegram. If you want to access data in the callback that changes at runtime (e.g. because you schedule jobs on demand), you can:
+
+1. Access `context.bot_data`.
+2. Pass [`{user, chat}_id`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.jobqueue.html#telegram.ext.JobQueue.run_once.params.chat_id) to any of the `run_*(...)` methods so you can access them in your `callback` as `context.{user, chat}_data`
+3. Use `run_*(…, context=additional_data)`. It can then be accessed within the `callback` as `context.job.context`.
+
+Note that `context.{user, chat}_data` will be `None`, if you don't pass the arguments `{user, chat}_id` to any of the `run_*(...)` methods.
### How can I check the version of PTB I am using?
@@ -142,7 +148,7 @@ There are three easy ways to do this. Two work from the command line: `pip show
All bot methods have a return value. For example to get the `message_id` of a text message sent by your bot, you can do
```python
-message = bot.send_message(…)
+message = await bot.send_message(…)
message_id = message.message_id
```
From 043f6f5ff7d5363ac5fc93d8b1d3fcc9614d4ae0 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 00:19:03 +0530
Subject: [PATCH 07/14] spelling update
---
Builder-Pattern.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Builder-Pattern.md b/Builder-Pattern.md
index b2703f0..632f728 100644
--- a/Builder-Pattern.md
+++ b/Builder-Pattern.md
@@ -12,14 +12,14 @@ In addition to those four, there are several other components, which are not as
All of those components have different parameters. Some of them are optional. Some are required. Some are mutually exclusive.
That's a lot to take in and when coding your bot and setting this all up by yourself would be tiresome.
-That's why `python-telegram-bot` makes an effort to make the setup easy with reasonable defaults. E.g. with
+That's why `python-telegram-bot` makes an effort to make the setup easy with reasonable defaults.
```python
from telegram.ext import Application
application = Application.builder().token('TOKEN').build()
```
-`python-telegram-bot` you will automatically have
+E.g. with `python-telegram-bot` you will automatically have
* the `Updater` available as `application.dispatcher`
* the `Bot` available as `application.(updater.)bot`
@@ -28,7 +28,7 @@ application = Application.builder().token('TOKEN').build()
But what if you want to customize some arguments that `Application`, `Updater`, `Bot`, `BaseRequest` or other components accept? Do you have to build all those objects yourself and glue them together? No! (Well, you can, but you don't have to.)
-This is, where the [builder patter](https://en.wikipedia.org/wiki/Builder_pattern) comes into play. The idea is roughly as follows: You went shopping and have all the ingredients for a nice stew, but you don't want to cook yourself. So you hand everything to a chef. The chef will tell you that some of your ingredients don't match and will discard them. Afterwards, he'll cook a nice stew for you and you never need to worry about how exactly that's done.
+This is, where the [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern) comes into play. The idea is roughly as follows: You went shopping and have all the ingredients for a nice stew, but you don't want to cook yourself. So you hand everything to a chef. The chef will tell you that some of your ingredients don't match and will discard them. Afterwards, he'll cook a nice stew for you and you never need to worry about how exactly that's done.
Let's get a bit more technical. First, we need the cook:
From da43ff0f0500d583a655cb59d696f7fd85dce120 Mon Sep 17 00:00:00 2001
From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Date: Tue, 19 Apr 2022 21:03:39 +0200
Subject: [PATCH 08/14] Updated Builder Pattern (markdown)
---
Builder-Pattern.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Builder-Pattern.md b/Builder-Pattern.md
index 632f728..d4a5f1e 100644
--- a/Builder-Pattern.md
+++ b/Builder-Pattern.md
@@ -13,13 +13,14 @@ All of those components have different parameters. Some of them are optional. So
That's a lot to take in and when coding your bot and setting this all up by yourself would be tiresome.
That's why `python-telegram-bot` makes an effort to make the setup easy with reasonable defaults.
+For example, after running
```python
from telegram.ext import Application
application = Application.builder().token('TOKEN').build()
```
-E.g. with `python-telegram-bot` you will automatically have
+you will automatically have
* the `Updater` available as `application.dispatcher`
* the `Bot` available as `application.(updater.)bot`
From 873c469eea1c1ac628cc06011f7cf41b1063d3ef Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 00:37:37 +0530
Subject: [PATCH 09/14] adapt to asyncio & v14 changes
---
Types-of-Handlers.md | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/Types-of-Handlers.md b/Types-of-Handlers.md
index 5aae315..bf4b560 100644
--- a/Types-of-Handlers.md
+++ b/Types-of-Handlers.md
@@ -2,12 +2,12 @@ A `Handler` is an instance derived from the base class [`telegram.ext.Handler`](
For example, if you want your bot to respond to the command `/start`, you can use a [`telegram.ext.CommandHandler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.commandhandler.html) that maps this user input to a callback named `start_callback`:
```python
-def start_callback(update, context):
- update.message.reply_text("Welcome to my awesome bot!")
+async def start_callback(update, context):
+ await update.message.reply_text("Welcome to my awesome bot!")
...
-dispatcher.add_handler(CommandHandler("start", start_callback))
+application.add_handler(CommandHandler("start", start_callback))
```
## Different types of `Update`s
@@ -19,7 +19,7 @@ For different kinds of user input, the received `telegram.Update` will have diff
3) multiple handlers for all the other different types of updates, e.g. [`telegram.ext.CallbackQueryhandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.callbackqueryhandler.html) for `update.callback_query` and [`telegram.ext.InlineQueryHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.inlinequeryhandler.html) for `update.inline_query`
4) A few more handlers for more advanced use cases
-The special thing about `MessageHandler` is that there is such a vast variety of message types (text, gif, image, document, sticker, …) that it's infeasible to provide a different `Handler` for each type. Instead `MessageHandler` is coupled with so called [filters](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html) that allow to make fine-grained distinctions: `MessageHandler(Filters.all, callback)` will handle all updates that contain
+The special thing about `MessageHandler` is that there is such a vast variety of message types (text, gif, image, document, sticker, …) that it's infeasible to provide a different `Handler` for each type. Instead `MessageHandler` is coupled with so called [filters](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html) that allow to make fine-grained distinctions: `MessageHandler(filters.ALL, callback)` will handle all updates that contain
* `update.message`
* `update.edited_message`
@@ -35,16 +35,16 @@ Because bot commands are another special part of the user interface of bots, the
It is also possible to work with parameters for commands offered by your bot. Let's extend the `start_callback` with some arguments so that the user can provide additional information in the same step:
```python
-def start_callback(update, context):
+async def start_callback(update, context):
user_says = " ".join(context.args)
- update.message.reply_text("You said: " + user_says)
+ await update.message.reply_text("You said: " + user_says)
...
-dispatcher.add_handler(CommandHandler("start", start_callback))
+application.add_handler(CommandHandler("start", start_callback))
```
-Sending "/start Hello World!" to your bot will now split everything after /start separated by the space character into a list of words and pass it on to the `args` parameter of `context`: `["Hello", "World!"]`. We join these chunks together by calling `" ".join(context.args)` and echo the resulting string back to the user.
+Sending `/start Hello World!` to your bot will now split everything after `/start` separated by the space character into a list of words and pass it on to the [`args` attribute of `context`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html#telegram.ext.CallbackContext.args): `["Hello", "World!"]`. We join these chunks together by calling `" ".join(context.args)` and echo the resulting string back to the user.
### Deep-Linking start parameters
The argument passing described above works exactly the same when the user clicks on a deeply linked start URL, like this one:
@@ -55,13 +55,13 @@ Clicking this link will open your Telegram Client and show a big START button. W
Note that since telegram doesn't support spaces in deep linking parameters, you will have to manually split the single `Hello_World` argument, into `["Hello", "World!"]` (using `context.args[0].split('_')` for example)
-You also have to pay attention to the maximum length accepted by Telegram itself. As stated in the [documentation](https://core.telegram.org/bots#deep-linking) the maximum length for the start parameter is 64.
+You also have to pay attention to the maximum length accepted by Telegram itself. As stated in the [documentation](https://core.telegram.org/bots#deep-linking) the maximum length for the start parameter is `64`.
Also, since this is an URL parameter, you have to pay attention on how to correctly pass the values in order to avoid passing URL reserved characters. Consider the usage of `base64.urlsafe_b64encode`.
-## Pattern matching: `Filters.regex`
+## Pattern matching: `filters.Regex`
-For more complex inputs you can employ the [`telegram.ext.MessageHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.messagehandler.html) with [`telegram.ext.Filters.regex`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html#telegram.ext.filters.Filters.regex), which internally uses the `re`-module to match textual user input with a supplied pattern.
+For more complex inputs you can employ the [`telegram.ext.MessageHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.messagehandler.html) with [`telegram.ext.filters.Regex`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html#telegram.ext.filters.Regex), which internally uses the `re`-module to match textual user input with a supplied pattern.
Keep in mind that for extracting URLs, #Hashtags, @Mentions, and other Telegram entities, there's no need to parse them with a regex filter because the Bot API already sends them to us with every update. Refer to [[this snippet|Code-snippets#message-entities]] to learn how to work with entities instead.
@@ -74,3 +74,5 @@ In some cases, it's useful to handle updates that are not from Telegram. E.g. yo
* [`TypeHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.typehandler.html)
* [`StringCommandHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.stringcommandhandler.html)
* [`StringRegexHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.stringregexhandler.html)
+
+See also this [FAQ entry](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Frequently-Asked-Questions#i-want-to-handle-updates-from-an-external-service-in-addition-to-the-telegram-updates-how-do-i-do-that)
From 1192f86b6febe8695cf515534da054c508ae7200 Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 00:44:55 +0530
Subject: [PATCH 10/14] tiny fixes
---
Extensions-–-Advanced-Filters.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/Extensions-–-Advanced-Filters.md b/Extensions-–-Advanced-Filters.md
index b808355..c4d1e6b 100644
--- a/Extensions-–-Advanced-Filters.md
+++ b/Extensions-–-Advanced-Filters.md
@@ -8,7 +8,7 @@ When using `MessageHandler` it is sometimes useful to have more than one filter.
from telegram.ext import MessageHandler, filters
handler = MessageHandler(
- filters.VIDEO | filters.PHOTO | filters.DOCUMET.ALL,
+ filters.VIDEO | filters.PHOTO | filters.Document.ALL,
callback
)
```
@@ -24,8 +24,8 @@ from telegram import MessageEntity
handler = MessageHandler(
filters.TEXT & (
- filters.entity(MessageEntity.URL) |
- filters.entity(MessageEntity.TEXT_LINK)
+ filters.Entity(MessageEntity.URL) |
+ filters.Entity(MessageEntity.TEXT_LINK)
),
callback
)
@@ -65,15 +65,15 @@ The class can of course be named however you want, the only important things are
The filter can then be used as:
```python
awesome_handler = MessageHandler(filter_awesome, callback)
-dispatcher.add_handler(awesome_handler)
+application.add_handler(awesome_handler)
```
## `Filters` and `CallbackContext`
-You may have noticed that when using `filters.regex`, the attributes `context.matches` and `context.match` are set to the corresponding matches. To achieve something like this for your custom filter, you can do the following:
+You may have noticed that when using [`filters.Regex`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.filters.html#telegram.ext.filters.Regex), the attributes [`context.matches`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.callbackcontext.html#telegram.ext.CallbackContext.matches) and [`context.match`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.callbackcontext.html#telegram.ext.CallbackContext.match) are set to the corresponding matches. To achieve something like this for your custom filter, you can do the following:
1. Set `self.data_filter=True` for your filter.
-2. If the update should be handled return a dictionary of the form `{attribute_name: [values]}`. This dict will be merged with the internal dict of the `context` argument making `value` available as `context.attribute_name`. This currently works with `MessageHandler`, `CommandHandler` and `PrefixHandler`, which are the only handlers that accept filters.
+2. If the update should be handled, return a dictionary of the form `{attribute_name: [values]}`. This dict will be merged with the internal dict of the `context` argument making `value` available as `context.attribute_name`. This currently works with `MessageHandler`, `CommandHandler` and `PrefixHandler`, which are the only handlers that accept filters.
**Note:** The values of the returned dict must be *lists*. This is necessary to make sure that multiple data filters can be merged meaningfully.
From 3a626ad0b3da94927cc269705b03b974ff8e64cc Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 01:02:23 +0530
Subject: [PATCH 11/14] spelling, simplify usage of migrate_chat_data
---
Storing-bot,-user-and-chat-related-data.md | 25 +++++++---------------
1 file changed, 8 insertions(+), 17 deletions(-)
diff --git a/Storing-bot,-user-and-chat-related-data.md b/Storing-bot,-user-and-chat-related-data.md
index a57efb0..503795d 100644
--- a/Storing-bot,-user-and-chat-related-data.md
+++ b/Storing-bot,-user-and-chat-related-data.md
@@ -18,7 +18,7 @@ async def put(update, context):
# Store value
context.user_data[key] = value
# Send the key to the user
- update.message.reply_text(key)
+ await update.message.reply_text(key)
async def get(update, context):
"""Usage: /get uuid"""
@@ -27,7 +27,7 @@ async def get(update, context):
# Load value and send it to the user
value = context.user_data.get(key, 'Not found')
- update.message.reply_text(value)
+ await update.message.reply_text(value)
if __name__ == '__main__':
application = Application.builder().token('TOKEN').build()
@@ -61,18 +61,7 @@ When a group migrates, Telegram will send an update that just states the new inf
async def chat_migration(update, context):
message = update.message
application = context.application
-
- # Get old and new chat ids
- old_id = message.migrate_from_chat_id or message.chat_id
- new_id = message.migrate_to_chat_id or message.chat_id
-
- # transfer data, only if old data is still present
- if old_id in application.chat_data:
- application.migrate_chat_data(
- old_chat_id=old_id,
- new_chat_id=new_id
- )
-
+ application.migrate_chat_data(message=message)
...
def main():
@@ -83,11 +72,13 @@ def main():
...
```
+See also: [`migrate_chat_data`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.application.html#telegram.ext.Application.migrate_chat_data)
+
To be entirely sure that the update will be processed by this handler, either add it first or put it in its own group.
### ChatMigrated Errors
-If you try e.g. sending a message to the old chat id, Telegram will respond by a Bad Request including the new chat id. You can access it using an error handler:
+If you try e.g. sending a message to the old chat id, Telegram will respond by a `BadRequest` including the new chat id. You can access it using an error handler:
```python
async def error(update, context):
@@ -101,7 +92,7 @@ Unfortunately, Telegram does *not* pass along the old chat id, so there is curre
```python
async def my_callback(update, context):
- applcation = context.applcation
+ application = context.application
...
try:
@@ -114,7 +105,7 @@ async def my_callback(update, context):
# Transfer data
if chat_id in application.chat_data:
- applcation.migrate_chat_data(
+ application.migrate_chat_data(
old_chat_id=chat_id,
new_chat_id=new_id
)
From b8f5e5ef918e16aa03e285c7e52c6c049692ebee Mon Sep 17 00:00:00 2001
From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
Date: Tue, 19 Apr 2022 21:45:43 +0200
Subject: [PATCH 12/14] Updated Storing bot, user and chat related data
(markdown)
---
Storing-bot,-user-and-chat-related-data.md | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/Storing-bot,-user-and-chat-related-data.md b/Storing-bot,-user-and-chat-related-data.md
index 503795d..52d623a 100644
--- a/Storing-bot,-user-and-chat-related-data.md
+++ b/Storing-bot,-user-and-chat-related-data.md
@@ -103,11 +103,17 @@ async def my_callback(update, context):
# Resend to new chat id
await context.bot.send_message(new_id, text)
- # Transfer data
- if chat_id in application.chat_data:
- application.migrate_chat_data(
- old_chat_id=chat_id,
- new_chat_id=new_id
- )
+ # Get old and new chat ids
+ old_id = message.migrate_from_chat_id or message.chat_id
+ new_id = message.migrate_to_chat_id or message.chat_id
+
+ # transfer data, only if old data is still present
+ # this step is important, as Telegram sends *two* updates
+ # about the migration
+ if old_id in application.chat_data:
+ application.migrate_chat_data(
+ old_chat_id=old_id,
+ new_chat_id=new_id
+ )
...
```
From cc62415386c39fee9cc89f994894e88ed395702e Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 01:29:40 +0530
Subject: [PATCH 13/14] tiny fixes
---
Making-your-bot-persistent.md | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/Making-your-bot-persistent.md b/Making-your-bot-persistent.md
index b79d8c6..bb2fcbe 100644
--- a/Making-your-bot-persistent.md
+++ b/Making-your-bot-persistent.md
@@ -22,7 +22,7 @@ See e.g. [`ptbcontrib/ptb_sqlalchemy_jobstore`](https://github.com/python-telegr
## Included persistence classes
Three classes concerning persistence in bots have been added.
* [BasePersistence](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.basepersistence.html) - Is an interface class for persistence classes.
-If you create your own persistence classes to maintain a database-connection for example, you must inherit from `BasePersistence` and ipmlement all abstract methods
+If you create your own persistence classes to maintain a database-connection for example, you must inherit from `BasePersistence` and implement all abstract methods
* [PicklePersistence](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.picklepersistence.html) - Uses pickle files to make the bot persistent.
* [DictPersistence](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dictpersistence.html) - Uses in memory dicts and easy conversion to and from JSON to make the bot persistent.
Note that this class is mainly intended as starting point for custom persistence classes that need to JSON-serialize the stored data before writing them to file/database and does *not* actually write any data to file/database.
@@ -50,13 +50,13 @@ For example `ConversationHandler(..., persistent=True, name='my_name')`. `persis
Adding these arguments and adding the conversation handler to a persistence-aware `Application` will make it persistent.
When starting the `Application` with `Application.start()` or `Application.run_{polling, webhook}`, it will automatically update the persistence in regular intervals.
-You can customize the interval via the corresponding argument of `Base/Pickle/Dict/…Persistence`.
+You can customize the interval via the [`update_interval`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.basepersistence.html#telegram.ext.BasePersistence.params.update_interval) argument of `Base/Pickle/Dict/…Persistence`.
## Refreshing at runtime
If your persistence reads the data from an external database, the entries in this database could change at runtime.
This is the case in particular, if the entries in the database are created by a 3rd party service independently of your bot.
-If you want to make sure that the data in `context.user/chat/bot_data` are always up-to-date, your persistence class should implement the methods `refresh_bot/chat/user_data`.
+If you want to make sure that the data in `context.user/chat/bot_data` are always up-to-date, your persistence class should implement the methods [`refresh_bot/chat/user_data`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.basepersistence.html#telegram.ext.BasePersistence.refresh_chat_data).
Those will be called when in update comes in, before any of your callbacks are called.
These methods can also be useful to implement a lazy-loading strategy.
@@ -67,12 +67,13 @@ Instances of `telegram.Bot` should not be serialized, because changes to the bot
For example, you might change the [[default values|Adding-defaults-to-your-bot]] used by the bot.
Or if you change the bots token, this may lead to e.g. `Chat not found` errors.
-This is relevant e.g., if you store Telegram objects like `Message` in `bot/user/chat_data`, as some of them hold a reference to `Apllication.bot` (which is how the shortcuts like `Message.reply_text` work).
+This is relevant e.g., if you store Telegram objects like `Message` in `bot/user/chat_data`, as some of them hold a reference to `Application.bot` (which is how the shortcuts like `Message.reply_text` work).
-The interface class `BaseRequest` does not question what kind of data you supply to its methods.
+The interface class `BasePersistence` does not question what kind of data you supply to its methods.
Hence, each implementation should take care that it does not try to serialize `telegram.Bot` instances.
For example, it can check if the data equals the attribute `BasePersistence.bot` (which will be the bot object used by the `Application`) and instead store a placeholder.
When loading the data, the `BasePersistence.bot` can be reinserted instead of the placeholder.
Indeed, this is basically what the built-in `PicklePersistence` does.
-For more technical details, please refer to the documentation of `{Base, Pickle}Persistence`.
\ No newline at end of file
+For more technical details, please refer to the documentation of [`BasePersistence`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.basepersistence.html#telegram-ext-basepersistence),
+[`PicklePersistence`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.picklepersistence.html#telegram-ext-picklepersistence)
\ No newline at end of file
From 2c0c374a4aeb3ae7428530c6092dd32ec6cb316b Mon Sep 17 00:00:00 2001
From: Harshil <37377066+harshil21@users.noreply.github.com>
Date: Wed, 20 Apr 2022 01:38:52 +0530
Subject: [PATCH 14/14] change to log level INFO, use f strings
---
Adding-defaults-to-your-bot.md | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/Adding-defaults-to-your-bot.md b/Adding-defaults-to-your-bot.md
index bbffbaf..bff5c27 100644
--- a/Adding-defaults-to-your-bot.md
+++ b/Adding-defaults-to-your-bot.md
@@ -24,28 +24,27 @@ from telegram.constants import ParseMode
from telegram.ext import MessageHandler, filters, Defaults, Application
logging.basicConfig(
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.DEBUG
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
async def job(context):
chat_id = context.job.chat_id
- local_now = dtm.datetime.now(context.bot.defaults.tzinfo)
+ timezone = context.bot.defaults.tzinfo
+ local_now = dtm.datetime.now(timezone)
utc_now = dtm.datetime.utcnow()
- text = 'Running job at {} in timezone {}, which equals {} UTC.'.format(
- local_now, context.bot.defaults.tzinfo, utc_now
- )
+ text = f'Running job at {local_now} in timezone {timezone}, which equals {utc_now} UTC.'
await context.bot.send_message(chat_id=chat_id, text=text)
async def echo(update, context):
+ text = update.message.text
# Send with default parse mode
- await update.message.reply_text('{}'.format(update.message.text))
+ await update.message.reply_text(f'{text}')
# Override default parse mode locally
- await update.message.reply_text(
- '*{}*'.format(update.message.text), parse_mode=ParseMode.MARKDOWN
- )
- await update.message.reply_text('*{}*'.format(update.message.text), parse_mode=None)
+ await update.message.reply_text(f'*{text}*', parse_mode=ParseMode.MARKDOWN)
+ # Send with no parse mode
+ await update.message.reply_text(f'*{text}*', parse_mode=None)
# Schedule job
context.job_queue.run_once(