diff --git a/.gitignore b/.gitignore index cd531cf..da3dabe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.env + # ---> C # Prerequisites *.d @@ -51,4 +53,3 @@ modules.order Module.symvers Mkfile.old dkms.conf - diff --git a/README.md b/README.md index 9e921ce..935124a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # discord.c -Discord API in C \ No newline at end of file +A simple Discord API in C, inspired by discord.py. diff --git a/include/discord.h b/include/discord.h index 36154d8..061f2df 100644 --- a/include/discord.h +++ b/include/discord.h @@ -1,9 +1,24 @@ #ifndef DISCORD_DISCORD_H #define DISCORD_DISCORD_H +#include "error.h" + +// @TODO: use opaque pointer +#include + #define DISCORD_BASE_URL "https://discord.com/api" #define DISCORD_API_VERSION "10" +#define DISCORD_MAX_API_URL 256 +#define DISCORD_MAX_TOKEN_VALUE 128 -typedef struct discord_client discord_client; +typedef struct discord_client { + // char *token_type; // Bot or Bearer + char token_value[DISCORD_MAX_TOKEN_VALUE]; + char api_url[DISCORD_MAX_API_URL]; + CURL *curl; +} discord_client; + +// remember to call curl_global_cleanup() +discord_error discord_init(discord_client *client, const char *token); #endif diff --git a/include/error.h b/include/error.h index f4d50c6..5928585 100644 --- a/include/error.h +++ b/include/error.h @@ -5,6 +5,8 @@ typedef enum discord_error { DISCORD_OK, DISCORD_ERR, DISCORD_AUTH_ERR, + DISCORD_NULL_VALUE, + DISCORD_AUTH_FAILED, } discord_error; /** diff --git a/include/meson.build b/include/meson.build index e69de29..53a90c1 100644 --- a/include/meson.build +++ b/include/meson.build @@ -0,0 +1,5 @@ +headers += files( + 'discord.h', + 'error.h', + 'request.h', +) diff --git a/include/request.h b/include/request.h new file mode 100644 index 0000000..304d4b8 --- /dev/null +++ b/include/request.h @@ -0,0 +1,8 @@ +#ifndef DISCORD_REQUEST_H +#define DISCORD_REQUEST_H + +#include + +int request_auth(CURL *curl, const char *token, const char *api_url); + +#endif diff --git a/meson.build b/meson.build index a7bcdea..5a3a197 100644 --- a/meson.build +++ b/meson.build @@ -7,8 +7,9 @@ project( sources = [] headers = [] -include_directories('src') -include_directories('include') +subdir('src') +subdir('include') + incdir = include_directories('include') libcurl = dependency('libcurl', required: true) diff --git a/src/discord.c b/src/discord.c index d2bef58..96e27e3 100644 --- a/src/discord.c +++ b/src/discord.c @@ -1,6 +1,50 @@ #include "discord.h" +#include "error.h" +#include "request.h" -typedef struct discord_client { - char *token_type; - char *token_value; -} discord_client; +#include +#include +#include + +static int authenticate(discord_client *client) { + int ret; + + ret = request_auth(client->curl, client->token_value, client->api_url); + if (ret != 0) { + return -1; + } + + return 0; +} + +discord_error discord_init(discord_client *client, const char *token) { + if (!client || !token) { + return DISCORD_NULL_VALUE; + } + + int ret; + CURLcode result; + + ret = snprintf(client->api_url, sizeof(client->api_url), "%s/v%s/users/@me", DISCORD_BASE_URL, DISCORD_API_VERSION); + if (ret > DISCORD_MAX_API_URL) { + return DISCORD_ERR; + } + + ret = snprintf(client->token_value, sizeof(client->token_value), "%s", token); + if (ret > DISCORD_MAX_TOKEN_VALUE) { + return DISCORD_ERR; + } + + result = curl_global_init(CURL_GLOBAL_ALL); + if (result != CURLE_OK) { + return DISCORD_ERR; + } + client->curl = curl_easy_init(); + + ret = authenticate(client); + if (ret != 0) { + return DISCORD_AUTH_ERR; + } + + return DISCORD_OK; +} diff --git a/src/error.c b/src/error.c index 6b81fbd..accfa36 100644 --- a/src/error.c +++ b/src/error.c @@ -13,6 +13,8 @@ static discord_err errors[] = { {DISCORD_OK, "OK"}, {DISCORD_ERR, "ERR"}, {DISCORD_AUTH_ERR, "Authentication error, maybe wrong token"}, + {DISCORD_NULL_VALUE, "NULL value passed"}, + {DISCORD_AUTH_FAILED, "Authentication failed"}, }; const char *discord_err_str(discord_error err) { diff --git a/src/meson.build b/src/meson.build index b298885..2637870 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1 +1,5 @@ -sources += files('main.c') +sources += files( + 'discord.c', + 'error.c', + 'request.c', +) diff --git a/src/request.c b/src/request.c new file mode 100644 index 0000000..2e825ce --- /dev/null +++ b/src/request.c @@ -0,0 +1,85 @@ +#include "request.h" + +#include +#include +#include +#include +#include +#include + +#define MAX_AUTH_LEN 256 + +struct response_buffer { + char *data; + size_t size; +}; + +static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) { + size_t realsize = size * nmemb; + struct response_buffer *mem = (struct response_buffer *)userp; + + char *ptr = realloc(mem->data, mem->size + realsize + 1); + if (!ptr) { + printf("not enough memory (realloc returned NULL)\n"); + return 0; + } + + mem->data = ptr; + memcpy(&(mem->data[mem->size]), contents, realsize); + mem->size += realsize; + mem->data[mem->size] = 0; + + return realsize; +} + +int request_auth(CURL *curl, const char *token, const char *api_url) { + if (!curl || !token) { + return -1; + } + + CURLcode result; + struct response_buffer buffer; + memset(&buffer, 0, sizeof(buffer)); + + char auth[MAX_AUTH_LEN]; + snprintf(auth, sizeof(auth), "Authorization: Bot %s", token); + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, auth); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_URL, api_url); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer); + // @TODO: change url and version + curl_easy_setopt(curl, CURLOPT_USERAGENT, "DiscordBot (https://discord.com, 0.1)"); + + result = curl_easy_perform(curl); + if (result != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(result)); + curl_slist_free_all(headers); + free(buffer.data); + + return -1; + } + + // Print response + fprintf(stdout, "[RESPONSE SIZE]: %ld\n", buffer.size); + fprintf(stdout, "[RESPONSE DATA]:\n%s\n", buffer.data); + + // Parse json + yyjson_doc *doc = yyjson_read(buffer.data, buffer.size, 0); + yyjson_val *root = yyjson_doc_get_root(doc); + yyjson_val *username = yyjson_obj_get(root, "username"); + fprintf(stdout, "[USERNAME]: %s\n", yyjson_get_str(username)); + + // Clear resources + yyjson_doc_free(doc); + free(buffer.data); + + curl_slist_free_all(headers); + + return 0; +} diff --git a/test/test1.c b/test/test1.c index 304799a..410bd5b 100644 --- a/test/test1.c +++ b/test/test1.c @@ -1,7 +1,19 @@ +#include "discord.h" +#include "error.h" + #include int main(void) { - puts("discord.c"); + puts("discord.c example"); + + const char token[] = ""; + + discord_client client; + discord_error err = discord_init(&client, token); + if (err != DISCORD_OK) { + fprintf(stderr, "discord_init(): %s\n", discord_err_str(err)); + return -1; + } return 0; }