discordc

discordc - C Discord ping bot.
git clone git://git.beep.wimdupont.com/discordc.git
Log | Files | Refs | README | LICENSE

discordapi.c (4799B)


      1 #include <curl/curl.h>
      2 #include <curl/websockets.h>
      3 #include <stdbool.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 
      9 #include "discordapi.h"
     10 
     11 #define REQUEST_MAX 1024
     12 #define AUTH_MAX 256
     13 
     14 typedef struct
     15 {
     16 	char *response;
     17 	size_t size;
     18 } Memory;
     19 
     20 static size_t writefunc(char *data, size_t size, size_t nmemb, Memory *mem);
     21 
     22 void
     23 discordapi_init(void)
     24 {
     25 	curl_global_init(CURL_GLOBAL_DEFAULT);
     26 }
     27 
     28 void
     29 discordapi_cleanup(void)
     30 {
     31 	curl_global_cleanup();
     32 }
     33 
     34 int
     35 discordapi_gateway_open(DiscordGateway *gateway, const char *gateway_url,
     36 		const char *user_agent)
     37 {
     38 	CURL *ws = curl_easy_init();
     39 	CURLcode res;
     40 
     41 	gateway->ws = NULL;
     42 	if (ws == NULL)
     43 		return -1;
     44 
     45 	curl_easy_setopt(ws, CURLOPT_URL, gateway_url);
     46 	curl_easy_setopt(ws, CURLOPT_USERAGENT, user_agent);
     47 	curl_easy_setopt(ws, CURLOPT_CONNECT_ONLY, 2L);
     48 
     49 	res = curl_easy_perform(ws);
     50 	if (res != CURLE_OK) {
     51 		fprintf(stderr, "curl_easy_perform() failed: %s\n",
     52 				curl_easy_strerror(res));
     53 		curl_easy_cleanup(ws);
     54 		return -1;
     55 	}
     56 
     57 	gateway->ws = ws;
     58 	return 0;
     59 }
     60 
     61 void
     62 discordapi_gateway_close(DiscordGateway *gateway)
     63 {
     64 	if (gateway->ws != NULL)
     65 		curl_easy_cleanup(gateway->ws);
     66 	gateway->ws = NULL;
     67 }
     68 
     69 DiscordApiRecvStatus
     70 discordapi_gateway_recv(DiscordGateway *gateway, char *buf, size_t bufsize,
     71 		unsigned int *close_code, char *close_reason,
     72 		size_t reason_size)
     73 {
     74 	const struct curl_ws_frame *meta;
     75 	size_t nread, len;
     76 	CURL *ws = gateway->ws;
     77 	CURLcode res;
     78 
     79 	if (bufsize == 0 || ws == NULL)
     80 		return DISCORDAPI_RECV_ERROR;
     81 
     82 	res = curl_ws_recv(ws, buf, bufsize - 1, &nread, &meta);
     83 	if (res == CURLE_AGAIN)
     84 		return DISCORDAPI_RECV_NONE;
     85 	if (res != CURLE_OK) {
     86 		fprintf(stderr, "curl_ws_recv() failed: %s\n",
     87 				curl_easy_strerror(res));
     88 		return DISCORDAPI_RECV_ERROR;
     89 	}
     90 	if (meta != NULL && (meta->flags & CURLWS_CLOSE)) {
     91 		*close_code = 0;
     92 		if (reason_size > 0)
     93 			close_reason[0] = '\0';
     94 		if (nread >= 2) {
     95 			*close_code = ((unsigned char)buf[0] << 8)
     96 				| (unsigned char)buf[1];
     97 			if (reason_size > 0) {
     98 				len = nread - 2 < reason_size - 1
     99 					? nread - 2 : reason_size - 1;
    100 				memcpy(close_reason, buf + 2, len);
    101 				close_reason[len] = '\0';
    102 			}
    103 		}
    104 		return DISCORDAPI_RECV_CLOSED;
    105 	}
    106 	if (meta == NULL || !(meta->flags & (CURLWS_TEXT | CURLWS_CONT)))
    107 		return DISCORDAPI_RECV_NONE;
    108 
    109 	len = nread;
    110 	while (meta->bytesleft > 0 && len < bufsize - 1) {
    111 		res = curl_ws_recv(ws, buf + len, bufsize - 1 - len, &nread, &meta);
    112 		if (res != CURLE_OK)
    113 			return DISCORDAPI_RECV_ERROR;
    114 		len += nread;
    115 	}
    116 	buf[len] = '\0';
    117 
    118 	return DISCORDAPI_RECV_PAYLOAD;
    119 }
    120 
    121 int
    122 discordapi_gateway_send(DiscordGateway *gateway, const char *json_string)
    123 {
    124 	size_t sent = 0, nsent, len = strlen(json_string);
    125 	CURL *ws = gateway->ws;
    126 	CURLcode res;
    127 
    128 	if (ws == NULL)
    129 		return -1;
    130 
    131 	while (sent < len) {
    132 		res = curl_ws_send(ws, json_string + sent, len - sent, &nsent, 0,
    133 				CURLWS_TEXT);
    134 		if (res == CURLE_OK)
    135 			sent += nsent;
    136 		else if (res == CURLE_AGAIN)
    137 			usleep(50000);
    138 		else {
    139 			fprintf(stderr, "curl_ws_send() failed: %s\n",
    140 					curl_easy_strerror(res));
    141 			return -1;
    142 		}
    143 	}
    144 
    145 	return 0;
    146 }
    147 
    148 int
    149 discordapi_post_message(const char *api_base, const char *channel_id,
    150 		const char *bot_token, const char *user_agent,
    151 		const char *json_string)
    152 {
    153 	char request[REQUEST_MAX], authorization[AUTH_MAX];
    154 	struct curl_slist *headers = NULL;
    155 	Memory mem = {0};
    156 	CURL *curl = curl_easy_init();
    157 	CURLcode res;
    158 
    159 	if (curl == NULL)
    160 		return -1;
    161 
    162 	snprintf(request, sizeof(request), "%s/channels/%s/messages",
    163 			api_base, channel_id);
    164 	snprintf(authorization, sizeof(authorization), "Authorization: Bot %s",
    165 			bot_token);
    166 
    167 	headers = curl_slist_append(headers, authorization);
    168 	headers = curl_slist_append(headers, "Content-Type: application/json");
    169 	headers = curl_slist_append(headers, "charset: utf-8");
    170 
    171 	curl_easy_setopt(curl, CURLOPT_URL, request);
    172 	curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "");
    173 	curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
    174 	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    175 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
    176 	curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
    177 	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_string);
    178 
    179 	res = curl_easy_perform(curl);
    180 	if (res != CURLE_OK)
    181 		fprintf(stderr, "curl_easy_perform() failed: %s\n",
    182 				curl_easy_strerror(res));
    183 
    184 	free(mem.response);
    185 	curl_slist_free_all(headers);
    186 	curl_easy_cleanup(curl);
    187 
    188 	return res == CURLE_OK ? 0 : -1;
    189 }
    190 
    191 size_t
    192 writefunc(char *data, size_t size, size_t nmemb, Memory *mem)
    193 {
    194 	char *ptr;
    195 	size_t realsize = size * nmemb;
    196 
    197 	if ((ptr = realloc(mem->response, mem->size + realsize + 1)) == NULL)
    198 		return 0;
    199 
    200 	mem->response = ptr;
    201 	memcpy(&(mem->response[mem->size]), data, realsize);
    202 	mem->size += realsize;
    203 	mem->response[mem->size] = 0;
    204 
    205 	return realsize;
    206 }