openclaw-seatalk 0.3.0 → 0.3.2
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/openclaw.plugin.json +80 -0
- package/package.json +7 -2
- package/src/accounts.ts +5 -2
- package/src/bot.ts +2 -1
- package/src/channel.ts +7 -60
- package/src/monitor.ts +2 -1
- package/src/outbound.ts +1 -1
- package/src/relay-client.ts +29 -1
- package/src/runtime.ts +1 -1
- package/src/tool.ts +1 -1
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,86 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-seatalk",
|
|
3
3
|
"channels": ["seatalk"],
|
|
4
|
+
"channelConfigs": {
|
|
5
|
+
"seatalk": {
|
|
6
|
+
"label": "SeaTalk",
|
|
7
|
+
"description": "SeaTalk internal messaging integration.",
|
|
8
|
+
"schema": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"additionalProperties": false,
|
|
11
|
+
"properties": {
|
|
12
|
+
"enabled": { "type": "boolean" },
|
|
13
|
+
"appId": { "type": "string" },
|
|
14
|
+
"appSecret": { "type": "string" },
|
|
15
|
+
"signingSecret": { "type": "string" },
|
|
16
|
+
"mode": { "type": "string", "enum": ["webhook", "relay"] },
|
|
17
|
+
"relayUrl": { "type": "string" },
|
|
18
|
+
"webhookPort": { "type": "integer", "minimum": 1 },
|
|
19
|
+
"webhookPath": { "type": "string" },
|
|
20
|
+
"dmPolicy": { "type": "string", "enum": ["open", "allowlist", "pairing"] },
|
|
21
|
+
"allowFrom": { "type": "array", "items": { "type": "string" } },
|
|
22
|
+
"groupPolicy": { "type": "string", "enum": ["disabled", "allowlist", "open"] },
|
|
23
|
+
"groupAllowFrom": { "type": "array", "items": { "type": "string" } },
|
|
24
|
+
"groupSenderAllowFrom": { "type": "array", "items": { "type": "string" } },
|
|
25
|
+
"outboundCoalescing": { "type": "boolean" },
|
|
26
|
+
"processingIndicator": { "type": "string", "enum": ["typing", "off"] },
|
|
27
|
+
"mediaAllowHosts": { "type": "array", "items": { "type": "string" } },
|
|
28
|
+
"tools": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"groupInfo": { "type": "boolean" },
|
|
32
|
+
"groupHistory": { "type": "boolean" },
|
|
33
|
+
"groupList": { "type": "boolean" },
|
|
34
|
+
"threadHistory": { "type": "boolean" },
|
|
35
|
+
"getMessage": { "type": "boolean" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"accounts": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"additionalProperties": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {
|
|
43
|
+
"enabled": { "type": "boolean" },
|
|
44
|
+
"appId": { "type": "string" },
|
|
45
|
+
"appSecret": { "type": "string" },
|
|
46
|
+
"signingSecret": { "type": "string" },
|
|
47
|
+
"mode": { "type": "string", "enum": ["webhook", "relay"] },
|
|
48
|
+
"relayUrl": { "type": "string" },
|
|
49
|
+
"webhookPort": { "type": "integer", "minimum": 1 },
|
|
50
|
+
"webhookPath": { "type": "string" },
|
|
51
|
+
"dmPolicy": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": ["open", "allowlist", "pairing"]
|
|
54
|
+
},
|
|
55
|
+
"allowFrom": { "type": "array", "items": { "type": "string" } },
|
|
56
|
+
"groupPolicy": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["disabled", "allowlist", "open"]
|
|
59
|
+
},
|
|
60
|
+
"groupAllowFrom": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "type": "string" }
|
|
63
|
+
},
|
|
64
|
+
"groupSenderAllowFrom": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"items": { "type": "string" }
|
|
67
|
+
},
|
|
68
|
+
"outboundCoalescing": { "type": "boolean" },
|
|
69
|
+
"processingIndicator": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"enum": ["typing", "off"]
|
|
72
|
+
},
|
|
73
|
+
"mediaAllowHosts": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": { "type": "string" }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
4
84
|
"configSchema": {
|
|
5
85
|
"type": "object",
|
|
6
86
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-seatalk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "OpenClaw SeaTalk channel plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"lint": "biome lint .",
|
|
22
22
|
"lint:fix": "biome lint --fix .",
|
|
23
23
|
"check": "biome check .",
|
|
24
|
-
"check:fix": "biome check --fix ."
|
|
24
|
+
"check:fix": "biome check --fix .",
|
|
25
|
+
"pack:clawhub": "mkdir -p dist && npm pack --pack-destination dist"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
28
|
"@sinclair/typebox": "0.34.48",
|
|
@@ -60,6 +61,10 @@
|
|
|
60
61
|
},
|
|
61
62
|
"compat": {
|
|
62
63
|
"pluginApi": ">=2026.3.22"
|
|
64
|
+
},
|
|
65
|
+
"build": {
|
|
66
|
+
"openclawVersion": "2026.3.22",
|
|
67
|
+
"pluginSdkVersion": "2026.3.22"
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
}
|
package/src/accounts.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_ACCOUNT_ID,
|
|
3
|
+
type OpenClawConfig,
|
|
4
|
+
normalizeAccountId,
|
|
5
|
+
} from "openclaw/plugin-sdk/core";
|
|
3
6
|
import type { ResolvedSeaTalkAccount, SeaTalkAccountConfig, SeaTalkConfig } from "./types.js";
|
|
4
7
|
|
|
5
8
|
function listConfiguredAccountIds(cfg: OpenClawConfig): string[] {
|
package/src/bot.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
1
|
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
|
3
2
|
import {
|
|
4
3
|
DM_GROUP_ACCESS_REASON,
|
|
5
4
|
resolveDmGroupAccessWithLists,
|
|
6
5
|
} from "openclaw/plugin-sdk/channel-policy";
|
|
6
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
7
7
|
import { resolveSendableOutboundReplyParts } from "openclaw/plugin-sdk/reply-payload";
|
|
8
|
+
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime";
|
|
8
9
|
import { checkGroupAccess } from "./access.js";
|
|
9
10
|
import { resolveSeaTalkAccount } from "./accounts.js";
|
|
10
11
|
import type { SeaTalkClient } from "./client.js";
|
package/src/channel.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
1
|
import { createPairingPrefixStripper } from "openclaw/plugin-sdk/channel-pairing";
|
|
2
|
+
import {
|
|
3
|
+
type ChannelPlugin,
|
|
4
|
+
type OpenClawConfig,
|
|
5
|
+
buildChannelConfigSchema,
|
|
6
|
+
} from "openclaw/plugin-sdk/core";
|
|
3
7
|
import { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk/core";
|
|
4
8
|
import {
|
|
5
9
|
listSeaTalkAccountIds,
|
|
@@ -7,6 +11,7 @@ import {
|
|
|
7
11
|
resolveSeaTalkAccount,
|
|
8
12
|
} from "./accounts.js";
|
|
9
13
|
import { resolveSeaTalkClient } from "./client.js";
|
|
14
|
+
import { SeaTalkConfigSchema } from "./config-schema.js";
|
|
10
15
|
import { seatalkOutbound } from "./outbound.js";
|
|
11
16
|
import { probeSeaTalk } from "./probe.js";
|
|
12
17
|
import { sendTextMessage } from "./send.js";
|
|
@@ -49,65 +54,7 @@ export const seatalkPlugin: ChannelPlugin<ResolvedSeaTalkAccount> = {
|
|
|
49
54
|
reply: false,
|
|
50
55
|
},
|
|
51
56
|
reload: { configPrefixes: ["channels.seatalk"] },
|
|
52
|
-
configSchema:
|
|
53
|
-
schema: {
|
|
54
|
-
type: "object",
|
|
55
|
-
additionalProperties: false,
|
|
56
|
-
properties: {
|
|
57
|
-
enabled: { type: "boolean" },
|
|
58
|
-
appId: { type: "string" },
|
|
59
|
-
appSecret: { type: "string" },
|
|
60
|
-
signingSecret: { type: "string" },
|
|
61
|
-
mode: { type: "string", enum: ["webhook", "relay"] },
|
|
62
|
-
relayUrl: { type: "string" },
|
|
63
|
-
webhookPort: { type: "integer", minimum: 1 },
|
|
64
|
-
webhookPath: { type: "string" },
|
|
65
|
-
dmPolicy: { type: "string", enum: ["open", "allowlist", "pairing"] },
|
|
66
|
-
allowFrom: { type: "array", items: { type: "string" } },
|
|
67
|
-
groupPolicy: { type: "string", enum: ["disabled", "allowlist", "open"] },
|
|
68
|
-
groupAllowFrom: { type: "array", items: { type: "string" } },
|
|
69
|
-
groupSenderAllowFrom: { type: "array", items: { type: "string" } },
|
|
70
|
-
processingIndicator: { type: "string", enum: ["typing", "off"] },
|
|
71
|
-
mediaAllowHosts: { type: "array", items: { type: "string" } },
|
|
72
|
-
tools: {
|
|
73
|
-
type: "object",
|
|
74
|
-
properties: {
|
|
75
|
-
groupInfo: { type: "boolean" },
|
|
76
|
-
groupHistory: { type: "boolean" },
|
|
77
|
-
groupList: { type: "boolean" },
|
|
78
|
-
threadHistory: { type: "boolean" },
|
|
79
|
-
getMessage: { type: "boolean" },
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
accounts: {
|
|
83
|
-
type: "object",
|
|
84
|
-
additionalProperties: {
|
|
85
|
-
type: "object",
|
|
86
|
-
properties: {
|
|
87
|
-
enabled: { type: "boolean" },
|
|
88
|
-
appId: { type: "string" },
|
|
89
|
-
appSecret: { type: "string" },
|
|
90
|
-
signingSecret: { type: "string" },
|
|
91
|
-
mode: { type: "string", enum: ["webhook", "relay"] },
|
|
92
|
-
relayUrl: { type: "string" },
|
|
93
|
-
webhookPort: { type: "integer", minimum: 1 },
|
|
94
|
-
webhookPath: { type: "string" },
|
|
95
|
-
dmPolicy: { type: "string", enum: ["open", "allowlist", "pairing"] },
|
|
96
|
-
allowFrom: { type: "array", items: { type: "string" } },
|
|
97
|
-
groupPolicy: {
|
|
98
|
-
type: "string",
|
|
99
|
-
enum: ["disabled", "allowlist", "open"],
|
|
100
|
-
},
|
|
101
|
-
groupAllowFrom: { type: "array", items: { type: "string" } },
|
|
102
|
-
groupSenderAllowFrom: { type: "array", items: { type: "string" } },
|
|
103
|
-
processingIndicator: { type: "string", enum: ["typing", "off"] },
|
|
104
|
-
mediaAllowHosts: { type: "array", items: { type: "string" } },
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
},
|
|
57
|
+
configSchema: buildChannelConfigSchema(SeaTalkConfigSchema),
|
|
111
58
|
config: {
|
|
112
59
|
listAccountIds: (cfg) => listSeaTalkAccountIds(cfg),
|
|
113
60
|
resolveAccount: (cfg, accountId) => resolveSeaTalkAccount({ cfg, accountId }),
|
package/src/monitor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import * as http from "node:http";
|
|
3
|
-
import type { OpenClawConfig
|
|
3
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
4
|
+
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime";
|
|
4
5
|
import {
|
|
5
6
|
listEnabledSeaTalkAccounts,
|
|
6
7
|
resolveSeaTalkAccount,
|
package/src/outbound.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
2
1
|
import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk/channel-send-result";
|
|
2
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
3
3
|
import { resolveSeaTalkAccount } from "./accounts.js";
|
|
4
4
|
import { type SeaTalkClient, resolveSeaTalkClient } from "./client.js";
|
|
5
5
|
import { getSeatalkRuntime } from "./runtime.js";
|
package/src/relay-client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { OpenClawConfig
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
2
|
+
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime";
|
|
2
3
|
import WebSocket from "ws";
|
|
3
4
|
import {
|
|
4
5
|
listEnabledSeaTalkAccounts,
|
|
@@ -13,6 +14,7 @@ import type { ResolvedSeaTalkAccount, SeaTalkCallbackRequest } from "./types.js"
|
|
|
13
14
|
const INITIAL_BACKOFF_MS = 1_000;
|
|
14
15
|
const MAX_BACKOFF_MS = 30_000;
|
|
15
16
|
const BACKOFF_MULTIPLIER = 2;
|
|
17
|
+
const STALE_TIMEOUT_MS = 75_000;
|
|
16
18
|
|
|
17
19
|
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
18
20
|
return new Promise((resolve) => {
|
|
@@ -63,13 +65,36 @@ async function connectSingleAccount(params: {
|
|
|
63
65
|
log(`seatalk[${accountId}]: connecting to relay ${relayUrl}...`);
|
|
64
66
|
const ws = new WebSocket(relayUrl);
|
|
65
67
|
|
|
68
|
+
let staleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
69
|
+
const clearStaleTimer = () => {
|
|
70
|
+
if (staleTimer) {
|
|
71
|
+
clearTimeout(staleTimer);
|
|
72
|
+
staleTimer = undefined;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const armStaleTimer = () => {
|
|
76
|
+
clearStaleTimer();
|
|
77
|
+
staleTimer = setTimeout(() => {
|
|
78
|
+
error(
|
|
79
|
+
`seatalk[${accountId}]: relay silent for ${STALE_TIMEOUT_MS}ms, terminating`,
|
|
80
|
+
);
|
|
81
|
+
ws.terminate();
|
|
82
|
+
}, STALE_TIMEOUT_MS);
|
|
83
|
+
};
|
|
84
|
+
|
|
66
85
|
const handleAbort = () => {
|
|
86
|
+
clearStaleTimer();
|
|
67
87
|
ws.close();
|
|
68
88
|
resolve();
|
|
69
89
|
};
|
|
70
90
|
abortSignal?.addEventListener("abort", handleAbort, { once: true });
|
|
71
91
|
|
|
92
|
+
ws.on("upgrade", (response) => {
|
|
93
|
+
response.socket.setKeepAlive(true, 60_000);
|
|
94
|
+
});
|
|
95
|
+
|
|
72
96
|
ws.on("open", () => {
|
|
97
|
+
armStaleTimer();
|
|
73
98
|
log(`seatalk[${accountId}]: relay connected, authenticating...`);
|
|
74
99
|
ws.send(
|
|
75
100
|
JSON.stringify({
|
|
@@ -84,6 +109,7 @@ async function connectSingleAccount(params: {
|
|
|
84
109
|
let authenticated = false;
|
|
85
110
|
|
|
86
111
|
ws.on("message", (raw) => {
|
|
112
|
+
armStaleTimer();
|
|
87
113
|
let msg: { type: string; event?: SeaTalkCallbackRequest; error?: string };
|
|
88
114
|
try {
|
|
89
115
|
msg = JSON.parse(String(raw));
|
|
@@ -131,6 +157,7 @@ async function connectSingleAccount(params: {
|
|
|
131
157
|
});
|
|
132
158
|
|
|
133
159
|
ws.on("close", (code, reason) => {
|
|
160
|
+
clearStaleTimer();
|
|
134
161
|
abortSignal?.removeEventListener("abort", handleAbort);
|
|
135
162
|
if (authenticated) {
|
|
136
163
|
log(
|
|
@@ -141,6 +168,7 @@ async function connectSingleAccount(params: {
|
|
|
141
168
|
});
|
|
142
169
|
|
|
143
170
|
ws.on("error", (err) => {
|
|
171
|
+
clearStaleTimer();
|
|
144
172
|
abortSignal?.removeEventListener("abort", handleAbort);
|
|
145
173
|
error(`seatalk[${accountId}]: relay connection error: ${String(err)}`);
|
|
146
174
|
resolve();
|
package/src/runtime.ts
CHANGED
package/src/tool.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
2
2
|
import { listEnabledSeaTalkAccounts, resolveSeaTalkAccount } from "./accounts.js";
|
|
3
3
|
import type { SeaTalkClient } from "./client.js";
|
|
4
4
|
import { resolveSeaTalkClient } from "./client.js";
|