From 2af94818377740dad047e1f3c85ce03a3e865ffe Mon Sep 17 00:00:00 2001 From: Stefano Marinelli Date: Fri, 12 Jan 2024 09:54:14 +0100 Subject: [PATCH 1/3] Added support for ntfy notifications. You can configure either a self-hosted server or use the official ntfy.sh, and you have the option to use a private token to protect access and topics. --- activitypub.c | 42 +++++++++++++++++++++++++++++++++++++++++- data.c | 15 +++++++++++++++ doc/snac.1 | 6 ++++++ html.c | 26 ++++++++++++++++++++++++++ snac.h | 1 + 5 files changed, 89 insertions(+), 1 deletion(-) diff --git a/activitypub.c b/activitypub.c index dc492ca..d937726 100644 --- a/activitypub.c +++ b/activitypub.c @@ -800,8 +800,23 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor, objid = actor; notify_add(snac, type, utype, actor, objid != NULL ? objid : id); -} + /* ntfy */ + char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); + char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); + + if (!xs_is_null(ntfy_server) && *ntfy_server ) + enqueue_ntfy(body, ntfy_server, ntfy_token); + + /* finally, store it in the notification folder */ + if (strcmp(type, "Follow") == 0) + objid = id; + else + if (strcmp(utype, "Follow") == 0) + objid = actor; + + notify_add(snac, type, utype, actor, objid != NULL ? objid : id); +} /** messages **/ @@ -2133,6 +2148,31 @@ void process_queue_item(xs_dict *q_item) srv_debug(0, xs_fmt("telegram post %d", status)); } else + if (strcmp(type, "ntfy") == 0) { + /* send this via ntfy */ + char *ntfy_server = xs_dict_get(q_item, "ntfy_server"); + char *msg = xs_dict_get(q_item, "message"); + char *ntfy_token = xs_dict_get(q_item, "ntfy_token"); + int status = 0; + + xs *url = xs_fmt("%s", ntfy_server); + //xs *body = xs_fmt("\"text\":\"%s\"}", msg); + xs *body = xs_fmt("%s", msg); + + xs *headers = xs_dict_new(); + headers = xs_dict_append(headers, "content-type", "text/plain"); + // Append the Authorization header only if ntfy_token is not NULL + if (ntfy_token != NULL) { + headers = xs_dict_append(headers, "Authorization", xs_fmt("Bearer %s", ntfy_token)); + } + + xs *rsp = xs_http_request("POST", url, headers, + body, strlen(body), &status, NULL, NULL, 0); + rsp = xs_free(rsp); + + srv_debug(0, xs_fmt("ntfy post %d", status)); + } + else if (strcmp(type, "purge") == 0) { srv_log(xs_dup("purge start")); diff --git a/data.c b/data.c index 5e2f43d..97cd94f 100644 --- a/data.c +++ b/data.c @@ -2232,6 +2232,21 @@ void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id) srv_debug(1, xs_fmt("enqueue_telegram %s %s", bot, chat_id)); } +void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token) +/* enqueues a message to be sent via ntfy */ +{ + xs *qmsg = _new_qmsg("ntfy", msg, 0); + char *ntid = xs_dict_get(qmsg, "ntid"); + xs *fn = xs_fmt("%s/queue/%s.json", srv_basedir, ntid); + + qmsg = xs_dict_append(qmsg, "ntfy_server", ntfy_server); + qmsg = xs_dict_append(qmsg, "ntfy_token", ntfy_token); + + + qmsg = _enqueue_put(fn, qmsg); + + srv_debug(1, xs_fmt("enqueue_ntfy %s %s", ntfy_server, ntfy_token)); +} void enqueue_message(snac *snac, const xs_dict *msg) /* enqueues an output message */ diff --git a/doc/snac.1 b/doc/snac.1 index 1f8d395..cb8462f 100644 --- a/doc/snac.1 +++ b/doc/snac.1 @@ -93,6 +93,12 @@ a Telegram channel and a bot for this; the process is rather cumbersome but it's documented everywhere. The Bot API key is a long string of alphanumeric characters and the chat id is a big, negative number. +.It ntfy notifications +To enable notifications via ntfy (both self-hosted or +standard ntfy.sh server), fill the two provided +fields (ntfy server/topic and, if protected, the token). +You need to refer to the https://ntfy.sh web site for +more information on this process. .It Maximum days to keep posts This numeric value specifies the number of days to pass before posts (yours and others') will be purged. This value overrides diff --git a/html.c b/html.c index 9dc42df..4abfa37 100644 --- a/html.c +++ b/html.c @@ -853,6 +853,14 @@ xs_html *html_top_controls(snac *snac) if (xs_is_null(telegram_chat_id)) telegram_chat_id = ""; + char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); + if (xs_is_null(ntfy_server)) + ntfy_server = ""; + + char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); + if (xs_is_null(ntfy_token)) + ntfy_token = ""; + char *purge_days = xs_dict_get(snac->config, "purge_days"); if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) purge_days = (char *)xs_number_str(purge_days); @@ -946,6 +954,20 @@ xs_html *html_top_controls(snac *snac) xs_html_attr("name", "telegram_chat_id"), xs_html_attr("value", telegram_chat_id), xs_html_attr("placeholder", "Chat id"))), + xs_html_tag("p", + xs_html_text(L("ntfy notifications (ntfy server and token):")), + xs_html_sctag("br", NULL), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", "ntfy_server"), + xs_html_attr("value", ntfy_server), + xs_html_attr("placeholder", "ntfy server - full URL (example: https://ntfy.sh/YourTopic)")), + xs_html_text(" "), + xs_html_sctag("input", + xs_html_attr("type", "text"), + xs_html_attr("name", "ntfy_token"), + xs_html_attr("value", ntfy_token), + xs_html_attr("placeholder", "ntfy token - if needed"))), xs_html_tag("p", xs_html_text(L("Maximum days to keep posts (0: server settings):")), xs_html_sctag("br", NULL), @@ -2890,6 +2912,10 @@ int html_post_handler(const xs_dict *req, const char *q_path, snac.config = xs_dict_set(snac.config, "telegram_bot", v); if ((v = xs_dict_get(p_vars, "telegram_chat_id")) != NULL) snac.config = xs_dict_set(snac.config, "telegram_chat_id", v); + if ((v = xs_dict_get(p_vars, "ntfy_server")) != NULL) + snac.config = xs_dict_set(snac.config, "ntfy_server", v); + if ((v = xs_dict_get(p_vars, "ntfy_token")) != NULL) + snac.config = xs_dict_set(snac.config, "ntfy_token", v); if ((v = xs_dict_get(p_vars, "purge_days")) != NULL) { xs *days = xs_number_new(atof(v)); snac.config = xs_dict_set(snac.config, "purge_days", days); diff --git a/snac.h b/snac.h index e960d0d..506257a 100644 --- a/snac.h +++ b/snac.h @@ -204,6 +204,7 @@ void enqueue_output(snac *snac, xs_dict *msg, xs_str *inbox, int retries, int p_ void enqueue_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries); void enqueue_email(xs_str *msg, int retries); void enqueue_telegram(const xs_str *msg, const char *bot, const char *chat_id); +void enqueue_ntfy(const xs_str *msg, const char *ntfy_server, const char *ntfy_token); void enqueue_message(snac *snac, const xs_dict *msg); void enqueue_close_question(snac *user, const char *id, int end_secs); void enqueue_request_replies(snac *user, const char *id); From 13a0560f7bec80d75a13f6bf3842d894351427a4 Mon Sep 17 00:00:00 2001 From: Stefano Marinelli Date: Fri, 12 Jan 2024 09:00:49 +0000 Subject: [PATCH 2/3] Updated RELEASE_NOTES.md to add ntfy.sh support --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1fd9f0d..6db186c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -8,6 +8,8 @@ New command-line option `state`, that dumps some information about the running s Mastodon API: added some fixes for integration with the Mona iOS app (contributed by jamesoff). +Added support for ntfy notifications, both using a self-hosted server or the official ntfy.sh. (contributed by Stefano Marinelli) + ## 2.44 Fixed a nasty bug that caused the in-memory output queue to be corrupted under heavy traffic loads. This is a good reason to upgrade (thanks to VĂ­ctor Moral and Stefano Marinelli for helping me in fixing this). From 82d57557bb9a16f18ed5acc874d283ef0e9f47f5 Mon Sep 17 00:00:00 2001 From: Stefano Marinelli Date: Fri, 12 Jan 2024 09:07:55 +0000 Subject: [PATCH 3/3] ntfy code cleanup --- activitypub.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/activitypub.c b/activitypub.c index d937726..566c240 100644 --- a/activitypub.c +++ b/activitypub.c @@ -805,7 +805,7 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor, char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); - if (!xs_is_null(ntfy_server) && *ntfy_server ) + if (!xs_is_null(ntfy_server) && *ntfy_server) enqueue_ntfy(body, ntfy_server, ntfy_token); /* finally, store it in the notification folder */ @@ -2156,7 +2156,6 @@ void process_queue_item(xs_dict *q_item) int status = 0; xs *url = xs_fmt("%s", ntfy_server); - //xs *body = xs_fmt("\"text\":\"%s\"}", msg); xs *body = xs_fmt("%s", msg); xs *headers = xs_dict_new();