imageЗдравствуйте. Это небольшое продолжение, коротенькой статьи про написание телеграмбота.

В предыдущем примере для получения и отправки сообщений использовалась библиотека libcurl. Здесь же она применяется только для получения, а отправка происходит посредством обычного curl'а.

В примере показано как устанавливать/удалять кнопки (обычные и инлаин) и отправлять картинки (видео, аудио, документы и голосовые сообщения отправляются так же как и картинки).

В исходнике даны необходимые комментарии.

Код
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <unistd.h>

#define BOT_ADDRESS "https://api.telegram.org/bot396653803:AwwhzdUskmV1thviIWOo66cpQ5-3o" // тута ваш токен
#define GET_METHOD_READ_SIZE 128
#define TELWRITESIZE 512 // следите за этим размером

typedef struct {
    size_t size;
    char *text;
} json_message;

char tel_write[TELWRITESIZE] = {0,};

//////////////////////////////// reciv from telegram /////////////////////////////////////////
size_t read_message(void *raw_message, size_t size, size_t nmemb, void *dest) // здесь принимаются сообщения 
{
    size_t real_size = size * nmemb;
    json_message* dest_message = (json_message*) dest;
    dest_message->text = realloc(dest_message->text, dest_message->size + real_size + 1);
    if(dest_message->text == NULL)
     {
       printf("Error: памяти недостаточно\n");
       return 0;
     }

    memcpy(&(dest_message->text[dest_message->size]), raw_message, real_size);
    dest_message->size += real_size;
    dest_message->text[dest_message->size] = '\0';
    return real_size;
}

////////////////////////////////////////////// clear_string ///////////////////////////////////////////////////////
static void clear_string(char *src) // убирает некоторые символы из полученной json-строки
{
  char *dst;
  if(!src) return;
  for(dst = src; *src; src++)
   {
     if(*src == '"' || *src == '{' || *src == '}' || *src == '[' || *src == ']' || *src == '\n') continue; 
     *dst++ = *src;
   }

  *dst = 0;
}

///////////////////////////////////////////////////////////////////////////////////
int main()
{
    char tel_write[TELWRITESIZE] = {0,};
    snprintf(tel_write, TELWRITESIZE - 1, "curl -F 'text=START TELEgetUP' '%s/sendMessage?chat_id=376716150' 2> /dev/null 1> /dev/null", BOT_ADDRESS);
    system(tel_write);
    
    int result;
    json_message in_message = {0, NULL};
    unsigned long update_id = 0; 
    curl_global_init(CURL_GLOBAL_ALL);
    char GET_read[GET_METHOD_READ_SIZE] = {0,};
    int ercount = 0;
    int er_tel = 0;

    while(1)
     {
       in_message.text = malloc(1);
       if(in_message.text == NULL) // если не удалось выделить память за 10 попыток, тогда exit
        {
          ercount++;
          printf("Error! memory not allocated.\n");
          if(ercount > 10) exit(0);
          sleep(1);
          continue;
        }
       in_message.text[0] = '\0';


       snprintf(GET_read, GET_METHOD_READ_SIZE - 1, "%s/getUpdates?limit=1&offset=%lu&timeout=30", BOT_ADDRESS, update_id); // обновления каждые 30 сек (timeout=30)
       CURL *read_handle = curl_easy_init();
       curl_easy_setopt(read_handle, CURLOPT_URL, GET_read);
       curl_easy_setopt(read_handle, CURLOPT_WRITEFUNCTION, read_message);
       curl_easy_setopt(read_handle, CURLOPT_WRITEDATA, &in_message);
       result = curl_easy_perform(read_handle);
       curl_easy_cleanup(read_handle);
       if(result != 0) printf("\nError cURL_1:%d\n", result);
       else printf("cURL_1_OK\n");
       
       clear_string(in_message.text); // эта функция очистит принятую json-строку от символов { } [ ] \n "
      
       update_id = 0;
     
       /////////////////////////// chek error ///////////////////////////////
       if(strstr(in_message.text, "ok:false") != NULL) // проверка ошибок, например неверный токен
        {
          er_tel++;
          printf("ERROR: %s_END\n\n", in_message.text + 9);
          if(er_tel > 10) exit(0);
        } 
         
       else er_tel = 0; 
 
       /////////////////////////// read update_id ///////////////////////////////
       char *p = NULL;
       if((p = strstr(in_message.text, "update_id:")) != NULL) 
        {
          update_id = strtoul(p + 10, NULL, 0);
          printf("\nMYupdate_1_id:%lu_END\n\n", update_id);
        }  
        
       if(update_id != 0) update_id++; 
        
       /////////////////////////// read chat_id ///////////////////////////////
       char chat_id[16] = {0,};
       if((p = strstr(in_message.text, "chat:id:")) != NULL) 
        {
          memccpy(chat_id, p + 8, ',', 15); 
          chat_id[strlen(chat_id) - 1] = 0;
        }        
      
       //////////////////////////// read msag  ////////////////////////////////
       char msg_text[16] = {0,};
       if((p = strstr(in_message.text, "text:")) != NULL) 
        {
          memcpy(msg_text, p + 5, 15); 
          msg_text[strlen(msg_text)] = 0;
        }       
       
       in_message.size = 0;
       free(in_message.text);

       ///////////////////////////// my functions  ////////////////////////////////
       if(strstr(msg_text, "2 but") != NULL) // установит 2 кнопки в один ряд
        {
	  snprintf(tel_write, TELWRITESIZE - 1, "curl  '%s/sendMessage?chat_id=%s' -F 'text=set 2 buttons' -F 'reply_markup={\"keyboard\":[[{\"text\":\"button 1\"},{\"text\":\"button 2\"}]],\"resize_keyboard\":true}' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
	  system(tel_write);  
        }
        
       if(strstr(msg_text, "5 but") != NULL) // установит 5 кнопок в два ряда
        {
	  snprintf(tel_write, TELWRITESIZE - 1, "curl  '%s/sendMessage?chat_id=%s' -F 'text=set 5 buttons' -F 'reply_markup={\"keyboard\":[[{\"text\":\"button 1\"},{\"text\":\"button 2\"}],[{\"text\":\"button 3\"},{\"text\":\"button 4\"},{\"text\":\"button 5\"}]],\"resize_keyboard\":true}' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
	  system(tel_write); 
        }  
        
       if(strstr(msg_text, "button 1") != NULL) // реакция на нажите button 1
        {
	  snprintf(tel_write, TELWRITESIZE - 1, "curl -F 'text=click button 1' '%s/sendMessage?chat_id=%s' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
          system(tel_write);
        } 
        
       if(strstr(msg_text, "del but") != NULL) // удалит кнопки
        {
	  snprintf(tel_write, TELWRITESIZE - 1, "curl  '%s/sendMessage?chat_id=%s' -F 'text=del buttons' -F 'reply_markup={\"remove_keyboard\":true}' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
	  system(tel_write);
        } 
        
       if(strstr(msg_text, "2 inlbut") != NULL) // установит 2 инлаин-кнопки в один ряд
        {
	  snprintf(tel_write, TELWRITESIZE - 1, "curl  '%s/sendMessage?chat_id=%s' -F 'text=set 2 inline-buttons' -F 'reply_markup={\"inline_keyboard\":[[{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"},{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"}]]}' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
	  system(tel_write);
        }  
        
       if(strstr(msg_text, "5 inlbut") != NULL) // установит 5 инлаин-кнопок в два ряда
        {
	  snprintf(tel_write, TELWRITESIZE - 1, "curl  '%s/sendMessage?chat_id=%s' -F 'text=set 5 inline-buttons' -F 'reply_markup={\"inline_keyboard\":[[{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"},{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"}],[{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"},{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"},{\"text\":\"Geektimes\",\"url\":\"https://geektimes.ru\"}]]}' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
	  system(tel_write);
        } 
        
       if(strstr(msg_text, "picurl") != NULL) // отправит картинку url
        {
          snprintf(tel_write, TELWRITESIZE - 1, "curl -F 'photo=https://hsto.org/getpro/geektimes/post_images/25f/64d/8ef/25f64d8ef8c20008190eb6747a303061.png' -F 'caption=Picture url' '%s/sendPhoto?chat_id=%s' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
          system(tel_write);
        }  
        
       if(strstr(msg_text, "locpic") != NULL)  // отправит локальную картинку
        {
          snprintf(tel_write, TELWRITESIZE - 1, "curl -F 'photo=@tux.png' -F 'caption=local picture' -H 'Content-Type:multipart/form-data' '%s/sendPhoto?chat_id=%s' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id); // либо добавте путь к картинке photo=@/tmp/tux.png
          system(tel_write);
        }        
        
       else if(strstr(msg_text, "/start") != NULL) 
        {
          snprintf(tel_write, TELWRITESIZE - 1, "curl -F 'text=Hello' '%s/sendMessage?chat_id=%s' 2> /dev/null 1> /dev/null", BOT_ADDRESS, chat_id);
          system(tel_write);
        }
 
       else printf("NO_DATA\n");
       printf("Tel_write=%d\n", (int)strlen(tel_write));
    }
   
   curl_global_cleanup();
   return 0;
}


// gcc -Wall -Wextra telegramgetup.c -o telegramgetup -lcurl

// ./telegramgetup


О том, как компилить для роутера, написано в прошлый раз.

Почему для отправки используется curl? Отчасти из-за того, что — «почему бы нет», а отчасти из-за того, что мне так и не удалось заставить libcurl отправить в заголовке «Content-Type: multipart/form-data» по https. )))

Спасибо.

П.С. токен показан не валидный.

Комментарии (12)


  1. grozaman
    16.03.2018 14:20

    так и не удалось заставить libcurl отправить в заголовке «Content-Type: multipart/form-data» по https
    В гугле на вид много решений. Подобные exec() серьезно ударяют по безопасности.


    1. stDistarik Автор
      16.03.2018 14:32

      Если не затруднит, бросьте ссылку.

      А что с безопасностью?


      1. vassabi
        16.03.2018 14:47

        ну навскидку токен передаете курлу прямым текстом. А курл может быть не ваш. И как быть, если он не отработал — тоже непонятно…


        1. stDistarik Автор
          16.03.2018 14:52

          В каком смысле курл не мой?


          1. grozaman
            16.03.2018 14:56

            Злые хакеры могут подменить бинарник курла, который делает что угодно. Это черная коробка, код не полностью под вашим контролем.

            В случае «домашнего» применения скорее всего ничего страшного, но вопрос стабильности всё равно открыт. Так как поймать ошибку таким образом мягко говоря сложней.


            1. stDistarik Автор
              16.03.2018 15:10

              Злые хакеры могут подменить бинарник курла

              А что мне в таком случае делать со всеми остальными программами на моём компьютере? )))


              1. ser-mk
                16.03.2018 15:22

                Вы можете сделать статическую линковку с библиотекой curl


  1. Hellsy22
    16.03.2018 14:51
    +1

    а отправка происходит посредством обычного curl'а

    Если вы уж скатились в грех обращения к внешним утилитам, то выбор С потерял последние обрывки оправданности — на perl/miniperl/microperl все это будет проще, изящнее. К слову, такое обращение с json чревато в будущем проблемами, поскольку вы «оригинально» удалили кавычки и не проверяете границы ключей. Появится в api какой-нибудь «old_chat» или «new_text» — и все. Но даже проверка границ не даст вам гарантии — могут появиться отдельные ветки с совпадающими названиями ключей. Вам следовало или написать полноценный парсер (тривиальная задача, к слову) или воспользоваться библиотекой типа jsmn.


    1. stDistarik Автор
      16.03.2018 15:33

      Если вы уж скатились в грех обращения к внешним утилитам

      Как Вы думаете, какую утилиту использует библиотека libcurl для своей работы?


      1. dmitrytheman
        16.03.2018 17:05

        Я от программирования далек, но как мне кажется, это утилиты используют библиотеки, а не наоборот.
        Весь смысл библиотек, в том что одну библиотеку может использовать несколько программ.
        Поправьте, если ошибаюсь.


  1. zagayevskiy
    16.03.2018 18:00

    Интересно, почему это плюсуют? Портянка говнокода (зато на СИ!) в качестве статьи? Зачем?


    1. Suvitruf
      16.03.2018 19:01

      Вы же сами уже ответили.