Skip to content

Commit

Permalink
Добавление Callback кнопок и update клавиатуры (#369)
Browse files Browse the repository at this point in the history
  • Loading branch information
chebotarevmichael committed Sep 11, 2020
1 parent 613e721 commit 047838f
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 25 deletions.
2 changes: 1 addition & 1 deletion examples/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def main():

keyboard = VkKeyboard(one_time=True)

keyboard.add_button('Белая кнопка', color=VkKeyboardColor.DEFAULT)
keyboard.add_button('Белая кнопка', color=VkKeyboardColor.SECONDARY)
keyboard.add_button('Зелёная кнопка', color=VkKeyboardColor.POSITIVE)

keyboard.add_line() # Переход на вторую строку
Expand Down
109 changes: 109 additions & 0 deletions examples/keyboard_inline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import json

from vk_api import VkApi
from vk_api.utils import get_random_id
from vk_api.bot_longpoll import VkBotLongPoll, VkBotEventType
from vk_api.keyboard import VkKeyboard, VkKeyboardColor


# Общие
GROUP_ID = "your_group_id"
GROUP_TOKEN = "your_group_token"
API_VERSION = "5.120"

# для callback-кнопки "открыть приложение"
APP_ID = 100500 # id IFrame приложения
OWNER_ID = 123456789 # id владельца приложения

# виды callback-кнопок
CALLBACK_TYPES = ("show_snackbar", "open_link", "open_app")


def main():
# Запускаем бот
vk_session = VkApi(token=GROUP_TOKEN, api_version=API_VERSION)
vk = vk_session.get_api()
longpoll = VkBotLongPoll(vk_session, group_id=GROUP_ID)

# Создаем 2 клавиатуры
# №1. Клавиатура с 3 кнопками: "показать всплывающее сообщение", "открыть URL" и изменить меню (свой собственный тип)
keyboard_1 = VkKeyboard(one_time=False, inline=True)
keyboard_1.add_callback_button(
label="Покажи pop-up сообщение",
color=VkKeyboardColor.SECONDARY,
payload={"type": "show_snackbar", "text": "Это исчезающее сообщение на экране"},
)
keyboard_1.add_line()
keyboard_1.add_callback_button(
label="Откртыть Url",
color=VkKeyboardColor.POSITIVE,
payload={"type": "open_link", "link": "https://vk.com/dev/bots_docs_5"},
)
keyboard_1.add_line()
keyboard_1.add_callback_button(
label="Открыть приложение",
color=VkKeyboardColor.NEGATIVE,
payload={
"type": "open_app",
"app_id": APP_ID,
"owner_id": OWNER_ID,
"hash": "anything_data_100500",
},
)
keyboard_1.add_line()
keyboard_1.add_callback_button(
label="Добавить красного ",
color=VkKeyboardColor.PRIMARY,
payload={"type": "my_own_100500_type_edit"},
)

# №2. Клавиатура с одной красной callback-кнопкой. Нажатие изменяет меню на предыдущее.
keyboard_2 = VkKeyboard(one_time=False, inline=True)
keyboard_2.add_callback_button(
"Назад",
color=VkKeyboardColor.NEGATIVE,
payload={"type": "my_own_100500_type_edit"},
)

# Запускаем пуллинг
f_toggle: bool = False
for event in longpoll.listen():
# отправляем меню 1го вида на любое текстовое сообщение от пользователя
if event.type == VkBotEventType.MESSAGE_NEW:
if event.obj.message["text"] != "":
if event.from_user:
# Если клиент пользователя не поддерживает callback-кнопки, нажатие на них будет отправлять текстовые
# сообщения. Т.е. они будут работать как обычные inline кнопки.
if "callback" not in event.obj.client_info["button_actions"]:
print(
f'Клиент user_id{event.obj.message["from_id"]} не поддерживает callback-кнопки.'
)

vk.messages.send(
user_id=event.obj.message["from_id"],
random_id=get_random_id(),
peer_id=event.obj.message["from_id"],
keyboard=keyboard_1.get_keyboard(),
message="Меню #1",
)
# обрабатываем клики по callback кнопкам
elif event.type == VkBotEventType.MESSAGE_EVENT:
if event.object.payload.get("type") in CALLBACK_TYPES:
r = vk.messages.sendMessageEventAnswer(
event_id=event.object.event_id,
user_id=event.object.user_id,
peer_id=event.object.peer_id,
event_data=json.dumps(event.object.payload),
)
elif event.object.payload.get("type") == "my_own_100500_type_edit":
last_id = vk.messages.edit(
peer_id=event.obj.peer_id,
message="Меню #2",
conversation_message_id=event.obj.conversation_message_id,
keyboard=(keyboard_1 if f_toggle else keyboard_2).get_keyboard(),
)
f_toggle = not f_toggle


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion tests/test_keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
def test_keyboard():
keyboard.add_button(
'Test-1',
color=VkKeyboardColor.DEFAULT,
color=VkKeyboardColor.SECONDARY,
payload={'test': 'some_payload'}
)
keyboard.add_line()
Expand Down
1 change: 1 addition & 0 deletions vk_api/bot_longpoll.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class VkBotEventType(Enum):
MESSAGE_NEW = 'message_new'
MESSAGE_REPLY = 'message_reply'
MESSAGE_EDIT = 'message_edit'
MESSAGE_EVENT = 'message_event'

MESSAGE_TYPING_STATE = 'message_typing_state'

Expand Down
91 changes: 68 additions & 23 deletions vk_api/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from .utils import sjson_dumps



MAX_BUTTONS_ON_LINE = 5
MAX_DEFAULT_LINES = 10
MAX_INLINE_LINES = 6


class VkKeyboardColor(Enum):
Expand All @@ -22,14 +24,15 @@ class VkKeyboardColor(Enum):
PRIMARY = 'primary'

#: Белая
DEFAULT = 'default'
SECONDARY = 'secondary'

#: Красная
NEGATIVE = 'negative'

#: Зелёная
POSITIVE = 'positive'


class VkKeyboardButton(Enum):
""" Возможные типы кнопки """

Expand All @@ -44,10 +47,12 @@ class VkKeyboardButton(Enum):

#: Кнопка с приложением VK Apps
VKAPPS = "open_app"

#: Кнопка с ссылкой
OPENLINK = "open_link"

#: Callback-кнопка
CALLBACK = "callback"


class VkKeyboard(object):
Expand Down Expand Up @@ -82,9 +87,9 @@ def get_empty_keyboard(cls):
keyboard.keyboard['buttons'] = []
return keyboard.get_keyboard()

def add_button(self, label, color=VkKeyboardColor.DEFAULT, payload=None):
def add_button(self, label, color=VkKeyboardColor.SECONDARY, payload=None):
""" Добавить кнопку с текстом.
Максимальное количество кнопок на строке - 4
Максимальное количество кнопок на строке - MAX_BUTTONS_ON_LINE
:param label: Надпись на кнопке и текст, отправляющийся при её нажатии.
:type label: str
Expand All @@ -96,8 +101,8 @@ def add_button(self, label, color=VkKeyboardColor.DEFAULT, payload=None):

current_line = self.lines[-1]

if len(current_line) >= 4:
raise ValueError('Max 4 buttons on a line')
if len(current_line) >= MAX_BUTTONS_ON_LINE:
raise ValueError(f'Max {MAX_BUTTONS_ON_LINE} buttons on a line')

color_value = color

Expand All @@ -118,6 +123,42 @@ def add_button(self, label, color=VkKeyboardColor.DEFAULT, payload=None):
}
})

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, six.string_types):
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):
""" Добавить кнопку с местоположением.
Всегда занимает всю ширину линии.
Expand All @@ -130,8 +171,8 @@ def add_location_button(self, payload=None):

if len(current_line) != 0:
raise ValueError(
'This type of button takes the entire width of the line'
)
'This type of button takes the entire width of the line'
)

if payload is not None and not isinstance(payload, six.string_types):
payload = sjson_dumps(payload)
Expand Down Expand Up @@ -160,8 +201,8 @@ def add_vkpay_button(self, hash, payload=None):

if len(current_line) != 0:
raise ValueError(
'This type of button takes the entire width of the line'
)
'This type of button takes the entire width of the line'
)

if payload is not None and not isinstance(payload, six.string_types):
payload = sjson_dumps(payload)
Expand Down Expand Up @@ -198,8 +239,8 @@ def add_vkapps_button(self, app_id, owner_id, label, hash, payload=None):

if len(current_line) != 0:
raise ValueError(
'This type of button takes the entire width of the line'
)
'This type of button takes the entire width of the line'
)

if payload is not None and not isinstance(payload, six.string_types):
payload = sjson_dumps(payload)
Expand All @@ -216,10 +257,10 @@ def add_vkapps_button(self, app_id, owner_id, label, hash, payload=None):
'hash': hash
}
})

def add_openlink_button(self, label, link, payload=None):
""" Добавить кнопку с ссылкой
Максимальное количество кнопок на строке - 4
Максимальное количество кнопок на строке - MAX_BUTTONS_ON_LINE
:param label: Надпись на кнопке
:type label: str
Expand All @@ -230,8 +271,8 @@ def add_openlink_button(self, label, link, payload=None):
"""
current_line = self.lines[-1]

if len(current_line) >= 4:
raise ValueError('Max 4 buttons on a line')
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, six.string_types):
payload = sjson_dumps(payload)
Expand All @@ -241,19 +282,23 @@ def add_openlink_button(self, label, link, payload=None):
current_line.append({
'action': {
'type': button_type,
'link' : link,
'link': link,
'label': label,
'payload': payload
}
})


def add_line(self):
""" Создаёт новую строку, на которой можно размещать кнопки.
Максимальное количество строк - 10.
Максимальное количество строк:
Стандартное отображение - MAX_DEFAULT_LINES;
Inline-отображение - MAX_INLINE_LINES.
"""

if len(self.lines) >= 10:
raise ValueError('Max 10 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([])

0 comments on commit 047838f

Please sign in to comment.