Skip to content
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 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([])