@@ -43,7 +43,7 @@ else() | |||
endif() | |||
set(CMAKE_C_STANDARD 99) | |||
add_library(discord lib/discord.c include/discord.h lib/rest.c lib/requests.c include/requests.h lib/log.c include/log.h) | |||
add_library(discord lib/discord.c include/discord.h lib/rest.c lib/requests.c include/requests.h lib/log.c include/log.h lib/utils.c) | |||
target_link_libraries(discord ${JANSSON_LIBRARIES} ${CURL_LIBRARIES}) | |||
if (EXAMPLES) |
@@ -63,7 +63,7 @@ int main(int argc, char **argv) | |||
fprintf(stderr, "Failed to create libdiscord context"); | |||
return -1; | |||
} | |||
/* | |||
printf("%s\n", ld_get_gateway(ctx)); | |||
struct ld_gateway_bot_resp *r = ld_get_gateway_bot(ctx); | |||
@@ -83,12 +83,36 @@ int main(int argc, char **argv) | |||
{ | |||
return -1; | |||
} | |||
*/ | |||
guild_channel_t channel; | |||
ld_get_channel(ctx, "444130990252752898", &channel); | |||
log_trace(channel.guild_id); | |||
struct ld_modify_channel_obj *m = malloc(sizeof(struct ld_modify_channel_obj)); | |||
memset(m, 0, sizeof(struct ld_modify_channel_obj)); | |||
m->name = "основной2"; | |||
m->user_limit = -1; | |||
m->bitrate = -1; | |||
m->nsfw = -1; | |||
m->position = -1; | |||
m->permission_overwrites = calloc(1, sizeof(overwrite_t)); | |||
m->overwrites_size = 1; | |||
m->permission_overwrites[0].id = "472393766989201408"; | |||
m->permission_overwrites[0].type = "member"; | |||
m->permission_overwrites[0].deny = 0; | |||
m->permission_overwrites[0].allow = 2048; | |||
guild_channel_t c; | |||
ld_modify_channel_result(ctx, "444130990252752898", m, &c); | |||
free(m); | |||
return 0; | |||
} |
@@ -8,6 +8,7 @@ | |||
#include <stdbool.h> | |||
#include <curl/curl.h> | |||
#include <jansson.h> | |||
#define LD_API_ENDPOINT "https://discordapp.com/api" | |||
@@ -244,11 +245,28 @@ typedef struct message { | |||
char* webhook_id; | |||
} message_t; | |||
struct ld_modify_channel_obj | |||
{ | |||
char *name; | |||
int position; | |||
char *topic; | |||
int nsfw; | |||
int bitrate; | |||
int user_limit; | |||
overwrite_t *permission_overwrites; | |||
size_t overwrites_size; | |||
char *parent_id; | |||
}; | |||
struct ld_context *ld_create_context(struct ld_context_info *info); | |||
char *ld_get_gateway(struct ld_context *ctx); | |||
struct ld_gateway_bot_resp *ld_get_gateway_bot(struct ld_context *ctx); | |||
int ld_create_message(struct ld_context *ctx, char *channel_id, char *content); | |||
int ld_get_channel(struct ld_context *ctx, char *channel_id, guild_channel_t *target); | |||
int ld_modify_channel_result(struct ld_context *ctx, char *channel_id, struct ld_modify_channel_obj *modify, guild_channel_t *result); | |||
int ld_modify_channel(struct ld_context *ctx, char *channel_id, struct ld_modify_channel_obj *modify); | |||
void ld_channel_obj_to_type(json_t *src, guild_channel_t *channel); | |||
#endif // _DISCORD_H |
@@ -58,6 +58,8 @@ CURLcode requests_post_headers(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc); | |||
CURLcode requests_put_headers(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc); | |||
CURLcode requests_patch(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc, int is_json); | |||
char *requests_url_encode(CURL *curl, char **data, int data_size); | |||
#endif |
@@ -386,6 +386,63 @@ static CURLcode requests_pt(CURL *curl, req_t *req, char *url, char *data, | |||
return rc; | |||
} | |||
// Extension for patch requests | |||
CURLcode requests_patch(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc, int is_json) | |||
{ | |||
CURLcode rc; | |||
struct curl_slist *slist = NULL; | |||
req->url = url; | |||
/* body data */ | |||
if (data != NULL) { | |||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); | |||
} else { | |||
/* content length header defaults to -1, which causes request to fail | |||
sometimes, so we need to manually set it to 0 */ | |||
char *cl_header = "Content-Length: 0"; | |||
slist = curl_slist_append(slist, cl_header); | |||
if (slist == NULL) | |||
return -1; | |||
if (custom_hdrv == NULL) | |||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); | |||
hdrv_append(&req->req_hdrv, &req->req_hdrc, cl_header); | |||
} | |||
/* headers */ | |||
if (custom_hdrv != NULL) { | |||
rc = process_custom_headers(&slist, req, custom_hdrv, custom_hdrc); | |||
if (rc != CURLE_OK) | |||
return rc; | |||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); | |||
} | |||
if (is_json) { | |||
curl_slist_append(slist, "Content-Type: application/json"); | |||
} | |||
common_opt(curl, req); | |||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); | |||
char *ua = user_agent(); | |||
curl_easy_setopt(curl, CURLOPT_USERAGENT, ua); | |||
rc = curl_easy_perform(curl); | |||
if (rc != CURLE_OK) | |||
return rc; | |||
long code; | |||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); | |||
req->code = code; | |||
req->ok = check_ok(code); | |||
if (slist != NULL) | |||
curl_slist_free_all(slist); | |||
free(ua); | |||
curl_easy_cleanup(curl); | |||
return rc; | |||
} | |||
/* | |||
* process_custom_headers - Adds custom headers to request and populates the | |||
* req_headerv and req_hdrc fields of the request struct using the supplied |
@@ -125,6 +125,51 @@ json_t *ld_post_request(struct ld_context *ctx, char *url, json_t *contents) | |||
return res; | |||
} | |||
json_t *ld_patch_request(struct ld_context *ctx, char *url, json_t *contents) | |||
{ | |||
req_t req; | |||
ctx->curl_handle = requests_init(&req); | |||
char *target = malloc(strlen(LD_API_ENDPOINT) + strlen(url) + 1); | |||
char *auth = malloc(strlen("Authorization: Bot ") + strlen(ctx->bot_token) + 1); | |||
sprintf(target, "%s%s", LD_API_ENDPOINT, url); | |||
sprintf(auth, "%s%s", "Authorization: Bot ", ctx->bot_token); | |||
if (!contents) | |||
{ | |||
requests_patch(ctx->curl_handle, &req, target, "", &auth, 1, 1); | |||
} | |||
else | |||
{ | |||
requests_patch(ctx->curl_handle, &req, target, json_dumps(contents, 0), &auth, 1, 1); | |||
} | |||
free(target); | |||
free(auth); | |||
if(!req.ok) | |||
{ | |||
_ld_set_last_rest_error(ctx, &req); | |||
return NULL; | |||
} | |||
json_t *res; | |||
json_error_t err; | |||
res = json_loads(req.text, 0, &err); | |||
requests_close(&req); | |||
if (!res) | |||
{ | |||
log_error("failed to parse json on line %d: %s", err.line, err.text); | |||
return NULL; | |||
} | |||
return res; | |||
} | |||
char *ld_get_gateway(struct ld_context *ctx) | |||
{ | |||
json_t *res = ld_get_request(ctx, "/gateway"); | |||
@@ -232,138 +277,98 @@ int ld_get_channel(struct ld_context *ctx, char *channel_id, guild_channel_t *ch | |||
return -1; | |||
} | |||
log_trace("%s", json_dumps(res, 0)); | |||
ld_channel_obj_to_type(res, channel); | |||
json_t *id = json_object_get(res, "id"); | |||
channel->id = strdup(json_string_value(id)); | |||
json_decref(id); | |||
return 0; | |||
} | |||
json_t *type = json_object_get(res, "type"); | |||
channel->type = (int)json_integer_value(type); | |||
json_decref(type); | |||
int ld_modify_channel(struct ld_context *ctx, char *channel_id, struct ld_modify_channel_obj *modify) | |||
{ | |||
ld_modify_channel_result(ctx, channel_id, modify, NULL); | |||
} | |||
if (channel->type == GUILD_TEXT || channel->type == GUILD_CATEGORY) | |||
int ld_modify_channel_result(struct ld_context *ctx, char *channel_id, struct ld_modify_channel_obj *modify, guild_channel_t *result) | |||
{ | |||
json_t *contents = json_object(); | |||
if (modify->name) | |||
{ | |||
json_t *guild_id = json_object_get(res, "guild_id"); | |||
channel->guild_id = strdup(json_string_value(guild_id)); | |||
json_decref(guild_id); | |||
json_object_set_new(contents, "name", json_string(modify->name)); | |||
} | |||
json_t *permission_overwrites = json_object_get(res, "permission_overwrites"); | |||
size_t osize = json_array_size(permission_overwrites); | |||
if (modify->position != -1) | |||
{ | |||
json_object_set_new(contents, "position", json_integer(modify->position)); | |||
} | |||
channel->permission_overwrites = calloc(osize, sizeof(overwrite_t)); | |||
if (modify->topic) | |||
{ | |||
json_object_set_new(contents, "topic", json_string(modify->topic)); | |||
} | |||
size_t index; | |||
json_t *value; | |||
if (modify->parent_id) | |||
{ | |||
json_object_set_new(contents, "parent_id", json_string(modify->parent_id)); | |||
} | |||
json_array_foreach(permission_overwrites, index, value) | |||
{ | |||
overwrite_t temp; | |||
if (modify->nsfw != -1) | |||
{ | |||
json_object_set_new(contents, "nsfw", json_boolean(modify->nsfw)); | |||
} | |||
temp.id = (char*)json_string_value(json_object_get(value, "id")); | |||
temp.type = (char*)json_string_value(json_object_get(value, "type")); | |||
temp.deny = (int)json_integer_value(json_object_get(value, "deny")); | |||
temp.allow = (int)json_integer_value(json_object_get(value, "allow")); | |||
channel->permission_overwrites[index] = temp; | |||
} | |||
if (modify->bitrate != -1) | |||
{ | |||
json_object_set_new(contents, "bitrate", json_integer(modify->bitrate)); | |||
} | |||
json_decref(permission_overwrites); | |||
if (modify->user_limit != -1) | |||
{ | |||
json_object_set_new(contents, "user_limit", json_integer(modify->user_limit)); | |||
} | |||
json_t *name = json_object_get(res, "name"); | |||
channel->name = strdup(json_string_value(name)); | |||
json_decref(name); | |||
if (channel->type == GUILD_TEXT) | |||
{ | |||
// Topic is nullable | |||
json_t *topic = json_object_get(res, "topic"); | |||
if (json_is_string(topic)) { | |||
channel->topic = strdup(json_string_value(topic)); | |||
} | |||
json_decref(topic); | |||
json_t *nsfw = json_object_get(res, "nsfw"); | |||
channel->nsfw = json_boolean_value(nsfw); | |||
json_decref(nsfw); | |||
json_t *last_message_id = json_object_get(res, "last_message_id"); | |||
if (last_message_id) { | |||
if (json_is_string(last_message_id)) { | |||
channel->last_message_id = strdup(json_string_value(last_message_id)); | |||
} | |||
json_decref(last_message_id); | |||
} | |||
} | |||
if (modify->permission_overwrites && modify->overwrites_size > 0) | |||
{ | |||
json_t *overwrites = json_array(); | |||
json_t *parent_id = json_object_get(res, "parent_id"); | |||
if (json_is_string(parent_id)) | |||
for (int i = 0; i < modify->overwrites_size; i++) | |||
{ | |||
channel->parent_id = strdup(json_string_value(parent_id)); | |||
} | |||
json_decref(parent_id); | |||
json_t *overwrite = json_object(); | |||
json_t *position = json_object_get(res, "position"); | |||
channel->position = (int)json_integer_value(position); | |||
json_decref(position); | |||
} | |||
else if (channel->type == GUILD_VOICE) | |||
{ | |||
json_t *bitrate = json_object_get(res, "bitrate"); | |||
channel->bitrate = (int)json_integer_value(bitrate); | |||
json_decref(bitrate); | |||
json_object_set_new(overwrite, "id", json_string(modify->permission_overwrites[i].id)); | |||
json_object_set_new(overwrite, "type", json_string(modify->permission_overwrites[i].type)); | |||
json_object_set_new(overwrite, "deny", json_integer(modify->permission_overwrites[i].deny)); | |||
json_object_set_new(overwrite, "allow", json_integer(modify->permission_overwrites[i].allow)); | |||
json_t *user_limit = json_object_get(res, "user_limit"); | |||
channel->user_limit = (int)json_integer_value(user_limit); | |||
json_decref(user_limit); | |||
} | |||
else if (channel->type == DM || channel->type == GROUP_DM) | |||
{ | |||
json_array_append_new(overwrites, overwrite); | |||
} | |||
json_t *recipients = json_object_get(res, "recipients"); | |||
size_t rsize = json_array_size(recipients); | |||
json_object_set_new(contents, "permission_overwrites", overwrites); | |||
} | |||
channel->recipients = calloc(rsize, sizeof(user_t)); | |||
char *target = malloc(strlen("/channels/") + strlen(channel_id)); | |||
size_t index; | |||
json_t *value; | |||
sprintf(target, "/channels/%s", channel_id); | |||
json_array_foreach(recipients, index, value) | |||
{ | |||
user_t temp; | |||
// TODO: Create function for converting json user objects to structs | |||
temp.id = (char*)json_string_value(json_object_get(value, "id")); | |||
temp.username = (char*)json_string_value(json_object_get(value, "username")); | |||
temp.discriminator = (char*)json_string_value(json_object_get(value, "discriminator")); | |||
json_t *avatar = json_object_get(value, "avatar"); | |||
if (avatar) | |||
{ | |||
temp.avatar = (char*)json_string_value(avatar); | |||
json_decref(avatar); | |||
} | |||
channel->recipients[index] = temp; | |||
} | |||
json_t *res = ld_patch_request(ctx, target, contents); | |||
json_t *icon = json_object_get(res, "icon"); | |||
if (icon) | |||
{ | |||
if (json_is_string(icon)) | |||
{ | |||
channel->icon = strdup(json_string_value(icon)); | |||
} | |||
json_decref(icon); | |||
} | |||
free(target); | |||
free(contents); | |||
json_t *owner_id = json_object_get(res, "owner_id"); | |||
channel->owner_id = strdup(json_string_value(owner_id)); | |||
json_decref(owner_id); | |||
if (!res) | |||
{ | |||
log_error("probably failed to modify channel"); | |||
return -1; | |||
} | |||
json_t *application_id = json_object_get(res, "application_id"); | |||
if (application_id) | |||
{ | |||
channel->application_id = strdup(json_string_value(application_id)); | |||
json_decref(application_id); | |||
} | |||
if (!result) | |||
{ | |||
json_decref(res); | |||
return 0; | |||
} | |||
ld_channel_obj_to_type(res, result); | |||
return 0; | |||
} |
@@ -0,0 +1,142 @@ | |||
// | |||
// Created by Memer on 25.08.18. | |||
// Copyright (c) 2018 Alexander Memer. All rights reserved. | |||
// | |||
#include <discord.h> | |||
#include <jansson.h> | |||
#include <string.h> | |||
void ld_channel_obj_to_type(json_t *src, guild_channel_t *channel) | |||
{ | |||
json_t *id = json_object_get(src, "id"); | |||
channel->id = strdup(json_string_value(id)); | |||
json_decref(id); | |||
json_t *type = json_object_get(src, "type"); | |||
channel->type = (int)json_integer_value(type); | |||
json_decref(type); | |||
if (channel->type == GUILD_TEXT || channel->type == GUILD_CATEGORY) | |||
{ | |||
json_t *guild_id = json_object_get(src, "guild_id"); | |||
channel->guild_id = strdup(json_string_value(guild_id)); | |||
json_decref(guild_id); | |||
json_t *permission_overwrites = json_object_get(src, "permission_overwrites"); | |||
size_t osize = json_array_size(permission_overwrites); | |||
channel->permission_overwrites = calloc(osize, sizeof(overwrite_t)); | |||
size_t index; | |||
json_t *value; | |||
json_array_foreach(permission_overwrites, index, value) | |||
{ | |||
overwrite_t temp; | |||
temp.id = strdup((char*)json_string_value(json_object_get(value, "id"))); | |||
temp.type = strdup((char*)json_string_value(json_object_get(value, "type"))); | |||
temp.deny = (int)json_integer_value(json_object_get(value, "deny")); | |||
temp.allow = (int)json_integer_value(json_object_get(value, "allow")); | |||
channel->permission_overwrites[index] = temp; | |||
} | |||
json_decref(permission_overwrites); | |||
json_t *name = json_object_get(src, "name"); | |||
channel->name = strdup(json_string_value(name)); | |||
json_decref(name); | |||
if (channel->type == GUILD_TEXT) | |||
{ | |||
// Topic is nullable | |||
json_t *topic = json_object_get(src, "topic"); | |||
if (json_is_string(topic)) { | |||
channel->topic = strdup(json_string_value(topic)); | |||
} | |||
json_decref(topic); | |||
json_t *nsfw = json_object_get(src, "nsfw"); | |||
channel->nsfw = json_boolean_value(nsfw); | |||
json_decref(nsfw); | |||
json_t *last_message_id = json_object_get(src, "last_message_id"); | |||
if (last_message_id) { | |||
if (json_is_string(last_message_id)) { | |||
channel->last_message_id = strdup(json_string_value(last_message_id)); | |||
} | |||
json_decref(last_message_id); | |||
} | |||
} | |||
json_t *parent_id = json_object_get(src, "parent_id"); | |||
if (json_is_string(parent_id)) | |||
{ | |||
channel->parent_id = strdup(json_string_value(parent_id)); | |||
} | |||
json_decref(parent_id); | |||
json_t *position = json_object_get(src, "position"); | |||
channel->position = (int)json_integer_value(position); | |||
json_decref(position); | |||
} | |||
else if (channel->type == GUILD_VOICE) | |||
{ | |||
json_t *bitrate = json_object_get(src, "bitrate"); | |||
channel->bitrate = (int)json_integer_value(bitrate); | |||
json_decref(bitrate); | |||
json_t *user_limit = json_object_get(src, "user_limit"); | |||
channel->user_limit = (int)json_integer_value(user_limit); | |||
json_decref(user_limit); | |||
} | |||
else if (channel->type == DM || channel->type == GROUP_DM) | |||
{ | |||
json_t *recipients = json_object_get(src, "recipients"); | |||
size_t rsize = json_array_size(recipients); | |||
channel->recipients = calloc(rsize, sizeof(user_t)); | |||
size_t index; | |||
json_t *value; | |||
json_array_foreach(recipients, index, value) | |||
{ | |||
user_t temp; | |||
// TODO: Create function for converting json user objects to structs | |||
temp.id = (char*)json_string_value(json_object_get(value, "id")); | |||
temp.username = (char*)json_string_value(json_object_get(value, "username")); | |||
temp.discriminator = (char*)json_string_value(json_object_get(value, "discriminator")); | |||
json_t *avatar = json_object_get(value, "avatar"); | |||
if (avatar) | |||
{ | |||
temp.avatar = (char*)json_string_value(avatar); | |||
json_decref(avatar); | |||
} | |||
channel->recipients[index] = temp; | |||
} | |||
json_t *icon = json_object_get(src, "icon"); | |||
if (icon) | |||
{ | |||
if (json_is_string(icon)) | |||
{ | |||
channel->icon = strdup(json_string_value(icon)); | |||
} | |||
json_decref(icon); | |||
} | |||
json_t *owner_id = json_object_get(src, "owner_id"); | |||
channel->owner_id = strdup(json_string_value(owner_id)); | |||
json_decref(owner_id); | |||
json_t *application_id = json_object_get(src, "application_id"); | |||
if (application_id) | |||
{ | |||
channel->application_id = strdup(json_string_value(application_id)); | |||
json_decref(application_id); | |||
} | |||
} | |||
} |