@wecode-ai/weibo-openclaw-plugin 1.0.0 → 1.0.1
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/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/src/accounts.d.ts +10 -0
- package/dist/src/accounts.d.ts.map +1 -0
- package/dist/src/accounts.js +113 -0
- package/dist/src/accounts.js.map +1 -0
- package/dist/src/bot.d.ts +26 -0
- package/dist/src/bot.d.ts.map +1 -0
- package/dist/src/bot.js +385 -0
- package/dist/src/bot.js.map +1 -0
- package/dist/src/channel.d.ts +4 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +317 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/client.d.ts +51 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +336 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/config-schema.d.ts +86 -0
- package/dist/src/config-schema.d.ts.map +1 -0
- package/{src/config-schema.ts → dist/src/config-schema.js} +19 -24
- package/dist/src/config-schema.js.map +1 -0
- package/dist/src/fingerprint.d.ts +4 -0
- package/dist/src/fingerprint.d.ts.map +1 -0
- package/dist/src/fingerprint.js +19 -0
- package/dist/src/fingerprint.js.map +1 -0
- package/dist/src/monitor.d.ts +13 -0
- package/dist/src/monitor.d.ts.map +1 -0
- package/dist/src/monitor.js +169 -0
- package/dist/src/monitor.js.map +1 -0
- package/dist/src/outbound-stream.d.ts +32 -0
- package/dist/src/outbound-stream.d.ts.map +1 -0
- package/dist/src/outbound-stream.js +184 -0
- package/dist/src/outbound-stream.js.map +1 -0
- package/dist/src/outbound.d.ts +3 -0
- package/dist/src/outbound.d.ts.map +1 -0
- package/dist/src/outbound.js +38 -0
- package/dist/src/outbound.js.map +1 -0
- package/dist/src/plugin-sdk-compat.d.ts +21 -0
- package/dist/src/plugin-sdk-compat.d.ts.map +1 -0
- package/dist/src/plugin-sdk-compat.js +47 -0
- package/dist/src/plugin-sdk-compat.js.map +1 -0
- package/dist/src/policy.d.ts +5 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/policy.js +6 -0
- package/dist/src/policy.js.map +1 -0
- package/dist/src/runtime.d.ts +4 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +11 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/search-schema.d.ts +6 -0
- package/dist/src/search-schema.d.ts.map +1 -0
- package/dist/src/search-schema.js +5 -0
- package/dist/src/search-schema.js.map +1 -0
- package/dist/src/send.d.ts +14 -0
- package/dist/src/send.d.ts.map +1 -0
- package/dist/src/send.js +58 -0
- package/dist/src/send.js.map +1 -0
- package/dist/src/sim-page.d.ts +51 -0
- package/dist/src/sim-page.d.ts.map +1 -0
- package/dist/src/sim-page.js +72 -0
- package/dist/src/sim-page.js.map +1 -0
- package/dist/src/sim-store.d.ts +54 -0
- package/dist/src/sim-store.d.ts.map +1 -0
- package/dist/src/sim-store.js +117 -0
- package/dist/src/sim-store.js.map +1 -0
- package/dist/src/targets.d.ts +4 -0
- package/dist/src/targets.d.ts.map +1 -0
- package/dist/src/targets.js +15 -0
- package/dist/src/targets.js.map +1 -0
- package/dist/src/token.d.ts +27 -0
- package/dist/src/token.d.ts.map +1 -0
- package/dist/src/token.js +144 -0
- package/dist/src/token.js.map +1 -0
- package/dist/src/tools-config.d.ts +21 -0
- package/dist/src/tools-config.d.ts.map +1 -0
- package/dist/src/tools-config.js +42 -0
- package/dist/src/tools-config.js.map +1 -0
- package/dist/src/types.d.ts +76 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/weibo-hot-search.d.ts +44 -0
- package/dist/src/weibo-hot-search.d.ts.map +1 -0
- package/dist/src/weibo-hot-search.js +215 -0
- package/dist/src/weibo-hot-search.js.map +1 -0
- package/dist/src/weibo-search.d.ts +71 -0
- package/dist/src/weibo-search.d.ts.map +1 -0
- package/dist/src/weibo-search.js +182 -0
- package/dist/src/weibo-search.js.map +1 -0
- package/dist/src/weibo-status.d.ts +61 -0
- package/dist/src/weibo-status.d.ts.map +1 -0
- package/dist/src/weibo-status.js +194 -0
- package/dist/src/weibo-status.js.map +1 -0
- package/openclaw.plugin.json +12 -0
- package/package.json +4 -5
- package/skills/weibo-hot-search/SKILL.md +161 -0
- package/skills/weibo-search/SKILL.md +116 -0
- package/skills/weibo-status/SKILL.md +97 -0
- package/index.ts +0 -67
- package/src/accounts.ts +0 -134
- package/src/bot.ts +0 -486
- package/src/channel.ts +0 -391
- package/src/client.ts +0 -435
- package/src/fingerprint.ts +0 -25
- package/src/monitor.ts +0 -206
- package/src/outbound-stream.ts +0 -241
- package/src/outbound.ts +0 -49
- package/src/plugin-sdk-compat.ts +0 -82
- package/src/policy.ts +0 -10
- package/src/runtime.ts +0 -14
- package/src/search-schema.ts +0 -7
- package/src/send.ts +0 -80
- package/src/sim-page.ts +0 -140
- package/src/sim-store.ts +0 -186
- package/src/targets.ts +0 -14
- package/src/token.ts +0 -207
- package/src/tools-config.ts +0 -55
- package/src/types.ts +0 -95
- package/src/weibo-hot-search.ts +0 -345
- package/src/weibo-search.ts +0 -333
- package/src/weibo-status.ts +0 -341
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
export { monitorWeiboProvider } from "./src/monitor.js";
|
|
3
|
+
export { sendMessageWeibo } from "./src/send.js";
|
|
4
|
+
export { weiboPlugin } from "./src/channel.js";
|
|
5
|
+
declare const plugin: {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
configSchema: {
|
|
10
|
+
type: "object";
|
|
11
|
+
properties: {};
|
|
12
|
+
};
|
|
13
|
+
register(api: OpenClawPluginApi): void;
|
|
14
|
+
};
|
|
15
|
+
export default plugin;
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAU7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,QAAA,MAAM,MAAM;;;;;;;;kBAKI,iBAAiB;CA6ChC,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { weiboPlugin } from "./src/channel.js";
|
|
2
|
+
import { setWeiboRuntime } from "./src/runtime.js";
|
|
3
|
+
import { reconnectWeiboMonitor } from "./src/monitor.js";
|
|
4
|
+
import { clearClientCache } from "./src/client.js";
|
|
5
|
+
import { clearTokenCache } from "./src/token.js";
|
|
6
|
+
import { registerWeiboSearchTools } from "./src/weibo-search.js";
|
|
7
|
+
import { registerWeiboStatusTools } from "./src/weibo-status.js";
|
|
8
|
+
import { registerWeiboHotSearchTools } from "./src/weibo-hot-search.js";
|
|
9
|
+
export { monitorWeiboProvider } from "./src/monitor.js";
|
|
10
|
+
export { sendMessageWeibo } from "./src/send.js";
|
|
11
|
+
export { weiboPlugin } from "./src/channel.js";
|
|
12
|
+
const plugin = {
|
|
13
|
+
id: "weibo",
|
|
14
|
+
name: "Weibo",
|
|
15
|
+
description: "Weibo DM channel plugin",
|
|
16
|
+
configSchema: { type: "object", properties: {} },
|
|
17
|
+
register(api) {
|
|
18
|
+
setWeiboRuntime(api.runtime);
|
|
19
|
+
api.registerChannel({ plugin: weiboPlugin });
|
|
20
|
+
registerWeiboSearchTools(api);
|
|
21
|
+
registerWeiboStatusTools(api);
|
|
22
|
+
registerWeiboHotSearchTools(api);
|
|
23
|
+
// 工具调用钩子
|
|
24
|
+
api.on("before_tool_call", (event) => {
|
|
25
|
+
if (event.toolName.startsWith("weibo_")) {
|
|
26
|
+
console.log(`[微博工具调用] ${event.toolName} 参数: ${JSON.stringify(event.params)}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
api.on("after_tool_call", (event) => {
|
|
30
|
+
if (event.toolName.startsWith("weibo_")) {
|
|
31
|
+
if (event.error) {
|
|
32
|
+
console.error(`[微博工具调用失败] ${event.toolName} 错误: ${event.error}`);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log(`[微博工具调用成功] ${event.toolName} 耗时: ${event.durationMs}ms`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
api.registerGatewayMethod("weibo.reconnect", async ({ params, respond, context }) => {
|
|
40
|
+
const accountId = typeof params.accountId === "string" && params.accountId.trim()
|
|
41
|
+
? params.accountId.trim()
|
|
42
|
+
: undefined;
|
|
43
|
+
try {
|
|
44
|
+
await reconnectWeiboMonitor(accountId);
|
|
45
|
+
clearClientCache(accountId);
|
|
46
|
+
clearTokenCache(accountId);
|
|
47
|
+
await context.stopChannel("weibo", accountId);
|
|
48
|
+
await context.startChannel("weibo", accountId);
|
|
49
|
+
respond(true, { ok: true, accountId: accountId ?? "default" });
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
53
|
+
respond(false, undefined, {
|
|
54
|
+
code: "weibo_reconnect_failed",
|
|
55
|
+
message,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export default plugin;
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ClawdbotConfig } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { ResolvedWeiboAccount } from "./types.js";
|
|
3
|
+
export declare function resolveWeiboAccount({ cfg, accountId, }: {
|
|
4
|
+
cfg: ClawdbotConfig;
|
|
5
|
+
accountId?: string;
|
|
6
|
+
}): ResolvedWeiboAccount;
|
|
7
|
+
export declare function listWeiboAccountIds(cfg: ClawdbotConfig): string[];
|
|
8
|
+
export declare function resolveDefaultWeiboAccountId(cfg: ClawdbotConfig): string;
|
|
9
|
+
export declare function listEnabledWeiboAccounts(cfg: ClawdbotConfig): ResolvedWeiboAccount[];
|
|
10
|
+
//# sourceMappingURL=accounts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.d.ts","sourceRoot":"","sources":["../../src/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAe,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAiBpE,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EACH,SAA8B,GAC/B,EAAE;IACD,GAAG,EAAE,cAAc,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,oBAAoB,CAwFvB;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,EAAE,CAQjE;AAED,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAExE;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,cAAc,GAAG,oBAAoB,EAAE,CAKpF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
2
|
+
const DEFAULT_WS_ENDPOINT = "ws://open-im.api.weibo.com/ws/stream";
|
|
3
|
+
const DEFAULT_TOKEN_ENDPOINT = "http://open-im.api.weibo.com/open/auth/ws_token";
|
|
4
|
+
function readOptionalNonBlankString(value) {
|
|
5
|
+
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
6
|
+
return String(value);
|
|
7
|
+
}
|
|
8
|
+
if (typeof value !== "string") {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
return trimmed ? trimmed : undefined;
|
|
13
|
+
}
|
|
14
|
+
export function resolveWeiboAccount({ cfg, accountId = DEFAULT_ACCOUNT_ID, }) {
|
|
15
|
+
const weiboCfg = cfg.channels?.weibo;
|
|
16
|
+
const isDefault = accountId === DEFAULT_ACCOUNT_ID;
|
|
17
|
+
const topLevelAppId = readOptionalNonBlankString(weiboCfg?.appId);
|
|
18
|
+
const topLevelAppSecret = readOptionalNonBlankString(weiboCfg?.appSecret);
|
|
19
|
+
const topLevelWsEndpoint = readOptionalNonBlankString(weiboCfg?.wsEndpoint);
|
|
20
|
+
const topLevelTokenEndpoint = readOptionalNonBlankString(weiboCfg?.tokenEndpoint);
|
|
21
|
+
if (isDefault && weiboCfg) {
|
|
22
|
+
const hasCredentials = !!(topLevelAppId && topLevelAppSecret);
|
|
23
|
+
return {
|
|
24
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
25
|
+
enabled: weiboCfg.enabled ?? true,
|
|
26
|
+
configured: hasCredentials,
|
|
27
|
+
name: "Default",
|
|
28
|
+
appId: topLevelAppId,
|
|
29
|
+
appSecret: topLevelAppSecret,
|
|
30
|
+
wsEndpoint: topLevelWsEndpoint ?? DEFAULT_WS_ENDPOINT,
|
|
31
|
+
tokenEndpoint: topLevelTokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT,
|
|
32
|
+
config: {
|
|
33
|
+
dmPolicy: weiboCfg.dmPolicy ?? "open",
|
|
34
|
+
allowFrom: weiboCfg.allowFrom ?? [],
|
|
35
|
+
tokenEndpoint: topLevelTokenEndpoint ?? DEFAULT_TOKEN_ENDPOINT,
|
|
36
|
+
wsEndpoint: topLevelWsEndpoint ?? DEFAULT_WS_ENDPOINT,
|
|
37
|
+
textChunkLimit: weiboCfg.textChunkLimit,
|
|
38
|
+
chunkMode: weiboCfg.chunkMode ?? "raw",
|
|
39
|
+
blockStreaming: weiboCfg.blockStreaming ?? true,
|
|
40
|
+
tools: weiboCfg.tools,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const accountCfg = weiboCfg?.accounts?.[accountId];
|
|
45
|
+
const topLevel = {
|
|
46
|
+
appId: topLevelAppId,
|
|
47
|
+
appSecret: topLevelAppSecret,
|
|
48
|
+
wsEndpoint: topLevelWsEndpoint,
|
|
49
|
+
tokenEndpoint: topLevelTokenEndpoint,
|
|
50
|
+
dmPolicy: weiboCfg?.dmPolicy,
|
|
51
|
+
allowFrom: weiboCfg?.allowFrom,
|
|
52
|
+
textChunkLimit: weiboCfg?.textChunkLimit,
|
|
53
|
+
chunkMode: weiboCfg?.chunkMode,
|
|
54
|
+
blockStreaming: weiboCfg?.blockStreaming,
|
|
55
|
+
tools: weiboCfg?.tools,
|
|
56
|
+
};
|
|
57
|
+
const merged = {
|
|
58
|
+
appId: readOptionalNonBlankString(accountCfg?.appId) ?? topLevel.appId,
|
|
59
|
+
appSecret: readOptionalNonBlankString(accountCfg?.appSecret) ?? topLevel.appSecret,
|
|
60
|
+
wsEndpoint: readOptionalNonBlankString(accountCfg?.wsEndpoint)
|
|
61
|
+
?? topLevel.wsEndpoint
|
|
62
|
+
?? DEFAULT_WS_ENDPOINT,
|
|
63
|
+
tokenEndpoint: readOptionalNonBlankString(accountCfg?.tokenEndpoint)
|
|
64
|
+
?? topLevel.tokenEndpoint
|
|
65
|
+
?? DEFAULT_TOKEN_ENDPOINT,
|
|
66
|
+
dmPolicy: accountCfg?.dmPolicy ?? topLevel.dmPolicy ?? "open",
|
|
67
|
+
allowFrom: accountCfg?.allowFrom ?? topLevel.allowFrom ?? [],
|
|
68
|
+
textChunkLimit: accountCfg?.textChunkLimit ?? topLevel.textChunkLimit,
|
|
69
|
+
chunkMode: accountCfg?.chunkMode ?? topLevel.chunkMode ?? "raw",
|
|
70
|
+
blockStreaming: accountCfg?.blockStreaming ?? topLevel.blockStreaming ?? true,
|
|
71
|
+
tools: accountCfg?.tools ?? topLevel.tools,
|
|
72
|
+
};
|
|
73
|
+
const hasCredentials = !!(merged.appId && merged.appSecret);
|
|
74
|
+
return {
|
|
75
|
+
accountId,
|
|
76
|
+
enabled: accountCfg?.enabled ?? weiboCfg?.enabled ?? true,
|
|
77
|
+
configured: hasCredentials,
|
|
78
|
+
name: accountCfg?.name,
|
|
79
|
+
appId: merged.appId,
|
|
80
|
+
appSecret: merged.appSecret,
|
|
81
|
+
wsEndpoint: merged.wsEndpoint,
|
|
82
|
+
tokenEndpoint: merged.tokenEndpoint,
|
|
83
|
+
config: {
|
|
84
|
+
dmPolicy: merged.dmPolicy,
|
|
85
|
+
allowFrom: merged.allowFrom,
|
|
86
|
+
tokenEndpoint: merged.tokenEndpoint,
|
|
87
|
+
wsEndpoint: merged.wsEndpoint,
|
|
88
|
+
textChunkLimit: merged.textChunkLimit,
|
|
89
|
+
chunkMode: merged.chunkMode,
|
|
90
|
+
blockStreaming: merged.blockStreaming,
|
|
91
|
+
tools: merged.tools,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function listWeiboAccountIds(cfg) {
|
|
96
|
+
const weiboCfg = cfg.channels?.weibo;
|
|
97
|
+
const accounts = weiboCfg?.accounts;
|
|
98
|
+
const ids = [DEFAULT_ACCOUNT_ID];
|
|
99
|
+
if (accounts) {
|
|
100
|
+
ids.push(...Object.keys(accounts));
|
|
101
|
+
}
|
|
102
|
+
return ids;
|
|
103
|
+
}
|
|
104
|
+
export function resolveDefaultWeiboAccountId(cfg) {
|
|
105
|
+
return DEFAULT_ACCOUNT_ID;
|
|
106
|
+
}
|
|
107
|
+
export function listEnabledWeiboAccounts(cfg) {
|
|
108
|
+
const ids = listWeiboAccountIds(cfg);
|
|
109
|
+
return ids
|
|
110
|
+
.map((id) => resolveWeiboAccount({ cfg, accountId: id }))
|
|
111
|
+
.filter((a) => a.enabled && a.configured);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/accounts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { WeiboInboundAttachmentPart, WeiboMessageContext, WeiboResponseMessageInputItem } from "./types.js";
|
|
3
|
+
export type WeiboMessageEvent = {
|
|
4
|
+
type: "message";
|
|
5
|
+
payload: {
|
|
6
|
+
messageId: string;
|
|
7
|
+
fromUserId: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
timestamp?: number;
|
|
10
|
+
input?: WeiboResponseMessageInputItem[];
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type NormalizedWeiboInboundInput = {
|
|
14
|
+
text: string;
|
|
15
|
+
images: WeiboInboundAttachmentPart[];
|
|
16
|
+
files: WeiboInboundAttachmentPart[];
|
|
17
|
+
};
|
|
18
|
+
export declare function normalizeWeiboInboundInput(event: WeiboMessageEvent): NormalizedWeiboInboundInput;
|
|
19
|
+
export type HandleWeiboMessageParams = {
|
|
20
|
+
cfg: ClawdbotConfig;
|
|
21
|
+
event: WeiboMessageEvent;
|
|
22
|
+
accountId: string;
|
|
23
|
+
runtime?: RuntimeEnv;
|
|
24
|
+
};
|
|
25
|
+
export declare function handleWeiboMessage(params: HandleWeiboMessageParams): Promise<WeiboMessageContext | null>;
|
|
26
|
+
//# sourceMappingURL=bot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../../src/bot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EACV,0BAA0B,EAC1B,mBAAmB,EAEnB,6BAA6B,EAC9B,MAAM,YAAY,CAAC;AAiDpB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,6BAA6B,EAAE,CAAC;KACzC,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,EAAE,CAAC;IACrC,KAAK,EAAE,0BAA0B,EAAE,CAAC;CACrC,CAAC;AAWF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,iBAAiB,GAAG,2BAA2B,CAwChG;AAoED,MAAM,MAAM,wBAAwB,GAAG;IACrC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB,CAAC;AAEF,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAgS9G"}
|
package/dist/src/bot.js
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { resolveWeiboAccount } from "./accounts.js";
|
|
3
|
+
import { createWeiboOutboundStream } from "./outbound-stream.js";
|
|
4
|
+
import { getWeiboRuntime } from "./runtime.js";
|
|
5
|
+
import { buildAgentMediaPayloadCompat } from "./plugin-sdk-compat.js";
|
|
6
|
+
// Simple in-memory dedup
|
|
7
|
+
const processedMessages = new Set();
|
|
8
|
+
const MAX_INBOUND_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
9
|
+
const MAX_INBOUND_FILE_BYTES = 5 * 1024 * 1024;
|
|
10
|
+
const SUPPORTED_IMAGE_MIME_TYPES = new Set([
|
|
11
|
+
"image/jpeg",
|
|
12
|
+
"image/png",
|
|
13
|
+
"image/gif",
|
|
14
|
+
"image/webp",
|
|
15
|
+
]);
|
|
16
|
+
function isDuplicate(messageId) {
|
|
17
|
+
if (processedMessages.has(messageId)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
processedMessages.add(messageId);
|
|
21
|
+
// Cleanup old entries periodically
|
|
22
|
+
if (processedMessages.size > 1000) {
|
|
23
|
+
const toDelete = Array.from(processedMessages).slice(0, 500);
|
|
24
|
+
toDelete.forEach((id) => processedMessages.delete(id));
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
function resolveInboundMessageId(event) {
|
|
29
|
+
const explicitMessageId = event.payload.messageId.trim();
|
|
30
|
+
if (explicitMessageId) {
|
|
31
|
+
return explicitMessageId;
|
|
32
|
+
}
|
|
33
|
+
const digest = createHash("sha1")
|
|
34
|
+
.update(JSON.stringify({
|
|
35
|
+
fromUserId: event.payload.fromUserId,
|
|
36
|
+
text: event.payload.text ?? "",
|
|
37
|
+
timestamp: event.payload.timestamp ?? null,
|
|
38
|
+
input: event.payload.input ?? [],
|
|
39
|
+
}))
|
|
40
|
+
.digest("hex")
|
|
41
|
+
.slice(0, 16);
|
|
42
|
+
return `weibo_inbound_${digest}`;
|
|
43
|
+
}
|
|
44
|
+
function isSupportedWeiboContentPart(part) {
|
|
45
|
+
if (!part || typeof part !== "object") {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const type = part.type;
|
|
49
|
+
return type === "input_text" || type === "input_image" || type === "input_file";
|
|
50
|
+
}
|
|
51
|
+
export function normalizeWeiboInboundInput(event) {
|
|
52
|
+
const textParts = [];
|
|
53
|
+
const images = [];
|
|
54
|
+
const files = [];
|
|
55
|
+
for (const item of event.payload.input ?? []) {
|
|
56
|
+
if (item.type !== "message" || item.role !== "user" || !Array.isArray(item.content)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const part of item.content) {
|
|
60
|
+
if (!isSupportedWeiboContentPart(part)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (part.type === "input_text") {
|
|
64
|
+
if (typeof part.text === "string" && part.text) {
|
|
65
|
+
textParts.push(part.text);
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const target = part.type === "input_image" ? images : files;
|
|
70
|
+
target.push({
|
|
71
|
+
mimeType: part.source.media_type,
|
|
72
|
+
filename: part.filename,
|
|
73
|
+
base64: part.source.data,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const normalizedText = textParts.length > 0
|
|
78
|
+
? textParts.join("\n")
|
|
79
|
+
: (event.payload.text ?? "");
|
|
80
|
+
return {
|
|
81
|
+
text: normalizedText,
|
|
82
|
+
images,
|
|
83
|
+
files,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async function persistWeiboInboundAttachments(params) {
|
|
87
|
+
const { normalized, runtimeCore, error } = params;
|
|
88
|
+
const mediaList = [];
|
|
89
|
+
for (const image of normalized.images) {
|
|
90
|
+
if (!SUPPORTED_IMAGE_MIME_TYPES.has(image.mimeType)) {
|
|
91
|
+
error(`weibo: unsupported image mime type: ${image.mimeType}`);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const buffer = Buffer.from(image.base64, "base64");
|
|
96
|
+
if (buffer.length === 0) {
|
|
97
|
+
error(`weibo: empty image payload: ${image.filename ?? "unknown"}`);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const saved = await runtimeCore.channel.media.saveMediaBuffer(buffer, image.mimeType, "inbound", MAX_INBOUND_IMAGE_BYTES, image.filename);
|
|
101
|
+
mediaList.push({
|
|
102
|
+
path: saved.path,
|
|
103
|
+
contentType: saved.contentType,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
error(`weibo: failed to persist image input: ${String(err)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const file of normalized.files) {
|
|
111
|
+
try {
|
|
112
|
+
const buffer = Buffer.from(file.base64, "base64");
|
|
113
|
+
if (buffer.length === 0) {
|
|
114
|
+
error(`weibo: empty file payload: ${file.filename ?? "unknown"}`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const saved = await runtimeCore.channel.media.saveMediaBuffer(buffer, file.mimeType, "inbound", MAX_INBOUND_FILE_BYTES, file.filename);
|
|
118
|
+
mediaList.push({
|
|
119
|
+
path: saved.path,
|
|
120
|
+
contentType: saved.contentType,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
error(`weibo: failed to persist file input: ${String(err)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return buildAgentMediaPayloadCompat(mediaList);
|
|
128
|
+
}
|
|
129
|
+
export async function handleWeiboMessage(params) {
|
|
130
|
+
const { cfg, event, accountId, runtime } = params;
|
|
131
|
+
const log = runtime?.log ?? console.log;
|
|
132
|
+
const error = runtime?.error ?? console.error;
|
|
133
|
+
const account = resolveWeiboAccount({ cfg, accountId });
|
|
134
|
+
if (!account.enabled || !account.configured) {
|
|
135
|
+
error(`weibo[${accountId}]: account not enabled or configured`);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const { fromUserId, timestamp } = event.payload;
|
|
139
|
+
const messageId = resolveInboundMessageId(event);
|
|
140
|
+
// Deduplication
|
|
141
|
+
if (isDuplicate(messageId)) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const inboundAcceptedAt = Date.now();
|
|
145
|
+
const streamDebugEnabled = process.env.WEIBO_STREAM_DEBUG === "1";
|
|
146
|
+
const streamDebug = (tag, data) => {
|
|
147
|
+
if (!streamDebugEnabled) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const payload = data ? ` ${JSON.stringify(data)}` : "";
|
|
151
|
+
log(`weibo[${accountId}][stream-debug] ${tag}${payload}`);
|
|
152
|
+
};
|
|
153
|
+
// Get runtime core
|
|
154
|
+
const core = getWeiboRuntime();
|
|
155
|
+
// Build message content
|
|
156
|
+
const normalized = normalizeWeiboInboundInput(event);
|
|
157
|
+
const content = normalized.text;
|
|
158
|
+
const hasText = content.trim().length > 0;
|
|
159
|
+
const hasAttachments = normalized.images.length > 0 || normalized.files.length > 0;
|
|
160
|
+
if (!hasText && !hasAttachments) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
const mediaPayload = await persistWeiboInboundAttachments({
|
|
164
|
+
normalized,
|
|
165
|
+
runtimeCore: core,
|
|
166
|
+
error,
|
|
167
|
+
});
|
|
168
|
+
const hasPersistedMedia = Array.isArray(mediaPayload.MediaPaths) && mediaPayload.MediaPaths.length > 0;
|
|
169
|
+
if (!hasText && !hasPersistedMedia) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
// Resolve routing - find which agent should handle this message
|
|
173
|
+
const route = core.channel.routing.resolveAgentRoute({
|
|
174
|
+
cfg,
|
|
175
|
+
channel: "weibo",
|
|
176
|
+
accountId,
|
|
177
|
+
peer: {
|
|
178
|
+
kind: "direct",
|
|
179
|
+
id: fromUserId,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
if (!route.agentId) {
|
|
183
|
+
log(`weibo[${accountId}]: no agent route found for ${fromUserId}`);
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
log(`weibo[${accountId}]: received message from ${fromUserId}, routing to ${route.agentId} (session=${route.sessionKey})`);
|
|
187
|
+
// Enqueue system event for logging/monitoring
|
|
188
|
+
const preview = content.replace(/\s+/g, " ").slice(0, 160);
|
|
189
|
+
core.system.enqueueSystemEvent(`Weibo[${accountId}] DM from ${fromUserId}: ${preview}`, {
|
|
190
|
+
sessionKey: route.sessionKey,
|
|
191
|
+
contextKey: `weibo:message:${fromUserId}:${messageId}`,
|
|
192
|
+
});
|
|
193
|
+
// Build the inbound envelope (message body for agent)
|
|
194
|
+
const body = core.channel.reply.formatInboundEnvelope({
|
|
195
|
+
channel: "Weibo",
|
|
196
|
+
from: fromUserId,
|
|
197
|
+
body: content,
|
|
198
|
+
timestamp: timestamp ?? Date.now(),
|
|
199
|
+
sender: { name: fromUserId, id: fromUserId },
|
|
200
|
+
});
|
|
201
|
+
// Resolve text chunking settings
|
|
202
|
+
const textChunkLimit = core.channel.text.resolveTextChunkLimit(cfg, "weibo", accountId, {
|
|
203
|
+
fallbackLimit: account.config.textChunkLimit ?? 4000,
|
|
204
|
+
});
|
|
205
|
+
const chunkMode = account.config.chunkMode
|
|
206
|
+
?? core.channel.text.resolveChunkMode(cfg, "weibo", accountId);
|
|
207
|
+
// Weibo real-time streaming is driven by onPartialReply; disable block streaming to avoid duplicate lanes.
|
|
208
|
+
const disableBlockStreaming = true;
|
|
209
|
+
streamDebug("dispatch_init", {
|
|
210
|
+
inboundMessageId: messageId,
|
|
211
|
+
fromUserId,
|
|
212
|
+
chunkMode,
|
|
213
|
+
textChunkLimit,
|
|
214
|
+
configuredBlockStreaming: account.config.blockStreaming,
|
|
215
|
+
disableBlockStreaming,
|
|
216
|
+
});
|
|
217
|
+
let currentOutboundMessageId = null;
|
|
218
|
+
let currentOutboundChunkId = 0;
|
|
219
|
+
let hasLoggedFirstChunkLatency = false;
|
|
220
|
+
const ensureOutboundMessageId = async () => {
|
|
221
|
+
if (currentOutboundMessageId) {
|
|
222
|
+
return currentOutboundMessageId;
|
|
223
|
+
}
|
|
224
|
+
const { generateWeiboMessageId } = await import("./send.js");
|
|
225
|
+
currentOutboundMessageId = generateWeiboMessageId();
|
|
226
|
+
currentOutboundChunkId = 0;
|
|
227
|
+
return currentOutboundMessageId;
|
|
228
|
+
};
|
|
229
|
+
const sendOutboundChunk = async (params) => {
|
|
230
|
+
const { sendMessageWeibo } = await import("./send.js");
|
|
231
|
+
const outboundMessageId = await ensureOutboundMessageId();
|
|
232
|
+
streamDebug("send_chunk", {
|
|
233
|
+
source: params.source,
|
|
234
|
+
messageId: outboundMessageId,
|
|
235
|
+
chunkId: currentOutboundChunkId,
|
|
236
|
+
done: params.done,
|
|
237
|
+
textLen: params.text.length,
|
|
238
|
+
preview: params.text.slice(0, 80),
|
|
239
|
+
});
|
|
240
|
+
await sendMessageWeibo({
|
|
241
|
+
cfg,
|
|
242
|
+
to: fromUserId,
|
|
243
|
+
text: params.text,
|
|
244
|
+
messageId: outboundMessageId,
|
|
245
|
+
chunkId: currentOutboundChunkId,
|
|
246
|
+
done: params.done,
|
|
247
|
+
});
|
|
248
|
+
if (!hasLoggedFirstChunkLatency && params.text.length > 0) {
|
|
249
|
+
const elapsedMs = Math.max(0, Date.now() - inboundAcceptedAt);
|
|
250
|
+
log(`weibo[${accountId}]: first chunk first-char latency=${elapsedMs}ms`);
|
|
251
|
+
hasLoggedFirstChunkLatency = true;
|
|
252
|
+
}
|
|
253
|
+
currentOutboundChunkId += 1;
|
|
254
|
+
};
|
|
255
|
+
const outboundStream = createWeiboOutboundStream({
|
|
256
|
+
chunkMode,
|
|
257
|
+
textChunkLimit,
|
|
258
|
+
emit: sendOutboundChunk,
|
|
259
|
+
chunkTextWithMode: (text, limit, mode) => core.channel.text.chunkTextWithMode(text, limit, mode),
|
|
260
|
+
streamDebug,
|
|
261
|
+
});
|
|
262
|
+
// Build final inbound context
|
|
263
|
+
const ctxPayload = core.channel.reply.finalizeInboundContext({
|
|
264
|
+
Body: body,
|
|
265
|
+
BodyForAgent: content,
|
|
266
|
+
BodyForCommands: content,
|
|
267
|
+
RawBody: content,
|
|
268
|
+
CommandBody: content,
|
|
269
|
+
From: `weibo:${fromUserId}`,
|
|
270
|
+
To: fromUserId,
|
|
271
|
+
SessionKey: route.sessionKey,
|
|
272
|
+
AccountId: route.accountId,
|
|
273
|
+
ChatType: "direct",
|
|
274
|
+
ConversationLabel: fromUserId,
|
|
275
|
+
SenderName: fromUserId,
|
|
276
|
+
SenderId: fromUserId,
|
|
277
|
+
Provider: "weibo",
|
|
278
|
+
Surface: "weibo",
|
|
279
|
+
MessageSid: messageId,
|
|
280
|
+
Timestamp: timestamp ?? Date.now(),
|
|
281
|
+
WasMentioned: true,
|
|
282
|
+
CommandAuthorized: true,
|
|
283
|
+
OriginatingChannel: "weibo",
|
|
284
|
+
OriginatingTo: fromUserId,
|
|
285
|
+
...mediaPayload,
|
|
286
|
+
});
|
|
287
|
+
// Create a dispatcher that sends replies back to Weibo
|
|
288
|
+
const { dispatcher, replyOptions, markDispatchIdle } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
289
|
+
deliver: async (reply, info) => {
|
|
290
|
+
const isFinalDeliver = info?.kind !== "block";
|
|
291
|
+
const before = outboundStream.snapshot();
|
|
292
|
+
streamDebug("deliver_enter", {
|
|
293
|
+
kind: info?.kind ?? "unknown",
|
|
294
|
+
isFinalDeliver,
|
|
295
|
+
textLen: (reply.text ?? "").length,
|
|
296
|
+
...before,
|
|
297
|
+
});
|
|
298
|
+
await outboundStream.pushDeliverText({
|
|
299
|
+
text: reply.text ?? "",
|
|
300
|
+
isFinal: isFinalDeliver,
|
|
301
|
+
});
|
|
302
|
+
streamDebug("deliver_exit", {
|
|
303
|
+
kind: info?.kind ?? "unknown",
|
|
304
|
+
isFinalDeliver,
|
|
305
|
+
...outboundStream.snapshot(),
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
onError: (err, info) => {
|
|
309
|
+
error(`weibo[${accountId}] ${info.kind} reply failed: ${String(err)}`);
|
|
310
|
+
},
|
|
311
|
+
onIdle: () => {
|
|
312
|
+
log(`weibo[${accountId}]: reply dispatcher idle`);
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
// Dispatch to agent
|
|
316
|
+
log(`weibo[${accountId}]: dispatching to agent (session=${route.sessionKey})`);
|
|
317
|
+
try {
|
|
318
|
+
const onSettled = async () => {
|
|
319
|
+
streamDebug("dispatcher_settled_before", {
|
|
320
|
+
currentOutboundMessageId,
|
|
321
|
+
currentOutboundChunkId,
|
|
322
|
+
...outboundStream.snapshot(),
|
|
323
|
+
});
|
|
324
|
+
await outboundStream.settle();
|
|
325
|
+
streamDebug("dispatcher_settled_after", {
|
|
326
|
+
currentOutboundMessageId,
|
|
327
|
+
currentOutboundChunkId,
|
|
328
|
+
...outboundStream.snapshot(),
|
|
329
|
+
});
|
|
330
|
+
currentOutboundMessageId = null;
|
|
331
|
+
currentOutboundChunkId = 0;
|
|
332
|
+
markDispatchIdle();
|
|
333
|
+
};
|
|
334
|
+
const runDispatch = () => core.channel.reply.dispatchReplyFromConfig({
|
|
335
|
+
ctx: ctxPayload,
|
|
336
|
+
cfg,
|
|
337
|
+
dispatcher,
|
|
338
|
+
replyOptions: {
|
|
339
|
+
...replyOptions,
|
|
340
|
+
disableBlockStreaming,
|
|
341
|
+
onPartialReply: async (payload) => {
|
|
342
|
+
streamDebug("on_partial_reply", {
|
|
343
|
+
textLen: (payload.text ?? "").length,
|
|
344
|
+
preview: (payload.text ?? "").slice(0, 80),
|
|
345
|
+
});
|
|
346
|
+
await outboundStream.pushPartialSnapshot(payload.text ?? "");
|
|
347
|
+
},
|
|
348
|
+
onAssistantMessageStart: () => {
|
|
349
|
+
streamDebug("on_assistant_message_start");
|
|
350
|
+
},
|
|
351
|
+
onReasoningEnd: () => {
|
|
352
|
+
streamDebug("on_reasoning_end");
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
const withReplyDispatcher = core.channel.reply.withReplyDispatcher;
|
|
357
|
+
const result = typeof withReplyDispatcher === "function"
|
|
358
|
+
? await withReplyDispatcher({
|
|
359
|
+
dispatcher,
|
|
360
|
+
onSettled,
|
|
361
|
+
run: runDispatch,
|
|
362
|
+
})
|
|
363
|
+
: await (async () => {
|
|
364
|
+
try {
|
|
365
|
+
return await runDispatch();
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
await onSettled();
|
|
369
|
+
}
|
|
370
|
+
})();
|
|
371
|
+
log(`weibo[${accountId}]: dispatch complete (queuedFinal=${result.queuedFinal}, replies=${result.counts.final})`);
|
|
372
|
+
}
|
|
373
|
+
catch (err) {
|
|
374
|
+
error(`weibo[${accountId}]: failed to dispatch message: ${String(err)}`);
|
|
375
|
+
}
|
|
376
|
+
// Build and return message context
|
|
377
|
+
const messageContext = {
|
|
378
|
+
messageId,
|
|
379
|
+
senderId: fromUserId,
|
|
380
|
+
text: content,
|
|
381
|
+
createTime: timestamp,
|
|
382
|
+
};
|
|
383
|
+
return messageContext;
|
|
384
|
+
}
|
|
385
|
+
//# sourceMappingURL=bot.js.map
|