2022-06-28 12:59:31 +02:00
|
|
|
import fs from 'fs'
|
|
|
|
import path from 'path'
|
|
|
|
import axios from 'axios'
|
|
|
|
import { unified } from 'unified'
|
2023-10-01 15:32:20 +02:00
|
|
|
import remarkParse from 'remark-parse'
|
|
|
|
import remarkRehype from 'remark-rehype'
|
|
|
|
import rehypeFormat from 'rehype-format'
|
|
|
|
import rehypeStringify from 'rehype-stringify'
|
|
|
|
import remarkGfm from 'remark-gfm'
|
2024-01-02 12:31:31 +01:00
|
|
|
import remarkSmartypants from 'remark-smartypants'
|
|
|
|
import remarkFrontmatter from 'remark-frontmatter'
|
2022-06-28 12:59:31 +02:00
|
|
|
import mustache from 'mustache'
|
|
|
|
import { testers } from '../config/newsletter-testers.mjs'
|
2023-01-09 20:46:32 +01:00
|
|
|
import { fileURLToPath } from 'url'
|
2023-04-02 10:34:07 +02:00
|
|
|
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2'
|
2022-06-27 17:15:18 +02:00
|
|
|
|
2022-06-28 12:59:31 +02:00
|
|
|
// Current working directory
|
|
|
|
const cwd = path.dirname(fileURLToPath(import.meta.url))
|
|
|
|
|
2025-04-06 16:56:32 +02:00
|
|
|
const backend = 'https://backend.freesewing.eu/'
|
2024-01-01 15:55:49 +01:00
|
|
|
|
|
|
|
const i18n = {
|
|
|
|
en: {
|
|
|
|
title: 'FreeSewing newsletter',
|
|
|
|
support: 'Support FreeSewing: Become a patron',
|
|
|
|
unsub1: 'You can unsubscribe at any time',
|
|
|
|
unsub2: 'Or reply and tell us you want out',
|
|
|
|
},
|
|
|
|
}
|
2022-06-27 17:15:18 +02:00
|
|
|
|
|
|
|
const asHtml = async (text) => {
|
2023-01-09 20:46:32 +01:00
|
|
|
const content = await unified()
|
2023-10-01 15:32:20 +02:00
|
|
|
.use(remarkParse)
|
|
|
|
.use(remarkGfm)
|
2024-01-02 12:31:31 +01:00
|
|
|
.use(remarkSmartypants)
|
|
|
|
.use(remarkFrontmatter, ['yaml'])
|
2023-10-01 15:32:20 +02:00
|
|
|
.use(remarkRehype)
|
|
|
|
.use(rehypeFormat)
|
|
|
|
.use(rehypeStringify)
|
2023-01-09 20:46:32 +01:00
|
|
|
.process(text)
|
2022-06-27 17:15:18 +02:00
|
|
|
|
2022-06-28 16:07:30 +02:00
|
|
|
return content.value
|
2022-06-27 17:15:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const getSubscribers = async (test = true) => {
|
|
|
|
if (test) return testers
|
2024-01-02 12:31:31 +01:00
|
|
|
let res = await axios.get(`${backend}admin/subscribers/key`, {
|
2024-01-01 15:55:49 +01:00
|
|
|
auth: {
|
|
|
|
username: process.env.NL_API_KEY,
|
|
|
|
password: process.env.NL_API_SECRET,
|
|
|
|
},
|
2022-06-27 17:15:18 +02:00
|
|
|
})
|
2024-01-02 12:31:31 +01:00
|
|
|
if (res.data && res.data.subscribers) return res.data.subscribers
|
2022-06-27 17:15:18 +02:00
|
|
|
else return false
|
|
|
|
}
|
|
|
|
|
|
|
|
const send = async (test = true) => {
|
2023-04-02 10:34:07 +02:00
|
|
|
const us = 'FreeSewing <info@freesewing.org>'
|
2022-06-28 12:59:31 +02:00
|
|
|
const template = fs.readFileSync(`${cwd}/../config/templates/newsletter.html`, 'utf8')
|
2022-06-27 17:15:18 +02:00
|
|
|
const subscribers = await getSubscribers(test)
|
|
|
|
|
2023-04-02 10:34:07 +02:00
|
|
|
// Oh AWS your APIs are such a clusterfuck
|
|
|
|
const client = new SESv2Client({ region: 'us-east-1' })
|
|
|
|
|
2022-06-27 17:15:18 +02:00
|
|
|
let i = 1
|
2024-01-01 15:55:49 +01:00
|
|
|
for (const lang in subscribers) {
|
2024-01-02 12:31:31 +01:00
|
|
|
let l = 1
|
2024-01-01 15:55:49 +01:00
|
|
|
let edition
|
|
|
|
try {
|
|
|
|
edition = await axios.get(
|
2025-04-06 16:56:32 +02:00
|
|
|
`https://raw.githubusercontent.com/freesewing/freesewing/refs/heads/develop/sites/org/newsletter/${process.env.NL_EDITION}/index.mdx`,
|
2024-01-01 15:55:49 +01:00
|
|
|
'utf8'
|
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err)
|
|
|
|
process.exit()
|
|
|
|
}
|
2024-01-02 12:31:31 +01:00
|
|
|
const text = edition.data
|
2024-01-01 15:55:49 +01:00
|
|
|
const content = await asHtml(text)
|
2023-04-02 10:34:07 +02:00
|
|
|
|
2024-01-01 15:55:49 +01:00
|
|
|
subscribers[lang].sort()
|
|
|
|
let subs = subscribers[lang].length
|
2024-01-02 12:31:31 +01:00
|
|
|
|
2024-01-01 15:55:49 +01:00
|
|
|
for (let sub of subscribers[lang]) {
|
2024-01-02 12:31:31 +01:00
|
|
|
if (l > 0) {
|
2025-04-06 16:56:32 +02:00
|
|
|
const unsubGet = `https://freesewing.eu/newsletter/unsubscribe?x=${sub.ehash}`
|
|
|
|
const unsubPost = `https://backend.freesewing.eu/ocunsub/${sub.ehash}`
|
2024-01-02 12:31:31 +01:00
|
|
|
const body = mustache.render(template, {
|
2025-04-01 16:15:20 +02:00
|
|
|
...i18n.en,
|
2024-04-01 16:57:35 +02:00
|
|
|
unsubscribe: unsubGet,
|
2024-01-01 15:55:49 +01:00
|
|
|
content,
|
|
|
|
})
|
2024-01-02 12:31:31 +01:00
|
|
|
console.log(`[${lang}] ${l}/${subs} (${i}) Sending to ${sub.email}`)
|
2024-01-01 15:55:49 +01:00
|
|
|
|
|
|
|
// Via API
|
|
|
|
const command = new SendEmailCommand({
|
|
|
|
ConfigurationSetName: 'Newsletter',
|
|
|
|
Content: {
|
|
|
|
Simple: {
|
|
|
|
Body: {
|
|
|
|
Text: {
|
|
|
|
Charset: 'utf-8',
|
|
|
|
Data: text,
|
|
|
|
},
|
|
|
|
Html: {
|
|
|
|
Charset: 'utf-8',
|
|
|
|
Data: body,
|
|
|
|
},
|
2023-04-02 10:34:07 +02:00
|
|
|
},
|
2024-01-01 15:55:49 +01:00
|
|
|
Subject: {
|
2023-04-02 10:34:07 +02:00
|
|
|
Charset: 'utf-8',
|
2025-04-01 16:15:20 +02:00
|
|
|
Data: i18n.en.title,
|
2023-04-02 10:34:07 +02:00
|
|
|
},
|
2024-04-01 16:57:35 +02:00
|
|
|
Headers: [
|
|
|
|
{
|
|
|
|
Name: 'List-Unsubscribe',
|
|
|
|
Value: unsubPost,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: 'List-Unsubscribe-Post',
|
|
|
|
Value: 'List-Unsubscribe=One-Click',
|
|
|
|
},
|
|
|
|
],
|
2023-04-02 10:34:07 +02:00
|
|
|
},
|
|
|
|
},
|
2024-01-01 15:55:49 +01:00
|
|
|
Destination: {
|
|
|
|
ToAddresses: [sub.email],
|
|
|
|
},
|
|
|
|
//FeedbackForwardingEmailAddress: us,
|
|
|
|
FromEmailAddress: us,
|
|
|
|
//FromEmailAddressIdentityArn: "arn:aws:ses:us-east-1:550348293871:identity/freesewing.org",
|
|
|
|
//ReplyToAddresses: us,
|
|
|
|
})
|
2024-01-02 12:31:31 +01:00
|
|
|
try {
|
|
|
|
await client.send(command)
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err)
|
|
|
|
return false
|
|
|
|
}
|
2023-04-02 10:34:07 +02:00
|
|
|
}
|
2024-01-01 15:55:49 +01:00
|
|
|
i++
|
2024-01-02 12:31:31 +01:00
|
|
|
l++
|
2022-06-27 17:15:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const sendTest = () => send(true)
|
|
|
|
const sendReal = () => send(false)
|
|
|
|
|
2023-01-09 20:46:32 +01:00
|
|
|
export { sendTest, sendReal }
|