基本完成QQ指令部分
This commit is contained in:
parent
dfd095e783
commit
568cd50c3c
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@
|
||||
|
||||
/config.toml
|
||||
/temp.json
|
||||
|
||||
/db.*
|
||||
|
2639
cmdparser.py
Normal file
2639
cmdparser.py
Normal file
File diff suppressed because it is too large
Load Diff
41
cmds.py
Normal file
41
cmds.py
Normal file
@ -0,0 +1,41 @@
|
||||
import secrets
|
||||
|
||||
from models import User
|
||||
from sio_model import SioDecorator, Message, SioRequest
|
||||
|
||||
|
||||
def cmds(app, data):
|
||||
# 此处会检查注册的指令是否重复
|
||||
sio_decorator = SioDecorator(app, data)
|
||||
|
||||
@sio_decorator.cmd('ping')
|
||||
async def ping(sqt: SioRequest):
|
||||
msg = Message(content='pong', room_id=sqt.room_id)
|
||||
await sqt.app.ctx.sio.emit('sendMessage', msg.to_json())
|
||||
|
||||
@sio_decorator.cmd('gitea')
|
||||
async def gitea(sqt: SioRequest):
|
||||
parser = sqt.parser
|
||||
parser.add_argument('-vd', '--validate', help='绑定QQ与gitea',
|
||||
action="store_true")
|
||||
args = parser.parse_args(sqt.args)
|
||||
if args.validate:
|
||||
state = secrets.token_urlsafe(16)
|
||||
user = await User.filter(id=sqt.sender_id).get_or_none()
|
||||
if user:
|
||||
user.name = sqt.sender_name
|
||||
user.state = state
|
||||
await user.save()
|
||||
else:
|
||||
user = User(id=sqt.sender_id, name=sqt.sender_name, state=state)
|
||||
await user.save()
|
||||
url = (f'{app.ctx.sio_config.validate_host}/login/oauth/authorize?'
|
||||
f'client_id={app.ctx.sio_config.client_id}&'
|
||||
f'redirect_uri={app.ctx.sio_config.localhost}/redirect&'
|
||||
f'response_type=code&state={state}')
|
||||
msg = Message(content=f'click: \n{url}', room_id=sqt.room_id)
|
||||
await sqt.app.ctx.sio.emit('sendMessage', msg.to_json())
|
||||
if len(sqt.args) == 0:
|
||||
parser.parse_args(["-h"])
|
||||
|
||||
return sio_decorator
|
15
command.py
15
command.py
@ -1,15 +0,0 @@
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from model import Message
|
||||
|
||||
|
||||
def command_convert(data: Dict[str, Any]) -> Optional[Message]:
|
||||
match (data.get('message').get('content')):
|
||||
case '/bot': # 测试存活
|
||||
return Message(content='icalingua bot test', room_id=data['roomId'])
|
||||
|
||||
case ['/gitea validate', '-v']:
|
||||
return Message()
|
||||
|
||||
case _:
|
||||
return None
|
@ -3,14 +3,14 @@ from tortoise.models import Model
|
||||
|
||||
|
||||
class User(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
id = fields.BigIntField(pk=True, autoincrement=False)
|
||||
name = fields.CharField(max_length=60)
|
||||
gusers = fields.ForeignKeyField('models.GiteaUser')
|
||||
state = fields.CharField(max_length=20)
|
||||
state = fields.CharField(max_length=60)
|
||||
|
||||
|
||||
class GiteaUser(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
id = fields.BigIntField(pk=True, autoincrement=False)
|
||||
qid = fields.ForeignKeyField('models.GiteaUser')
|
||||
name = fields.CharField(max_length=20)
|
||||
ac_tk = fields.CharField(max_length=256)
|
||||
rfs_tk = fields.CharField(max_length=256)
|
||||
|
13
orm.py
13
orm.py
@ -1,13 +0,0 @@
|
||||
from tortoise import Model, fields
|
||||
|
||||
|
||||
class User(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
name = fields.CharField(max_length=60)
|
||||
state = fields.CharField(max_length=20)
|
||||
|
||||
|
||||
class UserGroup(Model):
|
||||
id = fields.IntField(pk=True)
|
||||
name = fields.CharField(max_length=60)
|
||||
user = fields.ForeignKeyField('orm.User')
|
37
server.py
37
server.py
@ -7,9 +7,10 @@ from nacl.signing import SigningKey
|
||||
from sanic import Sanic, Request
|
||||
from sanic.log import logger, Colors
|
||||
from sanic.response import text
|
||||
from socketio import AsyncClient
|
||||
from tortoise.contrib.sanic import register_tortoise
|
||||
|
||||
from command import command_convert
|
||||
import cmds
|
||||
from gitea_model import WebHookIssueComment, WebHookIssue, GiteaEvent
|
||||
from sio_model import Ctx, SioConfig, Message
|
||||
from unit import sio_log_format, int2str, cas_log_fmt
|
||||
@ -37,7 +38,6 @@ async def setup_before_start(_app):
|
||||
# 使用casbin策略管理
|
||||
adapter = TortoiseAdapter()
|
||||
e = casbin.AsyncEnforcer('./casbin_data/model.conf', adapter)
|
||||
# e = casbin.Enforcer('./casbin_data/model.conf', './casbin_data/casbin.csv')
|
||||
_app.ctx.e = e
|
||||
|
||||
t1 = await _app.ctx.e.add_policy('admin', '*')
|
||||
@ -53,14 +53,13 @@ async def setup_before_start(_app):
|
||||
for u in list(rm_user):
|
||||
if await _app.ctx.e.delete_user(u):
|
||||
logger.debug(f'Delete {Colors.PURPLE}{u}{Colors.YELLOW} for group admin')
|
||||
|
||||
await _app.ctx.e.save_policy()
|
||||
|
||||
# 初始化sio
|
||||
# _app.ctx.sio = AsyncClient()
|
||||
# start_sio_listener()
|
||||
#
|
||||
# await _app.ctx.sio.connect(_app.ctx.sio_config.host)
|
||||
_app.ctx.sio = AsyncClient()
|
||||
start_sio_listener()
|
||||
|
||||
await _app.ctx.sio.connect(_app.ctx.sio_config.host)
|
||||
|
||||
|
||||
@app.post('/receive')
|
||||
@ -184,14 +183,24 @@ def start_sio_listener():
|
||||
|
||||
@app.ctx.sio.on('addMessage')
|
||||
async def add_message(data: Dict[str, Any]):
|
||||
logger.debug(sio_log_format('add_message:', data))
|
||||
sio_decorator = cmds.cmds(app, data)
|
||||
await sio_decorator.route2cmd_and_run()
|
||||
|
||||
sender_id = data['message']['senderId']
|
||||
|
||||
if sender_id != app.ctx.sio_config.self_id:
|
||||
msg = await command_convert(data, app)
|
||||
if msg is not None:
|
||||
await app.ctx.sio.emit('sendMessage', msg.to_json())
|
||||
# @app.ctx.sio.on('addMessage')
|
||||
# @SioDecorator.cmd('gitea', app)
|
||||
# async def gitea(sqt: SioRequest):
|
||||
# parser = sqt.parser
|
||||
# parser.add_argument('-vd', help='绑定QQ与gitea')
|
||||
# args = parser.parse_args(sqt.args)
|
||||
# if args.vd:
|
||||
# state = secrets.token_urlsafe(16)
|
||||
# user = User(id=sqt.sender_id, name=sqt.sender_name, state=state)
|
||||
# await user.save()
|
||||
# url = (f'{app.ctx.sio_config.validate_host}/login/oauth/authorize?'
|
||||
# f'client_id={app.ctx.sio_config.client_id}&'
|
||||
# f'redirect_uri={app.ctx.sio_config.localhost}/redirect&'
|
||||
# f'response_type=code&state={state}')
|
||||
# return Message(content=f'click: \n{url}')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
93
sio_model.py
93
sio_model.py
@ -1,17 +1,24 @@
|
||||
import shlex
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, List, Union, Literal
|
||||
from functools import wraps
|
||||
from typing import Optional, List, Union, Literal, Dict, Any
|
||||
|
||||
from lib_not_dr.types import Options
|
||||
from pydantic import BaseModel, Field
|
||||
from casbin import Enforcer
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
from sanic import Sanic
|
||||
from sanic.log import logger
|
||||
from socketio import AsyncClient
|
||||
|
||||
from cmdparser import ArgumentParser, PrintMessage
|
||||
from unit import sio_log_format
|
||||
|
||||
class AtElement(Options):
|
||||
|
||||
class AtElement(BaseModel):
|
||||
text: str
|
||||
id: Union[int, Literal['all']] = 'all'
|
||||
|
||||
|
||||
class ReplyMessage(Options):
|
||||
class ReplyMessage(BaseModel):
|
||||
id: str
|
||||
username: str = ''
|
||||
content: str = ''
|
||||
@ -26,7 +33,7 @@ class ReplyMessage(Options):
|
||||
}
|
||||
|
||||
|
||||
class Message(Options):
|
||||
class Message(BaseModel):
|
||||
content: str
|
||||
room_id: Optional[int] = None
|
||||
room: Optional[int] = None # room id 和 room 二选一 ( 实际上直接填 room id 就行了 )
|
||||
@ -69,7 +76,81 @@ class SioConfig(BaseModel):
|
||||
db_url: str
|
||||
|
||||
|
||||
class CmdExists(Exception):
|
||||
...
|
||||
|
||||
|
||||
class SioDecorator:
|
||||
def __init__(self, app: Sanic, data: Dict[str, Any]):
|
||||
logger.debug(sio_log_format('add_message:', data))
|
||||
self._content = data['message']['content']
|
||||
self._cmd = shlex.split(self._content)
|
||||
self.app = app
|
||||
self.data = data
|
||||
self.cmds = {}
|
||||
|
||||
def cmd(self, cmd_key: str):
|
||||
def decorator(func):
|
||||
self._cmds_append(cmd_key, func)
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(*args, **kwargs): # args 无参数名的 kw 有参数名的
|
||||
...
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
def _cmds_append(self, cmd, func):
|
||||
if cmd in self.cmds.keys():
|
||||
raise CmdExists(
|
||||
f"Command already registered: /{cmd}"
|
||||
)
|
||||
else:
|
||||
self.cmds[f'/{cmd}'] = func
|
||||
|
||||
async def route2cmd_and_run(self):
|
||||
func = self.cmds.get(self._cmd[0])
|
||||
if func:
|
||||
sender_id = self.data['message']['senderId']
|
||||
sender_name = self.data['message']['username']
|
||||
room_id = self.data['roomId']
|
||||
|
||||
if sender_id != self.app.ctx.sio_config.self_id:
|
||||
parser = ArgumentParser(self._cmd[0])
|
||||
|
||||
sqt = SioRequest(app=self.app,
|
||||
parser=parser,
|
||||
args=self._cmd[1:],
|
||||
key=self._cmd[0],
|
||||
sender_id=sender_id,
|
||||
sender_name=sender_name,
|
||||
content=self._content,
|
||||
room_id=room_id,
|
||||
data=self.data)
|
||||
try:
|
||||
await func(sqt)
|
||||
except PrintMessage as e:
|
||||
msg = Message(content=str(e), room_id=room_id)
|
||||
await self.app.ctx.sio.emit('sendMessage', msg.to_json())
|
||||
|
||||
|
||||
@dataclass
|
||||
class Ctx:
|
||||
sio: AsyncClient
|
||||
sio_config: SioConfig
|
||||
e: Enforcer
|
||||
sio_decorator: SioDecorator
|
||||
|
||||
|
||||
@dataclass
|
||||
class SioRequest:
|
||||
app: Sanic = None
|
||||
parser: Optional[ArgumentParser] = None
|
||||
args: Optional[list] = None
|
||||
key: Optional[str] = None
|
||||
data: Optional[Dict[str, Any]] = None
|
||||
sender_id: Optional[int] = None
|
||||
sender_name: str = ''
|
||||
content: str = ''
|
||||
room_id: Optional[int] = None
|
||||
|
Loading…
Reference in New Issue
Block a user