1#!/usr/bin/env python 2# 3# A library that provides a Python interface to the Telegram Bot API 4# Copyright (C) 2015-2020 5# Leandro Toledo de Souza <devs@python-telegram-bot.org> 6# 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU Lesser Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU Lesser Public License for more details. 16# 17# You should have received a copy of the GNU Lesser Public License 18# along with this program. If not, see [http://www.gnu.org/licenses/]. 19# pylint: disable=C0112, C0103, W0221 20"""This module contains the Filters for use with the MessageHandler class.""" 21 22import re 23import warnings 24 25from abc import ABC, abstractmethod 26from threading import Lock 27from typing import ( 28 Dict, 29 FrozenSet, 30 List, 31 Match, 32 Optional, 33 Pattern, 34 Set, 35 Tuple, 36 Union, 37 cast, 38 NoReturn, 39) 40 41from telegram import Chat, Message, MessageEntity, Update, User 42 43__all__ = [ 44 'Filters', 45 'BaseFilter', 46 'MessageFilter', 47 'UpdateFilter', 48 'InvertedFilter', 49 'MergedFilter', 50 'XORFilter', 51] 52 53from telegram.utils.deprecate import TelegramDeprecationWarning 54from telegram.utils.types import SLT 55 56 57class BaseFilter(ABC): 58 """Base class for all Filters. 59 60 Filters subclassing from this class can combined using bitwise operators: 61 62 And: 63 64 >>> (Filters.text & Filters.entity(MENTION)) 65 66 Or: 67 68 >>> (Filters.audio | Filters.video) 69 70 Exclusive Or: 71 72 >>> (Filters.regex('To Be') ^ Filters.regex('Not 2B')) 73 74 Not: 75 76 >>> ~ Filters.command 77 78 Also works with more than two filters: 79 80 >>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK))) 81 >>> Filters.text & (~ Filters.forwarded) 82 83 Note: 84 Filters use the same short circuiting logic as python's `and`, `or` and `not`. 85 This means that for example: 86 87 >>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)') 88 89 With ``message.text == x``, will only ever return the matches for the first filter, 90 since the second one is never evaluated. 91 92 93 If you want to create your own filters create a class inheriting from either 94 :class:`MessageFilter` or :class:`UpdateFilter` and implement a :meth:``filter`` method that 95 returns a boolean: :obj:`True` if the message should be 96 handled, :obj:`False` otherwise. 97 Note that the filters work only as class instances, not 98 actual class objects (so remember to 99 initialize your filter classes). 100 101 By default the filters name (what will get printed when converted to a string for display) 102 will be the class name. If you want to overwrite this assign a better name to the :attr:`name` 103 class variable. 104 105 Attributes: 106 name (:obj:`str`): Name for this filter. Defaults to the type of filter. 107 data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should 108 return a dict with lists. The dict will be merged with 109 :class:`telegram.ext.CallbackContext`'s internal dict in most cases 110 (depends on the handler). 111 """ 112 113 _name = None 114 data_filter = False 115 116 @abstractmethod 117 def __call__(self, update: Update) -> Optional[Union[bool, Dict]]: 118 pass 119 120 def __and__(self, other: 'BaseFilter') -> 'BaseFilter': 121 return MergedFilter(self, and_filter=other) 122 123 def __or__(self, other: 'BaseFilter') -> 'BaseFilter': 124 return MergedFilter(self, or_filter=other) 125 126 def __xor__(self, other: 'BaseFilter') -> 'BaseFilter': 127 return XORFilter(self, other) 128 129 def __invert__(self) -> 'BaseFilter': 130 return InvertedFilter(self) 131 132 @property 133 def name(self) -> Optional[str]: 134 return self._name 135 136 @name.setter 137 def name(self, name: Optional[str]) -> None: 138 self._name = name 139 140 def __repr__(self) -> str: 141 # We do this here instead of in a __init__ so filter don't have to call __init__ or super() 142 if self.name is None: 143 self.name = self.__class__.__name__ 144 return self.name 145 146 147class MessageFilter(BaseFilter, ABC): 148 """Base class for all Message Filters. In contrast to :class:`UpdateFilter`, the object passed 149 to :meth:`filter` is ``update.effective_message``. 150 151 Please see :class:`telegram.ext.BaseFilter` for details on how to create custom filters. 152 153 Attributes: 154 name (:obj:`str`): Name for this filter. Defaults to the type of filter. 155 data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should 156 return a dict with lists. The dict will be merged with 157 :class:`telegram.ext.CallbackContext`'s internal dict in most cases 158 (depends on the handler). 159 160 """ 161 162 def __call__(self, update: Update) -> Optional[Union[bool, Dict]]: 163 return self.filter(update.effective_message) 164 165 @abstractmethod 166 def filter(self, message: Message) -> Optional[Union[bool, Dict]]: 167 """This method must be overwritten. 168 169 Args: 170 message (:class:`telegram.Message`): The message that is tested. 171 172 Returns: 173 :obj:`dict` or :obj:`bool` 174 175 """ 176 177 178class UpdateFilter(BaseFilter, ABC): 179 """Base class for all Update Filters. In contrast to :class:`UpdateFilter`, the object 180 passed to :meth:`filter` is ``update``, which allows to create filters like 181 :attr:`Filters.update.edited_message`. 182 183 Please see :class:`telegram.ext.BaseFilter` for details on how to create custom filters. 184 185 Attributes: 186 name (:obj:`str`): Name for this filter. Defaults to the type of filter. 187 data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should 188 return a dict with lists. The dict will be merged with 189 :class:`telegram.ext.CallbackContext`'s internal dict in most cases 190 (depends on the handler). 191 192 """ 193 194 def __call__(self, update: Update) -> Optional[Union[bool, Dict]]: 195 return self.filter(update) 196 197 @abstractmethod 198 def filter(self, update: Update) -> Optional[Union[bool, Dict]]: 199 """This method must be overwritten. 200 201 Args: 202 update (:class:`telegram.Update`): The update that is tested. 203 204 Returns: 205 :obj:`dict` or :obj:`bool`. 206 207 """ 208 209 210class InvertedFilter(UpdateFilter): 211 """Represents a filter that has been inverted. 212 213 Args: 214 f: The filter to invert. 215 216 """ 217 218 def __init__(self, f: BaseFilter): 219 self.f = f 220 221 def filter(self, update: Update) -> bool: 222 return not bool(self.f(update)) 223 224 @property 225 def name(self) -> str: 226 return f"<inverted {self.f}>" 227 228 @name.setter 229 def name(self, name: str) -> NoReturn: 230 raise RuntimeError('Cannot set name for InvertedFilter') 231 232 233class MergedFilter(UpdateFilter): 234 """Represents a filter consisting of two other filters. 235 236 Args: 237 base_filter: Filter 1 of the merged filter. 238 and_filter: Optional filter to "and" with base_filter. Mutually exclusive with or_filter. 239 or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter. 240 241 """ 242 243 def __init__( 244 self, base_filter: BaseFilter, and_filter: BaseFilter = None, or_filter: BaseFilter = None 245 ): 246 self.base_filter = base_filter 247 if self.base_filter.data_filter: 248 self.data_filter = True 249 self.and_filter = and_filter 250 if ( 251 self.and_filter 252 and not isinstance(self.and_filter, bool) 253 and self.and_filter.data_filter 254 ): 255 self.data_filter = True 256 self.or_filter = or_filter 257 if self.or_filter and not isinstance(self.and_filter, bool) and self.or_filter.data_filter: 258 self.data_filter = True 259 260 @staticmethod 261 def _merge(base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> Dict: 262 base = base_output if isinstance(base_output, dict) else {} 263 comp = comp_output if isinstance(comp_output, dict) else {} 264 for k in comp.keys(): 265 # Make sure comp values are lists 266 comp_value = comp[k] if isinstance(comp[k], list) else [] 267 try: 268 # If base is a list then merge 269 if isinstance(base[k], list): 270 base[k] += comp_value 271 else: 272 base[k] = [base[k]] + comp_value 273 except KeyError: 274 base[k] = comp_value 275 return base 276 277 def filter(self, update: Update) -> Union[bool, Dict]: # pylint: disable=R0911 278 base_output = self.base_filter(update) 279 # We need to check if the filters are data filters and if so return the merged data. 280 # If it's not a data filter or an or_filter but no matches return bool 281 if self.and_filter: 282 # And filter needs to short circuit if base is falsey 283 if base_output: 284 comp_output = self.and_filter(update) 285 if comp_output: 286 if self.data_filter: 287 merged = self._merge(base_output, comp_output) 288 if merged: 289 return merged 290 return True 291 elif self.or_filter: 292 # Or filter needs to short circuit if base is truthey 293 if base_output: 294 if self.data_filter: 295 return base_output 296 return True 297 298 comp_output = self.or_filter(update) 299 if comp_output: 300 if self.data_filter: 301 return comp_output 302 return True 303 return False 304 305 @property 306 def name(self) -> str: 307 return ( 308 f"<{self.base_filter} {'and' if self.and_filter else 'or'} " 309 f"{self.and_filter or self.or_filter}>" 310 ) 311 312 @name.setter 313 def name(self, name: str) -> NoReturn: 314 raise RuntimeError('Cannot set name for MergedFilter') 315 316 317class XORFilter(UpdateFilter): 318 """Convenience filter acting as wrapper for :class:`MergedFilter` representing the an XOR gate 319 for two filters 320 321 Args: 322 base_filter: Filter 1 of the merged filter. 323 xor_filter: Filter 2 of the merged filter. 324 325 """ 326 327 def __init__(self, base_filter: BaseFilter, xor_filter: BaseFilter): 328 self.base_filter = base_filter 329 self.xor_filter = xor_filter 330 self.merged_filter = (base_filter & ~xor_filter) | (~base_filter & xor_filter) 331 332 def filter(self, update: Update) -> Optional[Union[bool, Dict]]: 333 return self.merged_filter(update) 334 335 @property 336 def name(self) -> str: 337 return f'<{self.base_filter} xor {self.xor_filter}>' 338 339 @name.setter 340 def name(self, name: str) -> NoReturn: 341 raise RuntimeError('Cannot set name for XORFilter') 342 343 344class _DiceEmoji(MessageFilter): 345 def __init__(self, emoji: str = None, name: str = None): 346 self.name = f'Filters.dice.{name}' if name else 'Filters.dice' 347 self.emoji = emoji 348 349 class _DiceValues(MessageFilter): 350 def __init__( 351 self, 352 values: SLT[int], 353 name: str, 354 emoji: str = None, 355 ): 356 self.values = [values] if isinstance(values, int) else values 357 self.emoji = emoji 358 self.name = f'{name}({values})' 359 360 def filter(self, message: Message) -> bool: 361 if message.dice and message.dice.value in self.values: 362 if self.emoji: 363 return message.dice.emoji == self.emoji 364 return True 365 return False 366 367 def __call__( # type: ignore[override] 368 self, update: Union[Update, List[int], Tuple[int]] 369 ) -> Union[bool, '_DiceValues']: 370 if isinstance(update, Update): 371 return self.filter(update.effective_message) 372 return self._DiceValues(update, self.name, emoji=self.emoji) 373 374 def filter(self, message: Message) -> bool: 375 if bool(message.dice): 376 if self.emoji: 377 return message.dice.emoji == self.emoji 378 return True 379 return False 380 381 382class Filters: 383 """Predefined filters for use as the ``filter`` argument of 384 :class:`telegram.ext.MessageHandler`. 385 386 Examples: 387 Use ``MessageHandler(Filters.video, callback_method)`` to filter all video 388 messages. Use ``MessageHandler(Filters.contact, callback_method)`` for all contacts. etc. 389 390 """ 391 392 class _All(MessageFilter): 393 name = 'Filters.all' 394 395 def filter(self, message: Message) -> bool: 396 return True 397 398 all = _All() 399 """All Messages.""" 400 401 class _Text(MessageFilter): 402 name = 'Filters.text' 403 404 class _TextStrings(MessageFilter): 405 def __init__(self, strings: Union[List[str], Tuple[str]]): 406 self.strings = strings 407 self.name = f'Filters.text({strings})' 408 409 def filter(self, message: Message) -> bool: 410 if message.text: 411 return message.text in self.strings 412 return False 413 414 def __call__( # type: ignore[override] 415 self, update: Union[Update, List[str], Tuple[str]] 416 ) -> Union[bool, '_TextStrings']: 417 if isinstance(update, Update): 418 return self.filter(update.effective_message) 419 return self._TextStrings(update) 420 421 def filter(self, message: Message) -> bool: 422 return bool(message.text) 423 424 text = _Text() 425 """Text Messages. If a list of strings is passed, it filters messages to only allow those 426 whose text is appearing in the given list. 427 428 Examples: 429 To allow any text message, simply use 430 ``MessageHandler(Filters.text, callback_method)``. 431 432 A simple use case for passing a list is to allow only messages that were sent by a 433 custom :class:`telegram.ReplyKeyboardMarkup`:: 434 435 buttons = ['Start', 'Settings', 'Back'] 436 markup = ReplyKeyboardMarkup.from_column(buttons) 437 ... 438 MessageHandler(Filters.text(buttons), callback_method) 439 440 Note: 441 * Dice messages don't have text. If you want to filter either text or dice messages, use 442 ``Filters.text | Filters.dice``. 443 * Messages containing a command are accepted by this filter. Use 444 ``Filters.text & (~Filters.command)``, if you want to filter only text messages without 445 commands. 446 447 Args: 448 update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which messages to allow. Only 449 exact matches are allowed. If not specified, will allow any text message. 450 """ 451 452 class _Caption(MessageFilter): 453 name = 'Filters.caption' 454 455 class _CaptionStrings(MessageFilter): 456 def __init__(self, strings: Union[List[str], Tuple[str]]): 457 self.strings = strings 458 self.name = f'Filters.caption({strings})' 459 460 def filter(self, message: Message) -> bool: 461 if message.caption: 462 return message.caption in self.strings 463 return False 464 465 def __call__( # type: ignore[override] 466 self, update: Union[Update, List[str], Tuple[str]] 467 ) -> Union[bool, '_CaptionStrings']: 468 if isinstance(update, Update): 469 return self.filter(update.effective_message) 470 return self._CaptionStrings(update) 471 472 def filter(self, message: Message) -> bool: 473 return bool(message.caption) 474 475 caption = _Caption() 476 """Messages with a caption. If a list of strings is passed, it filters messages to only 477 allow those whose caption is appearing in the given list. 478 479 Examples: 480 ``MessageHandler(Filters.caption, callback_method)`` 481 482 Args: 483 update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which captions to allow. Only 484 exact matches are allowed. If not specified, will allow any message with a caption. 485 """ 486 487 class _Command(MessageFilter): 488 name = 'Filters.command' 489 490 class _CommandOnlyStart(MessageFilter): 491 def __init__(self, only_start: bool): 492 self.only_start = only_start 493 self.name = f'Filters.command({only_start})' 494 495 def filter(self, message: Message) -> bool: 496 return bool( 497 message.entities 498 and any([e.type == MessageEntity.BOT_COMMAND for e in message.entities]) 499 ) 500 501 def __call__( # type: ignore[override] 502 self, update: Union[bool, Update] 503 ) -> Union[bool, '_CommandOnlyStart']: 504 if isinstance(update, Update): 505 return self.filter(update.effective_message) 506 return self._CommandOnlyStart(update) 507 508 def filter(self, message: Message) -> bool: 509 return bool( 510 message.entities 511 and message.entities[0].type == MessageEntity.BOT_COMMAND 512 and message.entities[0].offset == 0 513 ) 514 515 command = _Command() 516 """ 517 Messages with a :attr:`telegram.MessageEntity.BOT_COMMAND`. By default only allows 518 messages `starting` with a bot command. Pass :obj:`False` to also allow messages that contain a 519 bot command `anywhere` in the text. 520 521 Examples:: 522 523 MessageHandler(Filters.command, command_at_start_callback) 524 MessageHandler(Filters.command(False), command_anywhere_callback) 525 526 Note: 527 ``Filters.text`` also accepts messages containing a command. 528 529 Args: 530 update (:obj:`bool`, optional): Whether to only allow messages that `start` with a bot 531 command. Defaults to :obj:`True`. 532 """ 533 534 class regex(MessageFilter): 535 """ 536 Filters updates by searching for an occurrence of ``pattern`` in the message text. 537 The ``re.search()`` function is used to determine whether an update should be filtered. 538 539 Refer to the documentation of the ``re`` module for more information. 540 541 To get the groups and groupdict matched, see :attr:`telegram.ext.CallbackContext.matches`. 542 543 Examples: 544 Use ``MessageHandler(Filters.regex(r'help'), callback)`` to capture all messages that 545 contain the word 'help'. You can also use 546 ``MessageHandler(Filters.regex(re.compile(r'help', re.IGNORECASE)), callback)`` if 547 you want your pattern to be case insensitive. This approach is recommended 548 if you need to specify flags on your pattern. 549 550 Note: 551 Filters use the same short circuiting logic as python's `and`, `or` and `not`. 552 This means that for example: 553 554 >>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)') 555 556 With a message.text of `x`, will only ever return the matches for the first filter, 557 since the second one is never evaluated. 558 559 Args: 560 pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. 561 """ 562 563 data_filter = True 564 565 def __init__(self, pattern: Union[str, Pattern]): 566 if isinstance(pattern, str): 567 pattern = re.compile(pattern) 568 pattern = cast(Pattern, pattern) 569 self.pattern: Pattern = pattern 570 self.name = f'Filters.regex({self.pattern})' 571 572 def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]: 573 """""" # remove method from docs 574 if message.text: 575 match = self.pattern.search(message.text) 576 if match: 577 return {'matches': [match]} 578 return {} 579 580 class caption_regex(MessageFilter): 581 """ 582 Filters updates by searching for an occurrence of ``pattern`` in the message caption. 583 584 This filter works similarly to :class:`Filters.regex`, with the only exception being that 585 it applies to the message caption instead of the text. 586 587 Examples: 588 Use ``MessageHandler(Filters.photo & Filters.caption_regex(r'help'), callback)`` 589 to capture all photos with caption containing the word 'help'. 590 591 Note: 592 This filter will not work on simple text messages, but only on media with caption. 593 594 Args: 595 pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. 596 """ 597 598 data_filter = True 599 600 def __init__(self, pattern: Union[str, Pattern]): 601 if isinstance(pattern, str): 602 pattern = re.compile(pattern) 603 pattern = cast(Pattern, pattern) 604 self.pattern: Pattern = pattern 605 self.name = f'Filters.caption_regex({self.pattern})' 606 607 def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]: 608 """""" # remove method from docs 609 if message.caption: 610 match = self.pattern.search(message.caption) 611 if match: 612 return {'matches': [match]} 613 return {} 614 615 class _Reply(MessageFilter): 616 name = 'Filters.reply' 617 618 def filter(self, message: Message) -> bool: 619 return bool(message.reply_to_message) 620 621 reply = _Reply() 622 """Messages that are a reply to another message.""" 623 624 class _Audio(MessageFilter): 625 name = 'Filters.audio' 626 627 def filter(self, message: Message) -> bool: 628 return bool(message.audio) 629 630 audio = _Audio() 631 """Messages that contain :class:`telegram.Audio`.""" 632 633 class _Document(MessageFilter): 634 name = 'Filters.document' 635 636 class category(MessageFilter): 637 """Filters documents by their category in the mime-type attribute. 638 639 Note: 640 This Filter only filters by the mime_type of the document, 641 it doesn't check the validity of the document. 642 The user can manipulate the mime-type of a message and 643 send media with wrong types that don't fit to this handler. 644 645 Example: 646 Filters.documents.category('audio/') returns :obj:`True` for all types 647 of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. 648 """ 649 650 def __init__(self, category: Optional[str]): 651 """Initialize the category you want to filter 652 653 Args: 654 category (str, optional): category of the media you want to filter""" 655 self.category = category 656 self.name = f"Filters.document.category('{self.category}')" 657 658 def filter(self, message: Message) -> bool: 659 """""" # remove method from docs 660 if message.document: 661 return message.document.mime_type.startswith(self.category) 662 return False 663 664 application = category('application/') 665 audio = category('audio/') 666 image = category('image/') 667 video = category('video/') 668 text = category('text/') 669 670 class mime_type(MessageFilter): 671 """This Filter filters documents by their mime-type attribute 672 673 Note: 674 This Filter only filters by the mime_type of the document, 675 it doesn't check the validity of document. 676 The user can manipulate the mime-type of a message and 677 send media with wrong types that don't fit to this handler. 678 679 Example: 680 ``Filters.documents.mime_type('audio/mpeg')`` filters all audio in mp3 format. 681 """ 682 683 def __init__(self, mimetype: Optional[str]): 684 """Initialize the category you want to filter 685 686 Args: 687 mimetype (str, optional): mime_type of the media you want to filter""" 688 self.mimetype = mimetype 689 self.name = f"Filters.document.mime_type('{self.mimetype}')" 690 691 def filter(self, message: Message) -> bool: 692 """""" # remove method from docs 693 if message.document: 694 return message.document.mime_type == self.mimetype 695 return False 696 697 apk = mime_type('application/vnd.android.package-archive') 698 doc = mime_type('application/msword') 699 docx = mime_type('application/vnd.openxmlformats-officedocument.wordprocessingml.document') 700 exe = mime_type('application/x-ms-dos-executable') 701 gif = mime_type('video/mp4') 702 jpg = mime_type('image/jpeg') 703 mp3 = mime_type('audio/mpeg') 704 pdf = mime_type('application/pdf') 705 py = mime_type('text/x-python') 706 svg = mime_type('image/svg+xml') 707 txt = mime_type('text/plain') 708 targz = mime_type('application/x-compressed-tar') 709 wav = mime_type('audio/x-wav') 710 xml = mime_type('application/xml') 711 zip = mime_type('application/zip') 712 713 class file_extension(MessageFilter): 714 """This filter filters documents by their file ending/extension. 715 716 Note: 717 * This Filter only filters by the file ending/extension of the document, 718 it doesn't check the validity of document. 719 * The user can manipulate the file extension of a document and 720 send media with wrong types that don't fit to this handler. 721 * Case insensitive by default, 722 you may change this with the flag ``case_sensitive=True``. 723 * Extension should be passed without leading dot 724 unless it's a part of the extension. 725 * Pass :obj:`None` to filter files with no extension, 726 i.e. without a dot in the filename. 727 728 Example: 729 * ``Filters.document.file_extension("jpg")`` 730 filters files with extension ``".jpg"``. 731 * ``Filters.document.file_extension(".jpg")`` 732 filters files with extension ``"..jpg"``. 733 * ``Filters.document.file_extension("Dockerfile", case_sensitive=True)`` 734 filters files with extension ``".Dockerfile"`` minding the case. 735 * ``Filters.document.file_extension(None)`` 736 filters files without a dot in the filename. 737 """ 738 739 def __init__(self, file_extension: Optional[str], case_sensitive: bool = False): 740 """Initialize the extension you want to filter. 741 742 Args: 743 file_extension (:obj:`str` | :obj:`None`): 744 media file extension you want to filter. 745 case_sensitive (:obj:bool, optional): 746 pass :obj:`True` to make the filter case sensitive. 747 Default: :obj:`False`. 748 """ 749 self.is_case_sensitive = case_sensitive 750 if file_extension is None: 751 self.file_extension = None 752 self.name = "Filters.document.file_extension(None)" 753 elif case_sensitive: 754 self.file_extension = f".{file_extension}" 755 self.name = ( 756 f"Filters.document.file_extension({file_extension!r}," 757 " case_sensitive=True)" 758 ) 759 else: 760 self.file_extension = f".{file_extension}".lower() 761 self.name = f"Filters.document.file_extension({file_extension.lower()!r})" 762 763 def filter(self, message: Message) -> bool: 764 """""" # remove method from docs 765 if message.document is None: 766 return False 767 if self.file_extension is None: 768 return "." not in message.document.file_name 769 if self.is_case_sensitive: 770 filename = message.document.file_name 771 else: 772 filename = message.document.file_name.lower() 773 return filename.endswith(self.file_extension) 774 775 def filter(self, message: Message) -> bool: 776 return bool(message.document) 777 778 document = _Document() 779 """ 780 Subset for messages containing a document/file. 781 782 Examples: 783 Use these filters like: ``Filters.document.mp3``, 784 ``Filters.document.mime_type("text/plain")`` etc. Or use just 785 ``Filters.document`` for all document messages. 786 787 Attributes: 788 category: Filters documents by their category in the mime-type attribute 789 790 Note: 791 This Filter only filters by the mime_type of the document, 792 it doesn't check the validity of the document. 793 The user can manipulate the mime-type of a message and 794 send media with wrong types that don't fit to this handler. 795 796 Example: 797 ``Filters.documents.category('audio/')`` filters all types 798 of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. 799 application: Same as ``Filters.document.category("application")``. 800 audio: Same as ``Filters.document.category("audio")``. 801 image: Same as ``Filters.document.category("image")``. 802 video: Same as ``Filters.document.category("video")``. 803 text: Same as ``Filters.document.category("text")``. 804 mime_type: Filters documents by their mime-type attribute 805 806 Note: 807 This Filter only filters by the mime_type of the document, 808 it doesn't check the validity of document. 809 810 The user can manipulate the mime-type of a message and 811 send media with wrong types that don't fit to this handler. 812 813 Example: 814 ``Filters.documents.mime_type('audio/mpeg')`` filters all audio in mp3 format. 815 apk: Same as ``Filters.document.mime_type("application/vnd.android.package-archive")``- 816 doc: Same as ``Filters.document.mime_type("application/msword")``- 817 docx: Same as ``Filters.document.mime_type("application/vnd.openxmlformats-\ 818officedocument.wordprocessingml.document")``- 819 exe: Same as ``Filters.document.mime_type("application/x-ms-dos-executable")``- 820 gif: Same as ``Filters.document.mime_type("video/mp4")``- 821 jpg: Same as ``Filters.document.mime_type("image/jpeg")``- 822 mp3: Same as ``Filters.document.mime_type("audio/mpeg")``- 823 pdf: Same as ``Filters.document.mime_type("application/pdf")``- 824 py: Same as ``Filters.document.mime_type("text/x-python")``- 825 svg: Same as ``Filters.document.mime_type("image/svg+xml")``- 826 txt: Same as ``Filters.document.mime_type("text/plain")``- 827 targz: Same as ``Filters.document.mime_type("application/x-compressed-tar")``- 828 wav: Same as ``Filters.document.mime_type("audio/x-wav")``- 829 xml: Same as ``Filters.document.mime_type("application/xml")``- 830 zip: Same as ``Filters.document.mime_type("application/zip")``- 831 file_extension: This filter filters documents by their file ending/extension. 832 833 Note: 834 * This Filter only filters by the file ending/extension of the document, 835 it doesn't check the validity of document. 836 * The user can manipulate the file extension of a document and 837 send media with wrong types that don't fit to this handler. 838 * Case insensitive by default, 839 you may change this with the flag ``case_sensitive=True``. 840 * Extension should be passed without leading dot 841 unless it's a part of the extension. 842 * Pass :obj:`None` to filter files with no extension, 843 i.e. without a dot in the filename. 844 845 Example: 846 * ``Filters.document.file_extension("jpg")`` 847 filters files with extension ``".jpg"``. 848 * ``Filters.document.file_extension(".jpg")`` 849 filters files with extension ``"..jpg"``. 850 * ``Filters.document.file_extension("Dockerfile", case_sensitive=True)`` 851 filters files with extension ``".Dockerfile"`` minding the case. 852 * ``Filters.document.file_extension(None)`` 853 filters files without a dot in the filename. 854 """ 855 856 class _Animation(MessageFilter): 857 name = 'Filters.animation' 858 859 def filter(self, message: Message) -> bool: 860 return bool(message.animation) 861 862 animation = _Animation() 863 """Messages that contain :class:`telegram.Animation`.""" 864 865 class _Photo(MessageFilter): 866 name = 'Filters.photo' 867 868 def filter(self, message: Message) -> bool: 869 return bool(message.photo) 870 871 photo = _Photo() 872 """Messages that contain :class:`telegram.PhotoSize`.""" 873 874 class _Sticker(MessageFilter): 875 name = 'Filters.sticker' 876 877 def filter(self, message: Message) -> bool: 878 return bool(message.sticker) 879 880 sticker = _Sticker() 881 """Messages that contain :class:`telegram.Sticker`.""" 882 883 class _Video(MessageFilter): 884 name = 'Filters.video' 885 886 def filter(self, message: Message) -> bool: 887 return bool(message.video) 888 889 video = _Video() 890 """Messages that contain :class:`telegram.Video`.""" 891 892 class _Voice(MessageFilter): 893 name = 'Filters.voice' 894 895 def filter(self, message: Message) -> bool: 896 return bool(message.voice) 897 898 voice = _Voice() 899 """Messages that contain :class:`telegram.Voice`.""" 900 901 class _VideoNote(MessageFilter): 902 name = 'Filters.video_note' 903 904 def filter(self, message: Message) -> bool: 905 return bool(message.video_note) 906 907 video_note = _VideoNote() 908 """Messages that contain :class:`telegram.VideoNote`.""" 909 910 class _Contact(MessageFilter): 911 name = 'Filters.contact' 912 913 def filter(self, message: Message) -> bool: 914 return bool(message.contact) 915 916 contact = _Contact() 917 """Messages that contain :class:`telegram.Contact`.""" 918 919 class _Location(MessageFilter): 920 name = 'Filters.location' 921 922 def filter(self, message: Message) -> bool: 923 return bool(message.location) 924 925 location = _Location() 926 """Messages that contain :class:`telegram.Location`.""" 927 928 class _Venue(MessageFilter): 929 name = 'Filters.venue' 930 931 def filter(self, message: Message) -> bool: 932 return bool(message.venue) 933 934 venue = _Venue() 935 """Messages that contain :class:`telegram.Venue`.""" 936 937 class _StatusUpdate(UpdateFilter): 938 """Subset for messages containing a status update. 939 940 Examples: 941 Use these filters like: ``Filters.status_update.new_chat_members`` etc. Or use just 942 ``Filters.status_update`` for all status update messages. 943 944 """ 945 946 class _NewChatMembers(MessageFilter): 947 name = 'Filters.status_update.new_chat_members' 948 949 def filter(self, message: Message) -> bool: 950 return bool(message.new_chat_members) 951 952 new_chat_members = _NewChatMembers() 953 """Messages that contain :attr:`telegram.Message.new_chat_members`.""" 954 955 class _LeftChatMember(MessageFilter): 956 name = 'Filters.status_update.left_chat_member' 957 958 def filter(self, message: Message) -> bool: 959 return bool(message.left_chat_member) 960 961 left_chat_member = _LeftChatMember() 962 """Messages that contain :attr:`telegram.Message.left_chat_member`.""" 963 964 class _NewChatTitle(MessageFilter): 965 name = 'Filters.status_update.new_chat_title' 966 967 def filter(self, message: Message) -> bool: 968 return bool(message.new_chat_title) 969 970 new_chat_title = _NewChatTitle() 971 """Messages that contain :attr:`telegram.Message.new_chat_title`.""" 972 973 class _NewChatPhoto(MessageFilter): 974 name = 'Filters.status_update.new_chat_photo' 975 976 def filter(self, message: Message) -> bool: 977 return bool(message.new_chat_photo) 978 979 new_chat_photo = _NewChatPhoto() 980 """Messages that contain :attr:`telegram.Message.new_chat_photo`.""" 981 982 class _DeleteChatPhoto(MessageFilter): 983 name = 'Filters.status_update.delete_chat_photo' 984 985 def filter(self, message: Message) -> bool: 986 return bool(message.delete_chat_photo) 987 988 delete_chat_photo = _DeleteChatPhoto() 989 """Messages that contain :attr:`telegram.Message.delete_chat_photo`.""" 990 991 class _ChatCreated(MessageFilter): 992 name = 'Filters.status_update.chat_created' 993 994 def filter(self, message: Message) -> bool: 995 return bool( 996 message.group_chat_created 997 or message.supergroup_chat_created 998 or message.channel_chat_created 999 ) 1000 1001 chat_created = _ChatCreated() 1002 """Messages that contain :attr:`telegram.Message.group_chat_created`, 1003 :attr: `telegram.Message.supergroup_chat_created` or 1004 :attr: `telegram.Message.channel_chat_created`.""" 1005 1006 class _Migrate(MessageFilter): 1007 name = 'Filters.status_update.migrate' 1008 1009 def filter(self, message: Message) -> bool: 1010 return bool(message.migrate_from_chat_id or message.migrate_to_chat_id) 1011 1012 migrate = _Migrate() 1013 """Messages that contain :attr:`telegram.Message.migrate_from_chat_id` or 1014 :attr:`telegram.Message.migrate_to_chat_id`.""" 1015 1016 class _PinnedMessage(MessageFilter): 1017 name = 'Filters.status_update.pinned_message' 1018 1019 def filter(self, message: Message) -> bool: 1020 return bool(message.pinned_message) 1021 1022 pinned_message = _PinnedMessage() 1023 """Messages that contain :attr:`telegram.Message.pinned_message`.""" 1024 1025 class _ConnectedWebsite(MessageFilter): 1026 name = 'Filters.status_update.connected_website' 1027 1028 def filter(self, message: Message) -> bool: 1029 return bool(message.connected_website) 1030 1031 connected_website = _ConnectedWebsite() 1032 """Messages that contain :attr:`telegram.Message.connected_website`.""" 1033 1034 class _ProximityAlertTriggered(MessageFilter): 1035 name = 'Filters.status_update.proximity_alert_triggered' 1036 1037 def filter(self, message: Message) -> bool: 1038 return bool(message.proximity_alert_triggered) 1039 1040 proximity_alert_triggered = _ProximityAlertTriggered() 1041 """Messages that contain :attr:`telegram.Message.proximity_alert_triggered`.""" 1042 1043 name = 'Filters.status_update' 1044 1045 def filter(self, message: Update) -> bool: 1046 return bool( 1047 self.new_chat_members(message) 1048 or self.left_chat_member(message) 1049 or self.new_chat_title(message) 1050 or self.new_chat_photo(message) 1051 or self.delete_chat_photo(message) 1052 or self.chat_created(message) 1053 or self.migrate(message) 1054 or self.pinned_message(message) 1055 or self.connected_website(message) 1056 or self.proximity_alert_triggered(message) 1057 ) 1058 1059 status_update = _StatusUpdate() 1060 """Subset for messages containing a status update. 1061 1062 Examples: 1063 Use these filters like: ``Filters.status_update.new_chat_members`` etc. Or use just 1064 ``Filters.status_update`` for all status update messages. 1065 1066 Attributes: 1067 chat_created: Messages that contain 1068 :attr:`telegram.Message.group_chat_created`, 1069 :attr:`telegram.Message.supergroup_chat_created` or 1070 :attr:`telegram.Message.channel_chat_created`. 1071 connected_website: Messages that contain 1072 :attr:`telegram.Message.connected_website`. 1073 delete_chat_photo: Messages that contain 1074 :attr:`telegram.Message.delete_chat_photo`. 1075 left_chat_member: Messages that contain 1076 :attr:`telegram.Message.left_chat_member`. 1077 migrate: Messages that contain 1078 :attr:`telegram.Message.migrate_from_chat_id` or 1079 :attr: `telegram.Message.migrate_from_chat_id`. 1080 new_chat_members: Messages that contain 1081 :attr:`telegram.Message.new_chat_members`. 1082 new_chat_photo: Messages that contain 1083 :attr:`telegram.Message.new_chat_photo`. 1084 new_chat_title: Messages that contain 1085 :attr:`telegram.Message.new_chat_title`. 1086 pinned_message: Messages that contain 1087 :attr:`telegram.Message.pinned_message`. 1088 proximity_alert_triggered: Messages that contain 1089 :attr:`telegram.Message.proximity_alert_triggered`. 1090 """ 1091 1092 class _Forwarded(MessageFilter): 1093 name = 'Filters.forwarded' 1094 1095 def filter(self, message: Message) -> bool: 1096 return bool(message.forward_date) 1097 1098 forwarded = _Forwarded() 1099 """Messages that are forwarded.""" 1100 1101 class _Game(MessageFilter): 1102 name = 'Filters.game' 1103 1104 def filter(self, message: Message) -> bool: 1105 return bool(message.game) 1106 1107 game = _Game() 1108 """Messages that contain :class:`telegram.Game`.""" 1109 1110 class entity(MessageFilter): 1111 """ 1112 Filters messages to only allow those which have a :class:`telegram.MessageEntity` 1113 where their `type` matches `entity_type`. 1114 1115 Examples: 1116 Example ``MessageHandler(Filters.entity("hashtag"), callback_method)`` 1117 1118 Args: 1119 entity_type: Entity type to check for. All types can be found as constants 1120 in :class:`telegram.MessageEntity`. 1121 1122 """ 1123 1124 def __init__(self, entity_type: str): 1125 self.entity_type = entity_type 1126 self.name = f'Filters.entity({self.entity_type})' 1127 1128 def filter(self, message: Message) -> bool: 1129 """""" # remove method from docs 1130 return any(entity.type == self.entity_type for entity in message.entities) 1131 1132 class caption_entity(MessageFilter): 1133 """ 1134 Filters media messages to only allow those which have a :class:`telegram.MessageEntity` 1135 where their `type` matches `entity_type`. 1136 1137 Examples: 1138 Example ``MessageHandler(Filters.caption_entity("hashtag"), callback_method)`` 1139 1140 Args: 1141 entity_type: Caption Entity type to check for. All types can be found as constants 1142 in :class:`telegram.MessageEntity`. 1143 1144 """ 1145 1146 def __init__(self, entity_type: str): 1147 self.entity_type = entity_type 1148 self.name = f'Filters.caption_entity({self.entity_type})' 1149 1150 def filter(self, message: Message) -> bool: 1151 """""" # remove method from docs 1152 return any(entity.type == self.entity_type for entity in message.caption_entities) 1153 1154 class _Private(MessageFilter): 1155 name = 'Filters.private' 1156 1157 def filter(self, message: Message) -> bool: 1158 warnings.warn( 1159 'Filters.private is deprecated. Use Filters.chat_type.private instead.', 1160 TelegramDeprecationWarning, 1161 stacklevel=2, 1162 ) 1163 return message.chat.type == Chat.PRIVATE 1164 1165 private = _Private() 1166 """ 1167 Messages sent in a private chat. 1168 1169 Note: 1170 DEPRECATED. Use 1171 :attr:`telegram.ext.Filters.chat_type.private` instead. 1172 """ 1173 1174 class _Group(MessageFilter): 1175 name = 'Filters.group' 1176 1177 def filter(self, message: Message) -> bool: 1178 warnings.warn( 1179 'Filters.group is deprecated. Use Filters.chat_type.groups instead.', 1180 TelegramDeprecationWarning, 1181 stacklevel=2, 1182 ) 1183 return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] 1184 1185 group = _Group() 1186 """ 1187 Messages sent in a group or a supergroup chat. 1188 1189 Note: 1190 DEPRECATED. Use 1191 :attr:`telegram.ext.Filters.chat_type.groups` instead. 1192 """ 1193 1194 class _ChatType(MessageFilter): 1195 name = 'Filters.chat_type' 1196 1197 class _Channel(MessageFilter): 1198 name = 'Filters.chat_type.channel' 1199 1200 def filter(self, message: Message) -> bool: 1201 return message.chat.type == Chat.CHANNEL 1202 1203 channel = _Channel() 1204 1205 class _Group(MessageFilter): 1206 name = 'Filters.chat_type.group' 1207 1208 def filter(self, message: Message) -> bool: 1209 return message.chat.type == Chat.GROUP 1210 1211 group = _Group() 1212 1213 class _SuperGroup(MessageFilter): 1214 name = 'Filters.chat_type.supergroup' 1215 1216 def filter(self, message: Message) -> bool: 1217 return message.chat.type == Chat.SUPERGROUP 1218 1219 supergroup = _SuperGroup() 1220 1221 class _Groups(MessageFilter): 1222 name = 'Filters.chat_type.groups' 1223 1224 def filter(self, message: Message) -> bool: 1225 return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] 1226 1227 groups = _Groups() 1228 1229 class _Private(MessageFilter): 1230 name = 'Filters.chat_type.private' 1231 1232 def filter(self, message: Message) -> bool: 1233 return message.chat.type == Chat.PRIVATE 1234 1235 private = _Private() 1236 1237 def filter(self, message: Message) -> bool: 1238 return bool(message.chat.type) 1239 1240 chat_type = _ChatType() 1241 """Subset for filtering the type of chat. 1242 1243 Examples: 1244 Use these filters like: ``Filters.chat_type.channel`` or 1245 ``Filters.chat_type.supergroup`` etc. Or use just ``Filters.chat_type`` for all 1246 chat types. 1247 1248 Attributes: 1249 channel: Updates from channel 1250 group: Updates from group 1251 supergroup: Updates from supergroup 1252 groups: Updates from group *or* supergroup 1253 private: Updates sent in private chat 1254 """ 1255 1256 class _ChatUserBaseFilter(MessageFilter): 1257 def __init__( 1258 self, 1259 chat_id: SLT[int] = None, 1260 username: SLT[str] = None, 1261 allow_empty: bool = False, 1262 ): 1263 self.chat_id_name = 'chat_id' 1264 self.username_name = 'username' 1265 self.allow_empty = allow_empty 1266 self.__lock = Lock() 1267 1268 self._chat_ids: Set[int] = set() 1269 self._usernames: Set[str] = set() 1270 1271 self._set_chat_ids(chat_id) 1272 self._set_usernames(username) 1273 1274 @abstractmethod 1275 def get_chat_or_user(self, message: Message) -> Union[Chat, User, None]: 1276 pass 1277 1278 @staticmethod 1279 def _parse_chat_id(chat_id: SLT[int]) -> Set[int]: 1280 if chat_id is None: 1281 return set() 1282 if isinstance(chat_id, int): 1283 return {chat_id} 1284 return set(chat_id) 1285 1286 @staticmethod 1287 def _parse_username(username: SLT[str]) -> Set[str]: 1288 if username is None: 1289 return set() 1290 if isinstance(username, str): 1291 return {username[1:] if username.startswith('@') else username} 1292 return {chat[1:] if chat.startswith('@') else chat for chat in username} 1293 1294 def _set_chat_ids(self, chat_id: SLT[int]) -> None: 1295 with self.__lock: 1296 if chat_id and self._usernames: 1297 raise RuntimeError( 1298 f"Can't set {self.chat_id_name} in conjunction with (already set) " 1299 f"{self.username_name}s." 1300 ) 1301 self._chat_ids = self._parse_chat_id(chat_id) 1302 1303 def _set_usernames(self, username: SLT[str]) -> None: 1304 with self.__lock: 1305 if username and self._chat_ids: 1306 raise RuntimeError( 1307 f"Can't set {self.username_name} in conjunction with (already set) " 1308 f"{self.chat_id_name}s." 1309 ) 1310 self._usernames = self._parse_username(username) 1311 1312 @property 1313 def chat_ids(self) -> FrozenSet[int]: 1314 with self.__lock: 1315 return frozenset(self._chat_ids) 1316 1317 @chat_ids.setter 1318 def chat_ids(self, chat_id: SLT[int]) -> None: 1319 self._set_chat_ids(chat_id) 1320 1321 @property 1322 def usernames(self) -> FrozenSet[str]: 1323 with self.__lock: 1324 return frozenset(self._usernames) 1325 1326 @usernames.setter 1327 def usernames(self, username: SLT[str]) -> None: 1328 self._set_usernames(username) 1329 1330 def add_usernames(self, username: SLT[str]) -> None: 1331 with self.__lock: 1332 if self._chat_ids: 1333 raise RuntimeError( 1334 f"Can't set {self.username_name} in conjunction with (already set) " 1335 f"{self.chat_id_name}s." 1336 ) 1337 1338 parsed_username = self._parse_username(username) 1339 self._usernames |= parsed_username 1340 1341 def add_chat_ids(self, chat_id: SLT[int]) -> None: 1342 with self.__lock: 1343 if self._usernames: 1344 raise RuntimeError( 1345 f"Can't set {self.chat_id_name} in conjunction with (already set) " 1346 f"{self.username_name}s." 1347 ) 1348 1349 parsed_chat_id = self._parse_chat_id(chat_id) 1350 1351 self._chat_ids |= parsed_chat_id 1352 1353 def remove_usernames(self, username: SLT[str]) -> None: 1354 with self.__lock: 1355 if self._chat_ids: 1356 raise RuntimeError( 1357 f"Can't set {self.username_name} in conjunction with (already set) " 1358 f"{self.chat_id_name}s." 1359 ) 1360 1361 parsed_username = self._parse_username(username) 1362 self._usernames -= parsed_username 1363 1364 def remove_chat_ids(self, chat_id: SLT[int]) -> None: 1365 with self.__lock: 1366 if self._usernames: 1367 raise RuntimeError( 1368 f"Can't set {self.chat_id_name} in conjunction with (already set) " 1369 f"{self.username_name}s." 1370 ) 1371 parsed_chat_id = self._parse_chat_id(chat_id) 1372 self._chat_ids -= parsed_chat_id 1373 1374 def filter(self, message: Message) -> bool: 1375 """""" # remove method from docs 1376 chat_or_user = self.get_chat_or_user(message) 1377 if chat_or_user: 1378 if self.chat_ids: 1379 return chat_or_user.id in self.chat_ids 1380 if self.usernames: 1381 return bool(chat_or_user.username and chat_or_user.username in self.usernames) 1382 return self.allow_empty 1383 return False 1384 1385 @property 1386 def name(self) -> str: 1387 return ( 1388 f'Filters.{self.__class__.__name__}(' 1389 f'{", ".join(str(s) for s in (self.usernames or self.chat_ids))})' 1390 ) 1391 1392 @name.setter 1393 def name(self, name: str) -> NoReturn: 1394 raise RuntimeError(f'Cannot set name for Filters.{self.__class__.__name__}') 1395 1396 class user(_ChatUserBaseFilter): 1397 # pylint: disable=W0235 1398 """Filters messages to allow only those which are from specified user ID(s) or 1399 username(s). 1400 1401 Examples: 1402 ``MessageHandler(Filters.user(1234), callback_method)`` 1403 1404 Warning: 1405 :attr:`user_ids` will give a *copy* of the saved user ids as :class:`frozenset`. This 1406 is to ensure thread safety. To add/remove a user, you should use :meth:`add_usernames`, 1407 :meth:`add_user_ids`, :meth:`remove_usernames` and :meth:`remove_user_ids`. Only update 1408 the entire set by ``filter.user_ids/usernames = new_set``, if you are entirely sure 1409 that it is not causing race conditions, as this will complete replace the current set 1410 of allowed users. 1411 1412 Attributes: 1413 user_ids(set(:obj:`int`), optional): Which user ID(s) to allow through. 1414 usernames(set(:obj:`str`), optional): Which username(s) (without leading '@') to allow 1415 through. 1416 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user 1417 is specified in :attr:`user_ids` and :attr:`usernames`. 1418 1419 Args: 1420 user_id(:class:`telegram.utils.types.SLT[int]`, optional): 1421 Which user ID(s) to allow through. 1422 username(:class:`telegram.utils.types.SLT[str]`, optional): 1423 Which username(s) to allow through. Leading '@'s in usernames will be discarded. 1424 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user 1425 is specified in :attr:`user_ids` and :attr:`usernames`. Defaults to :obj:`False` 1426 1427 Raises: 1428 RuntimeError: If user_id and username are both present. 1429 1430 """ 1431 1432 def __init__( 1433 self, 1434 user_id: SLT[int] = None, 1435 username: SLT[str] = None, 1436 allow_empty: bool = False, 1437 ): 1438 super().__init__(chat_id=user_id, username=username, allow_empty=allow_empty) 1439 self.chat_id_name = 'user_id' 1440 1441 def get_chat_or_user(self, message: Message) -> Optional[User]: 1442 return message.from_user 1443 1444 @property 1445 def user_ids(self) -> FrozenSet[int]: 1446 return self.chat_ids 1447 1448 @user_ids.setter 1449 def user_ids(self, user_id: SLT[int]) -> None: 1450 self.chat_ids = user_id # type: ignore[assignment] 1451 1452 def add_usernames(self, username: SLT[str]) -> None: 1453 """ 1454 Add one or more users to the allowed usernames. 1455 1456 Args: 1457 username(:class:`telegram.utils.types.SLT[str]`, optional): 1458 Which username(s) to allow through. 1459 Leading '@'s in usernames will be discarded. 1460 """ 1461 return super().add_usernames(username) 1462 1463 def add_user_ids(self, user_id: SLT[int]) -> None: 1464 """ 1465 Add one or more users to the allowed user ids. 1466 1467 Args: 1468 user_id(:class:`telegram.utils.types.SLT[int]`, optional): 1469 Which user ID(s) to allow through. 1470 """ 1471 return super().add_chat_ids(user_id) 1472 1473 def remove_usernames(self, username: SLT[str]) -> None: 1474 """ 1475 Remove one or more users from allowed usernames. 1476 1477 Args: 1478 username(:class:`telegram.utils.types.SLT[str]`, optional): 1479 Which username(s) to disallow through. 1480 Leading '@'s in usernames will be discarded. 1481 """ 1482 return super().remove_usernames(username) 1483 1484 def remove_user_ids(self, user_id: SLT[int]) -> None: 1485 """ 1486 Remove one or more users from allowed user ids. 1487 1488 Args: 1489 user_id(:class:`telegram.utils.types.SLT[int]`, optional): 1490 Which user ID(s) to disallow through. 1491 """ 1492 return super().remove_chat_ids(user_id) 1493 1494 class via_bot(_ChatUserBaseFilter): 1495 # pylint: disable=W0235 1496 """Filters messages to allow only those which are from specified via_bot ID(s) or 1497 username(s). 1498 1499 Examples: 1500 ``MessageHandler(Filters.via_bot(1234), callback_method)`` 1501 1502 Warning: 1503 :attr:`bot_ids` will give a *copy* of the saved bot ids as :class:`frozenset`. This 1504 is to ensure thread safety. To add/remove a bot, you should use :meth:`add_usernames`, 1505 :meth:`add_bot_ids`, :meth:`remove_usernames` and :meth:`remove_bot_ids`. Only update 1506 the entire set by ``filter.bot_ids/usernames = new_set``, if you are entirely sure 1507 that it is not causing race conditions, as this will complete replace the current set 1508 of allowed bots. 1509 1510 Attributes: 1511 bot_ids(set(:obj:`int`), optional): Which bot ID(s) to allow through. 1512 usernames(set(:obj:`str`), optional): Which username(s) (without leading '@') to allow 1513 through. 1514 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no bot 1515 is specified in :attr:`bot_ids` and :attr:`usernames`. 1516 1517 Args: 1518 bot_id(:class:`telegram.utils.types.SLT[int]`, optional): 1519 Which bot ID(s) to allow through. 1520 username(:class:`telegram.utils.types.SLT[str]`, optional): 1521 Which username(s) to allow through. Leading '@'s in usernames will be discarded. 1522 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user 1523 is specified in :attr:`bot_ids` and :attr:`usernames`. Defaults to :obj:`False` 1524 1525 Raises: 1526 RuntimeError: If bot_id and username are both present. 1527 """ 1528 1529 def __init__( 1530 self, 1531 bot_id: SLT[int] = None, 1532 username: SLT[str] = None, 1533 allow_empty: bool = False, 1534 ): 1535 super().__init__(chat_id=bot_id, username=username, allow_empty=allow_empty) 1536 self.chat_id_name = 'bot_id' 1537 1538 def get_chat_or_user(self, message: Message) -> Optional[User]: 1539 return message.via_bot 1540 1541 @property 1542 def bot_ids(self) -> FrozenSet[int]: 1543 return self.chat_ids 1544 1545 @bot_ids.setter 1546 def bot_ids(self, bot_id: SLT[int]) -> None: 1547 self.chat_ids = bot_id # type: ignore[assignment] 1548 1549 def add_usernames(self, username: SLT[str]) -> None: 1550 """ 1551 Add one or more users to the allowed usernames. 1552 1553 Args: 1554 username(:class:`telegram.utils.types.SLT[str]`, optional): 1555 Which username(s) to allow through. 1556 Leading '@'s in usernames will be discarded. 1557 """ 1558 return super().add_usernames(username) 1559 1560 def add_bot_ids(self, bot_id: SLT[int]) -> None: 1561 """ 1562 1563 Add one or more users to the allowed user ids. 1564 1565 Args: 1566 bot_id(:class:`telegram.utils.types.SLT[int]`, optional): 1567 Which bot ID(s) to allow through. 1568 """ 1569 return super().add_chat_ids(bot_id) 1570 1571 def remove_usernames(self, username: SLT[str]) -> None: 1572 """ 1573 Remove one or more users from allowed usernames. 1574 1575 Args: 1576 username(:class:`telegram.utils.types.SLT[str]`, optional): 1577 Which username(s) to disallow through. 1578 Leading '@'s in usernames will be discarded. 1579 """ 1580 return super().remove_usernames(username) 1581 1582 def remove_bot_ids(self, bot_id: SLT[int]) -> None: 1583 """ 1584 Remove one or more users from allowed user ids. 1585 1586 Args: 1587 bot_id(:class:`telegram.utils.types.SLT[int]`, optional): 1588 Which bot ID(s) to disallow through. 1589 """ 1590 return super().remove_chat_ids(bot_id) 1591 1592 class chat(_ChatUserBaseFilter): 1593 # pylint: disable=W0235 1594 """Filters messages to allow only those which are from a specified chat ID or username. 1595 1596 Examples: 1597 ``MessageHandler(Filters.chat(-1234), callback_method)`` 1598 1599 Warning: 1600 :attr:`chat_ids` will give a *copy* of the saved chat ids as :class:`frozenset`. This 1601 is to ensure thread safety. To add/remove a chat, you should use :meth:`add_usernames`, 1602 :meth:`add_chat_ids`, :meth:`remove_usernames` and :meth:`remove_chat_ids`. Only update 1603 the entire set by ``filter.chat_ids/usernames = new_set``, if you are entirely sure 1604 that it is not causing race conditions, as this will complete replace the current set 1605 of allowed chats. 1606 1607 Attributes: 1608 chat_ids(set(:obj:`int`), optional): Which chat ID(s) to allow through. 1609 usernames(set(:obj:`str`), optional): Which username(s) (without leading '@') to allow 1610 through. 1611 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat 1612 is specified in :attr:`chat_ids` and :attr:`usernames`. 1613 1614 Args: 1615 chat_id(:class:`telegram.utils.types.SLT[int]`, optional): 1616 Which chat ID(s) to allow through. 1617 username(:class:`telegram.utils.types.SLT[str]`, optional): 1618 Which username(s) to allow through. 1619 Leading `'@'` s in usernames will be discarded. 1620 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat 1621 is specified in :attr:`chat_ids` and :attr:`usernames`. Defaults to :obj:`False` 1622 1623 Raises: 1624 RuntimeError: If chat_id and username are both present. 1625 1626 """ 1627 1628 def get_chat_or_user(self, message: Message) -> Optional[Chat]: 1629 return message.chat 1630 1631 def add_usernames(self, username: SLT[str]) -> None: 1632 """ 1633 Add one or more chats to the allowed usernames. 1634 1635 Args: 1636 username(:class:`telegram.utils.types.SLT[str]`, optional): 1637 Which username(s) to allow through. 1638 Leading `'@'` s in usernames will be discarded. 1639 """ 1640 return super().add_usernames(username) 1641 1642 def add_chat_ids(self, chat_id: SLT[int]) -> None: 1643 """ 1644 Add one or more chats to the allowed chat ids. 1645 1646 Args: 1647 chat_id(:class:`telegram.utils.types.SLT[int]`, optional): 1648 Which chat ID(s) to allow through. 1649 """ 1650 return super().add_chat_ids(chat_id) 1651 1652 def remove_usernames(self, username: SLT[str]) -> None: 1653 """ 1654 Remove one or more chats from allowed usernames. 1655 1656 Args: 1657 username(:class:`telegram.utils.types.SLT[str]`, optional): 1658 Which username(s) to disallow through. 1659 Leading '@'s in usernames will be discarded. 1660 """ 1661 return super().remove_usernames(username) 1662 1663 def remove_chat_ids(self, chat_id: SLT[int]) -> None: 1664 """ 1665 Remove one or more chats from allowed chat ids. 1666 1667 Args: 1668 chat_id(:class:`telegram.utils.types.SLT[int]`, optional): 1669 Which chat ID(s) to disallow through. 1670 """ 1671 return super().remove_chat_ids(chat_id) 1672 1673 class sender_chat(_ChatUserBaseFilter): 1674 # pylint: disable=W0235 1675 """Filters messages to allow only those which are from a specified sender chats chat ID or 1676 username. 1677 1678 Examples: 1679 * To filter for messages forwarded from a channel with ID ``-1234``, use 1680 ``MessageHandler(Filters.sender_chat(-1234), callback_method)``. 1681 * To filter for messages of anonymous admins in a super group with username 1682 ``@anonymous``, use 1683 ``MessageHandler(Filters.sender_chat(username='anonymous'), callback_method)``. 1684 * To filter for messages forwarded from *any* channel, use 1685 ``MessageHandler(Filters.sender_chat.channel, callback_method)``. 1686 * To filter for messages of anonymous admins in *any* super group, use 1687 ``MessageHandler(Filters.sender_chat.super_group, callback_method)``. 1688 1689 Warning: 1690 :attr:`chat_ids` will return a *copy* of the saved chat ids as :class:`frozenset`. This 1691 is to ensure thread safety. To add/remove a chat, you should use :meth:`add_usernames`, 1692 :meth:`add_chat_ids`, :meth:`remove_usernames` and :meth:`remove_chat_ids`. Only update 1693 the entire set by ``filter.chat_ids/usernames = new_set``, if you are entirely sure 1694 that it is not causing race conditions, as this will complete replace the current set 1695 of allowed chats. 1696 1697 Attributes: 1698 chat_ids(set(:obj:`int`), optional): Which sender chat chat ID(s) to allow through. 1699 usernames(set(:obj:`str`), optional): Which sender chat username(s) (without leading 1700 '@') to allow through. 1701 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no sender 1702 chat is specified in :attr:`chat_ids` and :attr:`usernames`. 1703 super_group: Messages whose sender chat is a super group. 1704 1705 Examples: 1706 ``Filters.sender_chat.supergroup`` 1707 channel: Messages whose sender chat is a channel. 1708 1709 Examples: 1710 ``Filters.sender_chat.channel`` 1711 1712 Args: 1713 chat_id(:class:`telegram.utils.types.SLT[int]`, optional): 1714 Which sender chat chat ID(s) to allow through. 1715 username(:class:`telegram.utils.types.SLT[str]`, optional): 1716 Which sender chat sername(s) to allow through. 1717 Leading `'@'` s in usernames will be discarded. 1718 allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no sender 1719 chat is specified in :attr:`chat_ids` and :attr:`usernames`. Defaults to 1720 :obj:`False` 1721 1722 Raises: 1723 RuntimeError: If chat_id and username are both present. 1724 1725 """ 1726 1727 def get_chat_or_user(self, message: Message) -> Optional[Chat]: 1728 return message.sender_chat 1729 1730 def add_usernames(self, username: SLT[str]) -> None: 1731 """ 1732 Add one or more sender chats to the allowed usernames. 1733 1734 Args: 1735 username(:class:`telegram.utils.types.SLT[str]`, optional): 1736 Which sender chat username(s) to allow through. 1737 Leading `'@'` s in usernames will be discarded. 1738 """ 1739 return super().add_usernames(username) 1740 1741 def add_chat_ids(self, chat_id: SLT[int]) -> None: 1742 """ 1743 Add one or more sender chats to the allowed chat ids. 1744 1745 Args: 1746 chat_id(:class:`telegram.utils.types.SLT[int]`, optional): 1747 Which sender chat ID(s) to allow through. 1748 """ 1749 return super().add_chat_ids(chat_id) 1750 1751 def remove_usernames(self, username: SLT[str]) -> None: 1752 """ 1753 Remove one or more sender chats from allowed usernames. 1754 1755 Args: 1756 username(:class:`telegram.utils.types.SLT[str]`, optional): 1757 Which sender chat username(s) to disallow through. 1758 Leading '@'s in usernames will be discarded. 1759 """ 1760 return super().remove_usernames(username) 1761 1762 def remove_chat_ids(self, chat_id: SLT[int]) -> None: 1763 """ 1764 Remove one or more sender chats from allowed chat ids. 1765 1766 Args: 1767 chat_id(:class:`telegram.utils.types.SLT[int]`, optional): 1768 Which sender chat ID(s) to disallow through. 1769 """ 1770 return super().remove_chat_ids(chat_id) 1771 1772 class _SuperGroup(MessageFilter): 1773 def filter(self, message: Message) -> bool: 1774 if message.sender_chat: 1775 return message.sender_chat.type == Chat.SUPERGROUP 1776 return False 1777 1778 class _Channel(MessageFilter): 1779 def filter(self, message: Message) -> bool: 1780 if message.sender_chat: 1781 return message.sender_chat.type == Chat.CHANNEL 1782 return False 1783 1784 super_group = _SuperGroup() 1785 channel = _Channel() 1786 1787 class _Invoice(MessageFilter): 1788 name = 'Filters.invoice' 1789 1790 def filter(self, message: Message) -> bool: 1791 return bool(message.invoice) 1792 1793 invoice = _Invoice() 1794 """Messages that contain :class:`telegram.Invoice`.""" 1795 1796 class _SuccessfulPayment(MessageFilter): 1797 name = 'Filters.successful_payment' 1798 1799 def filter(self, message: Message) -> bool: 1800 return bool(message.successful_payment) 1801 1802 successful_payment = _SuccessfulPayment() 1803 """Messages that confirm a :class:`telegram.SuccessfulPayment`.""" 1804 1805 class _PassportData(MessageFilter): 1806 name = 'Filters.passport_data' 1807 1808 def filter(self, message: Message) -> bool: 1809 return bool(message.passport_data) 1810 1811 passport_data = _PassportData() 1812 """Messages that contain a :class:`telegram.PassportData`""" 1813 1814 class _Poll(MessageFilter): 1815 name = 'Filters.poll' 1816 1817 def filter(self, message: Message) -> bool: 1818 return bool(message.poll) 1819 1820 poll = _Poll() 1821 """Messages that contain a :class:`telegram.Poll`.""" 1822 1823 class _Dice(_DiceEmoji): 1824 dice = _DiceEmoji('', 'dice') 1825 darts = _DiceEmoji('', 'darts') 1826 basketball = _DiceEmoji('', 'basketball') 1827 football = _DiceEmoji('⚽') 1828 slot_machine = _DiceEmoji('') 1829 1830 dice = _Dice() 1831 """Dice Messages. If an integer or a list of integers is passed, it filters messages to only 1832 allow those whose dice value is appearing in the given list. 1833 1834 Examples: 1835 To allow any dice message, simply use 1836 ``MessageHandler(Filters.dice, callback_method)``. 1837 To allow only dice with value 6, use 1838 ``MessageHandler(Filters.dice(6), callback_method)``. 1839 To allow only dice with value 5 `or` 6, use 1840 ``MessageHandler(Filters.dice([5, 6]), callback_method)``. 1841 1842 Args: 1843 update (:class:`telegram.utils.types.SLT[int]`, optional): 1844 Which values to allow. If not specified, will allow any dice message. 1845 1846 Note: 1847 Dice messages don't have text. If you want to filter either text or dice messages, use 1848 ``Filters.text | Filters.dice``. 1849 1850 Attributes: 1851 dice: Dice messages with the emoji . Passing a list of integers is supported just as for 1852 :attr:`Filters.dice`. 1853 darts: Dice messages with the emoji . Passing a list of integers is supported just as for 1854 :attr:`Filters.dice`. 1855 basketball: Dice messages with the emoji . Passing a list of integers is supported just 1856 as for :attr:`Filters.dice`. 1857 football: Dice messages with the emoji ⚽. Passing a list of integers is supported just 1858 as for :attr:`Filters.dice`. 1859 slot_machine: Dice messages with the emoji . Passing a list of integers is supported just 1860 as for :attr:`Filters.dice`. 1861 """ 1862 1863 class language(MessageFilter): 1864 """Filters messages to only allow those which are from users with a certain language code. 1865 1866 Note: 1867 According to official Telegram API documentation, not every single user has the 1868 `language_code` attribute. Do not count on this filter working on all users. 1869 1870 Examples: 1871 ``MessageHandler(Filters.language("en"), callback_method)`` 1872 1873 Args: 1874 lang (:class:`telegram.utils.types.SLT[str]`): 1875 Which language code(s) to allow through. 1876 This will be matched using ``.startswith`` meaning that 1877 'en' will match both 'en_US' and 'en_GB'. 1878 1879 """ 1880 1881 def __init__(self, lang: SLT[str]): 1882 if isinstance(lang, str): 1883 lang = cast(str, lang) 1884 self.lang = [lang] 1885 else: 1886 lang = cast(List[str], lang) 1887 self.lang = lang 1888 self.name = f'Filters.language({self.lang})' 1889 1890 def filter(self, message: Message) -> bool: 1891 """""" # remove method from docs 1892 return bool( 1893 message.from_user.language_code 1894 and any([message.from_user.language_code.startswith(x) for x in self.lang]) 1895 ) 1896 1897 class _UpdateType(UpdateFilter): 1898 name = 'Filters.update' 1899 1900 class _Message(UpdateFilter): 1901 name = 'Filters.update.message' 1902 1903 def filter(self, update: Update) -> bool: 1904 return update.message is not None 1905 1906 message = _Message() 1907 1908 class _EditedMessage(UpdateFilter): 1909 name = 'Filters.update.edited_message' 1910 1911 def filter(self, update: Update) -> bool: 1912 return update.edited_message is not None 1913 1914 edited_message = _EditedMessage() 1915 1916 class _Messages(UpdateFilter): 1917 name = 'Filters.update.messages' 1918 1919 def filter(self, update: Update) -> bool: 1920 return update.message is not None or update.edited_message is not None 1921 1922 messages = _Messages() 1923 1924 class _ChannelPost(UpdateFilter): 1925 name = 'Filters.update.channel_post' 1926 1927 def filter(self, update: Update) -> bool: 1928 return update.channel_post is not None 1929 1930 channel_post = _ChannelPost() 1931 1932 class _EditedChannelPost(UpdateFilter): 1933 name = 'Filters.update.edited_channel_post' 1934 1935 def filter(self, update: Update) -> bool: 1936 return update.edited_channel_post is not None 1937 1938 edited_channel_post = _EditedChannelPost() 1939 1940 class _ChannelPosts(UpdateFilter): 1941 name = 'Filters.update.channel_posts' 1942 1943 def filter(self, update: Update) -> bool: 1944 return update.channel_post is not None or update.edited_channel_post is not None 1945 1946 channel_posts = _ChannelPosts() 1947 1948 def filter(self, update: Update) -> bool: 1949 return bool(self.messages(update) or self.channel_posts(update)) 1950 1951 update = _UpdateType() 1952 """Subset for filtering the type of update. 1953 1954 Examples: 1955 Use these filters like: ``Filters.update.message`` or 1956 ``Filters.update.channel_posts`` etc. Or use just ``Filters.update`` for all 1957 types. 1958 1959 Attributes: 1960 message: Updates with :attr:`telegram.Update.message` 1961 edited_message: Updates with :attr:`telegram.Update.edited_message` 1962 messages: Updates with either :attr:`telegram.Update.message` or 1963 :attr:`telegram.Update.edited_message` 1964 channel_post: Updates with :attr:`telegram.Update.channel_post` 1965 edited_channel_post: Updates with 1966 :attr:`telegram.Update.edited_channel_post` 1967 channel_posts: Updates with either :attr:`telegram.Update.channel_post` or 1968 :attr:`telegram.Update.edited_channel_post` 1969 """ 1970