Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/source/telegram.chatlocation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
telegram.ChatLocation
=====================

.. autoclass:: telegram.ChatLocation
:members:
:show-inheritance:
2 changes: 2 additions & 0 deletions telegram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .user import User
from .files.chatphoto import ChatPhoto
from .chat import Chat
from .chatlocation import ChatLocation
from .chatmember import ChatMember
from .chatpermissions import ChatPermissions
from .files.photosize import PhotoSize
Expand Down Expand Up @@ -190,6 +191,7 @@
'InputTextMessageContent',
'InputVenueMessageContent',
'Location',
'ChatLocation',
'ProximityAlertTriggered',
'EncryptedCredentials',
'PassportFile',
Expand Down
12 changes: 10 additions & 2 deletions telegram/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2115,16 +2115,21 @@ def unban_chat_member(
user_id: Union[str, int],
timeout: float = None,
api_kwargs: JSONDict = None,
only_if_banned: bool = None,
) -> bool:
"""Use this method to unban a previously kicked user in a supergroup or channel.

The user will not return to the group automatically, but will be able to join via link,
etc. The bot must be an administrator in the group for this to work.
The user will *not* return to the group or channel automatically, but will be able to join
via link, etc. The bot must be an administrator for this to work. By default, this method
guarantees that after the call the user is not a member of the chat, but will be able to
join it. So if the user is a member of the chat they will also be *removed* from the chat.
If you don't want this, use the parameter :attr:`only_if_banned`.

Args:
chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username
of the target channel (in the format @channelusername).
user_id (:obj:`int`): Unique identifier of the target user.
only_if_banned (:obj:`bool`, optional): Do nothing if the user is not banned.
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).
Expand All @@ -2140,6 +2145,9 @@ def unban_chat_member(
"""
data: JSONDict = {'chat_id': chat_id, 'user_id': user_id}

if only_if_banned is not None:
data['only_if_banned'] = only_if_banned

result = self._post('unbanChatMember', data, timeout=timeout, api_kwargs=api_kwargs)

return result # type: ignore[return-value]
Expand Down
22 changes: 22 additions & 0 deletions telegram/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from telegram.utils.types import JSONDict

from .chatpermissions import ChatPermissions
from .chatlocation import ChatLocation

if TYPE_CHECKING:
from telegram import Bot, ChatMember, Message
Expand All @@ -44,6 +45,8 @@ class Chat(TelegramObject):
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.
photo (:class:`telegram.ChatPhoto`): Optional. Chat photo.
bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in
:meth:`telegram.Bot.get_chat`.
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. The most recent pinned message
Expand All @@ -56,6 +59,11 @@ class Chat(TelegramObject):
sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set.
can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the
sticker set.
linked_chat_id (:obj:`int`): Optional. Unique identifier for the linked chat, i.e. the
discussion group identifier for a channel and vice versa; for supergroups and channel
chats. Returned only in :meth:`telegram.Bot.get_chat`.
location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.

Args:
id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits
Expand All @@ -71,6 +79,8 @@ class Chat(TelegramObject):
last_name(:obj:`str`, optional): Last name of the other party in a private chat.
photo (:class:`telegram.ChatPhoto`, optional): Chat photo.
Returned only in :meth:`telegram.Bot.get_chat`.
bio (:obj:`str`, optional): Bio of the other party in a private chat. Returned only in
:meth:`telegram.Bot.get_chat`.
description (:obj:`str`, optional): Description, for groups, supergroups and channel chats.
Returned only in :meth:`telegram.Bot.get_chat`.
invite_link (:obj:`str`, optional): Chat invite link, for groups, supergroups and channel
Expand All @@ -89,6 +99,11 @@ class Chat(TelegramObject):
Returned only in :meth:`telegram.Bot.get_chat`.
can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the
sticker set. Returned only in :meth:`telegram.Bot.get_chat`.
linked_chat_id (:obj:`int`, optional): Unique identifier for the linked chat, i.e. the
discussion group identifier for a channel and vice versa; for supergroups and channel
chats. Returned only in :meth:`telegram.Bot.get_chat`.
location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which
the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`.
**kwargs (:obj:`dict`): Arbitrary keyword arguments.

"""
Expand Down Expand Up @@ -119,6 +134,9 @@ def __init__(
sticker_set_name: str = None,
can_set_sticker_set: bool = None,
slow_mode_delay: int = None,
bio: str = None,
linked_chat_id: int = None,
location: ChatLocation = None,
**_kwargs: Any,
):
# Required
Expand All @@ -132,13 +150,16 @@ def __init__(
# TODO: Remove (also from tests), when Telegram drops this completely
self.all_members_are_administrators = _kwargs.get('all_members_are_administrators')
self.photo = photo
self.bio = bio
self.description = description
self.invite_link = invite_link
self.pinned_message = pinned_message
self.permissions = permissions
self.slow_mode_delay = slow_mode_delay
self.sticker_set_name = sticker_set_name
self.can_set_sticker_set = can_set_sticker_set
self.linked_chat_id = linked_chat_id
self.location = location

self.bot = bot
self._id_attrs = (self.id,)
Expand All @@ -163,6 +184,7 @@ def de_json(cls, data: JSONDict, bot: 'Bot') -> Optional['Chat']:

data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot)
data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot)
data['location'] = ChatLocation.de_json(data.get('location'), bot)

return cls(bot=bot, **data)

Expand Down
70 changes: 70 additions & 0 deletions telegram/chatlocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# 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 location to which a chat is connected."""

from typing import TYPE_CHECKING, Any, Optional

from telegram import TelegramObject
from telegram.utils.types import JSONDict

from .files.location import Location

if TYPE_CHECKING:
from telegram import Bot


class ChatLocation(TelegramObject):
"""This object represents a location to which a chat is connected.

Objects of this class are comparable in terms of equality. Two objects of this class are
considered equal, if their :attr:`location` is equal.

Attributes:
location (:class:`telegram.Location`): The location to which the supergroup is connected.
address (:obj:`str`): Location address, as defined by the chat owner

Args:
location (:class:`telegram.Location`): The location to which the supergroup is connected.
Can't be a live location.
address (:obj:`str`): Location address; 1-64 characters, as defined by the chat owner
**kwargs (:obj:`dict`): Arbitrary keyword arguments.

"""

def __init__(
self,
location: Location,
address: str,
**_kwargs: Any,
):
self.location = location
self.address = address

self._id_attrs = (self.location,)

@classmethod
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatLocation']:
data = cls.parse_data(data)

if not data:
return None

data['location'] = Location.de_json(data.get('location'), bot)

return cls(bot=bot, **data)
12 changes: 7 additions & 5 deletions tests/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,15 +707,17 @@ def test(url, data, *args, **kwargs):
assert tz_bot.kick_chat_member(2, 32, until_date=until_timestamp)

# TODO: Needs improvement.
def test_unban_chat_member(self, monkeypatch, bot):
def test(url, data, *args, **kwargs):
@pytest.mark.parametrize('only_if_banned', [True, False, None])
def test_unban_chat_member(self, monkeypatch, bot, only_if_banned):
def make_assertion(url, data, *args, **kwargs):
chat_id = data['chat_id'] == 2
user_id = data['user_id'] == 32
return chat_id and user_id
o_i_b = data.get('only_if_banned', None) == only_if_banned
return chat_id and user_id and o_i_b

monkeypatch.setattr(bot.request, 'post', test)
monkeypatch.setattr(bot.request, 'post', make_assertion)

assert bot.unban_chat_member(2, 32)
assert bot.unban_chat_member(2, 32, only_if_banned=only_if_banned)

def test_set_chat_permissions(self, monkeypatch, bot, chat_permissions):
def test(url, data, *args, **kwargs):
Expand Down
30 changes: 24 additions & 6 deletions tests/test_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import pytest

from telegram import Chat, ChatAction, ChatPermissions
from telegram import Chat, ChatAction, ChatPermissions, ChatLocation, Location
from telegram import User


Expand All @@ -36,6 +36,9 @@ def chat(bot):
can_set_sticker_set=TestChat.can_set_sticker_set,
permissions=TestChat.permissions,
slow_mode_delay=TestChat.slow_mode_delay,
bio=TestChat.bio,
linked_chat_id=TestChat.linked_chat_id,
location=TestChat.location,
)


Expand All @@ -53,6 +56,9 @@ class TestChat:
can_invite_users=True,
)
slow_mode_delay = 30
bio = "I'm a Barbie Girl in a Barbie World"
linked_chat_id = 11880
location = ChatLocation(Location(123, 456), 'Barbie World')

def test_de_json(self, bot):
json_dict = {
Expand All @@ -65,6 +71,9 @@ def test_de_json(self, bot):
'can_set_sticker_set': self.can_set_sticker_set,
'permissions': self.permissions.to_dict(),
'slow_mode_delay': self.slow_mode_delay,
'bio': self.bio,
'linked_chat_id': self.linked_chat_id,
'location': self.location.to_dict(),
}
chat = Chat.de_json(json_dict, bot)

Expand All @@ -77,6 +86,10 @@ def test_de_json(self, bot):
assert chat.can_set_sticker_set == self.can_set_sticker_set
assert chat.permissions == self.permissions
assert chat.slow_mode_delay == self.slow_mode_delay
assert chat.bio == self.bio
assert chat.linked_chat_id == self.linked_chat_id
assert chat.location.location == self.location.location
assert chat.location.address == self.location.address

def test_to_dict(self, chat):
chat_dict = chat.to_dict()
Expand All @@ -89,6 +102,9 @@ def test_to_dict(self, chat):
assert chat_dict['all_members_are_administrators'] == chat.all_members_are_administrators
assert chat_dict['permissions'] == chat.permissions.to_dict()
assert chat_dict['slow_mode_delay'] == chat.slow_mode_delay
assert chat_dict['bio'] == chat.bio
assert chat_dict['linked_chat_id'] == chat.linked_chat_id
assert chat_dict['location'] == chat.location.to_dict()

def test_link(self, chat):
assert chat.link == 'https://t.me/{}'.format(chat.username)
Expand Down Expand Up @@ -145,14 +161,16 @@ def test(*args, **kwargs):
monkeypatch.setattr(chat.bot, 'kick_chat_member', test)
assert chat.kick_member(42, until_date=43)

def test_unban_member(self, monkeypatch, chat):
def test(*args, **kwargs):
@pytest.mark.parametrize('only_if_banned', [True, False, None])
def test_unban_member(self, monkeypatch, chat, only_if_banned):
def make_assertion(*args, **kwargs):
chat_id = args[0] == chat.id
user_id = args[1] == 42
return chat_id and user_id
o_i_b = kwargs.get('only_if_banned', None) == only_if_banned
return chat_id and user_id and o_i_b

monkeypatch.setattr(chat.bot, 'unban_chat_member', test)
assert chat.unban_member(42)
monkeypatch.setattr(chat.bot, 'unban_chat_member', make_assertion)
assert chat.unban_member(42, only_if_banned=only_if_banned)

def test_set_permissions(self, monkeypatch, chat):
def test(*args, **kwargs):
Expand Down
69 changes: 69 additions & 0 deletions tests/test_chatlocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2020
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# 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 Location, ChatLocation, User


@pytest.fixture(scope='class')
def chat_location(bot):
return ChatLocation(TestChatLocation.location, TestChatLocation.address)


class TestChatLocation:
location = Location(123, 456)
address = 'The Shire'

def test_de_json(self, bot):
json_dict = {
'location': self.location.to_dict(),
'address': self.address,
}
chat_location = ChatLocation.de_json(json_dict, bot)

assert chat_location.location == self.location
assert chat_location.address == self.address

def test_to_dict(self, chat_location):
chat_location_dict = chat_location.to_dict()

assert isinstance(chat_location_dict, dict)
assert chat_location_dict['location'] == chat_location.location.to_dict()
assert chat_location_dict['address'] == chat_location.address

def test_equality(self, chat_location):
a = chat_location
b = ChatLocation(self.location, self.address)
c = ChatLocation(self.location, 'Mordor')
d = ChatLocation(Location(456, 132), self.address)
e = User(456, '', False)

assert a == b
assert hash(a) == hash(b)
assert a is not b

assert a == c
assert hash(a) == hash(c)

assert a != d
assert hash(a) != hash(d)

assert a != e
assert hash(a) != hash(e)