openclaw-zulip 0.1.0

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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/actions.d.ts +3 -0
  4. package/dist/actions.d.ts.map +1 -0
  5. package/dist/actions.js +149 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/agent-prompt.d.ts +3 -0
  8. package/dist/agent-prompt.d.ts.map +1 -0
  9. package/dist/agent-prompt.js +21 -0
  10. package/dist/agent-prompt.js.map +1 -0
  11. package/dist/allowlist.d.ts +3 -0
  12. package/dist/allowlist.d.ts.map +1 -0
  13. package/dist/allowlist.js +50 -0
  14. package/dist/allowlist.js.map +1 -0
  15. package/dist/bindings.d.ts +24 -0
  16. package/dist/bindings.d.ts.map +1 -0
  17. package/dist/bindings.js +281 -0
  18. package/dist/bindings.js.map +1 -0
  19. package/dist/commands.d.ts +3 -0
  20. package/dist/commands.d.ts.map +1 -0
  21. package/dist/commands.js +7 -0
  22. package/dist/commands.js.map +1 -0
  23. package/dist/config-schema.d.ts +2 -0
  24. package/dist/config-schema.d.ts.map +1 -0
  25. package/dist/config-schema.js +34 -0
  26. package/dist/config-schema.js.map +1 -0
  27. package/dist/config.d.ts +5 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +53 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/directory.d.ts +3 -0
  32. package/dist/directory.d.ts.map +1 -0
  33. package/dist/directory.js +68 -0
  34. package/dist/directory.js.map +1 -0
  35. package/dist/gateway.d.ts +4 -0
  36. package/dist/gateway.d.ts.map +1 -0
  37. package/dist/gateway.js +324 -0
  38. package/dist/gateway.js.map +1 -0
  39. package/dist/groups.d.ts +3 -0
  40. package/dist/groups.d.ts.map +1 -0
  41. package/dist/groups.js +28 -0
  42. package/dist/groups.js.map +1 -0
  43. package/dist/index.d.ts +11 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +9 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/messaging.d.ts +3 -0
  48. package/dist/messaging.d.ts.map +1 -0
  49. package/dist/messaging.js +47 -0
  50. package/dist/messaging.js.map +1 -0
  51. package/dist/outbound.d.ts +24 -0
  52. package/dist/outbound.d.ts.map +1 -0
  53. package/dist/outbound.js +114 -0
  54. package/dist/outbound.js.map +1 -0
  55. package/dist/plugin.d.ts +5 -0
  56. package/dist/plugin.d.ts.map +1 -0
  57. package/dist/plugin.js +66 -0
  58. package/dist/plugin.js.map +1 -0
  59. package/dist/resolver.d.ts +3 -0
  60. package/dist/resolver.d.ts.map +1 -0
  61. package/dist/resolver.js +39 -0
  62. package/dist/resolver.js.map +1 -0
  63. package/dist/security.d.ts +4 -0
  64. package/dist/security.d.ts.map +1 -0
  65. package/dist/security.js +15 -0
  66. package/dist/security.js.map +1 -0
  67. package/dist/setup.d.ts +2 -0
  68. package/dist/setup.d.ts.map +1 -0
  69. package/dist/setup.js +25 -0
  70. package/dist/setup.js.map +1 -0
  71. package/dist/status.d.ts +14 -0
  72. package/dist/status.d.ts.map +1 -0
  73. package/dist/status.js +74 -0
  74. package/dist/status.js.map +1 -0
  75. package/dist/threading.d.ts +3 -0
  76. package/dist/threading.d.ts.map +1 -0
  77. package/dist/threading.js +25 -0
  78. package/dist/threading.js.map +1 -0
  79. package/dist/types.d.ts +42 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +4 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/zulip-client.d.ts +115 -0
  84. package/dist/zulip-client.d.ts.map +1 -0
  85. package/dist/zulip-client.js +232 -0
  86. package/dist/zulip-client.js.map +1 -0
  87. package/openclaw.plugin.json +33 -0
  88. package/package.json +53 -0
@@ -0,0 +1,324 @@
1
+ import { createChannelReplyPipeline, dispatchInboundReplyWithBase } from "openclaw/plugin-sdk/irc";
2
+ import { registerSessionBindingAdapter, unregisterSessionBindingAdapter, } from "openclaw/plugin-sdk/conversation-runtime";
3
+ import { resolveSenderCommandAuthorization, } from "openclaw/plugin-sdk/command-auth";
4
+ import { getZulipSection } from "./types.js";
5
+ import { ZulipClient } from "./zulip-client.js";
6
+ import { createZulipSessionBindingAdapter, clearZulipBindingStore, touchZulipBindingByConversation, } from "./bindings.js";
7
+ const CHANNEL_ID = "zulip";
8
+ export const zulipGatewayAdapter = {
9
+ async startAccount(ctx) {
10
+ const { account, abortSignal, log } = ctx;
11
+ const client = new ZulipClient({
12
+ serverUrl: account.serverUrl,
13
+ email: account.email,
14
+ apiKey: account.apiKey,
15
+ });
16
+ // Register session binding adapter for ACP topic bindings
17
+ const bindingAdapter = createZulipSessionBindingAdapter(account.accountId);
18
+ registerSessionBindingAdapter(bindingAdapter);
19
+ // Identify ourselves so we can filter our own messages
20
+ const self = await client.getOwnUser();
21
+ log?.info(`Connected as ${self.full_name} (${self.email}, id=${self.user_id})`);
22
+ // Register event queue
23
+ const queue = await client.registerEventQueue({
24
+ eventTypes: ["message"],
25
+ allPublicStreams: account.mode === "bot",
26
+ });
27
+ log?.info(`Event queue registered: ${queue.queue_id}`);
28
+ ctx.setStatus({
29
+ accountId: account.accountId,
30
+ enabled: true,
31
+ configured: true,
32
+ running: true,
33
+ connected: true,
34
+ lastConnectedAt: Date.now(),
35
+ });
36
+ let lastEventId = queue.last_event_id;
37
+ // Poll loop
38
+ while (!abortSignal.aborted) {
39
+ let events;
40
+ try {
41
+ events = await client.getEvents({
42
+ queueId: queue.queue_id,
43
+ lastEventId,
44
+ });
45
+ }
46
+ catch (err) {
47
+ if (abortSignal.aborted)
48
+ break;
49
+ log?.error(`Event poll error: ${err}`);
50
+ // Back off before retry
51
+ await new Promise((r) => setTimeout(r, 5000));
52
+ continue;
53
+ }
54
+ for (const event of events) {
55
+ if (abortSignal.aborted)
56
+ break;
57
+ lastEventId = event.id;
58
+ if (event.type === "heartbeat")
59
+ continue;
60
+ if (event.type !== "message" || !event.message)
61
+ continue;
62
+ const msg = event.message;
63
+ // Skip our own messages
64
+ if (msg.sender_id === self.user_id)
65
+ continue;
66
+ try {
67
+ await handleInboundMessage(ctx, client, msg);
68
+ }
69
+ catch (err) {
70
+ log?.error(`Error handling message ${msg.id}: ${err}`);
71
+ }
72
+ }
73
+ }
74
+ // Cleanup — deregister queue and binding adapter
75
+ try {
76
+ await client.deleteEventQueue(queue.queue_id);
77
+ log?.info("Event queue deregistered.");
78
+ }
79
+ catch {
80
+ // Queue may already be expired
81
+ }
82
+ unregisterSessionBindingAdapter({ channel: CHANNEL_ID, accountId: account.accountId, adapter: bindingAdapter });
83
+ clearZulipBindingStore(account.accountId);
84
+ ctx.setStatus({
85
+ accountId: account.accountId,
86
+ enabled: true,
87
+ configured: true,
88
+ running: false,
89
+ connected: false,
90
+ lastStopAt: Date.now(),
91
+ });
92
+ },
93
+ async stopAccount(ctx) {
94
+ ctx.log?.info(`Stopping account ${ctx.account.accountId}`);
95
+ // The abort signal in startAccount will break the poll loop
96
+ },
97
+ };
98
+ function resolveAckReactions(cfg) {
99
+ const section = getZulipSection(cfg);
100
+ const reactions = section?.reactions;
101
+ if (!reactions || reactions.enabled === false)
102
+ return { enabled: false };
103
+ return {
104
+ enabled: true,
105
+ onStart: reactions.onStart ?? undefined,
106
+ onSuccess: reactions.onSuccess ?? undefined,
107
+ onError: reactions.onError ?? undefined,
108
+ };
109
+ }
110
+ // ---------------------------------------------------------------------------
111
+ // Inbound message handling
112
+ // ---------------------------------------------------------------------------
113
+ async function handleInboundMessage(ctx, client, msg) {
114
+ const { cfg, account, log } = ctx;
115
+ const isGroup = msg.type === "stream";
116
+ const streamId = msg.stream_id;
117
+ const topic = msg.subject;
118
+ // Build peer info
119
+ let peerId;
120
+ let chatType;
121
+ if (isGroup && streamId != null) {
122
+ peerId = topic ? `${streamId}/${topic}` : String(streamId);
123
+ chatType = "group";
124
+ }
125
+ else {
126
+ peerId = String(msg.sender_id);
127
+ chatType = "direct";
128
+ }
129
+ // Touch any active binding for this conversation so idle timeout resets
130
+ touchZulipBindingByConversation(account.accountId, peerId);
131
+ // Resolve route via channelRuntime
132
+ if (!ctx.channelRuntime) {
133
+ log?.warn("channelRuntime not available — cannot dispatch inbound message");
134
+ return;
135
+ }
136
+ const route = ctx.channelRuntime.routing.resolveAgentRoute({
137
+ cfg,
138
+ channel: CHANNEL_ID,
139
+ accountId: account.accountId,
140
+ peer: { kind: chatType, id: peerId },
141
+ parentPeer: isGroup && streamId != null && topic
142
+ ? { kind: "group", id: String(streamId) }
143
+ : undefined,
144
+ });
145
+ const storePath = ctx.channelRuntime.session.resolveStorePath(undefined, {
146
+ agentId: route.agentId,
147
+ });
148
+ // Build context payload
149
+ const senderName = msg.sender_full_name;
150
+ const senderId = String(msg.sender_id);
151
+ const senderEmail = msg.sender_email;
152
+ const to = isGroup && streamId != null
153
+ ? String(streamId)
154
+ : senderId;
155
+ const groupChannel = isGroup && typeof msg.display_recipient === "string"
156
+ ? `#${msg.display_recipient}`
157
+ : undefined;
158
+ // Resolve command authorization from allowlists
159
+ const { commandAuthorized } = await resolveSenderCommandAuthorization({
160
+ cfg,
161
+ rawBody: msg.content,
162
+ isGroup,
163
+ dmPolicy: account.dmPolicy,
164
+ configuredAllowFrom: account.allowFrom.map(String),
165
+ senderId: senderEmail,
166
+ isSenderAllowed: (sid, allowFrom) => allowFrom.includes(sid) || allowFrom.includes(String(msg.sender_id)) || allowFrom.includes("*"),
167
+ readAllowFromStore: () => ctx.channelRuntime.pairing.readAllowFromStore({
168
+ channel: CHANNEL_ID,
169
+ accountId: account.accountId,
170
+ }),
171
+ shouldComputeCommandAuthorized: ctx.channelRuntime.commands.shouldComputeCommandAuthorized,
172
+ resolveCommandAuthorizedFromAuthorizers: ctx.channelRuntime.commands.resolveCommandAuthorizedFromAuthorizers,
173
+ });
174
+ const ctxPayload = ctx.channelRuntime.reply.finalizeInboundContext({
175
+ Body: msg.content,
176
+ From: senderEmail,
177
+ To: to,
178
+ SessionKey: route.sessionKey,
179
+ AccountId: account.accountId,
180
+ MessageSid: String(msg.id),
181
+ ChatType: chatType,
182
+ SenderName: senderName,
183
+ SenderId: senderEmail,
184
+ SenderUsername: senderEmail,
185
+ Timestamp: msg.timestamp * 1000,
186
+ Provider: CHANNEL_ID,
187
+ Surface: CHANNEL_ID,
188
+ OriginatingChannel: CHANNEL_ID,
189
+ OriginatingTo: to,
190
+ GroupChannel: groupChannel,
191
+ ThreadLabel: topic,
192
+ MessageThreadId: topic,
193
+ CommandAuthorized: commandAuthorized ?? false,
194
+ });
195
+ // ----- Typing indicators -----
196
+ const pipeline = createChannelReplyPipeline({
197
+ cfg,
198
+ agentId: route.agentId,
199
+ channel: CHANNEL_ID,
200
+ accountId: account.accountId,
201
+ typing: {
202
+ start: async () => {
203
+ if (isGroup && streamId != null && topic) {
204
+ await client.sendTypingNotification({
205
+ op: "start",
206
+ type: "stream",
207
+ streamId,
208
+ topic,
209
+ });
210
+ }
211
+ else {
212
+ await client.sendTypingNotification({
213
+ op: "start",
214
+ type: "direct",
215
+ to: [Number(to)],
216
+ });
217
+ }
218
+ },
219
+ stop: async () => {
220
+ if (isGroup && streamId != null && topic) {
221
+ await client.sendTypingNotification({
222
+ op: "stop",
223
+ type: "stream",
224
+ streamId,
225
+ topic,
226
+ });
227
+ }
228
+ else {
229
+ await client.sendTypingNotification({
230
+ op: "stop",
231
+ type: "direct",
232
+ to: [Number(to)],
233
+ });
234
+ }
235
+ },
236
+ onStartError: (err) => log?.debug?.(`Typing start error: ${err}`),
237
+ onStopError: (err) => log?.debug?.(`Typing stop error: ${err}`),
238
+ },
239
+ });
240
+ // ----- Ack reactions -----
241
+ const ackCfg = resolveAckReactions(cfg);
242
+ let ackStartApplied = false;
243
+ if (ackCfg.enabled && ackCfg.onStart) {
244
+ try {
245
+ await client.addReaction(msg.id, ackCfg.onStart);
246
+ ackStartApplied = true;
247
+ }
248
+ catch (err) {
249
+ log?.debug?.(`Ack reaction (onStart) failed: ${err}`);
250
+ }
251
+ }
252
+ let dispatchOk = true;
253
+ // Dispatch reply
254
+ await dispatchInboundReplyWithBase({
255
+ cfg,
256
+ channel: CHANNEL_ID,
257
+ accountId: account.accountId,
258
+ route,
259
+ storePath,
260
+ ctxPayload,
261
+ core: {
262
+ channel: {
263
+ session: {
264
+ recordInboundSession: ctx.channelRuntime.session.recordInboundSession,
265
+ },
266
+ reply: {
267
+ dispatchReplyWithBufferedBlockDispatcher: ctx.channelRuntime.reply.dispatchReplyWithBufferedBlockDispatcher,
268
+ },
269
+ },
270
+ },
271
+ replyOptions: {
272
+ onReplyStart: pipeline.typingCallbacks?.onReplyStart,
273
+ onTypingCleanup: pipeline.typingCallbacks?.onCleanup,
274
+ },
275
+ deliver: async (payload) => {
276
+ const text = payload.text ?? "";
277
+ if (!text.trim() && !payload.mediaUrl && !payload.mediaUrls?.length)
278
+ return;
279
+ const threadId = topic;
280
+ const targetTo = to;
281
+ if (isGroup && streamId != null && threadId) {
282
+ await client.sendMessage({
283
+ type: "stream",
284
+ to: String(streamId),
285
+ topic: threadId,
286
+ content: text,
287
+ });
288
+ }
289
+ else {
290
+ await client.sendMessage({
291
+ type: "direct",
292
+ to: [Number(targetTo)],
293
+ content: text,
294
+ });
295
+ }
296
+ },
297
+ onRecordError: (err) => log?.error(`Session record error: ${err}`),
298
+ onDispatchError: (err) => {
299
+ dispatchOk = false;
300
+ log?.error(`Dispatch error: ${err}`);
301
+ },
302
+ });
303
+ // ----- Finalize ack reactions -----
304
+ if (ackCfg.enabled) {
305
+ if (ackStartApplied && ackCfg.onStart) {
306
+ try {
307
+ await client.removeReaction(msg.id, ackCfg.onStart);
308
+ }
309
+ catch (err) {
310
+ log?.debug?.(`Ack reaction removal failed: ${err}`);
311
+ }
312
+ }
313
+ const terminalEmoji = dispatchOk ? ackCfg.onSuccess : ackCfg.onError;
314
+ if (terminalEmoji) {
315
+ try {
316
+ await client.addReaction(msg.id, terminalEmoji);
317
+ }
318
+ catch (err) {
319
+ log?.debug?.(`Ack terminal reaction failed: ${err}`);
320
+ }
321
+ }
322
+ }
323
+ }
324
+ //# sourceMappingURL=gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
2
+ export declare const zulipGroupsAdapter: NonNullable<ChannelPlugin["groups"]>;
3
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../src/groups.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAQ9D,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,aAAa,CAAC,QAAQ,CAAC,CAuBnE,CAAC"}
package/dist/groups.js ADDED
@@ -0,0 +1,28 @@
1
+ import { resolveZulipAccount } from "./config.js";
2
+ // ---------------------------------------------------------------------------
3
+ // Groups adapter — per-stream policies
4
+ // ---------------------------------------------------------------------------
5
+ export const zulipGroupsAdapter = {
6
+ resolveRequireMention({ cfg, accountId, groupId }) {
7
+ const account = resolveZulipAccount(cfg, accountId);
8
+ if (!groupId)
9
+ return undefined;
10
+ // Check per-stream config for requireMention
11
+ for (const [, streamConfig] of Object.entries(account.streams)) {
12
+ if (streamConfig.requireMention)
13
+ return true;
14
+ }
15
+ // Stream-specific lookup by name requires matching groupId to a stream name
16
+ // groupId is the stream ID string — we can't resolve name without an API call,
17
+ // so we rely on the streams config being keyed by name in the account config
18
+ const streamConfig = account.streams[groupId];
19
+ if (streamConfig?.requireMention !== undefined)
20
+ return streamConfig.requireMention;
21
+ return undefined;
22
+ },
23
+ resolveGroupIntroHint({ cfg, accountId }) {
24
+ const account = resolveZulipAccount(cfg, accountId);
25
+ return `This is a Zulip ${account.mode === "user" ? "user" : "bot"} account. Messages in streams require a topic.`;
26
+ },
27
+ };
28
+ //# sourceMappingURL=groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../src/groups.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,8EAA8E;AAC9E,uCAAuC;AACvC,8EAA8E;AAE9E,MAAM,CAAC,MAAM,kBAAkB,GAAyC;IACtE,qBAAqB,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE;QAC/C,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAiB,EAAE,SAAS,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAE/B,6CAA6C;QAC7C,KAAK,MAAM,CAAC,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/D,IAAI,YAAY,CAAC,cAAc;gBAAE,OAAO,IAAI,CAAC;QAC/C,CAAC;QAED,4EAA4E;QAC5E,+EAA+E;QAC/E,6EAA6E;QAC7E,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,YAAY,EAAE,cAAc,KAAK,SAAS;YAAE,OAAO,YAAY,CAAC,cAAc,CAAC;QAEnF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,qBAAqB,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE;QACtC,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAiB,EAAE,SAAS,CAAC,CAAC;QAClE,OAAO,mBAAmB,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,gDAAgD,CAAC;IACrH,CAAC;CACF,CAAC"}
@@ -0,0 +1,11 @@
1
+ declare const _default: {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ configSchema: import("openclaw/plugin-sdk/core").OpenClawPluginConfigSchema;
6
+ register: (api: import("openclaw/plugin-sdk/core").OpenClawPluginApi) => void;
7
+ channelPlugin: import("openclaw/plugin-sdk/core").ChannelPlugin<import("./types.js").ZulipResolvedAccount, import("./status.js").ZulipProbe>;
8
+ setChannelRuntime?: (runtime: import("openclaw/plugin-sdk/core").PluginRuntime) => void;
9
+ };
10
+ export default _default;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;AAGA,wBAKG"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
2
+ import { zulipPlugin } from "./plugin.js";
3
+ export default defineChannelPluginEntry({
4
+ id: "zulip",
5
+ name: "Zulip",
6
+ description: "OpenClaw channel plugin for Zulip — streams, topics, DMs, and ACP topic bindings.",
7
+ plugin: zulipPlugin,
8
+ });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,eAAe,wBAAwB,CAAC;IACtC,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,WAAW,EAAE,mFAAmF;IAChG,MAAM,EAAE,WAAW;CACpB,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
2
+ export declare const zulipMessagingAdapter: NonNullable<ChannelPlugin["messaging"]>;
3
+ //# sourceMappingURL=messaging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../src/messaging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAiDzE,CAAC"}
@@ -0,0 +1,47 @@
1
+ export const zulipMessagingAdapter = {
2
+ resolveSessionConversation({ rawId }) {
3
+ // rawId formats:
4
+ // group/channel: "<stream_id>" or "<stream_id>/<topic>"
5
+ const slashIdx = rawId.indexOf("/");
6
+ if (slashIdx === -1) {
7
+ return { id: rawId };
8
+ }
9
+ const streamPart = rawId.slice(0, slashIdx);
10
+ const topicPart = rawId.slice(slashIdx + 1);
11
+ return {
12
+ id: rawId,
13
+ threadId: topicPart,
14
+ baseConversationId: streamPart,
15
+ parentConversationCandidates: [streamPart],
16
+ };
17
+ },
18
+ resolveSessionTarget({ id }) {
19
+ return id;
20
+ },
21
+ parseExplicitTarget({ raw }) {
22
+ // Formats: "stream:<stream_id>/<topic>" or "dm:<user_id>" or "user:<user_id_or_email>"
23
+ if (raw.startsWith("dm:")) {
24
+ return { to: raw.slice(3), chatType: "direct" };
25
+ }
26
+ if (raw.startsWith("user:")) {
27
+ return { to: raw.slice(5), chatType: "direct" };
28
+ }
29
+ if (raw.startsWith("stream:")) {
30
+ const rest = raw.slice(7);
31
+ // Support both "stream:name/topic" and "stream:name:topic" separators
32
+ const slashIdx = rest.indexOf("/");
33
+ const colonIdx = rest.indexOf(":");
34
+ const sepIdx = slashIdx !== -1 ? slashIdx : colonIdx;
35
+ if (sepIdx === -1) {
36
+ return { to: rest, chatType: "group" };
37
+ }
38
+ return {
39
+ to: rest.slice(0, sepIdx),
40
+ threadId: rest.slice(sepIdx + 1),
41
+ chatType: "group",
42
+ };
43
+ }
44
+ return null;
45
+ },
46
+ };
47
+ //# sourceMappingURL=messaging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messaging.js","sourceRoot":"","sources":["../src/messaging.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,qBAAqB,GAA4C;IAC5E,0BAA0B,CAAC,EAAE,KAAK,EAAE;QAClC,iBAAiB;QACjB,6DAA6D;QAC7D,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,CAAC,CAAC,EAAE,CAAC;YACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAE5C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,QAAQ,EAAE,SAAS;YACnB,kBAAkB,EAAE,UAAU;YAC9B,4BAA4B,EAAE,CAAC,UAAU,CAAC;SAC3C,CAAC;IACJ,CAAC;IAED,oBAAoB,CAAC,EAAE,EAAE,EAAE;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,mBAAmB,CAAC,EAAE,GAAG,EAAE;QACzB,4FAA4F;QAC5F,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;QAClD,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;QAClD,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,sEAAsE;YACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;YACrD,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YACzC,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC;gBACzB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;gBAChC,QAAQ,EAAE,OAAO;aAClB,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk/core";
2
+ import { ZulipClient } from "./zulip-client.js";
3
+ export declare function buildClient(cfg: OpenClawConfig, accountId?: string | null): ZulipClient;
4
+ /**
5
+ * Parse the outbound `to` field into a resolved target.
6
+ *
7
+ * The SDK may deliver targets in several formats:
8
+ * - Numeric string: "8" (Zulip user ID for DMs, or stream ID for streams)
9
+ * - "user:<email_or_id>" — DM target (SDK convention)
10
+ * - "dm:<user_id>" — DM target (plugin convention)
11
+ * - "stream:<stream_id>/<topic>" — stream target (plugin convention)
12
+ * - Plain stream name or ID — stream target (when threadId is set)
13
+ */
14
+ export declare function resolveOutboundTarget(to: string, threadId?: string | number | null): {
15
+ type: "direct";
16
+ to: number[] | string[];
17
+ topic?: undefined;
18
+ } | {
19
+ type: "stream";
20
+ to: string;
21
+ topic: string;
22
+ };
23
+ export declare const zulipOutboundAdapter: NonNullable<ChannelPlugin["outbound"]>;
24
+ //# sourceMappingURL=outbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.d.ts","sourceRoot":"","sources":["../src/outbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMhD,wBAAgB,WAAW,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAavF;AAuBD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAoBhH;AAiBD,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,aAAa,CAAC,UAAU,CAAC,CAwCvE,CAAC"}
@@ -0,0 +1,114 @@
1
+ import { ZulipClient } from "./zulip-client.js";
2
+ import { resolveZulipAccount } from "./config.js";
3
+ const clientCache = new Map();
4
+ export function buildClient(cfg, accountId) {
5
+ const account = resolveZulipAccount(cfg, accountId);
6
+ const cacheKey = `${account.serverUrl}:${account.email}`;
7
+ let client = clientCache.get(cacheKey);
8
+ if (!client) {
9
+ client = new ZulipClient({
10
+ serverUrl: account.serverUrl,
11
+ email: account.email,
12
+ apiKey: account.apiKey,
13
+ });
14
+ clientCache.set(cacheKey, client);
15
+ }
16
+ return client;
17
+ }
18
+ const ZULIP_TEXT_CHUNK_LIMIT = 10_000;
19
+ function chunkText(text, limit) {
20
+ if (text.length <= limit)
21
+ return [text];
22
+ const chunks = [];
23
+ let remaining = text;
24
+ while (remaining.length > 0) {
25
+ if (remaining.length <= limit) {
26
+ chunks.push(remaining);
27
+ break;
28
+ }
29
+ // Try to break at a newline boundary near the limit
30
+ const slice = remaining.slice(0, limit);
31
+ const lastNewline = slice.lastIndexOf("\n");
32
+ const breakAt = lastNewline > limit * 0.5 ? lastNewline + 1 : limit;
33
+ chunks.push(remaining.slice(0, breakAt));
34
+ remaining = remaining.slice(breakAt);
35
+ }
36
+ return chunks;
37
+ }
38
+ /**
39
+ * Parse the outbound `to` field into a resolved target.
40
+ *
41
+ * The SDK may deliver targets in several formats:
42
+ * - Numeric string: "8" (Zulip user ID for DMs, or stream ID for streams)
43
+ * - "user:<email_or_id>" — DM target (SDK convention)
44
+ * - "dm:<user_id>" — DM target (plugin convention)
45
+ * - "stream:<stream_id>/<topic>" — stream target (plugin convention)
46
+ * - Plain stream name or ID — stream target (when threadId is set)
47
+ */
48
+ export function resolveOutboundTarget(to, threadId) {
49
+ // Explicit DM prefixes always win
50
+ if (to.startsWith("user:") || to.startsWith("dm:")) {
51
+ const recipient = to.startsWith("user:") ? to.slice(5) : to.slice(3);
52
+ const asNum = Number(recipient);
53
+ return {
54
+ type: "direct",
55
+ to: Number.isFinite(asNum) && String(asNum) === recipient
56
+ ? [asNum]
57
+ : [recipient],
58
+ };
59
+ }
60
+ // No threadId → DM
61
+ if (!threadId) {
62
+ return { type: "direct", to: [Number(to)] };
63
+ }
64
+ // Stream message
65
+ return { type: "stream", to, topic: String(threadId) };
66
+ }
67
+ async function sendToZulip(client, ctx) {
68
+ const target = resolveOutboundTarget(ctx.to, ctx.threadId);
69
+ const res = await client.sendMessage({
70
+ type: target.type,
71
+ to: target.to,
72
+ topic: target.type === "stream" ? target.topic : undefined,
73
+ content: ctx.text,
74
+ });
75
+ return { channel: "zulip", messageId: String(res.id) };
76
+ }
77
+ export const zulipOutboundAdapter = {
78
+ deliveryMode: "direct",
79
+ textChunkLimit: ZULIP_TEXT_CHUNK_LIMIT,
80
+ chunkerMode: "markdown",
81
+ async sendText(ctx) {
82
+ const client = buildClient(ctx.cfg, ctx.accountId);
83
+ return sendToZulip(client, ctx);
84
+ },
85
+ async sendFormattedText(ctx) {
86
+ const client = buildClient(ctx.cfg, ctx.accountId);
87
+ const chunks = chunkText(ctx.text, ZULIP_TEXT_CHUNK_LIMIT);
88
+ const results = [];
89
+ for (const chunk of chunks) {
90
+ const result = await sendToZulip(client, { ...ctx, text: chunk });
91
+ results.push(result);
92
+ if (ctx.abortSignal?.aborted)
93
+ break;
94
+ }
95
+ return results;
96
+ },
97
+ async sendMedia(ctx) {
98
+ const client = buildClient(ctx.cfg, ctx.accountId);
99
+ let content = ctx.text || "";
100
+ if (ctx.mediaUrl && ctx.mediaReadFile) {
101
+ const buffer = await ctx.mediaReadFile(ctx.mediaUrl);
102
+ const filename = ctx.mediaUrl.split("/").pop() ?? "file";
103
+ const upload = await client.uploadFile(filename, buffer);
104
+ // Zulip inline image/file syntax: [filename](url)
105
+ const mediaLine = `[${filename}](${upload.uri})`;
106
+ content = content ? `${mediaLine}\n${content}` : mediaLine;
107
+ }
108
+ else if (ctx.mediaUrl) {
109
+ content = content ? `${ctx.mediaUrl}\n${content}` : ctx.mediaUrl;
110
+ }
111
+ return sendToZulip(client, { ...ctx, text: content });
112
+ },
113
+ };
114
+ //# sourceMappingURL=outbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.js","sourceRoot":"","sources":["../src/outbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
2
+ import type { ZulipResolvedAccount } from "./types.js";
3
+ import type { ZulipProbe } from "./status.js";
4
+ export declare const zulipPlugin: ChannelPlugin<ZulipResolvedAccount, ZulipProbe>;
5
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAmB9C,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,oBAAoB,EAAE,UAAU,CA8CtE,CAAC"}