@elizaos/plugin-google-chat 2.0.0-alpha.6 → 2.0.3-beta.2

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 DELETED
@@ -1,1115 +0,0 @@
1
- // src/index.ts
2
- import { logger as logger3 } from "@elizaos/core";
3
-
4
- // src/types.ts
5
- var MAX_GOOGLE_CHAT_MESSAGE_LENGTH = 4000;
6
- var GOOGLE_CHAT_SERVICE_NAME = "google-chat";
7
- var GoogleChatEventTypes;
8
- ((GoogleChatEventTypes2) => {
9
- GoogleChatEventTypes2["MESSAGE_RECEIVED"] = "GOOGLE_CHAT_MESSAGE_RECEIVED";
10
- GoogleChatEventTypes2["MESSAGE_SENT"] = "GOOGLE_CHAT_MESSAGE_SENT";
11
- GoogleChatEventTypes2["SPACE_JOINED"] = "GOOGLE_CHAT_SPACE_JOINED";
12
- GoogleChatEventTypes2["SPACE_LEFT"] = "GOOGLE_CHAT_SPACE_LEFT";
13
- GoogleChatEventTypes2["REACTION_RECEIVED"] = "GOOGLE_CHAT_REACTION_RECEIVED";
14
- GoogleChatEventTypes2["REACTION_SENT"] = "GOOGLE_CHAT_REACTION_SENT";
15
- GoogleChatEventTypes2["WEBHOOK_READY"] = "GOOGLE_CHAT_WEBHOOK_READY";
16
- GoogleChatEventTypes2["CONNECTION_READY"] = "GOOGLE_CHAT_CONNECTION_READY";
17
- })(GoogleChatEventTypes ||= {});
18
-
19
- class GoogleChatPluginError extends Error {
20
- code;
21
- cause;
22
- constructor(message, code, cause) {
23
- super(message);
24
- this.code = code;
25
- this.cause = cause;
26
- this.name = "GoogleChatPluginError";
27
- }
28
- }
29
-
30
- class GoogleChatConfigurationError extends GoogleChatPluginError {
31
- setting;
32
- constructor(message, setting, cause) {
33
- super(message, "CONFIGURATION_ERROR", cause);
34
- this.name = "GoogleChatConfigurationError";
35
- this.setting = setting;
36
- }
37
- }
38
-
39
- class GoogleChatApiError extends GoogleChatPluginError {
40
- statusCode;
41
- constructor(message, statusCode, cause) {
42
- super(message, "API_ERROR", cause);
43
- this.name = "GoogleChatApiError";
44
- this.statusCode = statusCode;
45
- }
46
- }
47
-
48
- class GoogleChatAuthenticationError extends GoogleChatPluginError {
49
- constructor(message, cause) {
50
- super(message, "AUTHENTICATION_ERROR", cause);
51
- this.name = "GoogleChatAuthenticationError";
52
- }
53
- }
54
- var SPACE_NAME_REGEX = /^spaces\/[A-Za-z0-9_-]+$/;
55
- var USER_NAME_REGEX = /^users\/[A-Za-z0-9_-]+$/;
56
- function isValidGoogleChatSpaceName(name) {
57
- return SPACE_NAME_REGEX.test(name);
58
- }
59
- function isValidGoogleChatUserName(name) {
60
- return USER_NAME_REGEX.test(name);
61
- }
62
- function normalizeSpaceTarget(target) {
63
- const trimmed = target.trim();
64
- if (!trimmed) {
65
- return null;
66
- }
67
- if (trimmed.startsWith("spaces/")) {
68
- return trimmed;
69
- }
70
- if (/^[A-Za-z0-9_-]+$/.test(trimmed)) {
71
- return `spaces/${trimmed}`;
72
- }
73
- return null;
74
- }
75
- function normalizeUserTarget(target) {
76
- const trimmed = target.trim();
77
- if (!trimmed) {
78
- return null;
79
- }
80
- if (trimmed.startsWith("users/")) {
81
- return trimmed;
82
- }
83
- if (/^[A-Za-z0-9_-]+$/.test(trimmed)) {
84
- return `users/${trimmed}`;
85
- }
86
- return null;
87
- }
88
- function extractResourceId(resourceName) {
89
- const parts = resourceName.split("/");
90
- return parts[parts.length - 1];
91
- }
92
- function getUserDisplayName(user) {
93
- return user.displayName || extractResourceId(user.name);
94
- }
95
- function getSpaceDisplayName(space) {
96
- return space.displayName || extractResourceId(space.name);
97
- }
98
- function isDirectMessage(space) {
99
- return space.type === "DM" || space.singleUserBotDm === true;
100
- }
101
- function splitMessageForGoogleChat(text, maxLength = MAX_GOOGLE_CHAT_MESSAGE_LENGTH) {
102
- if (text.length <= maxLength) {
103
- return [text];
104
- }
105
- const chunks = [];
106
- let remaining = text;
107
- while (remaining.length > 0) {
108
- if (remaining.length <= maxLength) {
109
- chunks.push(remaining);
110
- break;
111
- }
112
- let breakPoint = maxLength;
113
- const newlineIndex = remaining.lastIndexOf(`
114
- `, maxLength);
115
- if (newlineIndex > maxLength * 0.5) {
116
- breakPoint = newlineIndex + 1;
117
- } else {
118
- const spaceIndex = remaining.lastIndexOf(" ", maxLength);
119
- if (spaceIndex > maxLength * 0.5) {
120
- breakPoint = spaceIndex + 1;
121
- }
122
- }
123
- chunks.push(remaining.slice(0, breakPoint).trimEnd());
124
- remaining = remaining.slice(breakPoint).trimStart();
125
- }
126
- return chunks;
127
- }
128
-
129
- // src/actions/listSpaces.ts
130
- var listSpaces = {
131
- name: "GOOGLE_CHAT_LIST_SPACES",
132
- similes: [
133
- "LIST_GOOGLE_CHAT_SPACES",
134
- "GCHAT_SPACES",
135
- "SHOW_GOOGLE_CHAT_SPACES"
136
- ],
137
- description: "List all Google Chat spaces the bot is a member of",
138
- validate: async (_runtime, message, _state) => {
139
- return message.content.source === "google-chat";
140
- },
141
- handler: async (runtime, message, _state, _options, callback) => {
142
- const gchatService = runtime.getService(GOOGLE_CHAT_SERVICE_NAME);
143
- if (!gchatService || !gchatService.isConnected()) {
144
- if (callback) {
145
- callback({
146
- text: "Google Chat service is not available.",
147
- source: "google-chat"
148
- });
149
- }
150
- return { success: false, error: "Google Chat service not available" };
151
- }
152
- const spaces = await gchatService.getSpaces();
153
- if (spaces.length === 0) {
154
- if (callback) {
155
- callback({
156
- text: "I'm not currently in any Google Chat spaces.",
157
- source: message.content.source
158
- });
159
- }
160
- return {
161
- success: true,
162
- data: { spaceCount: 0, spaces: [] }
163
- };
164
- }
165
- const spaceLines = spaces.map((space) => {
166
- const name = getSpaceDisplayName(space);
167
- const type = isDirectMessage(space) ? "DM" : space.type || "SPACE";
168
- const threaded = space.threaded ? " (threaded)" : "";
169
- return `• ${name} [${type}]${threaded}`;
170
- });
171
- const responseText = `Currently in ${spaces.length} space(s):
172
-
173
- ${spaceLines.join(`
174
- `)}`;
175
- if (callback) {
176
- callback({
177
- text: responseText,
178
- source: message.content.source
179
- });
180
- }
181
- return {
182
- success: true,
183
- data: {
184
- spaceCount: spaces.length,
185
- spaces: spaces.map((s) => ({
186
- name: s.name,
187
- displayName: s.displayName,
188
- type: s.type,
189
- threaded: s.threaded
190
- }))
191
- }
192
- };
193
- },
194
- examples: [
195
- [
196
- {
197
- name: "{{user1}}",
198
- content: { text: "What Google Chat spaces are you in?" }
199
- },
200
- {
201
- name: "{{agent}}",
202
- content: {
203
- text: "Let me check my Google Chat spaces.",
204
- actions: ["GOOGLE_CHAT_LIST_SPACES"]
205
- }
206
- }
207
- ],
208
- [
209
- {
210
- name: "{{user1}}",
211
- content: { text: "List all spaces" }
212
- },
213
- {
214
- name: "{{agent}}",
215
- content: {
216
- text: "I'll list all the Google Chat spaces I'm in.",
217
- actions: ["GOOGLE_CHAT_LIST_SPACES"]
218
- }
219
- }
220
- ]
221
- ]
222
- };
223
- // src/actions/sendMessage.ts
224
- import {
225
- composePromptFromState,
226
- logger,
227
- ModelType,
228
- parseJSONObjectFromText
229
- } from "@elizaos/core";
230
- var SEND_MESSAGE_TEMPLATE = `# Task: Extract Google Chat send message parameters
231
- Based on the conversation, determine what message to send and to which space.
232
-
233
- Recent conversation:
234
- {{recentMessages}}
235
-
236
- Extract the following:
237
- - text: The message content to send
238
- - space: The target space ID (or "current" for the current space)
239
- - thread: Optional thread name to reply in
240
-
241
- Respond with a JSON object:
242
- \`\`\`json
243
- {
244
- "text": "message content here",
245
- "space": "spaces/xxx or current",
246
- "thread": "optional thread name"
247
- }
248
- \`\`\``;
249
- var sendMessage = {
250
- name: "GOOGLE_CHAT_SEND_MESSAGE",
251
- similes: [
252
- "SEND_GOOGLE_CHAT_MESSAGE",
253
- "MESSAGE_GOOGLE_CHAT",
254
- "GCHAT_SEND",
255
- "GOOGLE_CHAT_TEXT"
256
- ],
257
- description: "Send a message to a Google Chat space",
258
- validate: async (_runtime, message, _state) => {
259
- return message.content.source === "google-chat";
260
- },
261
- handler: async (runtime, message, state, _options, callback) => {
262
- const gchatService = runtime.getService(GOOGLE_CHAT_SERVICE_NAME);
263
- if (!gchatService || !gchatService.isConnected()) {
264
- if (callback) {
265
- callback({
266
- text: "Google Chat service is not available.",
267
- source: "google-chat"
268
- });
269
- }
270
- return { success: false, error: "Google Chat service not available" };
271
- }
272
- const currentState = state ?? await runtime.composeState(message);
273
- const prompt = await composePromptFromState({
274
- template: SEND_MESSAGE_TEMPLATE,
275
- state: currentState
276
- });
277
- let messageInfo = null;
278
- for (let attempt = 0;attempt < 3; attempt++) {
279
- const response = await runtime.useModel(ModelType.TEXT_SMALL, {
280
- prompt
281
- });
282
- const parsed = parseJSONObjectFromText(response);
283
- if (parsed?.text) {
284
- messageInfo = {
285
- text: String(parsed.text),
286
- space: String(parsed.space || "current"),
287
- thread: parsed.thread ? String(parsed.thread) : undefined
288
- };
289
- break;
290
- }
291
- }
292
- if (!messageInfo || !messageInfo.text) {
293
- if (callback) {
294
- callback({
295
- text: "I couldn't understand what message you want me to send. Please try again.",
296
- source: "google-chat"
297
- });
298
- }
299
- return { success: false, error: "Could not extract message parameters" };
300
- }
301
- let targetSpace;
302
- if (messageInfo.space && messageInfo.space !== "current") {
303
- const normalized = normalizeSpaceTarget(messageInfo.space);
304
- if (normalized) {
305
- targetSpace = normalized;
306
- }
307
- }
308
- const spaceData = currentState.data?.space;
309
- if (!targetSpace && spaceData?.name) {
310
- targetSpace = String(spaceData.name);
311
- }
312
- if (!targetSpace) {
313
- if (callback) {
314
- callback({
315
- text: "I couldn't determine which space to send to. Please specify a space.",
316
- source: "google-chat"
317
- });
318
- }
319
- return { success: false, error: "Could not determine target space" };
320
- }
321
- const chunks = splitMessageForGoogleChat(messageInfo.text);
322
- let lastResult;
323
- for (const chunk of chunks) {
324
- const result = await gchatService.sendMessage({
325
- space: targetSpace,
326
- text: chunk,
327
- thread: messageInfo.thread
328
- });
329
- if (!result.success) {
330
- if (callback) {
331
- callback({
332
- text: `Failed to send message: ${result.error}`,
333
- source: "google-chat"
334
- });
335
- }
336
- return { success: false, error: result.error };
337
- }
338
- lastResult = { messageName: result.messageName };
339
- logger.debug(`Sent Google Chat message: ${result.messageName}`);
340
- }
341
- if (callback) {
342
- callback({
343
- text: "Message sent successfully.",
344
- source: message.content.source
345
- });
346
- }
347
- return {
348
- success: true,
349
- data: {
350
- space: targetSpace,
351
- messageName: lastResult?.messageName,
352
- chunksCount: chunks.length
353
- }
354
- };
355
- },
356
- examples: [
357
- [
358
- {
359
- name: "{{user1}}",
360
- content: { text: "Send a message saying 'Hello everyone!'" }
361
- },
362
- {
363
- name: "{{agent}}",
364
- content: {
365
- text: "I'll send that message to the space.",
366
- actions: ["GOOGLE_CHAT_SEND_MESSAGE"]
367
- }
368
- }
369
- ],
370
- [
371
- {
372
- name: "{{user1}}",
373
- content: {
374
- text: "Post 'Meeting starts in 5 minutes' to the team space"
375
- }
376
- },
377
- {
378
- name: "{{agent}}",
379
- content: {
380
- text: "I'll post that reminder to the team space.",
381
- actions: ["GOOGLE_CHAT_SEND_MESSAGE"]
382
- }
383
- }
384
- ]
385
- ]
386
- };
387
- // src/actions/sendReaction.ts
388
- import {
389
- composePromptFromState as composePromptFromState2,
390
- ModelType as ModelType2,
391
- parseJSONObjectFromText as parseJSONObjectFromText2
392
- } from "@elizaos/core";
393
- var SEND_REACTION_TEMPLATE = `# Task: Extract Google Chat reaction parameters
394
- Based on the conversation, determine the emoji reaction to add or remove.
395
-
396
- Recent conversation:
397
- {{recentMessages}}
398
-
399
- Extract the following:
400
- - emoji: The emoji to react with (Unicode emoji character)
401
- - messageName: The message resource name to react to
402
- - remove: Whether to remove the reaction (true/false)
403
-
404
- Respond with a JSON object:
405
- \`\`\`json
406
- {
407
- "emoji": "\uD83D\uDC4D",
408
- "messageName": "spaces/xxx/messages/yyy",
409
- "remove": false
410
- }
411
- \`\`\``;
412
- var sendReaction = {
413
- name: "GOOGLE_CHAT_SEND_REACTION",
414
- similes: [
415
- "REACT_GOOGLE_CHAT",
416
- "GCHAT_REACT",
417
- "GOOGLE_CHAT_EMOJI",
418
- "ADD_GOOGLE_CHAT_REACTION"
419
- ],
420
- description: "Add or remove an emoji reaction to a Google Chat message",
421
- validate: async (_runtime, message, _state) => {
422
- return message.content.source === "google-chat";
423
- },
424
- handler: async (runtime, message, state, _options, callback) => {
425
- const gchatService = runtime.getService(GOOGLE_CHAT_SERVICE_NAME);
426
- if (!gchatService || !gchatService.isConnected()) {
427
- if (callback) {
428
- callback({
429
- text: "Google Chat service is not available.",
430
- source: "google-chat"
431
- });
432
- }
433
- return { success: false, error: "Google Chat service not available" };
434
- }
435
- const currentState = state ?? await runtime.composeState(message);
436
- const prompt = await composePromptFromState2({
437
- template: SEND_REACTION_TEMPLATE,
438
- state: currentState
439
- });
440
- let reactionInfo = null;
441
- for (let attempt = 0;attempt < 3; attempt++) {
442
- const response = await runtime.useModel(ModelType2.TEXT_SMALL, {
443
- prompt
444
- });
445
- const parsed = parseJSONObjectFromText2(response);
446
- if (parsed?.emoji && parsed?.messageName) {
447
- reactionInfo = {
448
- emoji: String(parsed.emoji),
449
- messageName: String(parsed.messageName),
450
- remove: parsed.remove === true
451
- };
452
- break;
453
- }
454
- }
455
- if (!reactionInfo) {
456
- if (callback) {
457
- callback({
458
- text: "I couldn't understand the reaction details. Please try again.",
459
- source: "google-chat"
460
- });
461
- }
462
- return { success: false, error: "Could not extract reaction parameters" };
463
- }
464
- let targetMessage = reactionInfo.messageName;
465
- const messageData = currentState.data?.message;
466
- if (!targetMessage && messageData?.name) {
467
- targetMessage = String(messageData.name);
468
- }
469
- if (!targetMessage) {
470
- if (callback) {
471
- callback({
472
- text: "I couldn't determine which message to react to. Please specify the message.",
473
- source: "google-chat"
474
- });
475
- }
476
- return { success: false, error: "Could not determine target message" };
477
- }
478
- if (reactionInfo.remove) {
479
- const reactions = await gchatService.listReactions(targetMessage);
480
- const botUser = gchatService.getBotUser();
481
- const toRemove = reactions.filter((r) => {
482
- const userName = r.user?.name;
483
- if (botUser && userName !== botUser && userName !== "users/app") {
484
- return false;
485
- }
486
- if (reactionInfo?.emoji && r.emoji?.unicode !== reactionInfo?.emoji) {
487
- return false;
488
- }
489
- return true;
490
- });
491
- for (const reaction of toRemove) {
492
- if (reaction.name) {
493
- await gchatService.deleteReaction(reaction.name);
494
- }
495
- }
496
- if (callback) {
497
- callback({
498
- text: `Removed ${toRemove.length} reaction(s).`,
499
- source: message.content.source
500
- });
501
- }
502
- return {
503
- success: true,
504
- data: {
505
- removed: toRemove.length
506
- }
507
- };
508
- }
509
- const result = await gchatService.sendReaction(targetMessage, reactionInfo.emoji);
510
- if (!result.success) {
511
- if (callback) {
512
- callback({
513
- text: `Failed to add reaction: ${result.error}`,
514
- source: "google-chat"
515
- });
516
- }
517
- return { success: false, error: result.error };
518
- }
519
- if (callback) {
520
- callback({
521
- text: `Added ${reactionInfo.emoji} reaction.`,
522
- source: message.content.source
523
- });
524
- }
525
- return {
526
- success: true,
527
- data: {
528
- reactionName: result.name,
529
- emoji: reactionInfo.emoji
530
- }
531
- };
532
- },
533
- examples: [
534
- [
535
- {
536
- name: "{{user1}}",
537
- content: { text: "React with a thumbs up to that message" }
538
- },
539
- {
540
- name: "{{agent}}",
541
- content: {
542
- text: "I'll add a thumbs up reaction.",
543
- actions: ["GOOGLE_CHAT_SEND_REACTION"]
544
- }
545
- }
546
- ],
547
- [
548
- {
549
- name: "{{user1}}",
550
- content: { text: "Remove my reaction from that message" }
551
- },
552
- {
553
- name: "{{agent}}",
554
- content: {
555
- text: "I'll remove the reaction.",
556
- actions: ["GOOGLE_CHAT_SEND_REACTION"]
557
- }
558
- }
559
- ]
560
- ]
561
- };
562
- // src/providers/spaceState.ts
563
- var spaceStateProvider = {
564
- name: "googleChatSpaceState",
565
- description: "Provides information about the current Google Chat space context",
566
- get: async (runtime, message, state) => {
567
- if (message.content.source !== "google-chat") {
568
- return {
569
- data: {},
570
- values: {},
571
- text: ""
572
- };
573
- }
574
- const gchatService = runtime.getService(GOOGLE_CHAT_SERVICE_NAME);
575
- if (!gchatService || !gchatService.isConnected()) {
576
- return {
577
- data: { connected: false },
578
- values: { connected: false },
579
- text: ""
580
- };
581
- }
582
- const agentName = state?.agentName || "The agent";
583
- const space = state?.data?.space;
584
- const spaceName = space?.name;
585
- const spaceDisplayName = space?.displayName;
586
- const spaceType = space?.type;
587
- const isThreaded = space?.threaded;
588
- const isDm = spaceType === "DM" || space?.singleUserBotDm === true;
589
- let responseText = "";
590
- if (isDm) {
591
- responseText = `${agentName} is in a direct message conversation on Google Chat.`;
592
- } else {
593
- const label = spaceDisplayName || spaceName || "a Google Chat space";
594
- responseText = `${agentName} is currently in Google Chat space "${label}".`;
595
- if (isThreaded) {
596
- responseText += " This space uses threaded conversations.";
597
- }
598
- }
599
- responseText += `
600
-
601
- Google Chat is Google Workspace's team communication platform.`;
602
- return {
603
- data: {
604
- spaceName,
605
- spaceDisplayName,
606
- spaceType,
607
- isThreaded: isThreaded || false,
608
- isDirect: isDm || false,
609
- connected: true
610
- },
611
- values: {
612
- spaceName,
613
- spaceDisplayName,
614
- spaceType,
615
- isThreaded: isThreaded || false,
616
- isDirect: isDm || false
617
- },
618
- text: responseText
619
- };
620
- }
621
- };
622
- // src/providers/userContext.ts
623
- var userContextProvider = {
624
- name: "googleChatUserContext",
625
- description: "Provides information about the Google Chat user in the current conversation",
626
- get: async (runtime, message, state) => {
627
- if (message.content.source !== "google-chat") {
628
- return {
629
- data: {},
630
- values: {},
631
- text: ""
632
- };
633
- }
634
- const gchatService = runtime.getService(GOOGLE_CHAT_SERVICE_NAME);
635
- if (!gchatService || !gchatService.isConnected()) {
636
- return {
637
- data: { connected: false },
638
- values: { connected: false },
639
- text: ""
640
- };
641
- }
642
- const agentName = state?.agentName || "The agent";
643
- const sender = state?.data?.sender;
644
- if (!sender) {
645
- return {
646
- data: { connected: true },
647
- values: { connected: true },
648
- text: ""
649
- };
650
- }
651
- const userName = sender.name;
652
- const displayName = getUserDisplayName(sender);
653
- const userId = extractResourceId(userName);
654
- const email = sender.email;
655
- const userType = sender.type;
656
- let responseText = `${agentName} is talking to ${displayName}`;
657
- if (email) {
658
- responseText += ` (${email})`;
659
- }
660
- responseText += " on Google Chat.";
661
- if (userType === "BOT") {
662
- responseText += " This user is a bot.";
663
- }
664
- return {
665
- data: {
666
- userName,
667
- userId,
668
- displayName,
669
- email: email || undefined,
670
- userType: userType || "HUMAN",
671
- isBot: userType === "BOT"
672
- },
673
- values: {
674
- userName,
675
- userId,
676
- displayName,
677
- email: email || undefined
678
- },
679
- text: responseText
680
- };
681
- }
682
- };
683
- // src/service.ts
684
- import {
685
- logger as logger2,
686
- Service
687
- } from "@elizaos/core";
688
- import { GoogleAuth } from "google-auth-library";
689
- var CHAT_API_BASE = "https://chat.googleapis.com/v1";
690
- var CHAT_UPLOAD_BASE = "https://chat.googleapis.com/upload/v1";
691
- var CHAT_SCOPE = "https://www.googleapis.com/auth/chat.bot";
692
-
693
- class GoogleChatService extends Service {
694
- static serviceType = GOOGLE_CHAT_SERVICE_NAME;
695
- capabilityDescription = "Google Chat service for sending and receiving messages in Google Workspace";
696
- settings = null;
697
- auth = null;
698
- connected = false;
699
- cachedSpaces = [];
700
- static async start(runtime) {
701
- logger2.info("Starting Google Chat service...");
702
- const service = new GoogleChatService(runtime);
703
- service.settings = service.loadSettings();
704
- service.validateSettings();
705
- await service.initializeAuth();
706
- await service.testConnection();
707
- service.connected = true;
708
- logger2.info("Google Chat service started successfully");
709
- runtime.emitEvent("GOOGLE_CHAT_CONNECTION_READY" /* CONNECTION_READY */, {
710
- runtime,
711
- service
712
- });
713
- return service;
714
- }
715
- async stop() {
716
- logger2.info("Stopping Google Chat service...");
717
- this.connected = false;
718
- this.auth = null;
719
- logger2.info("Google Chat service stopped");
720
- }
721
- loadSettings() {
722
- const runtime = this.runtime;
723
- if (!runtime) {
724
- throw new GoogleChatConfigurationError("Runtime not initialized");
725
- }
726
- const getStringSetting = (key, envKey, defaultValue = "") => {
727
- const value = runtime.getSetting(key);
728
- if (typeof value === "string")
729
- return value;
730
- return process.env[envKey] || defaultValue;
731
- };
732
- const serviceAccount = getStringSetting("GOOGLE_CHAT_SERVICE_ACCOUNT", "GOOGLE_CHAT_SERVICE_ACCOUNT");
733
- const serviceAccountFile = getStringSetting("GOOGLE_CHAT_SERVICE_ACCOUNT_FILE", "GOOGLE_CHAT_SERVICE_ACCOUNT_FILE");
734
- const audienceType = getStringSetting("GOOGLE_CHAT_AUDIENCE_TYPE", "GOOGLE_CHAT_AUDIENCE_TYPE", "app-url");
735
- const audience = getStringSetting("GOOGLE_CHAT_AUDIENCE", "GOOGLE_CHAT_AUDIENCE");
736
- const webhookPath = getStringSetting("GOOGLE_CHAT_WEBHOOK_PATH", "GOOGLE_CHAT_WEBHOOK_PATH", "/googlechat");
737
- const spacesRaw = getStringSetting("GOOGLE_CHAT_SPACES", "GOOGLE_CHAT_SPACES");
738
- const requireMention = getStringSetting("GOOGLE_CHAT_REQUIRE_MENTION", "GOOGLE_CHAT_REQUIRE_MENTION", "true");
739
- const enabled = getStringSetting("GOOGLE_CHAT_ENABLED", "GOOGLE_CHAT_ENABLED", "true");
740
- const botUser = getStringSetting("GOOGLE_CHAT_BOT_USER", "GOOGLE_CHAT_BOT_USER") || undefined;
741
- return {
742
- serviceAccount: serviceAccount || undefined,
743
- serviceAccountFile: serviceAccountFile || undefined,
744
- audienceType,
745
- audience,
746
- webhookPath: webhookPath.startsWith("/") ? webhookPath : `/${webhookPath}`,
747
- spaces: spacesRaw ? spacesRaw.split(",").map((s) => s.trim()).filter(Boolean) : [],
748
- requireMention: requireMention.toLowerCase() !== "false",
749
- enabled: enabled.toLowerCase() !== "false",
750
- botUser: botUser || undefined
751
- };
752
- }
753
- validateSettings() {
754
- const settings = this.settings;
755
- if (!settings) {
756
- throw new GoogleChatConfigurationError("Settings not loaded");
757
- }
758
- if (!settings.serviceAccount && !settings.serviceAccountFile) {
759
- if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
760
- throw new GoogleChatConfigurationError("Google Chat requires service account credentials. Set GOOGLE_CHAT_SERVICE_ACCOUNT, GOOGLE_CHAT_SERVICE_ACCOUNT_FILE, or GOOGLE_APPLICATION_CREDENTIALS.", "GOOGLE_CHAT_SERVICE_ACCOUNT");
761
- }
762
- }
763
- if (!settings.audience) {
764
- throw new GoogleChatConfigurationError("GOOGLE_CHAT_AUDIENCE is required for webhook verification", "GOOGLE_CHAT_AUDIENCE");
765
- }
766
- if (!["app-url", "project-number"].includes(settings.audienceType)) {
767
- throw new GoogleChatConfigurationError("GOOGLE_CHAT_AUDIENCE_TYPE must be 'app-url' or 'project-number'", "GOOGLE_CHAT_AUDIENCE_TYPE");
768
- }
769
- }
770
- async initializeAuth() {
771
- const settings = this.settings;
772
- if (!settings) {
773
- throw new GoogleChatConfigurationError("Settings not loaded");
774
- }
775
- if (settings.serviceAccountFile) {
776
- this.auth = new GoogleAuth({
777
- keyFile: settings.serviceAccountFile,
778
- scopes: [CHAT_SCOPE]
779
- });
780
- } else if (settings.serviceAccount) {
781
- const credentials = JSON.parse(settings.serviceAccount);
782
- this.auth = new GoogleAuth({
783
- credentials,
784
- scopes: [CHAT_SCOPE]
785
- });
786
- } else {
787
- this.auth = new GoogleAuth({
788
- scopes: [CHAT_SCOPE]
789
- });
790
- }
791
- logger2.info("Google Auth initialized");
792
- }
793
- async testConnection() {
794
- const token = await this.getAccessToken();
795
- if (!token) {
796
- throw new GoogleChatAuthenticationError("Failed to obtain access token");
797
- }
798
- const url = `${CHAT_API_BASE}/spaces?pageSize=1`;
799
- const response = await fetch(url, {
800
- headers: {
801
- Authorization: `Bearer ${token}`,
802
- "Content-Type": "application/json"
803
- }
804
- });
805
- if (!response.ok) {
806
- const text = await response.text().catch(() => "");
807
- throw new GoogleChatApiError(`Failed to connect to Google Chat API: ${text || response.statusText}`, response.status);
808
- }
809
- logger2.info("Google Chat API connection verified");
810
- }
811
- isConnected() {
812
- return this.connected;
813
- }
814
- getBotUser() {
815
- return this.settings?.botUser;
816
- }
817
- async getAccessToken() {
818
- if (!this.auth) {
819
- throw new GoogleChatAuthenticationError("Auth not initialized");
820
- }
821
- const client = await this.auth.getClient();
822
- const tokenResponse = await client.getAccessToken();
823
- const token = typeof tokenResponse === "string" ? tokenResponse : tokenResponse?.token;
824
- if (!token) {
825
- throw new GoogleChatAuthenticationError("Failed to obtain access token");
826
- }
827
- return token;
828
- }
829
- async fetchApi(url, init = {}) {
830
- const token = await this.getAccessToken();
831
- const response = await fetch(url, {
832
- ...init,
833
- headers: {
834
- ...init.headers,
835
- Authorization: `Bearer ${token}`,
836
- "Content-Type": "application/json"
837
- }
838
- });
839
- if (!response.ok) {
840
- const text = await response.text().catch(() => "");
841
- throw new GoogleChatApiError(`Google Chat API error: ${text || response.statusText}`, response.status);
842
- }
843
- return await response.json();
844
- }
845
- async getSpaces() {
846
- const url = `${CHAT_API_BASE}/spaces`;
847
- const response = await this.fetchApi(url);
848
- this.cachedSpaces = response.spaces || [];
849
- return this.cachedSpaces;
850
- }
851
- async sendMessage(options) {
852
- if (!options.space) {
853
- return {
854
- success: false,
855
- error: "Space is required"
856
- };
857
- }
858
- const body = {};
859
- if (options.text) {
860
- body.text = options.text;
861
- }
862
- if (options.thread) {
863
- body.thread = { name: options.thread };
864
- }
865
- if (options.attachments && options.attachments.length > 0) {
866
- body.attachment = options.attachments.map((att) => ({
867
- attachmentDataRef: { attachmentUploadToken: att.attachmentUploadToken },
868
- ...att.contentName ? { contentName: att.contentName } : {}
869
- }));
870
- }
871
- const url = `${CHAT_API_BASE}/${options.space}/messages`;
872
- const result = await this.fetchApi(url, {
873
- method: "POST",
874
- body: JSON.stringify(body)
875
- });
876
- logger2.debug(`Message sent to ${options.space}: ${result.name}`);
877
- if (this.runtime) {
878
- this.runtime.emitEvent("GOOGLE_CHAT_MESSAGE_SENT" /* MESSAGE_SENT */, {
879
- runtime: this.runtime,
880
- messageName: result.name,
881
- space: options.space
882
- });
883
- }
884
- return {
885
- success: true,
886
- messageName: result.name,
887
- space: options.space
888
- };
889
- }
890
- async updateMessage(messageName, text) {
891
- const url = `${CHAT_API_BASE}/${messageName}?updateMask=text`;
892
- const result = await this.fetchApi(url, {
893
- method: "PATCH",
894
- body: JSON.stringify({ text })
895
- });
896
- return {
897
- success: true,
898
- messageName: result.name
899
- };
900
- }
901
- async deleteMessage(messageName) {
902
- const url = `${CHAT_API_BASE}/${messageName}`;
903
- const token = await this.getAccessToken();
904
- const response = await fetch(url, {
905
- method: "DELETE",
906
- headers: {
907
- Authorization: `Bearer ${token}`
908
- }
909
- });
910
- if (!response.ok) {
911
- const text = await response.text().catch(() => "");
912
- return {
913
- success: false,
914
- error: `Failed to delete message: ${text || response.statusText}`
915
- };
916
- }
917
- return { success: true };
918
- }
919
- async sendReaction(messageName, emoji) {
920
- const url = `${CHAT_API_BASE}/${messageName}/reactions`;
921
- const result = await this.fetchApi(url, {
922
- method: "POST",
923
- body: JSON.stringify({ emoji: { unicode: emoji } })
924
- });
925
- if (this.runtime) {
926
- this.runtime.emitEvent("GOOGLE_CHAT_REACTION_SENT" /* REACTION_SENT */, {
927
- runtime: this.runtime,
928
- messageName,
929
- emoji,
930
- reactionName: result.name
931
- });
932
- }
933
- return {
934
- success: true,
935
- name: result.name
936
- };
937
- }
938
- async deleteReaction(reactionName) {
939
- const url = `${CHAT_API_BASE}/${reactionName}`;
940
- const token = await this.getAccessToken();
941
- const response = await fetch(url, {
942
- method: "DELETE",
943
- headers: {
944
- Authorization: `Bearer ${token}`
945
- }
946
- });
947
- if (!response.ok) {
948
- const text = await response.text().catch(() => "");
949
- return {
950
- success: false,
951
- error: `Failed to delete reaction: ${text || response.statusText}`
952
- };
953
- }
954
- return { success: true };
955
- }
956
- async listReactions(messageName, limit) {
957
- const url = new URL(`${CHAT_API_BASE}/${messageName}/reactions`);
958
- if (limit && limit > 0) {
959
- url.searchParams.set("pageSize", String(limit));
960
- }
961
- const result = await this.fetchApi(url.toString());
962
- return result.reactions || [];
963
- }
964
- async findDirectMessage(userName) {
965
- const url = new URL(`${CHAT_API_BASE}/spaces:findDirectMessage`);
966
- url.searchParams.set("name", userName);
967
- const result = await this.fetchApi(url.toString());
968
- return result;
969
- }
970
- async uploadAttachment(space, filename, buffer, contentType) {
971
- const boundary = `elizaos-${crypto.randomUUID()}`;
972
- const metadata = JSON.stringify({ filename });
973
- const header = `--${boundary}\r
974
- Content-Type: application/json; charset=UTF-8\r
975
- \r
976
- ${metadata}\r
977
- `;
978
- const mediaHeader = `--${boundary}\r
979
- Content-Type: ${contentType || "application/octet-stream"}\r
980
- \r
981
- `;
982
- const footer = `\r
983
- --${boundary}--\r
984
- `;
985
- const body = Buffer.concat([
986
- Buffer.from(header, "utf8"),
987
- Buffer.from(mediaHeader, "utf8"),
988
- buffer,
989
- Buffer.from(footer, "utf8")
990
- ]);
991
- const token = await this.getAccessToken();
992
- const url = `${CHAT_UPLOAD_BASE}/${space}/attachments:upload?uploadType=multipart`;
993
- const response = await fetch(url, {
994
- method: "POST",
995
- headers: {
996
- Authorization: `Bearer ${token}`,
997
- "Content-Type": `multipart/related; boundary=${boundary}`
998
- },
999
- body
1000
- });
1001
- if (!response.ok) {
1002
- const text = await response.text().catch(() => "");
1003
- throw new GoogleChatApiError(`Failed to upload attachment: ${text || response.statusText}`, response.status);
1004
- }
1005
- const payload = await response.json();
1006
- return {
1007
- attachmentUploadToken: payload.attachmentDataRef?.attachmentUploadToken
1008
- };
1009
- }
1010
- async downloadMedia(resourceName, maxBytes) {
1011
- const url = `${CHAT_API_BASE}/media/${resourceName}?alt=media`;
1012
- const token = await this.getAccessToken();
1013
- const response = await fetch(url, {
1014
- headers: {
1015
- Authorization: `Bearer ${token}`
1016
- }
1017
- });
1018
- if (!response.ok) {
1019
- const text = await response.text().catch(() => "");
1020
- throw new GoogleChatApiError(`Failed to download media: ${text || response.statusText}`, response.status);
1021
- }
1022
- const contentLength = response.headers.get("content-length");
1023
- if (maxBytes && contentLength) {
1024
- const length = Number(contentLength);
1025
- if (Number.isFinite(length) && length > maxBytes) {
1026
- throw new GoogleChatApiError(`Media exceeds max bytes (${maxBytes})`, 413);
1027
- }
1028
- }
1029
- const arrayBuffer = await response.arrayBuffer();
1030
- const buffer = Buffer.from(arrayBuffer);
1031
- const contentType = response.headers.get("content-type") || undefined;
1032
- return { buffer, contentType };
1033
- }
1034
- getSettings() {
1035
- return this.settings;
1036
- }
1037
- async processWebhookEvent(event) {
1038
- const eventType = event.type;
1039
- if (!this.runtime)
1040
- return;
1041
- if (eventType === "MESSAGE") {
1042
- this.runtime.emitEvent("GOOGLE_CHAT_MESSAGE_RECEIVED" /* MESSAGE_RECEIVED */, {
1043
- runtime: this.runtime,
1044
- event,
1045
- message: event.message,
1046
- space: event.space,
1047
- user: event.user
1048
- });
1049
- } else if (eventType === "ADDED_TO_SPACE") {
1050
- this.runtime.emitEvent("GOOGLE_CHAT_SPACE_JOINED" /* SPACE_JOINED */, {
1051
- runtime: this.runtime,
1052
- space: event.space,
1053
- user: event.user
1054
- });
1055
- } else if (eventType === "REMOVED_FROM_SPACE") {
1056
- this.runtime.emitEvent("GOOGLE_CHAT_SPACE_LEFT" /* SPACE_LEFT */, {
1057
- runtime: this.runtime,
1058
- space: event.space,
1059
- user: event.user
1060
- });
1061
- }
1062
- }
1063
- }
1064
- // src/index.ts
1065
- var googleChatPlugin = {
1066
- name: "google-chat",
1067
- description: "Google Chat integration plugin for ElizaOS agents",
1068
- services: [GoogleChatService],
1069
- actions: [sendMessage, sendReaction, listSpaces],
1070
- providers: [spaceStateProvider, userContextProvider],
1071
- tests: [],
1072
- init: async (config, _runtime) => {
1073
- logger3.info("Initializing Google Chat plugin...");
1074
- const serviceAccount = config.GOOGLE_CHAT_SERVICE_ACCOUNT || process.env.GOOGLE_CHAT_SERVICE_ACCOUNT;
1075
- const serviceAccountFile = config.GOOGLE_CHAT_SERVICE_ACCOUNT_FILE || process.env.GOOGLE_CHAT_SERVICE_ACCOUNT_FILE;
1076
- const hasCredentials = Boolean(serviceAccount || serviceAccountFile || process.env.GOOGLE_APPLICATION_CREDENTIALS);
1077
- logger3.info(`Google Chat plugin configuration:`);
1078
- logger3.info(` - Credentials configured: ${hasCredentials ? "Yes" : "No"}`);
1079
- logger3.info(` - Audience type: ${config.GOOGLE_CHAT_AUDIENCE_TYPE || process.env.GOOGLE_CHAT_AUDIENCE_TYPE || "(not set)"}`);
1080
- logger3.info(` - Audience: ${config.GOOGLE_CHAT_AUDIENCE || process.env.GOOGLE_CHAT_AUDIENCE ? "(set)" : "(not set)"}`);
1081
- logger3.info(` - Webhook path: ${config.GOOGLE_CHAT_WEBHOOK_PATH || process.env.GOOGLE_CHAT_WEBHOOK_PATH || "/googlechat"}`);
1082
- if (!hasCredentials) {
1083
- logger3.warn("Google Chat service account credentials not configured. " + "Set GOOGLE_CHAT_SERVICE_ACCOUNT, GOOGLE_CHAT_SERVICE_ACCOUNT_FILE, or GOOGLE_APPLICATION_CREDENTIALS.");
1084
- }
1085
- logger3.info("Google Chat plugin initialized");
1086
- }
1087
- };
1088
- var src_default = googleChatPlugin;
1089
- export {
1090
- userContextProvider,
1091
- splitMessageForGoogleChat,
1092
- spaceStateProvider,
1093
- sendReaction,
1094
- sendMessage,
1095
- normalizeUserTarget,
1096
- normalizeSpaceTarget,
1097
- listSpaces,
1098
- isValidGoogleChatUserName,
1099
- isValidGoogleChatSpaceName,
1100
- isDirectMessage,
1101
- getUserDisplayName,
1102
- getSpaceDisplayName,
1103
- extractResourceId,
1104
- src_default as default,
1105
- MAX_GOOGLE_CHAT_MESSAGE_LENGTH,
1106
- GoogleChatService,
1107
- GoogleChatPluginError,
1108
- GoogleChatEventTypes,
1109
- GoogleChatConfigurationError,
1110
- GoogleChatAuthenticationError,
1111
- GoogleChatApiError,
1112
- GOOGLE_CHAT_SERVICE_NAME
1113
- };
1114
-
1115
- //# debugId=636667934C16DD1564756E2164756E21