|
| 1 | +import { |
| 2 | + GLOBAL_LANGUAGE_OPTIONS, |
| 3 | + TEMPLATE_ENGINE_OPTIONS, |
| 4 | +} from "../../common/constants.mjs"; |
| 5 | +import { parseObject } from "../../common/utils.mjs"; |
| 6 | +import app from "../../unione.app.mjs"; |
| 7 | + |
| 8 | +export default { |
| 9 | + key: "unione-send-email", |
| 10 | + name: "Send Email", |
| 11 | + description: "Send an email using UniOne. [See the documentation](https://docs.unione.io/en/web-api-ref#email-send)", |
| 12 | + version: "0.0.1", |
| 13 | + annotations: { |
| 14 | + destructiveHint: false, |
| 15 | + openWorldHint: true, |
| 16 | + readOnlyHint: false, |
| 17 | + }, |
| 18 | + type: "action", |
| 19 | + props: { |
| 20 | + app, |
| 21 | + recipients: { |
| 22 | + type: "string[]", |
| 23 | + label: "Recipients", |
| 24 | + description: "Array of recipient objects with email, substitutions (merge tags), and metadata. Each recipient can have: email (required), substitutions (object, optional), metadata (object, optional). If provided, this takes precedence over 'To' prop. Example: [{ \"email\": \"recipient@example.com\", \"substitutions\": { \"from_name\": \"John Doe\", \"subject\": \"Hello, {name}!\" }, \"metadata\": { \"company\": \"Example Inc.\" } }]. [See the documentation](https://docs.unione.io/en/web-api-ref#email-send)", |
| 25 | + optional: true, |
| 26 | + }, |
| 27 | + templateId: { |
| 28 | + propDefinition: [ |
| 29 | + app, |
| 30 | + "templateId", |
| 31 | + ], |
| 32 | + optional: true, |
| 33 | + }, |
| 34 | + tags: { |
| 35 | + propDefinition: [ |
| 36 | + app, |
| 37 | + "tags", |
| 38 | + ], |
| 39 | + optional: true, |
| 40 | + }, |
| 41 | + skipUnsubscribe: { |
| 42 | + type: "boolean", |
| 43 | + label: "Skip Unsubscribe", |
| 44 | + description: "Whether to skip or not appending default unsubscribe footer. You should [ask support](https://cp.unione.io/en/support?_gl=1*1afrczd*_ga*MTgyNTM0MDM4OS4xNzYyODkzNzky*_ga_37TV6WM09S*czE3NjI4OTM3OTIkbzEkZzEkdDE3NjI4OTQ1NTAkajQ5JGwwJGg4ODQyOTkzMzQ.) to approve.", |
| 45 | + optional: true, |
| 46 | + }, |
| 47 | + globalLanguage: { |
| 48 | + type: "string", |
| 49 | + label: "Global Language", |
| 50 | + description: "The language of the unsubscribe footer and unsubscribe page.", |
| 51 | + options: GLOBAL_LANGUAGE_OPTIONS, |
| 52 | + optional: true, |
| 53 | + }, |
| 54 | + templateEngine: { |
| 55 | + type: "string", |
| 56 | + label: "Template Engine", |
| 57 | + description: "The [template engine](https://docs.unione.io/en/template-engines) for handling the substitutions(merge tags).", |
| 58 | + optional: true, |
| 59 | + options: TEMPLATE_ENGINE_OPTIONS, |
| 60 | + }, |
| 61 | + globalSubstitutions: { |
| 62 | + type: "object", |
| 63 | + label: "Global Substitutions", |
| 64 | + description: "Object for passing the substitutions(merge tags) common for all recipients - e.g., company name. If the substitution names are duplicated in recipient 'substitutions', the values of the variables will be taken from the recipient 'substitutions'. Example: { \"body\": { \"html\": \"Hello, {name}!\", \"plaintext\": \"Hello, {name}!\", \"amp\": \"Hello, {name}!\"}, \"subject\": \"Hello, {name}!\", \"from_name\": \"John Doe\", \"options\": { \"unsubscribe_url\": \"https://example.com/unsubscribe\" } }.", |
| 65 | + optional: true, |
| 66 | + }, |
| 67 | + globalMetadata: { |
| 68 | + type: "object", |
| 69 | + label: "Global Metadata", |
| 70 | + description: "Object for passing the metadata common for all the recipients, such as 'key': 'value'. Max key quantity: 10. Max key length: 64 symbols. Max value length: 1024 symbols.", |
| 71 | + optional: true, |
| 72 | + }, |
| 73 | + body: { |
| 74 | + type: "object", |
| 75 | + label: "Body", |
| 76 | + description: "Contains HTML/plaintext/AMP parts of the email. Either html or plaintext part is required. Example: { \"html\": \"Hello, {name}!\", \"plaintext\": \"Hello, {name}!\", \"amp\": \"Hello, {name}!\" }.", |
| 77 | + }, |
| 78 | + subject: { |
| 79 | + type: "string", |
| 80 | + label: "Subject", |
| 81 | + description: "Email subject", |
| 82 | + }, |
| 83 | + fromEmail: { |
| 84 | + type: "string", |
| 85 | + label: "From Email", |
| 86 | + description: "Sender's email. Required only if `Template ID` prop is empty.", |
| 87 | + optional: true, |
| 88 | + }, |
| 89 | + fromName: { |
| 90 | + type: "string", |
| 91 | + label: "From Name", |
| 92 | + description: "Sender's name", |
| 93 | + optional: true, |
| 94 | + }, |
| 95 | + replyTo: { |
| 96 | + type: "string", |
| 97 | + label: "Reply To", |
| 98 | + description: "Reply-to email (in case it's different to sender's email)", |
| 99 | + optional: true, |
| 100 | + }, |
| 101 | + replyToName: { |
| 102 | + type: "string", |
| 103 | + label: "Reply To Name", |
| 104 | + description: "Reply-To name (if `Reply To` email is specified and you want to display not only this email but also the name)", |
| 105 | + optional: true, |
| 106 | + }, |
| 107 | + trackLinks: { |
| 108 | + type: "boolean", |
| 109 | + label: "Track Links", |
| 110 | + description: "If true, click tracking is on (default). If false, click tracking is off. To use track_links = false, you need to ask UniOne support to enable this feature.", |
| 111 | + optional: true, |
| 112 | + }, |
| 113 | + trackRead: { |
| 114 | + type: "boolean", |
| 115 | + label: "Track Read", |
| 116 | + description: "If true, read tracking is on (default). If false, read tracking is off. To use track_read = false, you need to ask support to enable this feature.", |
| 117 | + optional: true, |
| 118 | + }, |
| 119 | + bypassGlobal: { |
| 120 | + type: "boolean", |
| 121 | + label: "Bypass Global", |
| 122 | + description: "If true, the global unavailability list will be ignored. Even if the address was found to be unreachable while sending other UniOne users' emails, or its owner has issued complaints, the message will still be sent. The setting may be ignored for certain addresses.", |
| 123 | + optional: true, |
| 124 | + }, |
| 125 | + bypassUnavailable: { |
| 126 | + type: "boolean", |
| 127 | + label: "Bypass Unavailable", |
| 128 | + description: "If true, the current list of unsubscribed addresses for this account or project will be ignored. Works only if `Bypass Global` is set to true. The setting is available only for users that have been granted the right to omit the unsubscribe link (to request, please contact [support](https://cp.unione.io/en/support?_gl=1*1msqb8a*_ga*MTgyNTM0MDM4OS4xNzYyODkzNzky*_ga_37TV6WM09S*czE3NjI4OTM3OTIkbzEkZzEkdDE3NjI4OTQ1NTAkajQ5JGg4ODQyOTkzMzQ.)).", |
| 129 | + optional: true, |
| 130 | + }, |
| 131 | + bypassUnsubscribed: { |
| 132 | + type: "boolean", |
| 133 | + label: "Bypass Unsubscribed", |
| 134 | + description: "If true, the current list of unsubscribed addresses for this account or project will be ignored. Works only if `Bypass Global` is set to true. The setting is available only for users that have been granted the right to omit the unsubscribe link.", |
| 135 | + optional: true, |
| 136 | + }, |
| 137 | + bypassComplained: { |
| 138 | + type: "boolean", |
| 139 | + label: "Bypass Complained", |
| 140 | + description: "If true, the user's or project's complaint list will be ignored. Works only if `Bypass Global` is set to true. The setting is available only for users that have been granted the right to omit the unsubscribe link.", |
| 141 | + optional: true, |
| 142 | + }, |
| 143 | + idempotenceKey: { |
| 144 | + type: "string", |
| 145 | + label: "Idempotence Key", |
| 146 | + description: "A string of up to 64 characters containing a unique message key. This can be used to prevent occasional message duplicates. If you send another API request with the same message key within the next minute, it will be declined. We can generate a message key for each letter automatically; to enable this option, please contact our [tech support](https://cp.unione.io/en/support?_gl=1*dahmdm*_ga*MTgyNTM0MDM4OS4xNzYyODkzNzky*_ga_37TV6WM09S*czE3NjI4OTM3OTIkbzEkZzEkdDE3NjI4OTQ1NTAkajQ5JGwwJGg4ODQyOTkzMzQ.).", |
| 147 | + optional: true, |
| 148 | + }, |
| 149 | + headers: { |
| 150 | + type: "object", |
| 151 | + label: "Headers", |
| 152 | + description: "Contains email headers, maximum 50. Only headers with “X-” name prefix are accepted, all other are ignored, for example X-UNIONE-Global-Language, X-UNIONE-Template-Engine. Standard headers “To,” “CC,” and “BCC” are passed without the “X-.” Yet, they are processed in a particular way and, as a result, have a number of restrictions. You can find more details about it [here](https://docs.unione.io/cc-and-bcc). If our support have approved omitting standard unsubscription block for you, you can also pass List-Unsubscribe, List-Subscribe, List-Help, List-Owner, List-Archive, In-Reply-To and References headers. Example: { \"X-UNIONE-Global-Language\": \"en\", \"X-UNIONE-Template-Engine\": \"velocity\" }.", |
| 153 | + optional: true, |
| 154 | + }, |
| 155 | + sendAt: { |
| 156 | + type: "string", |
| 157 | + label: "Send At", |
| 158 | + description: "Date and time in 'YYYY-MM-DD hh:mm:ss' format in the UTC timezone. Allows schedule sending up to 24 hours in advance.", |
| 159 | + optional: true, |
| 160 | + }, |
| 161 | + unsubscribeUrl: { |
| 162 | + type: "string", |
| 163 | + label: "Unsubscribe URL", |
| 164 | + description: "Custom unsubscribe link. Read more [here](https://docs.unione.io/en/unsubscribe-link).", |
| 165 | + optional: true, |
| 166 | + }, |
| 167 | + }, |
| 168 | + async run({ $ }) { |
| 169 | + if (!this.templateId && !this.fromEmail) { |
| 170 | + throw new Error("`From Email` is required when `Template ID` prop is not provided"); |
| 171 | + } |
| 172 | + |
| 173 | + const response = await this.app.sendEmail({ |
| 174 | + $, |
| 175 | + data: { |
| 176 | + message: { |
| 177 | + recipients: parseObject(this.recipients), |
| 178 | + template_id: this.templateId, |
| 179 | + tags: parseObject(this.tags), |
| 180 | + skip_unsubscribe: this.skipUnsubscribe |
| 181 | + ? 1 |
| 182 | + : 0, |
| 183 | + global_language: parseObject(this.globalLanguage), |
| 184 | + template_engine: parseObject(this.templateEngine), |
| 185 | + global_substitutions: parseObject(this.globalSubstitutions), |
| 186 | + global_metadata: parseObject(this.globalMetadata), |
| 187 | + body: this.body && parseObject(this.body), |
| 188 | + subject: this.subject, |
| 189 | + from_email: this.fromEmail, |
| 190 | + from_name: this.fromName, |
| 191 | + reply_to: this.replyTo, |
| 192 | + reply_to_name: this.replyToName, |
| 193 | + track_links: this.trackLinks |
| 194 | + ? 1 |
| 195 | + : 0, |
| 196 | + track_read: this.trackRead |
| 197 | + ? 1 |
| 198 | + : 0, |
| 199 | + bypass_global: this.bypassGlobal |
| 200 | + ? 1 |
| 201 | + : 0, |
| 202 | + bypass_unavailable: this.bypassUnavailable |
| 203 | + ? 1 |
| 204 | + : 0, |
| 205 | + bypass_unsubscribed: this.bypassUnsubscribed |
| 206 | + ? 1 |
| 207 | + : 0, |
| 208 | + bypass_complained: this.bypassComplained |
| 209 | + ? 1 |
| 210 | + : 0, |
| 211 | + idempotence_key: this.idempotenceKey, |
| 212 | + headers: this.headers && parseObject(this.headers), |
| 213 | + options: { |
| 214 | + send_at: this.sendAt, |
| 215 | + unsubscribe_url: this.unsubscribeUrl, |
| 216 | + }, |
| 217 | + }, |
| 218 | + }, |
| 219 | + }); |
| 220 | + |
| 221 | + const recipientEmails = parseObject(this.recipients).map((r) => r.email || r) |
| 222 | + .join(", "); |
| 223 | + if (response.status === "success") { |
| 224 | + $.export("$summary", `Successfully sent email to ${recipientEmails}`); |
| 225 | + } else { |
| 226 | + $.export("$summary", `Email send request completed with status: ${response.status}`); |
| 227 | + } |
| 228 | + |
| 229 | + return response; |
| 230 | + }, |
| 231 | +}; |
| 232 | + |
0 commit comments