Notification and E-Mail service for the jointhefreeworld platform
  • Scheme 86.8%
  • Emacs Lisp 7.5%
  • Makefile 5.7%
Josep Bigorra 5433ea77e2
All checks were successful
ci/woodpecker/push/byggsteg Pipeline was successful
📦 ci: Improvements for Woodpecker pipeline
2025-08-10 13:55:40 +02:00
.woodpecker 📦 ci: Improvements for Woodpecker pipeline 2025-08-10 13:55:40 +02:00
app ­ 2025-06-03 20:36:13 +02:00
conf ­ 2025-05-20 22:11:30 +02:00
db/sm ­ 2025-04-28 11:09:00 +02:00
lib/pingwing ­ 2025-06-03 20:41:05 +02:00
log ­ 2025-04-28 11:09:00 +02:00
prv ­ 2025-04-28 11:09:00 +02:00
pub/img ­ 2025-04-28 11:09:00 +02:00
sys ­ 2025-04-28 11:09:00 +02:00
test ­ 2025-04-30 10:49:07 +02:00
.envrc ­ 2025-04-28 11:09:00 +02:00
.gitignore ­ 2025-04-28 16:42:50 +02:00
COPYING ­ 2025-05-20 22:10:34 +02:00
ENTRY ­ 2025-04-29 11:53:50 +02:00
Makefile ­ 2025-06-03 20:37:00 +02:00
manifest.scm use libcurl­ 2025-05-02 13:05:56 +02:00
README.org ­ 2025-06-04 18:11:27 +02:00

PingWing

https://jointhefreeworld.org/ggg/dist/scheme-guile.svg https://jointhefreeworld.org/ggg/dist/gnu-guix.svg https://jointhefreeworld.org/ggg/dist/gnu-make.svg https://jointhefreeworld.org/ggg/dist/license-agpl3+.svg https://jointhefreeworld.org/ggg/dist/license-fdl13+.svg https://jointhefreeworld.org/ggg/dist/gnu-artanis.svg

The free and open Lisp-powered notification nexus

If you like my work, please support me by buying me a cup of coffee so I can continue with a lot of motivation.


Do you want to work the complexity away of sending e-mail, Slack or other notifications, from every other program? Specially if you use a (micro)service oriented architecture?

pingwing, a key component of the jointhefreeworld ecosystem, emerges as a robust and extensible solution. Architected in the elegant and powerful dialect of Lisp known as Guile Scheme, this tool gives you power (via REST API and more) to become the central notification system for your platform.

At its core, pingwing exposes a programmatic interface (and more!) allowing you to dispatch messages, electronic mail, and critical alerts with finesse. Forget juggling disparate notification mechanisms; pingwing harmonizes these streams, routing them to your chosen endpoints.

Initial support includes SMTP for email delivery, with a pending integration for Slack (expect webhook wizardry soon!).

The architecture is designed for future expansion, promising connectivity to a diverse range of notification sinks.

This project is powered by Lisp (Guile Scheme), curl , libcurl, make , SXML and the GNU Artanis web framework, SQLite, among others.

https://jointhefreeworld.org/static-assets/pingwing/logo-small.png

This tool is compatible with any SMTP provider you can think of, thanks to its simple and agnostic approach.


Running

You will need to specify some settings for pingwing to be able to start in env.scm at the root of the project. Here follows an example for AWS SES email (via SMTP).

(define pingwing-mail-access-key "AK******")
(define pingwing-mail-access-secret "*********")
(define pingwing-mail-access-server "email-smtp.eu-west-3.amazonaws.com")
(define pingwing-mail-access-port 587)

Usage

Fetch tasks

Retrieve pending tasks from the database.

curl -v 'http://localhost:50077/api/v1/tasks'

Submit tasks

All you need to do to interact with pingwing is call the /api/v1/tasks with a POST method and give your preferences. See app/api/v1.scm for more details on the API, and lib/pingwing/tasks.scm for more.

curl -v \
     -H "Content-Type: application/json" \
     -d '{
         "task-type": "send-email",
         "template": "password-reset",
         "template-vars": {
           "system-name": "WikiMusic",
           "user": "jjbigorra@gmail.com",
           "reset-link": "https://gnu.org"
         },
         "sender-name": "No Reply - WikiMusic",
         "sender-address": "noreply@wikimusic.jointhefreeworld.org",
         "subject": "Wikimusic - Password Reset",
         "recipients": [
           {"name": "Josep Bigorra", "address": "jjbigorra@gmail.com"},
           {"name": "Another Person", "address": "jjbigorra+1@gmail.com"}
         ]
       }' \
     'http://localhost:50077/api/v1/tasks'

You can submit tasks at super high rates to pingwing since the ingestion and processing are done completely separately. This ensures that we can do a reliable retry mechanism and can handle high volumes of data.


What is a task?

A task can be defined as:

task-type an enum, can be send-email

template an enum, can be password-reset or system-alert See lib/pingwing/templates/ for more.

template-vars an object, where keys are variable names and values are their respective values, used for template compilation/interpretation.

sender-name string, the name to show as message sender

sender-address string, the address to show as message sender

subject string, the message subject, a UUID will be auto-appended to it.

recipients list of recipient objects

recipient object: object containing the recipient name and address.

TODO: user-specified template in HTTP req (send Scheme code over the wire) (unimplemented)

TODO : send-slack as task-type (unimplemented)


Licensing

pingwing and all of its source code are free software, licensed under the GNU Affero General Public License v3 (or newer at your convenience).

https://www.gnu.org/licenses/agpl-3.0.html

The documentation and examples, including this document, which are provided with pingwing, are all licensed under the GNU Free Documentation License v1.3 (or newer at your convenience).

https://www.gnu.org/licenses/fdl-1.3.html


Code of conduct

This project adheres to the jointhefreeworld code of conduct. Find it here:

https://jointhefreeworld.org/blog/articles/personal/jointhefreeworld-code-of-conduct/index.html

In summary, we foster an inclusive, respectful, and cooperative environment for all contributors and users of this free software project. Inspired by the ideals of the GNU Project, we strive to uphold freedom, equality, and community as guiding principles. We believe that collaboration in a community of mutual respect is essential to creating excellent free software.


PingWing Project

Contributing to free software is a uniquely beautiful act because it embodies principles of generosity, collaboration, and empowerment.

We welcome everyone to feel invited to the pingwing Project, and encourage active contribution in all forms, to improve it and/or suggest improvements, brainstorm with me, make it more modular/flexible, etc, feel free to contact me <jjbigorra@gmail.com> to chat, discuss or report feedback.

Find here the Backlog and Kanban boards for pingwing: https://lucidplan.jointhefreeworld.org/tickets/pingwing


Useful tips

It's recommended to run your SQLite database in WAL mode, for concurrent reads and writes. See the Makefile target init-db


Details on how tasks are sent via e-mail

After the task has submitted and picked up by the worker, a message like this will be produced:

Produce mail message

MIME-Version: 1.0
Subject: WikiMusic - Password Reset 998c42eb-7472-4e12-aa5a-ffdbe754b430
From: No Reply - WikiMusic <noreply@wikimusic.jointhefreeworld.org>
To: Josep Bigorra <jjbigorra@gmail.com>
Content-Type: multipart/alternative; boundary="pingwing-message-multipart-boundary-33c2b557-7f27-4339-9a85-2385e7ecde9b"

--pingwing-message-multipart-boundary-33c2b557-7f27-4339-9a85-2385e7ecde9b
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

Reset your password: 
We have received a request to reset the password for your user account:
User.............

--pingwing-message-multipart-boundary-33c2b557-7f27-4339-9a85-2385e7ecde9b
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" />..............

--pingwing-message-multipart-boundary-33c2b557-7f27-4339-9a85-2385e7ecde9b--

Send mail message via SMTP with curl (libcurl)

And the worker will pick it up and send it (programatically for you) with libcurl:

>>= sending mail to email-smtp.eu-west-3.amazonaws.com

% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Host email-smtp.eu-west-3.amazonaws.com:587 was resolved.
 IPv6: (none)
 IPv4: 15.236.217.177, 15.236.68.209, 15.237.2.166
   Trying 15.236.217.177:587...
 Connected to email-smtp.eu-west-3.amazonaws.com (15.236.217.177) port 587
< 220 email-smtp.amazonaws.com ESMTP SimpleEmailService-d-I
> EHLO 998c42eb-7472-4e12-aa5a-ffdbe754b430-noreply@wikimusic.jointhefreeworld.org-jjbigorra@gmail.com
< 250-email-smtp.amazonaws.com
< 250-8BITMIME
< 250-STARTTLS
< 250-AUTH PLAIN LOGIN
< 250 Ok
> STARTTLS
< 220 Ready to start TLS
.........................
< 235 Authentication successful.
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0> MAIL FROM:<noreply@wikimusic.jointhefreeworld.org>
< 250 Ok
> RCPT TO:<jjbigorra@gmail.com>
< 250 Ok
> DATA
< 354 End data with <CR><LF>.<CR><LF>q
} [3028 bytes data]
 We are completely uploaded and fine
< 250 Ok 011301968c239ad5-72018cb8-ab51-42dd-8ea6-fd6395124272-000000
100  3028    0     0  100  3028      0   5653 --:--:-- --:--:-- --:--:--  5649
 Connection #0 to host email-smtp.eu-west-3.amazonaws.com left intact