wip: Work on i18n support for newsletter
This commit is contained in:
parent
3cd8ecd392
commit
aff8714daa
5 changed files with 126 additions and 81 deletions
|
@ -15,3 +15,4 @@ all:
|
||||||
backend3.freesewing.org:
|
backend3.freesewing.org:
|
||||||
monorepo: /fs/monorepo
|
monorepo: /fs/monorepo
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
export const testers = [
|
export const testers = {
|
||||||
{
|
en: [
|
||||||
email: 'joost@joost.at',
|
{
|
||||||
ehash: 'asefsdfsdfsdlfsllehash',
|
email: 'joost@joost.at',
|
||||||
},
|
ehash: 'asefsdfsdfsdlfsllehash',
|
||||||
{
|
},
|
||||||
email: 'joost@decock.org',
|
],
|
||||||
ehash: 'zlasdadfsdlfsjjuehash',
|
nl: [
|
||||||
},
|
{
|
||||||
]
|
email: 'joost@decock.org',
|
||||||
|
ehash: 'zlasdadfsdlfsjjuehash',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<!-- Tell iOS not to automatically link certain text strings. -->
|
<!-- Tell iOS not to automatically link certain text strings. -->
|
||||||
<meta name="color-scheme" content="light" />
|
<meta name="color-scheme" content="light" />
|
||||||
<meta name="supported-color-schemes" content="light" />
|
<meta name="supported-color-schemes" content="light" />
|
||||||
<title>FreeSewing Newsletter</title>
|
<title>{{ title }}</title>
|
||||||
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
<!-- The title tag shows in email notifications, like Android 4.4. -->
|
||||||
|
|
||||||
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
<!-- What it does: Makes background images in 72ppi Outlook render at correct size. -->
|
||||||
|
@ -323,7 +323,7 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a href="https://freesewing.org/patrons/join"
|
<a href="https://freesewing.org/patrons/join"
|
||||||
><b>Support FreeSewing: Become a Patron</b></a
|
><b>{{ support }}</b></a
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -356,8 +356,7 @@
|
||||||
>
|
>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
<unsubscribe style="color: #ffffff; text-decoration: underline">
|
<unsubscribe style="color: #ffffff; text-decoration: underline">
|
||||||
You can <a href="{{{ unsubscribe }}}">unsubscribe</a> at any time. Or just reply and
|
<a href="{{{ unsubscribe }}}">{{ unsub1 }}</a>. {{ unsub2 }}
|
||||||
tell us you want out.
|
|
||||||
</unsubscribe>
|
</unsubscribe>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -103,6 +103,7 @@
|
||||||
"c8": "^8.0.0",
|
"c8": "^8.0.0",
|
||||||
"eslint-config-next": "^14.0.1",
|
"eslint-config-next": "^14.0.1",
|
||||||
"glob": "^10.3.10",
|
"glob": "^10.3.10",
|
||||||
|
"rehype-format": "^5.0.0",
|
||||||
"yarn": "^1.22.19"
|
"yarn": "^1.22.19"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.19"
|
"packageManager": "yarn@1.22.19"
|
||||||
|
|
|
@ -12,10 +12,50 @@ import { testers } from '../config/newsletter-testers.mjs'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2'
|
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2'
|
||||||
|
|
||||||
|
console.log({ edition: process.env.NL_EDITION })
|
||||||
// Current working directory
|
// Current working directory
|
||||||
const cwd = path.dirname(fileURLToPath(import.meta.url))
|
const cwd = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
const backend = 'https://backend.freesewing.org/'
|
const backend = 'https://backend3.freesewing.org/'
|
||||||
|
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
nl: {
|
||||||
|
title: 'FreeSewing nieuwsbrief',
|
||||||
|
support: 'Steun FreeSewing: Wordt mecenas',
|
||||||
|
unsub1: 'Je kan je op elk moment uitschrijven',
|
||||||
|
unsub2: 'Of stuur een reply en laat ons weten dat het niet meer hoeft',
|
||||||
|
},
|
||||||
|
fr: {
|
||||||
|
title: "Bulletin d'info FreeSewing",
|
||||||
|
support: 'Soutenir FreeSewing : Devenir mécène',
|
||||||
|
unsub1: 'Vous pouvez vous désabonner à tout moment',
|
||||||
|
unsub2: 'Ou répondez et dites-nous que vous voulez vous désabonner',
|
||||||
|
},
|
||||||
|
de: {
|
||||||
|
title: 'FreeSewing-Newsletter',
|
||||||
|
support: 'Unterstützen Sie FreeSewing: Werden Sie Gönner',
|
||||||
|
unsub1: 'Sie können sich jederzeit wieder abmelden',
|
||||||
|
unsub2: 'Oder antworten Sie uns und sagen Sie uns, dass Sie nicht mehr wollen',
|
||||||
|
},
|
||||||
|
de: {
|
||||||
|
title: 'FBoletín FreeSewing',
|
||||||
|
support: 'Apoya FreeSewing: Hazte mecenas',
|
||||||
|
unsub1: 'Puedes darte de baja en cualquier momento',
|
||||||
|
unsub2: 'O contesta y dinos que quieres darte de baja',
|
||||||
|
},
|
||||||
|
de: {
|
||||||
|
title: 'Інформаційний бюлетень FreeSewing',
|
||||||
|
support: 'Підтримайте FreeSewing: Стати меценатом',
|
||||||
|
unsub1: 'Ви можете відписатися в будь-який час',
|
||||||
|
unsub2: 'Або надішліть нам відповідь і скажіть, що хочете відмовитися',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const asHtml = async (text) => {
|
const asHtml = async (text) => {
|
||||||
const content = await unified()
|
const content = await unified()
|
||||||
|
@ -29,20 +69,13 @@ const asHtml = async (text) => {
|
||||||
return content.value
|
return content.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const getToken = async () => {
|
|
||||||
let res = await axios.post(`${backend}login`, {
|
|
||||||
username: process.env.FS_USER,
|
|
||||||
password: process.env.FS_PASSWORD,
|
|
||||||
})
|
|
||||||
if (res.data) return res.data.token
|
|
||||||
else if (res.err) return console.log(res.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSubscribers = async (test = true) => {
|
const getSubscribers = async (test = true) => {
|
||||||
if (test) return testers
|
if (test) return testers
|
||||||
let token = await getToken()
|
|
||||||
let res = await axios.get(`${backend}admin/subscribers`, {
|
let res = await axios.get(`${backend}admin/subscribers`, {
|
||||||
headers: { Authorization: 'Bearer ' + token },
|
auth: {
|
||||||
|
username: process.env.NL_API_KEY,
|
||||||
|
password: process.env.NL_API_SECRET,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if (res.data) return res.data
|
if (res.data) return res.data
|
||||||
else return false
|
else return false
|
||||||
|
@ -51,75 +84,82 @@ const getSubscribers = async (test = true) => {
|
||||||
const send = async (test = true) => {
|
const send = async (test = true) => {
|
||||||
const us = 'FreeSewing <info@freesewing.org>'
|
const us = 'FreeSewing <info@freesewing.org>'
|
||||||
const template = fs.readFileSync(`${cwd}/../config/templates/newsletter.html`, 'utf8')
|
const template = fs.readFileSync(`${cwd}/../config/templates/newsletter.html`, 'utf8')
|
||||||
let edition
|
|
||||||
try {
|
|
||||||
edition = await axios.get(
|
|
||||||
`https://posts.freesewing.org/newsletters?slug_eq=${process.env.NL_EDITION}`,
|
|
||||||
'utf8'
|
|
||||||
)
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
process.exit()
|
|
||||||
}
|
|
||||||
const text = edition.data[0].body
|
|
||||||
|
|
||||||
const subscribers = await getSubscribers(test)
|
const subscribers = await getSubscribers(test)
|
||||||
const content = await asHtml(text)
|
|
||||||
const inject = { content }
|
|
||||||
|
|
||||||
// Oh AWS your APIs are such a clusterfuck
|
// Oh AWS your APIs are such a clusterfuck
|
||||||
const client = new SESv2Client({ region: 'us-east-1' })
|
const client = new SESv2Client({ region: 'us-east-1' })
|
||||||
|
|
||||||
let i = 1
|
let i = 1
|
||||||
subscribers.sort()
|
let l = 1
|
||||||
let subs = subscribers.length
|
for (const lang in subscribers) {
|
||||||
for (let sub of subscribers) {
|
let edition
|
||||||
// If your SMTP relay start rate-limiting midway through
|
try {
|
||||||
// you can use this if loop to start just after the last
|
edition = await axios.get(
|
||||||
// successful delivery
|
`https://raw.githubusercontent.com/freesewing/freesewing/develop/markdown/org/newsletter/${process.env.NL_EDITION}/${lang}.md`,
|
||||||
if (i > 0) {
|
'utf8'
|
||||||
let unsub = `${backend}newsletter/unsubscribe/${sub.ehash}`
|
)
|
||||||
inject.unsubscribe = unsub
|
} catch (err) {
|
||||||
let body = mustache.render(template, inject)
|
console.log(err)
|
||||||
console.log(`${i}/${subs} Sending to ${sub.email}`)
|
process.exit()
|
||||||
|
}
|
||||||
|
console.log(edition.data)
|
||||||
|
const text = edition.data[0].body
|
||||||
|
const content = await asHtml(text)
|
||||||
|
|
||||||
// Via API
|
console.log(content)
|
||||||
const command = new SendEmailCommand({
|
|
||||||
ConfigurationSetName: 'Newsletter',
|
process.exit()
|
||||||
Content: {
|
|
||||||
Simple: {
|
subscribers[lang].sort()
|
||||||
Body: {
|
let subs = subscribers[lang].length
|
||||||
Text: {
|
for (let sub of subscribers[lang]) {
|
||||||
Charset: 'utf-8',
|
if (i > 0) {
|
||||||
Data: text,
|
let unsub = `${backend}newsletter/unsubscribe/${sub.ehash}`
|
||||||
|
inject.unsubscribe = unsub
|
||||||
|
let body = mustache.render(template, {
|
||||||
|
...i18n[lang],
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
console.log(`[${lang}] ${i}/${subs} Sending to ${sub.email}`)
|
||||||
|
|
||||||
|
// Via API
|
||||||
|
const command = new SendEmailCommand({
|
||||||
|
ConfigurationSetName: 'Newsletter',
|
||||||
|
Content: {
|
||||||
|
Simple: {
|
||||||
|
Body: {
|
||||||
|
Text: {
|
||||||
|
Charset: 'utf-8',
|
||||||
|
Data: text,
|
||||||
|
},
|
||||||
|
Html: {
|
||||||
|
Charset: 'utf-8',
|
||||||
|
Data: body,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Html: {
|
Subject: {
|
||||||
Charset: 'utf-8',
|
Charset: 'utf-8',
|
||||||
Data: body,
|
Data: 'FreeSewing newsletter: Autumn 2023',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Subject: {
|
|
||||||
Charset: 'utf-8',
|
|
||||||
Data: 'FreeSewing newsletter: Autumn 2023',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
Destination: {
|
||||||
Destination: {
|
ToAddresses: [sub.email],
|
||||||
ToAddresses: [sub.email],
|
},
|
||||||
},
|
//FeedbackForwardingEmailAddress: us,
|
||||||
//FeedbackForwardingEmailAddress: us,
|
FromEmailAddress: us,
|
||||||
FromEmailAddress: us,
|
//FromEmailAddressIdentityArn: "arn:aws:ses:us-east-1:550348293871:identity/freesewing.org",
|
||||||
//FromEmailAddressIdentityArn: "arn:aws:ses:us-east-1:550348293871:identity/freesewing.org",
|
//ReplyToAddresses: us,
|
||||||
//ReplyToAddresses: us,
|
})
|
||||||
})
|
//try {
|
||||||
try {
|
// await client.send(command)
|
||||||
await client.send(command)
|
//} catch (err) {
|
||||||
} catch (err) {
|
// console.log(err)
|
||||||
console.log(err)
|
// return false
|
||||||
return false
|
//}
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
i++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue