deknowny / vkquick Goto Github PK
View Code? Open in Web Editor NEWLightweight modern asynchronous framework for VK bots that can automatically build docs for your bots
Home Page: https://vk.com/vkquick
License: MIT License
Lightweight modern asynchronous framework for VK bots that can automatically build docs for your bots
Home Page: https://vk.com/vkquick
License: MIT License
Сделать обертку на чат и соответсвующие методы в контекст на получение этой обертки
any_text: bool
: реагировать на любой текст (аналог евент хендлера)
payload_names: ty.List[str]
: имена из пейлоада, на которые может реагировать команда
Одно из двух:
Сделать возможность клонирования ботов путем вызова метода у существующего бота и передачи нового токена (создается новый инстанс с теми же командами, обработчиками сигналов и событий, а так же с теми же настройками дебаггера, флага релиза и прочих полей)
Оставлять работу с одним инстансом бота, просто передавая необходимое количество дополнительных токенов
Присылайте сюда фильтры, которые хотели бы добавить в квик
При внешней перезагрузке бота в случае ошибок, возникает RuntimeError
, потому что сессия аиохттп закрывается, а проверка для открытия сессии существует только если она None
. Необходимо так же добавить условие на проверку, открыта ли сессия
Хедер X-Next-Ts, почему-то, приходит не всегда.
Необходимо перенести эти методы в объект сообщения и сделать их пропертями
ctx.msg.docs
ctx.msg.photos
Сейчас обработчики невалидного фильтра/аргументы обязаны принимать контекст. Задача заключается в том, чтобы в зависимости от обозначенного функцией этого аргумента контекст либо передавать его, либо не передавать вовсе (как сейчас это работает с реакцией на саму команду)
Служит для того чтобы в беседе бот мог работать без прав администратора и прав на чтение сообщений.
После обработки роутинга команды, которая начинается на роут команды (брак
и бракИ
) окончание может уйти в аргументы команды. Необходимо в случае аргументов проверить строку на пробельные символы с конца
В связи с тем, что возможность разных настроек поведения vkquick стало огромное множество (работа дебаггинга, включение подсветки в терминале, #15).
Предлагайте настройки, которые можно вынести в переменные окружения
сендер не срабоатет, если сообщение отправила группа. Необходимо доделать обертку на группу и внести изменения
Возникла необходимость в синхронных запросах к API, причем как со стороны разрабатки самого квика, так и в плане юзеджа.
В момент инициализации класса вызывается специальный define_token_owner
, который путем API запроса определяет, кому принадлежит токен -- пользователю или группе (класс не рассчитан на сервисные токена. Сейчас этот метод синхронный и реализован через urllib
. Реализация этого метода, фактически и есть реализация синхронной обертки под API, поэтому вполне разумно вынести все это дело.
Еще, скорее всего, нужно добавить для владельца токена отдельный аргумент при инициализации, т.е. если оно известно, то лишние запросы не производились бы.
Помимо прочего, иногда просто гораздо удобнее вызвать синхронный запрос, если весь код строится синхронно.
users = api.users.get(user_uds=1, sync_=True)
Либо полностью быть уверенным в том, что конфликтов у имен параметров не будет:
with api.synchronize():
users = api.users.get(user_ids=1)
Жду ваши мнения и предложения
Если команда не имеет аргументов, автоматически исключать контент сурс из контекста сразу после его инициализации
Сейчас время последнего запроса, используемое для просчета задержки, обновляется на константное значение. Нужно сделать обновление времени на разницу константного значения и времени, пройденного с момента последнего запроса
Я заметил ошибку, которая может возникать при инициализации в VkApiError::destruct_response. Т.к питон ожидает 3 значения для распаковки, но при некоторых ошибках в API VK (в моем случае ошибка №17) приходит 4 значение (в моём случае - это redirect_url
).
import asyncio
from vkquick import API
async def main():
"""
Creates a post in the group (the wall is open)
"""
api = API(
token="some_token",
version=5.124,
owner="user"
)
await api.wall.post(
owner_id=-group_id_with_an_open_wall,
message="Test"
)
asyncio.run(main())
... status_code, description, request_params = response["error"].values()
ValueError: too many values to unpack (expected 3)
{
"error":{
"error_code":17,
"error_msg":"Validation required: please open redirect_uri in browser",
"request_params":[
{
"key":"method",
"value":"wall.post"
},
{
"key":"oauth",
"value":"1"
},
{
"key":"v",
"value":"5.124"
},
{
"key":"owner_id",
"value":"-group_id_with_an_open_wall"
},
{
"key":"message",
"value":"Test"
}
],
"redirect_uri":"https://m.vk.com/activation?act=validate&api_hash=7005d2f15d640f3e02&hash=bb1ca97ec761fc37101"
}
}
Hash в
redirect_url
был заменён - ссылка недействительна
Бот делает всего пару запросов к SQLite, и прикручивание ORM только добавляет сложности, как мне кажется.
SQLAlchemy -- синхронная библиотека. Для работы с SQLite есть aiosqlite
.
Необходимо в экстра поле в контексте сохранять тот самый матч ее роутинга (то, что было заматчено в результате роутинга -- имя и префиксы)
Предлагайте сюда команды, которые хотите видеть в CLI
Сейчас на абсолютно каждое сообщение вызывается messages.getById
. Оптимизация заключается в том, что получать сообщение полностью только в том случае, если хотя бы один фильтр Command
прошел
Нужен враппер на группу
├── LICENSE
├── README.md
├── data
│ ├── configs
│ │ ├── custom.toml
│ │ ├── deploy.toml
│ │ ├── dev.toml
│ │ ├── docs.toml
│ │ └── tests.toml
│ ├── logs
│ │ ├── annotype.log
│ │ ├── api.log
│ │ ├── longpoll.log
│ │ ├── reaction.log
│ │ └── validator.log
│ └── patterns
│ ├── annotype.txt
│ ├── reaction.txt
│ ├── signal.txt
│ └── validator.txt
├── docs
├── src
│ ├── annotypes
│ ├── event_handlers
│ ├── signal_handlers
│ └── validators
└── tests
├── test_annotypes
├── test_event_handlers
├── test_signal_handlers
└── test_validators
Я не очень в курсе, как обычно содежат логи, поэтому если есть какое-то решение лучше -- без проблем добавим его. Конфиги по своей структуре тоже под вопросом.
Reaction
-> EventHandler
(EventsHandler
?)Signal
-> SignalHandler
Photo
-> PhotoUploader
(Делать отдельный класс для работы с полями пришедшего объекта от вк)Doc
-> DocumentUploader
rexp
. Может regex
?VkErr
-> APIError
/ VKAPIError
/ VkApiError
BuildType
, WordType
и все ..Type
. Честно, не вижу смысла в замене круглых скобок на квадратных и введении более "композиционного" стиля. Да, подвязать typing.List
и билдиновский лист и что-то подобное не только для листа это круто, но я думаю это можно сделать не ввязывая Type'ы для более детальной настройки. Однако, у меня есть одна мысль на этот счет. Я думаю, ты видел, как описывается терминальный инструмент через click
-- все флаги через декораторы, а дальше это уже падает в аргументы. И нагромождений нет, и расписать можно. Может, вот такие "детальные" настройки, по типу количества элементов выносить в декоратор? Но декораторы уже забиты под валидаторы...Мы как-то уже обсуждали гибкость аннотипов и пришли к тому, что нужно лишь несколько типов:
Возможно, мы что-то упустили, но команды бота -- это не bash, поэтому нужна ли им такая гибкость с билд типами и расширенными типами, или же можно просто обойтись аннотациями, какие они есть сейчас? (Я согласен на изменения логики внутри, меня не выткает именно использвоание)
Сейчас событие (vq.tools.event.Event
) -- это просто унаследованный от AttrMap
класс. Поскольку для v1.0 планируется добавление новых механик получения событий (в частности и возможность юзер ботов), было бы неплохо добавить общие методы по получение каких-то "межсобытийных" полей. Например, для сообщества (лп или кб событие) текст сообщения с версией 5.103
и выше будет event.object.message.text
, для ниже 5.103
event.object.text
, для пользователя вообще event[5]
. Поэтому для получения текста можно добавить метод, например, get_text()
, который бы рассматривал каждый кейс и получал текст сообщения в зависимости от события. И так с peer_id
, from_id
(здесь уже придется делать API запрос для юзерского лп) и другими полями. Для некоторых кейсов это даст универсальность написания ботов, которые будут совместимы как и с аккаунтом пользователя, так и сообщества, причем под апи разных версий. Как бы это странно не было, но юзер боты действительно пользуются популярностью, поэтому постараться сделать для них почти одинаковый интерфейс очень хорошая мысль.
Скорее всего bot
будет переделан с click
на cleo
, имхо, стиль гораздо удобнее
Убрать ReactionList
и SignalsList
вообще, а всю логику перенести в Bot
. Лишнее нагромождение.
Нужна ли поддержка своего стиля написания бота? Я боюсь, что если пользователю один раз показать, как делать бота в 1 файле -- он и будет делать его в 1 файле, вообще забивая на рефакторинг (что сейчас происходит в vk_api, vk.py, vkbottle, vkwave и во всех библиотеках)
Сущесвтует несколько оберток для вк апишных объектов (структура пользователя, структура фотографии, документа...). Все они немного похожи, поэтому, возможно, стоит задать им общий интерфейc.
Сейчас мне не очень нравится то, как выглядит загрузка фотографий и документов. Не только изнутри, но и внешне. Сама загрузка через апи (сырая) происходит в 3 этапа
2ой пункт полностью автоматизированный. Для 1ого и 3ьего можно передать некоторые параметры при запросе. Из этого можно составить примерную схему всей загрузки (уже используемую)
Либо объеденить 2ой и третий и запрашивать словарь парамтеров (если такое надо) на каждый из способов. Что-то в этом роде
photo = PhotoUploader.from_bytes(b'123')
await photo.upload_and_save.to_message(
{"peer_id": 123}, {"name": "foo"}
)
...
return vq.Message(attachment=photo)
Можно не мучать точки и передать путь загрузки на 2ом этапе,
photo = PhotoUploader.from_bytes(
b'123',
)
await photo.upload_and_save(
"message", # PhotoUploadPath.TO_MESSAGE ???
{"peer_id": 123}, {"name": "foo"}
)
Либо разбить на 3 этапа (можно оставить поддержку 2х способов)
photo = PhotoUploader.from_bytes(b'123',)
photo.upload_path = PhotoUploadPath.TO_MESSAGE
await photo.upload(peer_id=123)
await photo.save(name="foo")
Должен ли photo
сам стать объектом (используя мутабельность) -- или он вернет уже другой объект? Наверно, это две разные вещи, поэтому переменные нужно будет перебиндить
photo = PhotoUploader.from_bytes(
b'123',
)
# Вместо `await photo.upload_and_save(...`
photo = await photo.upload_and_save(
"message",
{"peer_id": 123}, {"name": "foo"}
)
Возможно, есть какие-то уже готовые инструменты, чтобы можно было обернуть байты/байтсио/что-угодно и получить сами байты.
В новой версии доки (стиль сайта будет тот же, старая уедет в отдельный раздел) хотелось бы четко разделить документацию от туториала. Я посмотрел инструменты, которые могут по сурсам сделать .md странички как документацию, но ничего толкового не нашел (не отрицаю, что плохо искал). Возможно, это тема уже нового проекта
Хачю пользователскую доку к боту на mkdocs material, как и дока vkquick
Как проекту бота работать с зависимостями? Просто вести requirements.txt или же вносить poetry? Или это уже забота пользвоателя?
Касательно того, как могут проходить тестирования. Сначала тесты нужно забилдить (построются файлы по сурсам). После чего можно будет эмулировать вызовы (используя параметрайз, естесна), куда будет "инъекцироваться" свое собственное собранное событие, идентичное вкшному (о нем чуть ниже). В ответ на эмуляцию вызова будет прилетать объект, описывающий вообще все, что приключилось внутри.
# Используя старый нейминг
import vkquick as vq
@vq.Cmd(names=["foo"])
@vq.Reaction("message_new")
async def foo(sender: vq.Sender()):
await api.some.method(param=1)
...
И где-то в тестах ты эмулируешь вызов с разными собыйтиями, на что получаешь такую схему:
{
"all_validators_passed": True,
"actions": { # Вызов апи, запросы к бд, еще что-нибудь, что происходит внутри реакции
"api_calling": [ # Все ретурны и илды также расцениваются как вызов апи метода
{
"method_name": "some.method",
"parameters": {
"param": 1
}
}
],
}
"arguments": {
"sender": <vq.Sender object at /dev/null lalala>
}
# Либо
# "arguments": [
# {
# "argument_name": "sender",
# "content": <vq.Sender object at /dev/null lalala>
# }
# ]
}
Как добавлять свои actions
? Либо оборачивать нужный класс/функцию специальной оберткой, либо хз. Честно. Но если придумать, как создавать свои штукенции для actions
, можно будет обеспечить шедевральную тестируемость, полностью совместимую с возможностями питеста.
Откуда брать события, которыми эмулируется вызов? Было бы круто, если бы vkquick сам записывал все события вызова команды, а потом сам предлагал на выбор какое-либо из них, а там уже, если надо, поля можно будет поменять под свои данные.
Вызвать execute через __getattr__
сейчас невозможно, приходится использовать method
. Необходимо добавить метод в класс по вызову execute
Ну понятно думаю и так все
Нужен фильтр, которым можно объединить несколько фильтров, который будет верен, если пройдет хотя бы один из них
Аналог typing.Literal
. Из дополнений можно будет опционально передать энум вместо списка из литералов, и в качестве аргументы получать значения этого энума
Требуется добавить аргумент в виде bot.shared_box
в аргументы вызова этих сигналов
fetch_chat_owner
: Возвращает создателя чата
fetch_chat_admins
: Возвращает админов чата и создателя (списком
Требуется параметр контекста для handle_event
, чтобы передать свой собственный контекст исходя из получения события через вейтер
contains_strings
отвечает за то, какие строки могут быть переданы в тексте команды. Ескейп так же зависим от автоейскейп-флага (т.е. есть поддержка регексов)
Дополнительные методы для контекста
async def run_infinity_sublistening(self, *comamnds)
: Создает в вечном цикле вейтеры и получает новые события, запуская обработку по всем переданным командам. Имеет дополнительный флаг, который при изменении (изменение доступно прямо внутри команд) остановит слежение за событиями
async def handle_until_called(self, *commands)
: получается новые события до тех пор, пока одна из команд не окажется обработанной
Добавить возможность инициализации бота/апи, указав имя глобальной переменной (начинается должна с $)
Возможностями current-объектов сейчас можно пользоваться очень удобно через функцию, возвращающую дескриптор проперти (речь о vkquick/current.py::fetch
). Единственная возникающая проблема -- это попытка использовать дескриптор в классе с classmethod/staticmethod методами. https://github.com/Rhinik/vkquick/blob/1.0/vkquick/wrappers/user.py. Приходится как бы руками дергать __get__
. Как вы на это смотрите?
Добавить возможность передавать юзеров просто по айди и с приставкой id
123
id123
[id123|abc]
Для UserMention
. Аналогичное для групп
Добавить флаг attach в методах загрузки через контекст для автоматического прикрепления вложения
await ctx.upload_photo("file.png", attach=True)
return "Send a photo"
Нужен флаг output
, ограничивающий исходящие сообщения.
Дополнительно: переименовать сообтвествующтй флаг в AllowAccessFor
Некорректный атрибут session
Для всех методов контекста по отправке сообщения нужно добавить установить disable_mentions=True
по умолчанию
Переделать на def вместо async def обработку Optional'а
Любые мысли/предложения по поводу бранчей. Под бранчами я подразумеваю получение нового события внутри реакции, дабы создавать некие "ветки"
Необходим флаг для инициализации exclude_only_whitespaces
, обозначающий, что слово принимает любые символы, кроме пробельных (\S+
)
Сделать флаг re.DOTALL
опциональным в текст каттере Стринг
try:
...
except VkApiError[8]:
...
except VkApiError[16]:
...
Необходимо в зависимости от типа бота (юзербот/группбот) опционально передавать клавиатуру (юзеры клавиатуры не поддерживают)
Нужно сделать проверку на ctx.msg.id
ноль, и в зависимости от этого выбрать способ, которым передать прикрепленное сообщение
Колбэки -- дополнительные валидаторы для текстовых аргументов
import vkquick as vq
@vq.Command(names="foo")
def foo(arg: vq.Integer):
return f"Arg is {arg}"
@foo.argument_callback
def arg(ctx, value):
if value > 10:
return vq.Decision(True, "Значение больше 10"
return vq.Decision(False, "Значение меньше 10")
Теперь если передать значение меньше 10, то обработка провалится. Вся необходимая информация появится в дебаггере, пользователь осведомится
Если же нужно как-то изменить аргумент, можно поменять его через контекст
ctx.reaction_arguments.arg = float(value)
Колбэки вызываются после того, как инициализированы все аргументы команды
Текст из Decision
(если передан False) по умолчанию будет отправляться пользователю в ответ. Отключить это можно по специальному флагу (для конкретного колбэка)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.