commit 1f094245ebfea454d7e8b644a9fa61bf5efcf6c2 Author: Francesco Date: Tue Jun 9 15:40:56 2026 +0200 feat: add main.c diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..5c59b2d --- /dev/null +++ b/compile.sh @@ -0,0 +1,2 @@ +gcc main.c -o bot $(pkgconf --libs libcurl) $(pkgconf --libs yyjson) +./bot YOUR_TOKEN_HERE diff --git a/main.c b/main.c new file mode 100644 index 0000000..6b73e88 --- /dev/null +++ b/main.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include + +#define API_LEN 256 +#define URL_LEN 512 +#define TEXT_LEN 4096 + +typedef struct tgbot { + CURL *curl; + char api[API_LEN]; + int64_t offset; +} tgbot; + +typedef struct tgmessage { + int64_t chat_id; + const char *text; +} tgmessage; + +void send_message(tgbot *bot, tgmessage *message) { + char url[512]; + char *encoded_text = curl_easy_escape(bot->curl, message->text, 0); + snprintf(url, sizeof(url), "%ssendMessage?chat_id=%ld&text=%s", bot->api, + message->chat_id, encoded_text); + curl_free(encoded_text); + + curl_easy_setopt(bot->curl, CURLOPT_URL, url); + curl_easy_perform(bot->curl); +} + +size_t write_cb(void *ptr, size_t size, size_t nmemb, char *data) { + strncat(data, ptr, size * nmemb); + return size * nmemb; +} + +int handle_updates(tgbot *bot) { + /* Prepare our update */ + char url[URL_LEN] = {0}; + char response[TEXT_LEN] = {0}; + snprintf(url, sizeof(url), "%sgetUpdates?timeout=30&offset=%ld", bot->api, + bot->offset + 1); + + /* A little bit higher than 30s polling */ + curl_easy_setopt(bot->curl, CURLOPT_TIMEOUT, 35L); + curl_easy_setopt(bot->curl, CURLOPT_URL, url); + curl_easy_setopt(bot->curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(bot->curl, CURLOPT_WRITEDATA, response); + + int res = curl_easy_perform(bot->curl); + if (res != CURLE_OK) { + return -1; + } + + /* Print json response */ + fprintf(stdout, "%s\n", response); + + /* Let's parse json */ + yyjson_doc *doc = yyjson_read(response, strlen(response), 0); + yyjson_val *root = yyjson_doc_get_root(doc); + + yyjson_val *ok = yyjson_obj_get(root, "ok"); + if (yyjson_get_bool(ok) != true) { + yyjson_doc_free(doc); + return -1; + } + + /* Parse result */ + yyjson_val *result = yyjson_obj_get(root, "result"); + /* result is an array, so iterate over it */ + yyjson_val *a_result; + size_t idx, max; + yyjson_arr_foreach(result, idx, max, a_result) { + /* Update offset */ + yyjson_val *update_id = yyjson_obj_get(a_result, "update_id"); + bot->offset = yyjson_get_sint(update_id); + + /* Get message */ + yyjson_val *message = yyjson_obj_get(a_result, "message"); + yyjson_val *text = yyjson_obj_get(message, "text"); + yyjson_val *chat = yyjson_obj_get(message, "chat"); + yyjson_val *id = yyjson_obj_get(chat, "id"); + + /* Echo message */ + tgmessage msg = { + .chat_id = yyjson_get_sint(id), + .text = yyjson_get_str(text), + }; + send_message(bot, &msg); + } + + return 0; +} + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "Usage: %s ", argv[0]); + return -1; + } + + /* Initialize curl */ + curl_global_init(CURL_GLOBAL_ALL); + + /* Initialize our bot struct */ + tgbot bot = { + .offset = 0, + .curl = curl_easy_init(), + }; + + /* Retrieve bot token from args */ + snprintf(bot.api, sizeof(bot.api), "https://api.telegram.org/bot%s/", + argv[1]); + + /* Bot loop */ + int err = 0; + while (!err) { + /* Find a better way to deal with CTRL-C, etc... and cleanup */ + err = handle_updates(&bot); + } + + /* Cleanup */ + curl_easy_cleanup(bot.curl); + curl_global_cleanup(); +}