summaryrefslogtreecommitdiff
path: root/mastoapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'mastoapi.c')
-rw-r--r--mastoapi.c342
1 files changed, 270 insertions, 72 deletions
diff --git a/mastoapi.c b/mastoapi.c
index 94912f1..dd80abc 100644
--- a/mastoapi.c
+++ b/mastoapi.c
@@ -1,5 +1,5 @@
1/* snac - A simple, minimalistic ActivityPub instance */ 1/* snac - A simple, minimalistic ActivityPub instance */
2/* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ 2/* copyright (c) 2022 - 2026 grunfink et al. / MIT license */
3 3
4#ifndef NO_MASTODON_API 4#ifndef NO_MASTODON_API
5 5
@@ -1158,6 +1158,103 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
1158 st = xs_dict_append(st, "tags", htl); 1158 st = xs_dict_append(st, "tags", htl);
1159 st = xs_dict_append(st, "emojis", eml); 1159 st = xs_dict_append(st, "emojis", eml);
1160 } 1160 }
1161 {
1162 xs *rl = object_get_emoji_reacts(id);
1163 xs *frl = xs_list_new(); /* final */
1164 xs *sfrl = xs_dict_new(); /* seen */
1165 int c = 0;
1166 const char *v;
1167
1168 while (xs_list_next(rl, &v, &c)) {
1169 xs *msg = NULL;
1170 if (valid_status(object_get_by_md5(v, &msg))) {
1171 const char *content = xs_dict_get(msg, "content");
1172 const char *actor = xs_dict_get(msg, "actor");
1173 const xs_list *contentl = xs_dict_get(sfrl, content);
1174
1175 if ((snac && is_muted(snac, actor)) || is_instance_blocked(actor))
1176 continue;
1177
1178 /* NOTE: idk when there are no actor, but i encountered that bug.
1179 * Probably because of one of my previous attempts.
1180 * Keeping this just in case, can remove later */
1181 const char *me = actor && snac && strcmp(actor, snac->actor) == 0 ?
1182 xs_stock(XSTYPE_TRUE) : xs_stock(XSTYPE_FALSE);
1183 int count = 1;
1184
1185 if (contentl) {
1186 count = atoi(xs_list_get(contentl, 0)) + 1;
1187 if (strncmp(xs_list_get(contentl, 1), xs_stock(XSTYPE_TRUE), 1) == 0)
1188 me = xs_stock(XSTYPE_TRUE);
1189 }
1190
1191 xs *fl = xs_list_new();
1192 xs *c1 = xs_fmt("%d", count);
1193 fl = xs_list_append(fl, c1, me);
1194 sfrl = xs_dict_append(sfrl, content, fl);
1195 }
1196 }
1197
1198 c = 0;
1199
1200 while (xs_list_next(rl, &v, &c)) {
1201 xs *msg = NULL;
1202 if (valid_status(object_get_by_md5(v, &msg))) {
1203 xs *d1 = xs_dict_new();
1204
1205 const xs_dict *icon = xs_dict_get(xs_list_get(xs_dict_get(msg, "tag"), 0), "icon");
1206 const char *o_url = xs_dict_get(icon, "url");
1207 const char *name = xs_dict_get(msg, "content");
1208 const char *actor = xs_dict_get(msg, "actor");
1209
1210 xs *nm = xs_dup(name);
1211 xs *url = NULL;
1212
1213 if (!xs_is_null(o_url)) {
1214 if (actor && snac && !strcmp(actor, snac->actor))
1215 url = make_url(o_url, NULL, 1);
1216 else
1217 url = xs_dup(o_url);
1218 }
1219
1220 xs *accounts = xs_list_new();
1221 if (actor) {
1222 xs *d2 = xs_dict_new();
1223 object_get(actor, &d2);
1224 xs *e_acct = mastoapi_account(snac, d2);
1225 accounts = xs_list_append(accounts, e_acct);
1226 }
1227
1228 const xs_list *item = xs_dict_get(sfrl, nm);
1229 const xs_str *nb = xs_list_get(item, 0);
1230 const xs_val *me = xs_list_get(item, 1);
1231 if (item == NULL)
1232 continue;
1233
1234 if (nm && strcmp(nm, "")) {
1235 if (url && strcmp(url, "")) {
1236 d1 = xs_dict_append(d1, "name", nm);
1237 d1 = xs_dict_append(d1, "shortcode", nm);
1238 d1 = xs_dict_append(d1, "accounts", accounts);
1239 d1 = xs_dict_append(d1, "me", me);
1240 d1 = xs_dict_append(d1, "url", url);
1241 d1 = xs_dict_append(d1, "static_url", url);
1242 d1 = xs_dict_append(d1, "visible_in_picker", xs_stock(XSTYPE_TRUE));
1243 d1 = xs_dict_append(d1, "count", nb);
1244 } else {
1245 d1 = xs_dict_append(d1, "name", nm);
1246 d1 = xs_dict_append(d1, "count", nb);
1247 d1 = xs_dict_append(d1, "me", me);
1248 d1 = xs_dict_append(d1, "visible_in_picker", xs_stock(XSTYPE_TRUE));
1249 }
1250 sfrl = xs_dict_del(sfrl, nm);
1251 frl = xs_list_append(frl, d1);
1252 }
1253 }
1254 }
1255
1256 st = xs_dict_append(st, "reactions", frl);
1257 }
1161 1258
1162 xs_free(idx); 1259 xs_free(idx);
1163 xs_free(ixc); 1260 xs_free(ixc);
@@ -1712,6 +1809,37 @@ xs_list *mastoapi_account_lists(snac *user, const char *uid)
1712} 1809}
1713 1810
1714 1811
1812xs_list *build_childrens(const xs_dict *msg, snac *snac1) {
1813 xs_list *ret = xs_list_new();
1814 xs *children = object_children(xs_dict_get(msg, "id"));
1815 char *p = children;
1816 const xs_str *v;
1817
1818 while (xs_list_iter(&p, &v)) {
1819 xs *m2 = NULL;
1820
1821 if (valid_status(timeline_get_by_md5(snac1, v, &m2))) {
1822 if (xs_is_null(xs_dict_get(m2, "name"))) {
1823 xs *st = mastoapi_status(snac1, m2);
1824
1825 if (st) {
1826 /* childrens children */
1827 xs *childs = build_childrens(m2, snac1);
1828 ret = xs_list_append(ret, st);
1829 if (xs_list_len(childs)) {
1830 char *p2 = childs;
1831 while (xs_list_iter(&p2, &v))
1832 ret = xs_list_append(ret, v);
1833
1834 }
1835 }
1836 }
1837 }
1838 }
1839 return ret;
1840}
1841
1842
1715int mastoapi_get_handler(const xs_dict *req, const char *q_path, 1843int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1716 char **body, int *b_size, char **ctype, xs_str **link) 1844 char **body, int *b_size, char **ctype, xs_str **link)
1717{ 1845{
@@ -1892,60 +2020,64 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
1892 } 2020 }
1893 else 2021 else
1894 if (strcmp(opt, "statuses") == 0) { /** **/ 2022 if (strcmp(opt, "statuses") == 0) { /** **/
1895 /* the public list of posts of a user */ 2023 if (logged_in || xs_type(xs_dict_get(snac2.config, "private")) == XSTYPE_FALSE) {
1896 const char *limit_s = xs_dict_get(args, "limit"); 2024 /* the public list of posts of a user */
1897 const char *o_max_id = xs_dict_get(args, "max_id"); 2025 const char *limit_s = xs_dict_get(args, "limit");
1898 int limit = limit_s ? atoi(limit_s) : 20; 2026 const char *o_max_id = xs_dict_get(args, "max_id");
1899 xs *max_id = o_max_id ? xs_tolower_i(xs_dup(o_max_id)) : NULL; 2027 int limit = limit_s ? atoi(limit_s) : 20;
2028 xs *max_id = o_max_id ? xs_tolower_i(xs_dup(o_max_id)) : NULL;
1900 2029
1901 srv_debug(1, xs_fmt("account statuses: max_id=%s limit=%d", max_id ? max_id : "(null)", limit)); 2030 srv_debug(1, xs_fmt("account statuses: max_id=%s limit=%d", max_id ? max_id : "(null)", limit));
1902 2031
1903 xs *timeline = timeline_simple_list(&snac2, "public", 0, 256, NULL); 2032 xs *timeline = timeline_simple_list(&snac2, "public", 0, 256, NULL);
1904 xs_list *p = timeline; 2033 xs_list *p = timeline;
1905 const xs_str *v; 2034 const xs_str *v;
1906 xs_set seen; 2035 xs_set seen;
1907 int cnt = 0; 2036 int cnt = 0;
1908 int skip_until_max = max_id != NULL; 2037 int skip_until_max = max_id != NULL;
1909 2038
1910 out = xs_list_new(); 2039 out = xs_list_new();
1911 xs_set_init(&seen); 2040 xs_set_init(&seen);
1912 2041
1913 while (xs_list_iter(&p, &v) && cnt < limit) { 2042 while (xs_list_iter(&p, &v) && cnt < limit) {
1914 xs *msg = NULL; 2043 xs *msg = NULL;
1915 2044
1916 if (valid_status(timeline_get_by_md5(&snac2, v, &msg))) { 2045 if (valid_status(timeline_get_by_md5(&snac2, v, &msg))) {
1917 const char *msg_id = xs_dict_get(msg, "id"); 2046 const char *msg_id = xs_dict_get(msg, "id");
1918 2047
1919 /* add only posts by the author */ 2048 /* add only posts by the author */
1920 if (!xs_is_null(msg_id) && 2049 if (!xs_is_null(msg_id) &&
1921 strcmp(xs_dict_get(msg, "type"), "Note") == 0 && 2050 strcmp(xs_dict_get(msg, "type"), "Note") == 0 &&
1922 xs_startswith(xs_dict_get(msg, "id"), snac2.actor) && is_msg_public(msg)) { 2051 is_msg_mine(&snac2, xs_dict_get(msg, "id")) && is_msg_public(msg)) {
1923 2052
1924 /* if max_id is set, skip entries until we find it */ 2053 /* if max_id is set, skip entries until we find it */
1925 if (skip_until_max) { 2054 if (skip_until_max) {
1926 xs *mid = mastoapi_id(msg); 2055 xs *mid = mastoapi_id(msg);
1927 if (strcmp(mid, max_id) == 0) { 2056 if (strcmp(mid, max_id) == 0) {
1928 skip_until_max = 0; 2057 skip_until_max = 0;
1929 srv_debug(2, xs_fmt("account statuses: found max_id, starting from next post")); 2058 srv_debug(2, xs_fmt("account statuses: found max_id, starting from next post"));
2059 }
2060 continue;
1930 } 2061 }
1931 continue;
1932 }
1933 2062
1934 /* deduplicate by message id */ 2063 /* deduplicate by message id */
1935 if (xs_set_add(&seen, msg_id) == 1) { 2064 if (xs_set_add(&seen, msg_id) == 1) {
1936 xs *st = mastoapi_status(&snac2, msg); 2065 xs *st = mastoapi_status(&snac2, msg);
1937 2066
1938 if (st) { 2067 if (st) {
1939 out = xs_list_append(out, st); 2068 out = xs_list_append(out, st);
1940 cnt++; 2069 cnt++;
2070 }
1941 } 2071 }
1942 } 2072 }
1943 } 2073 }
1944 } 2074 }
1945 }
1946 2075
1947 srv_debug(1, xs_fmt("account statuses: returning %d posts (requested %d)", cnt, limit)); 2076 srv_debug(1, xs_fmt("account statuses: returning %d posts (requested %d)", cnt, limit));
1948 xs_set_free(&seen); 2077 xs_set_free(&seen);
2078 }
2079 else
2080 status = HTTP_STATUS_UNAUTHORIZED;
1949 } 2081 }
1950 else 2082 else
1951 if (strcmp(opt, "featured_tags") == 0) { 2083 if (strcmp(opt, "featured_tags") == 0) {
@@ -2202,15 +2334,24 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2202 if (noti == NULL) 2334 if (noti == NULL)
2203 continue; 2335 continue;
2204 2336
2337 const xs_dict *tag = xs_list_get(xs_dict_get_path(noti, "msg.tag"), 0);
2338
2205 const char *type = xs_dict_get(noti, "type"); 2339 const char *type = xs_dict_get(noti, "type");
2206 const char *utype = xs_dict_get(noti, "utype"); 2340 const char *utype = xs_dict_get(noti, "utype");
2207 const char *objid = xs_dict_get(noti, "objid"); 2341 const char *objid = xs_dict_get(noti, "objid");
2208 const char *id = xs_dict_get(noti, "id"); 2342 const char *id = xs_dict_get(noti, "id");
2209 const char *actid = xs_dict_get(noti, "actor"); 2343 const char *actid = xs_dict_get(noti, "actor");
2344
2345 int isEmoji = 0;
2346
2210 xs *fid = xs_replace(id, ".", ""); 2347 xs *fid = xs_replace(id, ".", "");
2211 xs *actor = NULL; 2348 xs *actor = NULL;
2212 xs *entry = NULL; 2349 xs *entry = NULL;
2213 2350
2351 if (tag) {
2352 isEmoji = strcmp(xs_dict_get(tag, "type"), "Emoji") ? 0 : 1;
2353 }
2354
2214 if (!valid_status(actor_get(actid, &actor))) 2355 if (!valid_status(actor_get(actid, &actor)))
2215 continue; 2356 continue;
2216 2357
@@ -2234,9 +2375,12 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2234 } 2375 }
2235 2376
2236 /* convert the type */ 2377 /* convert the type */
2237 if (strcmp(type, "Like") == 0 || strcmp(type, "EmojiReact") == 0) 2378 if (strcmp(type, "Like") == 0 && !isEmoji)
2238 type = "favourite"; 2379 type = "favourite";
2239 else 2380 else
2381 if (isEmoji || strcmp(type, "EmojiReact") == 0)
2382 type = "reaction";
2383 else
2240 if (strcmp(type, "Announce") == 0) 2384 if (strcmp(type, "Announce") == 0)
2241 type = "reblog"; 2385 type = "reblog";
2242 else 2386 else
@@ -2277,8 +2421,31 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2277 if (strcmp(type, "follow") != 0 && !xs_is_null(objid)) { 2421 if (strcmp(type, "follow") != 0 && !xs_is_null(objid)) {
2278 xs *st = mastoapi_status(&snac1, entry); 2422 xs *st = mastoapi_status(&snac1, entry);
2279 2423
2280 if (st) 2424 if (st) {
2281 mn = xs_dict_append(mn, "status", st); 2425 mn = xs_dict_append(mn, "status", st);
2426
2427 if (strcmp(type, "reaction") == 0 && !xs_is_null(objid)) {
2428 const char *eid = NULL;
2429 const char *url = NULL;
2430 int utf = 0;
2431
2432 const xs_dict *tag = xs_list_get(xs_dict_get_path(noti, "msg.tag"), 0);
2433 const char *content = xs_dict_get_path(noti, "msg.content");
2434
2435 url = xs_dict_get(xs_dict_get(tag, "icon"), "url");
2436 eid = xs_dict_get(tag, "name");
2437
2438 if (eid && url) {
2439 mn = xs_dict_append(mn, "emoji", eid);
2440 mn = xs_dict_append(mn, "emoji_url", url);
2441 }
2442
2443 if (xs_is_emoji((utf = xs_utf8_dec(&content)))) {
2444 xs *s1 = xs_fmt("&#%d;", utf);
2445 mn = xs_dict_append(mn, "name", s1);
2446 }
2447 }
2448 }
2282 } 2449 }
2283 2450
2284 out = xs_list_append(out, mn); 2451 out = xs_list_append(out, mn);
@@ -2480,19 +2647,33 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2480 if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/ 2647 if (strcmp(cmd, "/v1/custom_emojis") == 0) { /** **/
2481 xs *emo = emojis(); 2648 xs *emo = emojis();
2482 xs *list = xs_list_new(); 2649 xs *list = xs_list_new();
2483 int c = 0;
2484 const xs_str *k; 2650 const xs_str *k;
2485 const xs_val *v; 2651 const xs_val *v;
2486 while(xs_dict_next(emo, &k, &v, &c)) { 2652 xs_dict_foreach(emo, k, v) {
2487 xs *current = xs_dict_new(); 2653 xs *current = xs_dict_new();
2488 if (xs_startswith(v, "https://") && xs_startswith((xs_mime_by_ext(v)), "image/")) { 2654 if ((xs_startswith(v, "https://") && xs_startswith((xs_mime_by_ext(v)), "image/")) || xs_type(v) == XSTYPE_DICT) {
2489 /* remove first and last colon */ 2655 /* remove first and last colon */
2490 xs *shortcode = xs_replace(k, ":", ""); 2656 if (xs_type(v) == XSTYPE_DICT) {
2491 current = xs_dict_append(current, "shortcode", shortcode); 2657 const char *v2;
2492 current = xs_dict_append(current, "url", v); 2658 const char *cat = k;
2493 current = xs_dict_append(current, "static_url", v); 2659 xs_dict_foreach(v, k, v2) {
2494 current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE)); 2660 xs *shortcode = xs_replace(k, ":", "");
2495 list = xs_list_append(list, current); 2661 current = xs_dict_append(current, "shortcode", shortcode);
2662 current = xs_dict_append(current, "url", v2);
2663 current = xs_dict_append(current, "static_url", v2);
2664 current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE));
2665 current = xs_dict_append(current, "category", cat);
2666 list = xs_list_append(list, current);
2667 }
2668 }
2669 else {
2670 xs *shortcode = xs_replace(k, ":", "");
2671 current = xs_dict_append(current, "shortcode", shortcode);
2672 current = xs_dict_append(current, "url", v);
2673 current = xs_dict_append(current, "static_url", v);
2674 current = xs_dict_append(current, "visible_in_picker", xs_stock(XSTYPE_TRUE));
2675 list = xs_list_append(list, current);
2676 }
2496 } 2677 }
2497 } 2678 }
2498 *body = xs_json_dumps(list, 0); 2679 *body = xs_json_dumps(list, 0);
@@ -2594,6 +2775,11 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2594 "\"max_expiration\":2629746," 2775 "\"max_expiration\":2629746,"
2595 "\"max_options\":8,\"min_expiration\":300}"); 2776 "\"max_options\":8,\"min_expiration\":300}");
2596 cfg = xs_dict_append(cfg, "polls", d14); 2777 cfg = xs_dict_append(cfg, "polls", d14);
2778
2779
2780 xs *d15 = xs_json_loads("{\"max_reactions\":50}");
2781 cfg = xs_dict_append(cfg, "reactions", d15);
2782
2597 } 2783 }
2598 2784
2599 ins = xs_dict_append(ins, "configuration", cfg); 2785 ins = xs_dict_append(ins, "configuration", cfg);
@@ -2680,8 +2866,6 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2680 /* return ancestors and children */ 2866 /* return ancestors and children */
2681 xs *anc = xs_list_new(); 2867 xs *anc = xs_list_new();
2682 xs *des = xs_list_new(); 2868 xs *des = xs_list_new();
2683 xs_list *p;
2684 const xs_str *v;
2685 char pid[MD5_HEX_SIZE]; 2869 char pid[MD5_HEX_SIZE];
2686 2870
2687 /* build the [grand]parent list, moving up */ 2871 /* build the [grand]parent list, moving up */
@@ -2701,21 +2885,9 @@ int mastoapi_get_handler(const xs_dict *req, const char *q_path,
2701 } 2885 }
2702 2886
2703 /* build the children list */ 2887 /* build the children list */
2704 xs *children = object_children(xs_dict_get(msg, "id")); 2888 xs *childs = build_childrens(msg, &snac1);
2705 p = children; 2889 if (xs_list_len(childs) > 0)
2706 2890 des = xs_list_cat(des, childs);
2707 while (xs_list_iter(&p, &v)) {
2708 xs *m2 = NULL;
2709
2710 if (valid_status(timeline_get_by_md5(&snac1, v, &m2))) {
2711 if (xs_is_null(xs_dict_get(m2, "name"))) {
2712 xs *st = mastoapi_status(&snac1, m2);
2713
2714 if (st)
2715 des = xs_list_append(des, st);
2716 }
2717 }
2718 }
2719 2891
2720 out = xs_dict_new(); 2892 out = xs_dict_new();
2721 out = xs_dict_append(out, "ancestors", anc); 2893 out = xs_dict_append(out, "ancestors", anc);
@@ -3044,7 +3216,10 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3044 xs *app = xs_dict_new(); 3216 xs *app = xs_dict_new();
3045 xs *id = xs_replace_i(tid(0), ".", ""); 3217 xs *id = xs_replace_i(tid(0), ".", "");
3046 xs *csec = random_str(); 3218 xs *csec = random_str();
3047 xs *vkey = random_str(); 3219 xs *vkey = xs_dup(xs_dict_get(srv_config, "vkey"));
3220 if (vkey == NULL)
3221 vkey = random_str();
3222
3048 xs *cid = NULL; 3223 xs *cid = NULL;
3049 3224
3050 /* pick a non-existent random cid */ 3225 /* pick a non-existent random cid */
@@ -3216,7 +3391,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3216 3391
3217 if (n_msg != NULL) { 3392 if (n_msg != NULL) {
3218 enqueue_message(&snac, n_msg); 3393 enqueue_message(&snac, n_msg);
3219 timeline_admire(&snac, xs_dict_get(n_msg, "object"), snac.actor, 1); 3394 timeline_admire(&snac, xs_dict_get(n_msg, "object"), snac.actor, 1, msg);
3220 3395
3221 out = mastoapi_status(&snac, msg); 3396 out = mastoapi_status(&snac, msg);
3222 } 3397 }
@@ -3232,12 +3407,35 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
3232 } 3407 }
3233 } 3408 }
3234 else 3409 else
3410 if (strcmp(op, "react") == 0) { /** **/
3411 const char *eid = xs_list_get(l, 5);
3412 xs *n_msg = msg_emoji_init(&snac, id, eid);
3413 if (n_msg)
3414 out = mastoapi_status(&snac, n_msg);
3415 }
3416 else
3417 if (strcmp(op, "unreact") == 0) { /** **/
3418 const char *eid = xs_list_get(l, 5);
3419 xs *content = xs_fmt("%s", eid);
3420
3421 if (eid) {
3422 xs *n_msg = msg_emoji_unreact(&snac, id, content);
3423
3424 if (n_msg != NULL) {
3425 enqueue_message(&snac, n_msg);
3426
3427 out = mastoapi_status(&snac, msg);
3428 }
3429 }
3430 }
3431
3432 else
3235 if (strcmp(op, "reblog") == 0) { /** **/ 3433 if (strcmp(op, "reblog") == 0) { /** **/
3236 xs *n_msg = msg_admiration(&snac, id, "Announce"); 3434 xs *n_msg = msg_admiration(&snac, id, "Announce");
3237 3435
3238 if (n_msg != NULL) { 3436 if (n_msg != NULL) {
3239 enqueue_message(&snac, n_msg); 3437 enqueue_message(&snac, n_msg);
3240 timeline_admire(&snac, xs_dict_get(n_msg, "object"), snac.actor, 0); 3438 timeline_admire(&snac, xs_dict_get(n_msg, "object"), snac.actor, 0, msg);
3241 3439
3242 out = mastoapi_status(&snac, msg); 3440 out = mastoapi_status(&snac, msg);
3243 } 3441 }
@@ -3824,7 +4022,7 @@ int mastoapi_delete_handler(const xs_dict *req, const char *q_path,
3824 if (valid_status(object_get_by_md5(p, &obj))) { 4022 if (valid_status(object_get_by_md5(p, &obj))) {
3825 const char *id = xs_dict_get(obj, "id"); 4023 const char *id = xs_dict_get(obj, "id");
3826 4024
3827 if (xs_is_string(id) && xs_startswith(id, snac.actor)) { 4025 if (xs_is_string(id) && is_msg_mine(&snac, id)) {
3828 xs *out = mastoapi_status(&snac, obj); 4026 xs *out = mastoapi_status(&snac, obj);
3829 4027
3830 xs *msg = msg_delete(&snac, id); 4028 xs *msg = msg_delete(&snac, id);