diff --git a/docs/source/telegram.chatpermissions.rst b/docs/source/telegram.chatpermissions.rst new file mode 100644 index 000000000..7cefdbc23 --- /dev/null +++ b/docs/source/telegram.chatpermissions.rst @@ -0,0 +1,6 @@ +telegram.ChatPermissions +======================== + +.. autoclass:: telegram.ChatPermissions + :members: + :show-inheritance: diff --git a/docs/source/telegram.rst b/docs/source/telegram.rst index 9241c7e7c..19c8b9ae2 100644 --- a/docs/source/telegram.rst +++ b/docs/source/telegram.rst @@ -13,6 +13,7 @@ telegram package telegram.chat telegram.chataction telegram.chatmember + telegram.chatpermissions telegram.chatphoto telegram.constants telegram.contact diff --git a/telegram/__init__.py b/telegram/__init__.py index e9a3c5091..76ad1281a 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -23,6 +23,7 @@ from .user import User from .files.chatphoto import ChatPhoto from .chat import Chat from .chatmember import ChatMember +from .chatpermissions import ChatPermissions from .files.photosize import PhotoSize from .files.audio import Audio from .files.voice import Voice @@ -125,8 +126,8 @@ from .version import __version__ # noqa: F401 __author__ = 'devs@python-telegram-bot.org' __all__ = [ - 'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatAction', 'ChosenInlineResult', 'CallbackQuery', - 'Contact', 'Document', 'File', 'ForceReply', 'InlineKeyboardButton', + 'Audio', 'Bot', 'Chat', 'ChatMember', 'ChatPermissions', 'ChatAction', 'ChosenInlineResult', + 'CallbackQuery', 'Contact', 'Document', 'File', 'ForceReply', 'InlineKeyboardButton', 'InlineKeyboardMarkup', 'InlineQuery', 'InlineQueryResult', 'InlineQueryResult', 'InlineQueryResultArticle', 'InlineQueryResultAudio', 'InlineQueryResultCachedAudio', 'InlineQueryResultCachedDocument', 'InlineQueryResultCachedGif', diff --git a/telegram/bot.py b/telegram/bot.py index f524a00c7..2e1f6e5ea 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -2685,14 +2685,18 @@ class Bot(TelegramObject): return result @log - def restrict_chat_member(self, chat_id, user_id, until_date=None, can_send_messages=None, - can_send_media_messages=None, can_send_other_messages=None, - can_add_web_page_previews=None, timeout=None, **kwargs): + def restrict_chat_member(self, chat_id, user_id, permissions, until_date=None, + timeout=None, **kwargs): """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift restrictions from a user. + Note: + Since Bot API 4.4, :attr:`restrict_chat_member` takes the new user permissions in a + single argument of type :class:`telegram.ChatPermissions`. The old way of passing + parameters will not keep working forever. + Args: chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername). @@ -2701,15 +2705,7 @@ class Bot(TelegramObject): will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever. - can_send_messages (:obj:`bool`, optional): Pass True, if the user can send text - messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`, optional): Pass True, if the user can send - audios, documents, photos, videos, video notes and voice notes, implies - can_send_messages. - can_send_other_messages (:obj:`bool`, optional): Pass True, if the user can send - animations, games, stickers and use inline bots, implies can_send_media_messages. - can_add_web_page_previews (:obj:`bool`, optional): Pass True, if the user may add - web page previews to their messages, implies can_send_media_messages. + permissions (:class:`telegram.ChatPermissions`): New user permissions. timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as the read timeout from the server (instead of the one specified during creation of the connection pool). @@ -2720,24 +2716,15 @@ class Bot(TelegramObject): Raises: :class:`telegram.TelegramError` - """ url = '{0}/restrictChatMember'.format(self.base_url) - data = {'chat_id': chat_id, 'user_id': user_id} + data = {'chat_id': chat_id, 'user_id': user_id, 'permissions': permissions.to_dict()} if until_date is not None: if isinstance(until_date, datetime): until_date = to_timestamp(until_date) data['until_date'] = until_date - if can_send_messages is not None: - data['can_send_messages'] = can_send_messages - if can_send_media_messages is not None: - data['can_send_media_messages'] = can_send_media_messages - if can_send_other_messages is not None: - data['can_send_other_messages'] = can_send_other_messages - if can_add_web_page_previews is not None: - data['can_add_web_page_previews'] = can_add_web_page_previews data.update(kwargs) result = self._request.post(url, data, timeout=timeout) @@ -2815,6 +2802,38 @@ class Bot(TelegramObject): return result + @log + def set_chat_permissions(self, chat_id, permissions, timeout=None, **kwargs): + """ + Use this method to set default chat permissions for all members. The bot must be an + administrator in the group or a supergroup for this to work and must have the + :attr:`can_restrict_members` admin rights. Returns True on success. + + Args: + chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of + the target supergroup (in the format `@supergroupusername`). + permissions (:class:`telegram.ChatPermissions`): New default chat permissions. + timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as + the read timeout from the server (instead of the one specified during creation of + the connection pool). + **kwargs (:obj:`dict`): Arbitrary keyword arguments + + Returns: + :obj:`bool`: Returns True on success. + + Raises: + :class:`telegram.TelegramError` + + """ + url = '{0}/setChatPermissions'.format(self.base_url) + + data = {'chat_id': chat_id, 'permissions': permissions.to_dict()} + data.update(kwargs) + + result = self._request.post(url, data, timeout=timeout) + + return result + @log def export_chat_invite_link(self, chat_id, timeout=None, **kwargs): """ @@ -2958,8 +2977,9 @@ class Bot(TelegramObject): @log def set_chat_description(self, chat_id, description, timeout=None, **kwargs): """ - Use this method to change the description of a supergroup or a channel. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. + Use this method to change the description of a group, a supergroup or a channel. The bot + must be an administrator in the chat for this to work and must have the appropriate admin + rights. Args: chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username @@ -3526,6 +3546,8 @@ class Bot(TelegramObject): """Alias for :attr:`restrict_chat_member`""" promoteChatMember = promote_chat_member """Alias for :attr:`promote_chat_member`""" + setChatPermissions = set_chat_permissions + """Alias for :attr:`set_chat_permissions`""" exportChatInviteLink = export_chat_invite_link """Alias for :attr:`export_chat_invite_link`""" setChatPhoto = set_chat_photo diff --git a/telegram/chat.py b/telegram/chat.py index af6708bb1..e2c6c7791 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -20,6 +20,7 @@ """This module contains an object that represents a Telegram Chat.""" from telegram import TelegramObject, ChatPhoto +from .chatpermissions import ChatPermissions class Chat(TelegramObject): @@ -32,12 +33,13 @@ class Chat(TelegramObject): username (:obj:`str`): Optional. Username. first_name (:obj:`str`): Optional. First name of the other party in a private chat. last_name (:obj:`str`): Optional. Last name of the other party in a private chat. - all_members_are_administrators (:obj:`bool`): Optional. photo (:class:`telegram.ChatPhoto`): Optional. Chat photo. - description (:obj:`str`): Optional. Description, for supergroups and channel chats. + description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats. invite_link (:obj:`str`): Optional. Chat invite link, for supergroups and channel chats. pinned_message (:class:`telegram.Message`): Optional. Pinned message, for supergroups. Returned only in get_chat. + permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions, + for groups and supergroups. Returned only in getChat. sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set. can_set_sticker_set (:obj:`bool`): Optional. ``True``, if the bot can change group the sticker set. @@ -54,15 +56,15 @@ class Chat(TelegramObject): available. first_name(:obj:`str`, optional): First name of the other party in a private chat. last_name(:obj:`str`, optional): Last name of the other party in a private chat. - all_members_are_administrators (:obj:`bool`, optional): True if a group has `All Members - Are Admins` enabled. photo (:class:`telegram.ChatPhoto`, optional): Chat photo. Returned only in getChat. - description (:obj:`str`, optional): Description, for supergroups and channel chats. + description (:obj:`str`, optional): Description, for groups, supergroups and channel chats. Returned only in get_chat. invite_link (:obj:`str`, optional): Chat invite link, for supergroups and channel chats. Returned only in get_chat. pinned_message (:class:`telegram.Message`, optional): Pinned message, for supergroups. Returned only in get_chat. + permissions (:class:`telegram.ChatPermission`): Optional. Default chat member permissions, + for groups and supergroups. Returned only in getChat. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. sticker_set_name (:obj:`str`, optional): For supergroups, name of Group sticker set. Returned only in get_chat. @@ -94,6 +96,7 @@ class Chat(TelegramObject): description=None, invite_link=None, pinned_message=None, + permissions=None, sticker_set_name=None, can_set_sticker_set=None, **kwargs): @@ -110,6 +113,7 @@ class Chat(TelegramObject): self.description = description self.invite_link = invite_link self.pinned_message = pinned_message + self.permissions = permissions self.sticker_set_name = sticker_set_name self.can_set_sticker_set = can_set_sticker_set @@ -132,6 +136,7 @@ class Chat(TelegramObject): data['photo'] = ChatPhoto.de_json(data.get('photo'), bot) from telegram import Message data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) + data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot) return cls(bot=bot, **data) @@ -221,6 +226,16 @@ class Chat(TelegramObject): """ return self.bot.unban_chat_member(self.id, *args, **kwargs) + def set_permissions(self, *args, **kwargs): + """Shortcut for:: + bot.set_chat_permissions(update.message.chat.id, *args, **kwargs) + + Returns: + :obj:`bool`: If the action was sent successfully. + + """ + return self.bot.set_chat_permissions(self.id, *args, **kwargs) + def send_message(self, *args, **kwargs): """Shortcut for:: diff --git a/telegram/chatmember.py b/telegram/chatmember.py index 571ed1dd2..9cb552638 100644 --- a/telegram/chatmember.py +++ b/telegram/chatmember.py @@ -32,18 +32,17 @@ class ChatMember(TelegramObject): for this user. can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator privileges of that user. - can_change_info (:obj:`bool`): Optional. If the administrator can change the chat title, - photo and other settings. + can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and + other settings. can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel. can_edit_messages (:obj:`bool`): Optional. If the administrator can edit messages of other users. can_delete_messages (:obj:`bool`): Optional. If the administrator can delete messages of other users. - can_invite_users (:obj:`bool`): Optional. If the administrator can invite new users to the - chat. + can_invite_users (:obj:`bool`): Optional. If the user can invite new users to the chat. can_restrict_members (:obj:`bool`): Optional. If the administrator can restrict, ban or unban chat members. - can_pin_messages (:obj:`bool`): Optional. If the administrator can pin messages. + can_pin_messages (:obj:`bool`): Optional. If the user can pin messages. can_promote_members (:obj:`bool`): Optional. If the administrator can add new administrators. is_member (:obj:`bool`): Optional. Restricted only. True, if the user is a member of the @@ -52,6 +51,8 @@ class ChatMember(TelegramObject): locations and venues. can_send_media_messages (:obj:`bool`): Optional. If the user can send media messages, implies can_send_messages. + can_send_polls (:obj:`bool`): Optional. True, if the user is allowed to + send polls. can_send_other_messages (:obj:`bool`): Optional. If the user can send animations, games, stickers and use inline bots, implies can_send_media_messages. can_add_web_page_previews (:obj:`bool`): Optional. If user may add web page previews to his @@ -65,20 +66,20 @@ class ChatMember(TelegramObject): restrictions will be lifted for this user. can_be_edited (:obj:`bool`, optional): Administrators only. True, if the bot is allowed to edit administrator privileges of that user. - can_change_info (:obj:`bool`, optional): Administrators only. True, if the administrator - can change the chat title, photo and other settings. + can_change_info (:obj:`bool`, optional): Administrators and restricted only. True, if the + user can change the chat title, photo and other settings. can_post_messages (:obj:`bool`, optional): Administrators only. True, if the administrator can post in the channel, channels only. can_edit_messages (:obj:`bool`, optional): Administrators only. True, if the administrator can edit messages of other users, channels only. can_delete_messages (:obj:`bool`, optional): Administrators only. True, if the administrator can delete messages of other user. - can_invite_users (:obj:`bool`, optional): Administrators only. True, if the administrator - can invite new users to the chat. + can_invite_users (:obj:`bool`, optional): Administrators and restricted only. True, if the + user can invite new users to the chat. can_restrict_members (:obj:`bool`, optional): Administrators only. True, if the administrator can restrict, ban or unban chat members. - can_pin_messages (:obj:`bool`, optional): Administrators only. True, if the administrator - can pin messages, supergroups only. + can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. True, if the + user can pin messages, supergroups only. can_promote_members (:obj:`bool`, optional): Administrators only. True, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators @@ -90,6 +91,8 @@ class ChatMember(TelegramObject): can_send_media_messages (:obj:`bool`, optional): Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages. + can_send_polls (:obj:`bool`, optional): Restricted only. True, if the user is allowed to + send polls. can_send_other_messages (:obj:`bool`, optional): Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies can_send_media_messages. can_add_web_page_previews (:obj:`bool`, optional): Restricted only. True, if user may add @@ -114,7 +117,7 @@ class ChatMember(TelegramObject): can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, can_send_messages=None, - can_send_media_messages=None, can_send_other_messages=None, + can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, is_member=None, **kwargs): # Required self.user = user @@ -131,6 +134,7 @@ class ChatMember(TelegramObject): self.can_promote_members = can_promote_members self.can_send_messages = can_send_messages self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls self.can_send_other_messages = can_send_other_messages self.can_add_web_page_previews = can_add_web_page_previews self.is_member = is_member diff --git a/telegram/chatpermissions.py b/telegram/chatpermissions.py new file mode 100644 index 000000000..70b787b09 --- /dev/null +++ b/telegram/chatpermissions.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2018 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains an object that represents a Telegram ChatPermission.""" + +from telegram import TelegramObject + + +class ChatPermissions(TelegramObject): + """Describes actions that a non-administrator user is allowed to take in a chat. + + Attributes: + can_send_messages (:obj:`bool`): Optional. True, if the user is allowed to send text + messages, contacts, locations and venues. + can_send_media_messages (:obj:`bool`): Optional. True, if the user is allowed to send + audios, documents, photos, videos, video notes and voice notes, implies + :attr:`can_send_messages`. + can_send_polls (:obj:`bool`): Optional. True, if the user is allowed to send polls, implies + :attr:`can_send_messages`. + can_send_other_messages (:obj:`bool`): Optional. True, if the user is allowed to send + animations, games, stickers and use inline bots, implies + :attr:`can_send_media_messages`. + can_add_web_page_previews (:obj:`bool`): Optional. True, if the user is allowed to add web + page previews to their messages, implies :attr:`can_send_media_messages`. + can_change_info (:obj:`bool`): Optional. True, if the user is allowed to change the chat + title, photo and other settings. Ignored in public supergroups. + can_invite_users (:obj:`bool`): Optional. True, if the user is allowed to invite new users + to the chat. + can_pin_messages (:obj:`bool`): Optional. True, if the user is allowed to pin messages. + Ignored in public supergroups. + + Args: + can_send_messages (:obj:`bool`, optional): True, if the user is allowed to send text + messages, contacts, locations and venues. + can_send_media_messages (:obj:`bool`, optional): True, if the user is allowed to send + audios, documents, photos, videos, video notes and voice notes, implies + :attr:`can_send_messages`. + can_send_polls (:obj:`bool`, optional): True, if the user is allowed to send polls, implies + :attr:`can_send_messages`. + can_send_other_messages (:obj:`bool`, optional): True, if the user is allowed to send + animations, games, stickers and use inline bots, implies + :attr:`can_send_media_messages`. + can_add_web_page_previews (:obj:`bool`, optional): True, if the user is allowed to add web + page previews to their messages, implies :attr:`can_send_media_messages`. + can_change_info (:obj:`bool`, optional): True, if the user is allowed to change the chat + title, photo and other settings. Ignored in public supergroups. + can_invite_users (:obj:`bool`, optional): True, if the user is allowed to invite new users + to the chat. + can_pin_messages (:obj:`bool`, optional): True, if the user is allowed to pin messages. + Ignored in public supergroups. + + """ + + def __init__(self, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, + can_send_other_messages=None, can_add_web_page_previews=None, + can_change_info=None, can_invite_users=None, can_pin_messages=None, **kwargs): + # Required + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + + @classmethod + def de_json(cls, data, bot): + if not data: + return None + + return cls(**data) diff --git a/telegram/files/chatphoto.py b/telegram/files/chatphoto.py index a3f517191..3e17c1b87 100644 --- a/telegram/files/chatphoto.py +++ b/telegram/files/chatphoto.py @@ -27,14 +27,14 @@ class ChatPhoto(TelegramObject): """This object represents a chat photo. Attributes: - small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. - big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo. + small_file_id (:obj:`str`): File identifier of small (160x160) chat photo. + big_file_id (:obj:`str`): File identifier of big (640x640) chat photo. Args: - small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This - file_id can be used only for photo download. - big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo. This file_id - can be used only for photo download. + small_file_id (:obj:`str`): File identifier of small (160x160) chat photo. This file_id can + be used only for photo download and only for as long as the photo is not changed. + big_file_id (:obj:`str`): File identifier of big (640x640) chat photo. This file_id can be + used only for photo download and only for as long as the photo is not changed. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods **kwargs (:obj:`dict`): Arbitrary keyword arguments. diff --git a/telegram/files/sticker.py b/telegram/files/sticker.py index e42509de3..042e39c95 100644 --- a/telegram/files/sticker.py +++ b/telegram/files/sticker.py @@ -28,6 +28,7 @@ class Sticker(TelegramObject): file_id (:obj:`str`): Unique identifier for this file. width (:obj:`int`): Sticker width. height (:obj:`int`): Sticker height. + is_animated (:obj:`bool`): True, if the sticker is animated. thumb (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the .webp or .jpg format. emoji (:obj:`str`): Optional. Emoji associated with the sticker. @@ -41,6 +42,7 @@ class Sticker(TelegramObject): file_id (:obj:`str`): Unique identifier for this file. width (:obj:`int`): Sticker width. height (:obj:`int`): Sticker height. + is_animated (:obj:`bool`): True, if the sticker is animated. thumb (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the .webp or .jpg format. emoji (:obj:`str`, optional): Emoji associated with the sticker @@ -58,6 +60,7 @@ class Sticker(TelegramObject): file_id, width, height, + is_animated, thumb=None, emoji=None, file_size=None, @@ -69,6 +72,7 @@ class Sticker(TelegramObject): self.file_id = str(file_id) self.width = int(width) self.height = int(height) + self.is_animated = is_animated # Optionals self.thumb = thumb self.emoji = emoji @@ -123,20 +127,23 @@ class StickerSet(TelegramObject): Attributes: name (:obj:`str`): Sticker set name. title (:obj:`str`): Sticker set title. + is_animated (:obj:`bool`): True, if the sticker set contains animated stickers. contains_masks (:obj:`bool`): True, if the sticker set contains masks. stickers (List[:class:`telegram.Sticker`]): List of all set stickers. Args: name (:obj:`str`): Sticker set name. title (:obj:`str`): Sticker set title. + is_animated (:obj:`bool`): True, if the sticker set contains animated stickers. contains_masks (:obj:`bool`): True, if the sticker set contains masks. stickers (List[:class:`telegram.Sticker`]): List of all set stickers. """ - def __init__(self, name, title, contains_masks, stickers, bot=None, **kwargs): + def __init__(self, name, title, is_animated, contains_masks, stickers, bot=None, **kwargs): self.name = name self.title = title + self.is_animated = is_animated self.contains_masks = contains_masks self.stickers = stickers diff --git a/tests/test_bot.py b/tests/test_bot.py index 341e8e334..b7dfb2ddc 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -28,7 +28,7 @@ from future.utils import string_types from telegram import (Bot, Update, ChatAction, TelegramError, User, InlineKeyboardMarkup, InlineKeyboardButton, InlineQueryResultArticle, InputTextMessageContent, - ShippingOption, LabeledPrice, Poll) + ShippingOption, LabeledPrice, ChatPermissions, Poll) from telegram.error import BadRequest, InvalidToken, NetworkError, RetryAfter from telegram.utils.helpers import from_timestamp @@ -50,6 +50,11 @@ def media_message(bot, chat_id): return bot.send_voice(chat_id, voice=f, caption='my caption', timeout=10) +@pytest.fixture(scope='class') +def chat_permissions(): + return ChatPermissions(can_send_messages=False, can_change_info=False, can_invite_users=False) + + class TestBot(object): @pytest.mark.parametrize('token', argvalues=[ '123', @@ -260,6 +265,16 @@ class TestBot(object): assert bot.unban_chat_member(2, 32) + def test_set_chat_permissions(self, monkeypatch, bot, chat_permissions): + def test(_, url, data, *args, **kwargs): + chat_id = data['chat_id'] == 2 + permissions = data['permissions'] == chat_permissions.to_dict() + return chat_id and permissions + + monkeypatch.setattr('telegram.utils.request.Request.post', test) + + assert bot.set_chat_permissions(2, chat_permissions) + # TODO: Needs improvement. Need an incoming callbackquery to test def test_answer_callback_query(self, monkeypatch, bot): # For now just test that our internals pass the correct data @@ -581,16 +596,13 @@ class TestBot(object): @flaky(3, 1) @pytest.mark.timeout(10) - def test_restrict_chat_member(self, bot, channel_id): + def test_restrict_chat_member(self, bot, channel_id, chat_permissions): # TODO: Add bot to supergroup so this can be tested properly with pytest.raises(BadRequest, match='Method is available only for supergroups'): assert bot.restrict_chat_member(channel_id, 95205500, - until_date=datetime.now(), - can_send_messages=False, - can_send_media_messages=False, - can_send_other_messages=False, - can_add_web_page_previews=False) + chat_permissions, + until_date=datetime.now()) @flaky(3, 1) @pytest.mark.timeout(10) diff --git a/tests/test_chat.py b/tests/test_chat.py index 681d42a3d..23e95806a 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -19,7 +19,7 @@ import pytest -from telegram import Chat, ChatAction +from telegram import Chat, ChatAction, ChatPermissions from telegram import User @@ -28,7 +28,8 @@ def chat(bot): return Chat(TestChat.id, TestChat.title, TestChat.type, username=TestChat.username, all_members_are_administrators=TestChat.all_members_are_administrators, bot=bot, sticker_set_name=TestChat.sticker_set_name, - can_set_sticker_set=TestChat.can_set_sticker_set) + can_set_sticker_set=TestChat.can_set_sticker_set, + permissions=TestChat.permissions) class TestChat(object): @@ -39,6 +40,11 @@ class TestChat(object): all_members_are_administrators = False sticker_set_name = 'stickers' can_set_sticker_set = False + permissions = ChatPermissions( + can_send_messages=True, + can_change_info=False, + can_invite_users=True, + ) def test_de_json(self, bot): json_dict = { @@ -48,7 +54,8 @@ class TestChat(object): 'username': self.username, 'all_members_are_administrators': self.all_members_are_administrators, 'sticker_set_name': self.sticker_set_name, - 'can_set_sticker_set': self.can_set_sticker_set + 'can_set_sticker_set': self.can_set_sticker_set, + 'permissions': self.permissions.to_dict() } chat = Chat.de_json(json_dict, bot) @@ -59,6 +66,7 @@ class TestChat(object): assert chat.all_members_are_administrators == self.all_members_are_administrators assert chat.sticker_set_name == self.sticker_set_name assert chat.can_set_sticker_set == self.can_set_sticker_set + assert chat.permissions == self.permissions def test_to_dict(self, chat): chat_dict = chat.to_dict() @@ -69,6 +77,7 @@ class TestChat(object): assert chat_dict['type'] == chat.type assert chat_dict['username'] == chat.username assert chat_dict['all_members_are_administrators'] == chat.all_members_are_administrators + assert chat_dict['permissions'] == chat.permissions.to_dict() def test_link(self, chat): assert chat.link == 'https://t.me/{}'.format(chat.username) @@ -133,6 +142,15 @@ class TestChat(object): monkeypatch.setattr('telegram.Bot.unban_chat_member', test) assert chat.unban_member(42) + def test_set_permissions(self, monkeypatch, chat): + def test(*args, **kwargs): + chat_id = args[1] == chat.id + permissions = args[2] == self.permissions + return chat_id and permissions + + monkeypatch.setattr('telegram.Bot.set_chat_permissions', test) + assert chat.set_permissions(self.permissions) + def test_instance_method_send_message(self, monkeypatch, chat): def test(*args, **kwargs): return args[1] == chat.id and args[2] == 'test' diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py index 0fdc1adf8..c94074300 100644 --- a/tests/test_chatmember.py +++ b/tests/test_chatmember.py @@ -61,8 +61,9 @@ class TestChatMember(object): 'can_promote_members': True, 'can_send_messages': False, 'can_send_media_messages': True, - 'can_send_other_messages': False, - 'can_add_web_page_previews': True} + 'can_send_polls': False, + 'can_send_other_messages': True, + 'can_add_web_page_previews': False} chat_member = ChatMember.de_json(json_dict, bot) @@ -79,8 +80,9 @@ class TestChatMember(object): assert chat_member.can_promote_members is True assert chat_member.can_send_messages is False assert chat_member.can_send_media_messages is True - assert chat_member.can_send_other_messages is False - assert chat_member.can_add_web_page_previews is True + assert chat_member.can_send_polls is False + assert chat_member.can_send_other_messages is True + assert chat_member.can_add_web_page_previews is False def test_to_dict(self, chat_member): chat_member_dict = chat_member.to_dict() diff --git a/tests/test_chatpermissions.py b/tests/test_chatpermissions.py new file mode 100644 index 000000000..129a5252b --- /dev/null +++ b/tests/test_chatpermissions.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2018 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +import pytest + +from telegram import ChatPermissions + + +@pytest.fixture(scope="class") +def chat_permissions(): + return ChatPermissions(can_send_messages=True, can_send_media_messages=True, + can_send_polls=True, can_send_other_messages=True, + can_add_web_page_previews=True, can_change_info=True, + can_invite_users=True, can_pin_messages=True) + + +class TestChatPermissions(object): + can_send_messages = True + can_send_media_messages = True + can_send_polls = True + can_send_other_messages = False + can_add_web_page_previews = False + can_change_info = False + can_invite_users = None + can_pin_messages = None + + def test_de_json(self, bot): + json_dict = { + 'can_send_messages': self.can_send_messages, + 'can_send_media_messages': self.can_send_media_messages, + 'can_send_polls': self.can_send_polls, + 'can_send_other_messages': self.can_send_other_messages, + 'can_add_web_page_previews': self.can_add_web_page_previews, + 'can_change_info': self.can_change_info, + 'can_invite_users': self.can_invite_users, + 'can_pin_messages': self.can_pin_messages + } + permissions = ChatPermissions.de_json(json_dict, bot) + + assert permissions.can_send_messages == self.can_send_messages + assert permissions.can_send_media_messages == self.can_send_media_messages + assert permissions.can_send_polls == self.can_send_polls + assert permissions.can_send_other_messages == self.can_send_other_messages + assert permissions.can_add_web_page_previews == self.can_add_web_page_previews + assert permissions.can_change_info == self.can_change_info + assert permissions.can_invite_users == self.can_invite_users + assert permissions.can_pin_messages == self.can_pin_messages + + def test_to_dict(self, chat_permissions): + permissions_dict = chat_permissions.to_dict() + + assert isinstance(permissions_dict, dict) + assert permissions_dict['can_send_messages'] == chat_permissions.can_send_messages + assert (permissions_dict['can_send_media_messages'] + == chat_permissions.can_send_media_messages) + assert permissions_dict['can_send_polls'] == chat_permissions.can_send_polls + assert (permissions_dict['can_send_other_messages'] + == chat_permissions.can_send_other_messages) + assert (permissions_dict['can_add_web_page_previews'] + == chat_permissions.can_add_web_page_previews) + assert permissions_dict['can_change_info'] == chat_permissions.can_change_info + assert permissions_dict['can_invite_users'] == chat_permissions.can_invite_users + assert permissions_dict['can_pin_messages'] == chat_permissions.can_pin_messages diff --git a/tests/test_helpers.py b/tests/test_helpers.py index acf9ec913..8e60d886e 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -47,7 +47,7 @@ class TestHelpers(object): assert helpers.effective_message_type(test_message) == 'text' test_message.text = None - test_message = build_test_message(sticker=Sticker('sticker_id', 50, 50)) + test_message = build_test_message(sticker=Sticker('sticker_id', 50, 50, False)) assert helpers.effective_message_type(test_message) == 'sticker' test_message.sticker = None diff --git a/tests/test_message.py b/tests/test_message.py index f2499ea5d..6aaf63a9e 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -57,7 +57,7 @@ def message(bot): [PhotoSize('game_photo_id', 30, 30), ])}, {'photo': [PhotoSize('photo_id', 50, 50)], 'caption': 'photo_file'}, - {'sticker': Sticker('sticker_id', 50, 50)}, + {'sticker': Sticker('sticker_id', 50, 50, True)}, {'video': Video('video_id', 12, 12, 12), 'caption': 'video_file'}, {'voice': Voice('voice_id', 5)}, diff --git a/tests/test_official.py b/tests/test_official.py index 7557baa71..cba7a5951 100644 --- a/tests/test_official.py +++ b/tests/test_official.py @@ -120,6 +120,8 @@ def check_object(h4): ignored |= {'credentials'} elif name == 'PassportElementError': ignored |= {'message', 'type', 'source'} + elif name == 'Chat': + ignored |= {'all_members_are_administrators'} assert (sig.parameters.keys() ^ checked) - ignored == set() diff --git a/tests/test_photo.py b/tests/test_photo.py index 4b6b4e44b..9a3d8aee6 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -317,7 +317,7 @@ class TestPhoto(object): b = PhotoSize(photo.file_id, self.width, self.height) c = PhotoSize(photo.file_id, 0, 0) d = PhotoSize('', self.width, self.height) - e = Sticker(photo.file_id, self.width, self.height) + e = Sticker(photo.file_id, self.width, self.height, False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_sticker.py b/tests/test_sticker.py index ab81b5b13..3dab2605c 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -49,6 +49,7 @@ class TestSticker(object): emoji = '💪' width = 510 height = 512 + is_animated = False file_size = 39518 thumb_width = 319 thumb_height = 320 @@ -66,6 +67,7 @@ class TestSticker(object): def test_expected_values(self, sticker): assert sticker.width == self.width assert sticker.height == self.height + assert sticker.is_animated == self.is_animated assert sticker.file_size == self.file_size assert sticker.thumb.width == self.thumb_width assert sticker.thumb.height == self.thumb_height @@ -81,6 +83,7 @@ class TestSticker(object): assert message.sticker.file_id != '' assert message.sticker.width == sticker.width assert message.sticker.height == sticker.height + assert message.sticker.is_animated == sticker.is_animated assert message.sticker.file_size == sticker.file_size assert isinstance(message.sticker.thumb, PhotoSize) @@ -132,6 +135,7 @@ class TestSticker(object): assert message.sticker.file_id != '' assert message.sticker.width == sticker.width assert message.sticker.height == sticker.height + assert message.sticker.is_animated == sticker.is_animated assert message.sticker.file_size == sticker.file_size assert isinstance(message.sticker.thumb, PhotoSize) @@ -146,6 +150,7 @@ class TestSticker(object): 'file_id': 'not a file id', 'width': self.width, 'height': self.height, + 'is_animated': self.is_animated, 'thumb': sticker.thumb.to_dict(), 'emoji': self.emoji, 'file_size': self.file_size @@ -155,6 +160,7 @@ class TestSticker(object): assert json_sticker.file_id == 'not a file id' assert json_sticker.width == self.width assert json_sticker.height == self.height + assert json_sticker.is_animated == self.is_animated assert json_sticker.emoji == self.emoji assert json_sticker.file_size == self.file_size assert json_sticker.thumb == sticker.thumb @@ -174,6 +180,7 @@ class TestSticker(object): assert sticker_dict['file_id'] == sticker.file_id assert sticker_dict['width'] == sticker.width assert sticker_dict['height'] == sticker.height + assert sticker_dict['is_animated'] == sticker.is_animated assert sticker_dict['file_size'] == sticker.file_size assert sticker_dict['thumb'] == sticker.thumb.to_dict() @@ -194,11 +201,11 @@ class TestSticker(object): bot.send_sticker(chat_id) def test_equality(self, sticker): - a = Sticker(sticker.file_id, self.width, self.height) - b = Sticker(sticker.file_id, self.width, self.height) - c = Sticker(sticker.file_id, 0, 0) - d = Sticker('', self.width, self.height) - e = PhotoSize(sticker.file_id, self.width, self.height) + a = Sticker(sticker.file_id, self.width, self.height, self.is_animated) + b = Sticker(sticker.file_id, self.width, self.height, self.is_animated) + c = Sticker(sticker.file_id, 0, 0, False) + d = Sticker('', self.width, self.height, self.is_animated) + e = PhotoSize(sticker.file_id, self.width, self.height, self.is_animated) assert a == b assert hash(a) == hash(b) @@ -224,8 +231,9 @@ def sticker_set(bot): class TestStickerSet(object): title = 'Test stickers' + is_animated = True contains_masks = False - stickers = [Sticker('file_id', 512, 512)] + stickers = [Sticker('file_id', 512, 512, True)] name = 'NOTAREALNAME' def test_de_json(self, bot): @@ -233,6 +241,7 @@ class TestStickerSet(object): json_dict = { 'name': name, 'title': self.title, + 'is_animated': self.is_animated, 'contains_masks': self.contains_masks, 'stickers': [x.to_dict() for x in self.stickers] } @@ -240,6 +249,7 @@ class TestStickerSet(object): assert sticker_set.name == name assert sticker_set.title == self.title + assert sticker_set.is_animated == self.is_animated assert sticker_set.contains_masks == self.contains_masks assert sticker_set.stickers == self.stickers @@ -258,6 +268,7 @@ class TestStickerSet(object): assert isinstance(sticker_set_dict, dict) assert sticker_set_dict['name'] == sticker_set.name assert sticker_set_dict['title'] == sticker_set.title + assert sticker_set_dict['is_animated'] == sticker_set.is_animated assert sticker_set_dict['contains_masks'] == sticker_set.contains_masks assert sticker_set_dict['stickers'][0] == sticker_set.stickers[0].to_dict() @@ -282,10 +293,10 @@ class TestStickerSet(object): assert sticker.get_file() def test_equality(self): - a = StickerSet(self.name, self.title, self.contains_masks, self.stickers) - b = StickerSet(self.name, self.title, self.contains_masks, self.stickers) - c = StickerSet(self.name, None, None, None) - d = StickerSet('blah', self.title, self.contains_masks, self.stickers) + a = StickerSet(self.name, self.title, self.is_animated, self.contains_masks, self.stickers) + b = StickerSet(self.name, self.title, self.is_animated, self.contains_masks, self.stickers) + c = StickerSet(self.name, None, None, None, None) + d = StickerSet('blah', self.title, self.is_animated, self.contains_masks, self.stickers) e = Audio(self.name, 0, None, None) assert a == b