Created
May 23, 2022 22:45
-
-
Save kraftwerk28/ee01228df3861d403db2bd04cb70b713 to your computer and use it in GitHub Desktop.
Telegram bot in C using json-c & libcurl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <curl/curl.h> | |
#include <json.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
static const char *API_URL = "https://api.telegram.org"; | |
struct response { | |
char *body; | |
size_t bodylen; | |
}; | |
void tg_url(const char *meth, char *out_url) { | |
char *token = getenv("BOT_TOKEN"); | |
assert(token && strlen(token) > 0); | |
sprintf(out_url, "%s/bot%s/%s", API_URL, token, meth); | |
} | |
size_t | |
curl_on_write(const void *data, size_t size, size_t nmemb, void *userdata) { | |
char **body = userdata; | |
size_t total = size * nmemb; | |
if (*body == NULL) { | |
*body = malloc(total + 1); | |
memcpy(*body, data, total); | |
(*body)[total] = '\0'; | |
} else { | |
size_t bodylen = strlen(*body); | |
*body = realloc(*body, bodylen + total + 1); | |
memcpy(*body + bodylen, data, total); | |
(*body)[bodylen + total] = '\0'; | |
} | |
return total; | |
} | |
json_object *wait_for_updates(long timeout, long offset) { | |
CURL *curl = curl_easy_init(); | |
assert(curl); | |
char url[1024] = {0}; | |
char *token = getenv("BOT_TOKEN"); | |
sprintf( | |
url, "%s/bot%s/getUpdates?offset=%ld&timeout=%ld", API_URL, token, | |
offset, timeout); | |
char *raw_response = NULL; | |
curl_easy_setopt(curl, CURLOPT_URL, url); | |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &raw_response); | |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_on_write); | |
curl_easy_perform(curl); | |
json_object *res_json = json_tokener_parse(raw_response); | |
free(raw_response); | |
curl_easy_cleanup(curl); | |
return res_json; | |
} | |
long update_offset(const json_object *updates) { | |
size_t nupdates = json_object_array_length(updates); | |
json_object *last = json_object_array_get_idx(updates, nupdates - 1); | |
json_object *uid_json = json_object_object_get(last, "update_id"); | |
long uid = json_object_get_int64(uid_json) + 1; | |
return uid; | |
} | |
void tg_html_escape(const char *s, char *out_s) { | |
for (size_t i = 0; s[i]; i++) { | |
switch (s[i]) { | |
case '<': | |
strcpy(out_s, "<"); | |
out_s += 4; | |
break; | |
case '>': | |
strcpy(out_s, ">"); | |
out_s += 4; | |
break; | |
case '&': | |
strcpy(out_s, "&"); | |
out_s += 5; | |
break; | |
default: | |
out_s[0] = s[i]; | |
out_s++; | |
break; | |
} | |
} | |
} | |
void send_raw_update( | |
const long chat_id, const long message_id, json_object *update) { | |
CURL *curl = curl_easy_init(); | |
char url[1024] = {0}; | |
char *token = getenv("BOT_TOKEN"); | |
sprintf(url, "%s/bot%s/sendMessage", API_URL, token); | |
json_object *req_json = json_object_new_object(); | |
json_object_object_add(req_json, "chat_id", json_object_new_int64(chat_id)); | |
json_object_object_add( | |
req_json, "parse_mode", json_object_new_string("HTML")); | |
json_object_object_add( | |
req_json, "reply_to_message_id", json_object_new_int64(message_id)); | |
char text[4096] = {0}; | |
char escaped_text[4096] = {0}; | |
const char *update_str = | |
json_object_to_json_string_ext(update, JSON_C_TO_STRING_PRETTY); | |
tg_html_escape(update_str, escaped_text); | |
sprintf(text, "<code>%s</code>", escaped_text); | |
json_object_object_add(req_json, "text", json_object_new_string(text)); | |
fprintf( | |
stderr, "%s\n", | |
json_object_to_json_string_ext(req_json, JSON_C_TO_STRING_PRETTY)); | |
struct curl_slist *req_headers = NULL; | |
req_headers = | |
curl_slist_append(req_headers, "Content-Type: application/json"); | |
curl_easy_setopt(curl, CURLOPT_URL, url); | |
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_headers); | |
curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1); | |
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); | |
curl_easy_setopt( | |
curl, CURLOPT_POSTFIELDS, json_object_to_json_string(req_json)); | |
curl_easy_perform(curl); | |
curl_slist_free_all(req_headers); | |
json_object_put(req_json); | |
curl_easy_cleanup(curl); | |
} | |
void process_update(json_object *update) { | |
json_object *msg_json = json_object_object_get(update, "message"); | |
if (json_object_get_type(msg_json) != json_type_object) | |
return; | |
long msg_id = | |
json_object_get_int64(json_object_object_get(msg_json, "message_id")); | |
json_object *chat_json = json_object_object_get(msg_json, "chat"); | |
if (json_object_get_type(chat_json) != json_type_object) | |
return; | |
long chat_id = | |
json_object_get_int64(json_object_object_get(chat_json, "id")); | |
send_raw_update(chat_id, msg_id, update); | |
} | |
int main(int argc, char *argv[]) { | |
char *token = getenv("BOT_TOKEN"); | |
if (!token || strlen(token) == 0) { | |
fprintf(stderr, "A BOT_TOKEN environment variable is required\n"); | |
return 1; | |
} | |
long offset = 0; | |
int i = 0; | |
for (;;) { | |
i++; | |
json_object *updates_json = wait_for_updates(30, offset); | |
json_object *updates = json_object_object_get(updates_json, "result"); | |
size_t nupdates = json_object_array_length(updates); | |
json_object *update = NULL; | |
for (size_t i = 0; i < nupdates; i++) { | |
update = json_object_array_get_idx(updates, i); | |
process_update(update); | |
} | |
if (update) { | |
offset = 1 + json_object_get_int64( | |
json_object_object_get(update, "update_id")); | |
} | |
json_object_put(updates_json); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment