From b2592d7334e5c61191d95c391a9ee6f4d63810a7 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 21 May 2025 12:27:03 +0200 Subject: [PATCH] refactoring project structure --- examples/echobot/echobot.c | 2 - include/common.h | 85 ++++++++++ include/json.h | 9 ++ include/methods.h | 22 +++ include/tgbot.h | 120 +------------- include/types.h | 20 +++ meson.build | 2 + src/json.c | 27 ++++ src/meson.build | 5 +- src/methods.c | 268 ++++++++++++++++++++++++++++++++ src/tgbot.c | 310 ------------------------------------- src/types.c | 20 +++ 12 files changed, 462 insertions(+), 428 deletions(-) create mode 100644 include/common.h create mode 100644 include/json.h create mode 100644 include/methods.h create mode 100644 include/types.h create mode 100644 src/json.c create mode 100644 src/methods.c create mode 100644 src/types.c diff --git a/examples/echobot/echobot.c b/examples/echobot/echobot.c index 141c907..253fb4e 100644 --- a/examples/echobot/echobot.c +++ b/examples/echobot/echobot.c @@ -3,7 +3,6 @@ #include #include - bool run = true; void sighandler(int signum) { @@ -19,7 +18,6 @@ void echo_message(tgbot *bot, tgbot_update *update) { } int main(void) { - printf("Running...\n"); /* Retrieve bot's token */ FILE *fp = fopen(".token", "r"); if (fp == NULL) { diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..d303705 --- /dev/null +++ b/include/common.h @@ -0,0 +1,85 @@ +#ifndef TGBOT_COMMON_H +#define TGBOT_COMMON_H + +#include +#include + +/** + * A structure used to get curl response + */ +struct memory_buffer { + char *data; + size_t size; +}; + +/** + * A structure to represent bot object + */ +struct tgbot_t { + char token[128]; /**< Bot token */ + char api[512]; /**< Bot API url */ + int32_t offset; /**< Bot offset */ + CURL *curl; /**< Curl object, used to send http requests */ +}; +typedef struct tgbot_t tgbot; + +/** + * A structure to represent Bot information got from getMe API + */ +struct tgbot_me_t { + char first_name[256]; + char username[32]; +}; +typedef struct tgbot_me_t tgbot_me; + +/** + * A structure to represent Update object + */ +struct tgbot_update_t { + int64_t update_id; /**< Update id */ + long message_id; /**< Message id */ + int64_t chat_id; /**< Chat id */ + char chat_first_name[256]; /**< Chat first name */ + char chat_last_name[256]; /**< Chat last name */ + char chat_username[32]; /**< Chat username */ + char chat_type[32]; /**< Chat type (private/public) */ + int32_t date; /**< Date in unix timestamp */ + char text[4096]; /**< Message text */ +}; +typedef struct tgbot_update_t tgbot_update; + +/** + * A structure to represent CallbackQuery object + */ +struct tgbot_cbquery_t { + int64_t update_id; + long message_id; + int64_t chat_id; + char chat_username[32]; + int32_t date; + char text[4096]; + char chat_instance[128]; + char data[64]; /**> Callback data */ +}; +typedef struct tgbot_cbquery_t tgbot_cbquery; + +/** + * Callback function pointer + */ +typedef void (*Callback)(tgbot *bot, tgbot_cbquery *query); + +/** + * A structure to represent error codes + */ +enum tgbot_rc { + TGBOT_OK = 0, + TGBOT_INIT_ERROR, + TGBOT_REQUEST_ERROR, + TGBOT_GETUPDATES_ERROR, + TGBOT_GETME_ERROR, + TGBOT_SENDMESSAGE_ERROR, + TGBOT_TELEGRAM_OK_ERROR, +}; +typedef enum tgbot_rc tgbot_rc; + +#endif diff --git a/include/json.h b/include/json.h new file mode 100644 index 0000000..26a9c0a --- /dev/null +++ b/include/json.h @@ -0,0 +1,9 @@ +#ifndef TGBOT_JSON_H +#define TGBOT_JSON_H + +#include "types.h" +#include + +json_object *tgbot_new_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns); + +#endif diff --git a/include/methods.h b/include/methods.h new file mode 100644 index 0000000..d164bbe --- /dev/null +++ b/include/methods.h @@ -0,0 +1,22 @@ +#ifndef TGBOT_METHODS_H +#define TGBOT_METHODS_H + +#include "types.h" +#include + +tgbot_rc tgbot_get_update(tgbot *bot, tgbot_update *update, Callback cbq_handler); +tgbot_rc tgbot_parse_message(tgbot *bot, tgbot_update *update, json_object *result); +tgbot_rc tgbot_parse_cbquery(tgbot *bot, tgbot_cbquery *query, json_object *result, Callback query_handler); + +/* Request */ +size_t write_callback(void *ptr, size_t size, size_t nmemb, char **userdata); +tgbot_rc tgbot_request(tgbot *bot, char *url, struct memory_buffer **mb, json_object *json); + +/* Methods */ +tgbot_rc tgbot_get_me(tgbot *bot, tgbot_me *me); +tgbot_rc tgbot_send_message(tgbot *bot, int64_t chat_id, char *text, char *parse_mode, tgbot_inlinekeyboardmarkup **reply_markup, size_t rows, size_t columns); + +/* Updating Methods */ +tgbot_rc tgbot_edit_message_text(tgbot *bot, int64_t chat_id, long message_id, char *text, tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns); + +#endif diff --git a/include/tgbot.h b/include/tgbot.h index 70930a5..eb35650 100644 --- a/include/tgbot.h +++ b/include/tgbot.h @@ -1,87 +1,9 @@ -#ifndef TGBOT_H -#define TGBOT_H +#ifndef TGBOT_MAIN_H +#define TGBOT_MAIN_H -#include -#include -#include - -/** - * A structure used to get curl response - */ -struct memory_buffer { - char *data; - size_t size; -}; - -/** - * A structure to represent bot object - */ -struct tgbot_t { - char token[128]; /**< Bot token */ - char api[512]; /**< Bot API url */ - int32_t offset; /**< Bot offset */ - CURL *curl; /**< Curl object, used to send http requests */ -}; -typedef struct tgbot_t tgbot; - -/** - * A structure to represent Bot information got from getMe API - */ -struct tgbot_me_t { - char first_name[256]; - char username[32]; -}; -typedef struct tgbot_me_t tgbot_me; - -/** - * A structure to represent Update object - */ -struct tgbot_update_t { - int64_t update_id; /**< Update id */ - long message_id; /**< Message id */ - int64_t chat_id; /**< Chat id */ - char chat_first_name[256]; /**< Chat first name */ - char chat_last_name[256]; /**< Chat last name */ - char chat_username[32]; /**< Chat username */ - char chat_type[32]; /**< Chat type (private/public) */ - int32_t date; /**< Date in unix timestamp */ - char text[4096]; /**< Message text */ -}; -typedef struct tgbot_update_t tgbot_update; - -/** - * A structure to represent CallbackQuery object - */ -struct tgbot_cbquery_t { - int64_t update_id; - long message_id; - int64_t chat_id; - char chat_username[32]; - int32_t date; - char text[4096]; - char chat_instance[128]; - char data[64]; /**> Callback data */ -}; -typedef struct tgbot_cbquery_t tgbot_cbquery; - -/** - * Callback function pointer - */ -typedef void (*Callback)(tgbot *bot, tgbot_cbquery *query); - -/** - * A structure to represent error codes - */ -enum tgbot_rc { - TGBOT_OK = 0, - TGBOT_INIT_ERROR, - TGBOT_REQUEST_ERROR, - TGBOT_GETUPDATES_ERROR, - TGBOT_GETME_ERROR, - TGBOT_SENDMESSAGE_ERROR, - TGBOT_TELEGRAM_OK_ERROR, -}; -typedef enum tgbot_rc tgbot_rc; +#include "common.h" +#include "methods.h" +#include "types.h" /** * Initializes the Bot object @@ -96,36 +18,4 @@ tgbot_rc tgbot_init(tgbot *bot, char *token); */ void tgbot_destroy(tgbot *bot); -/* Types definitions */ - -/** - * A structure to represent InlineKeyboardMarkup - */ -struct tgbot_inlinekeyboardmarkup_t { - char text[200]; /**< If this field is empty the button will be skipped */ - char url[200]; /**< (Optional) URL of the button */ - char callback_data[64]; /**< Callback data */ -}; -typedef struct tgbot_inlinekeyboardmarkup_t tgbot_inlinekeyboardmarkup; - -/* Types methods */ -tgbot_rc -tgbot_allocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup ***keyboard, size_t rows, size_t columns); -tgbot_rc tgbot_deallocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows); -json_object *tgbot_new_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns); - -/* Methods */ -tgbot_rc tgbot_get_update(tgbot *bot, tgbot_update *update, Callback cbq_handler); -tgbot_rc tgbot_parse_message(tgbot *bot, tgbot_update *update, json_object *result); -tgbot_rc tgbot_parse_cbquery(tgbot *bot, tgbot_cbquery *query, json_object *result, Callback query_handler); -tgbot_rc tgbot_get_me(tgbot *bot, tgbot_me *me); -tgbot_rc tgbot_send_message(tgbot *bot, int64_t chat_id, char *text, char *parse_mode, tgbot_inlinekeyboardmarkup **reply_markup, size_t rows, size_t columns); - -/* Updating Methods */ -tgbot_rc tgbot_edit_message_text(tgbot *bot, int64_t chat_id, long message_id, char *text, tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns); - -/* Request */ -size_t write_callback(void *ptr, size_t size, size_t nmemb, char **userdata); -tgbot_rc tgbot_request(tgbot *bot, char *url, struct memory_buffer **mb, json_object *json); - #endif // TGBOT_H diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..2e7c4a5 --- /dev/null +++ b/include/types.h @@ -0,0 +1,20 @@ +#ifndef TGBOT_TYPES_H +#define TGBOT_TYPES_H + +#include "common.h" + +/** + * A structure to represent InlineKeyboardMarkup + */ +struct tgbot_inlinekeyboardmarkup_t { + char text[200]; /**< If this field is empty the button will be skipped */ + char url[200]; /**< (Optional) URL of the button */ + char callback_data[64]; /**< Callback data */ +}; +typedef struct tgbot_inlinekeyboardmarkup_t tgbot_inlinekeyboardmarkup; + +tgbot_rc +tgbot_allocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup ***keyboard, size_t rows, size_t columns); +tgbot_rc tgbot_deallocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows); + +#endif diff --git a/meson.build b/meson.build index 99b6c81..8d1d4c2 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,8 @@ json_c_dep = dependency('json-c') deps = [curl_dep, json_c_dep] +add_project_arguments('-O2', language: 'c') + install_headers('include/tgbot.h') #install_subdir('.', install_dir: 'C:/tgbot') lib = library( diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..6e3de33 --- /dev/null +++ b/src/json.c @@ -0,0 +1,27 @@ +#include "json.h" + +json_object *tgbot_new_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns) { + json_object *reply_markup = json_object_new_object(); + json_object *inline_keyboard_array = json_object_new_array(); + + for (size_t i = 0; i < rows; ++i) { + json_object *row = json_object_new_array(); + for (size_t j = 0; j < columns; ++j) { + if (strcmp(keyboard[i][j].text, "") == 0) { + continue; + } + + json_object *button = json_object_new_object(); + json_object_object_add(button, "text", json_object_new_string(keyboard[i][j].text)); + json_object_object_add(button, "url", json_object_new_string(keyboard[i][j].url)); + json_object_object_add(button, "callback_data", json_object_new_string(keyboard[i][j].callback_data)); + + json_object_array_add(row, button); + } + json_object_array_add(inline_keyboard_array, row); + } + + json_object_object_add(reply_markup, "inline_keyboard", inline_keyboard_array); + + return reply_markup; +} diff --git a/src/meson.build b/src/meson.build index a94e50f..7540d73 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,3 +1,6 @@ sources += files( + 'json.c', + 'methods.c', 'tgbot.c', -) + 'types.c', +) \ No newline at end of file diff --git a/src/methods.c b/src/methods.c new file mode 100644 index 0000000..436d53a --- /dev/null +++ b/src/methods.c @@ -0,0 +1,268 @@ +#include + +#include "json.h" +#include "methods.h" + + +tgbot_rc tgbot_get_update(tgbot *bot, tgbot_update *update, Callback cbq_handler) { + char url[1024]; + + /* Clear Update */ + memset(update, 0, sizeof(tgbot_update)); + + json_object *rjson = json_object_new_object(); + json_object_object_add(rjson, "offset", json_object_new_int64(bot->offset)); + json_object_object_add(rjson, "limit", json_object_new_int(1)); + json_object_object_add(rjson, "timeout", json_object_new_int(30)); + + snprintf(url, sizeof(url), "%sgetUpdates", bot->api); + + struct memory_buffer *mb; + tgbot_rc ret = tgbot_request(bot, url, &mb, rjson); + json_object_put(rjson); + if (ret != TGBOT_OK) { + free(mb->data); + free(mb); + + return TGBOT_GETUPDATES_ERROR; + } + + json_object *json = json_tokener_parse(mb->data); + free(mb->data); + free(mb); + + json_object *ok = json_object_object_get(json, "ok"); + if (!json_object_is_type(ok, json_type_boolean) || !json_object_get_boolean(ok)) { + json_object_put(json); + + return TGBOT_TELEGRAM_OK_ERROR; + } + + json_object *results = json_object_object_get(json, "result"); + size_t results_len = json_object_array_length(results); + + if (results_len == 0) { + json_object_put(json); + + return TGBOT_OK; + } + + /* Check if it is a Message or a CallbackQuery*/ + json_object *result = json_object_array_get_idx(results, 0); + json_object *message = json_object_object_get(result, "message"); + if (message) { + tgbot_parse_message(bot, update, result); + } else { + tgbot_cbquery query; + tgbot_parse_cbquery(bot, &query, result, cbq_handler); + } + + json_object_put(json); + + return TGBOT_OK; +} + +tgbot_rc tgbot_parse_message(tgbot *bot, tgbot_update *update, json_object *result) { + json_object *update_id = json_object_object_get(result, "update_id"); + bot->offset = json_object_get_int(update_id) + 1; + update->update_id = json_object_get_int(update_id); + + json_object *message = json_object_object_get(result, "message"); + json_object *message_id = json_object_object_get(message, "message_id"); + update->message_id = json_object_get_int(message_id); + + json_object *chat = json_object_object_get(message, "chat"); + json_object *chat_id = json_object_object_get(chat, "id"); + update->chat_id = json_object_get_int64(chat_id); + + json_object *chat_first_name = json_object_object_get(chat, "first_name"); + strncpy(update->chat_first_name, json_object_get_string(chat_first_name), sizeof(update->chat_first_name) - 1); + json_object *chat_last_name = json_object_object_get(chat, "last_name"); + if (chat_last_name != NULL) { + strncpy(update->chat_last_name, json_object_get_string(chat_last_name), sizeof(update->chat_last_name) - 1); + } else { + update->chat_last_name[0] = '\0'; + } + json_object *chat_username = json_object_object_get(chat, "username"); + if (chat_username != NULL) { + strncpy(update->chat_username, json_object_get_string(chat_username), sizeof(update->chat_username) - 1); + } else { + update->chat_username[0] = '\0'; + } + json_object *chat_type = json_object_object_get(chat, "type"); + strncpy(update->chat_type, json_object_get_string(chat_type), sizeof(update->chat_type) - 1); + + json_object *date = json_object_object_get(message, "date"); + update->date = json_object_get_int(date); + + json_object *text = json_object_object_get(message, "text"); + strncpy(update->text, json_object_get_string(text), sizeof(update->text) - 1); + + return TGBOT_OK; +} + +tgbot_rc tgbot_parse_cbquery(tgbot *bot, tgbot_cbquery *query, json_object *result, Callback cbq_handler) { + json_object *update_id = json_object_object_get(result, "update_id"); + bot->offset = json_object_get_int(update_id) + 1; + query->update_id = json_object_get_int(update_id); + + json_object *callback_query = json_object_object_get(result, "callback_query"); + json_object *message = json_object_object_get(callback_query, "message"); + json_object *message_id = json_object_object_get(message, "message_id"); + query->message_id = json_object_get_int(message_id); + json_object *chat = json_object_object_get(message, "chat"); + json_object *chat_id = json_object_object_get(chat, "id"); + query->chat_id = json_object_get_int64(chat_id); + json_object *chat_username = json_object_object_get(chat, "username"); + /* TODO: add NULL checks */ + strncpy(query->chat_username, json_object_get_string(chat_username), sizeof(query->chat_username) - 1); + json_object *date = json_object_object_get(message, "date"); + query->date = json_object_get_int(date); + json_object *text = json_object_object_get(message, "text"); + strncpy(query->text, json_object_get_string(text), sizeof(query->text) - 1); + json_object *chat_instance = json_object_object_get(callback_query, "chat_instance"); + strncpy(query->chat_instance, json_object_get_string(chat_instance), sizeof(query->chat_instance) - 1); + json_object *data = json_object_object_get(callback_query, "data"); + strncpy(query->data, json_object_get_string(data), sizeof(query->data) - 1); + + cbq_handler(bot, query); + + return TGBOT_OK; +} + +size_t write_callback(void *ptr, size_t size, size_t nmemb, char **userdata) { + size_t real_size = size * nmemb; + struct memory_buffer *mem = (struct memory_buffer *)userdata; + + mem->data = realloc(mem->data, mem->size + real_size + 1); + memcpy(&(mem->data[mem->size]), ptr, real_size); + mem->size += real_size; + mem->data[mem->size] = '\0'; + + return real_size; +} + +tgbot_rc tgbot_request(tgbot *bot, char *url, struct memory_buffer **mb, json_object *json) { + const char *json_string = NULL; + *mb = calloc(1, sizeof(struct memory_buffer)); + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + curl_easy_setopt(bot->curl, CURLOPT_URL, url); + curl_easy_setopt(bot->curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(bot->curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)write_callback); + curl_easy_setopt(bot->curl, CURLOPT_WRITEDATA, *mb); + curl_easy_setopt(bot->curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(bot->curl, CURLOPT_TCP_KEEPALIVE, 30L); + + if (json != NULL) { + json_string = json_object_to_json_string_ext(json, JSON_C_TO_STRING_PLAIN); + curl_easy_setopt(bot->curl, CURLOPT_POSTFIELDS, json_string); + } + + CURLcode res = curl_easy_perform(bot->curl); + + curl_slist_free_all(headers); + + if (res != CURLE_OK) { + free((*mb)->data); + free(*mb); + *mb = NULL; + + return TGBOT_REQUEST_ERROR; + } + + return TGBOT_OK; +} + +tgbot_rc tgbot_get_me(tgbot *bot, tgbot_me *me) { + char url[1024]; + snprintf(url, sizeof(url), "%sgetMe", bot->api); + + struct memory_buffer *mb; + tgbot_rc ret = tgbot_request(bot, url, &mb, NULL); + if (ret != TGBOT_OK) { + free(mb->data); + free(mb); + + return TGBOT_GETME_ERROR; + } + + json_object *json = json_tokener_parse(mb->data); + free(mb->data); + free(mb); + + json_object *ok = json_object_object_get(json, "ok"); + if (!json_object_get_boolean(ok)) { + json_object_put(json); + + return TGBOT_GETME_ERROR; + } + + json_object *result = json_object_object_get(json, "result"); + json_object *first_name = json_object_object_get(result, "first_name"); + strncpy(me->first_name, json_object_get_string(first_name), sizeof(me->first_name) - 1); + json_object *username = json_object_object_get(result, "username"); + strncpy(me->username, json_object_get_string(username), sizeof(me->username) - 1); + + json_object_put(json); + + return TGBOT_OK; +} + +tgbot_rc tgbot_send_message(tgbot *bot, int64_t chat_id, char *text, char *parse_mode, tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns) { + char url[1024]; + + json_object *rjson = json_object_new_object(); + json_object_object_add(rjson, "chat_id", json_object_new_int64(chat_id)); + json_object_object_add(rjson, "text", json_object_new_string(text)); + json_object_object_add(rjson, "parse_mode", json_object_new_string(parse_mode)); + + if (keyboard != NULL) { + json_object *reply_markup = tgbot_new_inlinekeyboardmarkup(keyboard, rows, columns); + json_object_object_add(rjson, "reply_markup", reply_markup); + } + + snprintf(url, sizeof(url), "%ssendMessage", bot->api); + + struct memory_buffer *mb; + tgbot_rc ret = tgbot_request(bot, url, &mb, rjson); + json_object_put(rjson); + if (ret != TGBOT_OK) { + free(mb->data); + free(mb); + + return TGBOT_SENDMESSAGE_ERROR; + } + + free(mb->data); + free(mb); + + return TGBOT_OK; +} + +tgbot_rc tgbot_edit_message_text(tgbot *bot, int64_t chat_id, long message_id, char *text, tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns) { + char url[1024]; + + json_object *rjson = json_object_new_object(); + json_object_object_add(rjson, "chat_id", json_object_new_int64(chat_id)); + json_object_object_add(rjson, "message_id", json_object_new_int(message_id)); + json_object_object_add(rjson, "text", json_object_new_string(text)); + + snprintf(url, sizeof(url), "%seditMessageText", bot->api); + + if (keyboard != NULL) { + json_object *reply_markup = tgbot_new_inlinekeyboardmarkup(keyboard, rows, columns); + json_object_object_add(rjson, "reply_markup", reply_markup); + } + + struct memory_buffer *mb; + tgbot_request(bot, url, &mb, rjson); + json_object_put(rjson); + free(mb->data); + free(mb); + + return TGBOT_OK; +} diff --git a/src/tgbot.c b/src/tgbot.c index c76db22..3a8147d 100644 --- a/src/tgbot.c +++ b/src/tgbot.c @@ -2,8 +2,6 @@ #include #include #include -#include -#include #include "tgbot.h" @@ -28,311 +26,3 @@ void tgbot_destroy(tgbot *bot) { } curl_global_cleanup(); } - -tgbot_rc tgbot_allocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup ***keyboard, size_t rows, size_t columns) { - *keyboard = (tgbot_inlinekeyboardmarkup **)malloc(rows * sizeof(tgbot_inlinekeyboardmarkup *)); - for (size_t i = 0; i < rows; ++i) { - (*keyboard)[i] = (tgbot_inlinekeyboardmarkup *)malloc(columns * sizeof(tgbot_inlinekeyboardmarkup)); - } - - return TGBOT_OK; -} - -tgbot_rc tgbot_deallocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows) { - for (size_t i = 0; i < rows; ++i) { - free(keyboard[i]); - } - - free(keyboard); - - return TGBOT_OK; -} - -json_object *tgbot_new_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns) { - json_object *reply_markup = json_object_new_object(); - json_object *inline_keyboard_array = json_object_new_array(); - - for (size_t i = 0; i < rows; ++i) { - json_object *row = json_object_new_array(); - for (size_t j = 0; j < columns; ++j) { - if (strcmp(keyboard[i][j].text, "") == 0) { - continue; - } - - json_object *button = json_object_new_object(); - json_object_object_add(button, "text", json_object_new_string(keyboard[i][j].text)); - json_object_object_add(button, "url", json_object_new_string(keyboard[i][j].url)); - json_object_object_add(button, "callback_data", json_object_new_string(keyboard[i][j].callback_data)); - - json_object_array_add(row, button); - } - json_object_array_add(inline_keyboard_array, row); - } - - json_object_object_add(reply_markup, "inline_keyboard", inline_keyboard_array); - - return reply_markup; -} - -tgbot_rc tgbot_get_update(tgbot *bot, tgbot_update *update, Callback cbq_handler) { - char url[1024]; - - /* Clear Update */ - memset(update, 0, sizeof(tgbot_update)); - - json_object *rjson = json_object_new_object(); - json_object_object_add(rjson, "offset", json_object_new_int64(bot->offset)); - json_object_object_add(rjson, "limit", json_object_new_int(1)); - json_object_object_add(rjson, "timeout", json_object_new_int(30)); - - snprintf(url, sizeof(url), "%sgetUpdates", bot->api); - - struct memory_buffer *mb; - tgbot_rc ret = tgbot_request(bot, url, &mb, rjson); - json_object_put(rjson); - if (ret != TGBOT_OK) { - free(mb->data); - free(mb); - - return TGBOT_GETUPDATES_ERROR; - } - - json_object *json = json_tokener_parse(mb->data); - free(mb->data); - free(mb); - - json_object *ok = json_object_object_get(json, "ok"); - if (!json_object_is_type(ok, json_type_boolean) || !json_object_get_boolean(ok)) { - json_object_put(json); - - return TGBOT_TELEGRAM_OK_ERROR; - } - - json_object *results = json_object_object_get(json, "result"); - size_t results_len = json_object_array_length(results); - - if (results_len == 0) { - json_object_put(json); - - return TGBOT_OK; - } - - /* Check if it is a Message or a CallbackQuery*/ - json_object *result = json_object_array_get_idx(results, 0); - json_object *message = json_object_object_get(result, "message"); - if (message) { - tgbot_parse_message(bot, update, result); - } else { - tgbot_cbquery query; - tgbot_parse_cbquery(bot, &query, result, cbq_handler); - } - - json_object_put(json); - - return TGBOT_OK; -} - -tgbot_rc tgbot_parse_message(tgbot *bot, tgbot_update *update, json_object *result) { - json_object *update_id = json_object_object_get(result, "update_id"); - bot->offset = json_object_get_int(update_id) + 1; - update->update_id = json_object_get_int(update_id); - - json_object *message = json_object_object_get(result, "message"); - json_object *message_id = json_object_object_get(message, "message_id"); - update->message_id = json_object_get_int(message_id); - - json_object *chat = json_object_object_get(message, "chat"); - json_object *chat_id = json_object_object_get(chat, "id"); - update->chat_id = json_object_get_int64(chat_id); - - json_object *chat_first_name = json_object_object_get(chat, "first_name"); - strncpy(update->chat_first_name, json_object_get_string(chat_first_name), sizeof(update->chat_first_name)); - json_object *chat_last_name = json_object_object_get(chat, "last_name"); - if (chat_last_name != NULL) { - strncpy(update->chat_last_name, json_object_get_string(chat_last_name), sizeof(update->chat_last_name)); - } else { - update->chat_last_name[0] = '\0'; - } - json_object *chat_username = json_object_object_get(chat, "username"); - if (chat_username != NULL) { - strncpy(update->chat_username, json_object_get_string(chat_username), sizeof(update->chat_username)); - } else { - update->chat_username[0] = '\0'; - } - json_object *chat_type = json_object_object_get(chat, "type"); - strncpy(update->chat_type, json_object_get_string(chat_type), sizeof(update->chat_type)); - - json_object *date = json_object_object_get(message, "date"); - update->date = json_object_get_int(date); - - json_object *text = json_object_object_get(message, "text"); - strncpy(update->text, json_object_get_string(text), sizeof(update->text)); - - return TGBOT_OK; -} - -tgbot_rc tgbot_parse_cbquery(tgbot *bot, tgbot_cbquery *query, json_object *result, Callback cbq_handler) { - json_object *update_id = json_object_object_get(result, "update_id"); - bot->offset = json_object_get_int(update_id) + 1; - query->update_id = json_object_get_int(update_id); - - json_object *callback_query = json_object_object_get(result, "callback_query"); - json_object *message = json_object_object_get(callback_query, "message"); - json_object *message_id = json_object_object_get(message, "message_id"); - query->message_id = json_object_get_int(message_id); - json_object *chat = json_object_object_get(message, "chat"); - json_object *chat_id = json_object_object_get(chat, "id"); - query->chat_id = json_object_get_int64(chat_id); - json_object *chat_username = json_object_object_get(chat, "username"); - /* TODO: add NULL checks */ - strncpy(query->chat_username, json_object_get_string(chat_username), sizeof(query->chat_username)); - json_object *date = json_object_object_get(message, "date"); - query->date = json_object_get_int(date); - json_object *text = json_object_object_get(message, "text"); - strncpy(query->text, json_object_get_string(text), sizeof(query->text)); - json_object *chat_instance = json_object_object_get(callback_query, "chat_instance"); - strncpy(query->chat_instance, json_object_get_string(chat_instance), sizeof(query->chat_instance)); - json_object *data = json_object_object_get(callback_query, "data"); - strncpy(query->data, json_object_get_string(data), sizeof(query->data)); - - cbq_handler(bot, query); - - return TGBOT_OK; -} - -tgbot_rc tgbot_get_me(tgbot *bot, tgbot_me *me) { - char url[1024]; - snprintf(url, sizeof(url), "%sgetMe", bot->api); - - struct memory_buffer *mb; - tgbot_rc ret = tgbot_request(bot, url, &mb, NULL); - if (ret != TGBOT_OK) { - free(mb->data); - free(mb); - - return TGBOT_GETME_ERROR; - } - - json_object *json = json_tokener_parse(mb->data); - free(mb->data); - free(mb); - - json_object *ok = json_object_object_get(json, "ok"); - if (!json_object_get_boolean(ok)) { - json_object_put(json); - - return TGBOT_GETME_ERROR; - } - - json_object *result = json_object_object_get(json, "result"); - json_object *first_name = json_object_object_get(result, "first_name"); - strncpy(me->first_name, json_object_get_string(first_name), sizeof(me->first_name)); - json_object *username = json_object_object_get(result, "username"); - strncpy(me->username, json_object_get_string(username), sizeof(me->username)); - - json_object_put(json); - - return TGBOT_OK; -} - -tgbot_rc tgbot_send_message(tgbot *bot, int64_t chat_id, char *text, char *parse_mode, tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns) { - char url[1024]; - - json_object *rjson = json_object_new_object(); - json_object_object_add(rjson, "chat_id", json_object_new_int64(chat_id)); - json_object_object_add(rjson, "text", json_object_new_string(text)); - json_object_object_add(rjson, "parse_mode", json_object_new_string(parse_mode)); - - if (keyboard != NULL) { - json_object *reply_markup = tgbot_new_inlinekeyboardmarkup(keyboard, rows, columns); - json_object_object_add(rjson, "reply_markup", reply_markup); - } - - snprintf(url, sizeof(url), "%ssendMessage", bot->api); - - struct memory_buffer *mb; - tgbot_rc ret = tgbot_request(bot, url, &mb, rjson); - json_object_put(rjson); - if (ret != TGBOT_OK) { - free(mb->data); - free(mb); - - return TGBOT_SENDMESSAGE_ERROR; - } - - free(mb->data); - free(mb); - - return TGBOT_OK; -} - -tgbot_rc tgbot_edit_message_text(tgbot *bot, int64_t chat_id, long message_id, char *text, tgbot_inlinekeyboardmarkup **keyboard, size_t rows, size_t columns) { - char url[1024]; - - json_object *rjson = json_object_new_object(); - json_object_object_add(rjson, "chat_id", json_object_new_int64(chat_id)); - json_object_object_add(rjson, "message_id", json_object_new_int(message_id)); - json_object_object_add(rjson, "text", json_object_new_string(text)); - - snprintf(url, sizeof(url), "%seditMessageText", bot->api); - - if (keyboard != NULL) { - json_object *reply_markup = tgbot_new_inlinekeyboardmarkup(keyboard, rows, columns); - json_object_object_add(rjson, "reply_markup", reply_markup); - } - - struct memory_buffer *mb; - tgbot_request(bot, url, &mb, rjson); - json_object_put(rjson); - free(mb->data); - free(mb); - - return TGBOT_OK; -} - -size_t write_callback(void *ptr, size_t size, size_t nmemb, char **userdata) { - size_t real_size = size * nmemb; - struct memory_buffer *mem = (struct memory_buffer *)userdata; - - mem->data = realloc(mem->data, mem->size + real_size + 1); - memcpy(&(mem->data[mem->size]), ptr, real_size); - mem->size += real_size; - mem->data[mem->size] = '\0'; - - return real_size; -} - -tgbot_rc tgbot_request(tgbot *bot, char *url, struct memory_buffer **mb, json_object *json) { - const char *json_string = NULL; - *mb = calloc(1, sizeof(struct memory_buffer)); - - struct curl_slist *headers = NULL; - headers = curl_slist_append(headers, "Accept: application/json"); - headers = curl_slist_append(headers, "Content-Type: application/json"); - - curl_easy_setopt(bot->curl, CURLOPT_URL, url); - curl_easy_setopt(bot->curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(bot->curl, CURLOPT_WRITEFUNCTION, write_callback); - curl_easy_setopt(bot->curl, CURLOPT_WRITEDATA, *mb); - curl_easy_setopt(bot->curl, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(bot->curl, CURLOPT_TCP_KEEPALIVE, 30L); - - if (json != NULL) { - json_string = json_object_to_json_string_ext(json, JSON_C_TO_STRING_PLAIN); - curl_easy_setopt(bot->curl, CURLOPT_POSTFIELDS, json_string); - } - - CURLcode res = curl_easy_perform(bot->curl); - - curl_slist_free_all(headers); - - if (res != CURLE_OK) { - free((*mb)->data); - free(*mb); - *mb = NULL; - - return TGBOT_REQUEST_ERROR; - } - - return TGBOT_OK; -} diff --git a/src/types.c b/src/types.c new file mode 100644 index 0000000..798862b --- /dev/null +++ b/src/types.c @@ -0,0 +1,20 @@ +#include "types.h" + +tgbot_rc tgbot_allocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup ***keyboard, size_t rows, size_t columns) { + *keyboard = (tgbot_inlinekeyboardmarkup **)malloc(rows * sizeof(tgbot_inlinekeyboardmarkup *)); + for (size_t i = 0; i < rows; ++i) { + (*keyboard)[i] = (tgbot_inlinekeyboardmarkup *)malloc(columns * sizeof(tgbot_inlinekeyboardmarkup)); + } + + return TGBOT_OK; +} + +tgbot_rc tgbot_deallocate_inlinekeyboardmarkup(tgbot_inlinekeyboardmarkup **keyboard, size_t rows) { + for (size_t i = 0; i < rows; ++i) { + free(keyboard[i]); + } + + free(keyboard); + + return TGBOT_OK; +}