@alfe.ai/openclaw-google-chat 0.0.8 → 0.0.9

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
@@ -55,6 +55,17 @@ var StateManager = class {
55
55
  getLastSeenTimestamp(spaceName) {
56
56
  return this.state.spaces[spaceName]?.lastSeenTimestamp;
57
57
  }
58
+ /** Get cached agent user ID, but only if it was resolved for the same email. */
59
+ getCachedAgentUserId(currentEmail) {
60
+ if (this.state.agentEmail !== currentEmail) return void 0;
61
+ return this.state.agentUserId;
62
+ }
63
+ /** Cache the agent's global user ID and the email it was resolved from. */
64
+ setAgentIdentity(userId, email) {
65
+ this.state.agentUserId = userId;
66
+ this.state.agentEmail = email;
67
+ this.dirty = true;
68
+ }
58
69
  /** Get the agent's userId for a space (if previously resolved). */
59
70
  getAgentUserId(spaceName) {
60
71
  return this.state.spaces[spaceName]?.agentUserId;
@@ -143,6 +154,10 @@ async function sendMessage(token, spaceName, text) {
143
154
  body: JSON.stringify({ text })
144
155
  });
145
156
  }
157
+ /** Get a specific member of a space by user resource name or email alias. */
158
+ async function getMember(token, spaceName, userId) {
159
+ return request(token, `/${spaceName}/members/${userId}`);
160
+ }
146
161
  /** List members of a space. */
147
162
  async function listMembers(token, spaceName) {
148
163
  const allMembers = [];
@@ -207,6 +222,7 @@ var GChatPoller = class {
207
222
  log;
208
223
  stateManager;
209
224
  agentEmail;
225
+ agentClient;
210
226
  spaces = /* @__PURE__ */ new Map();
211
227
  seenMessages = /* @__PURE__ */ new Map();
212
228
  discoveryTimer = null;
@@ -217,6 +233,7 @@ var GChatPoller = class {
217
233
  this.runtime = init.runtime;
218
234
  this.log = init.log;
219
235
  this.agentEmail = init.agentEmail;
236
+ this.agentClient = init.agentClient ?? null;
220
237
  this.stateManager = new StateManager(init.log);
221
238
  }
222
239
  /** Start the polling engine. */
@@ -272,8 +289,7 @@ var GChatPoller = class {
272
289
  let peerUserId = this.stateManager.getPeerUserId(spaceName) ?? "";
273
290
  let peerDisplayName = null;
274
291
  if (!agentUserId || !peerUserId) try {
275
- const members = await listMembers(token, spaceName);
276
- const resolved = this.resolveMembers(members);
292
+ const resolved = await this.resolveMembers(token, spaceName);
277
293
  agentUserId = resolved.agentUserId;
278
294
  peerUserId = resolved.peerUserId;
279
295
  peerDisplayName = resolved.peerDisplayName;
@@ -299,20 +315,25 @@ var GChatPoller = class {
299
315
  this.seenMessages.set(spaceName, /* @__PURE__ */ new Set());
300
316
  this.scheduleSpacePoll(state);
301
317
  }
302
- resolveMembers(members) {
303
- let agentUserId = "";
318
+ async resolveMembers(token, spaceName) {
319
+ let agentUserId = this.stateManager.getCachedAgentUserId(this.agentEmail) ?? "";
320
+ if (!agentUserId && this.agentEmail) try {
321
+ agentUserId = (await getMember(token, spaceName, `users/${this.agentEmail}`)).member.name;
322
+ this.stateManager.setAgentIdentity(agentUserId, this.agentEmail);
323
+ this.log.info(`Resolved agent user ID: ${agentUserId}`);
324
+ } catch (err) {
325
+ this.log.warn(`Failed to look up agent by email: ${err instanceof Error ? err.message : String(err)}`);
326
+ }
327
+ const humans = (await listMembers(token, spaceName)).filter((m) => m.member.type === "HUMAN");
304
328
  let peerUserId = "";
305
329
  let peerDisplayName = null;
306
- const normalize = (s) => s.toLowerCase().replace(/[.\s_-]/g, "");
307
- const agentPrefix = this.agentEmail ? normalize(this.agentEmail.split("@")[0]) : "";
308
- const humans = members.filter((m) => m.member.type === "HUMAN");
309
- for (const m of humans) if (agentPrefix && normalize(m.member.displayName ?? "").includes(agentPrefix)) agentUserId = m.member.name;
310
- for (const m of humans) if (m.member.name !== agentUserId) {
330
+ if (agentUserId) for (const m of humans) {
331
+ if (m.member.name === agentUserId) continue;
311
332
  peerUserId = m.member.name;
312
333
  peerDisplayName = m.member.displayName || null;
313
334
  break;
314
335
  }
315
- if (!agentUserId && humans.length >= 2) {
336
+ else if (humans.length >= 2) {
316
337
  agentUserId = humans[0].member.name;
317
338
  peerUserId = humans[1].member.name;
318
339
  peerDisplayName = humans[1].member.displayName || null;
@@ -380,6 +401,23 @@ var GChatPoller = class {
380
401
  const senderDisplayName = message.sender.displayName;
381
402
  const peerLabel = senderDisplayName || state.peerDisplayName || "User";
382
403
  this.log.info(`Dispatching message from ${peerLabel} in ${state.spaceName}`);
404
+ let resolvedIdentityId;
405
+ let accessAllowed = true;
406
+ if (this.agentClient) try {
407
+ const senderUserId = senderName.startsWith("users/") ? senderName.slice(6) : senderName;
408
+ const result = await this.agentClient.resolveGoogleChatSender({
409
+ senderUserId,
410
+ spaceId: state.spaceName
411
+ });
412
+ resolvedIdentityId = result.identityId ?? void 0;
413
+ accessAllowed = result.accessAllowed;
414
+ if (!accessAllowed) {
415
+ this.log.info(`Skipping dispatch — access denied for sender ${peerLabel} (${senderUserId})`);
416
+ return;
417
+ }
418
+ } catch (err) {
419
+ this.log.warn(`Sender resolve failed; dispatching without IdentityId: ${err instanceof Error ? err.message : String(err)}`);
420
+ }
383
421
  try {
384
422
  const cfg = this.runtime.config.loadConfig();
385
423
  await this.dispatchInbound({
@@ -402,7 +440,8 @@ var GChatPoller = class {
402
440
  extraContext: {
403
441
  ChannelMode: "gchat",
404
442
  ConversationId: conversationId,
405
- ...senderDisplayName ? { SenderName: senderDisplayName } : {}
443
+ ...senderDisplayName ? { SenderName: senderDisplayName } : {},
444
+ ...resolvedIdentityId ? { IdentityId: resolvedIdentityId } : {}
406
445
  },
407
446
  deliver: async (payload) => {
408
447
  const raw = payload.text ?? "";
@@ -550,7 +589,7 @@ const plugin = {
550
589
  log.info("Google Chat not connected — polling disabled");
551
590
  return;
552
591
  }
553
- poller = new GChatPoller({
592
+ const init = {
554
593
  tokenManager: new (await (Promise.resolve().then(() => require("./gchat-token.cjs")))).TokenManager({
555
594
  refreshToken: creds.refreshToken,
556
595
  clientId: creds.clientId,
@@ -560,7 +599,9 @@ const plugin = {
560
599
  runtime: pluginRuntime,
561
600
  log,
562
601
  agentEmail: creds.email
563
- });
602
+ };
603
+ init.agentClient = client;
604
+ poller = new GChatPoller(init);
564
605
  await poller.start();
565
606
  log.info(`Google Chat poller started (account: ${creds.email})`);
566
607
  };
package/dist/plugin2.js CHANGED
@@ -55,6 +55,17 @@ var StateManager = class {
55
55
  getLastSeenTimestamp(spaceName) {
56
56
  return this.state.spaces[spaceName]?.lastSeenTimestamp;
57
57
  }
58
+ /** Get cached agent user ID, but only if it was resolved for the same email. */
59
+ getCachedAgentUserId(currentEmail) {
60
+ if (this.state.agentEmail !== currentEmail) return void 0;
61
+ return this.state.agentUserId;
62
+ }
63
+ /** Cache the agent's global user ID and the email it was resolved from. */
64
+ setAgentIdentity(userId, email) {
65
+ this.state.agentUserId = userId;
66
+ this.state.agentEmail = email;
67
+ this.dirty = true;
68
+ }
58
69
  /** Get the agent's userId for a space (if previously resolved). */
59
70
  getAgentUserId(spaceName) {
60
71
  return this.state.spaces[spaceName]?.agentUserId;
@@ -143,6 +154,10 @@ async function sendMessage(token, spaceName, text) {
143
154
  body: JSON.stringify({ text })
144
155
  });
145
156
  }
157
+ /** Get a specific member of a space by user resource name or email alias. */
158
+ async function getMember(token, spaceName, userId) {
159
+ return request(token, `/${spaceName}/members/${userId}`);
160
+ }
146
161
  /** List members of a space. */
147
162
  async function listMembers(token, spaceName) {
148
163
  const allMembers = [];
@@ -207,6 +222,7 @@ var GChatPoller = class {
207
222
  log;
208
223
  stateManager;
209
224
  agentEmail;
225
+ agentClient;
210
226
  spaces = /* @__PURE__ */ new Map();
211
227
  seenMessages = /* @__PURE__ */ new Map();
212
228
  discoveryTimer = null;
@@ -217,6 +233,7 @@ var GChatPoller = class {
217
233
  this.runtime = init.runtime;
218
234
  this.log = init.log;
219
235
  this.agentEmail = init.agentEmail;
236
+ this.agentClient = init.agentClient ?? null;
220
237
  this.stateManager = new StateManager(init.log);
221
238
  }
222
239
  /** Start the polling engine. */
@@ -272,8 +289,7 @@ var GChatPoller = class {
272
289
  let peerUserId = this.stateManager.getPeerUserId(spaceName) ?? "";
273
290
  let peerDisplayName = null;
274
291
  if (!agentUserId || !peerUserId) try {
275
- const members = await listMembers(token, spaceName);
276
- const resolved = this.resolveMembers(members);
292
+ const resolved = await this.resolveMembers(token, spaceName);
277
293
  agentUserId = resolved.agentUserId;
278
294
  peerUserId = resolved.peerUserId;
279
295
  peerDisplayName = resolved.peerDisplayName;
@@ -299,20 +315,25 @@ var GChatPoller = class {
299
315
  this.seenMessages.set(spaceName, /* @__PURE__ */ new Set());
300
316
  this.scheduleSpacePoll(state);
301
317
  }
302
- resolveMembers(members) {
303
- let agentUserId = "";
318
+ async resolveMembers(token, spaceName) {
319
+ let agentUserId = this.stateManager.getCachedAgentUserId(this.agentEmail) ?? "";
320
+ if (!agentUserId && this.agentEmail) try {
321
+ agentUserId = (await getMember(token, spaceName, `users/${this.agentEmail}`)).member.name;
322
+ this.stateManager.setAgentIdentity(agentUserId, this.agentEmail);
323
+ this.log.info(`Resolved agent user ID: ${agentUserId}`);
324
+ } catch (err) {
325
+ this.log.warn(`Failed to look up agent by email: ${err instanceof Error ? err.message : String(err)}`);
326
+ }
327
+ const humans = (await listMembers(token, spaceName)).filter((m) => m.member.type === "HUMAN");
304
328
  let peerUserId = "";
305
329
  let peerDisplayName = null;
306
- const normalize = (s) => s.toLowerCase().replace(/[.\s_-]/g, "");
307
- const agentPrefix = this.agentEmail ? normalize(this.agentEmail.split("@")[0]) : "";
308
- const humans = members.filter((m) => m.member.type === "HUMAN");
309
- for (const m of humans) if (agentPrefix && normalize(m.member.displayName ?? "").includes(agentPrefix)) agentUserId = m.member.name;
310
- for (const m of humans) if (m.member.name !== agentUserId) {
330
+ if (agentUserId) for (const m of humans) {
331
+ if (m.member.name === agentUserId) continue;
311
332
  peerUserId = m.member.name;
312
333
  peerDisplayName = m.member.displayName || null;
313
334
  break;
314
335
  }
315
- if (!agentUserId && humans.length >= 2) {
336
+ else if (humans.length >= 2) {
316
337
  agentUserId = humans[0].member.name;
317
338
  peerUserId = humans[1].member.name;
318
339
  peerDisplayName = humans[1].member.displayName || null;
@@ -380,6 +401,23 @@ var GChatPoller = class {
380
401
  const senderDisplayName = message.sender.displayName;
381
402
  const peerLabel = senderDisplayName || state.peerDisplayName || "User";
382
403
  this.log.info(`Dispatching message from ${peerLabel} in ${state.spaceName}`);
404
+ let resolvedIdentityId;
405
+ let accessAllowed = true;
406
+ if (this.agentClient) try {
407
+ const senderUserId = senderName.startsWith("users/") ? senderName.slice(6) : senderName;
408
+ const result = await this.agentClient.resolveGoogleChatSender({
409
+ senderUserId,
410
+ spaceId: state.spaceName
411
+ });
412
+ resolvedIdentityId = result.identityId ?? void 0;
413
+ accessAllowed = result.accessAllowed;
414
+ if (!accessAllowed) {
415
+ this.log.info(`Skipping dispatch — access denied for sender ${peerLabel} (${senderUserId})`);
416
+ return;
417
+ }
418
+ } catch (err) {
419
+ this.log.warn(`Sender resolve failed; dispatching without IdentityId: ${err instanceof Error ? err.message : String(err)}`);
420
+ }
383
421
  try {
384
422
  const cfg = this.runtime.config.loadConfig();
385
423
  await this.dispatchInbound({
@@ -402,7 +440,8 @@ var GChatPoller = class {
402
440
  extraContext: {
403
441
  ChannelMode: "gchat",
404
442
  ConversationId: conversationId,
405
- ...senderDisplayName ? { SenderName: senderDisplayName } : {}
443
+ ...senderDisplayName ? { SenderName: senderDisplayName } : {},
444
+ ...resolvedIdentityId ? { IdentityId: resolvedIdentityId } : {}
406
445
  },
407
446
  deliver: async (payload) => {
408
447
  const raw = payload.text ?? "";
@@ -550,7 +589,7 @@ const plugin = {
550
589
  log.info("Google Chat not connected — polling disabled");
551
590
  return;
552
591
  }
553
- poller = new GChatPoller({
592
+ const init = {
554
593
  tokenManager: new (await (import("./gchat-token.js"))).TokenManager({
555
594
  refreshToken: creds.refreshToken,
556
595
  clientId: creds.clientId,
@@ -560,7 +599,9 @@ const plugin = {
560
599
  runtime: pluginRuntime,
561
600
  log,
562
601
  agentEmail: creds.email
563
- });
602
+ };
603
+ init.agentClient = client;
604
+ poller = new GChatPoller(init);
564
605
  await poller.start();
565
606
  log.info(`Google Chat poller started (account: ${creds.email})`);
566
607
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/openclaw-google-chat",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
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",
@@ -27,7 +27,7 @@
27
27
  "openclaw.plugin.json"
28
28
  ],
29
29
  "dependencies": {
30
- "@alfe.ai/agent-api-client": "^0.0.13",
30
+ "@alfe.ai/agent-api-client": "^0.1.0",
31
31
  "@alfe.ai/config": "^0.0.8"
32
32
  },
33
33
  "peerDependencies": {