openclaw-seatalk 0.2.1 → 0.3.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/README.md CHANGED
@@ -26,8 +26,9 @@ OpenClaw channel plugin for [SeaTalk](https://seatalk.io/) messaging.
26
26
  - **Dual gateway mode** — **webhook** (direct HTTP server) or **relay** (WebSocket client via [seatalk-relay](https://github.com/lf4096/seatalk-relay))
27
27
  - **Security** — SHA256 signature verification for all incoming events
28
28
  - **Token management** — automatic access token obtain, cache, and refresh
29
+ - **Outbound coalescing** — consecutive reply payloads are merged into a single message with automatic markdown-aware chunking at 4000 chars; configurable via `outboundCoalescing`
29
30
  - **Deduplication** — event ID dedup + per-sender debounce buffer (thread-aware)
30
- - **Access control** — DM policy (`open`/`allowlist`), group policy (`disabled`/`allowlist`/`open`), per-group and per-sender allow-lists
31
+ - **Access control** — DM policy (`open`/`allowlist`/`pairing`), group policy (`disabled`/`allowlist`/`open`), per-group and per-sender allow-lists
31
32
  - **Email resolution** — email-to-employee_code lookup for outbound message targets
32
33
  - **Multi-account** — multiple SeaTalk bot apps in one OpenClaw instance
33
34
  - **Health probing** — connection health check on startup
@@ -149,8 +150,8 @@ Or edit the OpenClaw config file directly (`~/.openclaw/openclaw.json`).
149
150
  signingSecret: "your_signing_secret",
150
151
  webhookPort: 3210,
151
152
  webhookPath: "/callback",
152
- dmPolicy: "open", // or "allowlist"
153
- // allowFrom: ["12345678", "alice@company.com"],
153
+ dmPolicy: "open", // or "allowlist" | "pairing"
154
+ // allowFrom: ["e_12345678", "alice@company.com"],
154
155
  },
155
156
  },
156
157
  }
@@ -197,12 +198,14 @@ Or edit the OpenClaw config file directly (`~/.openclaw/openclaw.json`).
197
198
  | `webhookPort` | number | `8080` | HTTP port (webhook mode only) |
198
199
  | `webhookPath` | string | `"/callback"` | HTTP path (webhook mode only) |
199
200
  | `relayUrl` | string | — | WebSocket URL (relay mode only) |
200
- | `dmPolicy` | `"open"` \| `"allowlist"` | `"allowlist"` | Who can DM the bot |
201
+ | `dmPolicy` | `"open"` \| `"allowlist"` \| `"pairing"` | `"allowlist"` | Who can DM the bot (`pairing`: approve via CLI) |
201
202
  | `allowFrom` | string[] | — | Allowed DM senders (employee codes or emails) |
202
203
  | `groupPolicy` | `"disabled"` \| `"allowlist"` \| `"open"` | `"disabled"` | Group chat policy |
203
204
  | `groupAllowFrom` | string[] | — | Allowed group IDs (when `groupPolicy: "allowlist"`) |
204
205
  | `groupSenderAllowFrom` | string[] | — | Allowed senders within groups (employee codes or emails) |
206
+ | `outboundCoalescing` | boolean | `true` | Merge consecutive reply payloads into a single message (4000-char chunking) |
205
207
  | `processingIndicator` | `"typing"` \| `"off"` | `"typing"` | Show typing status while processing |
208
+ | `mediaAllowHosts` | string[] | `["openapi.seatalk.io"]` | Allowed hostnames for inbound media downloads (HTTPS only) |
206
209
  | `tools.groupInfo` | boolean | `true` | Enable `seatalk` tool `group_info` action |
207
210
  | `tools.groupHistory` | boolean | `true` | Enable `seatalk` tool `group_history` action |
208
211
  | `tools.groupList` | boolean | `true` | Enable `seatalk` tool `group_list` action |
@@ -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.2.1",
3
+ "version": "0.3.1",
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/access.ts ADDED
@@ -0,0 +1,46 @@
1
+ export function checkGroupAccess(params: {
2
+ groupPolicy: string;
3
+ groupAllowFrom?: string[];
4
+ groupSenderAllowFrom?: string[];
5
+ groupId: string;
6
+ senderEmployeeCode: string;
7
+ senderEmail?: string;
8
+ }): { allowed: boolean; reason?: string } {
9
+ const {
10
+ groupPolicy,
11
+ groupAllowFrom,
12
+ groupSenderAllowFrom,
13
+ groupId,
14
+ senderEmployeeCode,
15
+ senderEmail,
16
+ } = params;
17
+
18
+ if (groupPolicy === "disabled") {
19
+ return { allowed: false, reason: "groupPolicy is disabled" };
20
+ }
21
+
22
+ if (groupPolicy === "allowlist") {
23
+ const list = groupAllowFrom ?? [];
24
+ if (!list.includes(groupId)) {
25
+ return { allowed: false, reason: `group ${groupId} not in groupAllowFrom` };
26
+ }
27
+ }
28
+
29
+ if (groupSenderAllowFrom && groupSenderAllowFrom.length > 0) {
30
+ const match = groupSenderAllowFrom.some((entry) => {
31
+ const e = entry.trim();
32
+ if (e === "*") return true;
33
+ if (e === senderEmployeeCode) return true;
34
+ if (senderEmail && e.toLowerCase() === senderEmail.toLowerCase()) return true;
35
+ return false;
36
+ });
37
+ if (!match) {
38
+ return {
39
+ allowed: false,
40
+ reason: `sender ${senderEmployeeCode} not in groupSenderAllowFrom`,
41
+ };
42
+ }
43
+ }
44
+
45
+ return { allowed: true };
46
+ }
package/src/accounts.ts CHANGED
@@ -1,5 +1,8 @@
1
- import type { OpenClawConfig } from "openclaw/plugin-sdk";
2
- import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/core";
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[] {