feat: add main.c
This commit is contained in:
Executable
+2
@@ -0,0 +1,2 @@
|
||||
gcc main.c -o bot $(pkgconf --libs libcurl) $(pkgconf --libs yyjson)
|
||||
./bot YOUR_TOKEN_HERE
|
||||
@@ -0,0 +1,125 @@
|
||||
#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();
|
||||
}
|
||||
Reference in New Issue
Block a user