/** * Google Workspace: odeslani e-mailu pres Gmail. * * Sluzba: https://services.csbot.cz/apps/google-service * Endpoint: POST /google/gmail/messages/send * * Gmail neprijima predmet a telo zvlast. Chce **cely e-mail podle RFC 2822** * zakodovany do base64url, takze zpravu sklada skript. Predmet s diakritikou * se navic musi zabalit do MIME slova, jinak z nej cestou zbydou otazniky. * * Kodovani se dela pres `TextEncoder` a `btoa`, ne pres Buffer: jsou to * standardni funkce, ktere budou k dispozici i az skripty pobezi v sandboxu. */ export const manifest = { id: 'google.send-email', name: 'Odeslat e-mail', description: 'Pošle e-mail přes Gmail účtu, pod kterým je napojení. Příjemců může být víc, ' + 'oddělují se čárkou.', inputs: [ { id: 'to', label: 'Příjemci', type: 'string', required: true, hint: 'E-maily oddělené čárkou.', }, { id: 'cc', label: 'Kopie', type: 'string', required: false }, { id: 'subject', label: 'Předmět', type: 'string', required: true }, { id: 'body', label: 'Text zprávy', type: 'string', required: true, multiline: true }, { id: 'html', label: 'Text je HTML', type: 'boolean', required: false, default: false, hint: 'Vypnuté odešle zprávu jako čistý text.', }, { id: 'from', label: 'Odesílatel', type: 'string', required: false, hint: 'Jen když má účet povolený alias. Bez vyplnění pošle Gmail zprávu za vlastníka schránky.', }, ], outputs: [ { id: 'messageId', label: 'ID zprávy', type: 'string', required: true }, { id: 'threadId', label: 'ID konverzace', type: 'string', required: false }, { id: 'recipients', label: 'Počet příjemců', type: 'number', required: true }, ], timeoutMs: 30000, }; /** Base64 z textu. Pres bajty, aby diakritika prezila. */ function base64(value) { const bytes = new TextEncoder().encode(value); let binary = ''; for (const byte of bytes) binary += String.fromCharCode(byte); return btoa(binary); } /** Base64url bez vypln, jak ho chce Gmail. */ function base64Url(value) { return base64(value).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } /** Hlavicka s diakritikou musi byt MIME slovo, jinak z ni zbydou otazniky. */ function header(value) { for (const character of value) { if (character.codePointAt(0) > 127) return `=?UTF-8?B?${base64(value)}?=`; } return value; } export async function run(inputs, ctx) { const { pick, text, need, addresses } = ctx.util; // Clovek pise carky, Gmail chce jeden retezec. const to = addresses(inputs.to); if (to.length === 0) ctx.fail('Nezůstal žádný příjemce, zkontrolujte pole Příjemci.'); const cc = addresses(inputs.cc); const lines = [ `To: ${to.join(', ')}`, ...(cc.length > 0 ? [`Cc: ${cc.join(', ')}`] : []), ...(inputs.from ? [`From: ${inputs.from}`] : []), `Subject: ${header(inputs.subject)}`, 'MIME-Version: 1.0', `Content-Type: text/${inputs.html ? 'html' : 'plain'}; charset="UTF-8"`, 'Content-Transfer-Encoding: 8bit', '', inputs.body, ]; const { body } = await ctx.http.post('/google/gmail/messages/send', { raw: base64Url(lines.join('\r\n')), }); ctx.log(`Odesláno ${to.length + cc.length} příjemcům.`); return { messageId: need(text(pick(body, 'id')), 'ID zprávy'), threadId: text(pick(body, 'threadId')), recipients: to.length + cc.length, }; }