diff --git a/.clangd b/.clangd deleted file mode 100644 index 5c4c28b..0000000 --- a/.clangd +++ /dev/null @@ -1 +0,0 @@ -HeaderInsertion: Never diff --git a/examples/echobot/echobot.c b/examples/echobot/echobot.c index 06f1263..59dcf30 100644 --- a/examples/echobot/echobot.c +++ b/examples/echobot/echobot.c @@ -1,52 +1,51 @@ -#include -#include -#include -#include - -#include - -bool run = true; - -void sighandler(int signum) { - run = false; -} - -void echo_message(tgbot *bot, tgbot_update *update) { - tgbot_send_message(bot, update->chat_id, update->text, "MARKDOWN", NULL); -} - -int main(void) { - /* Retrieve bot's token */ - FILE *fp = fopen(".token", "r"); - if (fp == NULL) { - fprintf(stderr, "Unable to retrieve bot token\n"); - return 1; - } - - char token[256]; - fscanf(fp, "%s", token); - fprintf(stdout, "Token: %s\n", token); - fclose(fp); - - signal(SIGINT, sighandler); - - /* Initialize bot */ - tgbot bot; - tgbot_init(&bot, token); - tgbot_update update; - - while (run) { - tgbot_get_update(&bot, &update, NULL); - if (strcmp(update.text, "/start") == 0) { - /* Send dice if /start otherwise echo the message */ - tgbot_send_dice(&bot, update.chat_id, NULL); - } else { - echo_message(&bot, &update); - } - } - - fprintf(stdout, "Closing..."); - tgbot_destroy(&bot); - - return 0; +#include +#include +#include +#include + +#include + +bool run = true; + +void sighandler(int signum) { + run = false; +} + +void echo_message(tgbot_s *bot, tgbot_update_s *update) { + tgbot_send_message(bot, update->chat_id, update->text, "MARKDOWN", NULL); +} + +int main(void) { + /* Retrieve bot's token */ + FILE *fp = fopen(".token", "r"); + if (fp == NULL) { + fprintf(stderr, "Unable to retrieve bot token\n"); + return 1; + } + + char token[256]; + fscanf(fp, "%s", token); + fprintf(stdout, "Token: %s\n", token); + fclose(fp); + + signal(SIGINT, sighandler); + + /* Initialize bot */ + tgbot_s *bot = tgbot_new(token); + tgbot_update_s update; + + while (run) { + tgbot_get_update(bot, &update, NULL); + if (strcmp(update.text, "/start") == 0) { + /* Send dice if /start otherwise echo the message */ + tgbot_send_dice(bot, update.chat_id, NULL); + } else { + echo_message(bot, &update); + } + } + + fprintf(stdout, "Closing..."); + tgbot_free(bot); + + return 0; } \ No newline at end of file diff --git a/examples/inlinekeyboard/inlinekeyboard.c b/examples/inlinekeyboard/inlinekeyboard.c index a3d1ddd..94fae41 100644 --- a/examples/inlinekeyboard/inlinekeyboard.c +++ b/examples/inlinekeyboard/inlinekeyboard.c @@ -1,119 +1,119 @@ -#include -#include -#include -#include -#include - -#include - -#define WELCOME_MSG "Hi there! This bot is coded in C." - -void parse_command(tgbot *bot, tgbot_update *update); -void sighandler(int signum); -void callback_parser(tgbot *bot, tgbot_cbquery *query); - -bool run = true; -tgbot bot; -tgbot_inlinekeyboard *keyboard; - -/* Callback handler function */ -void callback_handler(tgbot *bot, tgbot_cbquery *query) { - if (strcmp("test-callback", query->data) == 0) { - /* Handle `test-callback` */ - tgbot_inlinekeyboard *home_keyboard = tgbot_new_inlinekeyboard(1, 1); - /* Add buttons */ - tgbot_inlinekeyboard_button(home_keyboard, 0, 0, "Home", "", "home"); - tgbot_edit_message_text(bot, query->chat_id, query->message_id, "Callback called!", home_keyboard); - tgbot_destroy_inlinekeyboard(home_keyboard); - - return; - } else if (strcmp("home", query->data) == 0) { - tgbot_edit_message_text(bot, query->chat_id, query->message_id, WELCOME_MSG, keyboard); - - return; - } -} - -void sighandler(int signum) { - fprintf(stdout, "Closing...\n"); - run = false; -} - -void parse_command(tgbot *bot, tgbot_update *update) { - tgbot_rc ret; - - if (strcmp("/start", update->text) == 0) { - ret = tgbot_send_message(bot, update->chat_id, WELCOME_MSG, "Markdown", keyboard); - - if (ret != TGBOT_OK) { - fprintf(stderr, "Failed to send message\n"); - } - - return; - } - - /* Echo the message */ - ret = tgbot_send_message(bot, update->chat_id, update->text, "Markdown", NULL); - if (ret != TGBOT_OK) { - fprintf(stderr, "Failed to send message\n"); - } -} - -int main(void) { - /* Find "your" way to free the resources */ - signal(SIGINT, sighandler); - - tgbot_rc ret; - char token[256]; - - FILE *fp = fopen(".token", "r"); - if (fp == NULL) { - fprintf(stderr, "No .token file found!\n"); - exit(EXIT_FAILURE); - } - fscanf(fp, "%s", token); - fprintf(stdout, "Token: %s\n", token); - fclose(fp); - - /* Initialize the bot */ - ret = tgbot_init(&bot, token); - if (ret != TGBOT_OK) { - fprintf(stderr, "tgbot_init()\n"); - exit(1); - } - - /* Calling tgbot_get_me() you can get bot's info */ - fprintf(stdout, "Running Telegram bot...\nPress Ctrl-C to close.\n"); - - tgbot_update update; - /* Allocate the new inline keyboard (remember to free!) */ - keyboard = tgbot_new_inlinekeyboard(1, 2); - if (keyboard == NULL) { - tgbot_destroy(&bot); - return 1; - } - - /* Populate the InlineKeyboardMarkup */ - tgbot_inlinekeyboard_button(keyboard, 0, 0, "Google", "https://google.com", ""); - tgbot_inlinekeyboard_button(keyboard, 0, 1, "Callback", "", "test-callback"); - /* If you want 3 buttons on 2 rows, for example on the first row 2 buttons and on the second only one */ - /* you have to put rows = 2, columns = 2 and pass an empty string to .text field */ - - /* Main loop */ - while (run) { - ret = tgbot_get_update(&bot, &update, callback_handler); - if (ret != TGBOT_OK) { - fprintf(stderr, "tgbot_get_updates()\n"); - continue; - } - - if (update.message_id > 0) { - parse_command(&bot, &update); - } - } - - tgbot_destroy_inlinekeyboard(keyboard); - tgbot_destroy(&bot); - - return 0; -} +#include +#include +#include +#include +#include + +#include + +#define WELCOME_MSG "Hi there! This bot is coded in C." + +void parse_command(tgbot_s *bot, tgbot_update_s *update); +void sighandler(int signum); +void callback_parser(tgbot_s *bot, tgbot_cbquery_s *query); + +bool run = true; +tgbot_s *bot; +tgbot_inlinekeyboard_s *keyboard; + +/* Callback handler function */ +void callback_handler(tgbot_s *bot, tgbot_cbquery_s *query) { + if (strcmp("test-callback", query->data) == 0) { + /* Handle `test-callback` */ + tgbot_inlinekeyboard_s *home_keyboard = tgbot_inlinekb_new(1, 1); + /* Add buttons */ + tgbot_inlinekb_button(home_keyboard, 0, 0, "Home", "", "home"); + tgbot_edit_message_text(bot, query->chat_id, query->message_id, "Callback called!", home_keyboard); + tgbot_inlinekb_free(home_keyboard); + + return; + } else if (strcmp("home", query->data) == 0) { + tgbot_edit_message_text(bot, query->chat_id, query->message_id, WELCOME_MSG, keyboard); + + return; + } +} + +void sighandler(int signum) { + fprintf(stdout, "Closing...\n"); + run = false; +} + +void parse_command(tgbot_s *bot, tgbot_update_s *update) { + tgbot_rc ret; + + if (strcmp("/start", update->text) == 0) { + ret = tgbot_send_message(bot, update->chat_id, WELCOME_MSG, "Markdown", keyboard); + + if (ret != TGBOT_OK) { + fprintf(stderr, "Failed to send message\n"); + } + + return; + } + + /* Echo the message */ + ret = tgbot_send_message(bot, update->chat_id, update->text, "Markdown", NULL); + if (ret != TGBOT_OK) { + fprintf(stderr, "Failed to send message\n"); + } +} + +int main(void) { + /* Find "your" way to free the resources */ + signal(SIGINT, sighandler); + + tgbot_rc ret; + char token[256]; + + FILE *fp = fopen(".token", "r"); + if (fp == NULL) { + fprintf(stderr, "No .token file found!\n"); + exit(EXIT_FAILURE); + } + fscanf(fp, "%s", token); + fprintf(stdout, "Token: %s\n", token); + fclose(fp); + + /* Initialize the bot */ + bot = tgbot_new(token); + if (!bot) { + fprintf(stderr, "tgbot_init()\n"); + exit(1); + } + + /* Calling tgbot_get_me() you can get bot's info */ + fprintf(stdout, "Running Telegram bot...\nPress Ctrl-C to close.\n"); + + tgbot_update_s update; + /* Allocate the new inline keyboard (remember to free!) */ + keyboard = tgbot_inlinekb_new(1, 2); + if (keyboard == NULL) { + tgbot_free(bot); + return 1; + } + + /* Populate the InlineKeyboardMarkup */ + tgbot_inlinekb_button(keyboard, 0, 0, "Google", "https://google.com", ""); + tgbot_inlinekb_button(keyboard, 0, 1, "Callback", "", "test-callback"); + /* If you want 3 buttons on 2 rows, for example on the first row 2 buttons and on the second only one */ + /* you have to put rows = 2, columns = 2 and pass an empty string to .text field */ + + /* Main loop */ + while (run) { + ret = tgbot_get_update(bot, &update, callback_handler); + if (ret != TGBOT_OK) { + fprintf(stderr, "tgbot_get_updates()\n"); + continue; + } + + if (update.message_id > 0) { + parse_command(bot, &update); + } + } + + tgbot_inlinekb_free(keyboard); + tgbot_free(bot); + + return 0; +} diff --git a/include/common.h b/include/common.h index 9eabb8a..14fd1f9 100644 --- a/include/common.h +++ b/include/common.h @@ -1,102 +1,108 @@ -#ifndef TGBOT_COMMON_H -#define TGBOT_COMMON_H - -#include -#include - -/** - * @brief A structure used to get curl response. - */ -struct memory_buffer { - char *data; - size_t size; -}; - -/** - * @brief 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. */ -}; -typedef struct tgbot_t tgbot; - -/** - * @brief A structure to represent Bot information got from getMe API. - */ -struct tgbot_me_t { - char first_name[256]; /**< Bot's first name. */ - char username[32]; /**< Bot's username. */ -}; -typedef struct tgbot_me_t tgbot_me; - -/** - * @brief 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; - -/** - * @brief 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; - -/** - * @brief Callback function pointer. - */ -typedef void (*Callback)(tgbot *bot, tgbot_cbquery *query); - -/** - * @brief An enum to represent error codes. - */ -enum tgbot_rc_t { - TGBOT_OK = 0, - TGBOT_INIT_ERROR, - TGBOT_REQUEST_ERROR, - TGBOT_GETUPDATES_ERROR, - TGBOT_GETME_ERROR, - TGBOT_SENDMESSAGE_ERROR, - TGBOT_EDITMESSAGETEXT_ERROR, - TGBOT_SENDDICE_ERROR, - TGBOT_TELEGRAM_OK_ERROR, -}; -typedef enum tgbot_rc_t tgbot_rc; - -enum tgbot_json_opt_type_t { - tgbot_opt_int, - tgbot_opt_int64, - tgbot_opt_string, - tgbot_opt_inlinekeyboard, - tgbot_opt_null, -}; -typedef enum tgbot_json_opt_type_t tgbot_json_opt_type; - -struct tgbot_json_option_t { - char key[32]; - void *value; - tgbot_json_opt_type type; -}; -typedef struct tgbot_json_option_t tgbot_json_option; - -#endif +#ifndef TGBOT_COMMON_H +#define TGBOT_COMMON_H + +#include +#include + +#define TOKEN_SIZE 128 +#define API_SIZE 512 + +#define FIRSTNAME_SIZE 256 +#define USERNAME_SIZE 32 + +#define CHAT_LASTNAME_SIZE 256 +#define CHAT_TYPE_SIZE 32 +#define CHAT_TEXT_SIZE 4096 + +/** + * @brief A structure used to get curl response. + */ +struct memory_buffer { + char *data; + size_t size; +}; + +/** + * @brief A structure to represent bot object. + */ +typedef struct tgbot { + char token[TOKEN_SIZE]; /**< Bot token. */ + char api[API_SIZE]; /**< Bot API url. */ + int32_t offset; /**< Bot offset. */ +} tgbot_s; + +/** + * @brief A structure to represent Bot information got from getMe API. + */ +typedef struct tgbot_me { + char first_name[FIRSTNAME_SIZE]; /**< Bot's first name. */ + char username[USERNAME_SIZE]; /**< Bot's username. */ +} tgbot_me_s; + +/** + * @brief A structure to represent Update object. + */ +typedef struct tgbot_update { + int64_t update_id; /**< Update id. */ + long message_id; /**< Message id. */ + int64_t chat_id; /**< Chat id. */ + char chat_first_name[FIRSTNAME_SIZE]; /**< Chat first name. */ + char chat_last_name[CHAT_LASTNAME_SIZE]; /**< Chat last name. */ + char chat_username[USERNAME_SIZE]; /**< Chat username. */ + char chat_type[CHAT_TYPE_SIZE]; /**< Chat type (private/public). */ + int32_t date; /**< Date in unix timestamp. */ + char text[CHAT_TEXT_SIZE]; /**< Message text. */ +} tgbot_update_s; + +/** + * @brief A structure to represent CallbackQuery object. + */ +typedef struct tgbot_cbquery { + int64_t update_id; + long message_id; + int64_t chat_id; + char chat_username[USERNAME_SIZE]; + int32_t date; + char text[CHAT_TEXT_SIZE]; + char chat_instance[128]; + char data[64]; /**> Callback data. */ +} tgbot_cbquery_s; + +/** + * @brief Callback function pointer. + */ +typedef void (*Callback)(tgbot_s *bot, tgbot_cbquery_s *query); + +/** + * @brief An enum to represent error codes. + */ +enum tgbot_rc_t { + TGBOT_OK = 0, + TGBOT_INIT_ERROR, + TGBOT_REQUEST_ERROR, + TGBOT_GETUPDATES_ERROR, + TGBOT_GETME_ERROR, + TGBOT_SENDMESSAGE_ERROR, + TGBOT_EDITMESSAGETEXT_ERROR, + TGBOT_SENDDICE_ERROR, + TGBOT_TELEGRAM_OK_ERROR, +}; +typedef enum tgbot_rc_t tgbot_rc; + +enum tgbot_json_opt_type_t { + tgbot_opt_int, + tgbot_opt_int64, + tgbot_opt_string, + tgbot_opt_inlinekeyboard, + tgbot_opt_null, +}; +typedef enum tgbot_json_opt_type_t tgbot_json_opt_type; + +struct tgbot_json_option_t { + char key[32]; + void *value; + tgbot_json_opt_type type; +}; +typedef struct tgbot_json_option_t tgbot_json_option; + +#endif diff --git a/include/json.h b/include/json.h index 3e36a7a..0eb763c 100644 --- a/include/json.h +++ b/include/json.h @@ -1,10 +1,11 @@ -#ifndef TGBOT_JSON_H -#define TGBOT_JSON_H - -#include "types.h" -#include - -json_object *tgbot_json_builder(tgbot_json_option *options, size_t optionslen); -json_object *tgbot_new_inlinekeyboardmarkup(tgbot_inlinekeyboard *keyboard); - -#endif +#ifndef TGBOT_JSON_H +#define TGBOT_JSON_H + +#include "types.h" +#include + +json_object *json_builder(tgbot_json_option *options, size_t optionslen); + +json_object *json_ikb_new(tgbot_inlinekeyboard_s *keyboard); + +#endif diff --git a/include/methods.h b/include/methods.h index fa06421..9af51b6 100644 --- a/include/methods.h +++ b/include/methods.h @@ -1,23 +1,23 @@ -#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(const 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, const char *text, const char *parse_mode, tgbot_inlinekeyboard *reply_markup); -tgbot_rc tgbot_send_dice(tgbot *bot, int64_t chat_id, const char *emoji); - -/* Updating Methods */ -tgbot_rc tgbot_edit_message_text(tgbot *bot, int64_t chat_id, long message_id, const char *text, tgbot_inlinekeyboard *keyboard); - -#endif +#ifndef TGBOT_METHODS_H +#define TGBOT_METHODS_H + +#include "types.h" +#include + +tgbot_rc tgbot_get_update(tgbot_s *bot, tgbot_update_s *update, Callback cbq_handler); +tgbot_rc tgbot_parse_message(tgbot_s *bot, tgbot_update_s *update, json_object *result); +tgbot_rc tgbot_parse_cbquery(tgbot_s *bot, tgbot_cbquery_s *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(const char *url, struct memory_buffer **mb, json_object *json); + +/* Methods */ +tgbot_rc tgbot_get_me(tgbot_s *bot, tgbot_me_s *me); +tgbot_rc tgbot_send_message(tgbot_s *bot, int64_t chat_id, const char *text, const char *parse_mode, tgbot_inlinekeyboard_s *reply_markup); +tgbot_rc tgbot_send_dice(tgbot_s *bot, int64_t chat_id, const char *emoji); + +/* Updating Methods */ +tgbot_rc tgbot_edit_message_text(tgbot_s *bot, int64_t chat_id, long message_id, const char *text, tgbot_inlinekeyboard_s *keyboard); + +#endif diff --git a/include/tgbot.h b/include/tgbot.h index 2bcb630..68daf7a 100644 --- a/include/tgbot.h +++ b/include/tgbot.h @@ -1,25 +1,24 @@ -#ifndef TGBOT_MAIN_H -#define TGBOT_MAIN_H - -#include "common.h" -#include "methods.h" -#include "types.h" - -/** - * @brief Initializes the Bot object. - * - * @param[out] bot The Bot object. - * @param[in] token The Bot token (obtained from @BotFather). - * - * @return TGBOT_OK on success. - */ -tgbot_rc tgbot_init(tgbot *bot, char *token); - -/** - * @brief Cleans the memory. - * - * @param[out] bot The Bot object. - */ -void tgbot_destroy(tgbot *bot); - -#endif // TGBOT_H +#ifndef TGBOT_MAIN_H +#define TGBOT_MAIN_H + +#include "common.h" +#include "methods.h" +#include "types.h" + +/** + * @brief Create a new Bot object. + * + * @param[in] token The Bot token (obtained from @BotFather). + * + * @return Bot pointer. + */ +tgbot_s *tgbot_new(char *token); + +/** + * @brief Cleans the memory. + * + * @param[out] bot The Bot object. + */ +void tgbot_free(tgbot_s *bot); + +#endif // TGBOT_H diff --git a/include/types.h b/include/types.h index 03081ca..ea1d428 100644 --- a/include/types.h +++ b/include/types.h @@ -1,68 +1,70 @@ -#ifndef TGBOT_TYPES_H -#define TGBOT_TYPES_H - -#include "common.h" - -/** - * @brief Represents a single button on an inline keyboard. - */ -struct tgbot_inlinekeyboardbutton_t { - char text[200]; /**< Text of the button. If empty, the button is ignored. */ - char url[200]; /**< (Optional) URL to be opened when the button is pressed. */ - char callback_data[64]; /**< (Optional) Data sent to the bot when the button is pressed. */ -}; -typedef struct tgbot_inlinekeyboardbutton_t tgbot_inlinekeyboardbutton; - -/** - * @brief Represents an inline keyboard. - */ -struct tgbot_inlinekeyboard_t { - size_t rows; /**< Number of rows in the keyboard. */ - size_t columns; /**< Number of columns per row. */ - struct tgbot_inlinekeyboardbutton_t *buttons; /**< Array of buttons. */ -}; -typedef struct tgbot_inlinekeyboard_t tgbot_inlinekeyboard; - -/** - * @brief Allocates a new inline keyboard. - * - * @param[in] rows Number of rows in the keyboard. - * @param[in] columns Number of columns in each row. - * - * @return The pointer to the keyboard or NULL on allocation failure. - */ -tgbot_inlinekeyboard *tgbot_new_inlinekeyboard(size_t rows, size_t columns); - -/** - * @brief Adds or updates a button at the specified position in the keyboard. - * - * @param[out] keyboard Pointer to the keyboard to modify. - * @param[in] row Row index of the button (starting from 0). - * @param[in] column Column index of the button (starting from 0). - * @param[in] text Display text for the button. - * @param[in] url Optional URL for the button. - * @param[in] callback_data Optional callback data for the button. - * - * @return TGBOT_OK on success. - */ -tgbot_rc tgbot_inlinekeyboard_button(tgbot_inlinekeyboard *keyboard, size_t row, size_t column, const char *text, const char *url, const char *callback_data); - -/** - * @brief Returns a pointer to the keyboard's button. - * - * @param[in] keyboard Keyboard. - * @param[in] row Row index of the button. - * @param[in] column Column index of the button. - * - * @return Pointer to the button, or NULL if the position is invalid. - */ -tgbot_inlinekeyboardbutton *tgbot_inlinekeyboard_button_at(tgbot_inlinekeyboard *keyboard, size_t row, size_t column); - -/** - * @brief Frees all memory associated with the given inline keyboard. - * - * @param[in,out] keyboard Pointer to the keyboard structure to deallocate. - */ -void tgbot_destroy_inlinekeyboard(tgbot_inlinekeyboard *keyboard); - -#endif +#ifndef TGBOT_TYPES_H +#define TGBOT_TYPES_H + +#include "common.h" + +#define TEXT_SIZE 200 +#define URL_SIZE 200 +#define CB_DATA_SIZE 64 + +/** + * @brief Represents a single button on an inline keyboard. + */ +typedef struct tgbot_inlinekeyboardbutton { + char text[TEXT_SIZE]; /**< Text of the button. If empty, the button is ignored. */ + char url[URL_SIZE]; /**< (Optional) URL to be opened when the button is pressed. */ + char callback_data[CB_DATA_SIZE]; /**< (Optional) Data sent to the bot when the button is pressed. */ +} tgbot_inlinekeyboardbutton_s; + +/** + * @brief Represents an inline keyboard. + */ +typedef struct tgbot_inlinekeyboard { + size_t rows; /**< Number of rows in the keyboard. */ + size_t columns; /**< Number of columns per row. */ + tgbot_inlinekeyboardbutton_s *buttons; /**< Array of buttons. */ +} tgbot_inlinekeyboard_s; + +/** + * @brief Allocates a new inline keyboard. + * + * @param[in] rows Number of rows in the keyboard. + * @param[in] columns Number of columns in each row. + * + * @return The pointer to the keyboard or NULL on allocation failure. + */ +tgbot_inlinekeyboard_s *tgbot_inlinekb_new(size_t rows, size_t columns); + +/** + * @brief Adds or updates a button at the specified position in the keyboard. + * + * @param[out] keyboard Pointer to the keyboard to modify. + * @param[in] row Row index of the button (starting from 0). + * @param[in] column Column index of the button (starting from 0). + * @param[in] text Display text for the button. + * @param[in] url Optional URL for the button. + * @param[in] callback_data Optional callback data for the button. + * + * @return TGBOT_OK on success. + */ +tgbot_rc tgbot_inlinekb_button(tgbot_inlinekeyboard_s *keyboard, size_t row, size_t column, const char *text, const char *url, const char *callback_data); + +/** + * @brief Returns a pointer to the keyboard's button. + * + * @param[in] keyboard Keyboard. + * @param[in] row Row index of the button. + * @param[in] column Column index of the button. + * + * @return Pointer to the button, or NULL if the position is invalid. + */ +tgbot_inlinekeyboardbutton_s *tgbot_inlinekb_button_at(tgbot_inlinekeyboard_s *keyboard, size_t row, size_t column); + +/** + * @brief Frees all memory associated with the given inline keyboard. + * + * @param[in,out] keyboard Pointer to the keyboard structure to deallocate. + */ +void tgbot_inlinekb_free(tgbot_inlinekeyboard_s *keyboard); + +#endif diff --git a/meson.build b/meson.build index ad98ed3..18de525 100644 --- a/meson.build +++ b/meson.build @@ -1,30 +1,35 @@ -project('tgbot', 'c', version: '0.1') - -sources = [] -subdir('src') - -inc_dir = include_directories('include') - -curl_dep = dependency('libcurl') -json_c_dep = dependency('json-c') - -deps = [curl_dep, json_c_dep] - -install_headers('include/tgbot.h') - -tgbot_lib = library( - 'tgbot', - sources, - dependencies: deps, - include_directories: inc_dir, - install: true, -) - -# Example (windows) -executable( - 'example', - 'examples/echobot/echobot.c', - dependencies: deps, - link_with: tgbot_lib, - include_directories: inc_dir, -) +project( + 'tgbot', + 'c', + version: '0.1', + default_options: ['warning_level=3', 'c_std=c18'], +) + +sources = [] +subdir('src') + +inc_dir = include_directories('include') + +curl_dep = dependency('libcurl') +json_c_dep = dependency('json-c') + +deps = [curl_dep, json_c_dep] + +install_headers('include/tgbot.h') + +tgbot_lib = library( + 'tgbot', + sources, + dependencies: deps, + include_directories: inc_dir, + install: true, +) + +# Example (windows) +executable( + 'example', + 'examples/echobot/echobot.c', + dependencies: deps, + link_with: tgbot_lib, + include_directories: inc_dir, +) \ No newline at end of file diff --git a/src/json.c b/src/json.c index c54978f..8d68b0c 100644 --- a/src/json.c +++ b/src/json.c @@ -1,56 +1,56 @@ -#include - -#include "json.h" - -json_object *tgbot_json_builder(tgbot_json_option *options, size_t optionslen) { - json_object *rjson = json_object_new_object(); - - for (size_t i = 0; i < optionslen; ++i) { - if (options[i].type == tgbot_opt_int) { - json_object_object_add(rjson, options[i].key, json_object_new_int(*((int32_t *)options[i].value))); - } else if (options[i].type == tgbot_opt_string) { - if (!options[i].value) { - continue; - } - json_object_object_add(rjson, options[i].key, json_object_new_string((char *)options[i].value)); - } else if (options[i].type == tgbot_opt_int64) { - json_object_object_add(rjson, options[i].key, json_object_new_int64(*((int64_t *)options[i].value))); - } else if (options[i].type == tgbot_opt_inlinekeyboard) { - if (options[i].value != NULL) { - json_object *reply_markup = tgbot_new_inlinekeyboardmarkup((tgbot_inlinekeyboard *)options[i].value); - json_object_object_add(rjson, "reply_markup", reply_markup); - } - } else if (options[i].type == tgbot_opt_null) { - continue; - } - } - - return rjson; -} - -json_object *tgbot_new_inlinekeyboardmarkup(tgbot_inlinekeyboard *keyboard) { - json_object *reply_markup = json_object_new_object(); - json_object *inline_keyboard_array = json_object_new_array(); - - for (size_t i = 0; i < keyboard->rows; ++i) { - json_object *row = json_object_new_array(); - for (size_t j = 0; j < keyboard->columns; ++j) { - tgbot_inlinekeyboardbutton *kbbutton = tgbot_inlinekeyboard_button_at(keyboard, i, j); - if (strcmp(kbbutton->text, "") == 0) { - continue; - } - - json_object *button = json_object_new_object(); - json_object_object_add(button, "text", json_object_new_string(kbbutton->text)); - json_object_object_add(button, "url", json_object_new_string(kbbutton->url)); - json_object_object_add(button, "callback_data", json_object_new_string(kbbutton->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; -} +#include + +#include "json.h" + +json_object *json_builder(tgbot_json_option *options, size_t optionslen) { + json_object *rjson = json_object_new_object(); + + for (size_t i = 0; i < optionslen; ++i) { + if (options[i].type == tgbot_opt_int) { + json_object_object_add(rjson, options[i].key, json_object_new_int(*((int32_t *)options[i].value))); + } else if (options[i].type == tgbot_opt_string) { + if (!options[i].value) { + continue; + } + json_object_object_add(rjson, options[i].key, json_object_new_string((char *)options[i].value)); + } else if (options[i].type == tgbot_opt_int64) { + json_object_object_add(rjson, options[i].key, json_object_new_int64(*((int64_t *)options[i].value))); + } else if (options[i].type == tgbot_opt_inlinekeyboard) { + if (options[i].value != NULL) { + json_object *reply_markup = json_ikb_new((tgbot_inlinekeyboard_s *)options[i].value); + json_object_object_add(rjson, "reply_markup", reply_markup); + } + } else if (options[i].type == tgbot_opt_null) { + continue; + } + } + + return rjson; +} + +json_object *json_ikb_new(tgbot_inlinekeyboard_s *keyboard) { + json_object *reply_markup = json_object_new_object(); + json_object *inline_keyboard_array = json_object_new_array(); + + for (size_t i = 0; i < keyboard->rows; ++i) { + json_object *row = json_object_new_array(); + for (size_t j = 0; j < keyboard->columns; ++j) { + tgbot_inlinekeyboardbutton_s *kbbutton = tgbot_inlinekb_button_at(keyboard, i, j); + if (strcmp(kbbutton->text, "") == 0) { + continue; + } + + json_object *button = json_object_new_object(); + json_object_object_add(button, "text", json_object_new_string(kbbutton->text)); + json_object_object_add(button, "url", json_object_new_string(kbbutton->url)); + json_object_object_add(button, "callback_data", json_object_new_string(kbbutton->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/methods.c b/src/methods.c index f9e9705..5ffa12f 100644 --- a/src/methods.c +++ b/src/methods.c @@ -1,334 +1,333 @@ -#include -#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)); - - int limit = 1; - int timeout = 30; - tgbot_json_option options[3] = { - {"offset", &bot->offset, tgbot_opt_int64}, - {"limit", &limit, tgbot_opt_int}, - {"timeout", &timeout, tgbot_opt_int}, - }; - json_object *rjson = tgbot_json_builder(options, 3); - - snprintf(url, sizeof(url), "%sgetUpdates", bot->api); - - struct memory_buffer *mb; - tgbot_rc ret = tgbot_request(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 if (cbq_handler != NULL) { - 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"); - if (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"); - if (chat_id) { - update->chat_id = json_object_get_int64(chat_id); - } - - json_object *chat_first_name = json_object_object_get(chat, "first_name"); - if (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) { - strncpy(update->chat_last_name, json_object_get_string(chat_last_name), sizeof(update->chat_last_name) - 1); - } - - 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); - } - - json_object *chat_type = json_object_object_get(chat, "type"); - if (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"); - if (date) { - update->date = json_object_get_int(date); - } - - json_object *text = json_object_object_get(message, "text"); - if (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"); - if (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"); - if (chat_id) { - query->chat_id = json_object_get_int64(chat_id); - } - - json_object *chat_username = json_object_object_get(chat, "username"); - if (chat_username) { - strncpy(query->chat_username, json_object_get_string(chat_username), sizeof(query->chat_username) - 1); - } - - json_object *date = json_object_object_get(message, "date"); - if (date) { - query->date = json_object_get_int(date); - } - - json_object *text = json_object_object_get(message, "text"); - if (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"); - if (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"); - if (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; -} - -size_t discard_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { - return size * nmemb; -} - -tgbot_rc tgbot_request(const char *url, struct memory_buffer **mb, json_object *json) { - CURL *curl = curl_easy_init(); - if (!curl) { - return TGBOT_REQUEST_ERROR; - } - - const char *json_string = NULL; - - 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(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - - if (mb != NULL) { - *mb = calloc(1, sizeof(struct memory_buffer)); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, *mb); - } else { - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discard_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL); - } - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(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(curl, CURLOPT_POSTFIELDS, json_string); - } - - CURLcode res = curl_easy_perform(curl); - - curl_slist_free_all(headers); - - if (res != CURLE_OK) { - if (mb != NULL && *mb) { - free((*mb)->data); - free(*mb); - *mb = NULL; - } - - return TGBOT_REQUEST_ERROR; - } - - curl_easy_cleanup(curl); - - 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(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, const char *text, const char *parse_mode, tgbot_inlinekeyboard *keyboard) { - char url[1024]; - - tgbot_json_option options[4] = { - {"chat_id", &chat_id, tgbot_opt_int64}, - {"text", (void *)text, tgbot_opt_string}, - {"parse_mode", (void *)parse_mode, tgbot_opt_string}, - {"reply_markup", keyboard, tgbot_opt_inlinekeyboard}, - }; - json_object *rjson = tgbot_json_builder(options, 4); - - snprintf(url, sizeof(url), "%ssendMessage", bot->api); - - tgbot_rc ret = tgbot_request(url, NULL, rjson); - json_object_put(rjson); - - if (ret != TGBOT_OK) { - return TGBOT_SENDMESSAGE_ERROR; - } - - return TGBOT_OK; -} - -tgbot_rc tgbot_edit_message_text(tgbot *bot, int64_t chat_id, long message_id, const char *text, tgbot_inlinekeyboard *keyboard) { - char url[1024]; - - tgbot_json_option options[4] = { - {"chat_id", &chat_id, tgbot_opt_int64}, - {"message_id", &message_id, tgbot_opt_int}, - {"text", (void *)text, tgbot_opt_string}, - {"reply_markup", keyboard, tgbot_opt_inlinekeyboard}, - }; - json_object *rjson = tgbot_json_builder(options, 4); - - snprintf(url, sizeof(url), "%seditMessageText", bot->api); - - tgbot_rc ret = tgbot_request(url, NULL, rjson); - json_object_put(rjson); - - if (ret != TGBOT_OK) { - return TGBOT_EDITMESSAGETEXT_ERROR; - } - - return TGBOT_OK; -} - -tgbot_rc tgbot_send_dice(tgbot *bot, int64_t chat_id, const char *emoji) { - char url[1024]; - - tgbot_json_option options[2] = { - {"chat_id", &chat_id, tgbot_opt_int64}, - {"emoji", (void *)emoji, tgbot_opt_string}, - }; - json_object *rjson = tgbot_json_builder(options, 2); - - snprintf(url, sizeof(url), "%ssendDice", bot->api); - - tgbot_rc ret = tgbot_request(url, NULL, rjson); - json_object_put(rjson); - - if (ret != TGBOT_OK) { - return TGBOT_SENDDICE_ERROR; - } - - return TGBOT_OK; -} +#include +#include + +#include "json.h" +#include "methods.h" + +tgbot_rc tgbot_get_update(tgbot_s *bot, tgbot_update_s *update, Callback cbq_handler) { + char url[1024]; + + memset(update, 0, sizeof(tgbot_update_s)); + + int limit = 1; + int timeout = 30; + tgbot_json_option options[3] = { + {"offset", &bot->offset, tgbot_opt_int64}, + {"limit", &limit, tgbot_opt_int}, + {"timeout", &timeout, tgbot_opt_int}, + }; + json_object *rjson = json_builder(options, 3); + + snprintf(url, sizeof(url), "%sgetUpdates", bot->api); + + struct memory_buffer *mb; + tgbot_rc ret = tgbot_request(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 if (cbq_handler != NULL) { + tgbot_cbquery_s query; + tgbot_parse_cbquery(bot, &query, result, cbq_handler); + } + + json_object_put(json); + + return TGBOT_OK; +} + +tgbot_rc tgbot_parse_message(tgbot_s *bot, tgbot_update_s *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"); + if (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"); + if (chat_id) { + update->chat_id = json_object_get_int64(chat_id); + } + + json_object *chat_first_name = json_object_object_get(chat, "first_name"); + if (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) { + strncpy(update->chat_last_name, json_object_get_string(chat_last_name), sizeof(update->chat_last_name) - 1); + } + + 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); + } + + json_object *chat_type = json_object_object_get(chat, "type"); + if (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"); + if (date) { + update->date = json_object_get_int(date); + } + + json_object *text = json_object_object_get(message, "text"); + if (text) { + strncpy(update->text, json_object_get_string(text), sizeof(update->text) - 1); + } + + return TGBOT_OK; +} + +tgbot_rc tgbot_parse_cbquery(tgbot_s *bot, tgbot_cbquery_s *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"); + if (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"); + if (chat_id) { + query->chat_id = json_object_get_int64(chat_id); + } + + json_object *chat_username = json_object_object_get(chat, "username"); + if (chat_username) { + strncpy(query->chat_username, json_object_get_string(chat_username), sizeof(query->chat_username) - 1); + } + + json_object *date = json_object_object_get(message, "date"); + if (date) { + query->date = json_object_get_int(date); + } + + json_object *text = json_object_object_get(message, "text"); + if (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"); + if (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"); + if (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; +} + +size_t discard_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { + return size * nmemb; +} + +tgbot_rc tgbot_request(const char *url, struct memory_buffer **mb, json_object *json) { + CURL *curl = curl_easy_init(); + if (!curl) { + return TGBOT_REQUEST_ERROR; + } + + const char *json_string = NULL; + + 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(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + if (mb != NULL) { + *mb = calloc(1, sizeof(struct memory_buffer)); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, *mb); + } else { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discard_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL); + } + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(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(curl, CURLOPT_POSTFIELDS, json_string); + } + + CURLcode res = curl_easy_perform(curl); + + curl_slist_free_all(headers); + + if (res != CURLE_OK) { + if (mb != NULL && *mb) { + free((*mb)->data); + free(*mb); + *mb = NULL; + } + + return TGBOT_REQUEST_ERROR; + } + + curl_easy_cleanup(curl); + + return TGBOT_OK; +} + +tgbot_rc tgbot_get_me(tgbot_s *bot, tgbot_me_s *me) { + char url[1024]; + snprintf(url, sizeof(url), "%sgetMe", bot->api); + + struct memory_buffer *mb; + tgbot_rc ret = tgbot_request(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_s *bot, int64_t chat_id, const char *text, const char *parse_mode, tgbot_inlinekeyboard_s *keyboard) { + char url[1024]; + + tgbot_json_option options[4] = { + {"chat_id", &chat_id, tgbot_opt_int64}, + {"text", (void *)text, tgbot_opt_string}, + {"parse_mode", (void *)parse_mode, tgbot_opt_string}, + {"reply_markup", keyboard, tgbot_opt_inlinekeyboard}, + }; + json_object *rjson = json_builder(options, 4); + + snprintf(url, sizeof(url), "%ssendMessage", bot->api); + + tgbot_rc ret = tgbot_request(url, NULL, rjson); + json_object_put(rjson); + + if (ret != TGBOT_OK) { + return TGBOT_SENDMESSAGE_ERROR; + } + + return TGBOT_OK; +} + +tgbot_rc tgbot_edit_message_text(tgbot_s *bot, int64_t chat_id, long message_id, const char *text, tgbot_inlinekeyboard_s *keyboard) { + char url[1024]; + + tgbot_json_option options[4] = { + {"chat_id", &chat_id, tgbot_opt_int64}, + {"message_id", &message_id, tgbot_opt_int}, + {"text", (void *)text, tgbot_opt_string}, + {"reply_markup", keyboard, tgbot_opt_inlinekeyboard}, + }; + json_object *rjson = json_builder(options, 4); + + snprintf(url, sizeof(url), "%seditMessageText", bot->api); + + tgbot_rc ret = tgbot_request(url, NULL, rjson); + json_object_put(rjson); + + if (ret != TGBOT_OK) { + return TGBOT_EDITMESSAGETEXT_ERROR; + } + + return TGBOT_OK; +} + +tgbot_rc tgbot_send_dice(tgbot_s *bot, int64_t chat_id, const char *emoji) { + char url[1024]; + + tgbot_json_option options[2] = { + {"chat_id", &chat_id, tgbot_opt_int64}, + {"emoji", (void *)emoji, tgbot_opt_string}, + }; + json_object *rjson = json_builder(options, 2); + + snprintf(url, sizeof(url), "%ssendDice", bot->api); + + tgbot_rc ret = tgbot_request(url, NULL, rjson); + json_object_put(rjson); + + if (ret != TGBOT_OK) { + return TGBOT_SENDDICE_ERROR; + } + + return TGBOT_OK; +} diff --git a/src/tgbot.c b/src/tgbot.c index b8071ef..28595a8 100644 --- a/src/tgbot.c +++ b/src/tgbot.c @@ -1,20 +1,26 @@ -#include -#include -#include -#include - -#include "tgbot.h" - -tgbot_rc tgbot_init(tgbot *bot, char *token) { - curl_global_init(CURL_GLOBAL_DEFAULT); - - snprintf(bot->api, sizeof(bot->api), "https://api.telegram.org/bot%s/", token); - snprintf(bot->token, sizeof(bot->token), "%s", token); - bot->offset = 0; - - return TGBOT_OK; -} - -void tgbot_destroy(tgbot *bot) { - curl_global_cleanup(); -} +#include +#include +#include +#include + +#include "tgbot.h" + +tgbot_s *tgbot_new(char *token) { + tgbot_s *bot = malloc(sizeof(tgbot_s)); + if (!bot) { + return NULL; + } + + curl_global_init(CURL_GLOBAL_DEFAULT); + + snprintf(bot->api, sizeof(bot->api), "https://api.telegram.org/bot%s/", token); + snprintf(bot->token, sizeof(bot->token), "%s", token); + bot->offset = 0; + + return bot; +} + +void tgbot_free(tgbot_s *bot) { + curl_global_cleanup(); + free(bot); +} diff --git a/src/types.c b/src/types.c index 4da0c23..e7ecf1e 100644 --- a/src/types.c +++ b/src/types.c @@ -1,41 +1,41 @@ -#include -#include - -#include "types.h" - -tgbot_inlinekeyboard *tgbot_new_inlinekeyboard(size_t rows, size_t columns) { - tgbot_inlinekeyboard *keyboard = (tgbot_inlinekeyboard *)malloc(sizeof(tgbot_inlinekeyboard)); - if (!keyboard) - return NULL; - keyboard->rows = rows; - keyboard->columns = columns; - keyboard->buttons = (tgbot_inlinekeyboardbutton *)malloc(rows * columns * sizeof(tgbot_inlinekeyboardbutton)); - if (!keyboard->buttons) { - free(keyboard); - - return NULL; - } - - memset(keyboard->buttons, 0, rows * columns * sizeof(tgbot_inlinekeyboardbutton)); - - return keyboard; -} - -void tgbot_destroy_inlinekeyboard(tgbot_inlinekeyboard *keyboard) { - free(keyboard->buttons); - free(keyboard); -} - -tgbot_rc tgbot_inlinekeyboard_button(tgbot_inlinekeyboard *keyboard, size_t row, size_t column, const char *text, const char *url, const char *callback_data) { - tgbot_inlinekeyboardbutton *button = tgbot_inlinekeyboard_button_at(keyboard, row, column); - - strncpy(button->text, text, sizeof(button->text) - 1); - strncpy(button->url, url, sizeof(button->url) - 1); - strncpy(button->callback_data, callback_data, sizeof(button->callback_data) - 1); - - return TGBOT_OK; -} - -tgbot_inlinekeyboardbutton *tgbot_inlinekeyboard_button_at(tgbot_inlinekeyboard *keyboard, size_t row, size_t column) { - return &keyboard->buttons[row * keyboard->columns + column]; -} +#include +#include + +#include "types.h" + +tgbot_inlinekeyboard_s *tgbot_inlinekb_new(size_t rows, size_t columns) { + tgbot_inlinekeyboard_s *keyboard = (tgbot_inlinekeyboard_s *)malloc(sizeof(tgbot_inlinekeyboard_s)); + if (!keyboard) + return NULL; + keyboard->rows = rows; + keyboard->columns = columns; + keyboard->buttons = (tgbot_inlinekeyboardbutton_s *)malloc(rows * columns * sizeof(tgbot_inlinekeyboardbutton_s)); + if (!keyboard->buttons) { + free(keyboard); + + return NULL; + } + + memset(keyboard->buttons, 0, rows * columns * sizeof(tgbot_inlinekeyboardbutton_s)); + + return keyboard; +} + +void tgbot_inlinekb_free(tgbot_inlinekeyboard_s *keyboard) { + free(keyboard->buttons); + free(keyboard); +} + +tgbot_inlinekeyboardbutton_s *tgbot_inlinekb_button_at(tgbot_inlinekeyboard_s *keyboard, size_t row, size_t column) { + return &keyboard->buttons[row * keyboard->columns + column]; +} + +tgbot_rc tgbot_inlinekb_button(tgbot_inlinekeyboard_s *keyboard, size_t row, size_t column, const char *text, const char *url, const char *callback_data) { + tgbot_inlinekeyboardbutton_s *button = tgbot_inlinekb_button_at(keyboard, row, column); + + strncpy(button->text, text, sizeof(button->text) - 1); + strncpy(button->url, url, sizeof(button->url) - 1); + strncpy(button->callback_data, callback_data, sizeof(button->callback_data) - 1); + + return TGBOT_OK; +}