From 037d81b4d2072fdf4cf2b0f30bc65c6c590ead82 Mon Sep 17 00:00:00 2001 From: sijanec Date: Wed, 10 Mar 2021 23:22:14 +0100 Subject: initial commit - TODO: receiving messages --- .gitignore | 4 + Makefile | 7 ++ src/i18n.h | 10 +++ src/lib.c | 26 ++++++ src/main.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 318 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/i18n.h create mode 100644 src/lib.c create mode 100644 src/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6097dbc --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +lib/cJSON.h +lib/cJSON.c +# the following is the binary +discord.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d6b3cc9 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +default: + gcc -Ilib -Isrc -I. src/main.c -lcurl -odiscord.c +prepare: + wget https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.c -O lib/cJSON.c + wget https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.h -O lib/cJSON.h +clean: + rm lib/cJSON.c lib/cJSON.h diff --git a/src/i18n.h b/src/i18n.h new file mode 100644 index 0000000..8232538 --- /dev/null +++ b/src/i18n.h @@ -0,0 +1,10 @@ +#define DC_I18N_FAILED "neuspel" +#define DC_I18N_UNREC_ARG "neprepoznan argument %c, poskusi -h" +#define DC_I18N_USAGE "uporaba: %s -e naslov@example -p geslo" +#define DC_I18N_ARG_ALREADY_SET "argument %c že ima nastavljeno vrednost (mogoče okoljske spremenljivke)" +#define DC_I18N_MISSING_EP "manjka poštni naslov in/ali geslo" +#define DC_I18N_LOGGED_IN "prijavljeni ste kot %s" +#define DC_I18N_LOGIN_FAILED "prijava neuspela. prijavite se v brskalniku. strežnik je odgovoril: %s" +#define DC_I18N_NOT_JOINED "preden lahko pišeš, se moraš pridružiti kanalu" +#define DC_I18N_GUILD_NOT_SET "skupina ni izbrana!" +#define DC_I18N_CHANNEL_NOT_SET "kanal ni izbran!" diff --git a/src/lib.c b/src/lib.c new file mode 100644 index 0000000..b6a5f3a --- /dev/null +++ b/src/lib.c @@ -0,0 +1,26 @@ +struct writefunc_string { + char *ptr; + size_t len; +}; +void init_writefunc_string(struct writefunc_string *s) { + s->len = 0; + s->ptr = malloc(s->len+1); + if (s->ptr == NULL) { + fprintf(stderr, "malloc() " DC_I18N_FAILED "\n"); + exit(EXIT_FAILURE); + } + s->ptr[0] = '\0'; +} +size_t writefunc(void *ptr, size_t size, size_t nmemb, struct writefunc_string *s) { + size_t new_len = s->len + size*nmemb; + s->ptr = realloc(s->ptr, new_len+1); + if (s->ptr == NULL) { + fprintf(stderr, "realloc() " DC_I18N_FAILED "\n"); + exit(EXIT_FAILURE); + } + memcpy(s->ptr+s->len, ptr, size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return size*nmemb; +} + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ed6a677 --- /dev/null +++ b/src/main.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define DC_API_PREFIX "https://discord.com/api/v8/" +#define DC_LOGIN_FORMAT "{\"login\":\"%s\",\"password\":\"%s\",\"undelete\":false,\"captcha_key\":null,\"login_source\":null,\"gift_code_sku_id\":null}" +#define DC_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" +#define DC_DEBUG 0 +#define DC_COMMAND_LINE_LENGTH 2000 +#define DC_COMMAND_CHAR '/' +int main(int argc, char ** argv) { + srand(time(NULL)); + int returnstatus = 0; + int opt; + cJSON * guilds = 0; + char * email = getenv("DC_E"); + char * password = getenv("DC_P"); + while ((opt = getopt(argc, argv, "e:p:h")) != -1) { + switch (opt) { + case 'h': + fprintf(stdout, DC_I18N_USAGE); + break; + case 'e': + if (!email) { + email = malloc(strlen(optarg)+1); + strcpy(email, optarg); + } else + fprintf(stderr, DC_I18N_ARG_ALREADY_SET "\n", opt); + break; + case 'p': + if (!password) { + password = malloc(strlen(optarg)+1); + strcpy(password, optarg); + } else + fprintf(stderr, DC_I18N_ARG_ALREADY_SET "\n", opt); + break; + default: + fprintf(stderr, DC_I18N_UNREC_ARG "\n", opt); + } + } + if (!email || !password) { + fprintf(stderr, DC_I18N_MISSING_EP "\n"); + free(email); email = NULL; + free(password); password = NULL; + return 1; + } + CURL *curl; + CURLcode res; + curl = curl_easy_init(); + if (!curl) { + fprintf(stderr, "curl_easy_init() " DC_I18N_FAILED ".\n"); + return 1; + } + struct writefunc_string s; + init_writefunc_string(&s); + + char * data = malloc(sizeof(DC_LOGIN_FORMAT)+strlen(email)+strlen(password)); + sprintf(data, DC_LOGIN_FORMAT, email, password); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, "User-Agent: " DC_USER_AGENT); + curl_easy_setopt(curl, CURLOPT_URL, DC_API_PREFIX "auth/login"); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() " DC_I18N_FAILED ": %s\n", curl_easy_strerror(res)); + cJSON * json = cJSON_Parse(s.ptr); + cJSON * token = cJSON_GetObjectItemCaseSensitive(json, "token"); + char * authorization = NULL; + if (cJSON_IsString(token) && (token->valuestring != NULL)) { + authorization = malloc(strlen(token->valuestring)+strlen("authorization: ")+1); + strcpy(authorization, "Authorization: "); + strcat(authorization, token->valuestring); + if(DC_DEBUG) fprintf(stderr, "d: %s\n", s.ptr); + cJSON_Delete(json); + } else { + fprintf(stderr, DC_I18N_LOGIN_FAILED "\n", s.ptr); + cJSON_Delete(json); + returnstatus = 3; + free(s.ptr); s.ptr = NULL; + goto returncleanly; + } + free(s.ptr); s.ptr = NULL; + init_writefunc_string(&s); + curl_easy_setopt(curl, CURLOPT_URL, DC_API_PREFIX "users/@me"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + headers = curl_slist_append(headers, authorization); + res = curl_easy_perform(curl); + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() " DC_I18N_FAILED ": %s\n", curl_easy_strerror(res)); + json = cJSON_Parse(s.ptr); + token = cJSON_GetObjectItemCaseSensitive(json, "username"); + cJSON * token2 = cJSON_GetObjectItemCaseSensitive(json, "discriminator"); + char * handle = NULL; + if (cJSON_IsString(token) && (token->valuestring != NULL) + && cJSON_IsString(token2) && (token2->valuestring != NULL)) { + handle = malloc(strlen(token->valuestring)+1+strlen(token2->valuestring)+1); + strcpy(handle, token->valuestring); + strcat(handle, "#"); + strcat(handle, token2->valuestring); + fprintf(stdout, DC_I18N_LOGGED_IN "\n", handle); + } else { + fprintf(stderr, DC_I18N_LOGIN_FAILED "\n", s.ptr); + cJSON_Delete(json); + returnstatus = 4; + free(s.ptr); s.ptr = NULL; + goto returncleanly; + } + free(s.ptr); s.ptr = NULL; + /* start command listener */ + char * fgetsret = NULL; + char cmd[DC_COMMAND_LINE_LENGTH+1]; + long long int setguild = 0; + long long int setchannel = 0; + char * joinedchannelname = NULL; + char * joinedchannelid = NULL; + while (1) { + fprintf(stderr, "%s%s> ", joinedchannelname ? "#" : "", joinedchannelname ? joinedchannelname : ""); + fflush(stderr); + fgetsret = fgets(cmd, DC_COMMAND_LINE_LENGTH, stdin); + if (fgetsret == NULL) { + fprintf(stderr, "fgets() " DC_I18N_FAILED "\n"); + returnstatus = 5; + goto returncleanly; + break; + } + if (cmd[0] == DC_COMMAND_CHAR) { + switch (cmd[1]) { + case 'g': + case 'G': + case 's': /* /guilds */ + case 'S': /* /guilds force-update */ + if (!guilds || (strchr(cmd, ' ') ? (strchr(cmd, ' ')+1)[0] == 'f' : 0)) { +#define DC_API_METHOD_GET(body) curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L) +#define DC_API_METHOD_POST(body) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body) +#define DC_API(endpoint, method, body, output) do {\ + curl_easy_setopt(curl, CURLOPT_URL, endpoint); \ + DC_API_METHOD_##method(body); \ + free(s.ptr); s.ptr = NULL; \ + init_writefunc_string(&s); \ + res = curl_easy_perform(curl); \ + if (res != CURLE_OK) \ + fprintf(stderr, "curl_easy_perform() " DC_I18N_FAILED ": %s\n", curl_easy_strerror(res)); \ + output == NULL ? cJSON_Delete(output):rand(); \ + output = cJSON_Parse(s.ptr); } while (0) + DC_API(DC_API_PREFIX "users/@me/guilds", GET, "", guilds); + } + int i = 0; + cJSON * guild = NULL; + cJSON_ArrayForEach(guild, guilds) { + token = cJSON_GetObjectItem(guild, "name"); + fprintf(stdout, " %d. %s\n", i++, token->valuestring); + } + break; + case 'c': + case 'C': + case 'k': /* /channels [force-update] */ + case 'K': + if (strchr(cmd, ' ') == NULL) { + fprintf(stderr, DC_I18N_GUILD_NOT_SET "\n"); + break; + } + setguild = strtoll(strchr(cmd, ' ')+1, NULL, 10); + cJSON_ArrayForEach(guild, guilds) { + if (setguild-- == 0) { + cJSON * guildchannels = cJSON_GetObjectItem(guild, "channels"); + if (!cJSON_IsObject(guildchannels) || + strchr(strchr(cmd,' ')+1,' ') ? 1 : 0) { +#define DC_UPDATE_CHANNELS(guild) do {\ + token = cJSON_GetObjectItem(guild, "id"); \ + char * tempstring = malloc(strlen(token->valuestring)+strlen("guilds//channels")+1+ strlen(DC_API_PREFIX)); \ + sprintf(tempstring, DC_API_PREFIX "guilds/%s/channels", token->valuestring); \ + guildchannels = cJSON_CreateObject(); \ + DC_API(tempstring, GET, "", guildchannels); \ + cJSON_AddItemToObject(guild, "channels", guildchannels); \ + free(tempstring); tempstring = NULL; } while (0) + DC_UPDATE_CHANNELS(guild); + } + setguild = 0; + cJSON * guildchannel = NULL; + cJSON_ArrayForEach(guildchannel, guildchannels) { + token = cJSON_GetObjectItem(guildchannel, "name"); + token2 = cJSON_GetObjectItem(guildchannel, "topic"); + cJSON * type = cJSON_GetObjectItem(guildchannel, "type"); + if (type->valuedouble == 0) /* samo za besedilne kanale */ + fprintf(stdout, " %lld. #%s%s%s\n", setguild++, token->valuestring, token2->valuestring ? " - " : "", token2->valuestring ? token2->valuestring : ""); + } + break; + } + } + break; + case 'j': + case 'J': + case 'p': + case 'P': + if (strchr(cmd, ' ') == NULL) { + fprintf(stderr, DC_I18N_GUILD_NOT_SET "\n"); + break; + } + if (strchr(strchr(cmd, ' ')+1, ' ') == NULL) { + fprintf(stderr, DC_I18N_CHANNEL_NOT_SET "\n"); + break; + } + setguild = strtoll(strchr(cmd, ' ')+1, NULL, 10); + setchannel = strtoll(strchr(strchr(cmd, ' ')+1, ' ')+1, NULL, 10); + if (!guilds) + DC_API(DC_API_PREFIX "users/@me/guilds", GET, "", guilds); + cJSON_ArrayForEach(guild, guilds) { + if (setguild-- == 0) { + cJSON * guildchannels = cJSON_GetObjectItem(guild, "channels"); + if (!cJSON_IsArray(guildchannels)) + DC_UPDATE_CHANNELS(guild); + cJSON * guildchannel = NULL; + cJSON_ArrayForEach(guildchannel, guildchannels) { + cJSON * type = cJSON_GetObjectItem(guildchannel, "type"); + if (type->valuedouble != 0) setchannel++; + if (setchannel-- == 0) { + joinedchannelid = cJSON_GetObjectItem(guildchannel, "id")->valuestring; + joinedchannelname = cJSON_GetObjectItem(guildchannel, "name")->valuestring; + } + } + } + } + break; + case 'q': + case 'Q': + case 'i': + case 'I': + goto returncleanly; + default: + goto to_je_sporocilo; + } + } else { + to_je_sporocilo: + if (joinedchannelid == NULL) { + fprintf(stderr, DC_I18N_NOT_JOINED "\n"); + continue; + } + cJSON * message = cJSON_CreateObject(); + cJSON * nons = cJSON_CreateNumber(rand()); + cJSON_AddItemToObject(message, "nonce", nons); + cJSON * content = cJSON_CreateString(cmd); + cJSON_AddItemToObject(message, "content", content); + cJSON * tts = cJSON_CreateFalse(); + cJSON_AddItemToObject(message, "tts", tts); + char * jsonmessage = cJSON_Print(message); + char * target = malloc(strlen(joinedchannelid)+strlen("channels//messages")+strlen(DC_API_PREFIX)+1); + sprintf(target, DC_API_PREFIX "channels/%s/messages", joinedchannelid); + cJSON * jsonresponse; + DC_API(target, POST, jsonmessage, jsonresponse); + free(jsonmessage); jsonmessage = NULL; + free(target); target = NULL; + cJSON_Delete(message); + cJSON_Delete(jsonresponse); /* jsonresponse izgleda sploh nismo uporabili */ + } + } + returncleanly: + cJSON_Delete(guilds); + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + return 0; +} -- cgit v1.2.3