5

Send Email

by
Published Aug 4, 2022

Send an email with optional cc/bcc, HTML body, attachments, send-as alias, or as a reply in a thread. Requires gmail.send (the default scope), gmail.compose or gmail.modify on the connection.

Script gmail Verified

The script

Submitted by hugo989 Bun
Verified 5 hours ago
1
import * as wmill from "windmill-client"
2
import { S3Object } from "windmill-client"
3

4
/**
5
 * Send Email
6
 * Send an email from the connected Gmail account, with optional cc/bcc, HTML body, file attachments (from Windmill object storage) and a `from` send-as alias (see List Send-As Aliases; an unverified alias falls back to the primary address). To reply in a thread, pass the original's `thread_id` and its `Message-ID` header as `in_reply_to`, and keep the same subject. Requires gmail.send (the default scope), gmail.compose or gmail.modify on the connection.
7
 */
8
export async function main(
9
  auth: RT.Gmail,
10
  to: string,
11
  subject: string,
12
  body: string,
13
  cc: string | undefined,
14
  bcc: string | undefined,
15
  from: string | undefined,
16
  is_html: boolean = false,
17
  attachments: S3Object[] | undefined,
18
  thread_id: string | undefined,
19
  in_reply_to: string | undefined
20
) {
21
  const boundary = `wm_${crypto.randomUUID()}`
22
  const headers = [
23
    from ? `From: ${from}` : undefined,
24
    `To: ${to}`,
25
    cc ? `Cc: ${cc}` : undefined,
26
    bcc ? `Bcc: ${bcc}` : undefined,
27
    `Subject: =?UTF-8?B?${b64(subject)}?=`,
28
    in_reply_to ? `In-Reply-To: ${in_reply_to}` : undefined,
29
    in_reply_to ? `References: ${in_reply_to}` : undefined,
30
    "MIME-Version: 1.0",
31
  ].filter((h) => h !== undefined)
32
  const text = [
33
    `Content-Type: text/${is_html ? "html" : "plain"}; charset="UTF-8"`,
34
    "Content-Transfer-Encoding: base64",
35
    "",
36
    wrap(b64(body)),
37
  ].join("\r\n")
38

39
  let mime: string
40
  if (!attachments?.length) {
41
    mime = `${headers.join("\r\n")}\r\n${text}`
42
  } else {
43
    const parts = [text]
44
    for (const file of attachments) {
45
      const bytes = await wmill.loadS3File(file)
46
      if (!bytes) throw new Error(`Attachment not found: ${file.s3}`)
47
      const name = file.s3.split("/").pop()!
48
      parts.push(
49
        [
50
          `Content-Type: ${Bun.file(name).type}; name="${name}"`,
51
          `Content-Disposition: attachment; filename="${name}"`,
52
          "Content-Transfer-Encoding: base64",
53
          "",
54
          wrap(b64(bytes)),
55
        ].join("\r\n")
56
      )
57
    }
58
    mime = [
59
      ...headers,
60
      `Content-Type: multipart/mixed; boundary="${boundary}"`,
61
      "",
62
      ...parts.map((p) => `--${boundary}\r\n${p}`),
63
      `--${boundary}--`,
64
    ].join("\r\n")
65
  }
66

67
  const response = await fetch(
68
    "https://gmail.googleapis.com/gmail/v1/users/me/messages/send",
69
    {
70
      method: "POST",
71
      headers: {
72
        Authorization: `Bearer ${auth.token}`,
73
        "Content-Type": "application/json",
74
        Accept: "application/json",
75
      },
76
      body: JSON.stringify({
77
        raw: Buffer.from(mime).toString("base64url"),
78
        threadId: thread_id || undefined,
79
      }),
80
    }
81
  )
82
  if (!response.ok) {
83
    throw new Error(`${response.status} ${await response.text()}`)
84
  }
85

86
  return await response.json()
87
}
88

89
function b64(s: string | Uint8Array) {
90
  return Buffer.from(s).toString("base64")
91
}
92

93
function wrap(s: string) {
94
  return s.replace(/.{76}/g, "$&\r\n")
95
}
96

Other submissions
  • Submitted by henri186 Deno
    Created 525 days ago
    1
    import { encode as base64UrlEncode } from "https://deno.land/[email protected]/encoding/base64url.ts";
    2
    
    
    3
    /**
    4
     * @param user_id User's email address. The special value `me` can be used to indicate the authenticated user.
    5
     */
    6
    type Gmail = {
    7
      token: string;
    8
    };
    9
    
    
    10
    export async function main(
    11
      gmail_auth: Gmail,
    12
      to_email: string,
    13
      subject: string,
    14
      message: string,
    15
      user_id: string = "me",
    16
    ) {
    17
      const token = gmail_auth["token"];
    18
      if (!token) {
    19
        throw Error(`
    20
        No authentication token was found.
    21
        Go to "https://app.windmill.dev/resources?connect_app=gmail" to connect Gmail, 
    22
        then select your token from the dropdown in the arguments window.
    23
        (Click "Refresh" if you don't see your resource in the list.)\n`);
    24
      }
    25
    
    
    26
      const text =
    27
        `From: <${user_id}>\nTo: <${to_email}>\nSubject: ${subject}\nContent-Type: text/html; charset="UTF-8"\n\r <p>${message}</p>`;
    28
      const textEncoder = new TextEncoder();
    29
      const email = base64UrlEncode(textEncoder.encode(text));
    30
      const body = JSON.stringify({
    31
        raw: email,
    32
      });
    33
      const SEND_URL =
    34
        `https://gmail.googleapis.com/gmail/v1/users/${user_id}/messages/send`;
    35
    
    
    36
      const response = await fetch(SEND_URL, {
    37
        method: "POST",
    38
        headers: { Authorization: "Bearer " + token },
    39
        body,
    40
      });
    41
    
    
    42
      const result = await handleSendEmailResult(await response.json(), to_email);
    43
      return result;
    44
    }
    45
    
    
    46
    async function handleSendEmailResult(result: object, to_email: string) {
    47
      if (Object.keys(result).includes("error")) {
    48
        return Promise.reject({ wm_to_email: to_email, ...result });
    49
      }
    50
    
    
    51
      return result;
    52
    }
    53
    
    
  • Submitted by hai418 Python3
    Created 1221 days ago
    1
    #import wmill
    2
    
    
    3
    def main(x: str):
    4
        return x
  • Submitted by nicolas.michel228 Bun
    Created 819 days ago
    1
    import { encode as base64UrlEncode } from "https://deno.land/[email protected]/encoding/base64url.ts";
    2
    
    
    3
    /**
    4
     * @param user_id User's email address. The special value `me` can be used to indicate the authenticated user.
    5
     */
    6
    type Gmail = {
    7
      token: string;
    8
    };
    9
    
    
    10
    export async function main(
    11
      gmail_auth: Gmail,
    12
      to_email: string,
    13
      subject: string,
    14
      message: string,
    15
      file_name: string,
    16
      file_content: string,
    17
      user_id: string = "me",
    18
    ) {
    19
      const token = gmail_auth["token"];
    20
      if (!token) {
    21
        throw Error(`
    22
        No authentication token was found.
    23
        Go to "https://app.windmill.dev/resources?connect_app=gmail" to connect Gmail, 
    24
        then select your token from the dropdown in the arguments window.
    25
        (Click "Refresh" if you don't see your resource in the list.)\n`);
    26
      }
    27
    
    
    28
      const boundary = "boundary_string";
    29
    
    
    30
      const text = [
    31
        `Content-Type: multipart/mixed; boundary="${boundary}"\r\n`,
    32
        'MIME-Version: 1.0\r\n',
    33
        `From: ${user_id}\r\n`,
    34
        `To: ${to_email}\r\n`,
    35
        `Subject: ${subject}\r\n\r\n`,
    36
    
    
    37
        `--${boundary}\r\n`,
    38
        'Content-Type: text/plain; charset="UTF-8"\r\n',
    39
        'MIME-Version: 1.0\r\n',
    40
        'Content-Transfer-Encoding: 7bit\r\n\r\n',
    41
    
    
    42
        `${message}\r\n\r\n`,
    43
    
    
    44
        `--${boundary}\r\n`,
    45
        'Content-Type: application/octet-stream\r\n',
    46
        'MIME-Version: 1.0\r\n',
    47
        'Content-Transfer-Encoding: base64\r\n',
    48
        `Content-Disposition: attachment; filename="${file_name}"\r\n\r\n`,
    49
    
    
    50
        `${file_content}\r\n\r\n`,
    51
    
    
    52
        `--${boundary}--`
    53
      ].join('');
    54
    
    
    55
      const textEncoder = new TextEncoder();
    56
      const email = base64UrlEncode(textEncoder.encode(text));
    57
      const body = JSON.stringify({
    58
        raw: email,
    59
      });
    60
      const SEND_URL =
    61
        `https://gmail.googleapis.com/gmail/v1/users/${user_id}/messages/send?uploadType=multipart`;
    62
    
    
    63
      const response = await fetch(SEND_URL, {
    64
        method: "POST",
    65
        headers: { Authorization: "Bearer " + token },
    66
        body,
    67
      });
    68
    
    
    69
      const result = await handleSendEmailResult(await response.json(), to_email);
    70
      return result;
    71
    }
    72
    
    
    73
    async function handleSendEmailResult(result: object, to_email: string) {
    74
      if (Object.keys(result).includes("error")) {
    75
        return Promise.reject({ wm_to_email: to_email, ...result });
    76
      }
    77
    
    
    78
      return result;
    79
    }
  • Submitted by rossmccrann Deno
    Created 1512 days ago
    1
    import * as wmill from "https://deno.land/x/[email protected]/mod.ts";
    2
    import {
    3
      encode as base64UrlEncode,
    4
    } from "https://deno.land/[email protected]/encoding/base64url.ts";
    5
    
    
    6
    export async function main(
    7
      gmail_auth: wmill.Resource<"gmail">,
    8
      to_email: string,
    9
      subject: string,
    10
      message: string,
    11
      userId: string = "me",
    12
    ) {
    13
      const token = gmail_auth["token"];
    14
    
    
    15
      const email_body =
    16
        `From: <${userId}>\nTo: <${to_email}>\nSubject: ${subject}\n\r ${message}`;
    17
    
    
    18
      const email = base64UrlEncode(email_body);
    19
      const SEND_EMAIL_URL =
    20
        `https://gmail.googleapis.com/gmail/v1/users/${userId}/messages/send`;
    21
      const body = {
    22
        "raw": email,
    23
      };
    24
      const response = await fetch(SEND_EMAIL_URL, {
    25
        method: "POST",
    26
        body: JSON.stringify(body),
    27
        headers: {
    28
          Authorization: "Bearer " + token,
    29
        },
    30
      });
    31
      const response_json = await response.json();
    32
    
    
    33
      return response_json;
    34
    }
  • Submitted by hugo989 Typescript (fetch-only)
    Created 133 days ago
    1
    //native
    2
    
    
    3
    /**
    4
     * @param user_id User's email address. The special value `me` can be used to indicate the authenticated user.
    5
     */
    6
    type Gmail = {
    7
      token: string;
    8
    };
    9
    
    
    10
    export async function main(
    11
      gmail_auth: Gmail,
    12
      to_email: string,
    13
      subject: string,
    14
      message: string,
    15
      user_id: string = "me",
    16
    ) {
    17
      const token = gmail_auth["token"];
    18
      if (!token) {
    19
        throw Error(`
    20
        No authentication token was found.
    21
        Go to "https://app.windmill.dev/resources?connect_app=gmail" to connect Gmail, 
    22
        then select your token from the dropdown in the arguments window.
    23
        (Click "Refresh" if you don't see your resource in the list.)\n`);
    24
      }
    25
    
    
    26
      const text =
    27
        `From: <${user_id}>\nTo: <${to_email}>\nSubject: ${subject}\nContent-Type: text/html; charset="UTF-8"\n\r <p>${message}</p>`;
    28
      const textEncoder = new TextEncoder();
    29
      const email = base64UrlEncode(textEncoder.encode(text));
    30
      const body = JSON.stringify({
    31
        raw: email,
    32
      });
    33
      const SEND_URL =
    34
        `https://gmail.googleapis.com/gmail/v1/users/${user_id}/messages/send`;
    35
    
    
    36
      const response = await fetch(SEND_URL, {
    37
        method: "POST",
    38
        headers: { Authorization: "Bearer " + token },
    39
        body,
    40
      });
    41
    
    
    42
      const result = await handleSendEmailResult(await response.json(), to_email);
    43
      return result;
    44
    }
    45
    
    
    46
    async function handleSendEmailResult(result: object, to_email: string) {
    47
      if (Object.keys(result).includes("error")) {
    48
        return Promise.reject({ wm_to_email: to_email, ...result });
    49
      }
    50
    
    
    51
      return result;
    52
    }
    53
    
    
    54
    function base64UrlEncode(data: string | Uint8Array) {
    55
      const bytes =
    56
        typeof data === "string" ? new TextEncoder().encode(data) : data;
    57
      let binary = "";
    58
      for (let i = 0; i < bytes.length; i++) {
    59
        binary += String.fromCharCode(bytes[i]);
    60
      }
    61
      return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
    62
    }
    63
    
    
  • Submitted by adam186 Deno
    Created 1157 days ago
    1
    import { encode as base64UrlEncode } from "https://deno.land/[email protected]/encoding/base64url.ts";
    2
    
    
    3
    /**
    4
     * @param user_id User's email address. The special value `me` can be used to indicate the authenticated user.
    5
     */
    6
    type Gmail = {
    7
      token: string;
    8
    };
    9
    export async function main(
    10
      gmail_auth: Gmail,
    11
      to_email: string,
    12
      subject: string,
    13
      message: string,
    14
      user_id: string = "me",
    15
    ) {
    16
      const token = gmail_auth["token"];
    17
      if (!token) {
    18
        throw Error(`
    19
        No authentication token was found.
    20
        Go to "https://app.windmill.dev/resources?connect_app=gmail" to connect Gmail, 
    21
        then select your token from the dropdown in the arguments window.
    22
        (Click "Refresh" if you don't see your resource in the list.)\n`);
    23
      }
    24
    
    
    25
      const text = `From: <${user_id}>\nTo: <${to_email}>\nSubject: ${subject}\n\r ${message}`;
    26
      const textEncoder = new TextEncoder();
    27
      const email = base64UrlEncode(textEncoder.encode(text));
    28
      const body = JSON.stringify({
    29
        raw: email,
    30
      });
    31
      const SEND_URL = `https://gmail.googleapis.com/gmail/v1/users/${user_id}/messages/send`;
    32
    
    
    33
      const response = await fetch(SEND_URL, {
    34
        method: "POST",
    35
        headers: { Authorization: "Bearer " + token },
    36
        body,
    37
      });
    38
    
    
    39
      const result = await handleSendEmailResult(await response.json(), to_email);
    40
      return result;
    41
    }
    42
    
    
    43
    async function handleSendEmailResult(result: object, to_email: string) {
    44
      if (Object.keys(result).includes("error")) {
    45
        return Promise.reject({ wm_to_email: to_email, ...result });
    46
      }
    47
    
    
    48
      return result;
    49
    }
    50