Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bot API 6.8: Stories, voting as a chat, and other minor changes #2030

Merged
merged 6 commits into from
Aug 20, 2023
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Both synchronous and asynchronous.</p>

## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#april-21-2023">6.7</a>!
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#august-18-2023">6.8</a>!

<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
<h2><a href='https://pytba.readthedocs.io/ru/latest/index.html'>Official ru documentation</a></h2>
Expand Down
18 changes: 18 additions & 0 deletions telebot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4571,6 +4571,24 @@ def answer_inline_query(
return apihelper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset,
button)

def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool:
"""
Use this method to clear the list of pinned messages in a General forum topic.
The bot must be an administrator in the chat for this to work and must have the
can_pin_messages administrator right in the supergroup.
Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages

:param chat_id: Unique identifier for the target chat or username of chat
:type chat_id: :obj:`int` | :obj:`str`

:return: On success, True is returned.
:rtype: :obj:`bool`
"""

return apihelper.unpin_all_general_forum_topic_messages(self.token, chat_id)

def answer_callback_query(
self, callback_query_id: int,
text: Optional[str]=None, show_alert: Optional[bool]=None,
Expand Down
5 changes: 5 additions & 0 deletions telebot/apihelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,11 @@ def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_message=No
return _make_request(token, method_url, params=payload)


def unpin_all_general_forum_topic_messages(token, chat_id):
method_url = 'unpinAllGeneralForumTopicMessages'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload, method='post')

# InlineQuery

def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None):
Expand Down
18 changes: 18 additions & 0 deletions telebot/async_telebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5424,6 +5424,24 @@ async def answer_inline_query(
return await asyncio_helper.answer_inline_query(self.token, inline_query_id, results, cache_time, is_personal, next_offset,
button)

async def unpin_all_general_forum_topic_messages(self, chat_id: Union[int, str]) -> bool:
"""
Use this method to clear the list of pinned messages in a General forum topic.
The bot must be an administrator in the chat for this to work and must have the
can_pin_messages administrator right in the supergroup.
Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#unpinAllGeneralForumTopicMessages

:param chat_id: Unique identifier for the target chat or username of chat
:type chat_id: :obj:`int` | :obj:`str`

:return: On success, True is returned.
:rtype: :obj:`bool`
"""

return await asyncio_helper.unpin_all_general_forum_topic_messages(self.token, chat_id)

async def answer_callback_query(
self, callback_query_id: int,
text: Optional[str]=None, show_alert: Optional[bool]=None,
Expand Down
5 changes: 5 additions & 0 deletions telebot/asyncio_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,11 @@ async def answer_pre_checkout_query(token, pre_checkout_query_id, ok, error_mess
return await _process_request(token, method_url, params=payload)


async def unpin_all_general_forum_topic_messages(token, chat_id):
method_url = 'unpinAllGeneralForumTopicMessages'
payload = {'chat_id': chat_id}
return await _process_request(token, method_url, params=payload, method='post')

# InlineQuery

async def answer_callback_query(token, callback_query_id, text=None, show_alert=None, url=None, cache_time=None):
Expand Down
63 changes: 55 additions & 8 deletions telebot/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,10 @@ class Chat(JsonDeserializable):
Returned only in getChat.
:type emoji_status_custom_emoji_id: :obj:`str`

:param emoji_status_expiration_date: Optional. Expiration date of the emoji status of the other party in a private chat,
if any. Returned only in getChat.
:type emoji_status_expiration_date: :obj:`int`

:param bio: Optional. Bio of the other party in a private chat. Returned only in getChat.
:type bio: :obj:`str`

Expand Down Expand Up @@ -638,7 +642,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None,
can_set_sticker_set=None, linked_chat_id=None, location=None,
join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None,
is_forum=None, active_usernames=None, emoji_status_custom_emoji_id=None,
has_hidden_members=None, has_aggressive_anti_spam_enabled=None, **kwargs):
has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, **kwargs):
self.id: int = id
self.type: str = type
self.title: str = title
Expand Down Expand Up @@ -667,6 +671,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None,
self.emoji_status_custom_emoji_id: str = emoji_status_custom_emoji_id
self.has_hidden_members: bool = has_hidden_members
self.has_aggressive_anti_spam_enabled: bool = has_aggressive_anti_spam_enabled
self.emoji_status_expiration_date: int = emoji_status_expiration_date


class MessageID(JsonDeserializable):
Expand Down Expand Up @@ -821,6 +826,9 @@ class Message(JsonDeserializable):
:param sticker: Optional. Message is a sticker, information about the sticker
:type sticker: :class:`telebot.types.Sticker`

:param story: Optional. Message is a forwarded story
:type story: :class:`telebot.types.Story`

:param video: Optional. Message is a video, information about the video
:type video: :class:`telebot.types.Video`

Expand Down Expand Up @@ -1177,6 +1185,9 @@ def de_json(cls, json_string):
if 'chat_shared' in obj:
opts['chat_shared'] = ChatShared.de_json(obj['chat_shared'])
content_type = 'chat_shared'
if 'story' in obj:
opts['story'] = Story.de_json(obj['story'])
content_type = 'story'
return cls(message_id, from_user, date, chat, content_type, opts, json_string)

@classmethod
Expand Down Expand Up @@ -1274,10 +1285,12 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso
self.write_access_allowed: Optional[WriteAccessAllowed] = None
self.user_shared: Optional[UserShared] = None
self.chat_shared: Optional[ChatShared] = None
self.story: Optional[Story] = None
for key in options:
setattr(self, key, options[key])
self.json = json_string


def __html_text(self, text, entities):
"""
Author: @sviat9440
Expand Down Expand Up @@ -6680,7 +6693,10 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable):
:param poll_id: Unique poll identifier
:type poll_id: :obj:`str`

:param user: The user, who changed the answer to the poll
:param voter_chat: Optional. The chat that changed the answer to the poll, if the voter is anonymous
:type voter_chat: :class:`telebot.types.Chat`

:param user: Optional. The user, who changed the answer to the poll
:type user: :class:`telebot.types.User`

:param option_ids: 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted
Expand All @@ -6694,21 +6710,34 @@ class PollAnswer(JsonSerializable, JsonDeserializable, Dictionaryable):
def de_json(cls, json_string):
if (json_string is None): return None
obj = cls.check_json(json_string)
obj['user'] = User.de_json(obj['user'])
if 'user' in obj:
obj['user'] = User.de_json(obj['user'])
if 'voter_chat' in obj:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User is now optional and should be processed the same as voter_chat with if

obj['voter_chat'] = Chat.de_json(obj['voter_chat'])
return cls(**obj)

def __init__(self, poll_id, user, option_ids, **kwargs):
def __init__(self, poll_id, option_ids, user=None, voter_chat=None, **kwargs):
self.poll_id: str = poll_id
self.user: User = user
self.user: Optional[User] = user
self.option_ids: List[int] = option_ids
self.voter_chat: Optional[Chat] = voter_chat


def to_json(self):
return json.dumps(self.to_dict())

def to_dict(self):
return {'poll_id': self.poll_id,
'user': self.user.to_dict(),
'option_ids': self.option_ids}
# Left for backward compatibility, but with no support for voter_chat
json_dict = {
"poll_id": self.poll_id,
"option_ids": self.option_ids
}
if self.user:
json_dict["user"] = self.user.to_dict()
if self.voter_chat:
json_dict["voter_chat"] = self.voter_chat
return json_dict



class ChatLocation(JsonSerializable, JsonDeserializable, Dictionaryable):
Expand Down Expand Up @@ -7743,3 +7772,21 @@ def to_dict(self) -> dict:

def to_json(self) -> str:
return json.dumps(self.to_dict())


class Story(JsonDeserializable):
"""
This object represents a message about a forwarded story in the chat.
Currently holds no information.
"""

@classmethod
def de_json(cls, json_string):
if json_string is None:
return None
obj = cls.check_json(json_string)
return cls(**obj)

def __init__(self) -> None:
pass

Loading