summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsijanec <anton@sijanec.eu>2021-03-10 23:22:14 +0100
committersijanec <anton@sijanec.eu>2021-03-10 23:22:14 +0100
commit037d81b4d2072fdf4cf2b0f30bc65c6c590ead82 (patch)
tree286c10ad29fc7751d136410d9bc8506c330a740e
downloaddiscord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.tar
discord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.tar.gz
discord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.tar.bz2
discord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.tar.lz
discord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.tar.xz
discord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.tar.zst
discord.c-037d81b4d2072fdf4cf2b0f30bc65c6c590ead82.zip
-rw-r--r--.gitignore4
-rw-r--r--Makefile7
-rw-r--r--src/i18n.h10
-rw-r--r--src/lib.c26
-rw-r--r--src/main.c271
5 files changed, 318 insertions, 0 deletions
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 <stdlib.h>
+#include <stdio.h>
+#include <curl/curl.h>
+#include <unistd.h>
+#include <i18n.h>
+#include <string.h>
+#include <lib.c>
+#include <cJSON.h>
+#include <cJSON.c>
+#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 <guild number> [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;
+}