@alfe.ai/openclaw-google-chat 0.0.4 → 0.0.6

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/plugin2.cjs CHANGED
@@ -156,9 +156,36 @@ async function listMembers(token, spaceName) {
156
156
  } while (pageToken);
157
157
  return allMembers;
158
158
  }
159
- /** Fetch the last N messages from a space (for backfill on new space discovery). */
160
- async function listRecentMessages(token, spaceName, limit) {
161
- return (await request(token, `/${spaceName}/messages?${new URLSearchParams({ pageSize: String(limit) }).toString()}`)).messages ?? [];
159
+ //#endregion
160
+ //#region src/markdown-to-gchat.ts
161
+ /**
162
+ * Convert standard Markdown to Google Chat text formatting.
163
+ *
164
+ * Google Chat supports: *bold*, _italic_, ~strikethrough~, `code`,
165
+ * ```code blocks```, but NOT standard Markdown syntax like **bold**,
166
+ * ## headings, or - bullet lists.
167
+ */
168
+ const CODE_BLOCK_RE = /```[\s\S]*?```/g;
169
+ const INLINE_CODE_RE = /`[^`]+`/g;
170
+ function markdownToGChat(text) {
171
+ const preserved = [];
172
+ const placeholder = (i) => `\x00CODE${String(i)}\x00`;
173
+ let result = text.replace(CODE_BLOCK_RE, (match) => {
174
+ preserved.push(match);
175
+ return placeholder(preserved.length - 1);
176
+ });
177
+ result = result.replace(INLINE_CODE_RE, (match) => {
178
+ preserved.push(match);
179
+ return placeholder(preserved.length - 1);
180
+ });
181
+ result = result.replace(/^#{1,6}\s+(.+)$/gm, "*$1*");
182
+ result = result.replace(/\*\*(.+?)\*\*/g, "*$1*");
183
+ result = result.replace(/__(.+?)__/g, "*$1*");
184
+ result = result.replace(/~~(.+?)~~/g, "~$1~");
185
+ result = result.replace(/^[\s]*[-*]\s+/gm, "• ");
186
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
187
+ for (let i = preserved.length - 1; i >= 0; i--) result = result.replace(placeholder(i), () => preserved[i]);
188
+ return result;
162
189
  }
163
190
  //#endregion
164
191
  //#region src/gchat-poller.ts
@@ -171,7 +198,6 @@ const WARM_THRESHOLD_MS = 1800 * 1e3;
171
198
  const DORMANT_THRESHOLD_MS = 7200 * 1e3;
172
199
  const DISCOVERY_INTERVAL_MS = 3e4;
173
200
  const DEDUP_SIZE = 100;
174
- const BACKFILL_COUNT = 20;
175
201
  const MAX_RETRY_DELAY_MS = 6e4;
176
202
  const BASE_RETRY_DELAY_MS = 2e3;
177
203
  var GChatPoller = class {
@@ -254,13 +280,7 @@ var GChatPoller = class {
254
280
  } catch (err) {
255
281
  this.log.warn(`Failed to resolve members for ${spaceName}: ${err instanceof Error ? err.message : String(err)}`);
256
282
  }
257
- let lastSeenTimestamp = this.stateManager.getLastSeenTimestamp(spaceName);
258
- try {
259
- const recent = await listRecentMessages(token, spaceName, BACKFILL_COUNT);
260
- if (recent.length > 0) lastSeenTimestamp = recent[recent.length - 1].createTime;
261
- } catch (err) {
262
- this.log.warn(`Backfill failed for ${spaceName}: ${err instanceof Error ? err.message : String(err)}`);
263
- }
283
+ const lastSeenTimestamp = this.stateManager.getLastSeenTimestamp(spaceName) || (/* @__PURE__ */ new Date()).toISOString();
264
284
  this.stateManager.updateSpace(spaceName, {
265
285
  lastSeenTimestamp,
266
286
  agentUserId,
@@ -355,7 +375,9 @@ var GChatPoller = class {
355
375
  return;
356
376
  }
357
377
  const conversationId = `alfe:gchat:${state.spaceName}`;
358
- const peerLabel = state.peerDisplayName ?? "User";
378
+ const senderName = message.sender.name || state.peerUserId;
379
+ const senderDisplayName = message.sender.displayName;
380
+ const peerLabel = senderDisplayName || state.peerDisplayName || "User";
359
381
  this.log.info(`Dispatching message from ${peerLabel} in ${state.spaceName}`);
360
382
  try {
361
383
  const cfg = this.runtime.config.loadConfig();
@@ -367,10 +389,10 @@ var GChatPoller = class {
367
389
  accountId: "default",
368
390
  peer: {
369
391
  kind: "direct",
370
- id: `gchat:${state.peerUserId}:conv:${conversationId}`
392
+ id: `gchat:${senderName}:conv:${conversationId}`
371
393
  },
372
- senderId: `gchat:${state.peerUserId}`,
373
- senderAddress: `user:gchat:${state.peerUserId}`,
394
+ senderId: `gchat:${senderName}`,
395
+ senderAddress: `user:gchat:${senderName}`,
374
396
  recipientAddress: "agent",
375
397
  conversationLabel: `[Google Chat] ${peerLabel}`,
376
398
  rawBody: message.text,
@@ -379,11 +401,12 @@ var GChatPoller = class {
379
401
  extraContext: {
380
402
  ChannelMode: "gchat",
381
403
  ConversationId: conversationId,
382
- ...state.peerDisplayName ? { SenderName: state.peerDisplayName } : {}
404
+ ...senderDisplayName ? { SenderName: senderDisplayName } : {}
383
405
  },
384
406
  deliver: async (payload) => {
385
- const text = payload.text ?? "";
386
- if (!text) return;
407
+ const raw = payload.text ?? "";
408
+ if (!raw) return;
409
+ const text = markdownToGChat(raw);
387
410
  try {
388
411
  await sendMessage(await this.tokenManager.getAccessToken(), state.spaceName, text);
389
412
  } catch (err) {
package/dist/plugin2.js CHANGED
@@ -156,9 +156,36 @@ async function listMembers(token, spaceName) {
156
156
  } while (pageToken);
157
157
  return allMembers;
158
158
  }
159
- /** Fetch the last N messages from a space (for backfill on new space discovery). */
160
- async function listRecentMessages(token, spaceName, limit) {
161
- return (await request(token, `/${spaceName}/messages?${new URLSearchParams({ pageSize: String(limit) }).toString()}`)).messages ?? [];
159
+ //#endregion
160
+ //#region src/markdown-to-gchat.ts
161
+ /**
162
+ * Convert standard Markdown to Google Chat text formatting.
163
+ *
164
+ * Google Chat supports: *bold*, _italic_, ~strikethrough~, `code`,
165
+ * ```code blocks```, but NOT standard Markdown syntax like **bold**,
166
+ * ## headings, or - bullet lists.
167
+ */
168
+ const CODE_BLOCK_RE = /```[\s\S]*?```/g;
169
+ const INLINE_CODE_RE = /`[^`]+`/g;
170
+ function markdownToGChat(text) {
171
+ const preserved = [];
172
+ const placeholder = (i) => `\x00CODE${String(i)}\x00`;
173
+ let result = text.replace(CODE_BLOCK_RE, (match) => {
174
+ preserved.push(match);
175
+ return placeholder(preserved.length - 1);
176
+ });
177
+ result = result.replace(INLINE_CODE_RE, (match) => {
178
+ preserved.push(match);
179
+ return placeholder(preserved.length - 1);
180
+ });
181
+ result = result.replace(/^#{1,6}\s+(.+)$/gm, "*$1*");
182
+ result = result.replace(/\*\*(.+?)\*\*/g, "*$1*");
183
+ result = result.replace(/__(.+?)__/g, "*$1*");
184
+ result = result.replace(/~~(.+?)~~/g, "~$1~");
185
+ result = result.replace(/^[\s]*[-*]\s+/gm, "• ");
186
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
187
+ for (let i = preserved.length - 1; i >= 0; i--) result = result.replace(placeholder(i), () => preserved[i]);
188
+ return result;
162
189
  }
163
190
  //#endregion
164
191
  //#region src/gchat-poller.ts
@@ -171,7 +198,6 @@ const WARM_THRESHOLD_MS = 1800 * 1e3;
171
198
  const DORMANT_THRESHOLD_MS = 7200 * 1e3;
172
199
  const DISCOVERY_INTERVAL_MS = 3e4;
173
200
  const DEDUP_SIZE = 100;
174
- const BACKFILL_COUNT = 20;
175
201
  const MAX_RETRY_DELAY_MS = 6e4;
176
202
  const BASE_RETRY_DELAY_MS = 2e3;
177
203
  var GChatPoller = class {
@@ -254,13 +280,7 @@ var GChatPoller = class {
254
280
  } catch (err) {
255
281
  this.log.warn(`Failed to resolve members for ${spaceName}: ${err instanceof Error ? err.message : String(err)}`);
256
282
  }
257
- let lastSeenTimestamp = this.stateManager.getLastSeenTimestamp(spaceName);
258
- try {
259
- const recent = await listRecentMessages(token, spaceName, BACKFILL_COUNT);
260
- if (recent.length > 0) lastSeenTimestamp = recent[recent.length - 1].createTime;
261
- } catch (err) {
262
- this.log.warn(`Backfill failed for ${spaceName}: ${err instanceof Error ? err.message : String(err)}`);
263
- }
283
+ const lastSeenTimestamp = this.stateManager.getLastSeenTimestamp(spaceName) || (/* @__PURE__ */ new Date()).toISOString();
264
284
  this.stateManager.updateSpace(spaceName, {
265
285
  lastSeenTimestamp,
266
286
  agentUserId,
@@ -355,7 +375,9 @@ var GChatPoller = class {
355
375
  return;
356
376
  }
357
377
  const conversationId = `alfe:gchat:${state.spaceName}`;
358
- const peerLabel = state.peerDisplayName ?? "User";
378
+ const senderName = message.sender.name || state.peerUserId;
379
+ const senderDisplayName = message.sender.displayName;
380
+ const peerLabel = senderDisplayName || state.peerDisplayName || "User";
359
381
  this.log.info(`Dispatching message from ${peerLabel} in ${state.spaceName}`);
360
382
  try {
361
383
  const cfg = this.runtime.config.loadConfig();
@@ -367,10 +389,10 @@ var GChatPoller = class {
367
389
  accountId: "default",
368
390
  peer: {
369
391
  kind: "direct",
370
- id: `gchat:${state.peerUserId}:conv:${conversationId}`
392
+ id: `gchat:${senderName}:conv:${conversationId}`
371
393
  },
372
- senderId: `gchat:${state.peerUserId}`,
373
- senderAddress: `user:gchat:${state.peerUserId}`,
394
+ senderId: `gchat:${senderName}`,
395
+ senderAddress: `user:gchat:${senderName}`,
374
396
  recipientAddress: "agent",
375
397
  conversationLabel: `[Google Chat] ${peerLabel}`,
376
398
  rawBody: message.text,
@@ -379,11 +401,12 @@ var GChatPoller = class {
379
401
  extraContext: {
380
402
  ChannelMode: "gchat",
381
403
  ConversationId: conversationId,
382
- ...state.peerDisplayName ? { SenderName: state.peerDisplayName } : {}
404
+ ...senderDisplayName ? { SenderName: senderDisplayName } : {}
383
405
  },
384
406
  deliver: async (payload) => {
385
- const text = payload.text ?? "";
386
- if (!text) return;
407
+ const raw = payload.text ?? "";
408
+ if (!raw) return;
409
+ const text = markdownToGChat(raw);
387
410
  try {
388
411
  await sendMessage(await this.tokenManager.getAccessToken(), state.spaceName, text);
389
412
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-google-chat",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "OpenClaw Google Chat plugin — DM polling and auto-reply via connected Google account",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin.js",