#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 cJSON_Izbrisi(item) do { item != NULL ? cJSON_Delete(item) : 0; } while (0) #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(strlen(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_Izbrisi(json); } else { fprintf(stderr, DC_I18N_LOGIN_FAILED "\n", s.ptr); cJSON_Izbrisi(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_Izbrisi(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; char * tempstring = 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)); \ cJSON_Izbrisi(output); \ 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); if (!guilds || !cJSON_IsObject(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) || strchr(strchr(cmd,' ')+1,' ') ? 1 : 0) { #define DC_UPDATE_CHANNELS(guild) do {\ token = cJSON_GetObjectItem(guild, "id"); \ tempstring = realloc(tempstring, 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->valueint != 0) setchannel++; if (setchannel-- == 0) { char * joinedchannel_pointer = (cJSON_GetObjectItem(guildchannel, "id")->valuestring); joinedchannelid = realloc(joinedchannelid, strlen(joinedchannel_pointer)+1); strcpy(joinedchannelid, joinedchannel_pointer); joinedchannel_pointer = cJSON_GetObjectItem(guildchannel, "name")->valuestring; joinedchannelname = realloc(joinedchannelname, strlen(joinedchannel_pointer)+1); strcpy(joinedchannelname, joinedchannel_pointer); } } } } char * queryparam = malloc(strlen(DC_API_PREFIX "channels//messages?limit=100")+strlen(joinedchannelid)+1); sprintf(queryparam, DC_API_PREFIX "channels/%s/messages?limit=100", joinedchannelid); DC_API(queryparam, GET, "", token); if (!cJSON_IsArray(token)) { fprintf(stderr, DC_I18N_MESSAGES_GET_FAIL "\n"); free(queryparam); queryparam = NULL; joinedchannelid = NULL; joinedchannelname = NULL; break; } cJSON * sporocilo = cJSON_GetArrayItem(token, cJSON_GetArraySize(token)-1); /* last item in array is the first chat */ do { #define cJSON_GetObjectItem2(root, name1, name2) (cJSON_GetObjectItem(root, name1) ? cJSON_GetObjectItem(cJSON_GetObjectItem(root, name1), name2) : NULL) fprintf(stdout, "%.19s %s#%s: %s\n", cJSON_GetObjectItem(sporocilo, "timestamp")->valuestring, cJSON_GetObjectItem2(sporocilo, "author", "username")->valuestring, cJSON_GetObjectItem2(sporocilo, "author", "discriminator")->valuestring, cJSON_GetObjectItem(sporocilo, "content")->valuestring ); sporocilo = sporocilo->prev; } while (sporocilo->prev->next != NULL); free(queryparam); queryparam = NULL; 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 = NULL; DC_API(target, POST, jsonmessage, jsonresponse); free(jsonmessage); jsonmessage = NULL; free(target); target = NULL; cJSON_Izbrisi(message); cJSON_Izbrisi(jsonresponse); /* jsonresponse izgleda sploh nismo uporabili */ } } returncleanly: cJSON_Izbrisi(guilds); curl_easy_cleanup(curl); curl_slist_free_all(headers); return 0; }