#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_easy_setopt(bot->curl, CURLOPT_URL, url); curl_easy_perform(bot->curl); curl_free(encoded_text); } 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(); }