initial commit
This commit is contained in:
0
kurocore/main/__init__.py
Normal file
0
kurocore/main/__init__.py
Normal file
42
kurocore/main/event.py
Normal file
42
kurocore/main/event.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from kurocore.utils.vk_utils import generate_random_id
|
||||
|
||||
|
||||
class ChatEvent:
|
||||
__slots__ = ('session', 'api', 'raw', 'type', 'member_id', 'text', 'photo')
|
||||
|
||||
def __init__(self, session, api, raw: dict):
|
||||
self.session = session
|
||||
self.api = api
|
||||
self.raw: dict = raw
|
||||
|
||||
self.type: str = self.raw.get('type', '')
|
||||
self.member_id: int = self.raw.get('member_id', 0)
|
||||
self.text: str = self.raw.get('text', '')
|
||||
self.photo: dict = self.raw.get('photo', {})
|
||||
|
||||
|
||||
class Event:
|
||||
__slots__ = ('session', 'api', 'raw', 'type')
|
||||
|
||||
def __init__(self, session, api, raw: dict):
|
||||
self.session = session
|
||||
self.api = api
|
||||
|
||||
self.raw: dict = raw['object']
|
||||
self.type: str = raw['type']
|
||||
|
||||
async def send_message(self, target_id, text, attachments: (str, list, tuple, set, frozenset) = '', **kwargs):
|
||||
data = kwargs.copy()
|
||||
data.update({
|
||||
'peer_id': target_id,
|
||||
'random_id': generate_random_id()
|
||||
})
|
||||
|
||||
if text:
|
||||
data.update({'message': text})
|
||||
if attachments:
|
||||
if type(attachments) == str:
|
||||
data.update({'attachment': attachments})
|
||||
else:
|
||||
data.update({'attachment': ','.join(attachments)})
|
||||
await self.api.messages.send(**data)
|
256
kurocore/main/handler.py
Normal file
256
kurocore/main/handler.py
Normal file
@@ -0,0 +1,256 @@
|
||||
import asyncio
|
||||
import traceback
|
||||
|
||||
from aiohttp.web import run_app
|
||||
from sentry_sdk import init as sentry_init, capture_exception, set_user
|
||||
|
||||
from .event import ChatEvent, Event
|
||||
from kurocore.utils.database.database import Database
|
||||
from kurocore.utils.vk.longpoll import VKLongPoll, VkBotEventType
|
||||
from kurocore.utils.vk.vk import VK
|
||||
from ..logger import BotLogger
|
||||
|
||||
|
||||
class Handler:
|
||||
__slots__ = (
|
||||
'config', 'session', 'api', 'plugins', 'middlewares', 'loop', 'app'
|
||||
)
|
||||
|
||||
def __init__(self, config, middlewares: list):
|
||||
self.config = config
|
||||
self.session = None
|
||||
self.api = None
|
||||
self.app = None
|
||||
self.plugins = []
|
||||
self.middlewares = middlewares
|
||||
self.loop = asyncio.get_event_loop()
|
||||
|
||||
def init(self):
|
||||
if not self.config.tokens:
|
||||
BotLogger.log.error('No access token!')
|
||||
exit()
|
||||
self.session = VK(self.config.tokens[0])
|
||||
self.api = self.session.get_api()
|
||||
|
||||
if not self.config.debug and self.config.sentry_dsn:
|
||||
sentry_init(
|
||||
self.config.sentry_dsn,
|
||||
traces_sample_rate=1.0
|
||||
)
|
||||
BotLogger.log.info('Sentry initialized!')
|
||||
|
||||
for p in self.config.plugins:
|
||||
for init in p.init_methods:
|
||||
BotLogger.log.debug(f'Init: {p.__class__.__name__}')
|
||||
init.call()
|
||||
self.plugins.append(p)
|
||||
|
||||
def shutdown(self):
|
||||
for p in self.plugins:
|
||||
for method in p.shutdown_methods:
|
||||
method.call()
|
||||
BotLogger.log.info('Bot has been shutdown!')
|
||||
|
||||
async def check_payload(self, msg):
|
||||
payload = msg.payload['command'] if 'command' in msg.payload else ''
|
||||
args = msg.payload['args'] if 'args' in msg.payload else []
|
||||
|
||||
for p in self.plugins:
|
||||
if p.custom_checker:
|
||||
try:
|
||||
for before_process in p.before_process_methods:
|
||||
before_process()
|
||||
await p.custom_checker(msg, p)
|
||||
return True
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
||||
|
||||
if payload in p.payloads.keys():
|
||||
try:
|
||||
msg.meta.update({'args': args})
|
||||
|
||||
for before_process in p.before_process_methods:
|
||||
before_process.call()
|
||||
|
||||
valid, args = await p._validate_payload_args(payload, args)
|
||||
|
||||
if not valid:
|
||||
await msg.answer('Неверное количество или тип аргументов!')
|
||||
return False
|
||||
|
||||
for before_process in p.before_process_methods:
|
||||
before_process.call()
|
||||
|
||||
if valid and args is not None:
|
||||
await p._process_payload_with_args(payload, msg, args)
|
||||
else:
|
||||
await p._process_payload(payload, msg)
|
||||
return True
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
async def check_command(self, msg):
|
||||
text = msg.text
|
||||
|
||||
for prefix in self.config.prefixes:
|
||||
if text.startswith(prefix):
|
||||
text = text[len(prefix):]
|
||||
msg.meta['prefix'] = prefix
|
||||
break
|
||||
else:
|
||||
if msg.is_chat:
|
||||
return
|
||||
|
||||
for p in self.plugins:
|
||||
if p.custom_checker:
|
||||
try:
|
||||
for before_process in p.before_process_methods:
|
||||
before_process()
|
||||
await p.custom_checker(msg, p)
|
||||
return
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
||||
|
||||
for command in p.commands:
|
||||
if text.startswith(command):
|
||||
# if p._is_vip_command(command):
|
||||
# user = await get_user_or_none(msg.from_id)
|
||||
# if not user or not user.group.is_vip:
|
||||
# return msg.answer('Для доступа к этой команде требует VIP доступ!')
|
||||
#
|
||||
# if p._is_admin_command(command):
|
||||
# user = await get_user_or_none(msg.from_id)
|
||||
# if not user or not user.group.is_admin:
|
||||
# return msg.answer('Данная комманда доступна только для администраторов!')
|
||||
try:
|
||||
msg.meta['cmd'] = command
|
||||
args = text[len(command) + 1:].split()
|
||||
msg.meta['args'] = args
|
||||
args_valid, args = await p._validate_command_args(command, args)
|
||||
if not args_valid:
|
||||
return await msg.answer('Неверное количество или тип аргументов!')
|
||||
|
||||
for before_process in p.before_process_methods:
|
||||
before_process.call()
|
||||
|
||||
await p._process_command(command, msg, args)
|
||||
return
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
||||
|
||||
async def check(self, msg):
|
||||
db = Database.db
|
||||
|
||||
if db:
|
||||
if not db.is_closed():
|
||||
BotLogger.log.debug('Connection reused!')
|
||||
else:
|
||||
db.connect()
|
||||
BotLogger.log.debug('Connection reopened!')
|
||||
else:
|
||||
BotLogger.log.debug('No database')
|
||||
|
||||
for m in self.middlewares:
|
||||
await m(msg)
|
||||
|
||||
for plugin in self.plugins:
|
||||
for method in plugin.before_check_methods:
|
||||
await method(msg)
|
||||
|
||||
if not await self.check_payload(msg):
|
||||
await self.check_command(msg)
|
||||
|
||||
# if db:
|
||||
# db.close()
|
||||
# BotLogger.log.debug('Connection closed!')
|
||||
|
||||
async def check_event(self, event: (ChatEvent, Event), msg):
|
||||
event_type = event.type
|
||||
|
||||
for plugin in self.plugins:
|
||||
if event_type in plugin.chat_events.keys():
|
||||
try:
|
||||
for before_process in plugin.before_process_methods:
|
||||
before_process.call()
|
||||
return await plugin.chat_events[event_type](event, msg)
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
||||
|
||||
elif event_type in plugin.events.keys():
|
||||
try:
|
||||
for before_process in plugin.before_process_methods:
|
||||
before_process.call()
|
||||
return await plugin.events[event_type](event)
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
||||
|
||||
def run(self):
|
||||
if self.config.callback.enabled:
|
||||
run_app(self.app, port=self.config.port)
|
||||
try:
|
||||
# Register all plugins tasks
|
||||
for p in self.config.plugins:
|
||||
for task in p.tasks:
|
||||
BotLogger.log.debug(f'registered task: {task.__name__}')
|
||||
self.loop.create_task(task())
|
||||
self.loop.run_until_complete(self._run())
|
||||
except KeyboardInterrupt:
|
||||
self.session.shutdown()
|
||||
# self.shutdown()
|
||||
|
||||
async def handle_event(self, event):
|
||||
from kurocore import Message
|
||||
if ((event.type == VkBotEventType.MESSAGE_NEW and 'action' not in event.obj)
|
||||
or event.type == VkBotEventType.MESSAGE_EVENT):
|
||||
msg = Message(self.session, self.api, event.obj)
|
||||
if msg.user_id > 0:
|
||||
set_user({'id': msg.user_id})
|
||||
await self.check(msg)
|
||||
|
||||
elif event.type == VkBotEventType.MESSAGE_NEW and 'action' in event.obj:
|
||||
e = ChatEvent(self.session, self.api, event.obj['action'])
|
||||
msg = Message(self.session, self.api, event.obj)
|
||||
if msg.user_id > 0:
|
||||
set_user({'id': msg.user_id})
|
||||
await self.check_event(e, msg)
|
||||
|
||||
else:
|
||||
e = Event(self.session, self.api, event.raw)
|
||||
await self.check_event(e, None)
|
||||
|
||||
async def _run(self):
|
||||
group = (await self.api.groups.getById())['groups'][0]
|
||||
BotLogger.log.info(f'Login as {group["name"]} (https://vk.com/public{group["id"]})')
|
||||
|
||||
lp = VKLongPoll(self.config, self.session)
|
||||
await lp.init_lp()
|
||||
|
||||
async for event in lp.listen():
|
||||
try:
|
||||
await self.loop.create_task(self.handle_event(event))
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
BotLogger.log.error(traceback.format_exc())
|
||||
else:
|
||||
capture_exception(e)
|
331
kurocore/main/message.py
Normal file
331
kurocore/main/message.py
Normal file
@@ -0,0 +1,331 @@
|
||||
import json
|
||||
from enum import Enum
|
||||
from typing import Union, Type, Tuple
|
||||
|
||||
from kurocore.utils.vk.keyboard import VkKeyboard
|
||||
from kurocore.utils.vk.vk import VKApiException
|
||||
from kurocore.utils.vk_utils import generate_random_id
|
||||
|
||||
|
||||
class MessageArgs(dict):
|
||||
def __init__(self, args: dict):
|
||||
self.__args: dict = args
|
||||
super().__init__(args)
|
||||
|
||||
def __getattr__(self, item):
|
||||
try:
|
||||
return self.__args[item]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
class VkObject:
|
||||
__slots__ = (
|
||||
'raw',
|
||||
)
|
||||
|
||||
def __init__(self, raw):
|
||||
self.raw = raw
|
||||
|
||||
|
||||
class PhotoSize:
|
||||
__slots__ = (
|
||||
'type', 'url', 'width', 'height'
|
||||
)
|
||||
|
||||
def __init__(self, raw: dict):
|
||||
self.type: str = raw['type']
|
||||
self.url: str = raw['url']
|
||||
self.width: int = raw['width']
|
||||
self.height: int = raw['height']
|
||||
|
||||
|
||||
class StickerSize:
|
||||
__slots__ = (
|
||||
'url', 'width', 'height'
|
||||
)
|
||||
|
||||
def __init__(self, raw: dict):
|
||||
self.url: str = raw['url']
|
||||
self.width: int = raw['width']
|
||||
self.height: int = raw['height']
|
||||
|
||||
|
||||
class Photo(VkObject):
|
||||
__slots__ = (
|
||||
'id', 'album_id', 'owner_id', 'access_key',
|
||||
'user_id', 'text', 'date', 'sizes',
|
||||
'type'
|
||||
)
|
||||
|
||||
def __init__(self, raw: dict):
|
||||
super().__init__(raw)
|
||||
self.type = 'photo'
|
||||
self.raw: dict = raw['photo']
|
||||
|
||||
self.id: int = self.raw['id']
|
||||
self.album_id: int = self.raw['album_id']
|
||||
self.owner_id: int = self.raw['owner_id']
|
||||
self.access_key: str = self.raw.get('access_key', '')
|
||||
self.user_id: int = self.raw.get('user_id', 0)
|
||||
|
||||
self.text: str = self.raw['text']
|
||||
self.date: int = self.raw['date'] # unix time
|
||||
self.sizes = [PhotoSize(r) for r in self.raw['sizes']]
|
||||
|
||||
def __repr__(self):
|
||||
return f'photo{self.owner_id}_{self.id}'
|
||||
|
||||
|
||||
class Sticker(VkObject):
|
||||
__slots__ = (
|
||||
'type', 'images', 'images_with_background',
|
||||
'animation_url', 'is_allowed'
|
||||
)
|
||||
|
||||
def __init__(self, raw: dict):
|
||||
super().__init__(raw)
|
||||
|
||||
self.type = 'sticker'
|
||||
self.raw: dict = raw['sticker']
|
||||
|
||||
self.images = [StickerSize(img) for img in self.raw['images']]
|
||||
self.images_with_background = [StickerSize(img) for img in self.raw['images_with_background']]
|
||||
|
||||
self.animation_url = self.raw.get('animation_url', '')
|
||||
self.is_allowed = self.raw.get('is_allowed')
|
||||
|
||||
def __repr__(self):
|
||||
return f'sticker{self}'
|
||||
|
||||
|
||||
class DocumentType(Enum):
|
||||
TEXT = 1
|
||||
ARCHIVE = 2
|
||||
GIF = 3
|
||||
PHOTO = 4
|
||||
AUDIO = 5
|
||||
VIDEO = 6
|
||||
BOOKS = 7
|
||||
UNKNOWN = 8
|
||||
|
||||
|
||||
class Document(VkObject):
|
||||
__slots__ = (
|
||||
'id', 'album_id', 'owner_id',
|
||||
'title', 'size', 'ext', 'url',
|
||||
'date', 'type'
|
||||
)
|
||||
|
||||
def __init__(self, raw: dict):
|
||||
super().__init__(raw)
|
||||
self.raw: dict = raw['doc']
|
||||
self.id: int = self.raw['id']
|
||||
self.owner_id: int = self.raw['owner_id']
|
||||
|
||||
self.title: str = self.raw['title']
|
||||
self.size: int = self.raw['size'] # размер в байтах
|
||||
self.ext: str = self.raw['ext'] # расширение
|
||||
self.url: str = self.raw['url']
|
||||
self.date: int = self.raw['date'] # unix time
|
||||
self.type: DocumentType = self.raw['type']
|
||||
|
||||
def __repr__(self):
|
||||
return f'doc{self.owner_id}_{self.id}'
|
||||
|
||||
|
||||
Attachment = Type[Photo], Type[Document], Type[Sticker]
|
||||
|
||||
|
||||
def load_attachments(raw: list[dict]) -> list[Attachment]:
|
||||
attachments = []
|
||||
for attachment in raw:
|
||||
match attachment['type']:
|
||||
case 'photo':
|
||||
attachments.append(Photo(attachment))
|
||||
case 'doc':
|
||||
attachments.append(Document(attachment))
|
||||
case 'sticker':
|
||||
attachments.append(Sticker(attachment))
|
||||
case _:
|
||||
attachments.append(VkObject(attachment))
|
||||
return attachments
|
||||
|
||||
|
||||
def dump_attachments(raw_attachments: list[Attachment]) -> str:
|
||||
return ','.join(map(repr, raw_attachments))
|
||||
|
||||
|
||||
class EventActions:
|
||||
SHOW_SNACKBAR = 'show_snackbar'
|
||||
OPEN_LINK = 'open_link'
|
||||
OPEN_APP = 'open_app'
|
||||
|
||||
|
||||
class MessageID:
|
||||
def __init__(self, body):
|
||||
if type(body) is dict:
|
||||
self.message_id = body.get('message_id', 0)
|
||||
self.cmid = body.get('cmid', 0)
|
||||
else:
|
||||
self.message_id = body
|
||||
self.cmid = body
|
||||
|
||||
|
||||
class Message(VkObject):
|
||||
__slots__ = (
|
||||
'vk', 'api', 'raw', 'id', 'conversation_message_id', 'cmid',
|
||||
'date', 'peer_id', 'from_id', 'user_id', 'chat_id', 'is_chat',
|
||||
'original_text', 'text', 'attachments', 'payload',
|
||||
'event_id', 'forwarded_messages', 'reply_message',
|
||||
'meta'
|
||||
)
|
||||
|
||||
def __init__(self, vk, api, raw):
|
||||
super().__init__(raw)
|
||||
|
||||
self.vk = vk
|
||||
self.api = api
|
||||
|
||||
if type(raw) is Message:
|
||||
self.raw = raw.raw
|
||||
else:
|
||||
self.raw = raw.get('message', raw)
|
||||
|
||||
self.id: int = self.raw.get('id', 0)
|
||||
self.conversation_message_id: int = self.raw.get('conversation_message_id', 0)
|
||||
self.cmid: int = self.conversation_message_id
|
||||
self.date: int = self.raw.get('date', 0)
|
||||
|
||||
self.peer_id: int = self.raw.get('peer_id', 0)
|
||||
self.from_id: int = self.raw.get('from_id', 0)
|
||||
self.user_id: int = self.raw.get('user_id', self.from_id)
|
||||
|
||||
self.chat_id: int = self.peer_id - 2000000000
|
||||
self.is_chat: bool = self.chat_id > 0
|
||||
|
||||
self.original_text: str = self.raw.get('text', '')
|
||||
self.text: str = self.original_text.lower()
|
||||
|
||||
self.attachments: list[Attachment] = load_attachments(self.raw.get('attachments', []))
|
||||
|
||||
raw_payload = self.raw.get('payload', '{}')
|
||||
if type(raw_payload) is dict:
|
||||
self.payload: dict = raw_payload
|
||||
else:
|
||||
self.payload: dict = json.loads(raw_payload)
|
||||
|
||||
self.event_id: str = self.raw.get('event_id', '')
|
||||
|
||||
self.forwarded_messages: list = self.raw.get('fwd_messages', [])
|
||||
self.reply_message = Message(self.vk, self.api, self.raw['reply_message']) \
|
||||
if 'reply_message' in self.raw else None
|
||||
|
||||
self.meta: dict = {}
|
||||
|
||||
async def send(self, peer_id, text: str = '', attachments: (Tuple[Attachment], Attachment, str) = '',
|
||||
keyboard: Union[VkKeyboard, dict] = None, **kwargs):
|
||||
data = kwargs.copy()
|
||||
data.update({
|
||||
'peer_id': peer_id,
|
||||
'random_id': generate_random_id()
|
||||
})
|
||||
|
||||
if text:
|
||||
data.update({'message': text})
|
||||
|
||||
if keyboard:
|
||||
if type(keyboard) is VkKeyboard:
|
||||
data.update({'keyboard': keyboard.get_keyboard()})
|
||||
else:
|
||||
data.update({'keyboard': keyboard})
|
||||
|
||||
if attachments:
|
||||
if type(attachments) is str:
|
||||
data.update({'attachment': attachments})
|
||||
elif type(attachments) in (Photo, Document):
|
||||
data.update({'attachment': str(attachments)})
|
||||
else:
|
||||
data.update({'attachment': dump_attachments(attachments)})
|
||||
|
||||
return MessageID(await self.api.messages.send(**data))
|
||||
|
||||
async def answer_event(self, event_data):
|
||||
data = {
|
||||
'peer_id': self.peer_id,
|
||||
'event_id': self.event_id,
|
||||
'user_id': self.user_id,
|
||||
'event_data': json.dumps(event_data),
|
||||
}
|
||||
|
||||
try:
|
||||
return await self.api.messages.sendMessageEventAnswer(**data)
|
||||
except VKApiException:
|
||||
...
|
||||
|
||||
async def answer_event_hide_keyboard(self, event_data):
|
||||
await self.answer_event(event_data)
|
||||
|
||||
await self.api.messages.edit(
|
||||
peer_id=self.peer_id,
|
||||
conversation_message_id=self.conversation_message_id,
|
||||
keyboard=VkKeyboard.get_empty_keyboard()
|
||||
)
|
||||
|
||||
async def answer(self, text: str = '', attachments: (list[Attachment], Attachment, str) = '', **kwargs):
|
||||
return await self.send(self.peer_id, text, attachments, **kwargs)
|
||||
|
||||
async def edit(self, text='', attachments: list[Attachment] = '', keyboard: Union[VkKeyboard, dict] = None,
|
||||
**kwargs):
|
||||
data: dict = kwargs.copy()
|
||||
data.update({
|
||||
'peer_id': self.peer_id
|
||||
})
|
||||
|
||||
if text:
|
||||
data.update({'message': text})
|
||||
|
||||
if keyboard:
|
||||
if type(keyboard) is VkKeyboard:
|
||||
data.update({'keyboard': keyboard.get_keyboard()})
|
||||
else:
|
||||
data.update({'keyboard': keyboard})
|
||||
|
||||
if attachments:
|
||||
if type(attachments) is str:
|
||||
data.update({'attachment': attachments})
|
||||
elif type(attachments) in (Photo, Document):
|
||||
data.update({'attachment': str(attachments)})
|
||||
elif type(attachments) is list:
|
||||
if type(attachments[0]) is dict:
|
||||
data.update({'attachment': dump_attachments(load_attachments(attachments))})
|
||||
else:
|
||||
data.update({'attachment': dump_attachments(attachments)})
|
||||
|
||||
if 'cmid' not in kwargs and 'conversation_message_id' not in kwargs:
|
||||
data.update({
|
||||
'conversation_message_id': self.conversation_message_id
|
||||
})
|
||||
if 'cmid' in kwargs:
|
||||
data.update({
|
||||
'conversation_message_id': kwargs['cmid']
|
||||
})
|
||||
kwargs.pop('cmid')
|
||||
|
||||
try:
|
||||
message_id = await self.api.messages.edit(**data)
|
||||
except VKApiException:
|
||||
message_id = await self.api.messages.send(**data, random_id=generate_random_id())
|
||||
|
||||
return MessageID(message_id)
|
||||
|
||||
async def get_by_cmid(self, cmid: int):
|
||||
data = {
|
||||
'peer_id': self.peer_id,
|
||||
'conversation_message_ids': [cmid]
|
||||
}
|
||||
res = await self.api.messages.getByConversationMessageId(**data)
|
||||
return Message(self.vk, self.api, res['items'][0])
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.raw)
|
249
kurocore/main/plugins.py
Normal file
249
kurocore/main/plugins.py
Normal file
@@ -0,0 +1,249 @@
|
||||
import inspect
|
||||
import re
|
||||
|
||||
|
||||
class MethodWithPriority:
|
||||
__slots__ = ('priority', 'method')
|
||||
|
||||
def __init__(self, method, priority):
|
||||
self.priority = priority
|
||||
self.method = method
|
||||
|
||||
def call(self):
|
||||
self.method()
|
||||
|
||||
|
||||
class Plugin:
|
||||
__slots__ = ('custom_checker', 'custom_processor',
|
||||
'commands', 'commands_args', 'commands_help',
|
||||
'args_help', 'before_check_methods',
|
||||
'vip_commands', 'admin_commands',
|
||||
'payloads', 'payloads_args',
|
||||
'events', 'chat_events', 'init_methods',
|
||||
'before_process_methods', 'shutdown_methods',
|
||||
'tasks')
|
||||
|
||||
def __init__(self, custom_checker=None, custom_processor=None):
|
||||
self.custom_checker = custom_checker
|
||||
self.custom_processor = custom_processor
|
||||
|
||||
self.before_check_methods: list = []
|
||||
|
||||
self.commands: dict = {}
|
||||
self.commands_args: dict = {}
|
||||
self.commands_help: dict = {}
|
||||
self.args_help: dict = {}
|
||||
|
||||
self.vip_commands: list = []
|
||||
self.admin_commands: list = []
|
||||
|
||||
self.payloads: dict = {}
|
||||
self.payloads_args: dict = {}
|
||||
|
||||
self.events: dict = {}
|
||||
self.chat_events: dict = {}
|
||||
|
||||
self.init_methods: list = []
|
||||
self.before_process_methods: list = []
|
||||
|
||||
self.shutdown_methods: list = []
|
||||
|
||||
self.tasks: list = []
|
||||
|
||||
def __repr__(self):
|
||||
return str({
|
||||
'custom_checker': self.custom_checker,
|
||||
'custom_processor': self.custom_processor,
|
||||
|
||||
'before_check_commands': self.before_check_methods,
|
||||
|
||||
'commands': list(self.commands.keys()),
|
||||
'commands_args': list(self.commands_args.keys()),
|
||||
'commands_help': list(self.commands_help.keys()),
|
||||
'args_help': list(self.args_help.keys()),
|
||||
|
||||
'vip_commands': self.vip_commands,
|
||||
'admin_commands': self.admin_commands,
|
||||
|
||||
'payloads': list(self.payloads.keys()),
|
||||
'payloads_args': list(self.payloads_args.keys()),
|
||||
|
||||
'events': list(self.events.keys()),
|
||||
'chat_events': list(self.chat_events.keys()),
|
||||
|
||||
'init_methods': self.init_methods,
|
||||
'before_process_methods': self.before_process_methods,
|
||||
|
||||
'shutdown_methods': self.shutdown_methods,
|
||||
|
||||
'tasks': self.tasks
|
||||
})
|
||||
|
||||
def init(self, priority: int = 0):
|
||||
def wrapper(f):
|
||||
self.init_methods.append(MethodWithPriority(f, priority))
|
||||
self.init_methods.sort(key=lambda method: method.priority, reverse=True)
|
||||
return f
|
||||
|
||||
return wrapper
|
||||
|
||||
def before_process(self, priority: int = 0):
|
||||
def wrapper(f):
|
||||
self.before_process_methods.append(MethodWithPriority(f, priority))
|
||||
self.before_process_methods.sort(key=lambda method: method.priority, reverse=True)
|
||||
return f
|
||||
|
||||
return wrapper
|
||||
|
||||
def on_command(self, *commands, args='', h=tuple(), is_admin: bool = False):
|
||||
def wrapper(f):
|
||||
self.commands.update(map(lambda cmd: (cmd, f), commands))
|
||||
if is_admin:
|
||||
self.admin_commands.append(*commands)
|
||||
|
||||
if args:
|
||||
self.commands_args.update(map(lambda cmd: (cmd, args), commands))
|
||||
|
||||
if h:
|
||||
self.commands_help.update({commands[0]: h[0]})
|
||||
if len(h) > 1:
|
||||
self.args_help.update({commands[0]: h[1:]})
|
||||
|
||||
return f
|
||||
|
||||
return wrapper
|
||||
|
||||
def before_check(self, f):
|
||||
self.before_check_methods.append(f)
|
||||
# print(self.before_check_methods)
|
||||
return f
|
||||
|
||||
def vip_command(self, f):
|
||||
for k in self.commands.keys():
|
||||
self.vip_commands.append(k)
|
||||
return f
|
||||
|
||||
def admin_command(self, f):
|
||||
for k in self.commands.keys():
|
||||
self.admin_commands.append(k)
|
||||
return f
|
||||
|
||||
def on_payload(self, *payloads: str, args=''):
|
||||
def wrapper(f):
|
||||
if args:
|
||||
self.payloads_args.update(map(lambda cmd: (cmd, args), payloads))
|
||||
self.payloads.update(dict(map(lambda payload: (payload, f), payloads)))
|
||||
return f
|
||||
|
||||
return wrapper
|
||||
|
||||
def on_event(self, *events):
|
||||
def wrapper(f):
|
||||
self.chat_events.update(
|
||||
map(lambda event: (event, f), filter(lambda event: event.startswith('chat'), events)))
|
||||
self.events.update(map(lambda event: (event, f),
|
||||
filter(lambda event: not event.startswith('chat'), events)))
|
||||
return f
|
||||
|
||||
return wrapper
|
||||
|
||||
def on_shutdown(self, priority: int = 0):
|
||||
def wrapper(f):
|
||||
self.shutdown_methods.append(MethodWithPriority(f, priority))
|
||||
self.shutdown_methods.sort(key=lambda method: method.priority, reverse=True)
|
||||
return f
|
||||
return wrapper
|
||||
|
||||
"""Decorator for task
|
||||
"""
|
||||
def task(self, f):
|
||||
self.tasks.append(f)
|
||||
return f
|
||||
|
||||
async def _process_command(self, command: str, msg, args):
|
||||
sig = inspect.signature(self.commands[command])
|
||||
if len(sig.parameters) == 1:
|
||||
await self.commands[command](msg)
|
||||
elif len(sig.parameters) == 2:
|
||||
await self.commands[command](msg, args)
|
||||
|
||||
async def _process_payload(self, payload: str, msg):
|
||||
await self.payloads[payload](msg)
|
||||
|
||||
async def _process_payload_with_args(self, payload: str, msg, args):
|
||||
await self.payloads[payload](msg, args)
|
||||
|
||||
def _is_vip_command(self, command: str) -> bool:
|
||||
return command in self.vip_commands
|
||||
|
||||
def _is_admin_command(self, command: str) -> bool:
|
||||
return command in self.admin_commands
|
||||
|
||||
async def _validate_command_args(self, command: str, cmd_args: tuple):
|
||||
from kurocore import MessageArgs
|
||||
commands_args = self.commands_args
|
||||
if command not in commands_args:
|
||||
return True, MessageArgs({})
|
||||
|
||||
args = commands_args[command].split()
|
||||
|
||||
if not cmd_args and not tuple(filter(lambda x: '?' not in x, args)):
|
||||
return True, MessageArgs({})
|
||||
|
||||
if len(cmd_args) < len(tuple(filter(lambda x: '?' not in x, args))):
|
||||
return False, None
|
||||
|
||||
args_map = []
|
||||
for arg in args:
|
||||
name, arg_type = arg.split(':')
|
||||
if name.endswith('?'):
|
||||
name = name[:-1]
|
||||
|
||||
arg_type = arg_type.replace('str', r'.').replace('int', r'\d')
|
||||
args_map.append((name, re.compile(arg_type)))
|
||||
|
||||
args = dict()
|
||||
|
||||
for index in range(len(cmd_args)):
|
||||
if len(args_map) == index:
|
||||
break
|
||||
name, expression = args_map[index]
|
||||
if not expression.match(cmd_args[index]):
|
||||
return False, None
|
||||
args.update({name: cmd_args[index]})
|
||||
|
||||
return True, MessageArgs(args)
|
||||
|
||||
async def _validate_payload_args(self, payload: str, msg_args: dict):
|
||||
from kurocore import MessageArgs
|
||||
payloads_args = self.payloads_args
|
||||
if payload not in payloads_args:
|
||||
return True, None
|
||||
|
||||
args = payloads_args[payload].split()
|
||||
if len(msg_args) < len(tuple(filter(lambda x: '?' not in x, args))):
|
||||
return False, None
|
||||
|
||||
args_map = []
|
||||
for arg in args:
|
||||
name, arg_type = arg.split(':')
|
||||
if name.endswith('?'):
|
||||
name = name[:-1]
|
||||
arg_type = arg_type.replace('str', r'.').replace('int', r'\d')
|
||||
args_map.append((name, re.compile(arg_type)))
|
||||
|
||||
args = dict()
|
||||
|
||||
for index in range(len(msg_args)):
|
||||
name, expression = args_map[index]
|
||||
if not expression.match(str(tuple(msg_args.values())[index])):
|
||||
return False, None
|
||||
args.update({name: tuple(msg_args.values())[index]})
|
||||
|
||||
return True, MessageArgs(args)
|
||||
|
||||
async def _process_event(self, event_type: str, event):
|
||||
await self.events[event_type](event)
|
||||
|
||||
async def _process_chat_event(self, event_type: str, event, msg):
|
||||
await self.chat_events[event_type](event, msg)
|
Reference in New Issue
Block a user