Skip to content

Instantly share code, notes, and snippets.

@kraftwerk28
Created May 23, 2022 22:45
Show Gist options
  • Save kraftwerk28/ee01228df3861d403db2bd04cb70b713 to your computer and use it in GitHub Desktop.
Save kraftwerk28/ee01228df3861d403db2bd04cb70b713 to your computer and use it in GitHub Desktop.
Telegram bot in C using json-c & libcurl
#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, "&lt;");
out_s += 4;
break;
case '>':
strcpy(out_s, "&gt;");
out_s += 4;
break;
case '&':
strcpy(out_s, "&amp;");
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