Edits history of script submission #23324 for ' Download Attachment (gmail)'

  • bun
    One script reply has been approved by the moderators
    Ap­pro­ved
    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