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
This commit is contained in:
grunfink 2024-01-12 17:09:58 +00:00
commit a0cb5c60cd
6 changed files with 90 additions and 1 deletions

View file

@ -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).

View file

@ -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
data.c
View file

@ -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 */

View file

@ -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
html.c
View file

@ -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
snac.h
View file

@ -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);