@wentorai/research-plugins 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "research-plugins",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "name": "Research Plugins",
5
5
  "description": "487 academic research skills, 13 agent tools, 150 MCP configs & 6 curated lists for Research-Claw",
6
6
  "skills": ["./skills"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wentorai/research-plugins",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "description": "487 academic research skills, 150 MCP configs, 13 agent tools, and 6 curated lists for Research-Claw and AI agents",
6
6
  "keywords": [
@@ -0,0 +1,227 @@
1
+ ---
2
+ name: qq-connect
3
+ description: "Connect QQ messaging to Research-Claw via QQ Bot API"
4
+ metadata:
5
+ openclaw:
6
+ emoji: "💬"
7
+ category: "tools"
8
+ subcategory: "document"
9
+ keywords: ["QQ", "QQ Bot", "messaging", "Tencent", "channel"]
10
+ source: "https://q.qq.com/"
11
+ ---
12
+
13
+ # QQ 连接指南 — Research-Claw × QQ Bot
14
+
15
+ 通过 QQ Bot 官方 API 将 QQ 消息通道连接到 Research-Claw,实现在 QQ 中直接与科研助手交流。
16
+
17
+ ## 架构概述
18
+
19
+ ```
20
+ QQ 用户 ⟶ QQ 开放平台 (WebSocket) ⟶ openclaw-qqbot 插件 ⟶ OpenClaw Gateway ⟶ Research-Claw Agent
21
+
22
+ research-claw-core (文献/任务/工作区/雷达)
23
+ ```
24
+
25
+ - **openclaw-qqbot** 是一个 OpenClaw channel plugin,使用腾讯 QQ Bot API v2(官方接口)
26
+ - 它与 research-claw-core 插件共存于同一 gateway 进程,共享 agent 上下文
27
+ - 安装后,QQ 用户的消息等同于 dashboard 中的对话 — agent 的全部 27 个工具均可用
28
+ - 附带 2 个技能:`qqbot-cron`(定时提醒)和 `qqbot-media`(图片/语音/视频/文件收发)
29
+
30
+ ## 前提条件
31
+
32
+ 1. **QQ 开放平台账号** — 前往 https://q.qq.com/ 注册开发者账号
33
+ 2. **创建机器人应用** — 在开放平台创建一个机器人,获取:
34
+ - **AppID**(应用 ID)
35
+ - **AppSecret**(应用密钥)
36
+ 3. **配置机器人权限**(在 QQ 开放平台控制台):
37
+ - 消息接收:开启「群聊」和/或「私聊」权限
38
+ - 推荐开启:`PUBLIC_GUILD_MESSAGES` + `GROUP_AND_C2C`(频道+群+私聊)
39
+ 4. **Research-Claw 已正常运行** — gateway 可访问 (`ws://127.0.0.1:18789`)
40
+
41
+ ## 安装步骤(Agent 可直接执行)
42
+
43
+ ### 步骤 1:安装 QQ Bot 插件
44
+
45
+ ```bash
46
+ openclaw plugins install @tencent-connect/openclaw-qqbot@latest
47
+ ```
48
+
49
+ 如果网络不通,可使用代理:
50
+ ```bash
51
+ HTTPS_PROXY=http://127.0.0.1:7890 openclaw plugins install @tencent-connect/openclaw-qqbot@latest
52
+ ```
53
+
54
+ ### 步骤 2:配置凭证
55
+
56
+ **方法 A — 使用 CLI(推荐):**
57
+
58
+ > 注意:`openclaw channels add` 仅支持内置通道(Telegram、Discord 等)。
59
+ > qqbot 作为自定义插件,需通过 `config set` 配置。
60
+
61
+ ```bash
62
+ openclaw config set channels.qqbot.appId "<APP_ID>"
63
+ openclaw config set channels.qqbot.clientSecret "<APP_SECRET>"
64
+ openclaw config set channels.qqbot.enabled true
65
+ ```
66
+
67
+ **方法 B — 直接编辑配置文件:**
68
+
69
+ 在 `~/.openclaw/openclaw.json` 中添加:
70
+
71
+ ```jsonc
72
+ {
73
+ "channels": {
74
+ "qqbot": {
75
+ "appId": "<APP_ID>",
76
+ "clientSecret": "<APP_SECRET>",
77
+ "enabled": true
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### 步骤 3:重启 Gateway
84
+
85
+ ```bash
86
+ openclaw gateway restart
87
+ ```
88
+
89
+ ### 步骤 4:验证连接
90
+
91
+ 重启后,在 QQ 中向你的机器人发送一条消息(如「你好」)。如果收到回复,连接成功。
92
+
93
+ 查看 gateway 日志确认:
94
+ ```bash
95
+ openclaw logs | grep -i qqbot
96
+ ```
97
+
98
+ 正常应看到类似:
99
+ ```
100
+ [qqbot] Connected to QQ gateway
101
+ [qqbot] Session ready: sessionId=xxx
102
+ ```
103
+
104
+ ## 可选配置
105
+
106
+ ```jsonc
107
+ {
108
+ "channels": {
109
+ "qqbot": {
110
+ "appId": "<APP_ID>",
111
+ "clientSecret": "<APP_SECRET>",
112
+ "enabled": true,
113
+
114
+ // 机器人显示名称(可选)
115
+ "name": "科研龙虾",
116
+
117
+ // 系统提示(可选,追加到 agent 默认提示之后)
118
+ "systemPrompt": "你现在通过 QQ 与用户交流,回复请简洁。",
119
+
120
+ // 私聊策略:open=开放 | pairing=需要用户先发消息 | allowlist=白名单
121
+ "dmPolicy": "open",
122
+
123
+ // 语音转文字(需要 STT 模型配置)
124
+ "stt": {
125
+ "provider": "openai",
126
+ "model": "whisper-1",
127
+ "enabled": true
128
+ },
129
+
130
+ // 文字转语音
131
+ "tts": {
132
+ "provider": "openai",
133
+ "model": "tts-1",
134
+ "voice": "nova",
135
+ "enabled": false
136
+ },
137
+
138
+ // 多账号(可选)
139
+ "accounts": {
140
+ "bot2": {
141
+ "appId": "<BOT2_APP_ID>",
142
+ "clientSecret": "<BOT2_APP_SECRET>",
143
+ "enabled": true
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## Agent 自动化设置流程
152
+
153
+ 当用户要求连接 QQ 时,按以下步骤操作:
154
+
155
+ 1. **询问凭证**:
156
+ > 要连接 QQ,需要你在 QQ 开放平台(https://q.qq.com/)创建机器人后获取的 AppID 和 AppSecret。
157
+ > 你已经有了吗?如果没有,我可以指导你创建。
158
+
159
+ 2. **获取 AppID 和 AppSecret 后,执行安装**:
160
+ ```bash
161
+ openclaw plugins install @tencent-connect/openclaw-qqbot@latest
162
+ openclaw config set channels.qqbot.appId "<用户提供的AppID>"
163
+ openclaw config set channels.qqbot.clientSecret "<用户提供的Secret>"
164
+ openclaw config set channels.qqbot.enabled true
165
+ openclaw gateway restart
166
+ ```
167
+
168
+ 3. **引导测试**:
169
+ > 安装完成!请在 QQ 中找到你的机器人,发送一条消息试试。
170
+
171
+ 4. **记录到 MEMORY.md**:
172
+ ```markdown
173
+ ### Environment
174
+ - QQ Bot: connected (AppID: <前4位>****)
175
+ ```
176
+
177
+ ## QQ Bot 注册指引(用于引导无账号的用户)
178
+
179
+ 1. 访问 https://q.qq.com/,使用 QQ 账号登录
180
+ 2. 点击「创建机器人」(或「应用管理」→「创建」)
181
+ 3. 填写机器人基本信息(名称、头像、简介)
182
+ 4. 在「开发设置」中获取 **AppID** 和 **AppSecret**
183
+ 5. 在「消息」→「消息订阅」中开启消息接收权限:
184
+ - 群消息(推荐)
185
+ - 私聊消息(推荐)
186
+ - 频道消息(可选)
187
+ 6. 审核通过后即可使用
188
+
189
+ > 注意:QQ 机器人需要通过腾讯审核。测试阶段可使用沙箱环境。
190
+
191
+ ## 消息能力
192
+
193
+ 连接 QQ 后,agent 可以:
194
+
195
+ | 能力 | 说明 |
196
+ |------|------|
197
+ | 文本对话 | QQ 消息 ↔ agent 双向交流 |
198
+ | 图片收发 | 用户发图自动下载,agent 用 `<qqimg>` 标签发图 |
199
+ | 语音处理 | STT 转文字(需配置),TTS 发语音 |
200
+ | 视频/文件 | `<qqvideo>` / `<qqfile>` 标签发送 |
201
+ | 定时提醒 | cron 工具创建一次性/周期提醒 |
202
+ | 群聊 | 支持群组中 @机器人 交流 |
203
+ | 私聊 | 支持一对一私聊 |
204
+
205
+ ## 常见问题
206
+
207
+ ### 连接超时
208
+ - 检查网络:QQ Bot API 需要能访问 `api.sgroup.qq.com` 和 `bots.qq.com`
209
+ - 如果在国内需要代理,在配置中不需要设置代理(QQ API 是国内服务)
210
+ - 如果在海外,可能需要配置代理
211
+
212
+ ### 权限不足(Intent 降级)
213
+ - 插件会自动尝试 3 个权限级别:全功能 → 群+频道 → 仅频道
214
+ - 如果只能用基础功能,去 QQ 开放平台检查机器人权限配置
215
+
216
+ ### 消息发不出去
217
+ - 确认 AppID 和 AppSecret 正确
218
+ - 确认机器人已通过审核(或在沙箱中测试)
219
+ - 检查 gateway 日志:`openclaw logs | grep qqbot`
220
+
221
+ ### 语音无法转文字
222
+ - 需要配置 STT 提供商(如 OpenAI Whisper)
223
+ - 在 `channels.qqbot.stt` 中设置 provider 和 model
224
+
225
+ ### 重启后断开
226
+ - 5 分钟内重启可自动恢复 session(session persistence)
227
+ - 超过 5 分钟会重新建立连接,不影响功能
@@ -1,13 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
3
+ import { toolResult } from "./util.js";
3
4
 
4
5
  const BASE = "https://export.arxiv.org/api/query";
5
6
 
6
- function toolResult(data: unknown) {
7
- const text = JSON.stringify(data, null, 2);
8
- return { content: [{ type: "text" as const, text }] };
9
- }
10
-
11
7
  function parseArxivXml(xml: string) {
12
8
  const entries: Record<string, unknown>[] = [];
13
9
  const entryBlocks = xml.split("<entry>").slice(1);
@@ -1,13 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
3
+ import { toolResult } from "./util.js";
3
4
 
4
5
  const BASE = "https://api.crossref.org";
5
6
 
6
- function toolResult(data: unknown) {
7
- const text = JSON.stringify(data, null, 2);
8
- return { content: [{ type: "text" as const, text }] };
9
- }
10
-
11
7
  export function createCrossRefTools(
12
8
  _ctx: OpenClawPluginToolContext,
13
9
  _api: OpenClawPluginApi,
@@ -1,13 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
3
+ import { toolResult } from "./util.js";
3
4
 
4
5
  const BASE = "https://api.openalex.org";
5
6
 
6
- function toolResult(data: unknown) {
7
- const text = JSON.stringify(data, null, 2);
8
- return { content: [{ type: "text" as const, text }] };
9
- }
10
-
11
7
  export function createOpenAlexTools(
12
8
  _ctx: OpenClawPluginToolContext,
13
9
  _api: OpenClawPluginApi,
@@ -1,13 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
3
+ import { toolResult } from "./util.js";
3
4
 
4
5
  const EUTILS = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";
5
6
 
6
- function toolResult(data: unknown) {
7
- const text = JSON.stringify(data, null, 2);
8
- return { content: [{ type: "text" as const, text }] };
9
- }
10
-
11
7
  export function createPubMedTools(
12
8
  _ctx: OpenClawPluginToolContext,
13
9
  _api: OpenClawPluginApi,
@@ -1,13 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
3
+ import { toolResult } from "./util.js";
3
4
 
4
5
  const BASE = "https://api.semanticscholar.org/graph/v1";
5
6
 
6
- function toolResult(data: unknown) {
7
- const text = JSON.stringify(data, null, 2);
8
- return { content: [{ type: "text" as const, text }] };
9
- }
10
-
11
7
  export function createSemanticScholarTools(
12
8
  ctx: OpenClawPluginToolContext,
13
9
  api: OpenClawPluginApi,
@@ -1,13 +1,9 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk";
3
+ import { toolResult } from "./util.js";
3
4
 
4
5
  const BASE = "https://api.unpaywall.org/v2";
5
6
 
6
- function toolResult(data: unknown) {
7
- const text = JSON.stringify(data, null, 2);
8
- return { content: [{ type: "text" as const, text }] };
9
- }
10
-
11
7
  export function createUnpaywallTools(
12
8
  _ctx: OpenClawPluginToolContext,
13
9
  _api: OpenClawPluginApi,
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Wrap raw data into the OpenClaw AgentTool return format.
3
+ *
4
+ * Expected shape: { content: [{ type: "text", text: string }], details: unknown }
5
+ * - content → displayed to the user / consumed by the model as text
6
+ * - details → structured data for programmatic access by downstream tools
7
+ */
8
+ export function toolResult(data: unknown) {
9
+ const text = JSON.stringify(data, null, 2);
10
+ return { content: [{ type: "text" as const, text }], details: data };
11
+ }