diff --git a/docs/source/telegram.proximityalerttriggered.rst b/docs/source/telegram.proximityalerttriggered.rst new file mode 100644 index 00000000000..ab01987ab86 --- /dev/null +++ b/docs/source/telegram.proximityalerttriggered.rst @@ -0,0 +1,6 @@ +telegram.ProximityAlertTriggered +================================ + +.. autoclass:: telegram.ProximityAlertTriggered + :members: + :show-inheritance: diff --git a/telegram/__init__.py b/telegram/__init__.py index a9f8d4e9b91..2efe106d4f6 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -53,6 +53,7 @@ from .games.game import Game from .poll import Poll, PollOption, PollAnswer from .loginurl import LoginUrl +from .proximityalerttriggered import ProximityAlertTriggered from .games.callbackgame import CallbackGame from .payment.shippingaddress import ShippingAddress from .payment.orderinfo import OrderInfo @@ -189,6 +190,7 @@ 'InputTextMessageContent', 'InputVenueMessageContent', 'Location', + 'ProximityAlertTriggered', 'EncryptedCredentials', 'PassportFile', 'EncryptedPassportElement', diff --git a/telegram/bot.py b/telegram/bot.py index b1d8e7fa304..80480033f13 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -1321,6 +1321,9 @@ def send_location( location: Location = None, live_period: int = None, api_kwargs: JSONDict = None, + horizontal_accuracy: float = None, + heading: int = None, + proximity_alert_radius: int = None, ) -> Optional[Message]: """Use this method to send point on the map. @@ -1333,8 +1336,15 @@ def send_location( latitude (:obj:`float`, optional): Latitude of location. longitude (:obj:`float`, optional): Longitude of location. location (:class:`telegram.Location`, optional): The location to send. + horizontal_accuracy (:obj:`int`, optional): The radius of uncertainty for the location, + measured in meters; 0-1500. live_period (:obj:`int`, optional): Period in seconds for which the location will be updated, should be between 60 and 86400. + heading (:obj:`int`, optional): For live locations, a direction in which the user is + moving, in degrees. Must be between 1 and 360 if specified. + proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance + for proximity alerts about approaching another chat member, in meters. Must be + between 1 and 100000 if specified. disable_notification (:obj:`bool`, optional): Sends the message silently. Users will receive a notification with no sound. reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the @@ -1373,6 +1383,12 @@ def send_location( if live_period: data['live_period'] = live_period + if horizontal_accuracy: + data['horizontal_accuracy'] = horizontal_accuracy + if heading: + data['heading'] = heading + if proximity_alert_radius: + data['proximity_alert_radius'] = proximity_alert_radius return self._message( # type: ignore[return-value] 'sendLocation', @@ -1396,6 +1412,9 @@ def edit_message_live_location( reply_markup: ReplyMarkup = None, timeout: float = None, api_kwargs: JSONDict = None, + horizontal_accuracy: float = None, + heading: int = None, + proximity_alert_radius: int = None, ) -> Union[Optional[Message], bool]: """Use this method to edit live location messages sent by the bot or via the bot (for inline bots). A location can be edited until its :attr:`live_period` expires or @@ -1415,6 +1434,13 @@ def edit_message_live_location( latitude (:obj:`float`, optional): Latitude of location. longitude (:obj:`float`, optional): Longitude of location. location (:class:`telegram.Location`, optional): The location to send. + horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the + location, measured in meters; 0-1500. + heading (:obj:`int`, optional): Direction in which the user is moving, in degrees. Must + be between 1 and 360 if specified. + proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts + about approaching another chat member, in meters. Must be between 1 and 100000 if + specified. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized object for a new inline keyboard. timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as @@ -1448,6 +1474,12 @@ def edit_message_live_location( data['message_id'] = message_id if inline_message_id: data['inline_message_id'] = inline_message_id + if horizontal_accuracy: + data['horizontal_accuracy'] = horizontal_accuracy + if heading: + data['heading'] = heading + if proximity_alert_radius: + data['proximity_alert_radius'] = proximity_alert_radius return self._message( 'editMessageLiveLocation', diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 7fc2467050d..88f9c99e292 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -1030,6 +1030,15 @@ def filter(self, message: Message) -> bool: connected_website = _ConnectedWebsite() """Messages that contain :attr:`telegram.Message.connected_website`.""" + class _ProximityAlertTriggered(MessageFilter): + name = 'Filters.status_update.proximity_alert_triggered' + + def filter(self, message: Message) -> bool: + return bool(message.proximity_alert_triggered) + + proximity_alert_triggered = _ProximityAlertTriggered() + """Messages that contain :attr:`telegram.Message.proximity_alert_triggered`.""" + name = 'Filters.status_update' def filter(self, message: Update) -> bool: @@ -1043,6 +1052,7 @@ def filter(self, message: Update) -> bool: or self.migrate(message) or self.pinned_message(message) or self.connected_website(message) + or self.proximity_alert_triggered(message) ) status_update = _StatusUpdate() @@ -1057,6 +1067,8 @@ def filter(self, message: Update) -> bool: :attr:`telegram.Message.group_chat_created`, :attr:`telegram.Message.supergroup_chat_created` or :attr:`telegram.Message.channel_chat_created`. + connected_website: Messages that contain + :attr:`telegram.Message.connected_website`. delete_chat_photo: Messages that contain :attr:`telegram.Message.delete_chat_photo`. left_chat_member: Messages that contain @@ -1072,6 +1084,8 @@ def filter(self, message: Update) -> bool: :attr:`telegram.Message.new_chat_title`. pinned_message: Messages that contain :attr:`telegram.Message.pinned_message`. + proximity_alert_triggered: Messages that contain + :attr:`telegram.Message.proximity_alert_triggered`. """ class _Forwarded(MessageFilter): diff --git a/telegram/files/location.py b/telegram/files/location.py index bf8fb3d9982..d8c29387c4b 100644 --- a/telegram/files/location.py +++ b/telegram/files/location.py @@ -32,17 +32,50 @@ class Location(TelegramObject): Attributes: longitude (:obj:`float`): Longitude as defined by sender. latitude (:obj:`float`): Latitude as defined by sender. + horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, + measured in meters. + live_period (:obj:`int`): Optional. Time relative to the message sending date, during which + the location can be updated, in seconds. For active live locations only. + heading (:obj:`int`): Optional. The direction in which user is moving, in degrees. + For active live locations only. + proximity_alert_radius (:obj:`int`): Optional. Maximum distance for proximity alerts about + approaching another chat member, in meters. For sent live locations only. Args: longitude (:obj:`float`): Longitude as defined by sender. latitude (:obj:`float`): Latitude as defined by sender. + horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, + measured in meters; 0-1500. + live_period (:obj:`int`, optional): Time relative to the message sending date, during which + the location can be updated, in seconds. For active live locations only. + heading (:obj:`int`, optional): The direction in which user is moving, in degrees; 1-360. + For active live locations only. + proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts about + approaching another chat member, in meters. For sent live locations only. **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ - def __init__(self, longitude: float, latitude: float, **_kwargs: Any): + def __init__( + self, + longitude: float, + latitude: float, + horizontal_accuracy: float = None, + live_period: int = None, + heading: int = None, + proximity_alert_radius: int = None, + **_kwargs: Any, + ): # Required self.longitude = float(longitude) self.latitude = float(latitude) + # Optionals + self.horizontal_accuracy = float(horizontal_accuracy) if horizontal_accuracy else None + self.live_period = int(live_period) if live_period else None + self.heading = int(heading) if heading else None + self.proximity_alert_radius = ( + int(proximity_alert_radius) if proximity_alert_radius else None + ) + self._id_attrs = (self.longitude, self.latitude) diff --git a/telegram/inline/inlinequeryresultlocation.py b/telegram/inline/inlinequeryresultlocation.py index 8701c1b5584..87a66a9a1b1 100644 --- a/telegram/inline/inlinequeryresultlocation.py +++ b/telegram/inline/inlinequeryresultlocation.py @@ -38,8 +38,14 @@ class InlineQueryResultLocation(InlineQueryResult): latitude (:obj:`float`): Location latitude in degrees. longitude (:obj:`float`): Location longitude in degrees. title (:obj:`str`): Location title. + horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, + measured in meters. live_period (:obj:`int`): Optional. Period in seconds for which the location can be updated, should be between 60 and 86400. + heading (:obj:`int`): Optional. For live locations, a direction in which the user is + moving, in degrees. + proximity_alert_radius (:obj:`int`): Optional. For live locations, a maximum distance for + proximity alerts about approaching another chat member, in meters. reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -53,8 +59,15 @@ class InlineQueryResultLocation(InlineQueryResult): latitude (:obj:`float`): Location latitude in degrees. longitude (:obj:`float`): Location longitude in degrees. title (:obj:`str`): Location title. + horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, + measured in meters; 0-1500. live_period (:obj:`int`, optional): Period in seconds for which the location can be updated, should be between 60 and 86400. + heading (:obj:`int`, optional): For live locations, a direction in which the user is + moving, in degrees. Must be between 1 and 360 if specified. + proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance for + proximity alerts about approaching another chat member, in meters. Must be between 1 + and 100000 if specified. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -78,12 +91,15 @@ def __init__( thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, + horizontal_accuracy: float = None, + heading: int = None, + proximity_alert_radius: int = None, **_kwargs: Any, ): # Required super().__init__('location', id) - self.latitude = latitude - self.longitude = longitude + self.latitude = float(latitude) + self.longitude = float(longitude) self.title = title # Optionals @@ -93,3 +109,8 @@ def __init__( self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height + self.horizontal_accuracy = float(horizontal_accuracy) if horizontal_accuracy else None + self.heading = int(heading) if heading else None + self.proximity_alert_radius = ( + int(proximity_alert_radius) if proximity_alert_radius else None + ) diff --git a/telegram/inline/inputlocationmessagecontent.py b/telegram/inline/inputlocationmessagecontent.py index abb446db26b..2a796ff9848 100644 --- a/telegram/inline/inputlocationmessagecontent.py +++ b/telegram/inline/inputlocationmessagecontent.py @@ -36,21 +36,44 @@ class InputLocationMessageContent(InputMessageContent): longitude (:obj:`float`): Longitude of the location in degrees. live_period (:obj:`int`): Optional. Period in seconds for which the location can be updated. + heading (:obj:`int`): Optional. For live locations, a direction in which the user is + moving, in degrees. + proximity_alert_radius (:obj:`int`): Optional. For live locations, a maximum distance for + proximity alerts about approaching another chat member, in meters. Args: latitude (:obj:`float`): Latitude of the location in degrees. longitude (:obj:`float`): Longitude of the location in degrees. live_period (:obj:`int`, optional): Period in seconds for which the location can be updated, should be between 60 and 86400. + heading (:obj:`int`, optional): For live locations, a direction in which the user is + moving, in degrees. Must be between 1 and 360 if specified. + proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance for + proximity alerts about approaching another chat member, in meters. Must be between 1 + and 100000 if specified. **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ # fmt: on - def __init__(self, latitude: float, longitude: float, live_period: int = None, **_kwargs: Any): + def __init__( + self, + latitude: float, + longitude: float, + live_period: int = None, + heading: int = None, + proximity_alert_radius: int = None, + **_kwargs: Any, + ): # Required self.latitude = latitude self.longitude = longitude - self.live_period = live_period + + # Optionals + self.live_period = int(live_period) if live_period else None + self.heading = int(heading) if heading else None + self.proximity_alert_radius = ( + int(proximity_alert_radius) if proximity_alert_radius else None + ) self._id_attrs = (self.latitude, self.longitude) diff --git a/telegram/message.py b/telegram/message.py index 7442388f9d3..d08794b2373 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -47,6 +47,7 @@ Video, VideoNote, Voice, + ProximityAlertTriggered, ) from telegram.utils.helpers import escape_markdown, from_timestamp, to_timestamp from telegram.utils.types import JSONDict @@ -145,6 +146,9 @@ class Message(TelegramObject): information about the poll. dice (:class:`telegram.Dice`): Optional. Message is a dice. via_bot (:class:`telegram.User`): Optional. Bot through which the message was sent. + proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`): Optional. Service + message. A user in the chat triggered another user's proximity alert while sharing + Live Location. reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. @@ -257,6 +261,9 @@ class Message(TelegramObject): information about the poll. dice (:class:`telegram.Dice`, optional): Message is a dice with random value from 1 to 6. via_bot (:class:`telegram.User`, optional): Message was sent through an inline bot. + proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`, optional): Service + message. A user in the chat triggered another user's proximity alert while sharing + Live Location. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. ``login_url`` buttons are represented as ordinary url buttons. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. @@ -298,6 +305,7 @@ class Message(TelegramObject): 'poll', 'dice', 'passport_data', + 'proximity_alert_triggered', ] + ATTACHMENT_TYPES def __init__( @@ -352,6 +360,7 @@ def __init__( bot: 'Bot' = None, dice: Dice = None, via_bot: User = None, + proximity_alert_triggered: ProximityAlertTriggered = None, sender_chat: Chat = None, **_kwargs: Any, ): @@ -406,6 +415,7 @@ def __init__( self.poll = poll self.dice = dice self.via_bot = via_bot + self.proximity_alert_triggered = proximity_alert_triggered self.reply_markup = reply_markup self.bot = bot @@ -469,6 +479,9 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> 'Message': data['poll'] = Poll.de_json(data.get('poll'), bot) data['dice'] = Dice.de_json(data.get('dice'), bot) data['via_bot'] = User.de_json(data.get('via_bot'), bot) + data['proximity_alert_triggered'] = ProximityAlertTriggered.de_json( + data.get('proximity_alert_triggered'), bot + ) data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot) return cls(bot=bot, **data) diff --git a/telegram/proximityalerttriggered.py b/telegram/proximityalerttriggered.py new file mode 100644 index 00000000000..bfab7a2d984 --- /dev/null +++ b/telegram/proximityalerttriggered.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2020 +# 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 Proximity Alert.""" +from typing import Any, Optional, TYPE_CHECKING + +from telegram import TelegramObject, User +from telegram.utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot + + +class ProximityAlertTriggered(TelegramObject): + """ + This object represents the content of a service message, sent whenever a user in the chat + triggers a proximity alert set by another user. + + Objects of this class are comparable in terms of equality. Two objects of this class are + considered equal, if their :attr:`traveler`, :attr:`watcher` and :attr:`distance` are equal. + + Attributes: + traveler (:class:`telegram.User`): User that triggered the alert + watcher (:class:`telegram.User`): User that set the alert + distance (:obj:`int`): The distance between the users + + Args: + traveler (:class:`telegram.User`): User that triggered the alert + watcher (:class:`telegram.User`): User that set the alert + distance (:obj:`int`): The distance between the users + """ + + def __init__(self, traveler: User, watcher: User, distance: int, **_kwargs: Any): + self.traveler = traveler + self.watcher = watcher + self.distance = distance + + self._id_attrs = (self.traveler, self.watcher, self.distance) + + @classmethod + def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ProximityAlertTriggered']: + data = cls.parse_data(data) + + if not data: + return None + + data['traveler'] = User.de_json(data.get('traveler'), bot) + data['watcher'] = User.de_json(data.get('watcher'), bot) + + return cls(bot=bot, **data) diff --git a/tests/test_filters.py b/tests/test_filters.py index e32c5dd7424..efcd80ccdab 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -847,6 +847,11 @@ def test_filters_status_update(self, update): assert Filters.status_update.connected_website(update) update.message.connected_website = None + update.message.proximity_alert_triggered = 'alert' + assert Filters.status_update(update) + assert Filters.status_update.proximity_alert_triggered(update) + update.message.proximity_alert_triggered = None + def test_filters_forwarded(self, update): assert not Filters.forwarded(update) update.message.forward_date = datetime.datetime.utcnow() diff --git a/tests/test_inlinequeryresultlocation.py b/tests/test_inlinequeryresultlocation.py index 7586f029256..e5d05cd25ba 100644 --- a/tests/test_inlinequeryresultlocation.py +++ b/tests/test_inlinequeryresultlocation.py @@ -41,6 +41,9 @@ def inline_query_result_location(): thumb_height=TestInlineQueryResultLocation.thumb_height, input_message_content=TestInlineQueryResultLocation.input_message_content, reply_markup=TestInlineQueryResultLocation.reply_markup, + horizontal_accuracy=TestInlineQueryResultLocation.horizontal_accuracy, + heading=TestInlineQueryResultLocation.heading, + proximity_alert_radius=TestInlineQueryResultLocation.proximity_alert_radius, ) @@ -50,7 +53,10 @@ class TestInlineQueryResultLocation: latitude = 0.0 longitude = 1.0 title = 'title' + horizontal_accuracy = 999 live_period = 70 + heading = 90 + proximity_alert_radius = 1000 thumb_url = 'thumb url' thumb_width = 10 thumb_height = 15 @@ -72,6 +78,9 @@ def test_expected_values(self, inline_query_result_location): == self.input_message_content.to_dict() ) assert inline_query_result_location.reply_markup.to_dict() == self.reply_markup.to_dict() + assert inline_query_result_location.heading == self.heading + assert inline_query_result_location.horizontal_accuracy == self.horizontal_accuracy + assert inline_query_result_location.proximity_alert_radius == self.proximity_alert_radius def test_to_dict(self, inline_query_result_location): inline_query_result_location_dict = inline_query_result_location.to_dict() @@ -111,6 +120,15 @@ def test_to_dict(self, inline_query_result_location): inline_query_result_location_dict['reply_markup'] == inline_query_result_location.reply_markup.to_dict() ) + assert ( + inline_query_result_location_dict['horizontal_accuracy'] + == inline_query_result_location.horizontal_accuracy + ) + assert inline_query_result_location_dict['heading'] == inline_query_result_location.heading + assert ( + inline_query_result_location_dict['proximity_alert_radius'] + == inline_query_result_location.proximity_alert_radius + ) def test_equality(self): a = InlineQueryResultLocation(self.id_, self.longitude, self.latitude, self.title) diff --git a/tests/test_inputlocationmessagecontent.py b/tests/test_inputlocationmessagecontent.py index 2dcfb9a1278..916d5049962 100644 --- a/tests/test_inputlocationmessagecontent.py +++ b/tests/test_inputlocationmessagecontent.py @@ -28,6 +28,8 @@ def input_location_message_content(): TestInputLocationMessageContent.latitude, TestInputLocationMessageContent.longitude, live_period=TestInputLocationMessageContent.live_period, + heading=TestInputLocationMessageContent.heading, + proximity_alert_radius=TestInputLocationMessageContent.proximity_alert_radius, ) @@ -35,11 +37,15 @@ class TestInputLocationMessageContent: latitude = -23.691288 longitude = -46.788279 live_period = 80 + heading = 90 + proximity_alert_radius = 999 def test_expected_values(self, input_location_message_content): assert input_location_message_content.longitude == self.longitude assert input_location_message_content.latitude == self.latitude assert input_location_message_content.live_period == self.live_period + assert input_location_message_content.heading == self.heading + assert input_location_message_content.proximity_alert_radius == self.proximity_alert_radius def test_to_dict(self, input_location_message_content): input_location_message_content_dict = input_location_message_content.to_dict() @@ -57,6 +63,14 @@ def test_to_dict(self, input_location_message_content): input_location_message_content_dict['live_period'] == input_location_message_content.live_period ) + assert ( + input_location_message_content_dict['heading'] + == input_location_message_content.heading + ) + assert ( + input_location_message_content_dict['proximity_alert_radius'] + == input_location_message_content.proximity_alert_radius + ) def test_equality(self): a = InputLocationMessageContent(123, 456, 70) diff --git a/tests/test_location.py b/tests/test_location.py index c111110d041..af7e24c2224 100644 --- a/tests/test_location.py +++ b/tests/test_location.py @@ -26,37 +26,78 @@ @pytest.fixture(scope='class') def location(): - return Location(latitude=TestLocation.latitude, longitude=TestLocation.longitude) + return Location( + latitude=TestLocation.latitude, + longitude=TestLocation.longitude, + horizontal_accuracy=TestLocation.horizontal_accuracy, + live_period=TestLocation.live_period, + heading=TestLocation.live_period, + proximity_alert_radius=TestLocation.proximity_alert_radius, + ) class TestLocation: latitude = -23.691288 longitude = -46.788279 + horizontal_accuracy = 999 + live_period = 60 + heading = 90 + proximity_alert_radius = 50 def test_de_json(self, bot): - json_dict = {'latitude': TestLocation.latitude, 'longitude': TestLocation.longitude} + json_dict = { + 'latitude': TestLocation.latitude, + 'longitude': TestLocation.longitude, + 'horizontal_accuracy': TestLocation.horizontal_accuracy, + 'live_period': TestLocation.live_period, + 'heading': TestLocation.heading, + 'proximity_alert_radius': TestLocation.proximity_alert_radius, + } location = Location.de_json(json_dict, bot) assert location.latitude == self.latitude assert location.longitude == self.longitude + assert location.horizontal_accuracy == self.horizontal_accuracy + assert location.live_period == self.live_period + assert location.heading == self.heading + assert location.proximity_alert_radius == self.proximity_alert_radius @flaky(3, 1) @pytest.mark.xfail @pytest.mark.timeout(10) def test_send_live_location(self, bot, chat_id): message = bot.send_location( - chat_id=chat_id, latitude=52.223880, longitude=5.166146, live_period=80 + chat_id=chat_id, + latitude=52.223880, + longitude=5.166146, + live_period=80, + horizontal_accuracy=50, + heading=90, + proximity_alert_radius=1000, ) assert message.location - assert message.location.latitude == 52.223880 - assert message.location.longitude == 5.166146 + assert pytest.approx(52.223880, message.location.latitude) + assert pytest.approx(5.166146, message.location.longitude) + assert message.location.live_period == 80 + assert message.location.horizontal_accuracy == 50 + assert message.location.heading == 90 + assert message.location.proximity_alert_radius == 1000 message2 = bot.edit_message_live_location( - message.chat_id, message.message_id, latitude=52.223098, longitude=5.164306 + message.chat_id, + message.message_id, + latitude=52.223098, + longitude=5.164306, + horizontal_accuracy=30, + heading=10, + proximity_alert_radius=500, ) - assert message2.location.latitude == 52.223098 - assert message2.location.longitude == 5.164306 + assert pytest.approx(52.223098, message2.location.latitude) + assert pytest.approx(5.164306, message2.location.longitude) + assert message2.location.horizontal_accuracy == 30 + assert message2.location.heading == 10 + assert message2.location.proximity_alert_radius == 500 bot.stop_message_live_location(message.chat_id, message.message_id) with pytest.raises(BadRequest, match="Message can't be edited"): @@ -66,14 +107,23 @@ def test_send_live_location(self, bot, chat_id): # TODO: Needs improvement with in inline sent live location. def test_edit_live_inline_message(self, monkeypatch, bot, location): - def test(url, data, **kwargs): + def make_assertion(url, data, **kwargs): lat = data['latitude'] == location.latitude lon = data['longitude'] == location.longitude id_ = data['inline_message_id'] == 1234 - return lat and lon and id_ - - monkeypatch.setattr(bot.request, 'post', test) - assert bot.edit_message_live_location(inline_message_id=1234, location=location) + ha = data['horizontal_accuracy'] == 50 + heading = data['heading'] == 90 + prox_alert = data['proximity_alert_radius'] == 1000 + return lat and lon and id_ and ha and heading and prox_alert + + monkeypatch.setattr(bot.request, 'post', make_assertion) + assert bot.edit_message_live_location( + inline_message_id=1234, + location=location, + horizontal_accuracy=50, + heading=90, + proximity_alert_radius=1000, + ) # TODO: Needs improvement with in inline sent live location. def test_stop_live_inline_message(self, monkeypatch, bot): @@ -125,6 +175,10 @@ def test_to_dict(self, location): assert location_dict['latitude'] == location.latitude assert location_dict['longitude'] == location.longitude + assert location_dict['horizontal_accuracy'] == location.horizontal_accuracy + assert location_dict['live_period'] == location.live_period + assert location['heading'] == location.heading + assert location['proximity_alert_radius'] == location.proximity_alert_radius def test_equality(self): a = Location(self.longitude, self.latitude) diff --git a/tests/test_message.py b/tests/test_message.py index 3de0c07af71..c38a84a214a 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -44,6 +44,7 @@ ParseMode, Poll, PollOption, + ProximityAlertTriggered, Dice, ) from telegram.ext import Defaults @@ -157,6 +158,11 @@ def message(bot): {'quote': True}, {'dice': Dice(4, '🎲')}, {'via_bot': User(9, 'A_Bot', True)}, + { + 'proximity_alert_triggered': ProximityAlertTriggered( + User(1, 'John', False), User(2, 'Doe', False), 42 + ) + }, {'sender_chat': Chat(-123, 'discussion_channel')}, ], ids=[ @@ -201,6 +207,7 @@ def message(bot): 'default_quote', 'dice', 'via_bot', + 'proximity_alert_triggered', 'sender_chat', ], ) diff --git a/tests/test_proximityalerttriggered.py b/tests/test_proximityalerttriggered.py new file mode 100644 index 00000000000..5ae8835c56c --- /dev/null +++ b/tests/test_proximityalerttriggered.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2020 +# 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 BotCommand, User, ProximityAlertTriggered + + +@pytest.fixture(scope="class") +def proximity_alert_triggered(): + return ProximityAlertTriggered( + traveler=TestProximityAlertTriggered.traveler, + watcher=TestProximityAlertTriggered.watcher, + distance=TestProximityAlertTriggered.distance, + ) + + +class TestProximityAlertTriggered: + traveler = User(1, 'foo', False) + watcher = User(2, 'bar', False) + distance = 42 + + def test_de_json(self, bot): + json_dict = { + 'traveler': self.traveler.to_dict(), + 'watcher': self.watcher.to_dict(), + 'distance': self.distance, + } + proximity_alert_triggered = ProximityAlertTriggered.de_json(json_dict, bot) + + assert proximity_alert_triggered.traveler == self.traveler + assert proximity_alert_triggered.traveler.first_name == self.traveler.first_name + assert proximity_alert_triggered.watcher == self.watcher + assert proximity_alert_triggered.watcher.first_name == self.watcher.first_name + assert proximity_alert_triggered.distance == self.distance + + def test_to_dict(self, proximity_alert_triggered): + proximity_alert_triggered_dict = proximity_alert_triggered.to_dict() + + assert isinstance(proximity_alert_triggered_dict, dict) + assert ( + proximity_alert_triggered_dict['traveler'] + == proximity_alert_triggered.traveler.to_dict() + ) + assert ( + proximity_alert_triggered_dict['watcher'] + == proximity_alert_triggered.watcher.to_dict() + ) + assert proximity_alert_triggered_dict['distance'] == proximity_alert_triggered.distance + + def test_equality(self, proximity_alert_triggered): + a = proximity_alert_triggered + b = ProximityAlertTriggered(User(1, 'John', False), User(2, 'Doe', False), 42) + c = ProximityAlertTriggered(User(3, 'John', False), User(2, 'Doe', False), 42) + d = ProximityAlertTriggered(User(1, 'John', False), User(3, 'Doe', False), 42) + e = ProximityAlertTriggered(User(1, 'John', False), User(2, 'Doe', False), 43) + f = BotCommand('start', 'description') + + assert a == b + assert hash(a) == hash(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) + + assert a != f + assert hash(a) != hash(f)