1 | import * as wmill from "windmill-client" |
2 | import { S3Object } from "windmill-client" |
3 |
|
4 | type Part = { |
5 | partId: string |
6 | mimeType: string |
7 | filename?: string |
8 | body?: { attachmentId?: string; data?: string; size?: number } |
9 | parts?: Part[] |
10 | } |
11 |
|
12 | async function getAttachmentParts(auth: RT.Gmail, message_id: string) { |
13 | const response = await fetch( |
14 | `https://gmail.googleapis.com/gmail/v1/users/me/messages/${message_id}?format=full`, |
15 | { |
16 | headers: { |
17 | Authorization: `Bearer ${auth.token}`, |
18 | Accept: "application/json", |
19 | }, |
20 | } |
21 | ) |
22 | if (!response.ok) { |
23 | throw new Error(`${response.status} ${await response.text()}`) |
24 | } |
25 | const { payload } = (await response.json()) as { payload: Part } |
26 | const found: Part[] = [] |
27 | const walk = (p: Part) => { |
28 | if (p.filename) found.push(p) |
29 | p.parts?.forEach(walk) |
30 | } |
31 | walk(payload) |
32 | return found |
33 | } |
34 |
|
35 | export type DynSelect_part_id = string |
36 |
|
37 | |
38 | export async function part_id(auth: RT.Gmail, message_id: string) { |
39 | return (await getAttachmentParts(auth, message_id)).map((p) => ({ |
40 | value: p.partId, |
41 | label: `${p.filename} (${p.mimeType}, ${p.body?.size ?? 0} bytes)`, |
42 | })) |
43 | } |
44 |
|
45 | |
46 | * Download Attachment |
47 | * Save one attachment of a Gmail message to Windmill object storage and return its S3 reference. `part_id` is the attachment's MIME `partId` (as in Get Message's `payload.parts`); pick it from the dropdown. Without `destination`, a key is generated. Requires gmail.readonly or gmail.modify on the connection. |
48 | */ |
49 | export async function main( |
50 | auth: RT.Gmail, |
51 | message_id: string, |
52 | part_id: DynSelect_part_id, |
53 | destination: S3Object | undefined |
54 | ) { |
55 | const part = (await getAttachmentParts(auth, message_id)).find( |
56 | (p) => p.partId === part_id |
57 | ) |
58 | if (!part) throw new Error(`No attachment with partId ${part_id}`) |
59 |
|
60 | let data = part.body?.data |
61 | if (!data) { |
62 | const response = await fetch( |
63 | `https://gmail.googleapis.com/gmail/v1/users/me/messages/${message_id}/attachments/${part.body?.attachmentId}`, |
64 | { |
65 | headers: { |
66 | Authorization: `Bearer ${auth.token}`, |
67 | Accept: "application/json", |
68 | }, |
69 | } |
70 | ) |
71 | if (!response.ok) { |
72 | throw new Error(`${response.status} ${await response.text()}`) |
73 | } |
74 | data = ((await response.json()) as { data: string }).data |
75 | } |
76 |
|
77 | const file = await wmill.writeS3File( |
78 | destination || undefined, |
79 | new Blob([Buffer.from(data, "base64url")]), |
80 | undefined, |
81 | part.mimeType |
82 | ) |
83 | return { ...file, filename: part.filename, mime_type: part.mimeType } |
84 | } |
85 |
|