Newer
Older
wizarr-notify / index.js
const express = require('express');
const Pushover = require('pushover-notifications');
const fs = require('node:fs');
const path = require('node:path');
const nodemailer = require('nodemailer');
const mustacheExpress = require('mustache-express');
const mustache = require('mustache');

const emailFrom = process.env.MAIL_FROM;
const emailSubject = process.env.MAIL_SUBJECT;

const notify_templates = getNotifyTemplates();
console.log("Listening for: " + Array.from(notify_templates.keys()).join(","));
  
const pushover = new Pushover(
    {
        user: process.env.PUSHOVER_USER,
        token: process.env.PUSHOVER_TOKEN,
    }
);

const testEmailData = {username:"Test User"};
function renderTemplateManual(template, data)
{
    return mustache.render(fs.readFileSync(`views/${template}.html`, {'encoding':'utf8'}), data)
}

async function sendEmail(from, to, subject, html)
{
    let transporter = nodemailer.createTransport({
        host: process.env.MAIL_HOST,
        port: 587, // Common port for secure SMTP (TLS)
        secure: false, // Use 'true' if using port 465 (SSL), 'false' for other ports like 587 (TLS)
        requireTLS: true,
        auth: {
            user: process.env.SMTP_USER,
            pass: process.env.SMTP_PASSWORD
        }
    });

    //await transporter.verify();

    await transporter.sendMail({from, to, subject, html});

    console.log(`Email sent to ${to}`);
}

const app = express();

// Register '.mustache' extension with The Mustache Express
app.engine('html', mustacheExpress());

app.set('view engine', 'html');
app.set('views', __dirname + '/views');

app.use(express.json());



app.get('/email.html', (req, res) => res.send(renderTemplateManual('email', testEmailData))); // res.render('email', testEmailData));
app.get('/email_test', async (req, res) =>
{
    await sendEmail(emailFrom, req.query.to, emailSubject, renderTemplateManual('email', testEmailData));
    res.send('sent');
});

app.post('/', async (req, res, next) =>
{
    console.log(req.body);
    
    if ('event' in req.body)
    {
        if (notify_templates.has(req.body.event))
        {        
            const template_output = JSON.parse(mustache.render(notify_templates.get(req.body.event), {
                ...req.body,
                user: `${req.body.username} (${req.body.email})`
            }));

            if ('title' in template_output && 'message' in template_output)
            {
                pushover.send({
                    title: template_output.title,
                    message: template_output.message
                });
            }

            if (req.body.event == 'user_joined')
            {
                try
                {
                    await sendEmail(emailFrom, req.body.email, emailSubject, renderTemplateManual('email', req.body));
                }
                catch (err)
                {
                    next(err);
                }
            }
        }
    }
    res.end();
})

app.use((_, res) => res.status(404).end());

app.listen(80);

function getNotifyTemplates()
{
    const templates = new Map();

    const templatesStat = fs.statSync("templates", {throwIfNoEntry: false});
    if (!templatesStat) fs.mkdirSync("templates");

    if (fs.readdirSync("templates").length == 0)
    {
        for (let filename of fs.readdirSync("default_templates", {encoding:'utf8'}))
        {
            fs.copyFileSync(path.join("default_templates", filename), path.join("templates", filename));
        }
    }

    for (let filename of fs.readdirSync("templates", {encoding:'utf8'}))
    {
        const is_comment = l => l.trim().startsWith("//");
        if (path.extname(filename) != '.json') continue;

        let lines = fs.readFileSync(path.join("templates", filename), {'encoding':'utf8'}).split(/[\r\n]/);
        if (lines.some(l => is_comment(l) && l.indexOf(":ignore") != -1)) continue;
        lines = lines.filter(l => !is_comment(l));

        templates.set(path.parse(filename).name, lines.join("\n"));
    }

    return templates;
}