@lansenger-pm/openclaw-lansenger-channel 3.1.9 → 3.2.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.js CHANGED
@@ -1,28 +1,14 @@
1
1
  import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
2
2
  import { lansengerPlugin } from "./src/channel.js";
3
3
  import { startLansengerGateway } from "./src/runtime.js";
4
- import { registerLansengerTools } from "./src/tools.js";
4
+ export { resolveAccount, makeClient } from "./src/channel.js";
5
+ export { getRunningClient, getRunningAccount, getLastInboundChatId } from "./src/runtime.js";
6
+ export { LansengerClient } from "./src/client.js";
5
7
  export default defineChannelPluginEntry({
6
8
  id: "lansenger",
7
9
  name: "Lansenger (蓝信)",
8
10
  description: "Lansenger enterprise messaging channel plugin for OpenClaw",
9
11
  plugin: lansengerPlugin,
10
- registerCliMetadata(api) {
11
- api.registerCli(({ program }) => {
12
- program.command("lansenger").description("Lansenger (蓝信) management");
13
- }, {
14
- descriptors: [
15
- {
16
- name: "lansenger",
17
- description: "Lansenger (蓝信) management",
18
- hasSubcommands: false,
19
- },
20
- ],
21
- });
22
- if (api.registrationMode === "full") {
23
- registerLansengerTools(api);
24
- }
25
- },
26
12
  registerFull(api) {
27
13
  startLansengerGateway(api);
28
14
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAExD,eAAe,wBAAwB,CAAC;IACtC,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,4DAA4D;IACzE,MAAM,EAAE,eAAe;IACvB,mBAAmB,CAAC,GAAG;QACrB,GAAG,CAAC,WAAW,CACb,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;YACd,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;QACxE,CAAC,EACD;YACE,WAAW,EAAE;gBACX;oBACE,IAAI,EAAE,WAAW;oBACjB,WAAW,EAAE,2BAA2B;oBACxC,cAAc,EAAE,KAAK;iBACtB;aACF;SACF,CACF,CAAC;QACF,IAAI,GAAG,CAAC,gBAAgB,KAAK,MAAM,EAAE,CAAC;YACpC,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,YAAY,CAAC,GAAG;QACd,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,eAAe,wBAAwB,CAAC;IACtC,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE,4DAA4D;IACzE,MAAM,EAAE,eAAe;IACvB,YAAY,CAAC,GAAG;QACd,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,407 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { LansengerClient, mediaTypeFromPath, buildI18n } from "./client.js";
3
+ function makeClient() {
4
+ return new LansengerClient({ appId: "test-id", appSecret: "test-secret" });
5
+ }
6
+ function jsonResp(data) {
7
+ const body = JSON.stringify(data);
8
+ return new Response(body, { status: 200, headers: { "content-type": "application/json" } });
9
+ }
10
+ function successApi(data = {}) {
11
+ return jsonResp({ errCode: 0, errMsg: "", data });
12
+ }
13
+ function errorApi(errCode, errMsg) {
14
+ return jsonResp({ errCode, errMsg, data: null });
15
+ }
16
+ describe("convertPxToPt", () => {
17
+ it("converts 16px to 12pt (clamped min)", () => {
18
+ const result = "font-size:16px text".replace(/font-size:\s*(\d+(?:\.\d+)?)px/gi, (_m, n) => `font-size:${Math.max(12, Math.min(36, Math.round(parseFloat(n) * 0.75)))}pt`);
19
+ expect(result).toBe("font-size:12pt text");
20
+ });
21
+ it("0px clamps to 12pt minimum", () => {
22
+ const result = "font-size:0px".replace(/font-size:\s*(\d+(?:\.\d+)?)px/gi, (_m, n) => `font-size:${Math.max(12, Math.min(36, Math.round(parseFloat(n) * 0.75)))}pt`);
23
+ expect(result).toBe("font-size:12pt");
24
+ });
25
+ it("100px clamps to 36pt maximum", () => {
26
+ const result = "font-size:100px".replace(/font-size:\s*(\d+(?:\.\d+)?)px/gi, (_m, n) => `font-size:${Math.max(12, Math.min(36, Math.round(parseFloat(n) * 0.75)))}pt`);
27
+ expect(result).toBe("font-size:36pt");
28
+ });
29
+ it("20px converts to 15pt", () => {
30
+ const result = "font-size:20px".replace(/font-size:\s*(\d+(?:\.\d+)?)px/gi, (_m, n) => `font-size:${Math.max(12, Math.min(36, Math.round(parseFloat(n) * 0.75)))}pt`);
31
+ expect(result).toBe("font-size:15pt");
32
+ });
33
+ it("no px values returns unchanged", () => {
34
+ const input = "font-size:12pt text";
35
+ const result = input.replace(/font-size:\s*(\d+(?:\.\d+)?)px/gi, (_m, n) => `font-size:${Math.max(12, Math.min(36, Math.round(parseFloat(n) * 0.75)))}pt`);
36
+ expect(result).toBe(input);
37
+ });
38
+ });
39
+ describe("mediaTypeFromPath", () => {
40
+ it("detects image extensions", () => {
41
+ expect(mediaTypeFromPath("photo.jpg")).toBe(2);
42
+ expect(mediaTypeFromPath("photo.jpeg")).toBe(2);
43
+ expect(mediaTypeFromPath("photo.png")).toBe(2);
44
+ expect(mediaTypeFromPath("photo.gif")).toBe(2);
45
+ expect(mediaTypeFromPath("photo.webp")).toBe(2);
46
+ });
47
+ it("detects video extensions", () => {
48
+ expect(mediaTypeFromPath("clip.mp4")).toBe(1);
49
+ expect(mediaTypeFromPath("clip.mov")).toBe(1);
50
+ expect(mediaTypeFromPath("clip.avi")).toBe(1);
51
+ });
52
+ it("detects file extensions", () => {
53
+ expect(mediaTypeFromPath("doc.pdf")).toBe(3);
54
+ expect(mediaTypeFromPath("data.xlsx")).toBe(3);
55
+ });
56
+ it("unknown extension returns file type", () => {
57
+ expect(mediaTypeFromPath("file.xyz")).toBe(3);
58
+ expect(mediaTypeFromPath("noext")).toBe(3);
59
+ });
60
+ it("uppercase extension normalized", () => {
61
+ expect(mediaTypeFromPath("photo.JPG")).toBe(2);
62
+ expect(mediaTypeFromPath("clip.MP4")).toBe(1);
63
+ });
64
+ });
65
+ describe("buildI18n", () => {
66
+ it("creates i18n object with all languages", () => {
67
+ const obj = buildI18n("简体", "繁体", "港繁", "English", "Français");
68
+ expect(obj.zhHans).toBe("简体");
69
+ expect(obj.zhHant).toBe("繁体");
70
+ expect(obj.zhHantHK).toBe("港繁");
71
+ expect(obj.en).toBe("English");
72
+ expect(obj.fr).toBe("Français");
73
+ });
74
+ });
75
+ describe("LansengerClient.detectLang", () => {
76
+ it("pure CJK text returns zh", () => {
77
+ const client = makeClient();
78
+ expect(client.detectLang("你好世界")).toBe("zh");
79
+ });
80
+ it("pure English text returns en", () => {
81
+ const client = makeClient();
82
+ expect(client.detectLang("Hello world")).toBe("en");
83
+ });
84
+ it("mixed text above threshold returns zh", () => {
85
+ const client = makeClient();
86
+ expect(client.detectLang("你好你好你好你好你好你")).toBe("zh");
87
+ });
88
+ it("mixed text below threshold returns en", () => {
89
+ const client = makeClient();
90
+ expect(client.detectLang("你a")).toBe("en");
91
+ });
92
+ it("empty string returns en", () => {
93
+ const client = makeClient();
94
+ expect(client.detectLang("")).toBe("en");
95
+ });
96
+ });
97
+ describe("LansengerClient.chatType caching", () => {
98
+ it("cache and retrieve group type", () => {
99
+ const client = makeClient();
100
+ client.cacheChatType("group-1", "group");
101
+ expect(client.getChatType("group-1")).toBe("group");
102
+ });
103
+ it("cache and retrieve dm type", () => {
104
+ const client = makeClient();
105
+ client.cacheChatType("user-1", "dm");
106
+ expect(client.getChatType("user-1")).toBe("dm");
107
+ });
108
+ it("unknown chatId returns undefined", () => {
109
+ const client = makeClient();
110
+ expect(client.getChatType("unknown")).toBe(undefined);
111
+ });
112
+ it("isGroupChat with cached group type", () => {
113
+ const client = makeClient();
114
+ client.cacheChatType("group-1", "group");
115
+ expect(client.isGroupChat("group-1")).toBe(true);
116
+ });
117
+ it("isGroupChat with group: prefix", () => {
118
+ const client = makeClient();
119
+ expect(client.isGroupChat("group:some-id")).toBe(true);
120
+ });
121
+ it("isGroupChat with dm type returns false", () => {
122
+ const client = makeClient();
123
+ client.cacheChatType("user-1", "dm");
124
+ expect(client.isGroupChat("user-1")).toBe(false);
125
+ });
126
+ });
127
+ describe("LansengerClient.userLang caching", () => {
128
+ it("cache and retrieve user lang", () => {
129
+ const client = makeClient();
130
+ client.cacheUserLang("user-1", "你好");
131
+ expect(client.getUserLang("user-1")).toBe("zh");
132
+ client.cacheUserLang("user-2", "Hello");
133
+ expect(client.getUserLang("user-2")).toBe("en");
134
+ });
135
+ it("unknown userId defaults to zh", () => {
136
+ const client = makeClient();
137
+ expect(client.getUserLang("unknown")).toBe("zh");
138
+ });
139
+ });
140
+ describe("LansengerClient.revokeMessage validation", () => {
141
+ it("rejects invalid chatType", async () => {
142
+ vi.stubGlobal("fetch", async (url) => {
143
+ const u = typeof url === "string" ? url : url.url;
144
+ if (u.includes("apptoken"))
145
+ return successApi({ appToken: "tok", expiresIn: 7200 });
146
+ return successApi({});
147
+ });
148
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
149
+ const result = await client.revokeMessage(["m1"], "staff");
150
+ expect(result.success).toBe(false);
151
+ expect(result.error).toContain("chatType must be");
152
+ vi.restoreAllMocks();
153
+ });
154
+ it("rejects group chatType without senderId", async () => {
155
+ vi.stubGlobal("fetch", async (url) => {
156
+ const u = typeof url === "string" ? url : url.url;
157
+ if (u.includes("apptoken"))
158
+ return successApi({ appToken: "tok", expiresIn: 7200 });
159
+ return successApi({});
160
+ });
161
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
162
+ const result = await client.revokeMessage(["m1"], "group");
163
+ expect(result.success).toBe(false);
164
+ expect(result.error).toContain("requires senderId");
165
+ vi.restoreAllMocks();
166
+ });
167
+ });
168
+ describe("LansengerClient with mocked fetch", () => {
169
+ beforeEach(() => {
170
+ vi.stubGlobal("fetch", async (url, init) => {
171
+ const u = typeof url === "string" ? url : url.url;
172
+ if (u.includes("apptoken")) {
173
+ return successApi({ appToken: "mock-token", expiresIn: 7200 });
174
+ }
175
+ if (u.includes("messages/create") || u.includes("messages/revoke") || u.includes("messages/dynamic/update")) {
176
+ return successApi({ msgId: "mock-msg-id" });
177
+ }
178
+ if (u.includes("groups/fetch")) {
179
+ return successApi({ totalGroupIds: 5, groupIds: ["g1", "g2"] });
180
+ }
181
+ return successApi({});
182
+ });
183
+ });
184
+ afterEach(() => {
185
+ vi.restoreAllMocks();
186
+ });
187
+ it("sendFormatText succeeds without reminder", async () => {
188
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
189
+ const result = await client.sendFormatText("chat1", "hello");
190
+ expect(result.success).toBe(true);
191
+ expect(result.messageId).toBe("mock-msg-id");
192
+ });
193
+ it("sendFormatText retries without reminder on failure with reminder", async () => {
194
+ let msgCallCount = 0;
195
+ vi.stubGlobal("fetch", async (url, init) => {
196
+ const u = typeof url === "string" ? url : url.url;
197
+ if (u.includes("apptoken"))
198
+ return successApi({ appToken: "tok", expiresIn: 7200 });
199
+ msgCallCount++;
200
+ if (msgCallCount === 1)
201
+ return errorApi(40001, "reminder error");
202
+ return successApi({ msgId: "m2" });
203
+ });
204
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
205
+ const result = await client.sendFormatText("chat1", "hello", { all: true });
206
+ expect(result.success).toBe(true);
207
+ expect(msgCallCount).toBe(2);
208
+ });
209
+ it("sendFormatText returns error when both attempts fail", async () => {
210
+ let msgCallCount = 0;
211
+ vi.stubGlobal("fetch", async (url, init) => {
212
+ const u = typeof url === "string" ? url : url.url;
213
+ if (u.includes("apptoken"))
214
+ return successApi({ appToken: "tok", expiresIn: 7200 });
215
+ msgCallCount++;
216
+ return errorApi(40001, "error");
217
+ });
218
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
219
+ const result = await client.sendFormatText("chat1", "hello", { all: true });
220
+ expect(result.success).toBe(false);
221
+ expect(msgCallCount).toBe(2);
222
+ });
223
+ it("sendFormatText no retry without reminder", async () => {
224
+ let msgCallCount = 0;
225
+ vi.stubGlobal("fetch", async (url, init) => {
226
+ const u = typeof url === "string" ? url : url.url;
227
+ if (u.includes("apptoken"))
228
+ return successApi({ appToken: "tok", expiresIn: 7200 });
229
+ msgCallCount++;
230
+ return errorApi(40001, "error");
231
+ });
232
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
233
+ const result = await client.sendFormatText("chat1", "hello");
234
+ expect(result.success).toBe(false);
235
+ expect(msgCallCount).toBe(1);
236
+ });
237
+ it("sendAppCard dynamic without headStatusInfo auto-adds default", async () => {
238
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
239
+ const card = { bodyTitle: "Approval", isDynamic: true };
240
+ const result = await client.sendAppCard("chat1", card);
241
+ expect(result.success).toBe(true);
242
+ });
243
+ it("sendAppCard dynamic with headStatusInfo keeps provided", async () => {
244
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
245
+ const card = { bodyTitle: "Approval", isDynamic: true, headStatusInfo: { description: "Custom", colour: "#FF0000" } };
246
+ const result = await client.sendAppCard("chat1", card);
247
+ expect(result.success).toBe(true);
248
+ });
249
+ it("sendAppCard non-dynamic no headStatusInfo addition", async () => {
250
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
251
+ const card = { bodyTitle: "Info card" };
252
+ const result = await client.sendAppCard("chat1", card);
253
+ expect(result.success).toBe(true);
254
+ });
255
+ it("sendLinkCard succeeds with defaults", async () => {
256
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
257
+ const result = await client.sendLinkCard("chat1", "Title", "https://link", { description: "desc" });
258
+ expect(result.success).toBe(true);
259
+ });
260
+ it("sendAppArticles succeeds with defaults", async () => {
261
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
262
+ const articles = [{ imgUrl: "https://img", title: "T", url: "https://u" }];
263
+ const result = await client.sendAppArticles("chat1", articles);
264
+ expect(result.success).toBe(true);
265
+ });
266
+ it("updateCardStatus pending zh succeeds", async () => {
267
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
268
+ const result = await client.updateCardStatus("m1", "pending", "zh");
269
+ expect(result.success).toBe(true);
270
+ });
271
+ it("updateCardStatus approved en succeeds", async () => {
272
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
273
+ const result = await client.updateCardStatus("m1", "approved", "en");
274
+ expect(result.success).toBe(true);
275
+ });
276
+ it("updateCardStatus denied defaults zh", async () => {
277
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
278
+ const result = await client.updateCardStatus("m1", "denied");
279
+ expect(result.success).toBe(true);
280
+ });
281
+ it("queryGroups succeeds", async () => {
282
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
283
+ const result = await client.queryGroups();
284
+ expect(result).toEqual({ totalGroupIds: 5, groupIds: ["g1", "g2"] });
285
+ });
286
+ it("queryGroups defaults 0/[] on null data", async () => {
287
+ vi.stubGlobal("fetch", async (url, init) => {
288
+ const u = typeof url === "string" ? url : url.url;
289
+ if (u.includes("apptoken"))
290
+ return successApi({ appToken: "tok", expiresIn: 7200 });
291
+ return successApi(null);
292
+ });
293
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
294
+ const result = await client.queryGroups();
295
+ expect(result).toEqual({ totalGroupIds: 0, groupIds: [] });
296
+ });
297
+ });
298
+ describe("LansengerClient.getAppToken", () => {
299
+ afterEach(() => { vi.restoreAllMocks(); });
300
+ it("returns null on HTTP error", async () => {
301
+ vi.stubGlobal("fetch", async () => new Response("Unauthorized", { status: 401 }));
302
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
303
+ const token = await client.getAppToken();
304
+ expect(token).toBeNull();
305
+ });
306
+ it("returns null on API error code", async () => {
307
+ vi.stubGlobal("fetch", async () => jsonResp({ errCode: 40001, errMsg: "bad", data: null }));
308
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
309
+ const token = await client.getAppToken();
310
+ expect(token).toBeNull();
311
+ });
312
+ it("returns token on success", async () => {
313
+ vi.stubGlobal("fetch", async () => successApi({ appToken: "tok123", expiresIn: 7200 }));
314
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
315
+ const token = await client.getAppToken();
316
+ expect(token).toBe("tok123");
317
+ });
318
+ it("caches token and skips refresh", async () => {
319
+ let fetchCalls = 0;
320
+ vi.stubGlobal("fetch", async () => { fetchCalls++; return successApi({ appToken: "cached-tok", expiresIn: 7200 }); });
321
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
322
+ const t1 = await client.getAppToken();
323
+ const t2 = await client.getAppToken();
324
+ expect(t1).toBe("cached-tok");
325
+ expect(t2).toBe("cached-tok");
326
+ expect(fetchCalls).toBe(1);
327
+ });
328
+ });
329
+ describe("LansengerClient.sendImageUrl error classification", () => {
330
+ afterEach(() => { vi.restoreAllMocks(); });
331
+ it("HTTP 404 returns specific error", async () => {
332
+ vi.stubGlobal("fetch", async () => new Response("Not Found", { status: 404, headers: { "content-type": "text/plain" } }));
333
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
334
+ const result = await client.sendImageUrl("chat1", "https://example.com/missing.jpg");
335
+ expect(result.success).toBe(false);
336
+ expect(result.error).toContain("HTTP 404");
337
+ });
338
+ it("HTTP 5xx returns server error", async () => {
339
+ vi.stubGlobal("fetch", async () => new Response("Error", { status: 500, headers: { "content-type": "text/plain" } }));
340
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
341
+ const result = await client.sendImageUrl("chat1", "https://example.com/img.jpg");
342
+ expect(result.success).toBe(false);
343
+ expect(result.error).toContain("HTTP 500");
344
+ });
345
+ it("non-image content-type returns specific error", async () => {
346
+ vi.stubGlobal("fetch", async () => new Response("<html>", { status: 200, headers: { "content-type": "text/html" } }));
347
+ const client = new LansengerClient({ appId: "id", appSecret: "secret" });
348
+ const result = await client.sendImageUrl("chat1", "https://example.com/page");
349
+ expect(result.success).toBe(false);
350
+ expect(result.error).toContain("non-image");
351
+ });
352
+ });
353
+ describe("LansengerClient processRawMessage", () => {
354
+ it("parses text message from raw JSON", async () => {
355
+ const client = makeClient();
356
+ const raw = JSON.stringify({
357
+ events: [{ data: { msgType: "text", messageId: "msg-123", chatType: "p2p", from: "user-1", conversationId: "conv-1", senderName: "Alice", msgData: { text: { content: "Hello!" } } } }],
358
+ });
359
+ const events = await client.processRawMessage(raw);
360
+ expect(events.length).toBe(1);
361
+ expect(events[0].text).toBe("Hello!");
362
+ expect(events[0].senderId).toBe("user-1");
363
+ expect(events[0].isGroup).toBe(false);
364
+ });
365
+ it("handles group messages", async () => {
366
+ const client = makeClient();
367
+ const raw = JSON.stringify({
368
+ events: [{ data: { msgType: "text", messageId: "msg-456", chatType: "group", from: "user-2", conversationId: "group-1", conversationTitle: "Team Chat", senderName: "Bob", msgData: { text: { content: "Hi team!" } } } }],
369
+ });
370
+ const events = await client.processRawMessage(raw);
371
+ expect(events.length).toBe(1);
372
+ expect(events[0].isGroup).toBe(true);
373
+ expect(events[0].chatName).toBe("Team Chat");
374
+ });
375
+ it("skips empty text messages", async () => {
376
+ const client = makeClient();
377
+ const raw = JSON.stringify({ events: [{ data: { msgType: "text", messageId: "msg-empty", msgData: { text: { content: "" } } } }] });
378
+ const events = await client.processRawMessage(raw);
379
+ expect(events.length).toBe(0);
380
+ });
381
+ it("handles invalid JSON", async () => {
382
+ const client = makeClient();
383
+ const events = await client.processRawMessage("not json");
384
+ expect(events.length).toBe(0);
385
+ });
386
+ it("handles formatText message", async () => {
387
+ const client = makeClient();
388
+ const raw = JSON.stringify({
389
+ events: [{ data: { msgType: "formatText", messageId: "fmt-1", chatType: "p2p", from: "user-1", conversationId: "conv-1", senderName: "Alice", msgData: { formatText: { formatType: 1, text: "**Bold**" } } } }],
390
+ });
391
+ const events = await client.processRawMessage(raw);
392
+ expect(events.length).toBe(1);
393
+ expect(events[0].text).toBe("**Bold**");
394
+ });
395
+ it("handles multiple events", async () => {
396
+ const client = makeClient();
397
+ const raw = JSON.stringify({
398
+ events: [
399
+ { data: { msgType: "text", messageId: "m1", chatType: "p2p", from: "u1", conversationId: "c1", senderName: "A", msgData: { text: { content: "First" } } } },
400
+ { data: { msgType: "text", messageId: "m2", chatType: "p2p", from: "u2", conversationId: "c2", senderName: "B", msgData: { text: { content: "Second" } } } },
401
+ ],
402
+ });
403
+ const events = await client.processRawMessage(raw);
404
+ expect(events.length).toBe(2);
405
+ });
406
+ });
407
+ //# sourceMappingURL=client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../src/client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,154 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { lansengerSetupWizard } from "./setup-wizard.js";
3
+ describe("lansengerSetupWizard", () => {
4
+ it("channel is 'lansenger'", () => {
5
+ expect(lansengerSetupWizard.channel).toBe("lansenger");
6
+ });
7
+ describe("status.resolveConfigured", () => {
8
+ it("returns true when accounts have valid appId+appSecret", () => {
9
+ const cfg = {
10
+ channels: {
11
+ lansenger: {
12
+ accounts: {
13
+ "bot1": { appId: "bot1", appSecret: "s1" },
14
+ },
15
+ },
16
+ },
17
+ };
18
+ expect(lansengerSetupWizard.status.resolveConfigured({ cfg })).toBe(true);
19
+ });
20
+ it("returns false when accounts have no valid entries", () => {
21
+ const cfg = {
22
+ channels: {
23
+ lansenger: {
24
+ accounts: {
25
+ "bot1": { appId: "bot1" },
26
+ },
27
+ },
28
+ },
29
+ };
30
+ expect(lansengerSetupWizard.status.resolveConfigured({ cfg })).toBe(false);
31
+ });
32
+ it("returns true when top-level has appId+appSecret", () => {
33
+ const cfg = { channels: { lansenger: { appId: "id", appSecret: "s" } } };
34
+ expect(lansengerSetupWizard.status.resolveConfigured({ cfg })).toBe(true);
35
+ });
36
+ it("returns false when nothing configured", () => {
37
+ const cfg = { channels: {} };
38
+ expect(lansengerSetupWizard.status.resolveConfigured({ cfg })).toBe(false);
39
+ });
40
+ });
41
+ describe("introNote.shouldShow", () => {
42
+ it("returns false when already configured via accounts", () => {
43
+ const cfg = {
44
+ channels: {
45
+ lansenger: {
46
+ accounts: { "bot1": { appId: "b1", appSecret: "s1" } },
47
+ },
48
+ },
49
+ };
50
+ expect(lansengerSetupWizard.introNote.shouldShow({ cfg })).toBe(false);
51
+ });
52
+ it("returns true when not configured", () => {
53
+ const cfg = { channels: {} };
54
+ expect(lansengerSetupWizard.introNote.shouldShow({ cfg })).toBe(true);
55
+ });
56
+ });
57
+ describe("credentials shouldPrompt", () => {
58
+ it("appToken shouldPrompt returns false when hasConfiguredValue", () => {
59
+ const state = { hasConfiguredValue: true };
60
+ expect(lansengerSetupWizard.credentials[0].shouldPrompt({ state })).toBe(false);
61
+ });
62
+ it("appToken shouldPrompt returns true when no value", () => {
63
+ const state = { hasConfiguredValue: false };
64
+ expect(lansengerSetupWizard.credentials[0].shouldPrompt({ state })).toBe(true);
65
+ });
66
+ it("secret shouldPrompt returns false when hasConfiguredValue", () => {
67
+ const state = { hasConfiguredValue: true };
68
+ expect(lansengerSetupWizard.credentials[1].shouldPrompt({ state })).toBe(false);
69
+ });
70
+ it("secret shouldPrompt returns true when no value", () => {
71
+ const state = { hasConfiguredValue: false };
72
+ expect(lansengerSetupWizard.credentials[1].shouldPrompt({ state })).toBe(true);
73
+ });
74
+ });
75
+ describe("credentials allowEnv", () => {
76
+ it("appToken allowEnv returns true for default accountId", () => {
77
+ expect(lansengerSetupWizard.credentials[0].allowEnv({ accountId: "default" })).toBe(true);
78
+ expect(lansengerSetupWizard.credentials[0].allowEnv({ accountId: undefined })).toBe(true);
79
+ });
80
+ it("appToken allowEnv returns false for specific accountId", () => {
81
+ expect(lansengerSetupWizard.credentials[0].allowEnv({ accountId: "2285568-10117376" })).toBe(false);
82
+ });
83
+ });
84
+ describe("credentials inspect", () => {
85
+ it("appToken inspect reads appId from config", () => {
86
+ const cfg = { channels: { lansenger: { appId: "my-app-id" } } };
87
+ const result = lansengerSetupWizard.credentials[0].inspect({ cfg, accountId: undefined });
88
+ expect(result.hasConfiguredValue).toBe(true);
89
+ expect(result.resolvedValue).toBe("my-app-id");
90
+ });
91
+ it("secret inspect reads appSecret from accounts", () => {
92
+ const cfg = {
93
+ channels: {
94
+ lansenger: {
95
+ accounts: { "bot1": { appSecret: "my-secret" } },
96
+ },
97
+ },
98
+ };
99
+ const result = lansengerSetupWizard.credentials[1].inspect({ cfg, accountId: "bot1" });
100
+ expect(result.hasConfiguredValue).toBe(true);
101
+ expect(result.resolvedValue).toBe("my-secret");
102
+ });
103
+ });
104
+ describe("textInputs baseUrl", () => {
105
+ it("initialValue returns from account config", () => {
106
+ const cfg = { channels: { lansenger: { apiGatewayUrl: "https://custom.url" } } };
107
+ const val = lansengerSetupWizard.textInputs[0].initialValue({ cfg, accountId: undefined });
108
+ expect(val).toBe("https://custom.url");
109
+ });
110
+ it("initialValue defaults to public cloud URL", () => {
111
+ const cfg = { channels: {} };
112
+ const val = lansengerSetupWizard.textInputs[0].initialValue({ cfg, accountId: undefined });
113
+ expect(val).toBe("https://open.e.lanxin.cn/open/apigw");
114
+ });
115
+ });
116
+ describe("dmPolicy.getCurrent", () => {
117
+ it("returns dmPolicy when present", () => {
118
+ const cfg = { channels: { lansenger: { dmPolicy: "open" } } };
119
+ expect(lansengerSetupWizard.dmPolicy.getCurrent(cfg)).toBe("open");
120
+ });
121
+ it("falls back to dmSecurity", () => {
122
+ const cfg = { channels: { lansenger: { dmSecurity: "paired" } } };
123
+ expect(lansengerSetupWizard.dmPolicy.getCurrent(cfg)).toBe("paired");
124
+ });
125
+ it("defaults to pairing", () => {
126
+ const cfg = { channels: {} };
127
+ expect(lansengerSetupWizard.dmPolicy.getCurrent(cfg)).toBe("pairing");
128
+ });
129
+ });
130
+ describe("allowFrom", () => {
131
+ it("parseId returns trimmed string", () => {
132
+ expect(lansengerSetupWizard.allowFrom.parseId(" 2285568-xxx ")).toBe("2285568-xxx");
133
+ });
134
+ it("parseId returns null for empty string", () => {
135
+ expect(lansengerSetupWizard.allowFrom.parseId("")).toBe(null);
136
+ });
137
+ it("resolveEntries maps entries to resolved objects", async () => {
138
+ const entries = ["2285568-xxx", "2285568-yyy"];
139
+ const result = await lansengerSetupWizard.allowFrom.resolveEntries({ entries });
140
+ expect(result).toEqual([
141
+ { input: "2285568-xxx", resolved: true, id: "2285568-xxx" },
142
+ { input: "2285568-yyy", resolved: true, id: "2285568-yyy" },
143
+ ]);
144
+ });
145
+ });
146
+ describe("disable", () => {
147
+ it("sets enabled to false", () => {
148
+ const cfg = { channels: { lansenger: { enabled: true, appId: "id" } } };
149
+ const result = lansengerSetupWizard.disable(cfg);
150
+ expect(result.channels.lansenger.enabled).toBe(false);
151
+ });
152
+ });
153
+ });
154
+ //# sourceMappingURL=setup-wizard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-wizard.test.js","sourceRoot":"","sources":["../../src/setup-wizard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
2
+ import { registerLansengerTools } from "./src/tools.js";
3
+ export default definePluginEntry({
4
+ id: "lansenger-tools",
5
+ name: "Lansenger Tools",
6
+ description: "Lansenger (蓝信) messaging tools for OpenClaw Agent",
7
+ register(api) {
8
+ if (api.registrationMode === "full") {
9
+ registerLansengerTools(api);
10
+ }
11
+ },
12
+ });
13
+ //# sourceMappingURL=tools-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-entry.js","sourceRoot":"","sources":["../tools-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAExD,eAAe,iBAAiB,CAAC;IAC/B,EAAE,EAAE,iBAAiB;IACrB,IAAI,EAAE,iBAAiB;IACvB,WAAW,EAAE,mDAAmD;IAChE,QAAQ,CAAC,GAAG;QACV,IAAI,GAAG,CAAC,gBAAgB,KAAK,MAAM,EAAE,CAAC;YACpC,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
package/index.ts CHANGED
@@ -1,32 +1,17 @@
1
1
  import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
2
2
  import { lansengerPlugin } from "./src/channel.js";
3
3
  import { startLansengerGateway } from "./src/runtime.js";
4
- import { registerLansengerTools } from "./src/tools.js";
4
+
5
+ export { resolveAccount, makeClient } from "./src/channel.js";
6
+ export type { ResolvedAccount } from "./src/channel.js";
7
+ export { getRunningClient, getRunningAccount, getLastInboundChatId } from "./src/runtime.js";
8
+ export { LansengerClient } from "./src/client.js";
5
9
 
6
10
  export default defineChannelPluginEntry({
7
11
  id: "lansenger",
8
12
  name: "Lansenger (蓝信)",
9
13
  description: "Lansenger enterprise messaging channel plugin for OpenClaw",
10
14
  plugin: lansengerPlugin,
11
- registerCliMetadata(api) {
12
- api.registerCli(
13
- ({ program }) => {
14
- program.command("lansenger").description("Lansenger (蓝信) management");
15
- },
16
- {
17
- descriptors: [
18
- {
19
- name: "lansenger",
20
- description: "Lansenger (蓝信) management",
21
- hasSubcommands: false,
22
- },
23
- ],
24
- },
25
- );
26
- if (api.registrationMode === "full") {
27
- registerLansengerTools(api);
28
- }
29
- },
30
15
  registerFull(api) {
31
16
  startLansengerGateway(api);
32
17
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lansenger-pm/openclaw-lansenger-channel",
3
- "version": "3.1.9",
3
+ "version": "3.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -1,34 +1,26 @@
1
1
  ---
2
2
  name: lansenger-messaging
3
- description: How to communicate on Lansenger (蓝信) — choose the right tool, send rich content, approvals, and pitfalls
3
+ description: Lansenger (蓝信) messaging use lansenger CLI commands via bash to send text, Markdown, files, cards, and manage messages
4
4
  metadata: {"openclaw":{"requires":{"config":["channels.lansenger"]},"primaryEnv":"LANSENGER_APP_ID"}}
5
5
  ---
6
6
 
7
- # Lansenger (蓝信) Messaging — Agent Quick Reference
7
+ # Lansenger (蓝信) Messaging — CLI Command Reference
8
8
 
9
- In a Lansenger session, **just write your reply normally** Markdown renders automatically as formatText. No tool needed for formatted text unless you also need @mentions.
9
+ Use `lansenger` CLI commands via bash to send messages. The OpenClaw plugin tools are NOT available as agent tools call CLI commands directly.
10
10
 
11
- However, Markdown and file attachments are **mutually exclusive**. If you need both formatting AND a file, send the Markdown reply first, then call `lansenger_send_file` separately.
11
+ ## Quick Decision: Which Command?
12
12
 
13
- **Never put raw Markdown in lansenger_send_text** it displays as ugly source code.
14
-
15
- > ⚠️ This is reference documentation, NOT a CLI command. `openclaw skill lansenger-messaging` does not exist. Use the agent tools directly.
16
-
17
- ## Quick Decision: What Tool Do I Use?
18
-
19
- Need to send something? Follow this decision tree:
20
-
21
- 1. Normal reply with formatting JUST WRITE IT. No tool needed.
22
- 2. Markdown + @mention lansenger_send_format_text (with reminder)
23
- 3. Plain text + @mention + file lansenger_send_text (with reminder + filePath)
24
- 4. Send a FILE → lansenger_send_file
25
- 5. Send image from URL → lansenger_send_image_url
26
- 6. Link card → lansenger_send_link_card
27
- 7. Multi-article card (图文) → lansenger_send_app_articles
28
- 8. Rich formatted card → lansenger_send_app_card
29
- 9. Approval workflow → sendAppCard(isDynamic=true) → updateDynamicCard
30
- 10. Revoke a message → lansenger_revoke_message
31
- 11. Find group IDs → lansenger_query_groups
13
+ 1. Normal reply just write it (Markdown renders as formatText)
14
+ 2. Markdown + @mention → `lansenger message send-markdown` with `--mention-all` or `--mention`
15
+ 3. Plain text + @mention + file `lansenger message send-text` with `--mention-all`/`--mention` and `--file`
16
+ 4. Send a FILE → `lansenger message send-file`
17
+ 5. Image from URL `lansenger message send-image-url`
18
+ 6. Link card → `lansenger message send-link-card`
19
+ 7. Multi-article (图文) `lansenger message send-app-articles`
20
+ 8. Rich card → `lansenger message send-app-card`
21
+ 9. Approval workflow`send-app-card --dynamic --status-desc` `update-dynamic-card`
22
+ 10. Revoke message`lansenger message revoke`
23
+ 11. Find group IDs`lansenger message query-groups`
32
24
 
33
25
  ## Message Type Capability Matrix
34
26
 
@@ -37,145 +29,188 @@ Need to send something? Follow this decision tree:
37
29
  | formatText | ✓ (auto) | ✓ (reminder) | ✗ |
38
30
  | text | ✗ | ✓ | ✓ |
39
31
 
40
- - **formatText**: Markdown renders, supports @mention via reminder param (auto-fallback if API rejects). No file attachments.
41
- - **text**: Plain text only. Supports @mention + file attachment. No Markdown rendering.
42
- - These two types are **mutually exclusive** pick one based on what you need.
43
-
44
- ## Tool Reference
45
-
46
- All tools accept optional `to` (chatId). Leave empty to auto-detect current conversation. Only fill for different chat. chatId is case-sensitive — never truncate or modify.
47
-
48
- ### lansenger_send_file
49
-
50
- | Param | Type | Required | Description |
51
- |----------------|----------|----------|----------------------------------------------------|
52
- | filePath | string | ✅ | Absolute local path (workspace, /tmp, Desktop, etc.) |
53
- | caption | string | ❌ | Plain-text caption (no Markdown) |
54
- | to | string | ❌ | Target chatId (auto if omitted) |
55
-
56
- > ⚠️ **MEDIA: tags currently broken** — they silently fail for ALL files, not just workspace ones. Use `lansenger_send_file` for every file attachment.
57
-
58
- ### lansenger_send_text
59
-
60
- | Param | Type | Required | Description |
61
- |----------------|----------|----------|----------------------------------------------------|
62
- | content | string | | Plain text only (NO Markdown) |
63
- | filePath | string | ❌ | Optional file to attach (content becomes caption) |
64
- | to | string | ❌ | Target chatId |
65
- | reminderAll | boolean | ❌ | @mention all members (group only) |
66
- | reminderUserIds | string[] | ❌ | @mention specific users (group only) |
67
-
68
- ### lansenger_send_format_text
69
-
70
- | Param | Type | Required | Description |
71
- |----------------|----------|----------|----------------------------------------------------|
72
- | content | string | ✅ | Markdown text |
73
- | to | string | ❌ | Target chatId |
74
- | reminderAll | boolean | | @mention all (group only) |
75
- | reminderUserIds | string[] | ❌ | @mention specific users (group only) |
76
-
77
- > Use when you need Markdown + @mention. No file attachments. Plugin auto-fallback if API rejects reminder.
78
-
79
- ### lansenger_send_image_url
80
-
81
- | Param | Type | Required | Description |
82
- |----------|----------|----------|----------------------------------------------------|
83
- | imageUrl | string | ✅ | Directly reachable image URL |
84
- | caption | string | ❌ | Plain-text caption (no Markdown) |
85
- | to | string | ❌ | Target chatId |
86
-
87
- > URL must be reachable from the gateway host. For local files, use `lansenger_send_file`.
88
-
89
- ### lansenger_send_link_card
90
-
91
- | Param | Type | Required | Description |
92
- |--------------|--------|----------|----------------------------------------------------|
93
- | title | string | ✅ | Card title |
94
- | link | string | | Click-through URL |
95
- | description | string | ❌ | Card description (API-required, defaults empty) |
96
- | iconLink | string | ❌ | Icon URL (defaults empty) |
97
- | pcLink | string | ❌ | PC client link |
98
- | fromName | string | ❌ | Source name (defaults empty) |
99
- | fromIconLink | string | ❌ | Source icon URL (defaults empty) |
100
- | to | string | ❌ | Target chatId |
101
-
102
- ### lansenger_send_app_articles
103
-
104
- | Param | Type | Required | Description |
105
- |----------|----------|----------|----------------------------------------------------|
106
- | articles | object[] | | Each: { imgUrl, title, url, summary?, pcUrl? } |
107
- | to | string | ❌ | Target chatId |
108
-
109
- > ⚠️ Article summary field is `summary`, NOT `description`. `description` is silently ignored by the API.
110
-
111
- ### lansenger_send_app_card
112
-
113
- | Param | Type | Required | Description |
114
- |----------------|----------|----------|----------------------------------------------------|
115
- | bodyTitle | string | ✅ | Card title (supports div-style) |
116
- | headTitle | string | | Header title |
117
- | bodySubTitle | string | | Subtitle (supports div-style) |
118
- | bodyContent | string | ❌ | Main content (supports div-style) |
119
- | signature | string | ❌ | Footer signature |
120
- | isDynamic | boolean | ❌ | Enable approval workflow (default: false) |
121
- | headStatusInfo | object | ❌ | { description, colour } — description: status TEXT (supports div-style for color, e.g. `<div style="color:#FFB116">待审批</div>`, max 30 bytes). colour: DOT/圆点 color (hex, e.g. #FFB116). These are TWO different things: text color vs dot color. |
122
- | fields | object[] | ❌ | Key-value pairs (max 10) |
123
- | links | object[] | ❌ | Links (max 3): { title, url } |
124
- | cardLink | string | ❌ | Card click-through link |
125
- | staffId | string | ❌ | Staff openId for sender avatar |
126
- | headIconUrl | string | ❌ | Header icon URL |
127
- | to | string | ❌ | Target chatId |
32
+ - formatText and text are **mutually exclusive**.
33
+ - If you need Markdown AND a file, send Markdown first, then `lansenger message send-file` separately.
34
+ - **Never put raw Markdown in `lansenger message send-text`** displays as ugly source code.
35
+
36
+ Just write your reply normally for Markdown.
37
+
38
+ Write normally for Markdown.
39
+
40
+ ## CLI Command Reference
41
+
42
+ All commands use `lansenger message <subcommand>`. chatId is case-sensitive. For group messages, add `--group` flag.
43
+
44
+ ### lansenger message send-text
45
+
46
+ ```bash
47
+ lansenger message send-text <chat_id> <content> [--file <path>] [--group] [--mention-all] [--mention <uid1> [--mention <uid2>]
48
+ ```
49
+
50
+ | Param | Flag | Required | Description |
51
+ |--------------|---------------|----------|--------------------------------------|
52
+ | chat_id | positional | | Chat ID (user/group) |
53
+ | content | positional | ✅ | Plain text only (NO Markdown) |
54
+ | --file | -f | | File path to attach |
55
+ | --group | -g | ❌ | Send as group message |
56
+ | --mention-all| | ❌ | @all in group |
57
+ | --mention | | ❌ | User IDs to @mention |
58
+ | --sender-id | | ❌ | Sender staff ID (group) |
59
+
60
+ ### lansenger message send-markdown
61
+
62
+ ```bash
63
+ lansenger message send-markdown <chat_id> <content> [--mention-all] [--mention <uid>] [--group] [--sender-id <id>]
64
+ ```
65
+
66
+ | Param | Flag | Required | Description |
67
+ |--------------|---------------|----------|--------------------------------------|
68
+ | chat_id | positional | ✅ | Chat ID |
69
+ | content | positional | ✅ | Markdown content |
70
+ | --mention-all| | ❌ | @all in group |
71
+ | --mention | | ❌ | User IDs to @mention |
72
+ | --group | -g | ❌ | Send as group message |
73
+ | --sender-id | | | Sender staff ID (group) |
74
+
75
+ > Auto-fallback: if API rejects reminder, retries without reminder.
76
+
77
+ ### lansenger message send-file
78
+ ```bash
79
+ lansenger message send-file <chat_id> <file_path> [--caption <text>] [--media-type <1|2|3>]
80
+ ```
81
+
82
+ | Param | Flag | Required | Description |
83
+ |-------------|---------------|----------|--------------------------------------|
84
+ | chat_id | positional | ✅ | Chat ID |
85
+ | file_path | positional | ✅ | Absolute local file path |
86
+ | --caption | -c | | Plain-text caption |
87
+ | --media-type| | ❌ | 1=video, 2=image, 3=file |
88
+
89
+ ### lansenger message send-image-url
90
+ ```bash
91
+ lansenger message send-image-url <chat_id> <image_url> [--caption <text>]
92
+ ```
93
+
94
+ | Param | Flag | Required | Description |
95
+ |-----------|-----------|----------|--------------------------------------|
96
+ | chat_id | positional | | Chat ID |
97
+ | image_url | positional | ✅ | Directly reachable image URL |
98
+ | --caption | -c | | Plain-text caption |
99
+
100
+ ### lansenger message send-link-card
101
+ ```bash
102
+ lansenger message send-link-card <chat_id> <title> <link> [--desc <text>] [--icon <url>] [--pc-link <url>] [--from-name <name>] [--from-icon <url>]
103
+ ```
104
+
105
+ | Param | Flag | Required | Description |
106
+ |--------------|---------------|----------|--------------------------------------|
107
+ | chat_id | positional | ✅ | Chat ID |
108
+ | title | positional | | Card title |
109
+ | link | positional | | Click-through URL |
110
+ | --desc | -d | ❌ | Card description |
111
+ | --icon | | ❌ | Icon URL |
112
+ | --pc-link | | ❌ | PC link URL |
113
+ | --from-name | | ❌ | Source name |
114
+ | --from-icon | | ❌ | Source icon URL |
115
+
116
+ ### lansenger message send-app-articles
117
+ ```bash
118
+ lansenger message send-app-articles <chat_id> '{"title":"T","url":"U","imgUrl":"I","summary":"S"}' '{"title":"T2","url":"U2"}'
119
+ ```
120
+
121
+ | Param | Type | Required | Description |
122
+ |----------|----------|----------|----------------------------------------------|
123
+ | chat_id | positional | ✅ | Chat ID |
124
+ | articles | positional | ✅ | JSON dicts (multiple). Fields: title, url, imgUrl, summary?, pcUrl? |
125
+
126
+ > ⚠️ Article field is `summary`, NOT `description`. `description` is silently ignored by the API.
127
+
128
+ ### lansenger message send-app-card
129
+ ```bash
130
+ lansenger message send-app-card <chat_id> <body_title> [--head-title <t>] [--sub-title <t>] [--content <t>] [--signature <t>] [--card-link <url>] [--pc-card-link <url>] [--pad-card-link <url>] [--dynamic] [--staff-id <id>] [--head-icon <url>] [--status-desc <div>] [--status-colour <hex>] [--field <json>] [--link <json>]
131
+ ```
132
+
133
+ | Param | Flag | Required | Description |
134
+ |-----------------|-------------------|----------|----------------------------------------------|
135
+ | chat_id | positional | ✅ | Chat ID |
136
+ | body_title | positional | ✅ | Card body title (supports div-style) |
137
+ | --head-title | | ❌ | Header title |
138
+ | --sub-title | | ❌ | Subtitle (supports div-style) |
139
+ | --content | | ❌ | Main content (supports div-style) |
140
+ | --signature | | ❌ | Footer signature |
141
+ | --card-link | | ❌ | Card click-through link |
142
+ | --pc-card-link | | ❌ | PC card link |
143
+ | --pad-card-link | | ❌ | Pad card link |
144
+ | --dynamic | | ❌ | Enable approval workflow |
145
+ | --staff-id | | ❌ | Staff openId for sender avatar |
146
+ | --head-icon | | ❌ | Header icon URL |
147
+ | --status-desc | | ❌ | Status description (div-style, max 30 bytes) |
148
+ | --status-colour | | ❌ | Status DOT colour (hex, e.g. #FFB116) |
149
+ | --field | | ❌ | Field as JSON {\"key\":\"k\",\"value\":\"v\"} |
150
+ | --link | | ❌ | Link as JSON {\"title\":\"T\",\"url\":\"U\"} |
128
151
 
129
152
  **div-style rules:**
130
153
  - `color`: hex (e.g. #333, #007BFF)
131
154
  - `font-size`: **MUST use pt** (12pt–36pt). px causes "invalid bodyContent" error.
132
155
  - `text-align`: left, center, right
133
156
  - `text-indent`: **MUST use 0em** — bare 0 causes silent failure
157
+ - Example: `<div style="color:#333;font-size:14pt;text-indent:0em;text-align:left">Content</div>`
158
+
159
+ **headStatusInfo**: `--status-desc` = status TEXT (supports div-style for text color), `--status-colour` = DOT/圆点 colour. These are TWO different things: text color vs dot color.
160
+
161
+ ### lansenger message update-dynamic-card
162
+ ```bash
163
+ lansenger message update-dynamic-card <msg_id> [--last] [--status-desc <div>] [--status-colour <hex>] [--link <json>]
164
+ ```
165
+
166
+ | Param | Flag | Required | Description |
167
+ |-----------------|-------------------|----------|----------------------------------------------|
168
+ | msg_id | positional | ✅ | Message ID from original send-app-card |
169
+ | --last | | ❌ | Mark as last update (card becomes static) |
170
+ | --status-desc | | ❌ | New status description (div-style) |
171
+ | --status-colour | | ❌ | New status DOT colour (hex) |
172
+ | --link | | ❌ | Updated link as JSON {\"title\":\"T\",\"url\":\"U\"} |
173
+
174
+ ### lansenger message revoke
175
+ ```bash
176
+ lansenger message revoke <msg_id1> <msg_id2> [--chat-type bot|group] [--sender-id <id>]
177
+ ```
178
+
179
+ | Param | Flag | Required | Description |
180
+ |-------------|---------------|----------|--------------------------------------|
181
+ | message_ids | positional | ✅ | Message IDs to revoke |
182
+ | --chat-type | | ❌ | bot (default) or group |
183
+ | --sender-id | | ❌ | Required if chat-type=group |
184
+
185
+ ### lansenger message query-groups
186
+ ```bash
187
+ lansenger message query-groups [--page <n>] [--size <n>]
188
+ ```
189
+
190
+ | Param | Flag | Required | Description |
191
+ |-------------|----------|----------|--------------------------------------|
192
+ | --page | -p | ❌ | Page offset (default: 1) |
193
+ | --size | -s | ❌ | Page size (default: 100) |
134
194
 
135
- Example: `<div style="color:#333;font-size:14pt;text-indent:0em;text-align:left">Content here</div>`
136
-
137
- ### lansenger_update_dynamic_card
138
-
139
- | Param | Type | Required | Description |
140
- |----------------|----------|----------|----------------------------------------------------|
141
- | msgId | string | ✅ | Message ID from original send_app_card response |
142
- | headStatusInfo | object | ❌ | { description, colour } — description: status TEXT (supports div-style for color). colour: DOT/圆点 color (hex). Independent. |
143
- | links | object[] | ❌ | Updated links (max 3) |
144
- | isLastUpdate | boolean | ❌ | True = final state, card becomes static (default: false) |
145
-
146
- > Set `isLastUpdate=true` on final approval decision. Intermediate updates: keep `isLastUpdate=false` (default).
147
-
148
- ### lansenger_revoke_message
149
-
150
- | Param | Type | Required | Description |
151
- |-----------|----------|----------|----------------------------------------------------|
152
- | messageIds | string[] | ✅ | List of message IDs to revoke |
153
- | chatType | string | ❌ | bot (default) or group |
154
- | senderId | string | ❌ | Required if chatType=group |
155
-
156
- ### lansenger_query_groups
157
-
158
- | Param | Type | Required | Description |
159
- |------------|---------|----------|----------------------------------------------------|
160
- | pageOffset | integer | ❌ | Page number (default: 1) |
161
- | pageSize | integer | ❌ | Groups per page (default: 100) |
195
+ ## Approval Workflow Pattern
162
196
 
163
- > ⚠️ May return errCode=10005 "API服务无权限" on enterprise deployments. Ask user for group IDs manually.
197
+ ```bash
198
+ # Step 1: Send approval card
199
+ lansenger message send-app-card <chat_id> "审批" --dynamic --status-desc '<div style="color:#FFB116">待审批</div>' --status-colour "#FFB116"
164
200
 
165
- ## Approval Workflow Pattern
201
+ # Step 2: User approves → Update to approved
202
+ lansenger message update-dynamic-card <msg_id> --last --status-desc '<div style="color:#198754">已批准</div>' --status-colour "#198754"
166
203
 
167
- 1. Send: `lansenger_send_app_card(bodyTitle="...", isDynamic=true, headStatusInfo={description: '<div style="color:#FFB116">待审批</div>', colour: "#FFB116"})`
168
- 2. User approves/denies OpenClaw processes approval
169
- 3. Update: `lansenger_update_dynamic_card(msgId="<from step 1>", headStatusInfo={description: '<div style="color:#198754">已批准</div>', colour: "#198754"}, isLastUpdate=true)`
204
+ # Step 3: User denies Update to denied
205
+ lansenger message update-dynamic-card <msg_id> --last --status-desc '<div style="color:#dc3545">已拒绝</div>' --status-colour "#dc3545"
206
+ ```
170
207
 
171
208
  Status colors: #FFB116 (pending), #198754 (approved), #dc3545 (denied)
172
209
 
173
210
  ## @Mention Rules
174
211
 
175
- Both `lansenger_send_format_text` and `lansenger_send_text` support @mentions via `reminderAll` + `reminderUserIds` params.
176
-
177
- 1. **Always include "@姓名" in message text** — reminder sends the push notification, "@姓名" makes it visible in the message itself. This is especially important in **group chat** — without "@姓名" the mentioned person won't see who is being addressed.
178
- 2. **reminder is optional** — use it when you want someone to receive a push notification about your message.
212
+ 1. **Always include "@姓名" in message text** reminder sends the push notification, "@姓名" makes it visible in the message. Especially important in group chat.
213
+ 2. **reminder is optional** — use it when you want someone to receive a push notification.
179
214
 
180
215
  ## Inbound Messages
181
216
 
@@ -193,13 +228,12 @@ Both `lansenger_send_format_text` and `lansenger_send_text` support @mentions vi
193
228
 
194
229
  ## Common Pitfalls
195
230
 
196
- | Pitfall | Fix |
197
- |--------------------------------|----------------------------------------------------------------------------------|
198
- | Raw Markdown in text tool | Never do this — shows as ugly source code. Write normally for Markdown. |
199
- | MEDIA: tag for file delivery | ⚠️ Currently broken for ALL files. Use `lansenger_send_file` instead. |
200
- | AppArticles `description` field | Use `summary`, not `description`. `description` is silently ignored by the API. |
201
- | AppCard `font-size: px` | Use `pt` units (12pt–36pt). px causes API "invalid bodyContent" error. |
202
- | AppCard `text-indent: 0` | Use `0em` with unit. Bare 0 causes silent failure. |
203
- | headStatusInfo div wrapping | description supports div-style for color. colour is the DOT/圆点 color. Separate. |
204
- | Message too long | ~4000 character limit. Split into multiple messages. |
205
- | `openclaw skill` command | Does NOT exist. Use agent tools directly. |
231
+ | Pitfall | Fix |
232
+ |----------------------------------|-----------------------------------------------------------|
233
+ | Raw Markdown in send-text | Never do this. Write normally for Markdown. |
234
+ | MEDIA: tag for file delivery | Use `lansenger message send-file` instead. |
235
+ | AppArticles `description` field | Use `summary`, not `description`. |
236
+ | AppCard font-size: px | Use pt (12pt–36pt). px causes "invalid bodyContent" error. |
237
+ | AppCard text-indent: 0 | Use `0em`. Bare 0 causes silent failure. |
238
+ | headStatusInfo div wrapping | `--status-desc` = text color, `--status-colour` = dot color. |
239
+ | Message too long | ~4000 char limit. Split into multiple messages. |