@dcrays/dcgchat-test 0.1.9 → 0.1.10

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.
package/src/channel.ts CHANGED
@@ -1,192 +1,192 @@
1
- import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
2
- import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
3
- import type { ResolvedDcgchatAccount, DcgchatConfig } from "./types.js";
4
- import { logDcgchat } from "./log.js";
5
- import { getWsConnection } from "./connection.js";
6
- import { ossUpload } from "./oss.js";
7
- import { getMsgParams } from "./tool.js";
8
-
9
- export function resolveAccount(cfg: OpenClawConfig, accountId?: string | null): ResolvedDcgchatAccount {
10
- const id = accountId ?? DEFAULT_ACCOUNT_ID;
11
- const raw = (cfg.channels?.["dcgchat"] as DcgchatConfig | undefined) ?? {};
12
- return {
13
- accountId: id,
14
- enabled: raw.enabled !== false,
15
- configured: Boolean(raw.wsUrl),
16
- wsUrl: raw.wsUrl ?? "",
17
- botToken: raw.botToken ?? "",
18
- userId: raw.userId ?? "",
19
- domainId: raw.domainId ?? "",
20
- appId: raw.appId ?? "",
21
- };
22
- }
23
-
24
- export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
25
- id: "dcgchat",
26
- meta: {
27
- id: "dcgchat",
28
- label: "DCG Chat",
29
- selectionLabel: "DCG Chat",
30
- docsPath: "/channels/dcgchat",
31
- docsLabel: "dcgchat",
32
- blurb: "连接 OpenClaw 与 DCG Chat 产品",
33
- order: 80,
34
- },
35
- capabilities: {
36
- chatTypes: ["direct"],
37
- polls: false,
38
- threads: true,
39
- media: true,
40
- nativeCommands: true,
41
- reactions: true,
42
- edit: false,
43
- reply: true,
44
- effects: true,
45
- // blockStreaming: true,
46
- },
47
- reload: { configPrefixes: ["channels.dcgchat"] },
48
- configSchema: {
49
- schema: {
50
- type: "object",
51
- additionalProperties: false,
52
- properties: {
53
- enabled: { type: "boolean" },
54
- wsUrl: { type: "string" },
55
- botToken: { type: "string" },
56
- userId: { type: "string" },
57
- appId: { type: "string" },
58
- domainId: { type: "string" },
59
- capabilities: { type: "array", items: { type: "string" } },
60
- },
61
- },
62
- },
63
- config: {
64
- listAccountIds: () => [DEFAULT_ACCOUNT_ID],
65
- resolveAccount: (cfg, accountId) => resolveAccount(cfg, accountId),
66
- defaultAccountId: () => DEFAULT_ACCOUNT_ID,
67
- setAccountEnabled: ({ cfg, enabled }) => ({
68
- ...cfg,
69
- channels: {
70
- ...cfg.channels,
71
- "dcgchat": {
72
- ...(cfg.channels?.["dcgchat"] as Record<string, unknown> | undefined),
73
- enabled,
74
- },
75
- },
76
- }),
77
- isConfigured: (account) => account.configured,
78
- describeAccount: (account) => ({
79
- accountId: account.accountId,
80
- enabled: account.enabled,
81
- configured: account.configured,
82
- wsUrl: account.wsUrl,
83
- botToken: account.botToken ? "***" : "",
84
- userId: account.userId,
85
- domainId: account.domainId,
86
- appId: account.appId,
87
- }),
88
- },
89
- messaging: {
90
- normalizeTarget: (raw) => raw?.trim() || undefined,
91
- targetResolver: {
92
- looksLikeId: (raw) => Boolean(raw?.trim()),
93
- hint: "userId",
94
- },
95
- },
96
- outbound: {
97
- deliveryMode: "direct",
98
- // textChunkLimit: 25,
99
- textChunkLimit: 4000,
100
- sendText: async (ctx) => {
101
- const target = ctx.to || "(implicit)";
102
- const ws = getWsConnection()
103
- const params = getMsgParams();
104
- if (ws?.readyState === WebSocket.OPEN) {
105
- const {botToken} = resolveAccount(ctx.cfg, ctx.accountId);
106
- const content = {
107
- messageType: "openclaw_bot_chat",
108
- _userId: target,
109
- source: "client",
110
- content: {
111
- bot_token: botToken,
112
- response: ctx.text,
113
- session_id: params.sessionId || Date.now().toString(),
114
- message_id: params.messageId ||Date.now().toString(),
115
- },
116
- };
117
- ws.send(JSON.stringify(content));
118
- logDcgchat.info(`dcgchat[${ctx.accountId}]: sendText to ${target}, ${JSON.stringify(content)}`);
119
- } else {
120
- logDcgchat.warn(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> ${ws?.readyState}: ${ctx.text}`);
121
- }
122
- return {
123
- channel: "dcgchat",
124
- messageId: `dcg-${Date.now()}`,
125
- chatId: target,
126
- };
127
- },
128
- sendMedia: async (ctx) => {
129
- const target = ctx.to || "(implicit)";
130
- const ws = getWsConnection()
131
- const params = getMsgParams();
132
- if (ws?.readyState === WebSocket.OPEN) {
133
- const {botToken} = resolveAccount(ctx.cfg, ctx.accountId);
134
- try {
135
- const url = ctx.mediaUrl ? await ossUpload(ctx.mediaUrl, botToken) : '';
136
- const fileName = ctx.mediaUrl?.split(/[\\/]/).pop() || ''
137
- const content = {
138
- messageType: "openclaw_bot_chat",
139
- _userId: target,
140
- source: "client",
141
- content: {
142
- bot_token: botToken,
143
- response: ctx.text + '\n' + `[${fileName}](${url})`,
144
- session_id: params.sessionId || Date.now().toString(),
145
- message_id: params.messageId ||Date.now().toString(),
146
- },
147
- };
148
- ws.send(JSON.stringify(content));
149
- logDcgchat.info(`dcgchat[${ctx.accountId}]: sendMedia to ${target}, ${JSON.stringify(content)}`);
150
- } catch (error) {
151
- const content = {
152
- messageType: "openclaw_bot_chat",
153
- _userId: target,
154
- source: "client",
155
- content: {
156
- bot_token: botToken,
157
- response: ctx.text + '\n' + ctx.mediaUrl,
158
- session_id: params.sessionId || Date.now().toString(),
159
- message_id: params.messageId ||Date.now().toString(),
160
- },
161
- };
162
- ws.send(JSON.stringify(content));
163
- logDcgchat.info(`dcgchat[${ctx.accountId}]: sendMedia to ${target}, ${JSON.stringify(content)}`);
164
- }
165
- } else {
166
- logDcgchat.warn(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> ${ws?.readyState}: ${ctx.text}`);
167
- }
168
- return {
169
- channel: "dcgchat",
170
- messageId: `dcg-${Date.now()}`,
171
- chatId: target,
172
- };
173
- },
174
- },
175
- gateway: {
176
- startAccount: async (ctx) => {
177
- const { monitorDcgchatProvider } = await import("./monitor.js");
178
- const account = resolveAccount(ctx.cfg, ctx.accountId);
179
- if (!account.wsUrl) {
180
- ctx.log?.warn(`dcgchat[${account.accountId}]: wsUrl not configured, skipping`);
181
- return;
182
- }
183
- // ctx.log?.info(`dcgchat[${account.accountId}]: connecting to ${account.wsUrl}`);
184
- return monitorDcgchatProvider({
185
- config: ctx.cfg,
186
- runtime: ctx.runtime,
187
- abortSignal: ctx.abortSignal,
188
- accountId: ctx.accountId,
189
- });
190
- },
191
- },
192
- };
1
+ import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
2
+ import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk";
3
+ import type { ResolvedDcgchatAccount, DcgchatConfig } from "./types.js";
4
+ import { logDcgchat } from "./log.js";
5
+ import { getWsConnection } from "./connection.js";
6
+ import { ossUpload } from "./oss.js";
7
+ import { getMsgParams } from "./tool.js";
8
+
9
+ export function resolveAccount(cfg: OpenClawConfig, accountId?: string | null): ResolvedDcgchatAccount {
10
+ const id = accountId ?? DEFAULT_ACCOUNT_ID;
11
+ const raw = (cfg.channels?.["dcgchat"] as DcgchatConfig | undefined) ?? {};
12
+ return {
13
+ accountId: id,
14
+ enabled: raw.enabled !== false,
15
+ configured: Boolean(raw.wsUrl),
16
+ wsUrl: raw.wsUrl ?? "",
17
+ botToken: raw.botToken ?? "",
18
+ userId: raw.userId ?? "",
19
+ domainId: raw.domainId ?? "",
20
+ appId: raw.appId ?? "",
21
+ };
22
+ }
23
+
24
+ export const dcgchatPlugin: ChannelPlugin<ResolvedDcgchatAccount> = {
25
+ id: "dcgchat",
26
+ meta: {
27
+ id: "dcgchat",
28
+ label: "DCG Chat",
29
+ selectionLabel: "DCG Chat",
30
+ docsPath: "/channels/dcgchat",
31
+ docsLabel: "dcgchat",
32
+ blurb: "连接 OpenClaw 与 DCG Chat 产品",
33
+ order: 80,
34
+ },
35
+ capabilities: {
36
+ chatTypes: ["direct"],
37
+ polls: false,
38
+ threads: true,
39
+ media: true,
40
+ nativeCommands: true,
41
+ reactions: true,
42
+ edit: false,
43
+ reply: true,
44
+ effects: true,
45
+ // blockStreaming: true,
46
+ },
47
+ reload: { configPrefixes: ["channels.dcgchat"] },
48
+ configSchema: {
49
+ schema: {
50
+ type: "object",
51
+ additionalProperties: false,
52
+ properties: {
53
+ enabled: { type: "boolean" },
54
+ wsUrl: { type: "string" },
55
+ botToken: { type: "string" },
56
+ userId: { type: "string" },
57
+ appId: { type: "string" },
58
+ domainId: { type: "string" },
59
+ capabilities: { type: "array", items: { type: "string" } },
60
+ },
61
+ },
62
+ },
63
+ config: {
64
+ listAccountIds: () => [DEFAULT_ACCOUNT_ID],
65
+ resolveAccount: (cfg, accountId) => resolveAccount(cfg, accountId),
66
+ defaultAccountId: () => DEFAULT_ACCOUNT_ID,
67
+ setAccountEnabled: ({ cfg, enabled }) => ({
68
+ ...cfg,
69
+ channels: {
70
+ ...cfg.channels,
71
+ "dcgchat": {
72
+ ...(cfg.channels?.["dcgchat"] as Record<string, unknown> | undefined),
73
+ enabled,
74
+ },
75
+ },
76
+ }),
77
+ isConfigured: (account) => account.configured,
78
+ describeAccount: (account) => ({
79
+ accountId: account.accountId,
80
+ enabled: account.enabled,
81
+ configured: account.configured,
82
+ wsUrl: account.wsUrl,
83
+ botToken: account.botToken ? "***" : "",
84
+ userId: account.userId,
85
+ domainId: account.domainId,
86
+ appId: account.appId,
87
+ }),
88
+ },
89
+ messaging: {
90
+ normalizeTarget: (raw) => raw?.trim() || undefined,
91
+ targetResolver: {
92
+ looksLikeId: (raw) => Boolean(raw?.trim()),
93
+ hint: "userId",
94
+ },
95
+ },
96
+ outbound: {
97
+ deliveryMode: "direct",
98
+ // textChunkLimit: 25,
99
+ textChunkLimit: 4000,
100
+ sendText: async (ctx) => {
101
+ const target = ctx.to || "(implicit)";
102
+ const ws = getWsConnection()
103
+ const params = getMsgParams();
104
+ if (ws?.readyState === WebSocket.OPEN) {
105
+ const {botToken} = resolveAccount(ctx.cfg, ctx.accountId);
106
+ const content = {
107
+ messageType: "openclaw_bot_chat",
108
+ _userId: target,
109
+ source: "client",
110
+ content: {
111
+ bot_token: botToken,
112
+ response: ctx.text,
113
+ session_id: params.sessionId,
114
+ message_id: params.messageId || Date.now().toString(),
115
+ },
116
+ };
117
+ ws.send(JSON.stringify(content));
118
+ logDcgchat.info(`dcgchat[${ctx.accountId}]: sendText to ${target}, ${JSON.stringify(content)}`);
119
+ } else {
120
+ logDcgchat.warn(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> ${ws?.readyState}: ${ctx.text}`);
121
+ }
122
+ return {
123
+ channel: "dcgchat",
124
+ messageId: `dcg-${Date.now()}`,
125
+ chatId: target,
126
+ };
127
+ },
128
+ sendMedia: async (ctx) => {
129
+ const target = ctx.to || "(implicit)";
130
+ const ws = getWsConnection()
131
+ const params = getMsgParams();
132
+ if (ws?.readyState === WebSocket.OPEN) {
133
+ const {botToken} = resolveAccount(ctx.cfg, ctx.accountId);
134
+ try {
135
+ const url = ctx.mediaUrl ? await ossUpload(ctx.mediaUrl, botToken) : '';
136
+ const fileName = ctx.mediaUrl?.split(/[\\/]/).pop() || ''
137
+ const content = {
138
+ messageType: "openclaw_bot_chat",
139
+ _userId: target,
140
+ source: "client",
141
+ content: {
142
+ bot_token: botToken,
143
+ response: ctx.text + '\n' + `[${fileName}](${url})`,
144
+ session_id: params.sessionId,
145
+ message_id: params.messageId ||Date.now().toString(),
146
+ },
147
+ };
148
+ ws.send(JSON.stringify(content));
149
+ logDcgchat.info(`dcgchat[${ctx.accountId}]: sendMedia to ${target}, ${JSON.stringify(content)}`);
150
+ } catch (error) {
151
+ const content = {
152
+ messageType: "openclaw_bot_chat",
153
+ _userId: target,
154
+ source: "client",
155
+ content: {
156
+ bot_token: botToken,
157
+ response: ctx.text + '\n' + ctx.mediaUrl,
158
+ session_id: params.sessionId || Date.now().toString(),
159
+ message_id: params.messageId ||Date.now().toString(),
160
+ },
161
+ };
162
+ ws.send(JSON.stringify(content));
163
+ logDcgchat.info(`dcgchat[${ctx.accountId}]: sendMedia to ${target}, ${JSON.stringify(content)}`);
164
+ }
165
+ } else {
166
+ logDcgchat.warn(`[dcgchat][${ctx.accountId ?? DEFAULT_ACCOUNT_ID}] outbound -> ${ws?.readyState}: ${ctx.text}`);
167
+ }
168
+ return {
169
+ channel: "dcgchat",
170
+ messageId: `dcg-${Date.now()}`,
171
+ chatId: target,
172
+ };
173
+ },
174
+ },
175
+ gateway: {
176
+ startAccount: async (ctx) => {
177
+ const { monitorDcgchatProvider } = await import("./monitor.js");
178
+ const account = resolveAccount(ctx.cfg, ctx.accountId);
179
+ if (!account.wsUrl) {
180
+ ctx.log?.warn(`dcgchat[${account.accountId}]: wsUrl not configured, skipping`);
181
+ return;
182
+ }
183
+ // ctx.log?.info(`dcgchat[${account.accountId}]: connecting to ${account.wsUrl}`);
184
+ return monitorDcgchatProvider({
185
+ config: ctx.cfg,
186
+ runtime: ctx.runtime,
187
+ abortSignal: ctx.abortSignal,
188
+ accountId: ctx.accountId,
189
+ });
190
+ },
191
+ },
192
+ };
package/src/connection.ts CHANGED
@@ -1,11 +1,11 @@
1
- import type WebSocket from "ws";
2
-
3
- let ws: WebSocket | null = null;
4
-
5
- export function setWsConnection(next: WebSocket | null) {
6
- ws = next;
7
- }
8
-
9
- export function getWsConnection(): WebSocket | null {
10
- return ws;
11
- }
1
+ import type WebSocket from "ws";
2
+
3
+ let ws: WebSocket | null = null;
4
+
5
+ export function setWsConnection(next: WebSocket | null) {
6
+ ws = next;
7
+ }
8
+
9
+ export function getWsConnection(): WebSocket | null {
10
+ return ws;
11
+ }
package/src/log.ts CHANGED
@@ -1,46 +1,46 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { fileURLToPath } from "url";
4
-
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- const logsDir = path.resolve(__dirname, "../logs");
7
-
8
- function getLogFilePath(): string {
9
- const date = new Date();
10
- const yyyy = date.getFullYear();
11
- const mm = String(date.getMonth() + 1).padStart(2, "0");
12
- const dd = String(date.getDate()).padStart(2, "0");
13
- return path.join(logsDir, `${yyyy}-${mm}-${dd}.log`);
14
- }
15
-
16
- function formatLine(level: string, message: string, extra?: unknown): string {
17
- const now = new Date().toISOString();
18
- const suffix = extra !== undefined ? " " + JSON.stringify(extra) : "";
19
- return `[${now}] [${level}] ${message}${suffix}\n`;
20
- }
21
-
22
- function writeLog(level: string, message: string, extra?: unknown): void {
23
- try {
24
- if (!fs.existsSync(logsDir)) {
25
- fs.mkdirSync(logsDir, { recursive: true });
26
- }
27
- fs.appendFileSync(getLogFilePath(), formatLine(level, message, extra), "utf-8");
28
- } catch {
29
- // 写日志失败时静默处理,避免影响主流程
30
- }
31
- }
32
-
33
- export const logDcgchat = {
34
- info(message: string, extra?: unknown): void {
35
- writeLog("INFO", message, extra);
36
- },
37
- warn(message: string, extra?: unknown): void {
38
- writeLog("WARN", message, extra);
39
- },
40
- error(message: string, extra?: unknown): void {
41
- writeLog("ERROR", message, extra);
42
- },
43
- debug(message: string, extra?: unknown): void {
44
- writeLog("DEBUG", message, extra);
45
- },
46
- };
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const logsDir = path.resolve(__dirname, "../logs");
7
+
8
+ function getLogFilePath(): string {
9
+ const date = new Date();
10
+ const yyyy = date.getFullYear();
11
+ const mm = String(date.getMonth() + 1).padStart(2, "0");
12
+ const dd = String(date.getDate()).padStart(2, "0");
13
+ return path.join(logsDir, `${yyyy}-${mm}-${dd}.log`);
14
+ }
15
+
16
+ function formatLine(level: string, message: string, extra?: unknown): string {
17
+ const now = new Date().toISOString();
18
+ const suffix = extra !== undefined ? " " + JSON.stringify(extra) : "";
19
+ return `[${now}] [${level}] ${message}${suffix}\n`;
20
+ }
21
+
22
+ function writeLog(level: string, message: string, extra?: unknown): void {
23
+ try {
24
+ if (!fs.existsSync(logsDir)) {
25
+ fs.mkdirSync(logsDir, { recursive: true });
26
+ }
27
+ fs.appendFileSync(getLogFilePath(), formatLine(level, message, extra), "utf-8");
28
+ } catch {
29
+ // 写日志失败时静默处理,避免影响主流程
30
+ }
31
+ }
32
+
33
+ export const logDcgchat = {
34
+ info(message: string, extra?: unknown): void {
35
+ writeLog("INFO", message, extra);
36
+ },
37
+ warn(message: string, extra?: unknown): void {
38
+ writeLog("WARN", message, extra);
39
+ },
40
+ error(message: string, extra?: unknown): void {
41
+ writeLog("ERROR", message, extra);
42
+ },
43
+ debug(message: string, extra?: unknown): void {
44
+ writeLog("DEBUG", message, extra);
45
+ },
46
+ };