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 }