imageЗдравствуйте.

Если вдруг кому-то понадобится простенький бот, то вот…

Работает без вебхука (getUpdates), можно установить на роутер.

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

#define BOT_ADDRESS "https://api.telegram.org/bot803596633:f1thviAAEnIIWOo6zdVcp6wwhQ5-km3UsoY" // тута ваш токен
#define GET_METHOD_READ_SIZE 128
#define GET_METHOD_WRITE_SIZE 128

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

//////////////////////////// здесь принимаются сообщения //////////////////////////
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;
}

///////////////////// функция нужна curl`у для отправки сообщений/////////////////////
size_t emp_function(void *no_use, size_t size, size_t nmemb, void *null) 
{
   no_use = no_use; null = null;
   return size * nmemb;
}

//////////////////////////// здесь отправляются сообщения //////////////////////////
void sendMessage(char *chat_id) 
{ 
  int result;
  char GET_write[GET_METHOD_WRITE_SIZE] = {0,};
  snprintf(GET_write, GET_METHOD_WRITE_SIZE - 1, "%s/sendMessage?chat_id=%s&text=Reciv 'w' OK!", BOT_ADDRESS, chat_id); // если отправляете что-то длинное, то увеличить GET_METHOD_WRITE_SIZE
printf("LenGET_write:%d_END\n", (int)strlen(GET_write));  
  CURL *write_handle = curl_easy_init();
  curl_easy_setopt(write_handle, CURLOPT_URL, GET_write);
  curl_easy_setopt(write_handle, CURLOPT_WRITEFUNCTION, emp_function);
  curl_easy_setopt(write_handle, CURLOPT_READDATA, NULL);
  result = curl_easy_perform(write_handle);
  curl_easy_cleanup(write_handle);
  if(result != 0) printf("\nError cURL_2:%d\n", result);
}

///////////////////////////////////////////////////////////////////////////////////
int main()
{
    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,};
    printf("START\n");

    while (1)
     {
       in_message.text = (char*)malloc(1 * sizeof(char));
       in_message.text[0] = '\0';

       snprintf(GET_read, GET_METHOD_READ_SIZE - 1, "%s/getUpdates?limit=1&offset=%lu&timeout=10", BOT_ADDRESS, update_id); // обновления каждые 10 секунд (timeout=10)
       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);
       
       update_id = 0;
 
       /////////////////////////// read update_id ///////////////////////////////
       char *p = NULL;
       if((p = strstr(in_message.text, "update_id\":")) != NULL) 
        {
          update_id = strtoul(p + 11, NULL, 0);
          printf("MYupdate_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 + 12, ',', 15); 
          chat_id[strlen(chat_id) - 1] = 0;
        }        
       
printf("MYChat_id:%s_END\n", chat_id);       
       
       //////////////////////////// read msag  ////////////////////////////////
       char msg_text[16] = {0,}; // если будут приходить сообщения длинней 15 симв., тогда увеличить буфер. Ну или калок/малок-реалок)))
       if((p = strstr(in_message.text, "text\":\"")) != NULL) 
        {
          memccpy(msg_text, p + 7, '"', 15); 
          msg_text[strlen(msg_text) - 1] = 0;
        }       
       
printf("Msg_text:%s_END\n", msg_text);       
 
       in_message.size = 0;
       free(in_message.text);
 
       ///////////////////////////// my functions  ////////////////////////////////
       if(strstr(msg_text, "w") != NULL) 
        {
          sendMessage(chat_id); 
        }

       else if(strstr(msg_text, "/start") != NULL) 
        {
          printf("Кто-то нашёл бота.\n");
        }
         
       else printf("NO_DATA\n");
    }
   
   curl_global_cleanup();
   return 0;
}


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

// ./telegramgetup


Makefile для роутера
include $(TOPDIR)/rules.mk

PKG_NAME:=telegramgetup
PKG_VERSION:=1
PKG_RELEASE:=1

PKG_BUILD_DIR:= $(BUILD_DIR)/$(PKG_NAME)

include $(INCLUDE_DIR)/package.mk


define Package/telegramgetup
	SECTION:=utils
	CATEGORY:=Utilities
	TITLE:=telegramgetup - Telegramgetup utility
	DEPENDS:=+libcurl
endef

define Package/telegramgetup/description
    telegramgetup - Telegramgetup utility
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef


define Build/Compile
	$(TARGET_CC) $(TARGET_CFLAGS) -c -o $(PKG_BUILD_DIR)/telegramgetup.o $(PKG_BUILD_DIR)/telegramgetup.c
	$(TARGET_CC) $(TARGET_LDFLAGS) -o $(PKG_BUILD_DIR)/telegramgetup $(PKG_BUILD_DIR)/telegramgetup.o -lcurl
endef

define Package/telegramgetup/install
	$(INSTALL_DIR) $(1)/usr/sbin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/telegramgetup $(1)/usr/sbin/
endef

$(eval $(call BuildPackage,telegramgetup))

// make package/telegramgetup/compile V=s


Потребуется установить libopenssl, curl и сертификаты. Если ставите на роутер, то чтоб не загромождать его, создайте файл — /etc/ssl/certs/Go_Daddy_Class_2_CA.crt, вот с таким содержимым…

Содержимое
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----


Отправьте боту букву w, он ответит — Reciv 'w' OK!

Чтиво на раскурку — раз и два.

Это всё, спасибо.

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


  1. berezuev
    22.02.2018 13:01
    -1

    Отлично. Еще немного сократите статью, и можно будет в Твиттер уместить.


    1. stDistarik Автор
      22.02.2018 13:10
      +3

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


      1. Dessloch
        23.02.2018 06:55

        Поддерживаю. А ещё мне понравилось «импортозамещение» вместо C(en)=>СИ(рус).


      1. extempl
        23.02.2018 11:01

        Статья формата "ответ на stackoverflow", и то, без подробностей. А Вы вот напишите, для чего это могло понадобиться, что Вас сподвигло написать его на C (необходимость установки на роутер? Зачем?), какие есть альтернативы, и т.п. Необязательно словоблудить лить воду, можно и по делу написать.


  1. Goron_Dekar
    22.02.2018 13:53
    +1

    Любимый формат статьи!
    Спасибо автору.


    1. stDistarik Автор
      22.02.2018 14:25
      +1

      Благодарю за тёплые слова.


  1. Agel_Nash
    22.02.2018 14:16
    +2

    Статья ведь для хабра


    1. extempl
      23.02.2018 10:57

      И код для гитхаба.


  1. semen-pro
    22.02.2018 14:54

    Бросил читать, когда понял, что там куча зависимостей и в ардуину не влезет.


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

      Вы имеете в виду ардуино Yun?


      1. semen-pro
        22.02.2018 15:15

        Нет, AVR 8 бит или esp8266


        1. stDistarik Автор
          22.02.2018 15:47
          +1

          Для esp8266 есть библиотека github.com/Gianbacchio/ESP8266-TelegramBot


          1. IronHead
            23.02.2018 22:18

            а для lwip на stm32?


  1. alexoron
    22.02.2018 17:26
    +1

    Ждем статью — «Telegrambot на ASM».
    А чо, тоже интересно.


    1. uselessnickname
      23.02.2018 01:28

      NES c Telegrambot и ZIP-файл в одном


    1. stDistarik Автор
      23.02.2018 01:42

      «Telegrambot на ASM».

      А какой в этом практический смысл?


      1. rionnagel
        23.02.2018 10:25

        Такой же как и на брэинфаке.


  1. slonofanya
    23.02.2018 11:59

    Спасибо большое, хоть я долго еще не буду использовать этот материал. По больше бы таких форматов. Люблю лаконичность


    1. stDistarik Автор
      23.02.2018 12:06

      И Вам спасибо.


  1. Mephistophiles
    23.02.2018 13:27

    Автор, есть хорошая либа для работы с json — jannson. С ней очень удобно парсить апи запросы


    1. stDistarik Автор
      23.02.2018 13:52

      Наверно Вы имеете в виду Jansson. Она у меня в openwrt что-то не собирается и я не знаю сколько она занимает места, свободно всего ~200Kb.


      1. Mephistophiles
        23.02.2018 17:03

        Да, опечатался. Собрал для embedded системы — 54кб, но там можно урезать до ~20, если вырезать json_pack/unpack. Не собирается возможно из-за того, что не линкуется с libm, я сталкивался с этим


        1. stDistarik Автор
          23.02.2018 22:08

          54кб

          Вот спасибо.


  1. loginsin
    23.02.2018 14:05

    Хороший пример того, как НЕ надо писать на C:

    // ...
        dest_message->text = realloc(dest_message->text, dest_message->size + real_size + 1);
        if(dest_message->text == NULL)
         {
    // ...
           in_message.text = (char*)malloc(1 * sizeof(char));
           in_message.text[0] = '\0';
    // ...
           curl_easy_setopt(read_handle, CURLOPT_WRITEFUNCTION, read_message);
           curl_easy_setopt(read_handle, CURLOPT_WRITEDATA, &in_message);
    // ...
           free(in_message.text);
    


    1. Течь памяти;
    2. Нет проверки malloc, сразу идет обращение к потенциально не выделенной памяти;
    3. К потенциально обнуленному поинтеру в read_message далее куча обращений;
    4.
           if((p = strstr(in_message.text, "chat\":{\"id\":")) != NULL) 
    

    А если "\n" добавят между chat и id?

    Еще много из bad practice в коде, но это, пожалуй, самое очевидное.


    1. stDistarik Автор
      23.02.2018 14:58

      valgrind говорит что память НЕ течёт.

      Вот пример из libcurl:

       mem->memory = realloc(mem->memory, mem->size + realsize + 1);
        if(mem->memory == NULL) {
          /* out of memory! */ 
          printf("not enough memory (realloc returned NULL)\n");
          return 0;
        }
       
        memcpy(&(mem->memory[mem->size]), contents, realsize);
        mem->size += realsize;
        mem->memory[mem->size] = 0;
       
        return realsize;
      }
       
      int main(void)
      {
        CURL *curl;
        CURLcode res;
        struct MemoryStruct chunk;
        static const char *postthis = "Field=1&Field=2&Field=3";
       
        chunk.memory = malloc(1);  /* will be grown as needed by realloc above */ 
        chunk.size = 0;    /* no data at this point */ 
      


      Согласен, наверное малок и стоит проверять, однако в подавляющем большинстве случаев этого никто не делает.

      А если "\n" добавят между chat и id?

      Изменить могут всё что угодно.


  1. loginsin
    24.02.2018 00:06

    https://m.habrahabr.ru/company/pvs-studio/blog/343508/
    Явная бага. Пример из libcurl не показатель. Realloc вернет NULL и потеряется указатель на выделенную malloc память.
    Непроверенный malloc (на секундочку, в роутере, где памяти в целом немного) вызовет отказ в обслуживании.


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