diff --git a/README.md b/README.md
index aad8465..5df5b79 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,63 @@
-# tgbot
-
-A minimal C Telegram API Framework.
-
-## Requirements
-
-- libcurl
-- json-c
-
-> Note: This project is purely educational. It does not aim to cover the entire Telegram Bot API, but only a selected subset of methods.
-
-## How to build
-
-
-
-Linux
-
-```bash
-$ meson setup build
-$ cd build
-$ meson compile
-$ meson install
-```
-
-
-
-
-
-Windows
-
-Install all the required library with `vcpkg` and then copy the DLL file.
-
-```powershell
-$ meson setup build --native-file meson-vcpkg.txt
-$ cd build
-$ meson compile
-$ meson install
-```
-
-
-
-## Examples
-
-You can find some examples [here](./examples/).
-
-### Supported Types
-
-- **InlineKeyboardMarkup**
- - Note: Standard `KeyboardMarkup` is intentionally not supported.
-
-#### Supported Methods
-
-- `getMe`
-- `sendMessage`
-- `editMessageText`
-- `sendDice`
-
-## Roadmap
-
-- `sendPhoto`
-- `sendAudio`
-- `sendDocument`
-- `sendVideo`
+# tgbot
+
+A minimal C Telegram API library.
+
+## Requirements
+
+- libcurl
+- json-c
+
+> Note: This project is purely educational. It does not aim to cover the entire Telegram Bot API, but only a selected subset of methods.
+
+## How to build
+
+
+
+Linux
+
+```bash
+$ meson setup build
+$ cd build
+$ meson compile
+$ meson install
+```
+
+
+
+
+
+Windows
+
+Install all the required library with `vcpkg` and then copy the DLL file.
+
+```powershell
+$ meson setup build --native-file meson-vcpkg.txt
+$ cd build
+$ meson compile
+$ meson install
+```
+
+
+
+## Examples
+
+You can find some examples [here](./examples/).
+
+### Supported Types
+
+- **InlineKeyboardMarkup**
+ - Note: Standard `KeyboardMarkup` is intentionally not supported.
+
+#### Supported Methods
+
+- `getMe`
+- `sendMessage`
+- `editMessageText`
+- `sendDice`
+- `sendPhoto`
+
+## Roadmap
+
+- `sendAudio`
+- `sendDocument`
+- `sendVideo`
diff --git a/examples/sender/sendpic.c b/examples/sender/sendpic.c
new file mode 100644
index 0000000..7a51682
--- /dev/null
+++ b/examples/sender/sendpic.c
@@ -0,0 +1,39 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define START_MESSAGE "Send /photo to receive a nice landscape!"
+#define PHOTO_PATH "my_photo.jpg"
+
+int main(void) {
+ FILE *tok = fopen(".token", "r");
+ if (!tok) {
+ exit(EXIT_FAILURE);
+ }
+
+ char token[512];
+ fscanf(tok, "%s", token);
+ fprintf(stdout, "Token: %s\n", token);
+
+ tgbot_s *bot = tgbot_new(token);
+ if (!bot) {
+ return -1;
+ }
+
+ tgbot_update_s update;
+ while (1) {
+ tgbot_get_update(bot, &update, NULL);
+
+ if (!strcmp(update.text, "/start")) {
+ tgbot_send_message(bot, update.chat_id, START_MESSAGE, "MARKDOWN", NULL);
+ } else if (!strcmp(update.text, "/photo")) {
+ tgbot_send_photo(bot, update.chat_id, PHOTO_PATH, "Mountains!");
+ }
+ }
+
+ return 0;
+}
diff --git a/include/methods.h b/include/methods.h
index 8acda64..190d632 100644
--- a/include/methods.h
+++ b/include/methods.h
@@ -11,6 +11,7 @@ int tgbot_get_me(const tgbot_s *bot, tgbot_me_s *me);
int tgbot_send_message(const tgbot_s *bot, int64_t chat_id, const char *text, const char *parse_mode,
tgbot_inlinekeyboard_s *reply_markup);
int tgbot_send_dice(const tgbot_s *bot, int64_t chat_id, const char *emoji);
+int tgbot_send_photo(const tgbot_s *bot, int64_t chat_id, const char *path, const char *caption);
/* Updating Methods */
int tgbot_edit_message_text(const tgbot_s *bot, int64_t chat_id, long message_id, const char *text,
diff --git a/meson.build b/meson.build
index 4569c6a..f523a6a 100644
--- a/meson.build
+++ b/meson.build
@@ -58,11 +58,9 @@ if cppcheck.found()
endif
# Example
-example = executable(
+executable(
'example',
- 'examples/inlinekeyboard/inlinekeyboard.c',
+ 'examples/sender/sendpic.c',
dependencies: tgbot_dep,
build_by_default: false,
-)
-
-test('example_inlinekeyboard', example)
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/src/methods.c b/src/methods.c
index fbdfb6d..3eacb4f 100644
--- a/src/methods.c
+++ b/src/methods.c
@@ -10,7 +10,7 @@
#define opt_size(arr) (sizeof(arr) / sizeof(arr[0]))
-static size_t write_callback(const void *ptr, size_t size, size_t nmemb, char *userdata) {
+static 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;
@@ -53,16 +53,12 @@ static int tgbot_request(const char *url, struct memory_buffer **mb, json_object
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, *mb);
} else {
- struct memory_buffer *mb_f = malloc(sizeof *mb_f);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discard_callback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, mb_f);
- if (mb_f) {
- free(mb_f);
- }
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
}
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
- curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 30L);
+ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
if (json != NULL) {
json_string = json_object_to_json_string_ext(json, JSON_C_TO_STRING_PLAIN);
@@ -100,6 +96,60 @@ static int tgbot_execute_method(const tgbot_s *bot, const char *method, tgbot_op
return ret;
}
+static int tgbot_execute_method_multipart(const tgbot_s *bot, const char *method, int64_t chat_id, const char *path,
+ const char *caption) {
+ CURL *curl = curl_easy_init();
+ if (!curl) {
+ return -1;
+ }
+
+ char url[URL_LEN] = {0};
+ int chars = snprintf(url, sizeof(url), "%s%s", bot->api, method);
+ if (chars < 0 || (size_t)chars >= sizeof(url)) {
+ curl_easy_cleanup(curl);
+ return -1;
+ }
+
+ curl_mime *mime = curl_mime_init(curl);
+ curl_mimepart *part;
+
+ char chat_id_str[512];
+ snprintf(chat_id_str, sizeof chat_id_str, "%ld", chat_id);
+
+ part = curl_mime_addpart(mime);
+ curl_mime_data(part, chat_id_str, CURL_ZERO_TERMINATED);
+ curl_mime_name(part, "chat_id");
+
+ part = curl_mime_addpart(mime);
+ curl_mime_filedata(part, path);
+ curl_mime_name(part, "photo");
+
+ part = curl_mime_addpart(mime);
+ curl_mime_data(part, caption, CURL_ZERO_TERMINATED);
+ curl_mime_name(part, "caption");
+
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
+
+ /* Do not print response to output */
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discard_callback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
+
+ CURLcode res = curl_easy_perform(curl);
+ if (res != CURLE_OK) {
+ fprintf(stderr, "curl_easy_perform: %s\n", curl_easy_strerror(res));
+ curl_easy_cleanup(curl);
+ curl_mime_free(mime);
+
+ return -1;
+ }
+
+ curl_easy_cleanup(curl);
+ curl_mime_free(mime);
+
+ return 0;
+}
+
int tgbot_get_update(tgbot_s *bot, tgbot_update_s *update, Callback cbq_handler) {
char url[URL_LEN];
@@ -191,10 +241,14 @@ int tgbot_get_me(const tgbot_s *bot, tgbot_me_s *me) {
const json_object *result = json_object_object_get(json, "result");
json_object *first_name = json_object_object_get(result, "first_name");
- snprintf(me->first_name, sizeof(me->first_name), "%s", json_object_get_string(first_name));
+ if (first_name) {
+ snprintf(me->first_name, sizeof(me->first_name), "%s", json_object_get_string(first_name));
+ }
json_object *username = json_object_object_get(result, "username");
- snprintf(me->username, sizeof(me->username), "%s", json_object_get_string(username));
+ if (username) {
+ snprintf(me->username, sizeof(me->username), "%s", json_object_get_string(username));
+ }
json_object_put(json);
@@ -233,3 +287,7 @@ int tgbot_send_dice(const tgbot_s *bot, int64_t chat_id, const char *emoji) {
return tgbot_execute_method(bot, "sendDice", options, opt_size(options));
}
+
+int tgbot_send_photo(const tgbot_s *bot, int64_t chat_id, const char *path, const char *caption) {
+ return tgbot_execute_method_multipart(bot, "sendPhoto", chat_id, path, caption);
+}