From bb4f6a71906382eb3f51f3c06e234835b687905d Mon Sep 17 00:00:00 2001 From: Badiboy Date: Sat, 25 Apr 2020 22:22:08 +0300 Subject: [PATCH] Polls are updated to the latest API state. Polls are updated to the latest API state. Minor code refactoring. --- telebot/__init__.py | 4 +- telebot/apihelper.py | 31 +++++++++++++-- telebot/types.py | 93 ++++++++++++++++++++++++++------------------ tests/test_types.py | 12 ++++++ 4 files changed, 97 insertions(+), 43 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index ad3d00d..d0dc434 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1208,7 +1208,7 @@ class TeleBot: disable_notification, reply_to_message_id, reply_markup, provider_data) return types.Message.de_json(result) - def send_poll(self, chat_id, poll, disable_notifications=False, reply_to_message=None, reply_markup=None): + def send_poll(self, chat_id, poll, explanation_parse_mode=None, disable_notifications=False, reply_to_message=None, reply_markup=None): """ Sends poll :param chat_id: @@ -1218,7 +1218,7 @@ class TeleBot: :param reply_markup: :return: """ - return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll.question, poll.options, disable_notifications, reply_to_message, reply_markup)) + return types.Message.de_json(apihelper.send_poll(self.token, chat_id, poll, explanation_parse_mode, disable_notifications, reply_to_message, reply_markup)) def stop_poll(self, chat_id, message_id): """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 8eb4744..c79bd71 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -996,14 +996,37 @@ def delete_sticker_from_set(token, sticker): return _make_request(token, method_url, params=payload, method='post') -def send_poll(token, chat_id, question, options, disable_notifications=False, reply_to_message_id=None, reply_markup=None): +def send_poll(token, chat_id, poll, explanation_parse_mode=None, disable_notifications=False, reply_to_message_id=None, reply_markup=None): method_url = r'sendPoll' - payload = {'chat_id': str(chat_id), 'question': question, 'options': _convert_list_json_serializable(options)} + payload = { + 'chat_id': str(chat_id), + 'question': poll.question, + 'options': _convert_list_json_serializable(poll.options)} + + if poll.is_anonymous is not None: + payload['is_anonymous'] = poll.is_anonymous + if poll.type is not None: + payload['type'] = poll.type + if poll.allows_multiple_answers is not None: + payload['allows_multiple_answers'] = poll.allows_multiple_answers + if poll.correct_option_id is not None: + payload['correct_option_id'] = poll.correct_option_id + if poll.explanation is not None: + payload['explanation'] = poll.explanation + if explanation_parse_mode is not None: + payload['explanation_parse_mode'] = explanation_parse_mode + if poll.open_period is not None: + payload['open_period'] = poll.open_period + if poll.close_date is not None: + payload['close_date'] = poll.close_date + if poll.is_closed is not None: + payload['is_closed'] = poll.is_closed + if disable_notifications: payload['disable_notification'] = disable_notifications - if reply_to_message_id: + if reply_to_message_id is not None: payload['reply_to_message_id'] = reply_to_message_id - if reply_markup: + if reply_markup is not None: payload['reply_markup'] = _convert_markup(reply_markup) return _make_request(token, method_url, params=payload) diff --git a/telebot/types.py b/telebot/types.py index 8929111..ee33ff2 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -34,10 +34,10 @@ class Dictionaryable(object): def to_dic(self): """ - Returns a JSON string representation of this class. + Returns a DICT with class field values This function must be overridden by subclasses. - :return: a JSON formatted string. + :return: a DICT """ raise NotImplementedError @@ -450,23 +450,23 @@ class Message(JsonDeserializable): "text_link": "{text}" } if hasattr(self, "custom_subs"): - for type in self.custom_subs: - _subs[type] = self.custom_subs[type] + for key, value in self.custom_subs.items(): + _subs[key] = value utf16_text = text.encode("utf-16-le") html_text = "" - def func(text, type=None, url=None, user=None): - text = text.decode("utf-16-le") - if type == "text_mention": - type = "url" + def func(upd_text, subst_type=None, url=None, user=None): + upd_text = upd_text.decode("utf-16-le") + if subst_type == "text_mention": + subst_type = "url" url = "tg://user?id={0}".format(user.id) - elif type == "mention": - url = "https://t.me/{0}".format(text[1:]) - text = text.replace("&", "&").replace("<", "<").replace(">", ">") - if not type or not _subs.get(type): - return text - subs = _subs.get(type) - return subs.format(text=text, url=url) + elif subst_type == "mention": + url = "https://t.me/{0}".format(upd_text[1:]) + upd_text = upd_text.replace("&", "&").replace("<", "<").replace(">", ">") + if not subst_type or not _subs.get(subst_type): + return upd_text + subs = _subs.get(subst_type) + return subs.format(text=upd_text, url=url) offset = 0 for entity in entities: @@ -527,7 +527,7 @@ class Dice(JsonSerializable, Dictionaryable, JsonDeserializable): self.value = value def to_json(self): - return json.dumps({'value': self.value}) + return json.dumps(self.to_dic()) def to_dic(self): return {'value': self.value} @@ -896,7 +896,7 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable): return json_dict -class LoginUrl(JsonSerializable): +class LoginUrl(Dictionaryable, JsonSerializable): def __init__(self, url, forward_text=None, bot_username=None, request_write_access=None): self.url = url self.forward_text = forward_text @@ -917,7 +917,7 @@ class LoginUrl(JsonSerializable): return json_dic -class InlineKeyboardButton(JsonSerializable): +class InlineKeyboardButton(Dictionaryable, JsonSerializable): def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None): self.text = text @@ -2063,7 +2063,8 @@ class Sticker(JsonDeserializable): self.file_size = file_size self.is_animated = is_animated -class MaskPosition(JsonDeserializable, JsonSerializable): + +class MaskPosition(Dictionaryable, JsonDeserializable, JsonSerializable): @classmethod def de_json(cls, json_string): if (json_string is None): return None @@ -2089,7 +2090,7 @@ class MaskPosition(JsonDeserializable, JsonSerializable): # InputMedia -class InputMedia(JsonSerializable): +class InputMedia(Dictionaryable, JsonSerializable): def __init__(self, type, media, caption=None, parse_mode=None): self.type = type self.media = media @@ -2216,15 +2217,14 @@ class PollOption(JsonSerializable, JsonDeserializable): obj = cls.check_json(json_string) text = obj['text'] voter_count = int(obj['voter_count']) - option = cls(text) - option.voter_count = voter_count - return option + return cls(text, voter_count) - def __init__(self, text): + def __init__(self, text, voter_count = 0): self.text = text - self.voter_count = 0 + self.voter_count = voter_count def to_json(self): + # send_poll Option is a simple string: https://core.telegram.org/bots/api#sendpoll return json.dumps(self.text) @@ -2235,7 +2235,6 @@ class Poll(JsonDeserializable): obj = cls.check_json(json_string) poll_id = obj['id'] question = obj['question'] - poll = cls(question) options = [] for opt in obj['options']: options.append(PollOption.de_json(opt)) @@ -2245,19 +2244,39 @@ class Poll(JsonDeserializable): poll_type = obj['type'] allows_multiple_answers = obj['allows_multiple_answers'] correct_option_id = obj.get('correct_option_id') - poll.id = poll_id - poll.options = options - poll.total_voter_count = total_voter_count - poll.is_closed = is_closed - poll.is_anonymous = is_anonymous - poll.type = poll_type - poll.allows_multiple_answers = allows_multiple_answers - poll.correct_option_id = correct_option_id - return poll + explanation = obj.get('explanation') + if 'explanation_entities' in obj: + explanation_entities = Message.parse_entities(obj['explanation_entities']) + else: + explanation_entities = None + open_period = obj.get('open_period') + close_date = obj.get('close_date') + #poll = + return cls( + question, options, + poll_id, total_voter_count, is_closed, is_anonymous, poll_type, + allows_multiple_answers, correct_option_id, explanation, explanation_entities, + open_period, close_date) - def __init__(self, question): - self.options = [] + def __init__( + self, + question, options, + poll_id=None, total_voter_count=None, is_closed=None, is_anonymous=None, poll_type=None, + allows_multiple_answers=None, correct_option_id=None, explanation=None, explanation_entities=None, + open_period=None, close_date=None): + self.id = poll_id self.question = question + self.options = options + self.total_voter_count = total_voter_count + self.is_closed = is_closed + self.is_anonymous = is_anonymous + self.type = poll_type + self.allows_multiple_answers = allows_multiple_answers + self.correct_option_id = correct_option_id + self.explanation = explanation + self.explanation_entities = explanation_entities if not(explanation_entities is None) else [] + self.open_period = open_period + self.close_date = close_date def add(self, option): if type(option) is PollOption: diff --git a/tests/test_types.py b/tests/test_types.py index d3bc80d..e603960 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -166,3 +166,15 @@ def test_InlineQueryResultCachedPhoto_with_markup(): assert 'Title' in json_str assert 'caption' not in json_str assert 'reply_markup' in json_str + + +def test_json_poll_1(): + jsonstring = r'{"message_id": 395020,"from": {"id": 111,"is_bot": false,"first_name": "FN","last_name": "LN","username": "Badiboy","language_code": "ru"},"chat": {"id": 111,"first_name": "FN","last_name": "LN","username": "Badiboy","type": "private"},"date": 1587841239,"poll": {"id": "5272018969396510722","question": "Test poll 1","options": [{"text": "Answer 1","voter_count": 0},{"text": "Answer 2","voter_count": 0}],"total_voter_count": 0,"is_closed": false,"is_anonymous": true,"type": "regular","allows_multiple_answers": true}}' + msg = types.Message.de_json(jsonstring) + assert msg.poll is not None + assert isinstance(msg.poll, types.Poll) + assert msg.poll.id == '5272018969396510722' + assert msg.poll.question is not None + assert msg.poll.options is not None + assert len(msg.poll.options) == 2 + assert msg.poll.allows_multiple_answers == True