@chat-adapter/gchat 4.1.0 → 4.3.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.
- package/README.md +174 -22
- package/dist/index.d.ts +41 -31
- package/dist/index.js +449 -294
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -40,37 +40,155 @@ chat.onNewMention(async (thread, message) => {
|
|
|
40
40
|
|
|
41
41
|
*Either `credentials` or `useADC: true` is required.
|
|
42
42
|
|
|
43
|
-
##
|
|
43
|
+
## Environment Variables
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
```bash
|
|
46
|
+
GOOGLE_CHAT_CREDENTIALS={"type":"service_account",...}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
# Optional: for receiving ALL messages, not just @mentions
|
|
49
|
+
GOOGLE_CHAT_PUBSUB_TOPIC=projects/your-project/topics/chat-events
|
|
50
|
+
GOOGLE_CHAT_IMPERSONATE_USER=admin@yourdomain.com
|
|
51
|
+
```
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
## Google Chat Setup
|
|
52
54
|
|
|
53
|
-
1.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
### 1. Create a GCP Project
|
|
56
|
+
|
|
57
|
+
1. Go to [console.cloud.google.com](https://console.cloud.google.com)
|
|
58
|
+
2. Click the project dropdown → **New Project**
|
|
59
|
+
3. Enter project name and click **Create**
|
|
60
|
+
|
|
61
|
+
### 2. Enable Required APIs
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
1. Go to **APIs & Services** → **Library**
|
|
64
|
+
2. Search and enable:
|
|
65
|
+
- **Google Chat API**
|
|
66
|
+
- **Google Workspace Events API** (for receiving all messages)
|
|
67
|
+
- **Cloud Pub/Sub API** (for receiving all messages)
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
2. Configure your app:
|
|
62
|
-
- App name and avatar
|
|
63
|
-
- HTTP endpoint URL for webhooks
|
|
64
|
-
- Enable interactive features
|
|
69
|
+
### 3. Create a Service Account
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
1. Go to **IAM & Admin** → **Service Accounts**
|
|
72
|
+
2. Click **Create Service Account**
|
|
73
|
+
3. Enter name and description
|
|
74
|
+
4. Click **Create and Continue**
|
|
75
|
+
5. Skip the optional steps, click **Done**
|
|
67
76
|
|
|
68
|
-
|
|
77
|
+
### 4. Create Service Account Key
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
> **Note**: If your organization has the `iam.disableServiceAccountKeyCreation` constraint enabled, you'll need to:
|
|
80
|
+
> 1. Go to **IAM & Admin** → **Organization Policies**
|
|
81
|
+
> 2. Find `iam.disableServiceAccountKeyCreation`
|
|
82
|
+
> 3. Click **Manage Policy** → **Override parent's policy**
|
|
83
|
+
> 4. Set to **Not enforced** (or add an exception for your project)
|
|
84
|
+
|
|
85
|
+
1. Click on your service account
|
|
86
|
+
2. Go to **Keys** tab
|
|
87
|
+
3. Click **Add Key** → **Create new key**
|
|
88
|
+
4. Select **JSON** and click **Create**
|
|
89
|
+
5. Save the downloaded file
|
|
90
|
+
6. Copy the entire JSON content → `GOOGLE_CHAT_CREDENTIALS` (as a single line)
|
|
91
|
+
|
|
92
|
+
### 5. Configure Google Chat App
|
|
93
|
+
|
|
94
|
+
1. Go to [console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)
|
|
95
|
+
2. Click **Configuration**
|
|
96
|
+
3. Fill in:
|
|
97
|
+
- **App name**: Your bot's display name
|
|
98
|
+
- **Avatar URL**: URL to your bot's avatar image
|
|
99
|
+
- **Description**: What your bot does
|
|
100
|
+
- **Interactive features**:
|
|
101
|
+
- Enable **Receive 1:1 messages**
|
|
102
|
+
- Enable **Join spaces and group conversations**
|
|
103
|
+
- **Connection settings**: Select **App URL**
|
|
104
|
+
- **App URL**: `https://your-domain.com/api/webhooks/gchat`
|
|
105
|
+
- **Visibility**: Choose who can discover and install your app
|
|
106
|
+
4. Click **Save**
|
|
107
|
+
|
|
108
|
+
**Important for button clicks**: The same App URL receives both message events and interactive events (card button clicks). Google Chat sends CARD_CLICKED events to this URL when users click buttons in cards.
|
|
109
|
+
|
|
110
|
+
### 6. Add Bot to a Space
|
|
111
|
+
|
|
112
|
+
1. Open Google Chat
|
|
113
|
+
2. Create or open a Space
|
|
114
|
+
3. Click the space name → **Manage apps & integrations** (or **Apps & integrations**)
|
|
115
|
+
4. Click **Add apps**
|
|
116
|
+
5. Search for your app name
|
|
117
|
+
6. Click **Add**
|
|
118
|
+
|
|
119
|
+
## (Optional) Pub/Sub for All Messages
|
|
120
|
+
|
|
121
|
+
By default, Google Chat only sends webhooks for @mentions. To receive ALL messages in a space (for conversation context), you need to set up Workspace Events with Pub/Sub.
|
|
122
|
+
|
|
123
|
+
### 1. Create Pub/Sub Topic
|
|
124
|
+
|
|
125
|
+
1. Go to **Pub/Sub** → **Topics**
|
|
126
|
+
2. Click **Create Topic**
|
|
127
|
+
3. Enter topic ID (e.g., `chat-events`)
|
|
128
|
+
4. Uncheck **Add a default subscription**
|
|
129
|
+
5. Click **Create**
|
|
130
|
+
6. Copy the full topic name → `GOOGLE_CHAT_PUBSUB_TOPIC`
|
|
131
|
+
- Format: `projects/your-project-id/topics/chat-events`
|
|
132
|
+
|
|
133
|
+
### 2. Grant Chat Service Account Access
|
|
134
|
+
|
|
135
|
+
> **Note**: If your organization has the `iam.allowedPolicyMemberDomains` constraint, you may need to temporarily relax it or use the console workaround below.
|
|
136
|
+
|
|
137
|
+
1. Go to your Pub/Sub topic
|
|
138
|
+
2. Click **Permissions** tab (or **Show Info Panel** → **Permissions**)
|
|
139
|
+
3. Click **Add Principal**
|
|
140
|
+
4. Enter: `chat-api-push@system.gserviceaccount.com`
|
|
141
|
+
5. Select role: **Pub/Sub Publisher**
|
|
142
|
+
6. Click **Save**
|
|
143
|
+
|
|
144
|
+
**If you get a policy error**, try via Cloud Console:
|
|
145
|
+
1. Go to **Pub/Sub** → **Topics**
|
|
146
|
+
2. Check the box next to your topic
|
|
147
|
+
3. Click **Permissions** in the info panel
|
|
148
|
+
4. Click **Add Principal**
|
|
149
|
+
5. Add `chat-api-push@system.gserviceaccount.com` with **Pub/Sub Publisher** role
|
|
150
|
+
|
|
151
|
+
### 3. Create Push Subscription
|
|
152
|
+
|
|
153
|
+
1. Go to **Pub/Sub** → **Subscriptions**
|
|
154
|
+
2. Click **Create Subscription**
|
|
155
|
+
3. Enter subscription ID (e.g., `chat-messages-push`)
|
|
156
|
+
4. Select your topic
|
|
157
|
+
5. **Delivery type**: Push
|
|
158
|
+
6. **Endpoint URL**: `https://your-domain.com/api/webhooks/gchat`
|
|
159
|
+
7. Click **Create**
|
|
160
|
+
|
|
161
|
+
### 4. Enable Domain-Wide Delegation
|
|
162
|
+
|
|
163
|
+
To create Workspace Events subscriptions and initiate DMs, you need domain-wide delegation:
|
|
164
|
+
|
|
165
|
+
**Step 1: Enable delegation on the Service Account (GCP Console)**
|
|
166
|
+
|
|
167
|
+
1. Go to [IAM & Admin → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts)
|
|
168
|
+
2. Click on your service account
|
|
169
|
+
3. Go to **Details** tab
|
|
170
|
+
4. Check **Enable Google Workspace Domain-wide Delegation**
|
|
171
|
+
5. Click **Save**
|
|
172
|
+
6. Go to **Advanced settings** (or click on the service account again)
|
|
173
|
+
7. Copy the **Client ID** - this is a **numeric ID** (e.g., `123456789012345678901`), NOT the email address
|
|
174
|
+
|
|
175
|
+
**Step 2: Authorize the Client ID (Google Admin Console)**
|
|
176
|
+
|
|
177
|
+
1. Go to [Google Admin Console](https://admin.google.com)
|
|
178
|
+
2. Go to **Security** → **Access and data control** → **API controls**
|
|
179
|
+
3. Click **Manage Domain Wide Delegation**
|
|
180
|
+
4. Click **Add new**
|
|
181
|
+
5. Enter:
|
|
182
|
+
- **Client ID**: The numeric ID from Step 1 (e.g., `123456789012345678901`)
|
|
183
|
+
- **OAuth Scopes** (all on one line, comma-separated):
|
|
184
|
+
```
|
|
185
|
+
https://www.googleapis.com/auth/chat.spaces.readonly,https://www.googleapis.com/auth/chat.messages.readonly,https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.spaces.create
|
|
186
|
+
```
|
|
187
|
+
6. Click **Authorize**
|
|
188
|
+
|
|
189
|
+
**Step 3: Set environment variable**
|
|
190
|
+
|
|
191
|
+
Set `GOOGLE_CHAT_IMPERSONATE_USER` to an admin user email in your domain (e.g., `admin@yourdomain.com`). This user will be impersonated when creating DM spaces and Workspace Events subscriptions.
|
|
74
192
|
|
|
75
193
|
## Features
|
|
76
194
|
|
|
@@ -88,6 +206,40 @@ For reaction events, you need Workspace Events via Pub/Sub:
|
|
|
88
206
|
- **Typing indicators**: Not supported by Google Chat API
|
|
89
207
|
- **Adding reactions**: Requires domain-wide delegation (appears from impersonated user, not bot)
|
|
90
208
|
|
|
209
|
+
## Troubleshooting
|
|
210
|
+
|
|
211
|
+
### No webhook received
|
|
212
|
+
- Verify the App URL is correct in Google Chat configuration
|
|
213
|
+
- Check that the Chat API is enabled
|
|
214
|
+
- Ensure the service account has the necessary permissions
|
|
215
|
+
|
|
216
|
+
### Pub/Sub not working
|
|
217
|
+
- Verify `chat-api-push@system.gserviceaccount.com` has Pub/Sub Publisher role
|
|
218
|
+
- Check that the push subscription URL is correct
|
|
219
|
+
- Verify domain-wide delegation is configured with correct scopes
|
|
220
|
+
- Check `GOOGLE_CHAT_IMPERSONATE_USER` is a valid admin email
|
|
221
|
+
|
|
222
|
+
### "Permission denied" for Workspace Events
|
|
223
|
+
- Ensure domain-wide delegation is configured
|
|
224
|
+
- Verify the OAuth scopes are exactly as specified
|
|
225
|
+
- Check that the impersonated user has access to the spaces
|
|
226
|
+
|
|
227
|
+
### "Insufficient Permission" for DMs (openDM)
|
|
228
|
+
- DMs require domain-wide delegation with `chat.spaces` and `chat.spaces.create` scopes
|
|
229
|
+
- Add these scopes to your domain-wide delegation configuration in Google Admin Console
|
|
230
|
+
- Set `GOOGLE_CHAT_IMPERSONATE_USER` to an admin email in your domain
|
|
231
|
+
- Scope changes can take up to 24 hours to propagate
|
|
232
|
+
|
|
233
|
+
### "unauthorized_client" error
|
|
234
|
+
- The Client ID is not registered in Google Admin Console
|
|
235
|
+
- Or domain-wide delegation is not enabled on the service account
|
|
236
|
+
|
|
237
|
+
### Button clicks (CARD_CLICKED) not received
|
|
238
|
+
- Verify "Interactive features" is enabled in the Google Chat app configuration
|
|
239
|
+
- Check that the App URL is correctly set and accessible
|
|
240
|
+
- Button clicks go to the same webhook URL as messages
|
|
241
|
+
- Ensure your button elements have valid `id` attributes (these become the `actionId`)
|
|
242
|
+
|
|
91
243
|
## License
|
|
92
244
|
|
|
93
245
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { CardElement, BaseFormatConverter, Root, Adapter, ChatInstance, WebhookOptions, AdapterPostableMessage, RawMessage, EmojiValue, FetchOptions,
|
|
1
|
+
import { CardElement, BaseFormatConverter, Root, Logger, Adapter, ChatInstance, WebhookOptions, AdapterPostableMessage, RawMessage, EmojiValue, FetchOptions, FetchResult, ThreadInfo, Message, FormattedContent } from 'chat';
|
|
2
2
|
import { google } from 'googleapis';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Thread ID encoding/decoding utilities for Google Chat adapter.
|
|
6
|
+
*/
|
|
7
|
+
/** Google Chat-specific thread ID data */
|
|
8
|
+
interface GoogleChatThreadId {
|
|
9
|
+
spaceName: string;
|
|
10
|
+
threadName?: string;
|
|
11
|
+
/** Whether this is a DM space */
|
|
12
|
+
isDM?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
/**
|
|
5
16
|
* Google Chat Card converter for cross-platform cards.
|
|
6
17
|
*
|
|
@@ -273,6 +284,8 @@ interface ServiceAccountCredentials {
|
|
|
273
284
|
}
|
|
274
285
|
/** Base config options shared by all auth methods */
|
|
275
286
|
interface GoogleChatAdapterBaseConfig {
|
|
287
|
+
/** Logger instance for error reporting */
|
|
288
|
+
logger: Logger;
|
|
276
289
|
/** Override bot username (optional) */
|
|
277
290
|
userName?: string;
|
|
278
291
|
/**
|
|
@@ -323,13 +336,7 @@ interface GoogleChatAdapterCustomAuthConfig extends GoogleChatAdapterBaseConfig
|
|
|
323
336
|
useApplicationDefaultCredentials?: never;
|
|
324
337
|
}
|
|
325
338
|
type GoogleChatAdapterConfig = GoogleChatAdapterServiceAccountConfig | GoogleChatAdapterADCConfig | GoogleChatAdapterCustomAuthConfig;
|
|
326
|
-
|
|
327
|
-
interface GoogleChatThreadId {
|
|
328
|
-
spaceName: string;
|
|
329
|
-
threadName?: string;
|
|
330
|
-
/** Whether this is a DM space */
|
|
331
|
-
isDM?: boolean;
|
|
332
|
-
}
|
|
339
|
+
|
|
333
340
|
/** Google Chat message structure */
|
|
334
341
|
interface GoogleChatMessage {
|
|
335
342
|
name: string;
|
|
@@ -451,6 +458,8 @@ declare class GoogleChatAdapter implements Adapter<GoogleChatThreadId, unknown>
|
|
|
451
458
|
private impersonatedChatApi?;
|
|
452
459
|
/** HTTP endpoint URL for button click actions */
|
|
453
460
|
private endpointUrl?;
|
|
461
|
+
/** User info cache for display name lookups - initialized later in initialize() */
|
|
462
|
+
private userInfoCache;
|
|
454
463
|
constructor(config: GoogleChatAdapterConfig);
|
|
455
464
|
initialize(chat: ChatInstance): Promise<void>;
|
|
456
465
|
/**
|
|
@@ -511,14 +520,6 @@ declare class GoogleChatAdapter implements Adapter<GoogleChatThreadId, unknown>
|
|
|
511
520
|
private handleMessageEvent;
|
|
512
521
|
private parseGoogleChatMessage;
|
|
513
522
|
postMessage(threadId: string, message: AdapterPostableMessage): Promise<RawMessage<unknown>>;
|
|
514
|
-
/**
|
|
515
|
-
* Extract card element from a message if present.
|
|
516
|
-
*/
|
|
517
|
-
private extractCard;
|
|
518
|
-
/**
|
|
519
|
-
* Extract files from a message if present.
|
|
520
|
-
*/
|
|
521
|
-
private extractFiles;
|
|
522
523
|
/**
|
|
523
524
|
* Create an Attachment object from a Google Chat attachment.
|
|
524
525
|
*/
|
|
@@ -538,13 +539,34 @@ declare class GoogleChatAdapter implements Adapter<GoogleChatThreadId, unknown>
|
|
|
538
539
|
* @param userId - The user's resource name (e.g., "users/123456")
|
|
539
540
|
*/
|
|
540
541
|
openDM(userId: string): Promise<string>;
|
|
541
|
-
fetchMessages(threadId: string, options?: FetchOptions): Promise<
|
|
542
|
+
fetchMessages(threadId: string, options?: FetchOptions): Promise<FetchResult<unknown>>;
|
|
543
|
+
/**
|
|
544
|
+
* Fetch messages in backward direction (most recent first).
|
|
545
|
+
* GChat API defaults to createTime ASC (oldest first), so we request DESC
|
|
546
|
+
* to get the most recent messages, then reverse for chronological order within page.
|
|
547
|
+
*/
|
|
548
|
+
private fetchMessagesBackward;
|
|
549
|
+
/**
|
|
550
|
+
* Fetch messages in forward direction (oldest first).
|
|
551
|
+
*
|
|
552
|
+
* GChat API defaults to createTime ASC (oldest first), which is what we want.
|
|
553
|
+
* For forward pagination, we:
|
|
554
|
+
* 1. If no cursor: Fetch ALL messages (already in chronological order)
|
|
555
|
+
* 2. If cursor: Cursor is a message name, skip to after that message
|
|
556
|
+
*
|
|
557
|
+
* Note: This is less efficient than backward for large message histories,
|
|
558
|
+
* as it requires fetching all messages to find the cursor position.
|
|
559
|
+
*/
|
|
560
|
+
private fetchMessagesForward;
|
|
561
|
+
/**
|
|
562
|
+
* Parse a message from the list API into the standard Message format.
|
|
563
|
+
* Resolves user display names and properly determines isMe.
|
|
564
|
+
*/
|
|
565
|
+
private parseGChatListMessage;
|
|
542
566
|
fetchThread(threadId: string): Promise<ThreadInfo>;
|
|
543
567
|
encodeThreadId(platformData: GoogleChatThreadId): string;
|
|
544
568
|
/**
|
|
545
569
|
* Check if a thread is a direct message conversation.
|
|
546
|
-
* Checks for the :dm marker in the thread ID which is set when
|
|
547
|
-
* processing DM messages or opening DMs.
|
|
548
570
|
*/
|
|
549
571
|
isDM(threadId: string): boolean;
|
|
550
572
|
decodeThreadId(threadId: string): GoogleChatThreadId;
|
|
@@ -569,18 +591,6 @@ declare class GoogleChatAdapter implements Adapter<GoogleChatThreadId, unknown>
|
|
|
569
591
|
* multi-bot spaces (especially via Pub/Sub).
|
|
570
592
|
*/
|
|
571
593
|
private isMessageFromSelf;
|
|
572
|
-
/**
|
|
573
|
-
* Cache user info for later lookup (e.g., when processing Pub/Sub messages).
|
|
574
|
-
*/
|
|
575
|
-
private cacheUserInfo;
|
|
576
|
-
/**
|
|
577
|
-
* Get cached user info.
|
|
578
|
-
*/
|
|
579
|
-
private getCachedUserInfo;
|
|
580
|
-
/**
|
|
581
|
-
* Resolve user display name, using cache if available.
|
|
582
|
-
*/
|
|
583
|
-
private resolveUserDisplayName;
|
|
584
594
|
private handleGoogleChatError;
|
|
585
595
|
}
|
|
586
596
|
declare function createGoogleChatAdapter(config: GoogleChatAdapterConfig): GoogleChatAdapter;
|