@@ -0,0 +1,2 @@ | |||
.idea | |||
cmake-build* |
@@ -0,0 +1,51 @@ | |||
cmake_minimum_required(VERSION 3.12) | |||
project(libdiscord C) | |||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") | |||
option(EXAMPLES "Build examples" ON) | |||
# Get the current working branch | |||
execute_process( | |||
COMMAND git rev-parse --abbrev-ref HEAD | |||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | |||
OUTPUT_VARIABLE GIT_BRANCH | |||
OUTPUT_STRIP_TRAILING_WHITESPACE | |||
) | |||
# Get the latest abbreviated commit hash of the working branch | |||
execute_process( | |||
COMMAND git log -1 --format=%h | |||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | |||
OUTPUT_VARIABLE GIT_COMMIT_HASH | |||
OUTPUT_STRIP_TRAILING_WHITESPACE | |||
) | |||
add_definitions("-DGIT_COMMIT_HASH=${GIT_COMMIT_HASH}") | |||
add_definitions("-DGIT_BRANCH=${GIT_BRANCH}") | |||
include_directories(include) | |||
find_package(CURL) | |||
if(NOT CURL_FOUND) | |||
message(SEND_ERROR "Failed to find CURL") | |||
return() | |||
else() | |||
include_directories(${CURL_INCLUDE_DIR}) | |||
endif() | |||
find_package(Jansson) | |||
if (NOT JANSSON_FOUND) | |||
message(SEND_ERROR "Failed to find Jansson") | |||
return() | |||
else() | |||
include_directories(${JANSSON_INCLUDE_DIRS}) | |||
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) | |||
target_link_libraries(discord ${JANSSON_LIBRARIES} ${CURL_LIBRARIES}) | |||
if (EXAMPLES) | |||
add_subdirectory(examples) | |||
endif() |
@@ -0,0 +1,59 @@ | |||
# - Try to find Jansson | |||
# Once done this will define | |||
# | |||
# JANSSON_FOUND - system has Jansson | |||
# JANSSON_INCLUDE_DIRS - the Jansson include directory | |||
# JANSSON_LIBRARIES - Link these to use Jansson | |||
# | |||
# Copyright (c) 2011 Lee Hambley <lee.hambley@gmail.com> | |||
# | |||
# Redistribution and use is allowed according to the terms of the New | |||
# BSD license. | |||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file. | |||
# | |||
if (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) | |||
# in cache already | |||
set(JANSSON_FOUND TRUE) | |||
else (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) | |||
find_path(JANSSON_INCLUDE_DIR | |||
NAMES | |||
jansson.h | |||
PATHS | |||
/usr/include | |||
/usr/local/include | |||
/opt/local/include | |||
/sw/include | |||
) | |||
find_library(JANSSON_LIBRARY | |||
NAMES | |||
jansson | |||
PATHS | |||
/usr/lib | |||
/usr/local/lib | |||
/opt/local/lib | |||
/sw/lib | |||
) | |||
set(JANSSON_INCLUDE_DIRS | |||
${JANSSON_INCLUDE_DIR} | |||
) | |||
if (JANSSON_LIBRARY) | |||
set(JANSSON_LIBRARIES | |||
${JANSSON_LIBRARIES} | |||
${JANSSON_LIBRARY} | |||
) | |||
endif (JANSSON_LIBRARY) | |||
include(FindPackageHandleStandardArgs) | |||
find_package_handle_standard_args(Jansson DEFAULT_MSG | |||
JANSSON_LIBRARIES JANSSON_INCLUDE_DIRS) | |||
# show the JANSSON_INCLUDE_DIRS and JANSSON_LIBRARIES variables only in the advanced view | |||
mark_as_advanced(JANSSON_INCLUDE_DIRS JANSSON_LIBRARIES) | |||
endif (JANSSON_LIBRARIES AND JANSSON_INCLUDE_DIRS) | |||
@@ -0,0 +1,3 @@ | |||
include_directories("${CMAKE_SOURCE_DIR}/include") | |||
add_executable(example main.c) | |||
target_link_libraries(example discord) |
@@ -0,0 +1,94 @@ | |||
// | |||
// Created by Memer on 24.08.18. | |||
// Copyright (c) 2018 Alexander Memer. All rights reserved. | |||
// | |||
#include <discord.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <log.h> | |||
#include <getopt.h> | |||
int main(int argc, char **argv) | |||
{ | |||
struct ld_context_info info; | |||
info.log_level = 0; | |||
while (1) | |||
{ | |||
int c; | |||
static struct option long_options[] = | |||
{ | |||
{"bot-token", required_argument, 0, 't'}, | |||
{"help", no_argument, 0, 'h'}, | |||
{"log-level", required_argument, 0, 'l'}, | |||
{0, 0, 0, 0} | |||
}; | |||
int option_index = 0; | |||
c = getopt_long(argc, argv, "t:hl:", long_options, &option_index); | |||
if (c == -1) | |||
{ | |||
break; | |||
} | |||
switch (c) { | |||
case 'h': | |||
printf("%s: [-t bot_token]\n"); | |||
return 0; | |||
case 't': | |||
info.bot_token = strdup(optarg); | |||
break; | |||
case 'l': | |||
info.log_level = atoi(optarg); | |||
break; | |||
default: | |||
abort(); | |||
} | |||
} | |||
if (!info.bot_token) | |||
{ | |||
log_fatal("no bot token specified"); | |||
return -1; | |||
} | |||
struct ld_context *ctx; | |||
ctx = ld_create_context(&info); | |||
if (ctx == NULL) | |||
{ | |||
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); | |||
if (!r) | |||
{ | |||
printf("Error (%d): %s", ctx->last_rest_status->what, ctx->last_rest_status->text); | |||
return -1; | |||
} | |||
printf("%s:%d\n", r->url, r->shards); | |||
free(r); | |||
int a = ld_create_message(ctx, "444130990252752898", "test"); | |||
if (a) | |||
{ | |||
return -1; | |||
} | |||
guild_channel_t channel; | |||
ld_get_channel(ctx, "444130990252752898", &channel); | |||
log_trace(channel.guild_id); | |||
return 0; | |||
} |
@@ -0,0 +1,246 @@ | |||
// | |||
// Created by Memer on 24.08.18. | |||
// | |||
#ifndef _DISCORD_H | |||
#define _DISCORD_H | |||
#include <stdbool.h> | |||
#include <curl/curl.h> | |||
#define LD_API_ENDPOINT "https://discordapp.com/api" | |||
enum | |||
{ | |||
LD_REST_UNAUTHORIZED = 401, | |||
LD_REST_BAD_REQUEST = 400, | |||
LD_REST_FORBIDDEN = 403, | |||
LD_REST_NOT_FOUND = 404, | |||
LD_REST_METHOD_NOT_ALLOWED = 405, | |||
LD_REST_TOO_MANY_REQUESTS = 429, | |||
LD_REST_GATEWAY_UNAVAILABLE = 502, | |||
LD_REST_SERVER_ERROR = 500, | |||
LD_REST_UNKNOWN = 1 | |||
}; | |||
struct ld_context | |||
{ | |||
// TODO: Websocket stuff | |||
char *bot_token; | |||
char *session_id; | |||
bool hello_triggered; | |||
int heartbeat_interval; | |||
int heartbeat_acks; | |||
int sequence; | |||
struct ld_rest_error *last_rest_status; | |||
CURL *curl_handle; | |||
// ld_user_t self; | |||
}; | |||
struct ld_context_info | |||
{ | |||
char *bot_token; | |||
int log_level; | |||
}; | |||
struct ld_gateway_bot_resp | |||
{ | |||
char *url; | |||
int shards; | |||
}; | |||
struct ld_rest_error | |||
{ | |||
int what; | |||
char *text; | |||
}; | |||
enum game_types { | |||
GAME = 0, | |||
STREAMING = 1 | |||
}; | |||
enum channel_types { | |||
GUILD_TEXT, | |||
DM, | |||
GUILD_VOICE, | |||
GROUP_DM, | |||
GUILD_CATEGORY | |||
}; | |||
enum { | |||
DISPATCH, | |||
HEARTBEAT, | |||
IDENTIFY, | |||
PRESENCE, | |||
VOICE_STATE, | |||
VOICE_PING, | |||
RESUME, | |||
RECONNECT, | |||
REQUEST_MEMBERS, | |||
INVALIDATE_SESSION, | |||
HELLO, | |||
HEARTBEAT_ACK | |||
}; | |||
typedef struct guild_channel { | |||
char *id; | |||
char *guild_id; | |||
char *name; | |||
int type; | |||
int position; | |||
bool nsfw; | |||
// TODO: permission_overwrites | |||
char *topic; | |||
char *last_message_id; | |||
int bitrate; | |||
int user_limit; | |||
// TODO: recipients | |||
char *icon; | |||
char *owner_id; | |||
char *application_id; | |||
char *parent_id; | |||
char *last_pin_timestamp; | |||
} guild_channel_t; | |||
typedef struct thumbnail { | |||
char* url; | |||
char* proxy_url; | |||
int height; | |||
int width; | |||
} thumbnail_t; | |||
typedef struct video { | |||
char* url; | |||
int height; | |||
int width; | |||
} video_t; | |||
typedef struct image { | |||
char* url; | |||
char* proxy_url; | |||
int height; | |||
int width; | |||
} image_t; | |||
typedef struct provider { | |||
char* name; | |||
char* url; | |||
} provider_t; | |||
typedef struct author { | |||
char* name; | |||
char* url; | |||
char* icon_url; | |||
char* proxy_icon_url; | |||
} author_t; | |||
typedef struct footer { | |||
char* text; | |||
char* icon_url; | |||
char* proxy_icon_url; | |||
} footer_t; | |||
typedef struct field { | |||
char* name; | |||
char* value; | |||
int _inline; | |||
} field_t; | |||
typedef struct embed { | |||
char* title; | |||
char* type; | |||
char* description; | |||
char* url; | |||
char* timestamp; | |||
int color; | |||
footer_t footer; | |||
image_t image; | |||
thumbnail_t thumbnail; | |||
video_t video; | |||
provider_t provider; | |||
author_t author; | |||
field_t fields; | |||
} embed_t; | |||
typedef struct user { | |||
char* id; | |||
char* username; | |||
char* discriminator; | |||
char* avatar; | |||
int bot; | |||
int mfa_enabled; | |||
int verified; | |||
char* email; | |||
} user_t; | |||
typedef struct dm_channel { | |||
char *id; | |||
int is_private; | |||
user_t recipient; | |||
char *last_message_id; | |||
} dm_channel_t; | |||
typedef struct game { | |||
char *name; | |||
int type; | |||
char *url; // Only if type is STREAMING (1) | |||
} game_t; | |||
typedef struct presence_update { | |||
user_t user; // Can be partial | |||
game_t game; // May be null | |||
char *guild_id; | |||
char *status; | |||
char *roles[200]; | |||
} presence_update_t; | |||
typedef struct emoji { | |||
char* id; | |||
char* name; | |||
} emoji_t; | |||
typedef struct reaction { | |||
int counter; | |||
int me; | |||
emoji_t _emoji; | |||
} reaction_t; | |||
typedef struct message { | |||
char* id; | |||
char* channel_id; | |||
user_t author; | |||
char* content; | |||
char* timestamp; | |||
char* edited_timestamp; | |||
int tts; | |||
int mention_everyone; | |||
user_t mentions[1024]; | |||
/* | |||
TODO: | |||
mention_roles | |||
attachments | |||
embeds | |||
nonce | |||
*/ | |||
embed_t embeds[1024]; | |||
reaction_t reactions[1024]; | |||
int pinned; | |||
char* webhook_id; | |||
} message_t; | |||
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); | |||
#endif // _DISCORD_H |
@@ -0,0 +1,35 @@ | |||
/** | |||
* Copyright (c) 2017 rxi | |||
* | |||
* This library is free software; you can redistribute it and/or modify it | |||
* under the terms of the MIT license. See `log.c` for details. | |||
*/ | |||
#ifndef LOG_H | |||
#define LOG_H | |||
#include <stdio.h> | |||
#include <stdarg.h> | |||
#define LOG_VERSION "0.1.0" | |||
typedef void (*log_LockFn)(void *udata, int lock); | |||
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; | |||
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) | |||
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) | |||
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) | |||
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) | |||
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) | |||
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) | |||
void log_set_udata(void *udata); | |||
void log_set_lock(log_LockFn fn); | |||
void log_set_fp(FILE *fp); | |||
void log_set_level(int level); | |||
void log_set_quiet(int enable); | |||
void log_log(int level, const char *file, int line, const char *fmt, ...); | |||
#endif |
@@ -0,0 +1,63 @@ | |||
#ifndef REQUESTS_H | |||
#define REQUESTS_H | |||
/* | |||
* requests.h -- librequests: header file | |||
* | |||
* The MIT License (MIT) | |||
* | |||
* Copyright (c) 2014 Mark Mossberg <mark.mossberg@gmail.com> | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#include <assert.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <curl/curl.h> | |||
#include <sys/utsname.h> | |||
#define __LIBREQ_VERS__ "v0.2" | |||
typedef struct { | |||
long code; | |||
char *url; | |||
char *text; | |||
size_t size; | |||
char **req_hdrv; | |||
int req_hdrc; | |||
char **resp_hdrv; | |||
int resp_hdrc; | |||
int ok; | |||
} req_t; | |||
CURL *requests_init(req_t *req); | |||
void requests_close(req_t *req); | |||
CURLcode requests_get(CURL *curl, req_t *req, char *url); | |||
CURLcode requests_post(CURL *curl, req_t *req, char *url, char *data); | |||
CURLcode requests_put(CURL *curl, req_t *req, char *url, char *data); | |||
CURLcode requests_get_headers(CURL *curl, req_t *req, char *url, | |||
char **custom_hdrv, int custom_hdrc); | |||
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); | |||
char *requests_url_encode(CURL *curl, char **data, int data_size); | |||
#endif |
@@ -0,0 +1,45 @@ | |||
// | |||
// Created by Memer on 24.08.18. | |||
// | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <discord.h> | |||
#include <log.h> | |||
struct ld_context *ld_create_context(struct ld_context_info *info) | |||
{ | |||
struct ld_context *ctx; | |||
// Initializing context | |||
ctx = malloc(sizeof(struct ld_context)); | |||
// Initializing CURL handle | |||
if ((ctx->curl_handle = curl_easy_init()) == NULL) | |||
{ | |||
log_fatal("Failed to initialize curl handle"); | |||
return NULL; | |||
} | |||
if (!info->bot_token) | |||
{ | |||
log_fatal("info->bot_token = NULL"); | |||
return NULL; | |||
} | |||
ctx->bot_token = malloc(strlen(info->bot_token) + 1); | |||
strcpy(ctx->bot_token, info->bot_token); | |||
log_trace("ctx->bot_token = %s", ctx->bot_token); | |||
log_set_level(info->log_level); | |||
ctx->hello_triggered = false; | |||
ctx->heartbeat_interval = 0; | |||
ctx->heartbeat_acks = 0; | |||
ctx->sequence = 0; | |||
return ctx; | |||
} |
@@ -0,0 +1,136 @@ | |||
/* | |||
* Copyright (c) 2017 rxi | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to | |||
* deal in the Software without restriction, including without limitation the | |||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |||
* sell copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |||
* IN THE SOFTWARE. | |||
*/ | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <stdarg.h> | |||
#include <string.h> | |||
#include <time.h> | |||
#include <log.h> | |||
static struct { | |||
void *udata; | |||
log_LockFn lock; | |||
FILE *fp; | |||
int level; | |||
int quiet; | |||
} L; | |||
static const char *level_names[] = { | |||
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" | |||
}; | |||
#ifdef LOG_USE_COLOR | |||
static const char *level_colors[] = { | |||
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" | |||
}; | |||
#endif | |||
static void lock(void) { | |||
if (L.lock) { | |||
L.lock(L.udata, 1); | |||
} | |||
} | |||
static void unlock(void) { | |||
if (L.lock) { | |||
L.lock(L.udata, 0); | |||
} | |||
} | |||
void log_set_udata(void *udata) { | |||
L.udata = udata; | |||
} | |||
void log_set_lock(log_LockFn fn) { | |||
L.lock = fn; | |||
} | |||
void log_set_fp(FILE *fp) { | |||
L.fp = fp; | |||
} | |||
void log_set_level(int level) { | |||
L.level = level; | |||
} | |||
void log_set_quiet(int enable) { | |||
L.quiet = enable ? 1 : 0; | |||
} | |||
void log_log(int level, const char *file, int line, const char *fmt, ...) { | |||
if (level < L.level) { | |||
return; | |||
} | |||
/* Acquire lock */ | |||
lock(); | |||
/* Get current time */ | |||
time_t t = time(NULL); | |||
struct tm *lt = localtime(&t); | |||
/* Log to stderr */ | |||
if (!L.quiet) { | |||
va_list args; | |||
char buf[16]; | |||
buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; | |||
#ifdef LOG_USE_COLOR | |||
fprintf( | |||
stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", | |||
buf, level_colors[level], level_names[level], file, line); | |||
#else | |||
fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); | |||
#endif | |||
va_start(args, fmt); | |||
vfprintf(stderr, fmt, args); | |||
va_end(args); | |||
fprintf(stderr, "\n"); | |||
fflush(stderr); | |||
} | |||
/* Log to file */ | |||
if (L.fp) { | |||
va_list args; | |||
char buf[32]; | |||
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; | |||
fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); | |||
va_start(args, fmt); | |||
vfprintf(L.fp, fmt, args); | |||
va_end(args); | |||
fprintf(L.fp, "\n"); | |||
fflush(L.fp); | |||
} | |||
/* Release lock */ | |||
unlock(); | |||
} |
@@ -0,0 +1,486 @@ | |||
/* | |||
* requests.c -- librequests: libcurl wrapper implementation | |||
* | |||
* The MIT License (MIT) | |||
* | |||
* Copyright (c) 2014 Mark Mossberg <mark.mossberg@gmail.com> | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be included in | |||
* all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
* THE SOFTWARE. | |||
*/ | |||
#include "requests.h" | |||
static int IS_FIRST = 1; | |||
/* | |||
* Prototypes | |||
*/ | |||
static void common_opt(CURL *curl, req_t *req); | |||
static char *user_agent(void); | |||
static int check_ok(long code); | |||
static CURLcode requests_pt(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc, int put_flag); | |||
static int hdrv_append(char ***hdrv, int *hdrc, char *new); | |||
static CURLcode process_custom_headers(struct curl_slist **slist, | |||
req_t *req, char **custom_hdrv, | |||
int custom_hdrc); | |||
/* | |||
* requests_init - Initializes requests struct data members | |||
* | |||
* Returns libcurl handle on success, or NULL on failure. | |||
* | |||
* @req: reference to req_t to be initialized | |||
*/ | |||
CURL *requests_init(req_t *req) | |||
{ | |||
/* if this is not their first request, free previous memory */ | |||
if (!IS_FIRST) | |||
requests_close(req); | |||
req->code = 0; | |||
req->url = NULL; | |||
req->size = 0; | |||
req->req_hdrc = 0; | |||
req->resp_hdrc = 0; | |||
req->ok = -1; | |||
req->text = calloc(1, 1); | |||
if (req->text == NULL) | |||
return NULL; | |||
req->req_hdrv = calloc(1, 1); | |||
if (req->req_hdrv == NULL) | |||
return NULL; | |||
req->resp_hdrv = calloc(1, 1); | |||
if (req->resp_hdrv == NULL) | |||
return NULL; | |||
IS_FIRST = 0; | |||
return curl_easy_init(); | |||
} | |||
/* | |||
* requests_close - Calls curl clean up and free allocated memory | |||
* | |||
* @req: requests struct | |||
*/ | |||
void requests_close(req_t *req) | |||
{ | |||
for (int i = 0; i < req->resp_hdrc; i++) | |||
free(req->resp_hdrv[i]); | |||
for (int i = 0; i < req->req_hdrc; i++) | |||
free(req->req_hdrv[i]); | |||
free(req->text); | |||
free(req->resp_hdrv); | |||
free(req->req_hdrv); | |||
IS_FIRST = 1; | |||
} | |||
/* | |||
* resp_callback - Callback function for requests, may be called multiple | |||
* times per request. Allocates memory and assembles response data. | |||
* | |||
* Note: `content' will not be NULL terminated. | |||
*/ | |||
static size_t resp_callback(char *content, size_t size, size_t nmemb, | |||
req_t *userdata) | |||
{ | |||
size_t real_size = size * nmemb; | |||
/* extra 1 is for NULL terminator */ | |||
userdata->text = realloc(userdata->text, userdata->size + real_size + 1); | |||
if (userdata->text == NULL) | |||
return -1; | |||
userdata->size += real_size; | |||
/* create NULL terminated version of `content' */ | |||
char *responsetext = strndup(content, real_size + 1); | |||
if (responsetext == NULL) | |||
return -1; | |||
strncat(userdata->text, responsetext, real_size); | |||
free(responsetext); | |||
return real_size; | |||
} | |||
/* | |||
* header_callback - Callback function for headers, called once for each | |||
* header. Allocates memory and assembles headers into string array. | |||
* | |||
* Note: `content' will not be NULL terminated. | |||
*/ | |||
static size_t header_callback(char *content, size_t size, size_t nmemb, | |||
req_t *userdata) | |||
{ | |||
size_t real_size = size * nmemb; | |||
/* the last header is always "\r\n" which we'll intentionally skip */ | |||
if (strcmp(content, "\r\n") == 0) | |||
return real_size; | |||
if (hdrv_append(&userdata->resp_hdrv, &userdata->resp_hdrc, content)) | |||
return -1; | |||
return real_size; | |||
} | |||
/* | |||
* requests_get - Performs GET request and populates req struct text member | |||
* with request response, code with response code, and size with size of | |||
* response. | |||
* | |||
* Returns the CURLcode return code provided from curl_easy_perform. CURLE_OK | |||
* is returned on success. | |||
* | |||
* @curl: libcurl handle | |||
* @req: request struct | |||
* @url: url to send request to | |||
*/ | |||
CURLcode requests_get(CURL *curl, req_t *req, char *url) | |||
{ | |||
CURLcode rc; | |||
char *ua = user_agent(); | |||
req->url = url; | |||
long code; | |||
common_opt(curl, req); | |||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_callback); | |||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); | |||
curl_easy_setopt(curl, CURLOPT_USERAGENT, ua); | |||
rc = curl_easy_perform(curl); | |||
if (rc != CURLE_OK) | |||
return rc; | |||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); | |||
req->code = code; | |||
req->ok = check_ok(code); | |||
curl_easy_cleanup(curl); | |||
free(ua); | |||
return rc; | |||
} | |||
/* | |||
* requests_get_headers - Performs GET request (same as above) but allows | |||
* custom headers. | |||
* | |||
* Returns the CURLcode return code provided from curl_easy_perform. CURLE_OK | |||
* is returned on success. | |||
* | |||
* @curl: libcurl handle | |||
* @req: request struct | |||
* @url: url to send request to | |||
* @custom_hdrv: char* array of custom headers | |||
* @custom_hdrc: length of `custom_hdrv` | |||
*/ | |||
CURLcode requests_get_headers(CURL *curl, req_t *req, char *url, | |||
char **custom_hdrv, int custom_hdrc) | |||
{ | |||
CURLcode rc; | |||
struct curl_slist *slist = NULL; | |||
char *ua = user_agent(); | |||
req->url = url; | |||
long code; | |||
/* 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); | |||
} | |||
common_opt(curl, req); | |||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_callback); | |||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); | |||
curl_easy_setopt(curl, CURLOPT_USERAGENT, ua); | |||
rc = curl_easy_perform(curl); | |||
if (rc != CURLE_OK) | |||
return rc; | |||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); | |||
req->code = code; | |||
if (slist != NULL) | |||
curl_slist_free_all(slist); | |||
req->ok = check_ok(code); | |||
curl_easy_cleanup(curl); | |||
free(ua); | |||
return rc; | |||
} | |||
/* | |||
* requests_url_encode - Url encoding function. Takes as input an array of | |||
* char strings and the size of the array. The array should consist of keys | |||
* and the corresponding value immediately after in the array. There must be | |||
* an even number of array elements (one value for every key). | |||
* | |||
* Returns pointer to url encoded string if successful, or NULL if | |||
* unsuccessful. | |||
* | |||
* @curl: libcurl handle | |||
* @data: char* array as described above | |||
* @data-size: length of array | |||
*/ | |||
char *requests_url_encode(CURL *curl, char **data, int data_size) | |||
{ | |||
char *key, *val, *tmp; | |||
int offset; | |||
size_t term_size; | |||
size_t tmp_len; | |||
if (data_size % 2 != 0) | |||
return NULL; | |||
/* loop through and get total sum of lengths */ | |||
size_t total_size = 0; | |||
for (int i = 0; i < data_size; i++) { | |||
tmp = data[i]; | |||
tmp_len = strlen(tmp); | |||
total_size += tmp_len; | |||
} | |||
char encoded[total_size]; /* clear junk bytes */ | |||
snprintf(encoded, total_size, "%s", ""); | |||
/* loop in groups of two, assembling key/val pairs */ | |||
for (int i = 0; i < data_size; i+=2) { | |||
key = data[i]; | |||
val = data[i+1]; | |||
offset = i == 0 ? 2 : 3; /* =, \0 and maybe & */ | |||
term_size = strlen(key) + strlen(val) + offset; | |||
char term[term_size]; | |||
if (i == 0) | |||
snprintf(term, term_size, "%s=%s", key, val); | |||
else | |||
snprintf(term, term_size, "&%s=%s", key, val); | |||
strncat(encoded, term, strlen(term)); | |||
} | |||
char *full_encoded = curl_easy_escape(curl, encoded, strlen(encoded)); | |||
return full_encoded; | |||
} | |||
CURLcode requests_post(CURL *curl, req_t *req, char *url, char *data) | |||
{ | |||
return requests_pt(curl, req, url, data, NULL, 0, 0); | |||
} | |||
CURLcode requests_put(CURL *curl, req_t *req, char *url, char *data) | |||
{ | |||
return requests_pt(curl, req, url, data, NULL, 0, 1); | |||
} | |||
CURLcode requests_post_headers(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc) | |||
{ | |||
return requests_pt(curl, req, url, data, custom_hdrv, custom_hdrc, 0); | |||
} | |||
CURLcode requests_put_headers(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc) | |||
{ | |||
return requests_pt(curl, req, url, data, custom_hdrv, custom_hdrc, 1); | |||
} | |||
/* | |||
* requests_pt - Performs POST or PUT request using supplied data and populates | |||
* req struct text member with request response, code with response code, and | |||
* size with size of response. To submit no data, use NULL for data, and 0 for | |||
* data_size. | |||
* | |||
* Returns CURLcode provided from curl_easy_perform. CURLE_OK is returned on | |||
* success. -1 returned if there are issues with libcurl's internal linked list | |||
* append. | |||
* | |||
* Typically this function isn't used directly, use requests_post() or | |||
* requests_put() instead. | |||
* | |||
* @curl: libcurl handle | |||
* @req: request struct | |||
* @url: url to send request to | |||
* @data: url encoded data to send in request body | |||
* @custom_hdrv: char* array of custom headers | |||
* @custom_hdrc: length of `custom_hdrv` | |||
* @put_flag: if not zero, sends PUT request, otherwise uses POST | |||
*/ | |||
static CURLcode requests_pt(CURL *curl, req_t *req, char *url, char *data, | |||
char **custom_hdrv, int custom_hdrc, int put_flag) | |||
{ | |||
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); | |||
} | |||
common_opt(curl, req); | |||
if (put_flag) | |||
/* use custom request instead of dedicated PUT, because dedicated | |||
PUT doesn't work with arbitrary request body data */ | |||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); | |||
else | |||
curl_easy_setopt(curl, CURLOPT_POST, 1); | |||
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 | |||
* custom headers. | |||
* | |||
* Returns CURLE_OK on success, CURLE_OUT_OF_MEMORY if realloc failed to | |||
* increase size of req_hdrv, or -1 if libcurl's linked list append fails. | |||
* | |||
* @slist: internal libcurl slist (string linked list) | |||
* @req: request struct | |||
* @custom_hdrv: char* array of custom headers | |||
* @custom_hdrc: length of `custom_hdrv` | |||
*/ | |||
static CURLcode process_custom_headers(struct curl_slist **slist, req_t *req, | |||
char **custom_hdrv, int custom_hdrc) | |||
{ | |||
for (int i = 0; i < custom_hdrc; i++) { | |||
/* add header to request */ | |||
*slist = curl_slist_append(*slist, custom_hdrv[i]); | |||
if (*slist == NULL) | |||
return -1; | |||
if (hdrv_append(&req->req_hdrv, &req->req_hdrc, custom_hdrv[i])) | |||
return CURLE_OUT_OF_MEMORY; | |||
} | |||
return CURLE_OK; | |||
} | |||
/* | |||
* hdrv_append - Appends to an arbitrary char* array and increments the given | |||
* array length. | |||
* | |||
* Returns 0 on success and -1 on memory error. | |||
* | |||
* @hdrv: pointer to the char* array | |||
* @hdrc: length of `hdrv' (NOTE: this value gets updated) | |||
* @new: char* to append | |||
*/ | |||
static int hdrv_append(char ***hdrv, int *hdrc, char *new) | |||
{ | |||
/* current array size in bytes */ | |||
size_t current_size = *hdrc * sizeof(char*); | |||
char *newdup = strndup(new, strlen(new)); | |||
if (newdup == NULL) | |||
return -1; | |||
*hdrv = realloc(*hdrv, current_size + sizeof(char*)); | |||
if (*hdrv == NULL) | |||
return -1; | |||
(*hdrc)++; | |||
(*hdrv)[*hdrc - 1] = newdup; | |||
return 0; | |||
} | |||
/* | |||
* common_opt - Sets common libcurl options. | |||
* | |||
* @curl: libcurl handle | |||
* @req: request struct | |||
*/ | |||
static void common_opt(CURL *curl, req_t *req) | |||
{ | |||
curl_easy_setopt(curl, CURLOPT_URL, req->url); | |||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_callback); | |||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); | |||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); | |||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, req); | |||
} | |||
/* | |||
* user_agent - Creates custom user agent. | |||
* | |||
* Returns a char* containing the user agent, or NULL on failure. | |||
*/ | |||
static char *user_agent(void) | |||
{ | |||
struct utsname name; | |||
uname(&name); | |||
char *kernel = name.sysname; | |||
char *version = name.release; | |||
char *ua; | |||
asprintf(&ua, "librequests/%s %s/%s", __LIBREQ_VERS__, kernel, version); | |||
return ua; | |||
} | |||
/* | |||
* check_ok - Utility function for setting "ok" struct field. Response codes | |||
* of 400+ are considered "not ok". | |||
* | |||
* @req: request struct | |||
*/ | |||
static int check_ok(long code) | |||
{ | |||
if (code >= 400 || code == 0) | |||
return 0; | |||
else | |||
return 1; | |||
} |
@@ -0,0 +1,325 @@ | |||
// | |||
// Created by Memer on 24.08.18. | |||
// Copyright (c) 2018 Alexander Memer. All rights reserved. | |||
// | |||
#include <discord.h> | |||
#include <requests.h> | |||
#include <jansson.h> | |||
#include <log.h> | |||
void _ld_set_last_rest_error(struct ld_context *ctx, req_t *req) | |||
{ | |||
log_warn("_ld_set_last_rest_error triggered"); | |||
ctx->last_rest_status = malloc(sizeof(struct ld_rest_error)); | |||
ctx->last_rest_status->what = (int)req->code; | |||
json_error_t err; | |||
json_t *res = json_loads(req->text, 0, &err); | |||
if (!res) | |||
{ | |||
log_error("failed to parse json at line %d: %s", err.line, err.text); | |||
ctx->last_rest_status->text = ""; | |||
json_decref(res); | |||
return; | |||
} | |||
json_t *message = json_object_get(res, "message"); | |||
if (!json_is_string(message)) | |||
{ | |||
log_error("message is not a string"); | |||
ctx->last_rest_status->text = ""; | |||
json_decref(res); | |||
return; | |||
} | |||
char *_message = (char*)json_string_value(message); | |||
ctx->last_rest_status->text = malloc(strlen(_message) + 1); | |||
strcpy(ctx->last_rest_status->text, _message); | |||
} | |||
json_t *ld_get_request(struct ld_context *ctx, char *url) | |||
{ | |||
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); | |||
requests_get_headers(ctx->curl_handle, &req, target, &auth, 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; | |||
} | |||
json_t *ld_post_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_post_headers(ctx->curl_handle, &req, target, "", &auth, 1); | |||
} | |||
else | |||
{ | |||
requests_post_headers(ctx->curl_handle, &req, target, json_dumps(contents, 0), &auth, 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"); | |||
if (!res) | |||
{ | |||
log_error("failed to get gateway url"); | |||
return NULL; | |||
} | |||
json_t *gateway = json_object_get(res, "url"); | |||
if (!json_is_string(gateway)) | |||
{ | |||
log_error("failed to parse url of gateway"); | |||
json_decref(res); | |||
return NULL; | |||
} | |||
char *url = strdup(json_string_value(gateway)); | |||
json_decref(gateway); | |||
json_decref(res); | |||
return url; | |||
} | |||
struct ld_gateway_bot_resp *ld_get_gateway_bot(struct ld_context *ctx) | |||
{ | |||
json_t *res = ld_get_request(ctx, "/gateway/bot"); | |||
if (!res) | |||
{ | |||
log_error("failed to get gateway url"); | |||
return NULL; | |||
} | |||
struct ld_gateway_bot_resp *r; | |||
r = malloc(sizeof(struct ld_gateway_bot_resp)); | |||
json_t *gateway = json_object_get(res, "url"); | |||
if (!json_is_string(gateway)) | |||
{ | |||
log_error("failed to parse json"); | |||
json_decref(res); | |||
return NULL; | |||
} | |||
json_t *shards = json_object_get(res, "shards"); | |||
if (!json_is_integer(shards)) | |||
{ | |||
log_error("failed to parse shards (not integer?)"); | |||
json_decref(res); | |||
return NULL; | |||
} | |||
r->url = strdup(json_string_value(gateway)); | |||
r->shards = (int)json_integer_value(shards); | |||
json_decref(gateway); | |||
json_decref(shards); | |||
json_decref(res); | |||
return r; | |||
} | |||
int ld_create_message(struct ld_context *ctx, char *channel_id, char *content) | |||
{ | |||
char *target = malloc(39); | |||
sprintf(target, "/channels/%s/messages", channel_id); | |||
json_t *contents = json_object(); | |||
json_t *_content = json_string(content); | |||
json_object_set_new(contents, "content", _content); | |||
json_t *res = ld_post_request(ctx, target, contents); | |||
free(target); | |||
if (json_object_size(res) <= 0) | |||
{ | |||
log_warn("probably failed to create message"); | |||
return -1; | |||
} | |||
json_decref(res); | |||
return 0; | |||
} | |||
int ld_get_channel(struct ld_context *ctx, char *channel_id, guild_channel_t *channel) | |||
{ | |||
char *target = malloc(28); | |||
sprintf(target, "/channels/%s", channel_id); | |||
json_t *res = ld_get_request(ctx, target); | |||
free(target); | |||
if (json_object_size(res) <= 0) | |||
{ | |||
log_warn("probably failed to get channel"); | |||
return -1; | |||
} | |||
log_trace("%s", json_dumps(res, 0)); | |||
json_t *id = json_object_get(res, "id"); | |||
channel->id = strdup(json_string_value(id)); | |||
json_decref(id); | |||
json_t *type = json_object_get(res, "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(res, "guild_id"); | |||
channel->guild_id = strdup(json_string_value(guild_id)); | |||
json_decref(guild_id); | |||
// TODO: permission_overwrites | |||
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); | |||
} | |||
} | |||
json_t *parent_id = json_object_get(res, "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(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_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) | |||
{ | |||
// TODO: recipients | |||
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); | |||
} | |||
json_t *owner_id = json_object_get(res, "owner_id"); | |||
channel->owner_id = strdup(json_string_value(owner_id)); | |||
json_decref(owner_id); | |||
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); | |||
} | |||
} | |||
return 0; | |||
} |