From db41670ac3ac105bec22c573eba8d5bf54edb0bc Mon Sep 17 00:00:00 2001 From: Hinrich Mahler Date: Wed, 6 Jan 2021 17:19:09 +0100 Subject: [PATCH] Overhaul some pages and restructure a bit. --- Avoiding-flood-limits.md | 17 +---- Exception-Handling.md | 66 ++---------------- Extensions-–-Advanced-Filters.md | 24 +++++-- Extensions-–-JobQueue.md | 16 +++-- Extensions-–-Your-first-Bot.md | 26 +++---- Home.md | 41 +++++++++-- Introduction-to-the-API.md | 9 +-- Making-your-bot-persistent.md | 12 ++-- Storing-bot,-user-and-chat-related-data.md | 81 +++------------------- Types-of-Handlers.md | 39 ++++++++--- Writing-Tests.md | 8 +-- _Sidebar.md | 46 ++++++------ 12 files changed, 156 insertions(+), 229 deletions(-) diff --git a/Avoiding-flood-limits.md b/Avoiding-flood-limits.md index ca05048..be71e3f 100644 --- a/Avoiding-flood-limits.md +++ b/Avoiding-flood-limits.md @@ -25,9 +25,7 @@ If you need more details on MQ implementation, [follow its docs](http://python-t ## MessageQueue from user perspective ### Current status -For now, it's still under development, but **could be already used**. More detailed, now it's detached from other Python-Telegram-Bot lib components and therefore you should do a little extra work to use it. We plan to tightly couple it with [`telegram.Bot`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html) so that it could be used more conveniently (and even implicitly unless other specified). But anyway, **the future releases would be backwards compatible with current [`MessageQueue`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagequeue.html) class and `queuedmessage` decorator**. - -So, feel free to use it and stay tuned for new features. We will NOT join the dark side and break the API. +For now, it's still under development and has some bugs (see [#2139](https://github.com/python-telegram-bot/python-telegram-bot/issues/2139) for details), but **could be already used**. More detailed, now it's detached from other Python-Telegram-Bot lib components and therefore you should do a little extra work to use it. We plan to tightly couple it with [`telegram.Bot`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html) so that it could be used more conveniently (and even implicitly unless other specified). But anyway, **the future releases would be backwards compatible with current [`MessageQueue`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagequeue.html) class and `queuedmessage` decorator**. ### Using MQ with @queuedmessage decorator [`MessageQueue`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagequeue.html) module includes a convenient `@queuedmessage` decorator, which allows to delegate the required send method calls to MQ. However, it requires you to do a little work by hand, mainly create a [`telegram.Bot`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.bot.html) subclass and decorate those methods. @@ -100,8 +98,6 @@ Which produces the following results (notice the delays happening, but be aware ![test_result](https://user-images.githubusercontent.com/16870636/28393529-e753ea26-6cef-11e7-981f-35b98fcddd61.png) | ---| -> **Note:** such way of MQ usage may become overkill in future as more convenient interface would be implemented, but as being said it would remain backwards-compatible. - > **Recommendations:**
As stated in [`@queuedmessage` docs](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagequeue.html#telegram.ext.messagequeue.queuedmessage), for now the user needs to provide the `isgroup` boolean argument to wrapped methods or rely on `False` default. If you need to use MQ with group-type messages, you could determine the message type by checking `chat_id` (for group-type messages it would be < 0 ). However, this is not officially documented in [Telegram's Bot docs](https://core.telegram.org/bots/) and therefore prone to change in future. Use it on your own risk. The more reliable way is to make a request to API to determine chat type before sending message and cache the result. We're working on implementing this approach, so stay tuned. @@ -125,13 +121,4 @@ As stated in [`@queuedmessage` docs](https://python-telegram-bot.readthedocs.io/ ### If you have any related questions Feel free to ask on our [Telegram Group](https://t.me/pythontelegrambotgroup). -Or you may directly ask the MQ responsive dev:
[thodnev @ Telegram](https://telegram.me/thodnev) (support in English or Russian; please, only MQ-related questions) - - - -## What to read next? -_Check out our_ [Extensions -- JobQueue](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue). _It's amazing!_ - -_Probably, you would be also interested in_ [Performance Optimizations](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Performance-Optimizations) _for your bot._ - -_You've coded something worth sharing or just want to participate in dev process? Great! We would really appreciate that. Check out our_ [Contribution Guide](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/.github/CONTRIBUTING.rst) _for more details._ \ No newline at end of file +Or you may directly ask the MQ responsive dev:
[thodnev @ Telegram](https://telegram.me/thodnev) (support in English or Russian; please, only MQ-related questions) \ No newline at end of file diff --git a/Exception-Handling.md b/Exception-Handling.md index 82e0a25..fe0a7ca 100644 --- a/Exception-Handling.md +++ b/Exception-Handling.md @@ -1,69 +1,11 @@ In `python-telegram-bot`, all Telegram-related errors are encapsulated in the `TelegramError` exception class and its subclasses, located in [`telegram.error`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.error.html) module. -Any error, including `TelegramError`, that is raised in one of your handlers (or while calling `get_updates` in the `Updater`), is forwarded to all registered error handlers, so you can react to them. You can register an error handler by calling `Dispatcher.add_error_handler(callback)`, where `callback` is a function that takes the `update` and `context`. `update` will be the update that caused the error and `context.error` the error that was raised. +Any error, including `TelegramError`, that is raised in one of your handlers (or while calling `get_updates` in the `Updater`), is forwarded to all registered error handlers, so you can react to them. You can register an error handler by calling `Dispatcher.add_error_handler(callback)`, where `callback` is a function that takes the `update` and `context`. `update` will be the update that caused the error (or `None` if the error wasn't caused by an update, e.g. for [Jobs](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-–-JobQueue)) and `context.error` the error that was raised. -Example: You're trying to send a message, but the user blocked the bot. An `Unauthorized` exception, a subclass of `TelegramError`, will be raised and delivered to your error handler, so you can delete it from your conversation list, if you keep one. +**Example:** You're trying to send a message, but the user blocked the bot. An `Unauthorized` exception, a subclass of `TelegramError`, will be raised and delivered to your error handler, so you can delete it from your conversation list, if you keep one. **Note:** The error handler might be only your last resort - of course you can also handle Exceptions as they occur. Only uncaught exceptions are forwarded to the error handler. -Here is an example code that uses all current subclasses of `TelegramError`: +## Example -```python -from telegram.error import (TelegramError, Unauthorized, BadRequest, - TimedOut, ChatMigrated, NetworkError) - -def error_callback(update, context): - try: - raise context.error - except Unauthorized: - # remove update.message.chat_id from conversation list - except BadRequest: - # handle malformed requests - read more below! - except TimedOut: - # handle slow connection problems - except NetworkError: - # handle other connection problems - except ChatMigrated as e: - # the chat_id of a group has changed, use e.new_chat_id instead - except TelegramError: - # handle all other telegram related errors - -dispatcher.add_error_handler(error_callback) -``` - -Here are some examples that would cause a `BadRequest` error to be raised: -```python ->>> bot.leave_chat(chat_id=) -[...] -telegram.error.BadRequest: Chat not found - ->>> bot.answer_callback_query() -[...] -telegram.error.BadRequest: Query_id_invalid - ->>> bot.get_file() -[...] -telegram.error.BadRequest: Invalid file id - ->>> bot.edit_message_text(chat_id, "sample old message") -[...] -telegram.error.BadRequest: Message is not modified - ->>> bot.send_message(chat_id, 'a'*40960) -[...] -telegram.error.BadRequest: Message is too long -``` - -For the last one you can check if your message is too long by comparing with `telegram.constants.MAX_MESSAGE_LENGTH`. There is something similar for captions: `telegram.constants.MAX_CAPTION_LENGTH`. - -### Other exceptions - -If a handler raises an uncaught exception that is no `TelegramError` (e.g. an `IndexError`), the exception will be caught and logged by the `Dispatcher`, so that the bot does not crash but you still have an indication of it and can address the issue. To take advantage of this, it is helpful to set up the `logging` module. - -Example code to set up the `logging` module: - -```python -logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - level=logging.INFO) -``` \ No newline at end of file +For an example on how an error handler might look like, please head over to the [examples directory](https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples). \ No newline at end of file diff --git a/Extensions-–-Advanced-Filters.md b/Extensions-–-Advanced-Filters.md index a4da167..2fdf783 100644 --- a/Extensions-–-Advanced-Filters.md +++ b/Extensions-–-Advanced-Filters.md @@ -1,8 +1,8 @@ This page describes advanced use cases for the filters used with `MessageHandler` from `telegram.ext`. -## Combining filters -When using `MessageHandler` it is sometimes useful to have more than one filter. This can be done using so called bit-wise operators. In python those operators are `&`, `|` and `~` meaning AND, OR and NOT respectively. -### Examples +# Combining filters +When using `MessageHandler` it is sometimes useful to have more than one filter. This can be done using so called bit-wise operators. In Python those operators are `&`, `|` and `~` meaning AND, OR and NOT respectively. Since version 13.1 filters support `^` for XOR. +## Examples #### Message is either video, photo, or document (generic file) ``` python from telegram.ext import MessageHandler, Filters @@ -31,7 +31,7 @@ handler = MessageHandler( handler = MessageHandler(Filters.photo & (~ Filters.forwarded), callback) ``` -## Custom filters +# Custom filters It is also possible to write our own filters. In essence, a filter is simply a function that receives either a `Message` instance or a `Update` instance and returns either `True` or `False`. This function has to be implemented in a new class that inherits from either `MessageFilter` or `UpdateFilter`, which allows it to be combined with other filters. If the combination of all filters evaluates to `True`, the message will be handled. The difference between `UpdateFilter` and `MessageFilter` is that the `filter` function of the former will receive the `update`, allowing e.g. to differentiate between channel post updates and message updates, while the `filter` function of the latter will receive the `update.effective_message`. @@ -57,4 +57,18 @@ 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) -``` \ No newline at end of file +``` + +## `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: + +1. Set `self.data_filter=True` for your filter. +2. If the update should be handled return a dictionary of the form `{attributen_name: value}`. 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. + +If you want this to work with your custom handler, make sure that `YourHandler.collect_additional_context` does something like + +```python +if isinstance(check_result, dict): + context.update(check_result) +``` diff --git a/Extensions-–-JobQueue.md b/Extensions-–-JobQueue.md index c4321ae..ad3bb50 100644 --- a/Extensions-–-JobQueue.md +++ b/Extensions-–-JobQueue.md @@ -1,14 +1,18 @@ -## Introduction +# Introduction The extension class `telegram.ext.JobQueue` allows you to perform tasks with a delay or even periodically, at a set interval. Among many other things, you can use it to send regular updates to your subscribers. -### When working with `JobQueue`, please keep in mind: +## When working with `JobQueue`, please keep in mind: * PTBs `JobQueue` provides an easy to use and ready to use way of scheduling tasks in a way that ties in with the PTB architecture * Managing scheduling logic is not the main intend of PTB and hence as of v13 a third party library is used * If you need highly customized scheduling thingies, you *can* use advanced features of the third party library * We can't guarantee that the backend will stay the same forever. For example, if the third party library is discontinued, we will have to look for alternatives. -## Usage +## Example + +In addition to the tuorial below there is also the `timerbot.py` example at the [examples directory](https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples). + +# Usage The `JobQueue` class is tightly integrated with other `telegram.ext` classes. Similar to `Updater` and `Dispatcher`, it runs asynchronously in a separate thread. To use the `JobQueue`, you don't have to do much. When you instantiate the `Updater`, it will create a `JobQueue` for you: @@ -24,9 +28,9 @@ This job queue is also linked to the dispatcher, which is discussed later in thi Tasks in the job queue are encapsulated by the `Job` class. It takes a callback function as a parameter, which will be executed when the time comes. This callback function always takes one parameter: `context`, a `telegram.ext.CallbackContext`. Like in the case of handler callbacks used by the `Dispatcher`, through this object you can access `context.bot`, the `Updater`'s `telegram.Bot` instance; and for this particular case you can also access `context.job`, which is the `Job` instance of the task that triggered the callback (more on that later). -You can use the following 3 methods to create jobs with different frequency and time: `job_queue.run_once`, `job_queue.run_repeating`, `job_queue.run_daily` and `job_queue.run_monthly`. (As before, you do not usually need to instantiate the `Job` class directly.) +You can use the following methods to create jobs with different frequency and time: `job_queue.run_once`, `job_queue.run_repeating`, `job_queue.run_daily` and `job_queue.run_monthly`. (As before, you do not usually need to instantiate the `Job` class directly.) -### Tutorial +## Tutorial Add your first job to the queue by defining a callback function and adding it to the job queue. For this tutorial, you can replace `'@examplechannel'` with a channel where your bot is an admin, or by your user id (use [@userinfobot](https://telegram.me/userinfobot) to find out your user id): @@ -38,8 +42,6 @@ def callback_minute(context: telegram.ext.CallbackContext): job_minute = j.run_repeating(callback_minute, interval=60, first=10) ``` -*(Ignore the type annotations if you're on Python 2)* - The `callback_minute` function will be executed every `60.0` seconds, the first time being after 10 seconds (because of `first=10`). The `interval` and `first` parameters are in seconds if they are `int` or `float`. They can also be `datetime` objects. See the [docs](http://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.jobqueue.html) for detailed explanation. The return value of these functions are the `Job` objects being created. You don't need to store the result of `run_repeating` (which is the newly instantiated `Job`) if you don't need it; we will make use of it later in this tutorial. diff --git a/Extensions-–-Your-first-Bot.md b/Extensions-–-Your-first-Bot.md index 41c855a..0b4d43a 100644 --- a/Extensions-–-Your-first-Bot.md +++ b/Extensions-–-Your-first-Bot.md @@ -1,13 +1,13 @@ ## 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 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). 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. -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](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Types-of-Handlers), but if you need something very specific, you can also subclass `Handler` yourself. +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](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Types-of-Handlers), but if you need something very specific, you can also subclass `Handler` yourself. To begin, you'll need an Access Token. If you have already read and followed [Introduction to the API](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API), you can use the one you generated then. If not: To generate an Access Token, you have to talk to [@BotFather](https://telegram.me/botfather) and follow a few simple steps (described [here](https://core.telegram.org/bots#6-botfather)). You should really read the introduction first, though. @@ -21,7 +21,7 @@ First, you have to create an `Updater` object. Replace `'TOKEN'` with your Bot's 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) +**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. @@ -47,7 +47,7 @@ Now, you can define a function that should process a specific type of update: def start(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!") ``` -**Related docs:** [sendMessage](https://core.telegram.org/bots/api#sendmessage), [CallbackContext (the type of the context argument)](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.callbackcontext.html) +**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) 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: @@ -56,14 +56,14 @@ from telegram.ext import CommandHandler start_handler = CommandHandler('start', start) dispatcher.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.Dispatcher.add_handler`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.dispatcher.html#telegram.ext.dispatcher.Dispatcher.add_handler) And that's all you need. To start the bot, run: ```python updater.start_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.Updater.start_polling`](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.updater.html#telegram.ext.updater.Updater.start_polling) Give it a try! Start a chat with your bot and issue the `/start` command - if all went right, it will reply. @@ -77,13 +77,13 @@ from telegram.ext import MessageHandler, Filters echo_handler = MessageHandler(Filters.text & (~Filters.command), echo) dispatcher.add_handler(echo_handler) ``` -**Related docs:** [telegram.ext.MessageHandler](http://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.messagehandler.html) +**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:** The `Filters` class contains a number of functions 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](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Advanced-Filters). +**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](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-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: @@ -96,7 +96,7 @@ caps_handler = CommandHandler('caps', caps) dispatcher.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`. 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. 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). @@ -136,7 +136,7 @@ unknown_handler = MessageHandler(Filters.command, unknown) dispatcher.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. +**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. If you're done playing around, stop the bot with: @@ -147,8 +147,8 @@ 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. #### 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). + Learn about the library exceptions and best practices in [Exception Handling](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Exception-Handling). -You want *more features*? Check out [Extensions – JobQueue](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue)! - -Or: Get inspired by our example Bots in the [examples folder](https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples). \ No newline at end of file +You want *more features*? Check out [Extensions – JobQueue](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue)! \ No newline at end of file diff --git a/Home.md b/Home.md index 758ba55..f85d8d7 100644 --- a/Home.md +++ b/Home.md @@ -1,10 +1,41 @@ [![Logo](https://github.com/python-telegram-bot/logos/raw/master/logo-text/png/ptb-logo-text_768.png)](https://python-telegram-bot.org/) -### About this Wiki +## About this Wiki For a long time, our documentation consisted of our [API documentation](http://python-telegram-bot.readthedocs.io/) and README. As a result, the README became simply too big to be useful. In an effort to change that, we moved large parts of our README into different Wiki articles. In the process, we improved the texts with additional information, clearer wording and some cross-references. -### How to read this Wiki -Ideally, this Wiki should read a bit like a [Choose Your Own Adventure](https://en.wikipedia.org/wiki/Choose_Your_Own_Adventure) book - You start with [page one](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Introduction-to-the-API) and choose your way through the docs, depending on your preferences. At the end of most articles, you will find a section called **What to read next?**, where related articles are linked. -### Contributing to this Wiki -If you stumble upon a part of the wiki that is unclear or missing important points, please go ahead and drop us a message in our [Telegram Group](https://t.me/pythontelegrambotgroup), or just edit the part yourself (anyone can contribute). We'd like our wiki to be as complete as possible 🙂 \ No newline at end of file +## Contributing to this Wiki +If you stumble upon a part of the wiki that is unclear or missing important points, please go ahead and drop us a message in our [Telegram Group](https://t.me/pythontelegrambotgroup), or just edit the part yourself (anyone can contribute). We'd like our wiki to be as complete as possible 🙂 + +## Structure of this Wiki + +In the sidebar to the right you find all important pages of this wiki. They are roughly organized by the following logic: + +### Must read + +Introductory articles and frequently referenced pages. + +### PTB Features + +Introductions & explanations of the different components the powerful `telegram.ext` offers. + +### Code Resources + +Some other resources that are not exclusively PTB-specific. + +### Examples Explained + +An effort to make the [examples](https://github.com/python-telegram-bot/python-telegram-bot/tree/master/examples) even more accessible. +*You are very welcome to extend this section!* + +### Networking + +Articles about advanced networking questions. + +### Transition Guides + +Major versions usually come with breaking changes, i.e. changes that lead to code failing after upgrading. Those guides help to ease the transition. + +### Administration + +Interesting mostly for the maintainers. They, too, need a place to take notes 🙂 \ No newline at end of file diff --git a/Introduction-to-the-API.md b/Introduction-to-the-API.md index f024b8c..d424e9e 100644 --- a/Introduction-to-the-API.md +++ b/Introduction-to-the-API.md @@ -1,4 +1,6 @@ -The API is exposed via the [telegram.Bot](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/bot.py) class. The methods are the snake_case equivalents of the methods described in the official [Telegram Bot API](https://core.telegram.org/bots/api). The exact camelCase method names as in the Telegram docs are also available for your convenience. So for example `telegram.Bot.send_message` is the same as `telegram.Bot.sendMessage`. +## Pure Telegram Bot API + +The Bot API is exposed via the [`telegram.Bot`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html) class. The methods are the snake_case equivalents of the methods described in the official [Telegram Bot API](https://core.telegram.org/bots/api). The exact camelCase method names as in the Telegram docs are also available for your convenience. So for example `telegram.Bot.send_message` is the same as `telegram.Bot.sendMessage`. All the classes of the Bot API can also be found in the `telegram` module, e.g. the `Message` class is available as [`telegram.Message`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html). To generate an Access Token, you have to talk to [BotFather](https://t.me/botfather) and follow a few simple steps (described [here](https://core.telegram.org/bots#6-botfather)). @@ -24,7 +26,6 @@ To check if your credentials are correct, call the [getMe](https://core.telegram **Note:** Bots can't initiate conversations with users. A user must either add them to a group or send them a message first. People can use ``telegram.me/`` links or username search to find your bot. -#### What to read next? -To get real and start building your first bot using the `telegram.ext` classes, read [Extensions – Your first Bot](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-–-Your-first-Bot) +## Beyond the pure API -If you want to continue learning about the API, read [Code snippets](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Code-snippets). \ No newline at end of file +That's all very nice, but usually you want your bot to actually react to some user input. That is, you want to build a chat-bot. `python-telegram-bot` offers a powerful extension module called `telegram.ext` that takes a lot of work of your shoulders. You can find an introduction at the [Tutorial: Your first bot](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-–-Your-first-Bot). \ No newline at end of file diff --git a/Making-your-bot-persistent.md b/Making-your-bot-persistent.md index 0994b63..7e2e734 100644 --- a/Making-your-bot-persistent.md +++ b/Making-your-bot-persistent.md @@ -13,15 +13,17 @@ In V12.0b1 we added a persistence mechanism to `telegram.ext`. This wiki page is ## 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` -[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 +* [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` +* [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. ## 3rd party persistence classes +Instead of manually handling a database to store data, consider implementing a subclass of `BasePersistence`. This allows you to simply pass an instance of that subclass to the `Updater/Dispatcher` and let PTB handle the loading, updating & storing of the data! + If you want to create your own persistence class, please carefully read the docs on [BasePersistence](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.basepersistence.html). It will tell you what methods you need to overwrite. -If you've written a persistence class that could benefit others (e.g. a general one covering all types of data), please add it below. +If you've written a persistence class that could benefit others (e.g. a general one covering all types of data), it would be great if you linked it here or even better made it available in [ptbcontrib](https://github.com/python-telegram-bot/ptbcontrib). ## What do I need to change? To make your bot persistent you need to do the following. @@ -36,7 +38,7 @@ Adding these arguments and adding the conversation handler to a persistence-awar ## Storing Bots -As of v13, persistence will try to replace `telegram.Bot` instances by [`REPLACED_BOT`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.basepersistence.html#telegram.ext.BasePersistence.REPLACED_BOT) and +As of v13, persistence will automatically try to replace `telegram.Bot` instances by [`REPLACED_BOT`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.basepersistence.html#telegram.ext.BasePersistence.REPLACED_BOT) and insert the bot set with [`set_bot`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.basepersistence.html#telegram.ext.BasePersistence.set_bot) upon loading of the data. This is to ensure that changes to the bot apply to the saved objects, too. For example, you might change the [default values](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Adding-defaults-to-your-bot) used by the bot. If you change the bots token, this may lead to e.g. `Chat not found` errors. For the limitations on replacing bots see diff --git a/Storing-bot,-user-and-chat-related-data.md b/Storing-bot,-user-and-chat-related-data.md index 0c2fc1a..45b7963 100644 --- a/Storing-bot,-user-and-chat-related-data.md +++ b/Storing-bot,-user-and-chat-related-data.md @@ -1,67 +1,9 @@ Sometimes you need to temporarily store some information about the current user and/or chat for later use. An example of this would be a survey bot that asks the user a series of questions one after another and saves them to your database when all answers are collected. + # `bot_data`, `user_data` and `chat_data` -The `telegram.ext` framework provides a built-in solution for this common task. To understand how it works, let's take a look at a naïve solution using a global variable. In case you're in a hurry, you can also [**jump straight to the explanation**](#explanation). -## Bad Example -The following complete example bot provides a very simple key/value storage. When you use the `/put` command to store a value, it returns an ID which you can use with the `/get` command to retrieve the stored value. +The `telegram.ext` framework provides a built-in solution for this common task. Let's jump straight to an example: -It uses a global dictionary named `all_user_data` that maps a user ID to a `dict` that represents the user specific storage. - -```python -from uuid import uuid4 -from telegram.ext import Updater, CommandHandler - -all_user_data = dict() - -def put(update, context): - """Usage: /put value""" - # Generate ID and seperate value from command - key = str(uuid4()) - # We don't use context.args here, because the value may contain whitespaces - value = update.message.text.partition(' ')[2] - - user_id = update.message.from_user.id - - # Create user dict if it doesn't exist - if user_id not in all_user_data: - all_user_data[user_id] = dict() - - # Store value - user_data = all_user_data[user_id] - user_data[key] = value - - update.message.reply_text(key) - -def get(update, context): - """Usage: /get uuid""" - # Seperate ID from command - key = context.args[0] - - user_id = update.message.from_user.id - - # Load value - try: - user_data = all_user_data[user_id] - value = user_data[key] - update.message.reply_text(value) - - except KeyError: - update.message.reply_text('Not found') - -if __name__ == '__main__': - updater = Updater('TOKEN', use_context=True) - dp = updater.dispatcher - - dp.add_handler(CommandHandler('put', put)) - dp.add_handler(CommandHandler('get', get)) - - updater.start_polling() - updater.idle() -``` - -If you read the code carefully, you might have noticed that the code that gets the current `user_data` from `all_user_data` is [repeated](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) in both callbacks. - -## Good Example ```python from uuid import uuid4 from telegram.ext import Updater, CommandHandler @@ -75,7 +17,7 @@ def put(update, context): # Store value context.user_data[key] = value - + # Send the key to the user update.message.reply_text(key) def get(update, context): @@ -83,7 +25,7 @@ def get(update, context): # Seperate ID from command key = context.args[0] - # Load value + # Load value and send it to the user value = context.user_data.get(key, 'Not found') update.message.reply_text(value) @@ -98,23 +40,16 @@ if __name__ == '__main__': updater.idle() ``` -**Note the following differences:** -- The global variable `all_user_data` was removed -- The repeated code to get the storage of the current user was removed -- The code to ensure that the storage exists was removed -- Both the `put` and `get` functions use context.user_data - -### Explanation +## Explanation By using `context.user_data` in any `Handler` callback, you have access to a user-specific `dict`. *Every time the bot receives a message*, the handler for that message finds (or creates) the `user_data` of the user who sent the message. This dictionary is *shared across all handlers* of the bot. -#### What about `bot_data` and `chat_data`? +## What about `bot_data` and `chat_data`? `chat_data` works in the exact same way as `user_data`, except it is managed per *chat* instead of every *user*. Use `context.chat_data` to get access to this dict. As of version 12.4 `bot_data` is provided as well and works in the exact same way as `user_data`, except it's a single dictionary for your bot. Use `context.bot_data` to get access to this dict. -#### Notes & Tips -- **Everything is stored in memory.** This means that all `bot_data`, `user_data` and `chat_data` is deleted when the bot process ends. If you don't want this, have a look at the [persistent page](Making-your-bot-persistent). -- Empty `bot_data`, `user_data` and `chat_data` dictionaries are automatically deleted from memory after the update is processed. +## Notes & Tips +- **Everything is stored in memory.** This means that all `bot_data`, `user_data` and `chat_data` is deleted when the bot process ends. If you don't want this, have a look at the [persistence page](Making-your-bot-persistent). - If not empty, `bot_data`, `user_data` and `chat_data` will be kept until the process ends. - `user_data` and `chat_data` are different dictionaries even for private chats. - You can not assign a new value to `bot_data`, `user_data` or `chat_data`. Instead of `user_data = {}` and `user_data = other_dict`, use `user_data.clear()` and/or `user_data.update(other_dict)` respectively. diff --git a/Types-of-Handlers.md b/Types-of-Handlers.md index 79343b2..bce14fa 100644 --- a/Types-of-Handlers.md +++ b/Types-of-Handlers.md @@ -1,6 +1,6 @@ -A `Handler` is an instance derived from the base class [telegram.ext.Handler](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.handler.html#telegram.ext.Handler) which is responsible for the routing of different kinds of updates (text, audio, inlinequery, button presses, ...) to their _corresponding callback function_ in your code. +A `Handler` is an instance derived from the base class [`telegram.ext.Handler`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.handler.html#telegram.ext.Handler) which is responsible for the routing of different kinds of updates (text, audio, inlinequery, button presses, ...) to their _corresponding callback function_ in your code. -For example, if you want your bot to respond to the command `/start`, you can use a [CommandHandler](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.commandhandler.html) that maps this user input to a callback named `start_callback`: +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!") @@ -10,6 +10,22 @@ def start_callback(update, context): dispatcher.add_handler(CommandHandler("start", start_callback)) ``` +## Different types of `Update`s + +For different kinds of user input, the received `telegram.Update` will have different attributes set. For example an incoming message will result in `update.message` containing the sent message. The pressing an inline button will result in `update.callback_query` being set. To differentiate between all those updates, `telegram.ext` provides + +1) [`telegram.ext.MessageHandler`](https://python-telegram-bot.readthedocs.io/en/stable/telegram.ext.messagehandler.html) for all updates for all message updates +2) 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` + +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 + +* `updat.message` +* `update.edited_message` +* `update.channel_post` +* `update.edited_channel_post` + +You can use the different filters to narrow down which updates your `MessageHandler` will handle. See also [this article](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-–-Advanced-Filters) for advanced usage of filters. + ## CommandHandlers with arguments 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: @@ -37,15 +53,20 @@ Note that since telegram doesn't support spaces in deep linking parameters, you 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`. +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: The RegexHandler +## Pattern matching: `Filters.regex` -Warning: Regexhandler is being deprecated. See our [Transition guide](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to-Version-12.0) for more information. - -For more complex inputs you can employ the [telegram.ext.RegexHandler](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.regexhandler.html), 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 `RegexHandler` because the Bot API already sends them to us with every update. Refer to [this snippet](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Code-snippets#message-entities) to learn how to work with entities instead. +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. +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](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Code-snippets#message-entities) to learn how to work with entities instead. This tutorial only covers some of the available handlers (for now). Refer to the documentation for all other types: https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html#handlers + +## Custom updates + +In some cases, it's useful to handle updates that are not from Telegram. E.g. you might want to handle notifications from a 3rd party service and forward them to your users. For such use cases, PTB provides + +* [`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) diff --git a/Writing-Tests.md b/Writing-Tests.md index dd745bf..9f85fb2 100644 --- a/Writing-Tests.md +++ b/Writing-Tests.md @@ -9,10 +9,4 @@ https://github.com/Eldinnie/ptbtest In contrast to unit tests, integration tests may test the system in its eventual environment together with service integrations, such as the Bot API. In order to test your bot in a real environment, you can make use of a [userbot](http://telegra.ph/How-a-Userbot-superacharges-your-Telegram-Bot-07-09) library that will send messages to your bot and evaluate whether it responds in the way it should. [Telethon](https://github.com/LonamiWebs/Telethon) or [Pyrogram](https://github.com/pyrogram/pyrogram) should be the choices in a Python environment. -The currently unmaintained [TgIntegration library](https://github.com/JosXa/tgintegration/) was written for exactly this purpose, so maybe someone could come along with a pull request to update it to the latest version of Pyrogram. - - -## Feature Tests -We are testing features of our bot using behave python module and using python-telegram-bot. -I am implementing this concept on this repo [chat bots behave test](https://github.com/mmdaz/feature_testing_chat_bots) -I will be happy if I get comments and pull requests for improving it. \ No newline at end of file +The currently unmaintained [TgIntegration library](https://github.com/JosXa/tgintegration/) was written for exactly this purpose, so maybe someone could come along with a pull request to update it to the latest version of Pyrogram. \ No newline at end of file diff --git a/_Sidebar.md b/_Sidebar.md index 61f5bb4..c05e0bd 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -4,41 +4,39 @@ 3. [FAQ](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Frequently-Asked-Questions) 4. [How to ask good questions](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Ask-Right) -## Extensions -1. [Job Queue](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue) -2. [Types of Handlers](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Types-Of-Handlers) -3. [Advanced Filters](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Advanced-Filters) -4. [Avoiding flood limits](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Avoiding-flood-limits) -5. [Storing data](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Storing-bot,-user-and-chat-related-data) -6. [Making your bot persistent](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Making-your-bot-persistent) -7. [Adding Defaults](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Adding-defaults-to-your-bot) +## PTB Features +1. [Types of Handlers](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Types-Of-Handlers) +2. [Advanced Filters](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-Advanced-Filters) +3. [Storing data](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Storing-bot,-user-and-chat-related-data) +4. [Making your bot persistent](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Making-your-bot-persistent) +5. [Adding Defaults](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Adding-defaults-to-your-bot) +6. [Exception Handling](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Exception-Handling) +7. [Job Queue](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Extensions-%E2%80%93-JobQueue) +8. [Avoiding flood limits](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Avoiding-flood-limits) + +## Code Resources +1. [Code snippets](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Code-snippets) +2. [Performance Optimizations](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Performance-Optimizations) +3. [Webhooks](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Webhooks) +4. [Telegram Passport](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport) +5. [Bots built with PTB](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples) +6. [Automated Bot Tests](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Writing-Tests) ## Examples explained 1. [InlineKeyboard Example](https://github.com/python-telegram-bot/python-telegram-bot/wiki/InlineKeyboard-Example) -## Code resources -1. [Code snippets](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Code-snippets) -2. [Exception Handling](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Exception-Handling) -3. [Performance Optimizations](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Performance-Optimizations) -4. [Webhooks](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Webhooks) -5. [Defaults](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Adding-defaults-to-your-bot) -6. [Writing Tests](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Writing-Tests) -7. [Telegram Passport](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Telegram-Passport) -8. [Examples](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Examples) -9. [Related Projects](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Related-Projects) -10. [Emoji](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Emoji) - ## Networking 1. [Working Behind a Proxy](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Working-Behind-a-Proxy) -1. [Handling network errors](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Handling-network-errors) +2. [Handling network errors](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Handling-network-errors) ## Other resources 1. [Where to host Telegram Bots](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Where-to-host-Telegram-Bots) 2. [How to host your bot](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Hosting-your-bot) -3. [Notes on GAE](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Notes-about-GAE---Google-App-Engine) +3. [Local API Server](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Local-Bot-API-Server) 4. [Press](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Press) -5. [Ask Support](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Ask-Support) -6. [Local API Server](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Local-Bot-API-Server) +5. [Notes on GAE](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Notes-about-GAE---Google-App-Engine) +6. [Related Projects](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Related-Projects) +7. [Emoji](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Emoji) ## Transition Guides - [Version 4](https://github.com/python-telegram-bot/python-telegram-bot/wiki/Transition-guide-to-Version-4.0)