1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Merge pull request #930 from mrpes/patch-5

Minor keyboard code redisign
This commit is contained in:
Badiboy 2020-08-02 18:49:32 +03:00 committed by GitHub
commit e987e40ee7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 45 deletions

View File

@ -18,7 +18,7 @@ console_output_handler = logging.StreamHandler(sys.stderr)
console_output_handler.setFormatter(formatter) console_output_handler.setFormatter(formatter)
logger.addHandler(console_output_handler) logger.addHandler(console_output_handler)
logger.setLevel(logging.ERROR) logger.setLevel(logging.WARNING)
from telebot import apihelper, types, util from telebot import apihelper, types, util
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend

View File

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
try: try:
import ujson as json import ujson as json
except ImportError: except ImportError:
@ -9,6 +11,7 @@ import six
from telebot import util from telebot import util
logger = logging.getLogger('TeleBot')
class JsonSerializable(object): class JsonSerializable(object):
""" """
@ -66,9 +69,9 @@ class JsonDeserializable(object):
:param json_type: :param json_type:
:return: :return:
""" """
if isinstance(json_type, dict): if util.is_dict(json_type):
return json_type return json_type
elif isinstance(json_type, str): elif util.is_string(json_type):
return json.loads(json_type) return json.loads(json_type)
else: else:
raise ValueError("json_type should be a json dict or string.") raise ValueError("json_type should be a json dict or string.")
@ -806,13 +809,17 @@ class ReplyKeyboardRemove(JsonSerializable):
class ReplyKeyboardMarkup(JsonSerializable): class ReplyKeyboardMarkup(JsonSerializable):
def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3): def __init__(self, resize_keyboard=None, one_time_keyboard=None, selective=None, row_width=3):
if row_width>12:
logger.warning('Telegram does not support reply keyboard row width over 12')
row_width=12
self.resize_keyboard = resize_keyboard self.resize_keyboard = resize_keyboard
self.one_time_keyboard = one_time_keyboard self.one_time_keyboard = one_time_keyboard
self.selective = selective self.selective = selective
self.row_width = row_width self.row_width = row_width
self.keyboard = [] self.keyboard = []
def add(self, *args): def add(self, *args, row_width=None):
""" """
This function adds strings to the keyboard, while not exceeding row_width. This function adds strings to the keyboard, while not exceeding row_width.
E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]} E.g. ReplyKeyboardMarkup#add("A", "B", "C") yields the json result {keyboard: [["A"], ["B"], ["C"]]}
@ -820,22 +827,28 @@ class ReplyKeyboardMarkup(JsonSerializable):
When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]} When row_width is set to 2, the following is the result of this function: {keyboard: [["A", "B"], ["C"]]}
See https://core.telegram.org/bots/api#replykeyboardmarkup See https://core.telegram.org/bots/api#replykeyboardmarkup
:param args: KeyboardButton to append to the keyboard :param args: KeyboardButton to append to the keyboard
:param row_width: width of row
:return: self, to allow function chaining.
""" """
i = 1 row_width = row_width or self.row_width
row = []
for button in args:
if util.is_string(button): if row_width>12:
row.append({'text': button}) logger.warning('Telegram does not support reply keyboard row width over 12')
elif isinstance(button, bytes): row_width=12
row.append({'text': button.decode('utf-8')})
else: for row in util.chunks(args, row_width):
row.append(button.to_dict()) button_array = []
if i % self.row_width == 0: for button in row:
self.keyboard.append(row) if util.is_string(button):
row = [] button_array.append({'text': button})
i += 1 elif util.is_bytes(button):
if len(row) > 0: button_array.append({'text': button.decode('utf-8')})
self.keyboard.append(row) else:
button_array.append(button.to_dict())
self.keyboard.append(button_array)
return self
def row(self, *args): def row(self, *args):
""" """
@ -845,14 +858,8 @@ class ReplyKeyboardMarkup(JsonSerializable):
:param args: strings :param args: strings
:return: self, to allow function chaining. :return: self, to allow function chaining.
""" """
btn_array = []
for button in args: return self.add(args, 12)
if util.is_string(button):
btn_array.append({'text': button})
else:
btn_array.append(button.to_dict())
self.keyboard.append(btn_array)
return self
def to_json(self): def to_json(self):
""" """
@ -904,13 +911,17 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable):
""" """
This object represents an inline keyboard that appears This object represents an inline keyboard that appears
right next to the message it belongs to. right next to the message it belongs to.
:return: :return:
""" """
if row_width>8:
logger.warning('Telegram does not support inline keyboard row width over 8')
row_width=8
self.row_width = row_width self.row_width = row_width
self.keyboard = [] self.keyboard = []
def add(self, *args): def add(self, *args, row_width=None):
""" """
This method adds buttons to the keyboard without exceeding row_width. This method adds buttons to the keyboard without exceeding row_width.
@ -920,20 +931,23 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable):
When row_width is set to 2, the result: When row_width is set to 2, the result:
{keyboard: [["A", "B"], ["C"]]} {keyboard: [["A", "B"], ["C"]]}
See https://core.telegram.org/bots/api#inlinekeyboardmarkup See https://core.telegram.org/bots/api#inlinekeyboardmarkup
:param args: Array of InlineKeyboardButton to append to the keyboard :param args: Array of InlineKeyboardButton to append to the keyboard
:param row_width: width of row
:return: self, to allow function chaining.
""" """
i = 1 row_width = row_width or self.row_width
row = []
for button in args: if row_width>8:
row.append(button.to_dict()) logger.warning('Telegram does not support inline keyboard row width over 8')
if i % self.row_width == 0: row_width=8
self.keyboard.append(row)
row = [] for row in util.chunks(args, row_width):
i += 1 button_array = [button.to_dict() for button in row]
if len(row) > 0: self.keyboard.append(button_array)
self.keyboard.append(row)
return self
def row(self, *args): def row(self, *args):
""" """
Adds a list of InlineKeyboardButton to the keyboard. Adds a list of InlineKeyboardButton to the keyboard.
@ -942,13 +956,12 @@ class InlineKeyboardMarkup(Dictionaryable, JsonSerializable):
InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs: InlineKeyboardMarkup.row("A").row("B", "C").to_json() outputs:
'{keyboard: [["A"], ["B", "C"]]}' '{keyboard: [["A"], ["B", "C"]]}'
See https://core.telegram.org/bots/api#inlinekeyboardmarkup See https://core.telegram.org/bots/api#inlinekeyboardmarkup
:param args: Array of InlineKeyboardButton to append to the keyboard :param args: Array of InlineKeyboardButton to append to the keyboard
:return: self, to allow function chaining. :return: self, to allow function chaining.
""" """
button_array = [button.to_dict() for button in args]
self.keyboard.append(button_array) return self.add(args, 8)
return self
def to_json(self): def to_json(self):
""" """
@ -2291,6 +2304,9 @@ class InputMedia(Dictionaryable, JsonSerializable):
class InputMediaPhoto(InputMedia): class InputMediaPhoto(InputMedia):
def __init__(self, media, caption=None, parse_mode=None): def __init__(self, media, caption=None, parse_mode=None):
if util.is_pil_image(media):
media = util.pil_image_to_file(media)
super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode) super(InputMediaPhoto, self).__init__(type="photo", media=media, caption=caption, parse_mode=parse_mode)
def to_dict(self): def to_dict(self):

View File

@ -166,6 +166,12 @@ def async_dec():
def is_string(var): def is_string(var):
return isinstance(var, string_types) return isinstance(var, string_types)
def is_dict(var):
return isinstance(var, dict)
def is_bytes(var):
return isinstance(var, bytes)
def is_pil_image(var): def is_pil_image(var):
return pil_imported and isinstance(var, PIL.Image.Image) return pil_imported and isinstance(var, PIL.Image.Image)
@ -278,6 +284,11 @@ def per_thread(key, construct_value, reset=False):
return getattr(thread_local, key) return getattr(thread_local, key)
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
# https://stackoverflow.com/a/312464/9935473
for i in range(0, len(lst), n):
yield lst[i:i + n]
def generate_random_token(): def generate_random_token():
return ''.join(random.sample(string.ascii_letters, 16)) return ''.join(random.sample(string.ascii_letters, 16))