304 lines
10 KiB
Python
304 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
:authors: python273, Helow19274, prostomarkeloff
|
||
:license: Apache License, Version 2.0, see LICENSE file
|
||
:copyright: (c) 2019 python273
|
||
"""
|
||
|
||
from enum import Enum
|
||
|
||
|
||
from .utils import sjson_dumps
|
||
|
||
|
||
MAX_BUTTONS_ON_LINE = 5
|
||
MAX_DEFAULT_LINES = 10
|
||
MAX_INLINE_LINES = 6
|
||
|
||
|
||
class VkKeyboardColor(Enum):
|
||
""" Возможные цвета кнопок """
|
||
|
||
#: Синяя
|
||
PRIMARY = 'primary'
|
||
|
||
#: Белая
|
||
SECONDARY = 'secondary'
|
||
|
||
#: Красная
|
||
NEGATIVE = 'negative'
|
||
|
||
#: Зелёная
|
||
POSITIVE = 'positive'
|
||
|
||
|
||
class VkKeyboardButton(Enum):
|
||
""" Возможные типы кнопки """
|
||
|
||
#: Кнопка с текстом
|
||
TEXT = "text"
|
||
|
||
#: Кнопка с местоположением
|
||
LOCATION = "location"
|
||
|
||
#: Кнопка с оплатой через VKPay
|
||
VKPAY = "vkpay"
|
||
|
||
#: Кнопка с приложением VK Apps
|
||
VKAPPS = "open_app"
|
||
|
||
#: Кнопка с ссылкой
|
||
OPENLINK = "open_link"
|
||
|
||
#: Callback-кнопка
|
||
CALLBACK = "callback"
|
||
|
||
|
||
class VkKeyboard(object):
|
||
""" Класс для создания клавиатуры для бота (https://vk.com/dev/bots_docs_3)
|
||
:param one_time: Если True, клавиатура исчезнет после нажатия на кнопку
|
||
:type one_time: bool
|
||
"""
|
||
|
||
__slots__ = ('one_time', 'lines', 'keyboard', 'inline')
|
||
|
||
def __init__(self, one_time=False, inline=False):
|
||
self.one_time = one_time
|
||
self.inline = inline
|
||
self.lines = [[]]
|
||
|
||
self.keyboard = {
|
||
'one_time': self.one_time,
|
||
'inline': self.inline,
|
||
'buttons': self.lines
|
||
}
|
||
|
||
def get_keyboard(self):
|
||
""" Получить json клавиатуры """
|
||
return sjson_dumps(self.keyboard)
|
||
|
||
@classmethod
|
||
def get_empty_keyboard(cls):
|
||
""" Получить json пустой клавиатуры.
|
||
Если отправить пустую клавиатуру, текущая у пользователя исчезнет.
|
||
"""
|
||
keyboard = cls()
|
||
keyboard.keyboard['buttons'] = []
|
||
return keyboard.get_keyboard()
|
||
|
||
def add_button(self, label, color=VkKeyboardColor.SECONDARY, payload=None):
|
||
""" Добавить кнопку с текстом.
|
||
Максимальное количество кнопок на строке - MAX_BUTTONS_ON_LINE
|
||
|
||
:param label: Надпись на кнопке и текст, отправляющийся при её нажатии.
|
||
:type label: str
|
||
:param color: цвет кнопки.
|
||
:type color: VkKeyboardColor or str
|
||
:param payload: Параметр для callback api
|
||
:type payload: str or list or dict
|
||
"""
|
||
|
||
current_line = self.lines[-1]
|
||
|
||
if len(current_line) >= MAX_BUTTONS_ON_LINE:
|
||
raise ValueError(f'Max {MAX_BUTTONS_ON_LINE} buttons on a line')
|
||
|
||
color_value = color
|
||
|
||
if isinstance(color, VkKeyboardColor):
|
||
color_value = color_value.value
|
||
|
||
if payload is not None and not isinstance(payload, str):
|
||
payload = sjson_dumps(payload)
|
||
|
||
button_type = VkKeyboardButton.TEXT.value
|
||
|
||
current_line.append({
|
||
'color': color_value,
|
||
'action': {
|
||
'type': button_type,
|
||
'payload': payload,
|
||
'label': label,
|
||
}
|
||
})
|
||
|
||
def add_callback_button(self, label, color=VkKeyboardColor.SECONDARY, payload=None):
|
||
""" Добавить callback-кнопку с текстом.
|
||
Максимальное количество кнопок на строке - MAX_BUTTONS_ON_LINE
|
||
|
||
:param label: Надпись на кнопке и текст, отправляющийся при её нажатии.
|
||
:type label: str
|
||
:param color: цвет кнопки.
|
||
:type color: VkKeyboardColor or str
|
||
:param payload: Параметр для callback api
|
||
:type payload: str or list or dict
|
||
"""
|
||
|
||
current_line = self.lines[-1]
|
||
|
||
if len(current_line) >= MAX_BUTTONS_ON_LINE:
|
||
raise ValueError(f'Max {MAX_BUTTONS_ON_LINE} buttons on a line')
|
||
|
||
color_value = color
|
||
|
||
if isinstance(color, VkKeyboardColor):
|
||
color_value = color_value.value
|
||
|
||
if payload is not None and not isinstance(payload, str):
|
||
payload = sjson_dumps(payload)
|
||
|
||
button_type = VkKeyboardButton.CALLBACK.value
|
||
|
||
current_line.append({
|
||
'color': color_value,
|
||
'action': {
|
||
'type': button_type,
|
||
'payload': payload,
|
||
'label': label,
|
||
}
|
||
})
|
||
|
||
def add_location_button(self, payload=None):
|
||
""" Добавить кнопку с местоположением.
|
||
Всегда занимает всю ширину линии.
|
||
|
||
:param payload: Параметр для callback api
|
||
:type payload: str or list or dict
|
||
"""
|
||
|
||
current_line = self.lines[-1]
|
||
|
||
if len(current_line) != 0:
|
||
raise ValueError(
|
||
'This type of button takes the entire width of the line'
|
||
)
|
||
|
||
if payload is not None and not isinstance(payload, str):
|
||
payload = sjson_dumps(payload)
|
||
|
||
button_type = VkKeyboardButton.LOCATION.value
|
||
|
||
current_line.append({
|
||
'action': {
|
||
'type': button_type,
|
||
'payload': payload
|
||
}
|
||
})
|
||
|
||
def add_vkpay_button(self, hash, payload=None):
|
||
""" Добавить кнопку с оплатой с помощью VKPay.
|
||
Всегда занимает всю ширину линии.
|
||
|
||
:param hash: Параметры платежа VKPay и ID приложения
|
||
(в поле aid) разделённые &
|
||
:type hash: str
|
||
:param payload: Параметр для совместимости со старыми клиентами
|
||
:type payload: str or list or dict
|
||
"""
|
||
|
||
current_line = self.lines[-1]
|
||
|
||
if len(current_line) != 0:
|
||
raise ValueError(
|
||
'This type of button takes the entire width of the line'
|
||
)
|
||
|
||
if payload is not None and not isinstance(payload, str):
|
||
payload = sjson_dumps(payload)
|
||
|
||
button_type = VkKeyboardButton.VKPAY.value
|
||
|
||
current_line.append({
|
||
'action': {
|
||
'type': button_type,
|
||
'payload': payload,
|
||
'hash': hash
|
||
}
|
||
})
|
||
|
||
def add_vkapps_button(self, app_id, owner_id, label, hash, payload=None):
|
||
""" Добавить кнопку с приложением VK Apps.
|
||
Всегда занимает всю ширину линии.
|
||
|
||
:param app_id: Идентификатор вызываемого приложения с типом VK Apps
|
||
:type app_id: int
|
||
:param owner_id: Идентификатор сообщества, в котором установлено
|
||
приложение, если требуется открыть в контексте сообщества
|
||
:type owner_id: int
|
||
:param label: Название приложения, указанное на кнопке
|
||
:type label: str
|
||
:param hash: хэш для навигации в приложении, будет передан в строке
|
||
параметров запуска после символа #
|
||
:type hash: str
|
||
:param payload: Параметр для совместимости со старыми клиентами
|
||
:type payload: str or list or dict
|
||
"""
|
||
|
||
current_line = self.lines[-1]
|
||
|
||
if len(current_line) != 0:
|
||
raise ValueError(
|
||
'This type of button takes the entire width of the line'
|
||
)
|
||
|
||
if payload is not None and not isinstance(payload, str):
|
||
payload = sjson_dumps(payload)
|
||
|
||
button_type = VkKeyboardButton.VKAPPS.value
|
||
|
||
current_line.append({
|
||
'action': {
|
||
'type': button_type,
|
||
'app_id': app_id,
|
||
'owner_id': owner_id,
|
||
'label': label,
|
||
'payload': payload,
|
||
'hash': hash
|
||
}
|
||
})
|
||
|
||
def add_openlink_button(self, label, link, payload=None):
|
||
""" Добавить кнопку с ссылкой
|
||
Максимальное количество кнопок на строке - MAX_BUTTONS_ON_LINE
|
||
|
||
:param label: Надпись на кнопке
|
||
:type label: str
|
||
:param link: ссылка, которую необходимо открыть по нажатию на кнопку
|
||
:type link: str
|
||
:param payload: Параметр для callback api
|
||
:type payload: str or list or dict
|
||
"""
|
||
current_line = self.lines[-1]
|
||
|
||
if len(current_line) >= MAX_BUTTONS_ON_LINE:
|
||
raise ValueError(f'Max {MAX_BUTTONS_ON_LINE} buttons on a line')
|
||
|
||
if payload is not None and not isinstance(payload, str):
|
||
payload = sjson_dumps(payload)
|
||
|
||
button_type = VkKeyboardButton.OPENLINK.value
|
||
|
||
current_line.append({
|
||
'action': {
|
||
'type': button_type,
|
||
'link': link,
|
||
'label': label,
|
||
'payload': payload
|
||
}
|
||
})
|
||
|
||
def add_line(self):
|
||
""" Создаёт новую строку, на которой можно размещать кнопки.
|
||
Максимальное количество строк:
|
||
Стандартное отображение - MAX_DEFAULT_LINES;
|
||
Inline-отображение - MAX_INLINE_LINES.
|
||
"""
|
||
if self.inline:
|
||
if len(self.lines) >= MAX_INLINE_LINES:
|
||
raise ValueError(f'Max {MAX_INLINE_LINES} lines for inline keyboard')
|
||
else:
|
||
if len(self.lines) >= MAX_DEFAULT_LINES:
|
||
raise ValueError(f'Max {MAX_DEFAULT_LINES} lines for default keyboard')
|
||
|
||
self.lines.append([])
|