#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 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 <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);
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;
}