@mercury-fw/channel-google-chat 0.5.0 → 0.6.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/CHANGELOG.md +10 -0
- package/README.md +2 -0
- package/dist/google-chat-provider.d.ts +4 -1
- package/google-chat-provider.ts +20 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @mercury-fw/channel-google-chat
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c24de78: A Google Chat sender is recognised as their ZITADEL user.
|
|
8
|
+
|
|
9
|
+
- `channel-google-chat` passes the sender's email on as a claim (`claims.email`), on messages and on card clicks, when Google vouches for it: a person's (`HUMAN`) only.
|
|
10
|
+
- `zitadelDirectory` resolves a Google Chat sender to the one active ZITADEL user with that email, verified, whose links to the Google identity provider (`ZITADEL_GOOGLE_IDP_ID`, new and optional) include the sender's own Google account. Without that variable nobody is recognised from Chat. Anyone else on Chat stays unknown and can link their account with a code.
|
|
11
|
+
- `mfw create` lists `ZITADEL_GOOGLE_IDP_ID` in the env example of an app with the ZITADEL directory.
|
|
12
|
+
|
|
3
13
|
## 0.5.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ channels: [googleChatChannel],
|
|
|
24
24
|
| `GOOGLE_CHAT_APP_CLIENT_EMAIL` | The service account the app authenticates as. |
|
|
25
25
|
| `GOOGLE_CHAT_APP_PRIVATE_KEY` | That service account's private key, on one line with literal `\n` (step 5 writes it that way). |
|
|
26
26
|
|
|
27
|
+
Each message reaches the core as `google-chat:users/<id>`, the sender's Chat user, with their email when Google vouches for it (a person's, never a bot's), which a user directory can join on: the ZITADEL directory of `@mercury-fw/plugin-zitadel` does.
|
|
28
|
+
|
|
27
29
|
## Table of contents
|
|
28
30
|
|
|
29
31
|
- [Setting up the Chat app](#setting-up-the-chat-app)
|
|
@@ -41,6 +41,8 @@ export type ParsedMessageEvent = {
|
|
|
41
41
|
space: string;
|
|
42
42
|
sender: string;
|
|
43
43
|
senderDisplayName: string | undefined;
|
|
44
|
+
/** The sender's email, which Google vouches for: a person's only, never a bot's. */
|
|
45
|
+
senderEmail: string | undefined;
|
|
44
46
|
/**
|
|
45
47
|
* True only when `space.type` is exactly `"DM"` (the Chat API's shape
|
|
46
48
|
* for a private 1:1 space) — anything else, including a missing field,
|
|
@@ -54,11 +56,12 @@ export type ParsedCardClickEvent = {
|
|
|
54
56
|
kind: "card-click";
|
|
55
57
|
space: string;
|
|
56
58
|
sender: string;
|
|
59
|
+
senderEmail: string | undefined;
|
|
57
60
|
parameters: Record<string, string>;
|
|
58
61
|
};
|
|
59
62
|
/** Parses one decoded Pub/Sub event, or `null` if it isn't a kind this provider acts on. */
|
|
60
63
|
export declare function parseChatEvent(raw: unknown): ParsedMessageEvent | ParsedCardClickEvent | null;
|
|
61
|
-
export type CardClickHandler = (params: Record<string, string>, space: string, sender: string) => Promise<void>;
|
|
64
|
+
export type CardClickHandler = (params: Record<string, string>, space: string, sender: string, senderEmail?: string) => Promise<void>;
|
|
62
65
|
export type GoogleChatProviderDeps = {
|
|
63
66
|
credentials: ServiceAccountCredentials;
|
|
64
67
|
subscription: string;
|
package/google-chat-provider.ts
CHANGED
|
@@ -197,6 +197,8 @@ export type ParsedMessageEvent = {
|
|
|
197
197
|
space: string;
|
|
198
198
|
sender: string;
|
|
199
199
|
senderDisplayName: string | undefined;
|
|
200
|
+
/** The sender's email, which Google vouches for: a person's only, never a bot's. */
|
|
201
|
+
senderEmail: string | undefined;
|
|
200
202
|
/**
|
|
201
203
|
* True only when `space.type` is exactly `"DM"` (the Chat API's shape
|
|
202
204
|
* for a private 1:1 space) — anything else, including a missing field,
|
|
@@ -211,9 +213,15 @@ export type ParsedCardClickEvent = {
|
|
|
211
213
|
kind: "card-click";
|
|
212
214
|
space: string;
|
|
213
215
|
sender: string;
|
|
216
|
+
senderEmail: string | undefined;
|
|
214
217
|
parameters: Record<string, string>;
|
|
215
218
|
};
|
|
216
219
|
|
|
220
|
+
/** The email Google vouches for on `sender`: a person's only (`HUMAN`), never a bot's or an unknown kind's. */
|
|
221
|
+
function emailOf(sender: RawChatSender | undefined): string | undefined {
|
|
222
|
+
return typeof sender?.email === "string" && sender.email !== "" && sender.type === "HUMAN" ? sender.email : undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
217
225
|
/** Parses one decoded Pub/Sub event, or `null` if it isn't a kind this provider acts on. */
|
|
218
226
|
export function parseChatEvent(raw: unknown): ParsedMessageEvent | ParsedCardClickEvent | null {
|
|
219
227
|
const event = raw as RawChatEvent;
|
|
@@ -229,6 +237,7 @@ export function parseChatEvent(raw: unknown): ParsedMessageEvent | ParsedCardCli
|
|
|
229
237
|
space: m.space.name,
|
|
230
238
|
sender: m.sender.name,
|
|
231
239
|
senderDisplayName: m.sender.displayName,
|
|
240
|
+
senderEmail: emailOf(m.sender),
|
|
232
241
|
isDirectMessage: m.space.type === "DM",
|
|
233
242
|
};
|
|
234
243
|
}
|
|
@@ -241,12 +250,12 @@ export function parseChatEvent(raw: unknown): ParsedMessageEvent | ParsedCardCli
|
|
|
241
250
|
parameters[p.key] = p.value;
|
|
242
251
|
}
|
|
243
252
|
}
|
|
244
|
-
return { kind: "card-click", space: event.space.name, sender: event.user.name, parameters };
|
|
253
|
+
return { kind: "card-click", space: event.space.name, sender: event.user.name, senderEmail: emailOf(event.user), parameters };
|
|
245
254
|
}
|
|
246
255
|
return null;
|
|
247
256
|
}
|
|
248
257
|
|
|
249
|
-
export type CardClickHandler = (params: Record<string, string>, space: string, sender: string) => Promise<void>;
|
|
258
|
+
export type CardClickHandler = (params: Record<string, string>, space: string, sender: string, senderEmail?: string) => Promise<void>;
|
|
250
259
|
|
|
251
260
|
export type GoogleChatProviderDeps = {
|
|
252
261
|
credentials: ServiceAccountCredentials;
|
|
@@ -310,13 +319,17 @@ export function createGoogleChatProvider(deps: GoogleChatProviderDeps): GoogleCh
|
|
|
310
319
|
*/
|
|
311
320
|
const onCardClick: CardClickHandler =
|
|
312
321
|
deps.onCardClick ??
|
|
313
|
-
(async (params, space, sender) => {
|
|
322
|
+
(async (params, space, sender, senderEmail) => {
|
|
314
323
|
const token = params.token;
|
|
315
324
|
if (!token) {
|
|
316
325
|
log(`[chat] card click with no token parameter`);
|
|
317
326
|
return;
|
|
318
327
|
}
|
|
319
|
-
const reply = await deps.confirm(token, deriveSessionKey(space, sender), {
|
|
328
|
+
const reply = await deps.confirm(token, deriveSessionKey(space, sender), {
|
|
329
|
+
id: sender,
|
|
330
|
+
provider: "google-chat",
|
|
331
|
+
...(senderEmail === undefined ? {} : { claims: { email: senderEmail } }),
|
|
332
|
+
});
|
|
320
333
|
if (reply !== null) {
|
|
321
334
|
log(`[chat:${space}] [out] ${reply}`);
|
|
322
335
|
const sent = await sendMessageFn(space, reply, clientDeps);
|
|
@@ -483,6 +496,8 @@ export function createGoogleChatProvider(deps: GoogleChatProviderDeps): GoogleCh
|
|
|
483
496
|
id: event.sender,
|
|
484
497
|
provider: "google-chat",
|
|
485
498
|
...(event.senderDisplayName ? { displayName: event.senderDisplayName } : {}),
|
|
499
|
+
// A directory joins a Chat identity to a person on it (see the ZITADEL directory).
|
|
500
|
+
...(event.senderEmail === undefined ? {} : { claims: { email: event.senderEmail } }),
|
|
486
501
|
};
|
|
487
502
|
|
|
488
503
|
const confirmReply = await deps.confirm(event.text, sessionKey, principal);
|
|
@@ -556,7 +571,7 @@ export function createGoogleChatProvider(deps: GoogleChatProviderDeps): GoogleCh
|
|
|
556
571
|
if (parsed.kind === "message") {
|
|
557
572
|
await processMessageEvent(parsed, handleTurn);
|
|
558
573
|
} else {
|
|
559
|
-
await onCardClick(parsed.parameters, parsed.space, parsed.sender);
|
|
574
|
+
await onCardClick(parsed.parameters, parsed.space, parsed.sender, parsed.senderEmail);
|
|
560
575
|
}
|
|
561
576
|
} catch (err) {
|
|
562
577
|
log(`[chat] event handling failed: ${String(err)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercury-fw/channel-google-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@mercury-fw/channel-types": ">=0.36.0 <1.0.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@mercury-fw/channel-types": "0.
|
|
37
|
+
"@mercury-fw/channel-types": "0.44.1",
|
|
38
38
|
"@mercury-fw/typescript-config": "*",
|
|
39
39
|
"@types/bun": "^1.4.2",
|
|
40
40
|
"typescript": "^6.0.3"
|