126 lines
3.1 KiB
C
126 lines
3.1 KiB
C
#include <curl/curl.h>
|
|
#include <curl/easy.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <yyjson.h>
|
|
|
|
#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 <token>", 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();
|
|
}
|