Initial commit
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import deque
|
||||
from typing import Any
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from opc.channels.provider_base import OptionalDependencyChannel
|
||||
from opc.core.models import SystemMessage
|
||||
|
||||
|
||||
class QQChannel(OptionalDependencyChannel):
|
||||
name = "qq"
|
||||
required_package = "botpy"
|
||||
delivery_mode = "socket"
|
||||
|
||||
def __init__(self, config: Any, bus: Any):
|
||||
super().__init__(config, bus)
|
||||
self._client: Any = None
|
||||
self._processed_ids: deque[str] = deque(maxlen=1000)
|
||||
self._msg_seq: int = 1
|
||||
|
||||
def get_required_config_fields(self) -> list[str]:
|
||||
return ["app_id", "secret"]
|
||||
|
||||
def normalize_event(self, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
author = payload.get("author", {})
|
||||
sender = str(author.get("id", payload.get("openid", "")))
|
||||
return {
|
||||
"sender_id": sender,
|
||||
"chat_id": str(payload.get("group_openid", payload.get("channel_id", sender))),
|
||||
"content": str(payload.get("content", "") or ""),
|
||||
"thread_id": "",
|
||||
"reply_to": str(payload.get("id", "") or ""),
|
||||
"metadata": {"message_id": str(payload.get("id", "") or "")},
|
||||
}
|
||||
|
||||
async def start(self) -> None:
|
||||
await super().start()
|
||||
import botpy
|
||||
|
||||
self._client = self._build_client(botpy)
|
||||
await self._client.start(appid=self.config.app_id, secret=self.config.secret)
|
||||
|
||||
def _build_client(self, botpy_module: Any) -> Any:
|
||||
intents = botpy_module.Intents(public_messages=True, direct_message=True)
|
||||
channel = self
|
||||
|
||||
class OPCQQClient(botpy_module.Client):
|
||||
def __init__(self) -> None:
|
||||
super().__init__(intents=intents, ext_handlers=False)
|
||||
|
||||
async def on_ready(self) -> None:
|
||||
logger.info("qq bot ready")
|
||||
|
||||
async def on_c2c_message_create(self, message: Any) -> None:
|
||||
await channel._on_message(message)
|
||||
|
||||
async def on_direct_message_create(self, message: Any) -> None:
|
||||
await channel._on_message(message)
|
||||
|
||||
return OPCQQClient()
|
||||
|
||||
async def stop(self) -> None:
|
||||
if self._client is not None:
|
||||
try:
|
||||
await self._client.close()
|
||||
except Exception:
|
||||
logger.exception("qq client close failed")
|
||||
self._client = None
|
||||
await super().stop()
|
||||
|
||||
async def _on_message(self, message: Any) -> None:
|
||||
if getattr(message, "id", "") in self._processed_ids:
|
||||
return
|
||||
self._processed_ids.append(getattr(message, "id", ""))
|
||||
author = getattr(message, "author", None)
|
||||
payload = {
|
||||
"id": str(getattr(message, "id", "") or ""),
|
||||
"author": {
|
||||
"id": str(getattr(author, "id", "") or getattr(author, "user_openid", "") or ""),
|
||||
},
|
||||
"content": str(getattr(message, "content", "") or ""),
|
||||
}
|
||||
await self.publish_normalized(self.normalize_event(payload))
|
||||
|
||||
async def send(self, message: SystemMessage) -> None:
|
||||
await super().send(message)
|
||||
if self._client is None:
|
||||
logger.warning("qq client not connected")
|
||||
return
|
||||
metadata = dict(message.metadata or {})
|
||||
chat_id = str(metadata.get("chat_id") or message.session_id or "")
|
||||
self._msg_seq += 1
|
||||
await self._client.api.post_c2c_message(
|
||||
openid=chat_id,
|
||||
msg_type=0,
|
||||
content=message.content,
|
||||
msg_id=str(metadata.get("reply_to", "") or metadata.get("message_id", "") or ""),
|
||||
msg_seq=self._msg_seq,
|
||||
)
|
||||
Reference in New Issue
Block a user