From 318d642c9077b844c79f2868e505195a5b57e910 Mon Sep 17 00:00:00 2001 From: Richard Chien Date: Wed, 15 Feb 2017 23:39:37 +0800 Subject: [PATCH] Support CoolQHttpApi message source --- README.md | 2 +- docs/Message_Sources.md | 10 +-- filters/message_logger_1000.py | 6 +- filters/split_at_xiaokai_50.py | 8 +-- msg_src_adapter.py | 2 +- msg_src_adapters/coolq_http_api.py | 107 +++++++++++++++++++++++++++++ msg_src_adapters/mojo_webqq.py | 2 +- msg_src_adapters/mojo_weixin.py | 2 +- 8 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 msg_src_adapters/coolq_http_api.py diff --git a/README.md b/README.md index 4c1e8950..c75d2588 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![QQ](https://img.shields.io/badge/qq-1647869577-orange.svg)](#) [![WeChat](https://img.shields.io/badge/wechat-cczu__xiaokai-brightgreen.svg)](#) -用 Python 编写的即时聊天平台机器人,通过适配器模式支持使用多种 bot 框架/平台作为消息源(目前支持 Mojo-Webqq、Mojo-Weixin),支持自定义插件。 +用 Python 编写的即时聊天平台机器人,通过适配器模式支持使用多种 bot 框架/平台作为消息源(目前支持 [Mojo-Webqq](https://github.com/sjdy521/Mojo-Webqq)、[Mojo-Weixin](https://github.com/sjdy521/Mojo-Weixin)、[CoolQ HTTP API](https://github.com/richardchien/coolq-http-api)),支持自定义插件。 请注意区分此程序和其它模拟登录或封装接口的聊天平台**客户端**,此程序不负责登录或维护即时聊天平台的账号的状态,而只负责收到消息之后对消息的分析、处理、回复等逻辑,本程序通过适配器来与所支持的聊天平台客户端进行通讯,通常包括上报数据的统一化、调用接口获取额外信息、发送消息等,而这些聊天平台客户端(很多时候它们的项目名称也是「某某 bot」,相当于机器人的前端)需要你自行运行。 diff --git a/docs/Message_Sources.md b/docs/Message_Sources.md index e5374203..1d211501 100644 --- a/docs/Message_Sources.md +++ b/docs/Message_Sources.md @@ -2,8 +2,8 @@ 「消息源」在文档的某些位置可能还称为「消息平台」「聊天平台客户端」「消息源客户端」「机器人前端」等。比如网上很多开源的 SmartQQ 封装或网页微信封装,我们这里就称他们为「消息源」,他们的功能通常是模拟登录账号、维护登录状态、上报接收到的消息、通过某种方式调用接口发送消息等。通过适配器,本程序可以支持多种消息源,下面是目前所支持的消息源列表: -| 消息源名称 | 官网/项目地址 | 配置文件中定义时必填项 | -| -------------------- | ---------------------------------------- | ----------------- | -| mojo_webqq | https://github.com/sjdy521/Mojo-Webqq | `api_url`:API 根地址 | -| mojo_weixin | https://github.com/sjdy521/Mojo-Weixin | `api_url`:API 根地址 | -| coolq_http_api(即将支持) | https://github.com/richardchien/coolq-http-api | | +| 消息源名称 | 官网/项目地址 | 配置文件中定义时填写项目 | +| -------------- | ---------------------------------------- | ---------------------------------------- | +| mojo_webqq | https://github.com/sjdy521/Mojo-Webqq | `api_url`:API 根地址(必填) | +| mojo_weixin | https://github.com/sjdy521/Mojo-Weixin | `api_url`:API 根地址(必填) | +| coolq_http_api | https://github.com/richardchien/coolq-http-api | `api_url`:API 根地址(必填);`token`:发送请求时的 token(设置了的情况下必填) | diff --git a/filters/message_logger_1000.py b/filters/message_logger_1000.py index 15189b65..1ae29db1 100644 --- a/filters/message_logger_1000.py +++ b/filters/message_logger_1000.py @@ -7,10 +7,10 @@ from filter import as_filter @as_filter(priority=1000) def _log_message(ctx_msg): - log = ctx_msg.get('sender') or ctx_msg.get('sender_id') or '未知用户' + log = ctx_msg.get('sender') or ctx_msg.get('sender_name') or ctx_msg.get('sender_id') or '未知用户' if ctx_msg.get('msg_type') == 'group': - log += '@' + ctx_msg.get('group') or ctx_msg.get('group_id') or '未知群组' + log += '@' + (ctx_msg.get('group') or ctx_msg.get('group_id') or '未知群组') if ctx_msg.get('msg_type') == 'discuss': - log += '@' + ctx_msg.get('discuss') or ctx_msg.get('discuss_id') or '未知讨论组' + log += '@' + (ctx_msg.get('discuss') or ctx_msg.get('discuss_id') or '未知讨论组') log += ': ' + ctx_msg.get('content', '') print(log) diff --git a/filters/split_at_xiaokai_50.py b/filters/split_at_xiaokai_50.py index 54fb55e8..44a9c0f6 100644 --- a/filters/split_at_xiaokai_50.py +++ b/filters/split_at_xiaokai_50.py @@ -15,12 +15,10 @@ def _split_at_xiaokai(ctx_msg): if ctx_msg.get('msg_type') == 'group' or ctx_msg.get('msg_type') == 'discuss': text = ctx_msg.get('text', '') if text.startswith('@'): - my_group_nick = ctx_msg.get('receiver') - if not my_group_nick: - return False + my_group_nick = ctx_msg.get('receiver') or ctx_msg.get('receiver_name') or '' at_me = '@' + my_group_nick - if not text.startswith(at_me): - user_info = get_adapter_by_ctx(ctx_msg).get_login_info(ctx_msg) + if not my_group_nick or not text.startswith(at_me): + user_info = get_adapter_by_ctx(ctx_msg).get_login_info() my_nick = user_info.get('nickname') if not my_nick: return False diff --git a/msg_src_adapter.py b/msg_src_adapter.py index 9575dc47..bbccf0e7 100644 --- a/msg_src_adapter.py +++ b/msg_src_adapter.py @@ -46,7 +46,7 @@ class Adapter(object): if 'user_tid' in target and 'sender_tid' in target: del target['user_tid'] - def get_login_info(self, ctx_msg: dict): + def get_login_info(self): return {} def is_sender_superuser(self, ctx_msg: dict): diff --git a/msg_src_adapters/coolq_http_api.py b/msg_src_adapters/coolq_http_api.py new file mode 100644 index 00000000..f6e5fc3a --- /dev/null +++ b/msg_src_adapters/coolq_http_api.py @@ -0,0 +1,107 @@ +import requests +from flask import request as flask_req + +from msg_src_adapter import Adapter, as_adapter, ConfigurationError + + +@as_adapter(via='coolq_http_api') +class CoolQHttpApiAdapter(Adapter): + def __init__(self, config: dict): + super().__init__(config) + if not config.get('api_url'): + raise ConfigurationError + self.api_url = config['api_url'] + self.token = config.get('token') + self.session = requests.Session() + if self.token: + self.session.headers['Authorization'] = 'token ' + self.token + + def unitize_context(self, ctx_msg: dict): + # Check token + if self.token: + if flask_req.headers.get('Authorization', '') != 'token ' + self.token: + return None + + new_ctx = {'raw_ctx': ctx_msg, 'post_type': ctx_msg['post_type'], 'via': ctx_msg['via'], + 'login_id': ctx_msg['login_id']} + if new_ctx['post_type'] != 'message': + return new_ctx + + new_ctx['time'] = ctx_msg['time'] + new_ctx['msg_type'] = ctx_msg['message_type'] + new_ctx['format'] = 'text' + new_ctx['content'] = ctx_msg['message'] + + login_info = self.get_login_info() + new_ctx['receiver_name'] = login_info['nickname'] + new_ctx['receiver_id'] = login_info['user_id'] + new_ctx['receiver_tid'] = login_info['user_id'] + + new_ctx['sender_id'] = str(ctx_msg.get('user_id', '')) + new_ctx['sender_tid'] = new_ctx['sender_id'] + json = self.session.get(self.api_url + '/get_stranger_info', + params={'user_id': new_ctx['sender_id']}).json() + if json and json.get('data'): + new_ctx['sender_name'] = json['data']['nickname'] + + if new_ctx['msg_type'] == 'group': + new_ctx['group_id'] = str(ctx_msg.get('group_id', '')) + new_ctx['group_tid'] = new_ctx['group_id'] + + if new_ctx['msg_type'] == 'discuss': + new_ctx['discuss_id'] = str(ctx_msg.get('discuss_id', '')) + new_ctx['discuss_tid'] = new_ctx['discuss_id'] + + import re + if re.search('\\[CQ:at,qq=%s\\]' % new_ctx['receiver_id'], new_ctx['content']): + new_ctx['content'] = re.sub('\\[CQ:at,qq=%s\\]' % new_ctx['receiver_id'], '', new_ctx['content']).lstrip() + new_ctx['is_at_me'] = True + + return new_ctx + + def get_login_info(self): + json = self.session.get(self.api_url + '/get_login_info').json() + if json and json.get('data'): + json['user_id'] = str(json['data'].get('user_id', '')) + json['user_tid'] = json['data']['user_id'] + json['nickname'] = json['data'].get('nickname', '') + return json + + def get_sender_group_role(self, ctx_msg: dict): + json = self.session.get( + self.api_url + '/get_group_member_info', + params={'group_id': ctx_msg.get('group_id'), 'user_id': ctx_msg.get('sender_id')} + ).json() + if json and json.get('data'): + return json['data']['role'] + return 'member' + + def send_private_message(self, target: dict, content: str): + params = None + if target.get('user_id'): + params = {'user_id': target.get('user_id')} + + if params: + params['message'] = content + params['is_raw'] = True + self.session.get(self.api_url + '/send_private_msg', params=params) + + def send_group_message(self, target: dict, content: str): + params = None + if target.get('group_id'): + params = {'group_id': target.get('group_id')} + + if params: + params['message'] = content + params['is_raw'] = True + self.session.get(self.api_url + '/send_group_msg', params=params) + + def send_discuss_message(self, target: dict, content: str): + params = None + if target.get('discuss_id'): + params = {'discuss_id': target.get('discuss_id')} + + if params: + params['message'] = content + params['is_raw'] = True + self.session.get(self.api_url + '/send_discuss_msg', params=params) diff --git a/msg_src_adapters/mojo_webqq.py b/msg_src_adapters/mojo_webqq.py index f7cd920f..f600aabe 100644 --- a/msg_src_adapters/mojo_webqq.py +++ b/msg_src_adapters/mojo_webqq.py @@ -49,7 +49,7 @@ class MojoWebqqAdapter(Adapter): return new_ctx - def get_login_info(self, ctx_msg: dict): + def get_login_info(self): json = requests.get(self.api_url + '/get_user_info').json() if json: json['user_tid'] = json.get('id') diff --git a/msg_src_adapters/mojo_weixin.py b/msg_src_adapters/mojo_weixin.py index b849dbdd..8e78429b 100644 --- a/msg_src_adapters/mojo_weixin.py +++ b/msg_src_adapters/mojo_weixin.py @@ -52,7 +52,7 @@ class MojoWeixinAdapter(Adapter): return new_ctx - def get_login_info(self, ctx_msg: dict): + def get_login_info(self): json = requests.get(self.api_url + '/get_user_info').json() if json: json['user_tid'] = json.get('id')