commit e50a6ba

Oliver  ·  2025-08-23 09:04:11 +0000 UTC
parents eecfeec, 3611f42
Merge pull request 'master' (#11) from grunfink/snac2:master into master

Reviewed-on: https://codeberg.org/zen/snac2/pulls/11
6 files changed,  +128, -1
M data.c
M main.c
M snac.h
+2, -0
1@@ -12,6 +12,8 @@ Mastodon API: Fixed repeated entries in timelines.
2 
3 Added nodeinfo 2.1 support.
4 
5+Fixed boosts from the command line not showing in the public timeline (contributed by xvello).
6+
7 ## 2.81
8 
9 If the `propagate_local_purge` configuration variable is set to `true` in `server.json`, purged local posts generate a `Delete` activity that is sent everywhere, instead of only deleted from the filesystem.
+94, -0
  1@@ -936,6 +936,94 @@ xs_str *process_tags(snac *snac, const char *content, xs_list **tag)
  2 }
  3 
  4 
  5+void collect_replies(snac *user, const char *id)
  6+/* collects all replies for a post */
  7+{
  8+    xs *obj = NULL;
  9+
 10+    if (!valid_status(object_get(id, &obj))) {
 11+        snac_debug(user, 1, xs_fmt("collect_replies: object '%s' is not here", id));
 12+        return;
 13+    }
 14+
 15+    const char *next = xs_dict_get_path(obj, "replies.first.next");
 16+    if (!xs_is_string(next)) {
 17+        snac_debug(user, 1, xs_fmt("collect_replies: object '%s' does not have a replies.first.next URL", id));
 18+        return;
 19+    }
 20+
 21+    /* pick the first level replies (may be empty) */
 22+    const xs_list *level0_replies = xs_dict_get_path(obj, "replies.first.items");
 23+
 24+    xs *reply_obj = NULL;
 25+
 26+    if (!valid_status(object_get(next, &reply_obj))) {
 27+        if (!valid_status(activitypub_request(user, next, &reply_obj))) {
 28+            snac_debug(user, 1, xs_fmt("collect_replies: cannot get replies object '%s'", next));
 29+            return;
 30+        }
 31+    }
 32+
 33+    const xs_list *level1_replies = xs_dict_get(reply_obj, "items");
 34+    if (!xs_is_list(level1_replies)) {
 35+        snac_debug(user, 1, xs_fmt("collect_replies: cannot get reply items from object '%s'", next));
 36+        return;
 37+    }
 38+
 39+    xs *items = NULL;
 40+
 41+    if (xs_is_list(level0_replies))
 42+        items = xs_list_cat(xs_dup(level0_replies), level1_replies);
 43+    else
 44+        items = xs_dup(level1_replies);
 45+
 46+    const xs_val *v;
 47+
 48+    xs_list_foreach(items, v) {
 49+        xs *reply = NULL;
 50+
 51+        if (xs_is_string(v)) {
 52+            /* request the object */
 53+            if (!valid_status(object_get(v, &reply))) {
 54+                if (!valid_status(activitypub_request(user, v, &reply))) {
 55+                    snac_debug(user, 1, xs_fmt("collect_replies: error requesting object '%s'", v));
 56+                    continue;
 57+                }
 58+            }
 59+        }
 60+        else
 61+        if (xs_is_dict(v)) {
 62+            /* actually the post object */
 63+            reply = xs_dup(v);
 64+        }
 65+
 66+        if (!xs_is_dict(reply))
 67+            continue;
 68+
 69+        const char *id      = xs_dict_get(reply, "id");
 70+        const char *type    = xs_dict_get(reply, "type");
 71+        const char *attr_to = get_atto(reply);
 72+
 73+        if (!xs_is_string(id) || !xs_is_string(type) || !xs_is_string(attr_to))
 74+            continue;
 75+
 76+        if (!xs_match(type, POSTLIKE_OBJECT_TYPE))
 77+            continue;
 78+
 79+        if (timeline_here(user, id)) {
 80+            snac_debug(user, 1, xs_fmt("collect_replies: item already in timeline %s", id));
 81+            continue;
 82+        }
 83+
 84+        enqueue_actor_refresh(user, attr_to, 0);
 85+
 86+        timeline_add(user, id, reply);
 87+
 88+        snac_log(user, xs_fmt("new '%s' (collect_replies) %s %s", type, attr_to, id));
 89+    }
 90+}
 91+
 92+
 93 void notify(snac *snac, const char *type, const char *utype, const char *actor, const xs_dict *msg)
 94 /* notifies the user of relevant events */
 95 {
 96@@ -2826,6 +2914,12 @@ void process_user_queue_item(snac *user, xs_dict *q_item)
 97             }
 98         }
 99     }
100+    else
101+    if (strcmp(type, "collect_replies") == 0) {
102+        const char *post = xs_dict_get(q_item, "message");
103+
104+        collect_replies(user, post);
105+    }
106     else
107         snac_log(user, xs_fmt("unexpected user q_item type '%s'", type));
108 }
M data.c
+13, -0
 1@@ -3576,6 +3576,19 @@ void enqueue_notify_webhook(snac *user, const xs_dict *noti, int retries)
 2 }
 3 
 4 
 5+void enqueue_collect_replies(snac *user, const char *post)
 6+/* enqueues a collect replies request */
 7+{
 8+    xs *qmsg = _new_qmsg("collect_replies", post, 0);
 9+    const char *ntid = xs_dict_get(qmsg, "ntid");
10+    xs *fn = xs_fmt("%s/queue/%s.json", user->basedir, ntid);
11+
12+    qmsg = _enqueue_put(fn, qmsg);
13+
14+    snac_debug(user, 1, xs_fmt("enqueue_collect_replies %s", post));
15+}
16+
17+
18 int was_question_voted(snac *user, const char *id)
19 /* returns true if the user voted in this poll */
20 {
+7, -0
 1@@ -71,6 +71,10 @@ standalone one.
 2 If you set this checkbox, your text will not be sent when you
 3 push the Post button, but stored for later modification in
 4 the "Drafts" section.
 5+.It Language
 6+A dropdown to select which language your post will be written
 7+in. This control only appears after you configure the set of
 8+languages you expect to use in the User Settings (see below).
 9 .It Scheduled post...
10 This dropdown menu allows setting a date and time for the
11 post publication.
12@@ -188,6 +192,9 @@ can be selected here.
13 .It Time zone
14 The time zone the user is on (default: UTC). Only
15 used for scheduled posts.
16+.It Languages you usually post in
17+Fill this string with the ISO 639 Language Codes you expect to
18+write your posts in, space-separated (example: en fr pt_BR).
19 .It Password
20 Write the same string in these two fields to change your
21 password. Don't write anything if you don't want to do this.
M main.c
+9, -1
 1@@ -39,6 +39,7 @@ int usage(const char *cmd)
 2         "unfollow {basedir} {uid} {actor}     Unfollows an actor\n"
 3         "request {basedir} {uid} {url}        Requests an object\n"
 4         "insert {basedir} {uid} {url}         Requests an object and inserts it into the timeline\n"
 5+        "collect_replies {basedir} {uid} {url} Collects all replies from a post\n"
 6         "actor {basedir} [{uid}] {url}        Requests an actor\n"
 7         "note {basedir} {uid} {text} [files...] Sends a note with optional attachments\n"
 8         "note_unlisted {basedir} {uid} {text} [files...] Sends an unlisted note with optional attachments\n"
 9@@ -61,7 +62,7 @@ int usage(const char *cmd)
10         "verify_links {basedir} {uid}         Verifies a user's links (in the metadata)\n"
11         "search {basedir} {uid} {regex}       Searches posts by content\n"
12         "export_csv {basedir} {uid}           Exports followers, lists, MUTEd and bookmarks to CSV\n"
13-        "export_posts {basedir} {iod}         Exports all posts to outbox.json\n"
14+        "export_posts {basedir} {uid}         Exports all posts to outbox.json\n"
15         "alias {basedir} {uid} {account}      Sets account (@user@host or actor url) as an alias\n"
16         "migrate {basedir} {uid}              Migrates to the account defined as the alias\n"
17         "import_csv {basedir} {uid}           Imports data from CSV files\n"
18@@ -494,6 +495,7 @@ int main(int argc, char *argv[])
19 
20         if (msg != NULL) {
21             enqueue_message(&snac, msg);
22+            timeline_admire(&snac, xs_dict_get(msg, "object"), snac.actor, 0);
23 
24             if (dbglevel) {
25                 xs_json_dump(msg, 4, stdout);
26@@ -730,6 +732,12 @@ int main(int argc, char *argv[])
27         return 0;
28     }
29 
30+    if (strcmp(cmd, "collect_replies") == 0) { /** **/
31+        enqueue_collect_replies(&snac, url);
32+
33+        return 0;
34+    }
35+
36     if (strcmp(cmd, "insert") == 0) { /** **/
37         int status;
38         xs *data = NULL;
M snac.h
+3, -0
 1@@ -297,6 +297,7 @@ void enqueue_verify_links(snac *user);
 2 void enqueue_actor_refresh(snac *user, const char *actor, int forward_secs);
 3 void enqueue_webmention(const xs_dict *msg);
 4 void enqueue_notify_webhook(snac *user, const xs_dict *noti, int retries);
 5+void enqueue_collect_replies(snac *user, const char *post);
 6 
 7 int was_question_voted(snac *user, const char *id);
 8 
 9@@ -334,6 +335,8 @@ const char *default_avatar_base64(void);
10 
11 xs_str *process_tags(snac *snac, const char *content, xs_list **tag);
12 
13+void collect_replies(snac *user, const char *id);
14+
15 const char *get_atto(const xs_dict *msg);
16 const char *get_in_reply_to(const xs_dict *msg);
17 xs_list *get_attachments(const xs_dict *msg);