import * as wmill from "windmill-client"
import { S3Object } from "windmill-client"
type Part = {
partId: string
mimeType: string
filename?: string
body?: { attachmentId?: string; data?: string; size?: number }
parts?: Part[]
}
async function getAttachmentParts(auth: RT.Gmail, message_id: string) {
const response = await fetch(
`https://gmail.googleapis.com/gmail/v1/users/me/messages/${message_id}?format=full`,
{
headers: {
Authorization: `Bearer ${auth.token}`,
Accept: "application/json",
},
}
)
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
const { payload } = (await response.json()) as { payload: Part }
const found: Part[] = []
const walk = (p: Part) => {
if (p.filename) found.push(p)
p.parts?.forEach(walk)
}
walk(payload)
return found
}
export type DynSelect_part_id = string
// Dropdown of the message's attachments (by MIME part id, which stays stable across fetches; attachmentId does not).
export async function part_id(auth: RT.Gmail, message_id: string) {
return (await getAttachmentParts(auth, message_id)).map((p) => ({
value: p.partId,
label: `${p.filename} (${p.mimeType}, ${p.body?.size ?? 0} bytes)`,
}))
}
/**
* Download Attachment
* 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.
*/
export async function main(
auth: RT.Gmail,
message_id: string,
part_id: DynSelect_part_id,
destination: S3Object | undefined
) {
const part = (await getAttachmentParts(auth, message_id)).find(
(p) => p.partId === part_id
)
if (!part) throw new Error(`No attachment with partId ${part_id}`)
let data = part.body?.data
if (!data) {
const response = await fetch(
`https://gmail.googleapis.com/gmail/v1/users/me/messages/${message_id}/attachments/${part.body?.attachmentId}`,
{
headers: {
Authorization: `Bearer ${auth.token}`,
Accept: "application/json",
},
}
)
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
data = ((await response.json()) as { data: string }).data
}
const file = await wmill.writeS3File(
destination || undefined,
new Blob([Buffer.from(data, "base64url")]),
undefined,
part.mimeType
)
return { ...file, filename: part.filename, mime_type: part.mimeType }
}
Submitted by hugo989 6 hours ago