Răsfoiți Sursa

Merge pull request 'Added support for ntfy notifications with enhanced privacy when utilizing a self-hosted server, eliminating the need for external services.' (#102) from draga79/snac2:master into master

Reviewed-on: https://codeberg.org/grunfink/snac2/pulls/102
grunfink 2 ani în urmă
părinte
comite
a0cb5c60cd
6 a modificat fișierele cu 90 adăugiri și 1 ștergeri
  1. 2 0
      RELEASE_NOTES.md
  2. 40 1
      activitypub.c
  3. 15 0
      data.c
  4. 6 0
      doc/snac.1
  5. 26 0
      html.c
  6. 1 0
      snac.h

+ 2 - 0
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).
 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
 ## 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).
 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).

+ 40 - 1
activitypub.c

@@ -800,8 +800,23 @@ void notify(snac *snac, const char *type, const char *utype, const char *actor,
         objid = actor;
         objid = actor;
 
 
     notify_add(snac, type, utype, actor, objid != NULL ? objid : id);
     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 **/
 /** messages **/
 
 
@@ -2133,6 +2148,30 @@ void process_queue_item(xs_dict *q_item)
         srv_debug(0, xs_fmt("telegram post %d", status));
         srv_debug(0, xs_fmt("telegram post %d", status));
     }
     }
     else
     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("%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) {
     if (strcmp(type, "purge") == 0) {
         srv_log(xs_dup("purge start"));
         srv_log(xs_dup("purge start"));
 
 

+ 15 - 0
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));
     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)
 void enqueue_message(snac *snac, const xs_dict *msg)
 /* enqueues an output message */
 /* enqueues an output message */

+ 6 - 0
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
 cumbersome but it's documented everywhere. The Bot API key
 is a long string of alphanumeric characters and the chat id
 is a long string of alphanumeric characters and the chat id
 is a big, negative number.
 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
 .It Maximum days to keep posts
 This numeric value specifies the number of days to pass before
 This numeric value specifies the number of days to pass before
 posts (yours and others') will be purged. This value overrides
 posts (yours and others') will be purged. This value overrides

+ 26 - 0
html.c

@@ -853,6 +853,14 @@ xs_html *html_top_controls(snac *snac)
     if (xs_is_null(telegram_chat_id))
     if (xs_is_null(telegram_chat_id))
         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");
     char *purge_days = xs_dict_get(snac->config, "purge_days");
     if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER)
     if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER)
         purge_days = (char *)xs_number_str(purge_days);
         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("name", "telegram_chat_id"),
                         xs_html_attr("value", telegram_chat_id),
                         xs_html_attr("value", telegram_chat_id),
                         xs_html_attr("placeholder", "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_tag("p",
                     xs_html_text(L("Maximum days to keep posts (0: server settings):")),
                     xs_html_text(L("Maximum days to keep posts (0: server settings):")),
                     xs_html_sctag("br", NULL),
                     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);
             snac.config = xs_dict_set(snac.config, "telegram_bot", v);
         if ((v = xs_dict_get(p_vars, "telegram_chat_id")) != NULL)
         if ((v = xs_dict_get(p_vars, "telegram_chat_id")) != NULL)
             snac.config = xs_dict_set(snac.config, "telegram_chat_id", v);
             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) {
         if ((v = xs_dict_get(p_vars, "purge_days")) != NULL) {
             xs *days    = xs_number_new(atof(v));
             xs *days    = xs_number_new(atof(v));
             snac.config = xs_dict_set(snac.config, "purge_days", days);
             snac.config = xs_dict_set(snac.config, "purge_days", days);

+ 1 - 0
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_output_by_actor(snac *snac, xs_dict *msg, const xs_str *actor, int retries);
 void enqueue_email(xs_str *msg, 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_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_message(snac *snac, const xs_dict *msg);
 void enqueue_close_question(snac *user, const char *id, int end_secs);
 void enqueue_close_question(snac *user, const char *id, int end_secs);
 void enqueue_request_replies(snac *user, const char *id);
 void enqueue_request_replies(snac *user, const char *id);