summaryrefslogtreecommitdiff
path: root/activitypub.c
diff options
context:
space:
mode:
authorGravatar violette2025-12-18 07:58:24 +0100
committerGravatar grunfink2025-12-18 07:58:24 +0100
commit85ed0eb0d535700a5df837c37f51848811e461a0 (patch)
tree818c80cc48c445b2950796230946c63eca3a3242 /activitypub.c
parentBumped version. (diff)
downloadsnac2-85ed0eb0d535700a5df837c37f51848811e461a0.tar.gz
snac2-85ed0eb0d535700a5df837c37f51848811e461a0.tar.xz
snac2-85ed0eb0d535700a5df837c37f51848811e461a0.zip
Added emoji reactions (contributed by violette).
Diffstat (limited to 'activitypub.c')
-rw-r--r--activitypub.c161
1 files changed, 156 insertions, 5 deletions
diff --git a/activitypub.c b/activitypub.c
index 90230d8..19f0cc6 100644
--- a/activitypub.c
+++ b/activitypub.c
@@ -4,6 +4,7 @@
4#include "xs.h" 4#include "xs.h"
5#include "xs_json.h" 5#include "xs_json.h"
6#include "xs_curl.h" 6#include "xs_curl.h"
7#include "xs_url.h"
7#include "xs_mime.h" 8#include "xs_mime.h"
8#include "xs_openssl.h" 9#include "xs_openssl.h"
9#include "xs_regex.h" 10#include "xs_regex.h"
@@ -1530,11 +1531,24 @@ xs_dict *msg_update(snac *snac, const xs_dict *object)
1530 1531
1531 1532
1532xs_dict *msg_admiration(snac *snac, const char *object, const char *type) 1533xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
1533/* creates a Like or Announce message */ 1534/* creates a Like, Announce or EmojiReact message */
1534{ 1535{
1535 xs *a_msg = NULL; 1536 xs *a_msg = NULL;
1536 xs_dict *msg = NULL; 1537 xs_dict *msg = NULL;
1537 xs *wrk = NULL; 1538 xs *wrk = NULL;
1539 char t = 0;
1540
1541 switch (*type) {
1542 case 'L':
1543 t = 'l';
1544 break;
1545 case 'A':
1546 t = 'a';
1547 break;
1548 case 'E':
1549 t = 'e';
1550 break;
1551 }
1538 1552
1539 /* call the object */ 1553 /* call the object */
1540 timeline_request(snac, &object, &wrk, 0); 1554 timeline_request(snac, &object, &wrk, 0);
@@ -1542,7 +1556,7 @@ xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
1542 if (valid_status(object_get(object, &a_msg))) { 1556 if (valid_status(object_get(object, &a_msg))) {
1543 xs *rcpts = xs_list_new(); 1557 xs *rcpts = xs_list_new();
1544 xs *o_md5 = xs_md5_hex(object, strlen(object)); 1558 xs *o_md5 = xs_md5_hex(object, strlen(object));
1545 xs *id = xs_fmt("%s/%s/%s", snac->actor, *type == 'L' ? "l" : "a", o_md5); 1559 xs *id = xs_fmt("%s/%c/%s", snac->actor, t, o_md5);
1546 1560
1547 msg = msg_base(snac, type, id, snac->actor, "@now", object); 1561 msg = msg_base(snac, type, id, snac->actor, "@now", object);
1548 1562
@@ -1586,6 +1600,113 @@ xs_dict *msg_repulsion(snac *user, const char *id, const char *type)
1586 return msg; 1600 return msg;
1587} 1601}
1588 1602
1603xs_dict *msg_emoji_init(snac *snac, const char *mid, const char *eid)
1604/* creates an emoji reaction from a local user */
1605{
1606 xs_dict *n_msg = msg_admiration(snac, mid, "EmojiReact");
1607
1608 eid = xs_strip_chars_i(xs_dup(eid), ":");
1609 xs *content = NULL;
1610 xs *tag = xs_list_new();
1611 xs *dict = xs_dict_new();
1612 xs *icon = xs_dict_new();
1613 xs *accounts = xs_list_new();
1614
1615 /* may be a default emoji */
1616 xs *eidd = xs_dup(eid);
1617 const char *eidda = eid;
1618
1619 if (xs_is_emoji(xs_utf8_dec(&eidda)))
1620 content = xs_dup(eid);
1621
1622 else if (*eid == '%') {
1623 content = xs_url_dec_emoji(xs_dup(eid));
1624 if (content == NULL) {
1625 return NULL;
1626 }
1627 }
1628 else if (xs_dict_get(emojis(), xs_fmt(":%s:", eid)) == NULL)
1629 return NULL;
1630 else {
1631 content = xs_fmt(":%s:", eid);
1632 icon = xs_dict_set(icon, "type", "Image");
1633 icon = xs_dict_set(icon, "url", xs_fmt("%s/s/%s.png", snac->actor, eid));
1634 dict = xs_dict_set(dict, "icon", icon);
1635
1636 dict = xs_dict_set(dict, "id", xs_fmt("%s/s/%s.png", snac->actor, eid));
1637 dict = xs_dict_set(dict, "name", content);
1638 dict = xs_dict_set(dict, "type", "Emoji");
1639 tag = xs_list_append(tag, dict);
1640 }
1641
1642 accounts = xs_list_append(accounts, snac->actor);
1643
1644 n_msg = xs_dict_set(n_msg, "content", content);
1645 n_msg = xs_dict_set(n_msg, "accounts", accounts);
1646 n_msg = xs_dict_set(n_msg, "attributedTo", xs_list_get(xs_dup(xs_dict_get(n_msg, "to")), 1));
1647 n_msg = xs_dict_set(n_msg, "accountId", snac->uid);
1648 n_msg = xs_dict_set(n_msg, "tag", tag);
1649
1650 int ret = timeline_admire(snac, xs_dict_get(n_msg, "object"), snac->actor, 1, n_msg);
1651 if (ret == 200 || ret == 201) {
1652 enqueue_message(snac, n_msg);
1653 return n_msg;
1654 }
1655
1656 return NULL;
1657}
1658
1659xs_dict *msg_emoji_unreact(snac *user, const char *mid, const char *eid)
1660/* creates an Undo + emoji reaction message */
1661{
1662 xs *a_msg = NULL;
1663 xs_dict *msg = NULL;
1664
1665 if (valid_status(object_get(mid, &a_msg))) {
1666 /* create a clone of the original admiration message */
1667 xs *object = msg_admiration(user, mid, "EmojiReact");
1668
1669 /* delete the published date */
1670 object = xs_dict_del(object, "published");
1671
1672 /* create an undo message for this object */
1673 msg = msg_undo(user, object);
1674
1675 /* copy the 'to' field */
1676 msg = xs_dict_set(msg, "to", xs_dict_get(object, "to"));
1677 }
1678
1679 xs *emotes = object_get_emoji_reacts(mid);
1680 const char *v;
1681 int c = 0;
1682
1683 /* may be a default emoji */
1684 if (strlen(eid) == 12 && *eid == '%') {
1685 eid = xs_url_dec(eid);
1686 if (eid == NULL) {
1687 return NULL;
1688 }
1689 }
1690
1691 /* lets get all emotes for this msg, and compare it to our content */
1692 while (xs_list_next(emotes, &v, &c)) {
1693 xs_dict *e = NULL;
1694 if (valid_status(object_get_by_md5(v, &e))) {
1695 const char *content = xs_dict_get(e, "content");
1696 const char *id = xs_dict_get(e, "id");
1697 const char *actor = xs_dict_get(e, "actor");
1698 /* maybe formated as :{emoteName}: too */
1699 if (xs_str_in(eid, content) != -1)
1700 if (strcmp(user->actor, actor) == 0) {
1701 object_rm_emoji_react(mid, id);
1702 return msg;
1703 }
1704 }
1705 }
1706
1707 return NULL;
1708}
1709
1589 1710
1590xs_dict *msg_actor_place(snac *user, const char *label) 1711xs_dict *msg_actor_place(snac *user, const char *label)
1591/* creates a Place object, if the user has a location defined */ 1712/* creates a Place object, if the user has a location defined */
@@ -2605,6 +2726,16 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2605 else 2726 else
2606 if (strcmp(type, "Undo") == 0) { /** **/ 2727 if (strcmp(type, "Undo") == 0) { /** **/
2607 const char *id = xs_dict_get(object, "object"); 2728 const char *id = xs_dict_get(object, "object");
2729 const char *content = xs_dict_get(object, "content");
2730 /* misskey sends emojis as like + tag */
2731 xs *cd = xs_dup(content);
2732 const char *sna = cd;
2733 const xs_dict *tag = xs_dict_get(object, "tag");
2734 unsigned int utf = xs_utf8_dec((const char **)&sna);
2735
2736 int isEmoji = 0;
2737 if (xs_is_emoji(utf) || (tag && xs_list_len(tag) > 0))
2738 isEmoji = 1;
2608 2739
2609 if (xs_type(object) != XSTYPE_DICT) { 2740 if (xs_type(object) != XSTYPE_DICT) {
2610 snac_debug(snac, 1, xs_fmt("undo: overriding utype %s | %s | %s", 2741 snac_debug(snac, 1, xs_fmt("undo: overriding utype %s | %s | %s",
@@ -2633,8 +2764,19 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2633 else 2764 else
2634 snac_log(snac, xs_fmt("error deleting follower %s", actor)); 2765 snac_log(snac, xs_fmt("error deleting follower %s", actor));
2635 } 2766 }
2767 /* *key emojis are like w/ Emoji tag */
2768 else
2769 if ((isEmoji || strcmp(utype, "EmojiReact") == 0) &&
2770 (content && strcmp(content, "♥") != 0)) {
2771 const xs_val *mid = xs_dict_get(object, "id");
2772 int status = object_rm_emoji_react((char *)id, mid);
2773 /* ensure *key notifications type */
2774 utype = "EmojiReact";
2775
2776 snac_log(snac, xs_fmt("Undo 'EmojiReact' for %s %d", id, status));
2777 }
2636 else 2778 else
2637 if (strcmp(utype, "Like") == 0 || strcmp(utype, "EmojiReact") == 0) { /** **/ 2779 if (strcmp(utype, "Like") == 0) { /** **/
2638 int status = object_unadmire(id, actor, 1); 2780 int status = object_unadmire(id, actor, 1);
2639 2781
2640 snac_log(snac, xs_fmt("Undo '%s' for %s %d", utype, id, status)); 2782 snac_log(snac, xs_fmt("Undo '%s' for %s %d", utype, id, status));
@@ -2771,13 +2913,22 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2771 } 2913 }
2772 else 2914 else
2773 if (strcmp(type, "Like") == 0 || strcmp(type, "EmojiReact") == 0) { /** **/ 2915 if (strcmp(type, "Like") == 0 || strcmp(type, "EmojiReact") == 0) { /** **/
2916 /* misskey sends emojis as Like + tag.
2917 * It is easier to handle them both at the same time. */
2918 const char *sna = xs_dict_get(msg, "content");
2919 const xs_dict *tag = xs_dict_get(msg, "tag");
2920 unsigned int utf = xs_utf8_dec((const char **)&sna);
2921
2922 if (xs_is_emoji(utf) || (tag && xs_list_len(tag) > 0))
2923 type = "EmojiReact";
2924
2774 if (xs_type(object) == XSTYPE_DICT) 2925 if (xs_type(object) == XSTYPE_DICT)
2775 object = xs_dict_get(object, "id"); 2926 object = xs_dict_get(object, "id");
2776 2927
2777 if (xs_is_null(object)) 2928 if (xs_is_null(object))
2778 snac_log(snac, xs_fmt("malformed message: no 'id' field")); 2929 snac_log(snac, xs_fmt("malformed message: no 'id' field"));
2779 else 2930 else
2780 if (timeline_admire(snac, object, actor, 1) == HTTP_STATUS_CREATED) 2931 if (timeline_admire(snac, object, actor, 1, xs_dup(msg)) == HTTP_STATUS_CREATED)
2781 snac_log(snac, xs_fmt("new '%s' %s %s", type, actor, object)); 2932 snac_log(snac, xs_fmt("new '%s' %s %s", type, actor, object));
2782 else 2933 else
2783 snac_log(snac, xs_fmt("repeated '%s' from %s to %s", type, actor, object)); 2934 snac_log(snac, xs_fmt("repeated '%s' from %s to %s", type, actor, object));
@@ -2818,7 +2969,7 @@ int process_input_message(snac *snac, const xs_dict *msg, const xs_dict *req)
2818 xs *this_relay = xs_fmt("%s/relay", srv_baseurl); 2969 xs *this_relay = xs_fmt("%s/relay", srv_baseurl);
2819 2970
2820 if (strcmp(actor, this_relay) != 0) { 2971 if (strcmp(actor, this_relay) != 0) {
2821 if (valid_status(timeline_admire(snac, object, actor, 0))) 2972 if (valid_status(timeline_admire(snac, object, actor, 0, a_msg)))
2822 snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object)); 2973 snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
2823 else 2974 else
2824 snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s", 2975 snac_log(snac, xs_fmt("repeated 'Announce' from %s to %s",