diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | src/api.c | 170 | ||||
-rw-r--r-- | src/h.c | 57 |
3 files changed, 200 insertions, 31 deletions
@@ -79,6 +79,10 @@ completing this task would make `discord.c` the first and the only alternative c it would be useful to have an android port, and luckily this is possible with little effort due to the GTK broadway backend that interfaces with a HTML renderer, WebView for example. +### misc + +* message attachments + ## developer notes * use an `i386` compatible machine with `debian` `bullseye` (I use a Dell Latitude D620) @@ -31,7 +31,8 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn struct dc_client * client = pass->api_io.client; struct dc_program * program = pass->api_io.program; pass->json_reason = reason; - if (reason == LEJPCB_FAILED || reason == LEJPCB_COMPLETE || reason == LEJPCB_START) { + if (reason == LEJPCB_FAILED || reason == LEJPCB_START || (reason == LEJPCB_COMPLETE && pass->packet != DC_MESSAGE_CREATE /* hack for lejp bug with wrong path on OBJECT_END */)) { + if (reason == LEJPCB_COMPLETE) DC_API_IO_GC(pass->api_io); pass->packet = DC_NONE; return '\0'; @@ -59,7 +60,7 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn break; case DC_JSON_T: for (size_t i = 0; i < sizeof(dc_ws_packet)/sizeof(dc_ws_packet[0]); i++) - if (strcmp(dc_ws_packet[i], ctx->buf)) { + if (!strcmp(dc_ws_packet[i], ctx->buf)) { pass->packet = DC_STRPKTOFF+i; break; } @@ -129,9 +130,10 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn } pass->api_io.user = NULL; } - if ((path == DC_JSON_DM || path == DC_JSON_GUILD_CHANNEL) && reason == LEJPCB_OBJECT_START) { + if ((path == DC_JSON_DM || path == DC_JSON_GUILD_CHANNEL || path == DC_JSON_MESSAGE_MENTION_CHANNEL) && reason == LEJPCB_OBJECT_START) { pass->api_io.channel = dc_channel_init(); pass->api_io.channel->status |= DC_IN_PROGRESS; + pass->api_io.id = 0; } if (path == DC_JSON_DM && reason == LEJPCB_OBJECT_END) { struct dc_channel ** channel = &client->guilds[0]->channel; /* 1. guild = DMs */ @@ -166,20 +168,23 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn pass->api_io.channel->guild = client->guilds[0]; pass->api_io.channel = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */ } - if (path == DC_JSON_GUILD_CHANNEL && reason == LEJPCB_OBJECT_END) { - if (!pass->api_io.channel->id || !DC_CHANNEL_SUPPORTED(pass->api_io.channel->type)) { + if ((path == DC_JSON_GUILD_CHANNEL || path == DC_JSON_MESSAGE_MENTION_CHANNEL || path == DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL) && reason == LEJPCB_OBJECT_END) { + struct dc_guild * gu = NULL; + if (!pass->api_io.channel->id || !DC_CHANNEL_SUPPORTED(pass->api_io.channel->type) || (path == DC_JSON_MESSAGE_MENTION_CHANNEL && (!pass->api_io.id || !(gu = dc_find_guild(program->guilds, program->guilds_length, pass->api_io.id)))) || (path == DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL && (!pass->api_io.id || !(gu = dc_find_guild(program->guilds, program->guilds_length, pass->api_io.id))))) { dc_channel_free(pass->api_io.channel, DC_UNSET); pass->api_io.channel = NULL; return '\0'; } - struct dc_channel ** channel = &pass->api_io.guild->channel; + if (!gu) + gu = pass->api_io.guild; + struct dc_channel ** channel = &gu->channel; while (*channel) channel = &(*channel)->next; struct dc_channel * ch; if ((ch = dc_find_channel(program->channels, program->channels_length, pass->api_io.channel->id))) DC_TRANSFER_CHANNEL(pass->api_io.channel, ch); pass->api_io.channel = dc_addr_channel(program, DC_ISAE(program->channels), pass->api_io.channel, DC_MAY_FREE | DC_REPLACE); - if (!dc_find_ll_channel(pass->api_io.guild->channel, pass->api_io.channel->id)) { + if (!dc_find_ll_channel(gu->channel, pass->api_io.channel->id)) { fprintf(stderr, "new channel id=%llu (:\n", pass->api_io.channel->id); pass->api_io.channel->next = NULL; *channel = pass->api_io.channel; @@ -189,6 +194,19 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn (*pe)->channel = pass->api_io.channel; pe = &(*pe)->next; } + switch (path) { + case DC_JSON_MESSAGE_MENTION_CHANNEL: + if (pass->api_io.message) + dc_add_channel(DC_ISAE(pass->api_io.message->channels), pass->api_io.channel, DC_UNSET); + pass->api_io.id = 0; + break; + case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL: + if (pass->api_io.message && pass->api_io.message->reply) + dc_add_channel(DC_ISAE(pass->api_io.message->reply->channels), pass->api_io.channel, DC_UNSET); + pass->api_io.id = 0; + default: + break; + } pass->api_io.channel = NULL; /* we're done, NULL it or */ } if (path == DC_JSON_GUILD && reason == LEJPCB_OBJECT_START) { @@ -233,7 +251,7 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn struct dc_role * ro; if ((ro = dc_find_role(program->roles, program->roles_length, pass->api_io.role->id))) dc_transfer_role(pass->api_io.role, ro); - pass->api_io.role = dc_add_role(DC_ISAE(program->roles), pass->api_io.role, DC_MAY_FREE | DC_REPLACE); + pass->api_io.role = dc_addr_role(program, DC_ISAE(program->roles), pass->api_io.role, DC_MAY_FREE | DC_REPLACE); struct dc_role ** role; role = &pass->api_io.guild->role; while (*role) @@ -250,6 +268,69 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn } if (path == DC_JSON_MEMBERSHIP && (reason == LEJPCB_OBJECT_END || reason == LEJPCB_OBJECT_START)) pass->api_io.id = 0; + if (pass->packet == DC_MESSAGE_CREATE && reason == LEJPCB_COMPLETE) { + pass->api_io.message = dc_message_init(); + pass->api_io.message->reply = dc_message_init(); + pass->api_io.message->reply->status |= DC_IN_PROGRESS; + pass->api_io.message->status |= DC_IN_PROGRESS; + } + if (pass->packet == DC_MESSAGE_CREATE && pass->api_io.message && path == DC_JSON_MESSAGE && reason == LEJPCB_COMPLETE /* hack for lejp bug with wrong path on OBJECT_END */ ) { + struct dc_message * me; + if (!pass->api_io.message->id || !pass->api_io.message->channel || !DC_MESSAGE_SUPPORTED(pass->api_io.message->type)) { + dc_message_free(pass->api_io.message, DC_UNSET); + pass->api_io.message = NULL; + return '\0'; + } + if (pass->api_io.message->reply) { + if (!pass->api_io.message->reply->id || !pass->api_io.message->reply->channel || !DC_MESSAGE_SUPPORTED(pass->api_io.message->reply->type)) { + dc_message_free(pass->api_io.message->reply, DC_UNSET); + pass->api_io.message->reply = NULL; + } + pass->api_io.message->reply->status &= ~DC_IN_PROGRESS; + pass->api_io.message->reply->time = DC_ID2TIME(pass->api_io.message->reply->id); + if ((me = dc_find_message(program->messages, program->messages_length, pass->api_io.message->reply->id))) + dc_transfer_message(pass->api_io.message, me); + pass->api_io.message->reply = dc_addr_message(program, DC_ISAE(program->messages), pass->api_io.message->reply, DC_MAY_FREE | DC_REPLACE); + struct dc_message ** message = &pass->api_io.message->reply->channel->message; + while (*message) { + if ((*message)->time < pass->api_io.message->reply->time && (*message)->next && (*message)->next->time > pass->api_io.message->reply->time) + break; + message = &(*message)->next; + } + if (!dc_find_ll_message(pass->api_io.message->reply->channel->message, pass->api_io.message->reply->id)) { + fprintf(stderr, "new message reply id=%llu (:\n", pass->api_io.message->reply->id); + if (*message) { + pass->api_io.message->reply->next = (*message)->next; + (*message)->next = pass->api_io.message->reply; + } else { + pass->api_io.message->reply->next = NULL; + *message = pass->api_io.message->reply; + } + } + } + pass->api_io.message->status &= ~DC_IN_PROGRESS; + pass->api_io.message->time = DC_ID2TIME(pass->api_io.message->id); + if ((me = dc_find_message(program->messages, program->messages_length, pass->api_io.message->id))) + dc_transfer_message(pass->api_io.message, me); + pass->api_io.message = dc_addr_message(program, DC_ISAE(program->messages), pass->api_io.message, DC_MAY_FREE | DC_REPLACE); + struct dc_message ** message = &pass->api_io.message->channel->message; + while (*message) { + if ((*message)->time < pass->api_io.message->time && (*message)->next && (*message)->next->time > pass->api_io.message->time) /* if *message is not NULL, */ + break; /* we should insert new message after message into linked list */ + message = &(*message)->next; /* otherwise just insert at the end */ + } + if (!dc_find_ll_message(pass->api_io.message->channel->message, pass->api_io.message->id)) { + fprintf(stderr, "new message id=%llu (:\n", pass->api_io.message->id); + if (*message) { + pass->api_io.message->next = (*message)->next; /* based linked lists */ + (*message)->next = pass->api_io.message; + } else { + pass->api_io.message->next = NULL; + *message = pass->api_io.message; + } + } + pass->api_io.message = NULL; + } if (reason & LEJP_FLAG_CB_IS_VALUE) { struct dc_user * user; /* this is just a var for you use */ struct dc_role * role; /* now we rely on -Wuninitialized to detect weird stuff */ @@ -269,36 +350,50 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn case DC_JSON_ME_USERNAME: /* we are always checking for user because evil srv */ case DC_JSON_USER_NAME: /* might insert dots in keys and prevent OBJECT_CREATE */ case DC_JSON_MEMBER_USERNAME: + case DC_JSON_MESSAGE_AUTHOR_USERNAME: if (pass->api_io.user && !pass->api_io.user->username) /* no trust srv */ pass->api_io.user->username = strdup(ctx->buf); break; case DC_JSON_ME_ID: case DC_JSON_USER_ID: case DC_JSON_MEMBER_ID: + case DC_JSON_MESSAGE_AUTHOR_ID: if (pass->api_io.user) pass->api_io.user->id = strtoll(ctx->buf, NULL, 10); break; case DC_JSON_ME_DISCRIMINATOR: case DC_JSON_USER_DISCRIMINATOR: case DC_JSON_MEMBER_DISCRIMINATOR: + case DC_JSON_MESSAGE_AUTHOR_DISCRIMINATOR: if (pass->api_io.user) pass->api_io.user->discriminator = atoi(ctx->buf); break; /* yeah, we don't care about nicknames */ case DC_JSON_DM_TYPE: case DC_JSON_GUILD_CHANNEL_TYPE: + case DC_JSON_MESSAGE_MENTION_CHANNEL_TYPE: + case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_TYPE: if (pass->api_io.channel) pass->api_io.channel->type = atoi(ctx->buf); break; case DC_JSON_DM_ID: case DC_JSON_GUILD_CHANNEL_ID: + case DC_JSON_MESSAGE_MENTION_CHANNEL_ID: + case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_ID: if (pass->api_io.channel) pass->api_io.channel->id = strtoull(ctx->buf, NULL, 10); break; case DC_JSON_DM_NAME: case DC_JSON_GUILD_CHANNEL_NAME: + case DC_JSON_MESSAGE_MENTION_CHANNEL_NAME: + case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_NAME: if (pass->api_io.channel && !pass->api_io.channel->name) pass->api_io.channel->name = strdup(ctx->buf); break; + case DC_JSON_MESSAGE_MENTION_CHANNEL_GUILD: + case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_GUILD: + if (pass->api_io.channel) + pass->api_io.id = strtoull(ctx->buf, NULL, 10); + break; case DC_JSON_GUILD_NAME: if (pass->api_io.guild && !pass->api_io.guild->name) pass->api_io.guild->name = strdup(ctx->buf); @@ -341,20 +436,75 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn if (!(user = dc_find_user(program->users, program->users_length, pass->api_io.id))) { št |= 1; user = dc_user_init(); + user->id = pass->api_io.id; } if (!(role = dc_find_role(program->roles, program->roles_length, id))) { št |= 2; role = dc_role_init(); role->id = id; } + if (!dc_find_user(role->users, role->users_length, user->id)) + fprintf(stderr, "new role membership user %s#%d (usr=%llu role=%llu)\n", user->username ? user->username : "", user->discriminator, user->id, role->id); dc_add_user(DC_ISAE(role->users), user, DC_UNSET); if (št & 1) dc_addr_user(program, DC_ISAE(program->users), user, DC_UNSET); if (št & 2) dc_addr_role(program, DC_ISAE(program->roles), role, DC_UNSET); - fprintf(stderr, "new role membership user %s#%d (usr=%llu role=%llu)\n", user->username ? user->username : "", user->discriminator, user->id, role->id); pass->api_io.id = 0; break; + case DC_JSON_MESSAGE_ID: + if (pass->api_io.message) + pass->api_io.message->id = strtoull(ctx->buf, NULL, 10); + break; + case DC_JSON_MESSAGE_TYPE: + if (pass->api_io.message) + pass->api_io.message->type = atoi(ctx->buf); + break; + case DC_JSON_MESSAGE_CHANNEL: + if (pass->api_io.message) + pass->api_io.message->channel = dc_find_channel(program->channels, program->channels_length, strtoull(ctx->buf, NULL, 10)); /* if we don't find it, too bad, we won't store msg */ + break; + case DC_JSON_MESSAGE_CONTENT: + if (pass->api_io.message) { /* lejp parses in chunks of 256, msg is 2k */ + if (!pass->api_io.message->message) /* so we don't have garbag */ + pass->api_io.message->message = strdup(""); + pass->api_io.message->message = realloc(pass->api_io.message->message, strlen(pass->api_io.message->message)+strlen(ctx->buf)+1); + strcat(pass->api_io.message->message, ctx->buf); + } + break; + case DC_JSON_MESSAGE_MENTION_ROLE: + if (pass->api_io.message) { + struct dc_role * role = dc_find_role(program->roles, program->roles_length, strtoull(ctx->buf, NULL, 10)); + if (role) + dc_add_role(DC_ISAE(pass->api_io.message->roles), role, DC_UNSET); + } + break; + case DC_JSON_MESSAGE_REFOBJ_MENTION_ROLE: + if (pass->api_io.message && pass->api_io.message->reply) { + struct dc_role * role = dc_find_role(program->roles, program->roles_length, strtoull(ctx->buf, NULL, 10)); + if (role) + dc_add_role(DC_ISAE(pass->api_io.message->reply->roles), role, DC_UNSET); + } + break; + case DC_JSON_MESSAGE_REFOBJ_ID: + if (pass->api_io.message && pass->api_io.message->reply) + pass->api_io.message->reply->id = strtoull(ctx->buf, NULL, 10); + break; + case DC_JSON_MESSAGE_REFOBJ_TYPE: + if (pass->api_io.message && pass->api_io.message->reply) + pass->api_io.message->reply->type = atoi(ctx->buf); + break; + case DC_JSON_MESSAGE_REFOBJ_CHANNEL: + if (pass->api_io.message && pass->api_io.message->reply) + pass->api_io.message->reply->channel = dc_find_channel(program->channels, program->channels_length, strtoull(ctx->buf, NULL, 10)); + break; + case DC_JSON_MESSAGE_REFOBJ_CONTENT: + if (pass->api_io.message && pass->api_io.message->reply) { + if (!pass->api_io.message->reply->message) + pass->api_io.message->reply->message = strdup(""); + pass->api_io.message->reply->message = realloc(pass->api_io.message->reply->message, strlen(pass->api_io.message->reply->message)+strlen(ctx->buf)+1); + strcat(pass->api_io.message->reply->message, ctx->buf); + } default: break; } @@ -362,8 +512,6 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warn if (pass->packet == DC_NONE) /* useless to do anything BELOW if we haven't recvd packet type */ return '\0'; switch (pass->packet) { - case DC_MESSAGE_CREATE: - break; default: break; } @@ -16,6 +16,7 @@ #define DC_LWS_MAX_FD 64 /* max file descriptors LWS will have open. if unset, LWS acquires all unused */ #define DC_LWS_MAX_HEADER_LENGTH 64 /* _MAX_HEADER_LENGTH*_HEADERS_LENGTH is allocated for every reque */ #define DC_WS_PING_FORMAT "{\"op\":1,\"d\":%lld}" +#define DC_ID2TIME(x) ((x >> 22 /* this gives us ms since 2015 */)/1000 + 1420070400 /* UNIX 2015 */) #ifdef DC_UI_GTK #define DC_IF_UI_GTK(...) __VA_ARGS__ #else @@ -71,12 +72,11 @@ enum dc_ws_packet { /* op numbers of websocket packets or json objects in other DC_PING = 1, DC_STRPKTOFF = 100, /* here follow string types (t) */ DC_NONE = DC_STRPKTOFF + 0, /* unknown packet or packet type not yet defermined */ - DC_MESSAGE_CREATE = DC_STRPKTOFF + 0, + DC_MESSAGE_CREATE = DC_STRPKTOFF + 1, }; /* intents enum was removed - intents're for bots, clients have capabilities - are not understood */ char * dc_ws_packet[] = { "", - "MESSAGE_CREATE", - "" + "MESSAGE_CREATE" }; enum dc_message_type { /* other types exist, but are not implemented/understood, same values as server */ DC_MESSAGE = 0, @@ -136,14 +136,19 @@ struct dc_api_io { /* output struct does NOT contain void * data (user pointer) struct dc_lws_pass * pass; unsigned long long int id; }; -#define DC_API_IO_MEMB_GC(m, t) if (m && m->status & DC_IN_PROGRESS) { \ - t##_free(m, DC_UNSET); \ - m = NULL; } \ +#define DC_API_IO_MEMB_GC(m, t) do { \ + if (m && m->status & DC_IN_PROGRESS) { \ + t##_free(m, DC_UNSET); \ + m = NULL; \ + } \ if (m && m->status & DC_EXPLICIT_NULL) { \ m->status &= ~DC_EXPLICIT_NULL; \ m = NULL; \ - } -#define DC_API_IO_GC(s) do { /* this is called if: json_cb will start parsing another before callback, */ \ + } \ + } while (0) +#define DC_API_IO_GC(s) do { /* this is called if: */ \ + if (s.message) \ + DC_API_IO_MEMB_GC(s.message->reply, dc_message); \ DC_API_IO_MEMB_GC(s.message, dc_message); /* WS connection closed, meaning json_cb */ \ DC_API_IO_MEMB_GC(s.channel, dc_channel); /* will never be called again */ \ DC_API_IO_MEMB_GC(s.guild, dc_guild); \ @@ -220,8 +225,8 @@ enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we c DC_JSON_MEMBER_ID, DC_JSON_MEMBER_DISCRIMINATOR, DC_JSON_MEMBER_ROLE, /* id, array */ - DC_JSON_MESSAGE, - DC_JSON_MESSAGE_ATTACHMENTS, + DC_JSON_MESSAGE, /* also check if pass->packet == DC_MESSAGE_CREATE */ + DC_JSON_MESSAGE_ATTACHMENTS, /* not implemented yet */ DC_JSON_MESSAGE_ID, DC_JSON_MESSAGE_TYPE, DC_JSON_MESSAGE_CHANNEL, /* id */ @@ -230,8 +235,8 @@ enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we c DC_JSON_MESSAGE_AUTHOR_USERNAME, DC_JSON_MESSAGE_AUTHOR_DISCRIMINATOR, DC_JSON_MESSAGE_AUTHOR_ID, - DC_JSON_MESSAGE_REFERENCE, /* id */ - DC_JSON_MESSAGE_MENTION_CHANNEL, /* TODO: implement role and user mentions fetching */ + DC_JSON_MESSAGE_REFERENCE, /* id, not implemented, only DC_JSON_MESSAGE_REFOBJ is used */ + DC_JSON_MESSAGE_MENTION_CHANNEL, DC_JSON_MESSAGE_MENTION_CHANNEL_ID, DC_JSON_MESSAGE_MENTION_CHANNEL_GUILD, /* id */ DC_JSON_MESSAGE_MENTION_CHANNEL_NAME, @@ -241,6 +246,7 @@ enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we c DC_JSON_MESSAGE_MENTION_USER_ROLE, DC_JSON_MESSAGE_MENTION_USER_ID, DC_JSON_MESSAGE_MENTION_USER_DISCRIMINATOR, + DC_JSON_MESSAGE_MENTION_ROLE, /* id, array */ DC_JSON_MESSAGE_REFOBJ, /* server only sends reference object one level deep */ DC_JSON_MESSAGE_REFOBJ_ATTACHMENTS, DC_JSON_MESSAGE_REFOBJ_ID, @@ -263,6 +269,7 @@ enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we c DC_JSON_MESSAGE_REFOBJ_MENTION_USER_ROLE, DC_JSON_MESSAGE_REFOBJ_MENTION_USER_ID, DC_JSON_MESSAGE_REFOBJ_MENTION_USER_DISCRIMINATOR, + DC_JSON_MESSAGE_REFOBJ_MENTION_ROLE, /* id */ DC_JSON_PATHS_LENGTH /* we have 256 max length, because that's what library supports */ }; char * dc_json_paths[] = { /* array of paths we are interested in */ @@ -327,11 +334,12 @@ char * dc_json_paths[] = { /* array of paths we are interested in */ "d.mention_channels[].type", "d.mentions[]", "d.mentions[].username", - "d.mentions[].member.roles[]", + "d.mentions[].member.roles", "d.mentions[].id", "d.mentions[].discriminator", + "d.mentions[].mention_roles", "d.referenced_message", - "d.referenced_message.attachments[]", + "d.referenced_message.attachments", "d.referenced_message.id", "d.referenced_message.type", "d.referenced_message.channel_id", @@ -340,7 +348,7 @@ char * dc_json_paths[] = { /* array of paths we are interested in */ "d.referenced_message.author.username", "d.referenced_message.author.discriminator", "d.referenced_message.author.id", - "d.referenced_message.member.roles[]", + "d.referenced_message.member.roles", "d.referenced_message.message_reference", /* this is ID */ "d.referenced_message.mention_channels[]", "d.referenced_message.mention_channels[].id", @@ -349,9 +357,10 @@ char * dc_json_paths[] = { /* array of paths we are interested in */ "d.referenced_message.mention_channels[].type", "d.referenced_message.mentions[]", "d.referenced_message.mentions[].username", - "d.referenced_message.mentions[].member.roles[]", + "d.referenced_message.mentions[].member.roles", "d.referenced_message.mentions[].id", "d.referenced_message.mentions[].discriminator", + "d.referenced_message.mention_roles" }; struct dc_lws_pass { /* struct that is allocated for in dc_lws_cb unique per connection in void * us */ DC_STRUCT_PREFIX @@ -505,14 +514,14 @@ void dc_channel_free (struct dc_channel * s, enum dc_status t) { struct dc_message { DC_STRUCT_PREFIX char * message; /* yesfree */ - char * attachment; /* yesfree - this is a HTTP URL. it would be nice to request file and store to ~/.cache/discord.c/ and only then use (RENDER IMAGE) from disk */ struct dc_channel * channel; /* nofree */ struct dc_user * user; /* nofree */ - time_t time; + time_t time; /* obtained with DC_ID2TIME, edited timestamp is in ISO string format¬ implemt */ unsigned long long int id; struct dc_message * next; /* next message (linked list of all messages of dc_channel) */ struct dc_message * reply; /* nofree - this message replies to another message or NULL */ enum dc_status status; + enum dc_message_type type; DC_ISASQ(user); /* yesfree pointer array only - mentions */ DC_ISASQ(role); /* yesfree pointer array only - mentions */ DC_ISASQ(channel); /* yesfree pointer array only - mentions */ @@ -528,10 +537,9 @@ void dc_message_free (struct dc_message * s, enum dc_status t) { if (!s) return; free(s->message); - free(s->attachment); free(s->users); free(s->roles); - free(s->channel); + free(s->channels); if (!(t & DC_REPLACE)) free(s); } @@ -715,6 +723,7 @@ void dc_program_free (struct dc_program * s, enum dc_status t) { } void dc_api_stack (struct dc_api_io); #define DC_FIND_X(x) struct dc_##x * dc_find_##x(struct dc_##x ** p, size_t l, unsigned long long int id) { \ + assert(id); \ for (size_t i = 0; i < l; i++) \ if (p[i]->id == id) { \ /* fprintf(stderr, "id %llu was found!\n", id); */ /* too much */ \ @@ -735,6 +744,7 @@ void dc_api_stack (struct dc_api_io); } /* untested, not sure if works!!! */ #define DC_ADD_X(x) struct dc_##x * dc_add_##x (struct dc_##x *** p, size_t * so, size_t * l, struct dc_##x * u, enum dc_status s) { \ struct dc_##x * us; \ + assert(u); \ if ((us = dc_find_##x(*p, *l, u->id))) { \ /* fprintf(stderr, "debug: %s found already existing member\n", __func__); */ \ if (us == u) \ @@ -782,6 +792,8 @@ DC_FIND_LL_X(channel) DC_GEN_X(guild, GUILD) DC_GEN_X(role, ROLE) DC_FIND_LL_X(role) +DC_GEN_X(message, MESSAGE) +DC_FIND_LL_X(message) #define DC_ISAE(a) &(a), &(a##_sizeof), &(a##_length) /* ISA Expand */ #define DC_ISAN NULL, NULL, NULL /* ISA NULL */ #define DC_TRANSFER_CHANNEL(n, o) do { /* n is going to DC_REPLACE o, transfer important data */ \ @@ -810,3 +822,8 @@ void dc_transfer_role (struct dc_role * n, struct dc_role * o) { o->users_length = 0; } } +void dc_transfer_message (struct dc_message * n, struct dc_message * o) { + n->next = o->next; + if (!n->reply) + n->reply = o->reply; +} |