Giter VIP home page Giter VIP logo

vkquick's Introduction

VK Quick шапка

VK Quickэто современный асинхронный фреймворк для создания ботов ВКонтакте, автоматически генерирующий документацию к командам бота в виде сайта


Ключевые особенности:

PyPI - Python Version Downloads

  • Скорость: VK Quick использует конкурентность в одном потоке (asyncio) и является одним из самых быстрых фреймворков для разработки ботов

  • Компактность кода: Разработка требует меньше времени в несколько раз, код становится короче, вероятность возникновения багов уменьшается. VK Quick автоматически создает документацию к написанному боту, позволяя сконцентрироваться разработчику именно на самом коде

  • Легкое обучение: Создавать ботов невероятно просто вместе с VK Quick! Обучение проходит быстро и легко

  • Инструменты для упрощения разработки: Из коробки VK Quick представляет CLI (терминальная утилита) — инструмент, облегающий процесс создания команд, настройки проекта и выстраивания архитектуры

  • Поддержка актуального API: Множество разных возможностей для ботов перенесены в удобный Python-стиль, любые нововведения в социальной сети незамедлительно отображаются в самом фреймворке

  • Отзывчивое коммьюнити: Вы всегда можете обратиться с вопросом, на который обязательно ответят наши специалисты по разработке ботов в официальной беседе нашего сообщества


Установка

python -m pip install vkquick

До релиза 1.0: python -m pip install https://github.com/deknowny/vkquick/archive/master.zip

Вместе с фреймворком устанавливается треминальная утилита — kwik:

kwik --help

Echo-бот

Прежде чем создать своего первого бота, нужно получить специальный токен — ключ, через который можно взаимодействовать с ресурсами ВК. VK Quick позволяет писать ботов как для групп, так и пользователей в одном стиле — достаточно запустить код с нужным токеном.

import vkquick as vq


app = vq.App()


@app.command("пинг", "ping")
async def greeting():
    """
    Самая обычная пинг-понг команда
    """
    return "Понг!"


@app.command("дата", prefixes=["/"])
async def resolve_user(user: vq.User):
    """
    Возвращает дату регистрации указанного пользователя
    """
    registration_date = await vq.get_user_registration_date(user.id)
    formatted_date = registration_date.strftime("%d.%m.%Y")
    return f"Дата регистрации пользователя {user:@[fullname]}: {formatted_date}"


app.run("token")

Остается подставить вместо "token" свой токен. Теперь у нас есть бот сразу с двумя командами!

Пример работы бота

И автоматически созданная документация по командам в папке autodocs

Пример автоматически сгенерированной документации

Хотите больше возможностей? Переходите на наш официальный сайт https://vkquick.rtfd.io и продолжайте углубляться в разработку ботов вместе с VK Quick!

vkquick's People

Contributors

alexdev505 avatar decorator-factory avatar deknowny avatar hateme02 avatar iinagibatorii avatar ketre3 avatar monosans avatar nikitolpl avatar nikitolproject avatar trard avatar voronin9032 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

vkquick's Issues

Оптимизировать расчёт времени задержки для API запросов

Сейчас время последнего запроса, используемое для просчета задержки, обновляется на константное значение. Нужно сделать обновление времени на разницу константного значения и времени, пройденного с момента последнего запроса

Флаг для Word (1.1)

Необходим флаг для инициализации exclude_only_whitespaces, обозначающий, что слово принимает любые символы, кроме пробельных (\S+)

Сендер для групп (1.1)

сендер не срабоатет, если сообщение отправила группа. Необходимо доделать обертку на группу и внести изменения

Переменные окружения, используемые для определения поведения работы

В связи с тем, что возможность разных настроек поведения vkquick стало огромное множество (работа дебаггинга, включение подсветки в терминале, #15).

Предлагайте настройки, которые можно вынести в переменные окружения

Приколы с сессиями и хедером (1.0)

  1. При внешней перезагрузке бота в случае ошибок, возникает RuntimeError, потому что сессия аиохттп закрывается, а проверка для открытия сессии существует только если она None. Необходимо так же добавить условие на проверку, открыта ли сессия

  2. Хедер X-Next-Ts, почему-то, приходит не всегда.

Некорректная обработка имени (1.0)

После обработки роутинга команды, которая начинается на роут команды (брак и бракИ) окончание может уйти в аргументы команды. Необходимо в случае аргументов проверить строку на пробельные символы с конца

Опциональные аргументы в обработчиках невалидного фильтра или аргумента

Сейчас обработчики невалидного фильтра/аргументы обязаны принимать контекст. Задача заключается в том, чтобы в зависимости от обозначенного функцией этого аргумента контекст либо передавать его, либо не передавать вовсе (как сейчас это работает с реакцией на саму команду)

Колбэки для текстовых аргументов (1.1)

Колбэки -- дополнительные валидаторы для текстовых аргументов

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) по умолчанию будет отправляться пользователю в ответ. Отключить это можно по специальному флагу (для конкретного колбэка)

Враппер на чат (1.1)

Сделать обертку на чат и соответсвующие методы в контекст на получение этой обертки

Мысли по v1.0

Структура проекта бота

├── 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 -- все флаги через декораторы, а дальше это уже падает в аргументы. И нагромождений нет, и расписать можно. Может, вот такие "детальные" настройки, по типу количества элементов выносить в декоратор? Но декораторы уже забиты под валидаторы...

Мы как-то уже обсуждали гибкость аннотипов и пришли к тому, что нужно лишь несколько типов:

  • List(...)
  • Integer
  • Word
  • String
  • Literal
  • Разного рода упоминания
  • Optional
  • GroupedList, он же список с туплами
  • Кастомный регекс на самый крайний случай
  • Возможно что-то с временем и датой, но тут очень тонко

Возможно, мы что-то упустили, но команды бота -- это не 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 этапа

    1. Получение урла сервера, куда отправить запрос
    2. Отправление фотографии/документа на этот урл
    3. Сохранение через определенный апишный метод

2ой пункт полностью автоматизированный. Для 1ого и 3ьего можно передать некоторые параметры при запросе. Из этого можно составить примерную схему всей загрузки (уже используемую)

  1. Инициализация объекта по байтами/пути до файла/скачиванию по урлу/BytesIO/TextIOWrapper/ все, что возможно. Очевидно, что кажадя инициализация будет через свой статический метод
  2. Загрузка (передача параметров для метода при загрузке)
  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 сам записывал все события вызова команды, а потом сам предлагал на выбор какое-либо из них, а там уже, если надо, поля можно будет поменять под свои данные.

Ошибка в VkApiError::destruct_response (vkquick-1.0)

Описание

Я заметил ошибку, которая может возникать при инициализации в 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())

Ошибка в VkApiError::destruct_response:

 ... status_code, description, request_params = response["error"].values()
ValueError: too many values to unpack (expected 3)

JSON ошибки, который присылает API VK:

{
   "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 был заменён - ссылка недействительна

Синхронизация API и проблема с определением владельца токена

Возникла необходимость в синхронных запросах к 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)

Жду ваши мнения и предложения

Реализация бранчей

Любые мысли/предложения по поводу бранчей. Под бранчами я подразумеваю получение нового события внутри реакции, дабы создавать некие "ветки"

Клонирование бота

Одно из двух:

  • Сделать возможность клонирования ботов путем вызова метода у существующего бота и передачи нового токена (создается новый инстанс с теми же командами, обработчиками сигналов и событий, а так же с теми же настройками дебаггера, флага релиза и прочих полей)

  • Оставлять работу с одним инстансом бота, просто передавая необходимое количество дополнительных токенов

Методы контекста (1.1)

fetch_chat_owner: Возвращает создателя чата
fetch_chat_admins: Возвращает админов чата и создателя (списком

Каттер Literal (1.1)

Аналог typing.Literal. Из дополнений можно будет опционально передать энум вместо списка из литералов, и в качестве аргументы получать значения этого энума

Флаг для RetractAccessFor (1.0)

Нужен флаг output, ограничивающий исходящие сообщения.

Дополнительно: переименовать сообтвествующтй флаг в AllowAccessFor

SQLAlchemy в примере бота

  1. Бот делает всего пару запросов к SQLite, и прикручивание ORM только добавляет сложности, как мне кажется.

  2. SQLAlchemy -- синхронная библиотека. Для работы с SQLite есть aiosqlite.

`execute` метод для API (1.1)

Вызвать execute через __getattr__ сейчас невозможно, приходится использовать method. Необходимо добавить метод в класс по вызову execute

Дескрипторы для куррента

Возможностями current-объектов сейчас можно пользоваться очень удобно через функцию, возвращающую дескриптор проперти (речь о vkquick/current.py::fetch). Единственная возникающая проблема -- это попытка использовать дескриптор в классе с classmethod/staticmethod методами. https://github.com/Rhinik/vkquick/blob/1.0/vkquick/wrappers/user.py. Приходится как бы руками дергать __get__. Как вы на это смотрите?

OrFilter (1.1)

Нужен фильтр, которым можно объединить несколько фильтров, который будет верен, если пройдет хотя бы один из них

Дополнительные методы для работы с новым событием (1.1)

Дополнительные методы для контекста

  • async def run_infinity_sublistening(self, *comamnds): Создает в вечном цикле вейтеры и получает новые события, запуская обработку по всем переданным командам. Имеет дополнительный флаг, который при изменении (изменение доступно прямо внутри команд) остановит слежение за событиями

  • async def handle_until_called(self, *commands): получается новые события до тех пор, пока одна из команд не окажется обработанной

Дополнительные флаги в `Command`

any_text: bool: реагировать на любой текст (аналог евент хендлера)
payload_names: ty.List[str]: имена из пейлоада, на которые может реагировать команда

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.