-
Notifications
You must be signed in to change notification settings - Fork 0
Гайд по содержимому v3
- Авторизация
- Запросы к API
- LongPoll
- Клавиатура
- Карусели
- Загрузка документов (doc, graffiti, voice messages)
- Инструменты
После установки пакета, о которой можно почитать здесь, пакет можно импортировать:
import vk_dev
Авторизация осуществляется классомvk_dev.Api(token, version, group_id=0)
:
import vk_dev
api = vk_dev.Auth(
token='token',
v=5.103,
group_id=123456789
)
К api
можно обращаться 3мя способами:
- Через точку, что вызовет соответсвующий метод API вк.
- С помощью
asyncio.run()
и тем же вызовом через точку, чтобы запустить корутину вне вашей реакции - Через побитовый сдвиг вправо, т.е.
>>
, что позволяет передать инкапсулирующим API классам и функциям ваш токен, версию, всякие айдификаторы и позволяет производить запросы от вашего имени. Все, что делает>>
при побитовом сдвиге:
def __rrshift__(self, cls):
cls.api = self
return cls
Это позволяет создавать собственные икапсуляторы, просто определив __rrshift__
. Удобный функционал по созданию подобных классов будет позже.
Обо всем подробнее чуть позже
Авторизация пользователей и сообществ проходит одинаково, за исключением передачи параметра group_id
. Если вы не используете этот параметр в инициализаторе класса (оставляя значение по умолчанию), пакет будет делать все запросы от лица пользователя.
О том, что такое токен и какие они бывают, можно почитать вот здесь
Запросы к API осуществляются объектом класса Api
, который представляет собой перегруженные методы асинхронные __getattr__
и __call__
для легкого взаимодействия.
## Кодом выше была инициализация класса Api
from asyncio import run
users = run(api.users.get(user_ids=210700286, fields=['bdate']))
Вам следует обращаться к апи вне корутин через
asyncio.run()
, т.к. по умолчанию все обращения к апи работают ассинхронно.
Это позволит выполнить VK метод users.get
с параметрами user_ids=1&fields=bdate
. Проще говоря, состоявляется POST запрос такого вида
https://api.vk.com/method/users.get?user_ids=210700286&fields=bdate&access_token=token&v=5.103
# Условимся, что query тут -- это body запроса
Что присвоит для users
словарь
{
"id": 210700286,
"first_name": "Lindsey",
"last_name": "Stirling",
"bdate": "21.9.1986"
}
Все неудачные запросы поднимут исключение vk_dev.VkErr
. "Положительные" ответы от VK автоматически возвращают поле response
. Вы можете получить доступ к вернувшимся параметрам, используя как синтаксис получения ключа из словаря, так и точечный способ.
Если вы не знаете, что из себя представляют методы VK, почитать об этом можно тут
Обработка LongPoll -- самый мощный инстуремент этого пакета. Для начала, нужно инициализировать vk_dev.LongPoll(default: bool = True, faileds: List[int] = [1, 2, 3, 4], **kwargs)
(получение адреса сервера, номер события и ключ). Класс одинаковый для пользователей и групп.
lp = api >> vk_dev.LongPoll()
Разберем поля
Параметр | Описание |
---|---|
default | Устанавливает настройки LongPoll по умолчанию. О них чуть ниже |
faileds | Список возможных возвращаемых от LongPoll ошибок, которые следует обработать автоматически. В ином случае поднимается исключениеvk_dev.VkErr . Информация об ошибках для пользователей и сообществ
|
**kwargs | Предусмотрено для каких-либо обновлений LongPoll. Все поля kwargs перегрывают поля default. |
Настройки поля default
для класса vk_dev.LongPoll
## Для пользователей
user_get = {
'need_pts': False,
'lp_version': 10
}
## Для сообществ
group_get = {
# group_id
}
Чтобы запустить процесс "ловли" событий от вк, нужно вызвать этот объект, используя его __call__(self, default: bool = True, **kwargs)
. Одинаковый для сообществ и пользвоателей
if __name__ == '__main__':
lp()
Поле default
, также как в инициализаторе, возвращает настройки по умолчанию. Все они могут быть перекрыты через kwargs
## Для пользовтелей
user_init = {
'wait': 25,
'mode': 8,
'version': 3
}
## Для сообществ
group_init = {
'wait': 25
}
Тут начинается самое интересное. Все ваши "реакции" на события должны быть выражены в форме функций с декоратором lp и указанием события
Вот пример кода, отвечающий Hello user
на все события message_new
import vk_dev
api = vk_dev.Api(
token='token',
v=5.103,
group_id=123456789
)
lp = api >> vk_dev.LongPoll()
@lp.message_new() # Скобки обязательны
async def reaction(event, pl):
await api.messages.send(
peer_id=event.object.message.peer_id,
message='Hello user',
random_id=vk_dev.random_id()
)
if __name__ == '__main__':
lp()
Все реакции должны принимать 2 аргумента -- event
и pl
. В event
будет передано само событие от LongPoll, pl
же нужен для полезной нагрузки, о которой речь пойдет чуть позже.
Используя точечный синтаксис в декоратое @lp
вы должны указать, какое событие должно быть поймано, чтобы функция сработала. О событиях можно ознакомиться здесь
Фукция
vk_dev.random_id(between: int = 2**31)
возвращает случайное число в диопазоне [-between; between]. Обязательно для отправки сообщения в параметреrandom_id
. Однако, сообщества могут просто использовать 0. О таких полезных инструментах вы можете узнать из главы про инструменты
pl, он же payload -- полезная нагрузка. Представьте, что вы обрабатываете событие, и в процессе обработки вам нужно достать какие-то данные из базы данных, какие-то получить от вк, какие-то расчитать, да и вообще неплохо было бы настругать для все этого класс и кучку полезных методов. Чтобы не делать это каждый раз, вы можете использовать аргумент pl
. Для пользования этим аргументом вам нужно указать фукнцию, которая вызывается сразу после выбора события. Возвращать она должна словарь. Вы же можете можете получать значения этого словаря как с помощью синтаксиса словаря, так и точечным способом. Вот пример кода, в котором создается функция для pl
. Она банально возвращает объект message
, в котором есть нужные нам поля text
и peer_id
. Ими мы воспользуемся в реакции
import vk_dev
## Auth, api, lp...
@vk_dev.payload
def mypayload(event):
return {
'msg': event.object.message
}
@lp.message_new(mypayload)
async def reaction(event, pl):
"""
Пишет пользователю текст, который он написал нам
"""
await api.messages.send(
peer_id=pl.msg.peer_id,
message=f'You said {pl.msg.text}',
random_id=vk_dev.random_id()
)
Ваша функция или корутина должна быть обернута в декоратор @vk_dev.payload
и принимать аргумент event
(их может быть сколько угодно, но event обязателен). После чего объект этой функции нужно передать в скобки @lp.message_new
и вуа-ля! К слову, вы можете использовать несколько payload-генераторов для ваших реакции. При взаимодействии с одинаковыми ключами они будут перекрываться, то есть аналогично dict.update()
Вся суть такой обработки имеет еще одну важную деталь -- условия исполнения. Если вы хотите, чтобы ваш код исполнялся только если сообщение пользователя начинается с определенных префиксов, то вы бы, скорее всего, написали бы что-то подобное
@lp.message_new()
def reaction(event, pl):
if event.object.message.text.startswith(['/', '!']):
api.messages.send(
peer_id=event.object.message.peer_id,
message='Hi!',
random_id=vk_dev.random_id()
)
Чтобы не нагромождать ваш код ветками условий, этот пакет предоставляет вам инструменты по созданию декораторов-условий, на подобие этих. Давайте перенесем нашу проверку на префикс в "инструмент"
import vk_dev
## Auth, api, lp...
from vk_dev import Condition
class Prefix(vk_dev.Condition):
"""
Check how message start
*prefixes: List[str]
"""
def __init__(self, *prefixes):
self._prefixes = prefixes
def code(self, event, pl):
if event.object.message.text.startswith(self._prefixes):
return True
return False
@Prefix('/', '!')
@lp.message_new()
async def reaction(event, pl):
await api.messages.send(
peer_id=event.object.message.peer_id,
message='Hi!',
random_id=vk_dev.random_id()
)
Для начала, нам нужно создать класс, который будет наследоваться от vk_dev.Condition
. Родитель представляет собой абстрактный класс, требующий реализовать метод code
, а также уже имеющий __call__
, чье поведение вам изменять нельзя. После чего вы можете определить __init__
, в который передадите всех необходимые аргументы. Давайте имена таким аргументам с знака подчеркивания _
. Вы не будете использовать их вне класса и это также исключит возможные конфликты с именами родительскго класса. code
может быть как функцией, так и корутиной и должен принимать два аргумента -- event
и pl
и возвращать либо True
, либо False
. Теперь вы можете поместить декоратор над @lp
и быть уверенным, что реакция сработает только если сообщение началось с символов / или !. Вы можете создавать ваши эекземпляры на скольких угодно реакциях и передавать им разные аргументы. К слову, пакет имеет встроенный набор таких вот условий. Они лежат в подпакете vk_dev.cond
. Prefix
(абсолютно идентичный) там уже есть. Полезные условия будут добавляться со временем. Если вы считаете, что созданное вами условие является очень полезной вещью -- вы можете предложить его.
Создание таких условий выигрывает по времени. Перед тем, как вызвать реакцию, пакет пробегается по всем условиям и если хотя бы один вернул ложь -- условия больше не проверяются. Причем важная вещь -- порядок ваших собственных декораторов ничего не сломает. Вы можете размещать их как угодно, но @lp
должен быть первым. Поменяется лишь логика обработки событий. Будьте аккуратны с сменой значений pl
внутри ваших conditions
.
Для очень любопытных --
super().__init__(self)
вы можете не делать. Родитель имеет init по умолчанию от классаobject
.
Для клавиатуры предусмотрен класс vk_dev.Keyboard
. На вход он принимает словарь, описывающий клавиатуру бота. Для использования просто отправьте экземпляр этого класса в параметре keyboard
@vk_dev.Prefix('/kb')
@lp.message_new()
async def keyboard(event, pl):
from vk_dev import Keyboard
"""
Отправит inline клавиатуру с 1 кнопкой
"""
keyboard = {
'inline': True,
'buttons': [
[
{
'action': {
'type': 'text',
'payload': '{}',
'label': 'Some text'
},
'color': 'positive'
}
]
]
}
await api.messages.send(
peer_id=event.object.message.peer_id,
message='Your keyboard, sir',
keyboard=Keyboard(keyboard),
random_id=vk_dev.random_id()
)
Вы можете выстроить клавиатуру с помощью специальных констуркторов Button
. Например
@vk_dev.cond.Prefix('/kb')
@lp.message_new()
async def kb(event, pl):
"""
Send Keyboard
"""
from vk_dev import Keyboard, Button
kb =\
Keyboard(inline=True).create(
Button.text(payload='{}', label='Some text').positive(),
Button.text(payload='{}', label='Text near').secondary(),
Button.line(),
Button.text(payload='{}', label='Text under').negative(),
Button.text(payload='{}', label='Text in corner').primary()
)
await api.messages.send(
peer_id=447532348,
message='Your keyboard, sir.',
keyboard=kb,
random_id=0
)
Инициализация пустой клавиатуры происходит тогда, когда не передан словарь в инициализатор Keyboard
. Метод .create()
принимает в себя кнопки. Кнопки являются объектами класса Button
и работают следующим образом:
- Вы вы вызываете статический метод класса, тем самым указывая на
action.type
кнопки. К примеру.location
или.text
. Далее передаете осталное содержимое поляaction
. Если ваша кнопка типаtext
, она поддерживает цвета, которые можно указать, вызвав соответствующий метод. Новую строку с кнопками можно начать с помощьюButton.line()
Относительно новый функционал позволяет отправлять пользователям "карусели". Из себя они представляют набор окошечек, перемещающихся по горизонтали и имеющие что-то из заголовока, описания, фотографий или даже набор простенькой клавиатуры. Вы можете вручную сгенерировать дерево для карусели и поместить его в класс vk_dev.Template
или же воспользоваться функционалом и генерировать его с помощью yield-генератора:
from vk_dev import Template, Element, Keyboard, Button
import vk_dev
@vk_dev.cond.Prefix('tp')
@lp.message_new()
async def tp(event, pl):
"""
Отправляет Карусель пользвователю
с продуктами в качестве ассортимента
"""
tp = Template()
@tp
def elems():
"""
"""
# Заголовки для элементов карусели
titles = [
'Морковь',
'Картошка',
'Ягоды'
]
# Описания для элементов. Порядок долже сохраниться
descs = [
'Вытянутый оранжевый овощ. Любимец кроликов',
'Кусок крахмала, но вкусный до безумия в пюрешке',
'Просто наборчик полезных сладостей'
]
# Клавиатура для элемента
keyboards = [
Keyboard().create(
Button.text(
label='Купить',
payload={"name": name.lower()}
)
) for name in titles
]
# Просто проходимся фором по каждому из элементов
for title, desc, kb in zip(titles, descs, keyboards):
# Создаем экземпляр Element(**kwargs)
elem = Element(
title=title,
description=desc,
buttons=kb
)
# Yield'им элемент, не забывая добавлять событие,
# которое должно случиться при нажатии на элемент.
# Все описано в документации вк
yield elem.open_link('https://google.com')
await api.messages.send(
peer_id=event.object.message.peer_id,
message='Наш ассортимент',
template=tp,
random_id=0
)
Для экземпляра Template()
нужно вернуть генератор, который будет выплевывать элементы для карусели. Все максимально просто.
Загрузка документов представляется следующим образом
from vk_dev import Document
import vk_dev
@vk_dev.cond.Prefix('/doc')
@lp.message_new()
async def doc(event, pl):
"""
Отправляет файл пользователю
"""
with open('file.png', 'rb') as file:
doc = api >> Document.to_message(
peer_id=event.object.message.peer_id,
type='doc'
)
doc = await doc.load(file=file)
await api.messages.send(
peer_id=447532348,
message="asda",
attachment=doc,
random_id=vk_dev.random_id()
)
Загрузка происходит в 2 этапа
- Вы указываете видимость для документа. Доступно
.to_message
и.to_wall_and_message
. В них вы указываете параметры для соответсвующих методовdocs.getMessagesUploadServer
иdocs.getWallUploadServer
. - Эвейтите корутину и передаете параметры под
docs.save
, но в качестве файла указываете именно ваш объект файла. Позже будет добавлена возможность загрузки документов по пути в вашей системе и по URL.
Полезные инстурменты, которые могу пригодиться вам. Находятся в vk_dev
Инструмент | Описание |
---|---|
random_id(between: int = 2**31) | Генерирует случаной число в диапазоне [-between; between]. Требуется для параметра random_id в messages.send
|
peer | Пременная. Является int(2e9) |
123