mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Merge branch 'dev-2.x' into patch-2
This commit is contained in:
commit
bef9c66929
47 changed files with 1728 additions and 720 deletions
98
.github/ISSUE_TEMPLATE/bug.yaml
vendored
Normal file
98
.github/ISSUE_TEMPLATE/bug.yaml
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
name: Bug report
|
||||
description: Report issues affecting the framework or the documentation.
|
||||
labels:
|
||||
- bug
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: I am sure the error is coming from aiogram code
|
||||
required: true
|
||||
- label: I have searched in the issue tracker for similar bug reports, including closed ones
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Context
|
||||
|
||||
Please provide as much information as possible. This will help us to reproduce the issue and fix it.
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating system
|
||||
placeholder: e.g. Ubuntu 20.04.2 LTS
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Python version
|
||||
placeholder: e.g. 3.10.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: aiogram version
|
||||
placeholder: e.g. 2.21 or 3.0b3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: Please describe the behavior you are expecting.
|
||||
placeholder: E.g. the bot should send a message with the text "Hello, world!".
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current behavior
|
||||
description: Please describe the behavior you are currently experiencing.
|
||||
placeholder: E.g. the bot doesn't send any message.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please describe the steps you took to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. step 1
|
||||
2. step 2
|
||||
3. ...
|
||||
4. you get it...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Code example
|
||||
description: Provide a [minimal, reproducible](https://stackoverflow.com/help/minimal-reproducible-example) and properly formatted example (if applicable).
|
||||
placeholder: |
|
||||
from aiogram import Bot, Dispatcher
|
||||
...
|
||||
render: python3
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Logs
|
||||
description: Provide the complete traceback (if applicable) or other kind of logs.
|
||||
placeholder: |
|
||||
Traceback (most recent call last):
|
||||
File "main.py", line 1, in <module>
|
||||
...
|
||||
SomeException: ...
|
||||
render: sh
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Please provide any additional information that may help us to reproduce the issue.
|
||||
placeholder: |
|
||||
E.g. this behavior is reproducible only in group chats.
|
||||
|
||||
You can also attach additional screenshots, logs, or other files.
|
||||
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
45
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,45 +0,0 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
|
||||
|
||||
* Operating System:
|
||||
* Python Version:
|
||||
* aiogram version:
|
||||
* aiohttp version:
|
||||
* uvloop version (if installed):
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Please describe the behavior you are expecting
|
||||
|
||||
## Current Behavior
|
||||
|
||||
What is the current behavior?
|
||||
|
||||
## Failure Information (for bugs)
|
||||
|
||||
Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
Please provide detailed steps for reproducing the issue.
|
||||
|
||||
1. step 1
|
||||
2. step 2
|
||||
3. you get it...
|
||||
|
||||
### Failure Logs
|
||||
|
||||
Please include any relevant log snippets or files here.
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Discuss anything related to the framework
|
||||
url: https://github.com/aiogram/aiogram/discussions
|
||||
about: Ask a question about aiogram or share your code snippets and ideas.
|
||||
- name: Join our Telegram channel
|
||||
url: https://t.me/aiogram_live
|
||||
about: Get the latest updates about the framework.
|
||||
- name: Join our Telegram chat
|
||||
url: https://t.me/aiogram
|
||||
about: Get help, ask questions, and discuss the framework in real-time.
|
||||
44
.github/ISSUE_TEMPLATE/feature.yaml
vendored
Normal file
44
.github/ISSUE_TEMPLATE/feature.yaml
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
name: Feature request
|
||||
description: Report features you would like to see or improve in the framework.
|
||||
labels:
|
||||
- enhancement
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem
|
||||
description: Is your feature request related to a specific problem? If not, please describe the general idea of your request.
|
||||
placeholder: e.g. I want to send a photo to a user by url.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Possible solution
|
||||
description: Describe the solution you would like to see in the framework.
|
||||
placeholder: e.g. Add a method to send a photo to a user by url.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: What other solutions do you have in mind?
|
||||
placeholder: e.g. I'm sending a text message with photo url.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Code example
|
||||
description: A small code example that demonstrates the behavior you would like to see.
|
||||
placeholder: |
|
||||
await bot.send_photo(user_id, photo_url)
|
||||
...
|
||||
render: python3
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: Any additional information you would like to provide.
|
||||
placeholder: |
|
||||
E.g. this method should also cache images to speed up further sending.
|
||||
|
||||
You can also attach additional pictures or other files.
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
51
.github/PULL_REQUEST_TEMPLATE.md
vendored
51
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,35 +1,52 @@
|
|||
# Description
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
|
||||
<!--
|
||||
Please include a summary of the change.
|
||||
|
||||
Fixes # (issue)
|
||||
e.g. Add a new awesome feature or Fix documentation typo.
|
||||
|
||||
Please also include relevant motivation and context.
|
||||
If you are fixing an issue, specify what issue is fixed.
|
||||
|
||||
e.g. Fix #12345
|
||||
-->
|
||||
|
||||
Type here...
|
||||
|
||||
## Type of change
|
||||
|
||||
<!--
|
||||
Please delete options that are not relevant.
|
||||
-->
|
||||
|
||||
- [ ] Documentation (typos, code examples or any documentation update)
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- Bug fix (non-breaking change that fixes an issue)
|
||||
- New feature (non-breaking change that adds functionality)
|
||||
- Documentation (typos, code examples or any documentation update)
|
||||
|
||||
# How Has This Been Tested?
|
||||
# How has this been tested?
|
||||
|
||||
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
|
||||
<!--
|
||||
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
|
||||
Please also list any relevant details for your test configuration.
|
||||
-->
|
||||
|
||||
- [ ] Test A
|
||||
- [ ] Test B
|
||||
Type here...
|
||||
|
||||
**Test Configuration**:
|
||||
* Operating System:
|
||||
* Python version:
|
||||
## Test Configuration
|
||||
- Operating system: e.g. Ubuntu 20.04.2 LTS
|
||||
-Python version: e.g. 3.10.1
|
||||
|
||||
# Checklist:
|
||||
|
||||
<!--
|
||||
Please delete options that are not relevant to your change.
|
||||
-->
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works as expected
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
- [ ] My changes generate no new warnings or errors
|
||||
- [ ] My changes are compatible with minimum requirements of the project
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
|
|
|
|||
17
.github/workflows/label_pr.yaml
vendored
Normal file
17
.github/workflows/label_pr.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
name: Label new pull request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- dev-2.x
|
||||
|
||||
jobs:
|
||||
put-label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add 2.x label
|
||||
uses: andymckay/labeler@master
|
||||
with:
|
||||
add-labels: 2.x
|
||||
2
Makefile
2
Makefile
|
|
@ -1,4 +1,4 @@
|
|||
VENV_NAME := venv
|
||||
VENV_NAME := .venv
|
||||
PYTHON := $(VENV_NAME)/bin/python
|
||||
AIOGRAM_VERSION := $(shell $(PYTHON) -c "import aiogram;print(aiogram.__version__)")
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
[](https://pypi.python.org/pypi/aiogram)
|
||||
[](https://pypi.python.org/pypi/aiogram)
|
||||
[](https://pypi.python.org/pypi/aiogram)
|
||||
[](https://core.telegram.org/bots/api)
|
||||
[](https://core.telegram.org/bots/api)
|
||||
[](http://docs.aiogram.dev/en/latest/?badge=latest)
|
||||
[](https://github.com/aiogram/aiogram/issues)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ AIOGramBot
|
|||
:target: https://pypi.python.org/pypi/aiogram
|
||||
:alt: Supported python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.7-blue.svg?style=flat-square&logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-6.2-blue.svg?style=flat-square&logo=telegram
|
||||
:target: https://core.telegram.org/bots/api
|
||||
:alt: Telegram Bot API
|
||||
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ __all__ = (
|
|||
'utils',
|
||||
)
|
||||
|
||||
__version__ = '2.19'
|
||||
__api_version__ = '5.7'
|
||||
__version__ = '2.22'
|
||||
__api_version__ = '6.2'
|
||||
|
|
|
|||
|
|
@ -269,6 +269,7 @@ class Methods(Helper):
|
|||
SEND_STICKER = Item() # sendSticker
|
||||
GET_STICKER_SET = Item() # getStickerSet
|
||||
UPLOAD_STICKER_FILE = Item() # uploadStickerFile
|
||||
GET_CUSTOM_EMOJI_STICKERS = Item() # getCustomEmojiStickers
|
||||
CREATE_NEW_STICKER_SET = Item() # createNewStickerSet
|
||||
ADD_STICKER_TO_SET = Item() # addStickerToSet
|
||||
SET_STICKER_POSITION_IN_SET = Item() # setStickerPositionInSet
|
||||
|
|
@ -278,8 +279,16 @@ class Methods(Helper):
|
|||
# Inline mode
|
||||
ANSWER_INLINE_QUERY = Item() # answerInlineQuery
|
||||
|
||||
ANSWER_WEB_APP_QUERY = Item() # answerWebAppQuery
|
||||
SET_CHAT_MENU_BUTTON = Item() # setChatMenuButton
|
||||
GET_CHAT_MENU_BUTTON = Item() # getChatMenuButton
|
||||
|
||||
SET_MY_DEFAULT_ADMINISTRATOR_RIGHTS = Item() # setMyDefaultAdministratorRights
|
||||
GET_MY_DEFAULT_ADMINISTRATOR_RIGHTS = Item() # getMyDefaultAdministratorRights
|
||||
|
||||
# Payments
|
||||
SEND_INVOICE = Item() # sendInvoice
|
||||
CREATE_INVOICE_LINK = Item() # createInvoiceLink
|
||||
ANSWER_SHIPPING_QUERY = Item() # answerShippingQuery
|
||||
ANSWER_PRE_CHECKOUT_QUERY = Item() # answerPreCheckoutQuery
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class BaseBot:
|
|||
validate_token: Optional[base.Boolean] = True,
|
||||
parse_mode: typing.Optional[base.String] = None,
|
||||
disable_web_page_preview: Optional[base.Boolean] = None,
|
||||
protect_content: Optional[base.Boolean] = None,
|
||||
timeout: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] = None,
|
||||
server: TelegramAPIServer = TELEGRAM_PRODUCTION
|
||||
):
|
||||
|
|
@ -60,6 +61,9 @@ class BaseBot:
|
|||
:type parse_mode: :obj:`str`
|
||||
:param disable_web_page_preview: You can set default disable web page preview parameter
|
||||
:type disable_web_page_preview: :obj:`bool`
|
||||
:param protect_content: Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:type protect_content: :obj:`typing.Optional[base.Boolean]`
|
||||
:param timeout: Request timeout
|
||||
:type timeout: :obj:`typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]]`
|
||||
:param server: Telegram Bot API Server endpoint.
|
||||
|
|
@ -111,6 +115,7 @@ class BaseBot:
|
|||
self.parse_mode = parse_mode
|
||||
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
self.protect_content = protect_content
|
||||
|
||||
async def get_new_session(self) -> aiohttp.ClientSession:
|
||||
return aiohttp.ClientSession(
|
||||
|
|
@ -361,5 +366,22 @@ class BaseBot:
|
|||
def disable_web_page_preview(self):
|
||||
self.disable_web_page_preview = None
|
||||
|
||||
@property
|
||||
def protect_content(self):
|
||||
return getattr(self, "_protect_content", None)
|
||||
|
||||
@protect_content.setter
|
||||
def protect_content(self, value):
|
||||
if value is None:
|
||||
setattr(self, "_protect_content", None)
|
||||
return
|
||||
if not isinstance(value, bool):
|
||||
raise TypeError(f"Protect content must be bool, not {type(value)}")
|
||||
setattr(self, "_protect_content", value)
|
||||
|
||||
@protect_content.deleter
|
||||
def protect_content(self):
|
||||
self.protect_content = None
|
||||
|
||||
def check_auth_widget(self, data):
|
||||
return check_integrity(self.__token, data)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import warnings
|
|||
from .base import BaseBot, api
|
||||
from .. import types
|
||||
from ..types import base
|
||||
from ..utils.deprecated import deprecated, removed_argument
|
||||
from ..utils.deprecated import deprecated
|
||||
from ..utils.exceptions import ValidationError
|
||||
from ..utils.mixins import DataMixin, ContextInstanceMixin
|
||||
from ..utils.payload import generate_payload, prepare_arg, prepare_attachment, prepare_file
|
||||
|
|
@ -117,6 +117,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
max_connections: typing.Optional[base.Integer] = None,
|
||||
allowed_updates: typing.Optional[typing.List[base.String]] = None,
|
||||
drop_pending_updates: typing.Optional[base.Boolean] = None,
|
||||
secret_token: typing.Optional[str] = None,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method to specify a url and receive incoming updates via an outgoing
|
||||
|
|
@ -165,6 +166,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
:param drop_pending_updates: Pass True to drop all pending updates
|
||||
:type drop_pending_updates: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param secret_token: A secret token to be sent in a header “X-Telegram-Bot-Api-Secret-Token”
|
||||
in every webhook request, 1-256 characters. Only characters A-Z, a-z, 0-9, _ and - are allowed.
|
||||
The header is useful to ensure that the request comes from a webhook set by you.
|
||||
:type secret_token: :obj:`typing.Optional[str]`
|
||||
:return: Returns true
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
|
|
@ -330,6 +335,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.disable_web_page_preview:
|
||||
payload.setdefault('disable_web_page_preview', self.disable_web_page_preview)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_MESSAGE, payload)
|
||||
return types.Message(**result)
|
||||
|
|
@ -370,6 +377,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
:rtype: :obj:`types.Message`
|
||||
"""
|
||||
payload = generate_payload(**locals())
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.FORWARD_MESSAGE, payload)
|
||||
return types.Message(**result)
|
||||
|
|
@ -452,6 +461,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals())
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.COPY_MESSAGE, payload)
|
||||
return types.MessageId(**result)
|
||||
|
|
@ -520,6 +531,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals(), exclude=['photo'])
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'photo', photo)
|
||||
|
|
@ -610,6 +623,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals(), exclude=['audio', 'thumb'])
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'audio', audio)
|
||||
|
|
@ -700,6 +715,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals(), exclude=['document'])
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'document', document)
|
||||
|
|
@ -792,6 +809,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals(), exclude=['video', 'thumb'])
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'video', video)
|
||||
|
|
@ -887,6 +906,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals(), exclude=["animation", "thumb"])
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'animation', animation)
|
||||
|
|
@ -967,6 +988,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals(), exclude=['voice'])
|
||||
if self.parse_mode and caption_entities is None:
|
||||
payload.setdefault('parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'voice', voice)
|
||||
|
|
@ -1033,6 +1056,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
reply_markup = prepare_arg(reply_markup)
|
||||
payload = generate_payload(**locals(), exclude=['video_note'])
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'video_note', video_note)
|
||||
|
|
@ -1089,12 +1114,15 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
|
||||
# Check MediaGroup quantity
|
||||
if not (1 <= len(media.media) <= 10):
|
||||
raise ValidationError("Media group must include 2-10 items as written in docs, but also it works with 1 element")
|
||||
raise ValidationError(
|
||||
"Media group must include 2-10 items as written in docs, but also it works with 1 element")
|
||||
|
||||
files = dict(media.get_files())
|
||||
|
||||
media = prepare_arg(media)
|
||||
payload = generate_payload(**locals(), exclude=['files'])
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_MEDIA_GROUP, payload, files)
|
||||
return [types.Message(**message) for message in result]
|
||||
|
|
@ -1168,6 +1196,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
reply_markup = prepare_arg(reply_markup)
|
||||
payload = generate_payload(**locals())
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_LOCATION, payload)
|
||||
return types.Message(**result)
|
||||
|
|
@ -1346,6 +1376,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
reply_markup = prepare_arg(reply_markup)
|
||||
payload = generate_payload(**locals())
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_VENUE, payload)
|
||||
return types.Message(**result)
|
||||
|
|
@ -1409,6 +1441,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.SEND_CONTACT, payload)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
return types.Message(**result)
|
||||
|
||||
async def send_poll(self,
|
||||
|
|
@ -1527,6 +1561,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
payload = generate_payload(**locals())
|
||||
if self.parse_mode and explanation_entities is None:
|
||||
payload.setdefault('explanation_parse_mode', self.parse_mode)
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_POLL, payload)
|
||||
return types.Message(**result)
|
||||
|
|
@ -1586,6 +1622,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
|
||||
reply_markup = prepare_arg(reply_markup)
|
||||
payload = generate_payload(**locals())
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_DICE, payload)
|
||||
return types.Message(**result)
|
||||
|
|
@ -1704,9 +1742,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
datetime.datetime, datetime.timedelta, None]`
|
||||
|
||||
:param revoke_messages: Pass True to delete all messages from
|
||||
the chat for the user that is being removed. If False, the user
|
||||
will be able to see messages in the group that were sent before
|
||||
the user was removed. Always True for supergroups and channels.
|
||||
the chat for the user that is being removed. If False, the user
|
||||
will be able to see messages in the group that were sent before
|
||||
the user was removed. Always True for supergroups and channels.
|
||||
:type revoke_messages: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:return: Returns True on success
|
||||
|
|
@ -1834,6 +1872,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
can_restrict_members: typing.Optional[base.Boolean] = None,
|
||||
can_pin_messages: typing.Optional[base.Boolean] = None,
|
||||
can_promote_members: typing.Optional[base.Boolean] = None,
|
||||
can_manage_video_chats: typing.Optional[base.Boolean] = None,
|
||||
) -> base.Boolean:
|
||||
"""
|
||||
Use this method to promote or demote a user in a supergroup or a channel.
|
||||
|
|
@ -1885,9 +1924,17 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
directly or indirectly (promoted by administrators that were appointed by him)
|
||||
:type can_promote_members: :obj:`typing.Optional[base.Boolean]`
|
||||
|
||||
:param can_manage_video_chats: Pass True, if the administrator can manage video chats
|
||||
|
||||
:return: Returns True on success
|
||||
:rtype: :obj:`base.Boolean`
|
||||
"""
|
||||
if can_manage_voice_chats:
|
||||
warnings.warn(
|
||||
"Argument `can_manage_voice_chats` was renamed to `can_manage_video_chats` and will be removed in aiogram 2.21")
|
||||
can_manage_video_chats = can_manage_voice_chats
|
||||
can_manage_voice_chats = None
|
||||
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.PROMOTE_CHAT_MEMBER, payload)
|
||||
|
|
@ -1910,11 +1957,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
|
||||
return await self.request(api.Methods.SET_CHAT_ADMINISTRATOR_CUSTOM_TITLE, payload)
|
||||
|
||||
@removed_argument("until_date", "2.19")
|
||||
async def ban_chat_sender_chat(
|
||||
self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
sender_chat_id: base.Integer,
|
||||
self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
sender_chat_id: base.Integer,
|
||||
):
|
||||
"""Ban a channel chat in a supergroup or a channel.
|
||||
|
||||
|
|
@ -1937,9 +1983,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
return await self.request(api.Methods.BAN_CHAT_SENDER_CHAT, payload)
|
||||
|
||||
async def unban_chat_sender_chat(
|
||||
self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
sender_chat_id: base.Integer,
|
||||
self,
|
||||
chat_id: typing.Union[base.Integer, base.String],
|
||||
sender_chat_id: base.Integer,
|
||||
):
|
||||
"""Unban a previously banned channel chat in a supergroup or
|
||||
channel.
|
||||
|
|
@ -2587,6 +2633,87 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
result = await self.request(api.Methods.GET_MY_COMMANDS, payload)
|
||||
return [types.BotCommand(**bot_command_data) for bot_command_data in result]
|
||||
|
||||
async def set_chat_menu_button(self, chat_id: typing.Optional[base.Integer] = None,
|
||||
menu_button: typing.Optional[types.MenuButton] = None) -> bool:
|
||||
"""
|
||||
Use this method to change bot's menu button in a private chat, or the default menu button.
|
||||
|
||||
Returns True on success.
|
||||
|
||||
Source https://core.telegram.org/bots/api#setchatmenubutton
|
||||
|
||||
:param chat_id: Unique identifier for the target private chat.
|
||||
If not specified, default bot's menu button will be changed
|
||||
:param menu_button:
|
||||
A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault
|
||||
:return: Returns True on success.
|
||||
"""
|
||||
menu_button = prepare_arg(menu_button)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.SET_CHAT_MENU_BUTTON, payload)
|
||||
|
||||
async def get_chat_menu_button(self, chat_id: typing.Optional[base.Integer] = None) -> typing.Union[
|
||||
"types.MenuButtonCommands",
|
||||
"types.MenuButtonDefault",
|
||||
"types.MenuButtonWebApp",
|
||||
]:
|
||||
"""
|
||||
Use this method to get the current value of the bot's menu button in a private chat,
|
||||
or the default menu button.
|
||||
|
||||
Returns MenuButton on success.
|
||||
|
||||
Source https://core.telegram.org/bots/api#getchatmenu
|
||||
|
||||
:param chat_id: Unique identifier for the target private chat. If not specified,
|
||||
default bot's menu button will be returned
|
||||
:return: Returns MenuButton on success.
|
||||
"""
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.GET_CHAT_MENU_BUTTON, payload)
|
||||
return types.MenuButton.resolve(**result)
|
||||
|
||||
async def set_my_default_administrator_rights(self, rights: typing.Optional[types.ChatAdministratorRights] = None,
|
||||
for_channels: typing.Optional[base.Boolean] = None) -> base.Boolean:
|
||||
"""
|
||||
Use this method to change default administrator rights of the bot for adding it as an administrator
|
||||
to groups or channels.
|
||||
Returns True on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setmydefaultadministratorrights
|
||||
|
||||
:param rights: A JSON-serialized object, describing new default administrator rights.
|
||||
If not specified, the default administrator rights will be cleared.
|
||||
:param for_channels:
|
||||
Pass True to change default administrator rights of the bot in channels.
|
||||
Otherwise, default administrator rights of the bot for groups and supergroups will be changed.
|
||||
:return: Returns True on success.
|
||||
"""
|
||||
rights = prepare_arg(rights)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.SET_MY_DEFAULT_ADMINISTRATOR_RIGHTS, payload)
|
||||
|
||||
async def get_my_default_administrator_rights(self,
|
||||
for_channels: typing.Optional[base.Boolean] = None
|
||||
) -> types.ChatAdministratorRights:
|
||||
"""
|
||||
Use this method to get the current default administrator rights of the bot.
|
||||
Returns ChatAdministratorRights on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getmydefaultadministratorrights
|
||||
|
||||
:param for_channels: Pass True to get default administrator rights of the bot in channels.
|
||||
Otherwise, default administrator rights of the bot for groups and supergroups will be returned.
|
||||
:return:
|
||||
"""
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.GET_MY_DEFAULT_ADMINISTRATOR_RIGHTS, payload)
|
||||
return types.ChatAdministratorRights(**result)
|
||||
|
||||
async def edit_message_text(self,
|
||||
text: base.String,
|
||||
chat_id: typing.Union[base.Integer, base.String, None] = None,
|
||||
|
|
@ -2871,6 +2998,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
reply_markup = prepare_arg(reply_markup)
|
||||
payload = generate_payload(**locals(), exclude=['sticker'])
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'sticker', sticker)
|
||||
|
|
@ -2917,6 +3046,23 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
result = await self.request(api.Methods.UPLOAD_STICKER_FILE, payload, files)
|
||||
return types.File(**result)
|
||||
|
||||
async def get_custom_emoji_stickers(self, custom_emoji_ids: typing.List[base.String]) -> typing.List[types.Sticker]:
|
||||
"""
|
||||
Use this method to get information about custom emoji stickers by their identifiers.
|
||||
|
||||
|
||||
Source: https://core.telegram.org/bots/api#uploadstickerfile
|
||||
|
||||
:param custom_emoji_ids: User identifier of sticker file owner
|
||||
:type custom_emoji_ids: :obj:`typing.List[base.String]`
|
||||
:return: Returns an Array of Sticker objects.
|
||||
:rtype: :obj:`typing.List[types.Sticker]`
|
||||
"""
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
result = await self.request(api.Methods.GET_CUSTOM_EMOJI_STICKERS, payload)
|
||||
return [types.Sticker(**item) for item in result]
|
||||
|
||||
async def create_new_sticker_set(self,
|
||||
user_id: base.Integer,
|
||||
name: base.String,
|
||||
|
|
@ -2926,6 +3072,7 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
tgs_sticker: base.InputFile = None,
|
||||
webm_sticker: base.InputFile = None,
|
||||
contains_masks: typing.Optional[base.Boolean] = None,
|
||||
sticker_type: typing.Optional[base.String] = None,
|
||||
mask_position: typing.Optional[types.MaskPosition] = None) -> base.Boolean:
|
||||
"""
|
||||
Use this method to create a new sticker set owned by a user.
|
||||
|
|
@ -2954,7 +3101,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
:type tgs_sticker: :obj:`base.InputFile`
|
||||
:param webm_sticker: WEBM video with the sticker, uploaded using multipart/form-data.
|
||||
See https://core.telegram.org/stickers#video-sticker-requirements for technical requirements
|
||||
:type webm_sticker: :obj:`base.InputFile`
|
||||
:type webm_sticker: :obj:`base.String`
|
||||
:param sticker_type: Type of stickers in the set, pass “regular” or “mask”.
|
||||
Custom emoji sticker sets can't be created via the Bot API at the moment.
|
||||
By default, a regular sticker set is created.
|
||||
:type sticker_type: :obj:`base.InputFile`
|
||||
:param emojis: One or more emoji corresponding to the sticker
|
||||
:type emojis: :obj:`base.String`
|
||||
:param contains_masks: Pass True, if a set of mask stickers should be created
|
||||
|
|
@ -2966,6 +3117,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
mask_position = prepare_arg(mask_position)
|
||||
payload = generate_payload(**locals(), exclude=['png_sticker', 'tgs_sticker', 'webm_sticker'])
|
||||
if contains_masks is not None:
|
||||
warnings.warn(
|
||||
message="The parameter `contains_masks` deprecated, use `sticker_type` instead.",
|
||||
category=DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
files = {}
|
||||
prepare_file(payload, files, 'png_sticker', png_sticker)
|
||||
|
|
@ -3133,6 +3290,25 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
|
||||
return await self.request(api.Methods.ANSWER_INLINE_QUERY, payload)
|
||||
|
||||
async def answer_web_app_query(self, web_app_query_id: base.String,
|
||||
result: types.InlineQueryResult) -> types.SentWebAppMessage:
|
||||
"""
|
||||
Use this method to set result of interaction with web app and send corresponding message
|
||||
on behalf of the user to the chat from which the query originated.
|
||||
On success, SentWebAppMessage is returned.
|
||||
|
||||
Source https://core.telegram.org/bots/api#answerwebappquery
|
||||
|
||||
:param web_app_query_id: Unique identifier for the answered query
|
||||
:param result: A JSON-serialized object with a description of the message to send
|
||||
:return: On success, SentWebAppMessage is returned.
|
||||
"""
|
||||
result = prepare_arg(result)
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
response = await self.request(api.Methods.ANSWER_WEB_APP_QUERY, payload)
|
||||
return types.SentWebAppMessage(**response)
|
||||
|
||||
# === Payments ===
|
||||
# https://core.telegram.org/bots/api#payments
|
||||
|
||||
|
|
@ -3284,10 +3460,75 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
reply_markup = prepare_arg(reply_markup)
|
||||
provider_data = prepare_arg(provider_data)
|
||||
payload_ = generate_payload(**locals())
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_INVOICE, payload_)
|
||||
return types.Message(**result)
|
||||
|
||||
async def create_invoice_link(self,
|
||||
title: base.String,
|
||||
description: base.String,
|
||||
payload: base.String,
|
||||
provider_token: base.String,
|
||||
currency: base.String,
|
||||
prices: typing.List[types.LabeledPrice],
|
||||
max_tip_amount: typing.Optional[int] = None,
|
||||
suggested_tip_amounts: typing.Optional[typing.List[int]] = None,
|
||||
provider_data: typing.Optional[base.String] = None,
|
||||
photo_url: typing.Optional[str] = None,
|
||||
photo_size: typing.Optional[int] = None,
|
||||
photo_width: typing.Optional[int] = None,
|
||||
photo_height: typing.Optional[int] = None,
|
||||
need_name: typing.Optional[bool] = None,
|
||||
need_phone_number: typing.Optional[bool] = None,
|
||||
need_email: typing.Optional[bool] = None,
|
||||
need_shipping_address: typing.Optional[bool] = None,
|
||||
send_phone_number_to_provider: typing.Optional[bool] = None,
|
||||
send_email_to_provider: typing.Optional[bool] = None,
|
||||
is_flexible: typing.Optional[bool] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Use this method to create a link for an invoice. On success, the created link is returned.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#createinvoicelink
|
||||
|
||||
:param title: Product name, 1-32 characters
|
||||
:param description: Product description, 1-255 characters
|
||||
:param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
|
||||
:param provider_token: Payment provider token, obtained via BotFather
|
||||
:param currency: Three-letter ISO 4217 currency code, see more on currencies
|
||||
:param prices: Price breakdown, a JSON-serialized list of components
|
||||
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
|
||||
:param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency
|
||||
(integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145.
|
||||
See the exp parameter in currencies.json, it shows the number of digits past the decimal point for
|
||||
each currency (2 for the majority of currencies). Defaults to 0
|
||||
:param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest units
|
||||
of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified.
|
||||
The suggested tip amounts must be positive, passed in a strictly increased order and must not
|
||||
exceed max_tip_amount.
|
||||
:param provider_data: JSON-serialized data about the invoice, which will be shared with the payment provider.
|
||||
A detailed description of required fields should be provided by the payment provider.
|
||||
:param photo_url: URL of the product photo for the invoice.
|
||||
Can be a photo of the goods or a marketing image for a service.
|
||||
:param photo_size: Photo size in bytes
|
||||
:param photo_width: Photo width
|
||||
:param photo_height: Photo height
|
||||
:param need_name: Pass True, if you require the user's full name to complete the order
|
||||
:param need_phone_number: Pass True, if you require the user's phone number to complete the order
|
||||
:param need_email: Pass True, if you require the user's email address to complete the order
|
||||
:param need_shipping_address: Pass True, if you require the user's shipping address to complete the order
|
||||
:param send_phone_number_to_provider: Pass True, if the user's phone number should be sent to the provider
|
||||
:param send_email_to_provider: Pass True, if the user's email address should be sent to the provider
|
||||
:param is_flexible: Pass True, if the final price depends on the shipping method
|
||||
:return:
|
||||
"""
|
||||
prices = prepare_arg([price.to_python() if hasattr(price, 'to_python') else price for price in prices])
|
||||
payload = generate_payload(**locals())
|
||||
|
||||
return await self.request(api.Methods.CREATE_INVOICE_LINK, payload)
|
||||
|
||||
async def answer_shipping_query(self, shipping_query_id: base.String, ok: base.Boolean,
|
||||
shipping_options: typing.Union[typing.List[types.ShippingOption], None] = None,
|
||||
error_message: typing.Optional[base.String] = None) -> base.Boolean:
|
||||
|
|
@ -3426,6 +3667,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
|
|||
"""
|
||||
reply_markup = prepare_arg(reply_markup)
|
||||
payload = generate_payload(**locals())
|
||||
if self.protect_content is not None:
|
||||
payload.setdefault('protect_content', self.protect_content)
|
||||
|
||||
result = await self.request(api.Methods.SEND_GAME, payload)
|
||||
return types.Message(**result)
|
||||
|
|
|
|||
|
|
@ -213,5 +213,5 @@ class MongoStorage(BaseStorage):
|
|||
:return: list of tuples where first element is chat id and second is user id
|
||||
"""
|
||||
db = await self.get_db()
|
||||
items = await db[STATE].find().to_list()
|
||||
items = await db[STATE].find().to_list(length=None)
|
||||
return [(int(item['chat']), int(item['user'])) for item in items]
|
||||
|
|
|
|||
|
|
@ -1367,7 +1367,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin):
|
|||
|
||||
bucket = await self.storage.get_bucket(chat=chat_id, user=user_id)
|
||||
if bucket and key in bucket:
|
||||
del bucket['key']
|
||||
del bucket[key]
|
||||
await self.storage.set_bucket(chat=chat_id, user=user_id, bucket=bucket)
|
||||
return True
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from babel.support import LazyProxy
|
|||
|
||||
from aiogram import types
|
||||
from aiogram.dispatcher.filters.filters import BoundFilter, Filter
|
||||
from aiogram.types import CallbackQuery, ChatType, InlineQuery, Message, Poll, ChatMemberUpdated
|
||||
from aiogram.types import CallbackQuery, ChatType, InlineQuery, Message, Poll, ChatMemberUpdated, BotCommand
|
||||
|
||||
ChatIDArgumentType = typing.Union[typing.Iterable[typing.Union[int, str]], str, int]
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ class Command(Filter):
|
|||
By default this filter is registered for messages and edited messages handlers.
|
||||
"""
|
||||
|
||||
def __init__(self, commands: Union[Iterable, str],
|
||||
def __init__(self, commands: Union[Iterable[Union[str, BotCommand]], str, BotCommand],
|
||||
prefixes: Union[Iterable, str] = '/',
|
||||
ignore_case: bool = True,
|
||||
ignore_mention: bool = False,
|
||||
|
|
@ -66,8 +66,19 @@ class Command(Filter):
|
|||
@dp.message_handler(commands=['myCommand'], commands_ignore_caption=False, content_types=ContentType.ANY)
|
||||
@dp.message_handler(Command(['myCommand'], ignore_caption=False), content_types=[ContentType.TEXT, ContentType.DOCUMENT])
|
||||
"""
|
||||
if isinstance(commands, str):
|
||||
if isinstance(commands, (str, BotCommand)):
|
||||
commands = (commands,)
|
||||
elif isinstance(commands, Iterable):
|
||||
if not all(isinstance(cmd, (str, BotCommand)) for cmd in commands):
|
||||
raise ValueError(
|
||||
"Command filter only supports str, BotCommand object or their Iterable"
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Command filter doesn't support {} as input. "
|
||||
"It only supports str, BotCommand object or their Iterable".format(type(commands))
|
||||
)
|
||||
commands = [cmd.command if isinstance(cmd, BotCommand) else cmd for cmd in commands]
|
||||
|
||||
self.commands = list(map(str.lower, commands)) if ignore_case else commands
|
||||
self.prefixes = prefixes
|
||||
|
|
@ -728,18 +739,20 @@ class ChatTypeFilter(BoundFilter):
|
|||
|
||||
self.chat_type: typing.Set[str] = set(chat_type)
|
||||
|
||||
async def check(self, obj: Union[Message, CallbackQuery, ChatMemberUpdated]):
|
||||
async def check(self, obj: Union[Message, CallbackQuery, ChatMemberUpdated, InlineQuery]):
|
||||
if isinstance(obj, Message):
|
||||
obj = obj.chat
|
||||
chat_type = obj.chat.type
|
||||
elif isinstance(obj, CallbackQuery):
|
||||
obj = obj.message.chat
|
||||
chat_type = obj.message.chat.type if obj.message else None
|
||||
elif isinstance(obj, ChatMemberUpdated):
|
||||
obj = obj.chat
|
||||
chat_type = obj.chat.type
|
||||
elif isinstance(obj, InlineQuery):
|
||||
chat_type = obj.chat_type
|
||||
else:
|
||||
warnings.warn("ChatTypeFilter doesn't support %s as input", type(obj))
|
||||
return False
|
||||
|
||||
return obj.type in self.chat_type
|
||||
return chat_type in self.chat_type
|
||||
|
||||
|
||||
class MediaGroupFilter(BoundFilter):
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ class Handler:
|
|||
"""
|
||||
for handler_obj in self.handlers:
|
||||
registered = handler_obj.handler
|
||||
if handler is registered:
|
||||
self.handlers.remove(handler_obj)
|
||||
if handler in self.handlers:
|
||||
self.handlers.remove(handler)
|
||||
return True
|
||||
raise ValueError('This handler is not registered!')
|
||||
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ class BaseStorage:
|
|||
:param user:
|
||||
:return:
|
||||
"""
|
||||
await self.set_data(chat=chat, user=user, data={})
|
||||
await self.set_bucket(chat=chat, user=user, bucket={})
|
||||
|
||||
@staticmethod
|
||||
def resolve_state(value):
|
||||
|
|
|
|||
|
|
@ -448,6 +448,29 @@ class DisableWebPagePreviewMixin:
|
|||
return bot.disable_web_page_preview
|
||||
|
||||
|
||||
class ProtectContentMixin:
|
||||
def protect_content(self):
|
||||
"""
|
||||
Protect content
|
||||
|
||||
:return:
|
||||
"""
|
||||
setattr(self, "protect_content", True)
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def _global_protect_content():
|
||||
"""
|
||||
Detect global protect content value
|
||||
|
||||
:return:
|
||||
"""
|
||||
from aiogram import Bot
|
||||
bot = Bot.get_current()
|
||||
if bot is not None:
|
||||
return bot.protect_content
|
||||
|
||||
|
||||
class ParseModeMixin:
|
||||
def as_html(self):
|
||||
"""
|
||||
|
|
@ -480,7 +503,9 @@ class ParseModeMixin:
|
|||
return bot.parse_mode
|
||||
|
||||
|
||||
class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificationMixin, DisableWebPagePreviewMixin):
|
||||
class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin,
|
||||
DisableNotificationMixin, DisableWebPagePreviewMixin,
|
||||
ProtectContentMixin):
|
||||
"""
|
||||
You can send message with webhook by using this instance of this object.
|
||||
All arguments is equal with Bot.send_message method.
|
||||
|
|
@ -488,7 +513,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
|
|||
|
||||
__slots__ = ('chat_id', 'text', 'parse_mode',
|
||||
'disable_web_page_preview', 'disable_notification',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_MESSAGE
|
||||
|
||||
|
|
@ -497,6 +522,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
|
|||
parse_mode: Optional[String] = None,
|
||||
disable_web_page_preview: Optional[Boolean] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -509,6 +535,8 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
|
|||
:param disable_web_page_preview: Boolean (Optional) - Disables link previews for links in this message
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently. Users will receive
|
||||
a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
|
|
@ -520,12 +548,15 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
|
|||
parse_mode = self._global_parse_mode()
|
||||
if disable_web_page_preview is None:
|
||||
disable_web_page_preview = self._global_disable_web_page_preview()
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.text = text
|
||||
self.parse_mode = parse_mode
|
||||
self.disable_web_page_preview = disable_web_page_preview
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -536,6 +567,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
|
|||
'parse_mode': self.parse_mode,
|
||||
'disable_web_page_preview': self.disable_web_page_preview,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
|
@ -565,18 +597,19 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio
|
|||
return self
|
||||
|
||||
|
||||
class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for forward messages of any kind on to webhook.
|
||||
"""
|
||||
__slots__ = ('chat_id', 'from_chat_id', 'message_id', 'disable_notification')
|
||||
__slots__ = ('chat_id', 'from_chat_id', 'message_id', 'disable_notification', 'protect_content')
|
||||
|
||||
method = api.Methods.FORWARD_MESSAGE
|
||||
|
||||
def __init__(self, chat_id: Union[Integer, String] = None,
|
||||
from_chat_id: Union[Integer, String] = None,
|
||||
message_id: Integer = None,
|
||||
disable_notification: Optional[Boolean] = None):
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None):
|
||||
"""
|
||||
:param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the
|
||||
target channel (in the format @channelusername)
|
||||
|
|
@ -584,12 +617,18 @@ class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
message was sent (or channel username in the format @channelusername)
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently. Users will receive a
|
||||
notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param message_id: Integer - Message identifier in the chat specified in from_chat_id
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.from_chat_id = from_chat_id
|
||||
self.message_id = message_id
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
|
||||
def message(self, message: types.Message):
|
||||
"""
|
||||
|
|
@ -607,16 +646,18 @@ class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'chat_id': self.chat_id,
|
||||
'from_chat_id': self.from_chat_id,
|
||||
'message_id': self.message_id,
|
||||
'disable_notification': self.disable_notification
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
}
|
||||
|
||||
|
||||
class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send photo on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'photo', 'caption', 'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
__slots__ = ('chat_id', 'photo', 'caption', 'disable_notification',
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_PHOTO
|
||||
|
||||
|
|
@ -624,6 +665,7 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
photo: String,
|
||||
caption: Optional[String] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -637,15 +679,21 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
0-1024 characters after entities parsing
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently. Users will receive
|
||||
a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.photo = photo
|
||||
self.caption = caption
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -655,18 +703,20 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'photo': self.photo,
|
||||
'caption': self.caption,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send audio on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'audio', 'caption', 'duration', 'performer', 'title',
|
||||
'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
'disable_notification', 'protect_content',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_AUDIO
|
||||
|
||||
|
|
@ -677,6 +727,7 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
performer: Optional[String] = None,
|
||||
title: Optional[String] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -693,11 +744,16 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param title: String (Optional) - Track name
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.audio = audio
|
||||
self.caption = caption
|
||||
|
|
@ -705,6 +761,7 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
self.performer = performer
|
||||
self.title = title
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -717,17 +774,19 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'performer': self.performer,
|
||||
'title': self.title,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send document on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'document', 'caption', 'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
__slots__ = ('chat_id', 'document', 'caption', 'disable_notification',
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_DOCUMENT
|
||||
|
||||
|
|
@ -735,6 +794,7 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
document: String,
|
||||
caption: Optional[String] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -749,15 +809,21 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
(may also be used when resending documents by file_id), 0-1024 characters after entities parsing
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.document = document
|
||||
self.caption = caption
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -767,17 +833,19 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'document': self.document,
|
||||
'caption': self.caption,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send video on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'video', 'duration', 'width', 'height', 'caption', 'disable_notification',
|
||||
__slots__ = ('chat_id', 'video', 'duration', 'width', 'height', 'caption',
|
||||
'disable_notification', 'protect_content',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_VIDEO
|
||||
|
|
@ -789,6 +857,7 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
height: Optional[Integer] = None,
|
||||
caption: Optional[String] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -806,11 +875,16 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
0-1024 characters after entities parsing
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.video = video
|
||||
self.duration = duration
|
||||
|
|
@ -818,6 +892,7 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
self.height = height
|
||||
self.caption = caption
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -830,18 +905,19 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'height': self.height,
|
||||
'caption': self.caption,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send voice on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'voice', 'caption', 'duration', 'disable_notification',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_VOICE
|
||||
|
||||
|
|
@ -850,6 +926,7 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
caption: Optional[String] = None,
|
||||
duration: Optional[Integer] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -864,16 +941,22 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param duration: Integer (Optional) - Duration of the voice message in seconds
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard,
|
||||
instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.voice = voice
|
||||
self.caption = caption
|
||||
self.duration = duration
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -884,18 +967,19 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'caption': self.caption,
|
||||
'duration': self.duration,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send video note on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'video_note', 'duration', 'length', 'disable_notification',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_VIDEO_NOTE
|
||||
|
||||
|
|
@ -904,6 +988,7 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
duration: Optional[Integer] = None,
|
||||
length: Optional[Integer] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -917,16 +1002,22 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param length: Integer (Optional) - Video width and height
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.video_note = video_note
|
||||
self.duration = duration
|
||||
self.length = length
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -937,23 +1028,26 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'duration': self.duration,
|
||||
'length': self.length,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use this method to send a group of photos or videos as an album.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'media', 'disable_notification', 'reply_to_message_id')
|
||||
__slots__ = ('chat_id', 'media', 'disable_notification',
|
||||
'protect_content', 'reply_to_message_id')
|
||||
|
||||
method = api.Methods.SEND_MEDIA_GROUP
|
||||
|
||||
def __init__(self, chat_id: Union[Integer, String],
|
||||
media: Union[types.MediaGroup, List] = None,
|
||||
disable_notification: typing.Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: typing.Optional[Integer] = None):
|
||||
"""
|
||||
Use this method to send a group of photos or videos as an album.
|
||||
|
|
@ -966,6 +1060,8 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:type media: :obj:`typing.Union[types.MediaGroup, typing.List]`
|
||||
:param disable_notification: Sends the message silently. Users will receive a notification with no sound.
|
||||
:type disable_notification: :obj:`typing.Optional[base.Boolean]`
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: If the message is a reply, ID of the original message
|
||||
:type reply_to_message_id: :obj:`typing.Optional[base.Integer]`
|
||||
:return: On success, an array of the sent Messages is returned.
|
||||
|
|
@ -976,10 +1072,13 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
elif isinstance(media, list):
|
||||
# Convert list to MediaGroup
|
||||
media = types.MediaGroup(media)
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.media = media
|
||||
self.disable_notifications = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
|
||||
def prepare(self):
|
||||
|
|
@ -993,6 +1092,7 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'chat_id': self.chat_id,
|
||||
'media': media,
|
||||
'disable_notifications': self.disable_notifications,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id
|
||||
}
|
||||
|
||||
|
|
@ -1023,18 +1123,20 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
return self
|
||||
|
||||
|
||||
class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send location on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'latitude', 'longitude', 'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
__slots__ = ('chat_id', 'latitude', 'longitude', 'disable_notification',
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_LOCATION
|
||||
|
||||
def __init__(self, chat_id: Union[Integer, String],
|
||||
latitude: Float, longitude: Float,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -1045,15 +1147,21 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param longitude: Float - Longitude of location
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -1063,18 +1171,21 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'latitude': self.latitude,
|
||||
'longitude': self.longitude,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send venue on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'latitude', 'longitude', 'title', 'address', 'foursquare_id',
|
||||
'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
__slots__ = ('chat_id', 'latitude', 'longitude', 'title',
|
||||
'address', 'foursquare_id',
|
||||
'disable_notification', 'protect_content',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_VENUE
|
||||
|
||||
|
|
@ -1085,6 +1196,7 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
address: String,
|
||||
foursquare_id: Optional[String] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -1098,11 +1210,16 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param foursquare_id: String (Optional) - Foursquare identifier of the venue
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
|
|
@ -1110,6 +1227,7 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
self.address = address
|
||||
self.foursquare_id = foursquare_id
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -1122,18 +1240,19 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'address': self.address,
|
||||
'foursquare_id': self.foursquare_id,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
||||
|
||||
class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send contact on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'phone_number', 'first_name', 'last_name', 'disable_notification',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_CONTACT
|
||||
|
||||
|
|
@ -1142,6 +1261,7 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
first_name: String,
|
||||
last_name: Optional[String] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[Union[
|
||||
types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None):
|
||||
|
|
@ -1153,16 +1273,22 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param last_name: String (Optional) - Contact's last name
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional)
|
||||
- Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.phone_number = phone_number
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -1173,6 +1299,7 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'first_name': self.first_name,
|
||||
'last_name': self.last_name,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
|
@ -1730,18 +1857,20 @@ class DeleteMessage(BaseResponse):
|
|||
}
|
||||
|
||||
|
||||
class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send sticker on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'sticker', 'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
__slots__ = ('chat_id', 'sticker', 'disable_notification', 'protect_content',
|
||||
'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_STICKER
|
||||
|
||||
def __init__(self, chat_id: Union[Integer, String],
|
||||
sticker: String,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[
|
||||
Union[types.InlineKeyboardMarkup,
|
||||
|
|
@ -1755,14 +1884,20 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
or upload a new one using multipart/form-data. More info on Sending Files »
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] (Optional) -
|
||||
Additional interface options. A JSON-serialized object for an inline keyboard,
|
||||
custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.sticker = sticker
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -1771,6 +1906,7 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'chat_id': self.chat_id,
|
||||
'sticker': self.sticker,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
|
@ -1985,7 +2121,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'currency', 'prices', 'photo_url', 'photo_size', 'photo_width', 'photo_height',
|
||||
'need_name', 'need_phone_number', 'need_email', 'need_shipping_address',
|
||||
'send_phone_number_to_provider', 'send_email_to_provider', 'is_flexible',
|
||||
'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
'disable_notification', 'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_INVOICE
|
||||
|
||||
|
|
@ -2009,6 +2145,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
send_email_to_provider: Optional[Boolean] = None,
|
||||
is_flexible: Optional[Boolean] = None,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[types.InlineKeyboardMarkup] = None):
|
||||
"""
|
||||
|
|
@ -2042,6 +2179,8 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
:param is_flexible: Boolean (Optional) - Pass True, if the final price depends on the shipping method
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: types.InlineKeyboardMarkup (Optional) - A JSON-serialized object for an inline keyboard.
|
||||
If empty, one 'Pay total price' button will be shown. If not empty, the first button must be a Pay button.
|
||||
|
|
@ -2066,6 +2205,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
self.send_email_to_provider = send_email_to_provider
|
||||
self.is_flexible = is_flexible
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -2091,6 +2231,7 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'send_email_to_provider': self.send_email_to_provider,
|
||||
'is_flexible': self.is_flexible,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
|
@ -2168,18 +2309,20 @@ class AnswerPreCheckoutQuery(BaseResponse):
|
|||
}
|
||||
|
||||
|
||||
class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
||||
class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin, ProtectContentMixin):
|
||||
"""
|
||||
Use that response type for send game on to webhook.
|
||||
"""
|
||||
|
||||
__slots__ = ('chat_id', 'game_short_name', 'disable_notification', 'reply_to_message_id', 'reply_markup')
|
||||
__slots__ = ('chat_id', 'game_short_name', 'disable_notification',
|
||||
'protect_content', 'reply_to_message_id', 'reply_markup')
|
||||
|
||||
method = api.Methods.SEND_GAME
|
||||
|
||||
def __init__(self, chat_id: Integer,
|
||||
game_short_name: String,
|
||||
disable_notification: Optional[Boolean] = None,
|
||||
protect_content: Optional[Boolean] = None,
|
||||
reply_to_message_id: Optional[Integer] = None,
|
||||
reply_markup: Optional[types.InlineKeyboardMarkup] = None):
|
||||
"""
|
||||
|
|
@ -2188,13 +2331,19 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
Set up your games via Botfather.
|
||||
:param disable_notification: Boolean (Optional) - Sends the message silently.
|
||||
Users will receive a notification with no sound.
|
||||
:param protect_content: Boolean (Optional) - Protects the contents of sent messages
|
||||
from forwarding and saving
|
||||
:param reply_to_message_id: Integer (Optional) - If the message is a reply, ID of the original message
|
||||
:param reply_markup: types.InlineKeyboardMarkup (Optional) - A JSON-serialized object for an inline keyboard.
|
||||
If empty, one ‘Play game_title’ button will be shown. If not empty, the first button must launch the game.
|
||||
"""
|
||||
if protect_content is None:
|
||||
protect_content = self._global_protect_content()
|
||||
|
||||
self.chat_id = chat_id
|
||||
self.game_short_name = game_short_name
|
||||
self.disable_notification = disable_notification
|
||||
self.protect_content = protect_content
|
||||
self.reply_to_message_id = reply_to_message_id
|
||||
self.reply_markup = reply_markup
|
||||
|
||||
|
|
@ -2203,6 +2352,7 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin):
|
|||
'chat_id': self.chat_id,
|
||||
'game_short_name': self.game_short_name,
|
||||
'disable_notification': self.disable_notification,
|
||||
'protect_content': self.protect_content,
|
||||
'reply_to_message_id': self.reply_to_message_id,
|
||||
'reply_markup': prepare_arg(self.reply_markup),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ from .bot_command_scope import BotCommandScope, BotCommandScopeAllChatAdministra
|
|||
from .callback_game import CallbackGame
|
||||
from .callback_query import CallbackQuery
|
||||
from .chat import Chat, ChatActions, ChatType
|
||||
from .chat_administrator_rights import ChatAdministratorRights
|
||||
from .chat_invite_link import ChatInviteLink
|
||||
from .chat_join_request import ChatJoinRequest
|
||||
from .chat_location import ChatLocation
|
||||
|
|
@ -48,6 +49,7 @@ from .labeled_price import LabeledPrice
|
|||
from .location import Location
|
||||
from .login_url import LoginUrl
|
||||
from .mask_position import MaskPosition
|
||||
from .menu_button import MenuButton, MenuButtonCommands, MenuButtonWebApp, MenuButtonDefault
|
||||
from .message import ContentType, ContentTypes, Message, ParseMode
|
||||
from .message_auto_delete_timer_changed import MessageAutoDeleteTimerChanged
|
||||
from .message_entity import MessageEntity, MessageEntityType
|
||||
|
|
@ -64,6 +66,7 @@ from .pre_checkout_query import PreCheckoutQuery
|
|||
from .proximity_alert_triggered import ProximityAlertTriggered
|
||||
from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, KeyboardButtonPollType
|
||||
from .response_parameters import ResponseParameters
|
||||
from .sent_web_app_message import SentWebAppMessage
|
||||
from .shipping_address import ShippingAddress
|
||||
from .shipping_option import ShippingOption
|
||||
from .shipping_query import ShippingQuery
|
||||
|
|
@ -75,12 +78,18 @@ from .user import User
|
|||
from .user_profile_photos import UserProfilePhotos
|
||||
from .venue import Venue
|
||||
from .video import Video
|
||||
from .video_chat_ended import VideoChatEnded
|
||||
from .video_chat_participants_invited import VideoChatParticipantsInvited
|
||||
from .video_chat_scheduled import VideoChatScheduled
|
||||
from .video_chat_started import VideoChatStarted
|
||||
from .video_note import VideoNote
|
||||
from .voice import Voice
|
||||
from .voice_chat_ended import VoiceChatEnded
|
||||
from .voice_chat_participants_invited import VoiceChatParticipantsInvited
|
||||
from .voice_chat_scheduled import VoiceChatScheduled
|
||||
from .voice_chat_started import VoiceChatStarted
|
||||
from .web_app_data import WebAppData
|
||||
from .web_app_info import WebAppInfo
|
||||
from .webhook_info import WebhookInfo
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -102,6 +111,7 @@ __all__ = (
|
|||
'CallbackQuery',
|
||||
'Chat',
|
||||
'ChatActions',
|
||||
'ChatAdministratorRights',
|
||||
'ChatInviteLink',
|
||||
'ChatJoinRequest',
|
||||
'ChatLocation',
|
||||
|
|
@ -174,6 +184,10 @@ __all__ = (
|
|||
'Location',
|
||||
'LoginUrl',
|
||||
'MaskPosition',
|
||||
'MenuButton',
|
||||
'MenuButtonCommands',
|
||||
'MenuButtonWebApp',
|
||||
'MenuButtonDefault',
|
||||
'MediaGroup',
|
||||
'Message',
|
||||
'MessageAutoDeleteTimerChanged',
|
||||
|
|
@ -201,6 +215,7 @@ __all__ = (
|
|||
'ReplyKeyboardMarkup',
|
||||
'ReplyKeyboardRemove',
|
||||
'ResponseParameters',
|
||||
'SentWebAppMessage',
|
||||
'ShippingAddress',
|
||||
'ShippingOption',
|
||||
'ShippingQuery',
|
||||
|
|
@ -212,12 +227,18 @@ __all__ = (
|
|||
'UserProfilePhotos',
|
||||
'Venue',
|
||||
'Video',
|
||||
'VideoChatEnded',
|
||||
'VideoChatParticipantsInvited',
|
||||
'VideoChatScheduled',
|
||||
'VideoChatStarted',
|
||||
'VideoNote',
|
||||
'Voice',
|
||||
'VoiceChatEnded',
|
||||
'VoiceChatParticipantsInvited',
|
||||
'VoiceChatScheduled',
|
||||
'VoiceChatStarted',
|
||||
'WebAppData',
|
||||
'WebAppInfo',
|
||||
'WebhookInfo',
|
||||
'base',
|
||||
'fields',
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ from __future__ import annotations
|
|||
import io
|
||||
import logging
|
||||
import typing
|
||||
import warnings
|
||||
from typing import TypeVar
|
||||
|
||||
from babel.support import LazyProxy
|
||||
|
||||
from .fields import BaseField
|
||||
from ..utils import json
|
||||
from ..utils.exceptions import AIOGramWarning
|
||||
from ..utils.mixins import ContextInstanceMixin
|
||||
if typing.TYPE_CHECKING:
|
||||
from ..bot.bot import Bot
|
||||
|
|
@ -243,8 +245,10 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject):
|
|||
return self.props[key].set_value(self, value, self.conf.get('parent', self))
|
||||
self.values[key] = value
|
||||
|
||||
# Log warning when Telegram silently adds new Fields
|
||||
log.warning("Field '%s' doesn't exist in %s", key, self.__class__)
|
||||
# Show warning when Telegram silently adds new Fields
|
||||
warnings.warn(f"Bot API Field {key!r} is not defined in {self.__class__!r} class.\n"
|
||||
"Bot API has been updated. Check for updates at https://telegram.org/blog and "
|
||||
"https://github.com/aiogram/aiogram/releases", AIOGramWarning)
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ class Chat(base.TelegramObject):
|
|||
photo: ChatPhoto = fields.Field(base=ChatPhoto)
|
||||
bio: base.String = fields.Field()
|
||||
has_private_forwards: base.Boolean = fields.Field()
|
||||
has_restricted_voice_and_video_messages: base.Boolean = fields.Field()
|
||||
join_to_send_messages: base.Boolean = fields.Field()
|
||||
join_by_request: base.Boolean = fields.Field()
|
||||
description: base.String = fields.Field()
|
||||
invite_link: base.String = fields.Field()
|
||||
pinned_message: 'Message' = fields.Field(base='Message')
|
||||
|
|
@ -652,6 +655,7 @@ class ChatType(helper.Helper):
|
|||
"""
|
||||
List of chat types
|
||||
|
||||
:key: SENDER
|
||||
:key: PRIVATE
|
||||
:key: GROUP
|
||||
:key: SUPER_GROUP
|
||||
|
|
@ -661,6 +665,7 @@ class ChatType(helper.Helper):
|
|||
|
||||
mode = helper.HelperMode.lowercase
|
||||
|
||||
SENDER = helper.Item() # sender
|
||||
PRIVATE = helper.Item() # private
|
||||
GROUP = helper.Item() # group
|
||||
SUPERGROUP = helper.Item() # supergroup
|
||||
|
|
|
|||
46
aiogram/types/chat_administrator_rights.py
Normal file
46
aiogram/types/chat_administrator_rights.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
|
||||
|
||||
class ChatAdministratorRights(base.TelegramObject):
|
||||
"""
|
||||
Represents rights of an administrator in a chat.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#chatadministratorrights
|
||||
"""
|
||||
is_anonymous: base.Boolean = fields.Field()
|
||||
can_manage_chat: base.Boolean = fields.Field()
|
||||
can_delete_messages: base.Boolean = fields.Field()
|
||||
can_manage_video_chats: base.Boolean = fields.Field()
|
||||
can_restrict_members: base.Boolean = fields.Field()
|
||||
can_promote_members: base.Boolean = fields.Field()
|
||||
can_change_info: base.Boolean = fields.Field()
|
||||
can_invite_users: base.Boolean = fields.Field()
|
||||
can_post_messages: base.Boolean = fields.Field()
|
||||
can_edit_messages: base.Boolean = fields.Field()
|
||||
can_pin_messages: base.Boolean = fields.Field()
|
||||
|
||||
def __init__(self,
|
||||
is_anonymous: base.Boolean = None,
|
||||
can_manage_chat: base.Boolean = None,
|
||||
can_delete_messages: base.Boolean = None,
|
||||
can_manage_video_chats: base.Boolean = None,
|
||||
can_restrict_members: base.Boolean = None,
|
||||
can_promote_members: base.Boolean = None,
|
||||
can_change_info: base.Boolean = None,
|
||||
can_invite_users: base.Boolean = None,
|
||||
can_post_messages: base.Boolean = None,
|
||||
can_edit_messages: base.Boolean = None,
|
||||
can_pin_messages: base.Boolean = None):
|
||||
super(ChatAdministratorRights, self).__init__(
|
||||
is_anonymous=is_anonymous,
|
||||
can_manage_chat=can_manage_chat,
|
||||
can_delete_messages=can_delete_messages,
|
||||
can_manage_video_chats=can_manage_video_chats,
|
||||
can_restrict_members=can_restrict_members,
|
||||
can_promote_members=can_promote_members,
|
||||
can_change_info=can_change_info,
|
||||
can_invite_users=can_invite_users,
|
||||
can_post_messages=can_post_messages,
|
||||
can_edit_messages=can_edit_messages,
|
||||
can_pin_messages=can_pin_messages)
|
||||
|
|
@ -5,7 +5,6 @@ from . import base, fields
|
|||
from .user import User
|
||||
from ..utils import helper
|
||||
|
||||
|
||||
T = typing.TypeVar('T')
|
||||
|
||||
|
||||
|
|
@ -153,6 +152,7 @@ class ChatMemberAdministrator(ChatMember):
|
|||
can_edit_messages: base.Boolean = fields.Field()
|
||||
can_delete_messages: base.Boolean = fields.Field()
|
||||
can_manage_voice_chats: base.Boolean = fields.Field()
|
||||
can_manage_video_chats: base.Boolean = fields.Field()
|
||||
can_restrict_members: base.Boolean = fields.Field()
|
||||
can_promote_members: base.Boolean = fields.Field()
|
||||
can_change_info: base.Boolean = fields.Field()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import abc
|
||||
import datetime
|
||||
import weakref
|
||||
import sys
|
||||
|
||||
__all__ = ('BaseField', 'Field', 'ListField', 'DateTimeField', 'TextField', 'ListOfLists', 'ConstField')
|
||||
|
||||
|
|
@ -168,8 +169,13 @@ class DateTimeField(Field):
|
|||
out: datetime
|
||||
"""
|
||||
|
||||
def serialize(self, value: datetime.datetime):
|
||||
return round(value.timestamp())
|
||||
if sys.platform == "win32":
|
||||
def serialize(self, value: datetime.datetime):
|
||||
return round((value - datetime.datetime(1970, 1, 1)).total_seconds())
|
||||
|
||||
else:
|
||||
def serialize(self, value: datetime.datetime):
|
||||
return round(value.timestamp())
|
||||
|
||||
def deserialize(self, value, parent=None):
|
||||
return datetime.datetime.fromtimestamp(value)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from . import base
|
|||
from . import fields
|
||||
from .callback_game import CallbackGame
|
||||
from .login_url import LoginUrl
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
class InlineKeyboardMarkup(base.TelegramObject):
|
||||
|
|
@ -95,6 +96,7 @@ class InlineKeyboardButton(base.TelegramObject):
|
|||
switch_inline_query_current_chat: base.String = fields.Field()
|
||||
callback_game: CallbackGame = fields.Field(base=CallbackGame)
|
||||
pay: base.Boolean = fields.Field()
|
||||
web_app: WebAppInfo = fields.Field(base=WebAppInfo)
|
||||
|
||||
def __init__(self, text: base.String,
|
||||
url: base.String = None,
|
||||
|
|
@ -103,7 +105,9 @@ class InlineKeyboardButton(base.TelegramObject):
|
|||
switch_inline_query: base.String = None,
|
||||
switch_inline_query_current_chat: base.String = None,
|
||||
callback_game: CallbackGame = None,
|
||||
pay: base.Boolean = None, **kwargs):
|
||||
pay: base.Boolean = None,
|
||||
web_app: WebAppInfo = None,
|
||||
**kwargs):
|
||||
super(InlineKeyboardButton, self).__init__(text=text,
|
||||
url=url,
|
||||
login_url=login_url,
|
||||
|
|
@ -111,4 +115,6 @@ class InlineKeyboardButton(base.TelegramObject):
|
|||
switch_inline_query=switch_inline_query,
|
||||
switch_inline_query_current_chat=switch_inline_query_current_chat,
|
||||
callback_game=callback_game,
|
||||
pay=pay, **kwargs)
|
||||
pay=pay,
|
||||
web_app=web_app,
|
||||
**kwargs)
|
||||
|
|
|
|||
86
aiogram/types/menu_button.py
Normal file
86
aiogram/types/menu_button.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import typing
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from .web_app_info import WebAppInfo
|
||||
from ..utils import helper
|
||||
from ..utils.helper import Item
|
||||
|
||||
|
||||
class MenuButton(base.TelegramObject):
|
||||
"""
|
||||
This object describes the bot's menu button in a private chat. It should be one of
|
||||
|
||||
- MenuButtonCommands
|
||||
- MenuButtonWebApp
|
||||
- MenuButtonDefault
|
||||
|
||||
If a menu button other than MenuButtonDefault is set for a private chat,
|
||||
then it is applied in the chat.
|
||||
Otherwise the default menu button is applied.
|
||||
By default, the menu button opens the list of bot commands.
|
||||
"""
|
||||
type: base.String = fields.Field(default='default')
|
||||
|
||||
@classmethod
|
||||
def resolve(cls, **kwargs) -> typing.Union[
|
||||
"MenuButtonCommands",
|
||||
"MenuButtonDefault",
|
||||
"MenuButtonWebApp",
|
||||
]:
|
||||
type_ = kwargs.get('type')
|
||||
mapping = {
|
||||
MenuButtonType.DEFAULT: MenuButtonDefault,
|
||||
MenuButtonType.COMMANDS: MenuButtonCommands,
|
||||
MenuButtonType.WEB_APP: MenuButtonWebApp,
|
||||
}
|
||||
class_ = mapping.get(type_)
|
||||
if not class_:
|
||||
raise ValueError(f'Unknown MenuButton type: {type_}')
|
||||
return class_(**kwargs)
|
||||
|
||||
|
||||
class MenuButtonCommands(MenuButton):
|
||||
"""
|
||||
Represents a menu button, which opens the bot's list of commands.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubuttoncommands
|
||||
"""
|
||||
type: base.String = fields.Field(default='commands')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(type='commands', **kwargs)
|
||||
|
||||
|
||||
class MenuButtonWebApp(MenuButton):
|
||||
"""
|
||||
Represents a menu button, which launches a Web App.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubuttonwebapp
|
||||
"""
|
||||
type: base.String = fields.Field(default='web_app')
|
||||
text: base.String = fields.Field()
|
||||
web_app: WebAppInfo = fields.Field(base=WebAppInfo)
|
||||
|
||||
def __init__(self, text: base.String, web_app: WebAppInfo, **kwargs):
|
||||
super().__init__(type='web_app', text=text, web_app=web_app, **kwargs)
|
||||
|
||||
|
||||
class MenuButtonDefault(MenuButton):
|
||||
"""
|
||||
Describes that no specific value for the menu button was set.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#menubuttondefault
|
||||
"""
|
||||
type: base.String = fields.Field(default='default')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(type='default', **kwargs)
|
||||
|
||||
|
||||
class MenuButtonType(helper.Helper):
|
||||
mode = helper.HelperMode.lowercase
|
||||
|
||||
DEFAULT = Item()
|
||||
COMMANDS = Item()
|
||||
WEB_APP = Item()
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,9 +1,9 @@
|
|||
import sys
|
||||
|
||||
from ..utils import helper, markdown
|
||||
from ..utils.deprecated import deprecated
|
||||
from . import base, fields
|
||||
from .user import User
|
||||
from ..utils import helper, markdown
|
||||
from ..utils.deprecated import deprecated
|
||||
|
||||
|
||||
class MessageEntity(base.TelegramObject):
|
||||
|
|
@ -19,16 +19,18 @@ class MessageEntity(base.TelegramObject):
|
|||
url: base.String = fields.Field()
|
||||
user: User = fields.Field(base=User)
|
||||
language: base.String = fields.Field()
|
||||
custom_emoji_id: base.String = fields.Field()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: base.String,
|
||||
offset: base.Integer,
|
||||
length: base.Integer,
|
||||
url: base.String = None,
|
||||
user: User = None,
|
||||
language: base.String = None,
|
||||
**kwargs
|
||||
self,
|
||||
type: base.String,
|
||||
offset: base.Integer,
|
||||
length: base.Integer,
|
||||
url: base.String = None,
|
||||
user: User = None,
|
||||
language: base.String = None,
|
||||
custom_emoji_id: base.String = None,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
type=type,
|
||||
|
|
@ -37,6 +39,7 @@ class MessageEntity(base.TelegramObject):
|
|||
url=url,
|
||||
user=user,
|
||||
language=language,
|
||||
custom_emoji_id=custom_emoji_id,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
|
@ -94,6 +97,8 @@ class MessageEntity(base.TelegramObject):
|
|||
return method(entity_text, self.url)
|
||||
if self.type == MessageEntityType.TEXT_MENTION and self.user:
|
||||
return self.user.get_mention(entity_text, as_html=as_html)
|
||||
if self.type == MessageEntityType.CUSTOM_EMOJI and self.user:
|
||||
return entity_text
|
||||
|
||||
return entity_text
|
||||
|
||||
|
|
@ -118,6 +123,7 @@ class MessageEntityType(helper.Helper):
|
|||
:key: PRE
|
||||
:key: TEXT_LINK
|
||||
:key: TEXT_MENTION
|
||||
:key: CUSTOM_EMOJI
|
||||
"""
|
||||
|
||||
mode = helper.HelperMode.snake_case
|
||||
|
|
@ -138,3 +144,4 @@ class MessageEntityType(helper.Helper):
|
|||
PRE = helper.Item() # pre - monowidth block
|
||||
TEXT_LINK = helper.Item() # text_link - for clickable text URLs
|
||||
TEXT_MENTION = helper.Item() # text_mention - for users without usernames
|
||||
CUSTOM_EMOJI = helper.Item() # custom_emoji
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import typing
|
|||
|
||||
from . import base
|
||||
from . import fields
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
class KeyboardButtonPollType(base.TelegramObject):
|
||||
|
|
@ -117,16 +118,19 @@ class KeyboardButton(base.TelegramObject):
|
|||
request_contact: base.Boolean = fields.Field()
|
||||
request_location: base.Boolean = fields.Field()
|
||||
request_poll: KeyboardButtonPollType = fields.Field()
|
||||
web_app: WebAppInfo = fields.Field(base=WebAppInfo)
|
||||
|
||||
def __init__(self, text: base.String,
|
||||
request_contact: base.Boolean = None,
|
||||
request_location: base.Boolean = None,
|
||||
request_poll: KeyboardButtonPollType = None,
|
||||
web_app: WebAppInfo = None,
|
||||
**kwargs):
|
||||
super(KeyboardButton, self).__init__(text=text,
|
||||
request_contact=request_contact,
|
||||
request_location=request_location,
|
||||
request_poll=request_poll,
|
||||
web_app=web_app,
|
||||
**kwargs)
|
||||
|
||||
|
||||
|
|
|
|||
11
aiogram/types/sent_web_app_message.py
Normal file
11
aiogram/types/sent_web_app_message.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
|
||||
|
||||
class SentWebAppMessage(base.TelegramObject):
|
||||
"""
|
||||
Contains information about an inline message sent by a Web App on behalf of a user.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#sentwebappmessage
|
||||
"""
|
||||
inline_message_id: base.String = fields.Field()
|
||||
|
|
@ -3,6 +3,7 @@ from . import fields
|
|||
from . import mixins
|
||||
from .mask_position import MaskPosition
|
||||
from .photo_size import PhotoSize
|
||||
from .file import File
|
||||
|
||||
|
||||
class Sticker(base.TelegramObject, mixins.Downloadable):
|
||||
|
|
@ -13,6 +14,7 @@ class Sticker(base.TelegramObject, mixins.Downloadable):
|
|||
"""
|
||||
file_id: base.String = fields.Field()
|
||||
file_unique_id: base.String = fields.Field()
|
||||
type: base.String = fields.Field()
|
||||
width: base.Integer = fields.Field()
|
||||
height: base.Integer = fields.Field()
|
||||
is_animated: base.Boolean = fields.Field()
|
||||
|
|
@ -20,7 +22,9 @@ class Sticker(base.TelegramObject, mixins.Downloadable):
|
|||
thumb: PhotoSize = fields.Field(base=PhotoSize)
|
||||
emoji: base.String = fields.Field()
|
||||
set_name: base.String = fields.Field()
|
||||
premium_animation: File = fields.Field(base=File)
|
||||
mask_position: MaskPosition = fields.Field(base=MaskPosition)
|
||||
custom_emoji_id: base.String = fields.Field()
|
||||
file_size: base.Integer = fields.Field()
|
||||
|
||||
async def set_position_in_set(self, position: base.Integer) -> base.Boolean:
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ class StickerSet(base.TelegramObject):
|
|||
"""
|
||||
name: base.String = fields.Field()
|
||||
title: base.String = fields.Field()
|
||||
sticker_type: base.String = fields.Field()
|
||||
is_animated: base.Boolean = fields.Field()
|
||||
is_video: base.Boolean = fields.Field()
|
||||
contains_masks: base.Boolean = fields.Field()
|
||||
contains_masks: base.Boolean = fields.Field() # Deprecated
|
||||
stickers: typing.List[Sticker] = fields.ListField(base=Sticker)
|
||||
thumb: PhotoSize = fields.Field(base=PhotoSize)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ class User(base.TelegramObject):
|
|||
last_name: base.String = fields.Field()
|
||||
username: base.String = fields.Field()
|
||||
language_code: base.String = fields.Field()
|
||||
is_premium: base.Boolean = fields.Field()
|
||||
added_to_attachment_menu: base.Boolean = fields.Field()
|
||||
can_join_groups: base.Boolean = fields.Field()
|
||||
can_read_all_group_messages: base.Boolean = fields.Field()
|
||||
supports_inline_queries: base.Boolean = fields.Field()
|
||||
|
|
|
|||
13
aiogram/types/video_chat_ended.py
Normal file
13
aiogram/types/video_chat_ended.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
from . import mixins
|
||||
|
||||
|
||||
class VideoChatEnded(base.TelegramObject, mixins.Downloadable):
|
||||
"""
|
||||
This object represents a service message about a video chat scheduled in the chat.
|
||||
|
||||
https://core.telegram.org/bots/api#videochatended
|
||||
"""
|
||||
|
||||
duration: base.Integer = fields.Field()
|
||||
16
aiogram/types/video_chat_participants_invited.py
Normal file
16
aiogram/types/video_chat_participants_invited.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import typing
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
from . import mixins
|
||||
from .user import User
|
||||
|
||||
|
||||
class VideoChatParticipantsInvited(base.TelegramObject, mixins.Downloadable):
|
||||
"""
|
||||
This object represents a service message about new members invited to a video chat.
|
||||
|
||||
https://core.telegram.org/bots/api#videochatparticipantsinvited
|
||||
"""
|
||||
|
||||
users: typing.List[User] = fields.ListField(base=User)
|
||||
14
aiogram/types/video_chat_scheduled.py
Normal file
14
aiogram/types/video_chat_scheduled.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from datetime import datetime
|
||||
|
||||
from . import base
|
||||
from . import fields
|
||||
|
||||
|
||||
class VideoChatScheduled(base.TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a video chat scheduled in the chat.
|
||||
|
||||
https://core.telegram.org/bots/api#videochatscheduled
|
||||
"""
|
||||
|
||||
start_date: datetime = fields.DateTimeField()
|
||||
11
aiogram/types/video_chat_started.py
Normal file
11
aiogram/types/video_chat_started.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from . import base
|
||||
from . import mixins
|
||||
|
||||
|
||||
class VideoChatStarted(base.TelegramObject, mixins.Downloadable):
|
||||
"""
|
||||
his object represents a service message about a video chat started in the chat. Currently holds no information.
|
||||
|
||||
https://core.telegram.org/bots/api#videochatstarted
|
||||
"""
|
||||
pass
|
||||
12
aiogram/types/web_app_data.py
Normal file
12
aiogram/types/web_app_data.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
|
||||
|
||||
class WebAppData(base.TelegramObject):
|
||||
"""
|
||||
Contains data sent from a Web App to the bot.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#webappdata
|
||||
"""
|
||||
data: str = fields.Field()
|
||||
button_text: str = fields.Field()
|
||||
11
aiogram/types/web_app_info.py
Normal file
11
aiogram/types/web_app_info.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from . import base
|
||||
from . import fields
|
||||
|
||||
|
||||
class WebAppInfo(base.TelegramObject):
|
||||
"""
|
||||
Contains information about a Web App.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#webappinfo
|
||||
"""
|
||||
url: base.String = fields.Field()
|
||||
|
|
@ -18,3 +18,4 @@ class WebhookInfo(base.TelegramObject):
|
|||
last_error_message: base.String = fields.Field()
|
||||
max_connections: base.Integer = fields.Field()
|
||||
allowed_updates: typing.List[base.String] = fields.ListField()
|
||||
last_synchronization_error_date: base.Integer = fields.DateTimeField()
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import warnings
|
||||
|
||||
try:
|
||||
import emoji
|
||||
except ImportError:
|
||||
|
|
@ -5,8 +7,14 @@ except ImportError:
|
|||
|
||||
|
||||
def emojize(text):
|
||||
warnings.warn(message="'aiogram.utils.emoji' module deprecated, use emoji symbols directly instead,"
|
||||
"this function will be removed in aiogram v2.22",
|
||||
category=DeprecationWarning, stacklevel=2)
|
||||
return emoji.emojize(text, use_aliases=True)
|
||||
|
||||
|
||||
def demojize(text):
|
||||
warnings.warn(message="'aiogram.utils.emoji' module deprecated, use emoji symbols directly instead,"
|
||||
"this function will be removed in aiogram v2.22",
|
||||
category=DeprecationWarning, stacklevel=2)
|
||||
return emoji.demojize(text)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ class TextDecoration(ABC):
|
|||
return self.link(value=text, link=f"tg://user?id={user.id}")
|
||||
if entity.type == "text_link":
|
||||
return self.link(value=text, link=cast(str, entity.url))
|
||||
if entity.type == "custom_emoji":
|
||||
return self.custom_emoji(value=text, custom_emoji_id=entity.custom_emoji_id)
|
||||
|
||||
return self.quote(text)
|
||||
|
||||
|
|
@ -143,6 +145,10 @@ class TextDecoration(ABC):
|
|||
def quote(self, value: str) -> str: # pragma: no cover
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def custom_emoji(self, value: str, custom_emoji_id: str) -> str: # pragma: no cover
|
||||
pass
|
||||
|
||||
|
||||
class HtmlDecoration(TextDecoration):
|
||||
def link(self, value: str, link: str) -> str:
|
||||
|
|
@ -175,6 +181,9 @@ class HtmlDecoration(TextDecoration):
|
|||
def quote(self, value: str) -> str:
|
||||
return html.escape(value, quote=False)
|
||||
|
||||
def custom_emoji(self, value: str, custom_emoji_id: str) -> str:
|
||||
return f'<tg-emoji emoji-id="{custom_emoji_id}">{value}</tg-emoji>'
|
||||
|
||||
|
||||
class MarkdownDecoration(TextDecoration):
|
||||
MARKDOWN_QUOTE_PATTERN: Pattern[str] = re.compile(r"([_*\[\]()~`>#+\-=|{}.!\\])")
|
||||
|
|
@ -209,6 +218,9 @@ class MarkdownDecoration(TextDecoration):
|
|||
def quote(self, value: str) -> str:
|
||||
return re.sub(pattern=self.MARKDOWN_QUOTE_PATTERN, repl=r"\\\1", string=value)
|
||||
|
||||
def custom_emoji(self, value: str, custom_emoji_id: str) -> str:
|
||||
return self.link(value=value, link=f"tg://emoji?id={custom_emoji_id}")
|
||||
|
||||
|
||||
html_decoration = HtmlDecoration()
|
||||
markdown_decoration = MarkdownDecoration()
|
||||
|
|
|
|||
67
aiogram/utils/web_app.py
Normal file
67
aiogram/utils/web_app.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import hashlib
|
||||
import hmac
|
||||
from operator import itemgetter
|
||||
from typing import Callable, Any, Dict
|
||||
from urllib.parse import parse_qsl
|
||||
|
||||
|
||||
def check_webapp_signature(token: str, init_data: str) -> bool:
|
||||
"""
|
||||
Check incoming WebApp init data signature
|
||||
|
||||
Source: https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
|
||||
|
||||
:param token:
|
||||
:param init_data:
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
parsed_data = dict(parse_qsl(init_data))
|
||||
except ValueError:
|
||||
# Init data is not a valid query string
|
||||
return False
|
||||
if "hash" not in parsed_data:
|
||||
# Hash is not present in init data
|
||||
return False
|
||||
|
||||
hash_ = parsed_data.pop('hash')
|
||||
data_check_string = "\n".join(
|
||||
f"{k}={v}" for k, v in sorted(parsed_data.items(), key=itemgetter(0))
|
||||
)
|
||||
secret_key = hmac.new(
|
||||
key=b"WebAppData", msg=token.encode(), digestmod=hashlib.sha256
|
||||
)
|
||||
calculated_hash = hmac.new(
|
||||
key=secret_key.digest(), msg=data_check_string.encode(), digestmod=hashlib.sha256
|
||||
).hexdigest()
|
||||
return calculated_hash == hash_
|
||||
|
||||
|
||||
def parse_init_data(init_data: str, _loads: Callable[..., Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Parse WebApp init data and return it as dict
|
||||
|
||||
:param init_data:
|
||||
:param _loads:
|
||||
:return:
|
||||
"""
|
||||
result = {}
|
||||
for key, value in parse_qsl(init_data):
|
||||
if (value.startswith('[') and value.endswith(']')) or (value.startswith('{') and value.endswith('}')):
|
||||
value = _loads(value)
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
def safe_parse_webapp_init_data(token: str, init_data: str, _loads: Callable[..., Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Validate WebApp init data and return it as dict
|
||||
|
||||
:param token:
|
||||
:param init_data:
|
||||
:param _loads:
|
||||
:return:
|
||||
"""
|
||||
if check_webapp_signature(token, init_data):
|
||||
return parse_init_data(init_data, _loads)
|
||||
raise ValueError("Invalid init data signature")
|
||||
|
|
@ -22,7 +22,7 @@ Welcome to aiogram's documentation!
|
|||
:target: https://pypi.python.org/pypi/aiogram
|
||||
:alt: Supported python versions
|
||||
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.7-blue.svg?style=flat-square&logo=telegram
|
||||
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-6.2-blue.svg?style=flat-square&logo=telegram
|
||||
:target: https://core.telegram.org/bots/api
|
||||
:alt: Telegram Bot API
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Set
|
||||
from typing import Set, Union, Iterable
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
|
@ -6,9 +6,9 @@ import pytest
|
|||
from aiogram.dispatcher.filters.builtin import (
|
||||
Text,
|
||||
extract_chat_ids,
|
||||
ChatIDArgumentType, ForwardedMessageFilter, IDFilter,
|
||||
ChatIDArgumentType, ForwardedMessageFilter, IDFilter, Command,
|
||||
)
|
||||
from aiogram.types import Message
|
||||
from aiogram.types import Message, BotCommand
|
||||
from tests.types.dataset import MESSAGE, MESSAGE_FROM_CHANNEL
|
||||
|
||||
|
||||
|
|
@ -108,3 +108,42 @@ class TestIDFilter:
|
|||
filter = IDFilter(chat_id=message_from_channel.chat.id)
|
||||
|
||||
assert await filter.check(message_from_channel)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("command", [
|
||||
"/start",
|
||||
"/start some args",
|
||||
])
|
||||
@pytest.mark.parametrize("cmd_filter", [
|
||||
"start",
|
||||
("start",),
|
||||
BotCommand(command="start", description="my desc"),
|
||||
(BotCommand(command="start", description="bar"),),
|
||||
(BotCommand(command="start", description="foo"), "help"),
|
||||
])
|
||||
@pytest.mark.asyncio
|
||||
async def test_commands_filter(command: str, cmd_filter: Union[Iterable[Union[str, BotCommand]], str, BotCommand]):
|
||||
message_with_command = Message(**MESSAGE)
|
||||
message_with_command.text = command
|
||||
|
||||
start_filter = Command(commands=cmd_filter)
|
||||
|
||||
assert await start_filter.check(message_with_command)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_commands_filter_not_checked():
|
||||
message_with_command = Message(**MESSAGE)
|
||||
message_with_command.text = "/start"
|
||||
|
||||
start_filter = Command(commands=["help", BotCommand("about", "my desc")])
|
||||
|
||||
assert not await start_filter.check(message_with_command)
|
||||
|
||||
|
||||
def test_commands_filter_raises_error():
|
||||
with pytest.raises(ValueError):
|
||||
start_filter = Command(commands=42) # noqa
|
||||
with pytest.raises(ValueError):
|
||||
start_filter = Command(commands=[42]) # noqa
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue