Skip to content

Commit

Permalink
feat: bumped api_version for vkontakte, provided more typing for meth…
Browse files Browse the repository at this point in the history
…ods (should fix #93)
  • Loading branch information
Michael Kryukov committed Sep 8, 2024
1 parent f73f83f commit 6eda0da
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: ["3.8", "3.10", "3.11"]
python-version: ["3.8", "3.11", '3.12']
defaults:
run:
shell: bash
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
> Changes to public API are marked as `^`. Possible changes
> to public API are marked as `^?`.
- v6.0.1
- Changes
- (VKontakte) Bumped default `api_version` to `5.199`.
- Updated version of python for tests and lintings.
- Refactor
- (Core) Provided more typing for some of the plugin's methods.

- v6.0.0
- Notes
- This version focuses on removing features that are too hard
Expand Down
18 changes: 9 additions & 9 deletions example/plugins/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ async def _(msg, ctx):

await ctx.reply("Document", attachments=[doc])

# Audio message
with open(get_path(__file__, "assets/audio.ogg"), "rb") as fh:
audio_message = Attachment(
kind=AttachmentKind.VOICE,
content=("audio.ogg", fh.read()),
)

await ctx.reply("Audio message", attachments=[audio_message])

# Video
with open(get_path(__file__, "assets/video.mp4"), "rb") as fh:
video = Attachment(
Expand All @@ -34,12 +43,3 @@ async def _(msg, ctx):
)

await ctx.reply("Video", attachments=[video])

# Audio message
with open(get_path(__file__, "assets/audio.ogg"), "rb") as fh:
audio_message = Attachment(
kind=AttachmentKind.VOICE,
content=("audio.ogg", fh.read()),
)

await ctx.reply("Audio message", attachments=[audio_message])
7 changes: 3 additions & 4 deletions kutana/backends/vkontakte/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def __init__(
self,
token,
requests_per_second=19,
api_version="5.131",
api_version="5.199",
api_url="https://api.vk.com",
):
if not token:
Expand Down Expand Up @@ -224,9 +224,8 @@ def _make_update(self, raw_update):
)

async def _update_group_data(self):
groups = await self._direct_request("groups.getById", {"fields": "screen_name"})

self.group = groups[0]
data = await self._direct_request("groups.getById", {"fields": "screen_name"})
self.group = data["groups"][0]

async def on_start(self, app):
await self._update_group_data()
Expand Down
2 changes: 1 addition & 1 deletion kutana/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ async def reply(self, text=None, attachments=None, **kwargs):
)


def split_large_text(text, length=4096):
def split_large_text(text: str, length=4096):
"""
Split text into chunks with specified length.
Expand Down
19 changes: 14 additions & 5 deletions kutana/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from functools import wraps
from typing import Callable, List

from .backend import Backend
from .context import Context
Expand All @@ -24,7 +25,7 @@ async def wrapper(message: Message, context: Context):
return decorator


def expect_backend(expected_identity):
def expect_backend(expected_identity: str):
"""
Return decorators that skips all updates acquired from
backends with identity different from specified one.
Expand All @@ -42,7 +43,11 @@ async def wrapper(message: Message, context: Context):
return decorator


async def _get_user_statuses_vk(backend, chat_id, user_id):
async def _get_user_statuses_vk(
backend: Backend,
chat_id: str,
user_id: str,
) -> List[str]:
members_response = await backend.request(
"messages.getConversationMembers",
{"peer_id": chat_id, "fields": ""},
Expand All @@ -63,7 +68,11 @@ async def _get_user_statuses_vk(backend, chat_id, user_id):
return []


async def _get_user_statuses_tg(backend, chat_id, user_id):
async def _get_user_statuses_tg(
backend: Backend,
chat_id: str,
user_id: str,
) -> List[str]:
chat_administrators = await backend.request(
"getChatAdministrators",
{"chat_id": chat_id},
Expand All @@ -83,7 +92,7 @@ async def _get_user_statuses_tg(backend, chat_id, user_id):
return []


async def _get_user_statuses(backend: Backend, chat_id, user_id):
async def _get_user_statuses(backend: Backend, chat_id: str, user_id: str):
if backend.get_identity() == "vk":
return await _get_user_statuses_vk(backend, chat_id, user_id)

Expand All @@ -93,7 +102,7 @@ async def _get_user_statuses(backend: Backend, chat_id, user_id):
raise NotImplementedError()


def _expect_sender_status(func, expected_status):
def _expect_sender_status(func: Callable, expected_status: str):
@expect_recipient_kind(RecipientKind.GROUP_CHAT)
@wraps(func)
async def wrapper(message: Message, context: Context):
Expand Down
6 changes: 3 additions & 3 deletions kutana/handler.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
class Symbol:
class HandledResultSymbol:
def __init__(self, name: str):
self.name = name

def __repr__(self):
return f"<Symbol({self.name})>"


PROCESSED = Symbol("PROCESSED")
SKIPPED = Symbol("SKIPPED")
PROCESSED = HandledResultSymbol("PROCESSED")
SKIPPED = HandledResultSymbol("SKIPPED")
18 changes: 6 additions & 12 deletions kutana/kutana.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio
import logging
from itertools import groupby
from typing import Dict, List
from typing import Callable, Dict, List

from .backend import Backend
from .context import Context
Expand Down Expand Up @@ -43,7 +43,7 @@ def __init__(
self._storages: Dict[str, Storage] = {"default": MemoryStorage()}
self._root_router: Router

self._hooks = {
self._hooks: Dict[str, List[Callable]] = {
"start": [],
"exception": [],
"completion": [],
Expand Down Expand Up @@ -79,7 +79,7 @@ def _prepare_routers(self):

self._root_router = root_router

async def _handle_event(self, event, *args, **kwargs):
async def _handle_event(self, event: str, *args, **kwargs):
for handler in self._hooks[event]:
try:
await handler(*args, **kwargs)
Expand All @@ -96,11 +96,8 @@ def add_storage(self, name, storage):
def storages(self):
return self._storages

def add_plugin(self, plugin):
def add_plugin(self, plugin: Plugin):
"""Add plugin to the application."""
if not isinstance(plugin, Plugin):
raise ValueError(f"Provided value is not a plugin: {plugin}")

if plugin in self._plugins:
raise RuntimeError("Plugin already added")

Expand All @@ -113,11 +110,8 @@ def add_plugin(self, plugin):
def plugins(self):
return self._plugins

def add_backend(self, backend):
def add_backend(self, backend: Backend):
"""Add backend to the application."""
if not isinstance(backend, Backend):
raise ValueError(f"Provided value is not a backend: {backend}")

if backend in self._backends:
raise RuntimeError("Backend already added")

Expand Down Expand Up @@ -174,7 +168,7 @@ async def _run(self):
self.stop()
raise

async def _handle_update(self, context):
async def _handle_update(self, context: Context):
logging.debug("Processing update %s", context.update)

try:
Expand Down
66 changes: 42 additions & 24 deletions kutana/plugin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import functools
import re
from typing import Any, List
from typing import Any, Awaitable, Callable, Dict, List, Optional, Union

from .backends.vkontakte import VkontaktePluginExtension
from .handler import SKIPPED
from .context import Context
from .handler import SKIPPED, HandledResultSymbol
from .router import AttachmentsRouter, CommandsRouter, ListRouter, Router
from .storage import Document
from .update import Message

HandlerType = Callable[[Message, Context], Awaitable[Optional[HandledResultSymbol]]]


class Plugin:
"""
Expand All @@ -32,14 +35,6 @@ def __init__(self, name: str, **kwargs):
# Setup extensions
self.vk = VkontaktePluginExtension(self)

def __getattr__(self, name):
"""Defined for typing"""
return super().__getattribute__(name)

def __setattr__(self, name, value):
"""Defined for typing"""
return super().__setattr__(name, value)

def on_start(self):
"""
Return decorator for registering coroutines that will be called
Expand Down Expand Up @@ -91,8 +86,8 @@ def decorator(coro):

def on_commands(
self,
commands,
priority=0,
commands: List[str],
priority: int = 0,
):
"""
Return decorator for registering handler that will be called
Expand All @@ -112,7 +107,7 @@ def on_commands(
other handlers are not executed further.
"""

def decorator(coro):
def decorator(coro: HandlerType):
router = CommandsRouter(priority=priority)
for command in commands:
router.add_handler(command, coro)
Expand All @@ -123,7 +118,11 @@ def decorator(coro):

return decorator

def on_match(self, patterns, priority=0):
def on_match(
self,
patterns: List[Union[str, re.Pattern[str]]],
priority: int = 0,
):
"""
Return decorator for registering handler that will be called
when incoming update is a message and it's message matches any
Expand Down Expand Up @@ -168,8 +167,8 @@ async def _wrapper(update, ctx):

def on_attachments(
self,
kinds,
priority=0,
kinds: List[str],
priority: int = 0,
):
"""
Return decorator for registering handler that will be called
Expand All @@ -180,7 +179,7 @@ def on_attachments(
about 'priority' and return values.
"""

def decorator(coro):
def decorator(coro: HandlerType):
router = AttachmentsRouter(priority=priority)
for kind in kinds:
router.add_handler(kind, coro)
Expand All @@ -191,7 +190,10 @@ def decorator(coro):

return decorator

def on_messages(self, priority=-1):
def on_messages(
self,
priority: int = -1,
):
"""
Return decorator for registering handler that will be called
when incoming update is a message. Handler will always be
Expand All @@ -202,7 +204,7 @@ def on_messages(self, priority=-1):
about 'priority' and return values.
"""

def decorator(coro):
def decorator(coro: HandlerType):
router = ListRouter(priority=priority)

@functools.wraps(coro)
Expand All @@ -219,7 +221,10 @@ async def _wrapper(update, context):

return decorator

def on_updates(self, priority=0):
def on_updates(
self,
priority: int = 0,
):
"""
Return decorator for registering handler that will be always
called (for messages and not messages).
Expand All @@ -228,7 +233,7 @@ def on_updates(self, priority=0):
about 'priority' and return values.
"""

def decorator(coro):
def decorator(coro: HandlerType):
router = ListRouter(priority=priority)
router.add_handler(coro)

Expand All @@ -238,7 +243,12 @@ def decorator(coro):

return decorator

def with_storage(self, check_sender=None, check_recipient=None, storage="default"):
def with_storage(
self,
check_sender: Optional[Dict] = None,
check_recipient: Optional[Dict] = None,
storage: str = "default",
):
"""
This decorator allow plugins to implicitly require access to database.
Context is populated with the following fields:
Expand Down Expand Up @@ -276,12 +286,12 @@ def with_storage(self, check_sender=None, check_recipient=None, storage="default
context.sender.update_and_save(field2="value2") # update data and store it in database
"""

def _perform_check(data, check):
def _perform_check(data: Document, check: Optional[Dict]):
"""Return true if handler should be called."""

return not check or all(data.get(k) == v for k, v in check.items())

def decorator(coro):
def decorator(coro: HandlerType):
@functools.wraps(coro)
async def wrapper(update, context):
context.storage = self.app.storages[storage]
Expand Down Expand Up @@ -315,3 +325,11 @@ async def wrapper(update, context):
return wrapper

return decorator

def __getattr__(self, name: str):
"""Defined for typing"""
return super().__getattribute__(name)

def __setattr__(self, name: str, value):
"""Defined for typing"""
return super().__setattr__(name, value)
Loading

0 comments on commit 6eda0da

Please sign in to comment.