summaryrefslogblamecommitdiffstats
path: root/src/main.c
blob: ad7c1748dceb9acf3beaa260381b22a27d2bfed8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14













                                                                                                                                                        
                                                                                   














































                                                                                           
                                                                                     




















                                                                                                         
                                    

                                                                  
                                    
























                                                                                                         
                                    











                                           
                                 


























                                                                                                                    
                                


















                                                                                                          

                                                                                                          


                                                                                                                       
                                                                                                                                     

                                                   
                                                                                                                           











































                                                                                                                                                                                                                                
                                                                                                      
                                                                                        





                                                                                                                                                              



                                                                 





















                                                                                                                                                              
























                                                                                                                             
                                                    


                                                                        

                                                                                                     


                      
                              



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