Cで「Hello、World」電報ボットを作成します

みなさんこんにちは、なぜこれが必要なのかわかりませんが、誰かが役に立つかもしれません...



免責事項:私は決してプロのCプログラマーではありません。



必要なもの:



1. Linux、Ubuntu、Centos、MacOS上の任意のコンピューター...インターネットからポート443または8443にアクセスできます。

2.任意のCコンパイラ

3. Opensslライブラリlibssl-dev(Ubuntuの場合、ターミナルで「apt-get install openssl libssl-dev」)



それでは、始めましょう...



最初に行うことは、すべての@BotFatherボットの父のためにボットを作成し、すべての詳細を省略して、全員がこのタスクに対処してトークンを受け取ったと想定することです。

373288854:AAHHT77v5_ZNEMus4bfno6dxiMeeEwgwJ



次に、SSL証明書を作成してWebHookをインストールします。 コマンドは次のようになります。



openssl req -newkey rsa:2048 -sha256 -nodes -keyout private.key -x509 -days 365 -out public.pem
      
      





キーと公開証明書を1つのファイルにパックします。



 cat private.key public.pem > cert.pem
      
      





WebHookをインストールします。



 curl -F"url=https://_IP:( 443,  8443)/_URI(   ,    )/" -F"certificate=@public.pem" https://api.telegram.org/bot/setWebhook/
      
      





成功のような何かのJSON応答が来るはずです:true ...そうでない場合は、すべてをチェックして、再試行してください。



楽しい部分に到達する:



main.cファイルを作成し、任意のエディターで開きます。 必要なライブラリを含めます。



 #include <stdio.h> #include <openssl/bio.h> #include <openssl/ssl.h> #include <unistd.h> #include <openssl/err.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <resolv.h> #include <netdb.h>
      
      





ソケット初期化関数:



 int InitializeSocket(int port) { int sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) exit(-1); struct sockaddr_in s_addr; s_addr.sin_family = AF_INET; s_addr.sin_addr.s_addr = INADDR_ANY; s_addr.sin_port = htons(port); if (bind(sd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) { printf("Binding Error!\n"); exit(-3); } return sd; }
      
      





SSL / TLSを有効にします。



 SSL_CTX * InitializeSSL(char[] certificate) { OpenSSL_add_all_algorithms(); SSL_load_error_strings(); SSL_library_init(); SSL_CTX * sslctx = SSL_CTX_new(TLSv1_2_server_method()); if (SSL_CTX_use_certificate_file(sslctx, certificate , SSL_FILETYPE_PEM) <= 0) { exit(-2); } if (SSL_CTX_use_PrivateKey_file(sslctx, certificate, SSL_FILETYPE_PEM) <= 0) { exit(-2); } if (!SSL_CTX_check_private_key(sslctx)) { exit(-2); } return sslctx; }
      
      





実際にメイン()自体:



 int main() { SSL_CTX * sslctx = InitializeSSL("cert.pem"); //         int sd = InitializeSocket(8443); //      WebHook listen(sd, 5); //     while (1) { //   int client = accept(sd, NULL, NULL) // accept   ,     ,    sockaddr,           ,         NULL,    . SSL * ssl = SSL_new(sslctx); //C ssl  SSL_set_fd(ssl, client); //     if (SSL_accept(ssl) <= 0) { //  ,           SSL_clear(ssl); close(newsd); continue; } //     fork()      ,         int pid = fork(); if (pid != 0) { //  ,         SSL_clear(ssl); close(newsd); continue; } //       //     .... exit(0); //   } }
      
      





TelegramはHTTPプロトコルを使用しているため、いくつかの点を説明します。



HTTPリクエストは「\ r \ n」で区切られたヘッダーで構成され、ヘッダー「\ r \ n \ r \ n」で区切られた本文は空の場合がありますが、セパレーター「\ r \ n \ r \ n」は常に存在します。 電文リクエストはPOSTメソッドを使用して受信され、本文はJSON形式になります。



Telegramに似たリクエストの例:



 POST /(URI    WebHook) HTTP/1.1\r\n ....     Content-Type: application/json\r\n (   ) Content-Length: 256\r\n (   ,  ) ..../r/n/r/n Json 
      
      





人がボットにメッセージを送信するたびに、テレグラムサーバーはサーバーに同様のリクエストを送信します。 一般的な場合、それらに応答する必要はありませんが、Telegramの場合は必須です。そうでない場合は、同じリクエストを周期的に送信します。



これを行うには、短いHTTP応答を準備します。



 HTTP/1.1 200 OK\r\n Connection: close\r\n\r\n
      
      





これらの2つのフィールドは、すべてが正常であり、答えは200であり、接続を閉じることができることをTelegramサーバーに伝えるのに十分です。



引き続きプログラムを作成します。 子プロセスを作成した後のループ内...



 char[] response = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"; // HTTP response char header[1024]; bzero(header,1024); //               . int s = 0; int n = 0; while (strcmp(header + s - strlen("\r\n\r\n"), "\r\n\r\n") != 0) { //strcmp         0,      strlen("\r\n\r\n")   "\r\n\r\n",      n = SSL_read(ssl,header+s,1); //      header + s, s -  -   s += n; //n - -     } //,  ,     , uri, content-type   content-length . if (strstr(header,"POST /(URI    WebHook) HTTP/1.1\r\n") == NULL) { //   POST ....  header,      NULL,    ,       SSL_clear(ssl); close(client); exit(0); } //   ,   application/json; if (strstr(header, "Content-Type: application/json") == NULL) { SSL_clear(ssl); close(client); exit(0); } //  ,     int len = atoi(strstr(header, "Content-Length: ") + strlen("Content-Length: ")); //strstr       ,    "Content-Length: ",  -      ,     "Content-Length: "      int  atoi(char *); char body[len+2]; bzero(body, len+2); //   ,          ,    ,           -  n = 0; s = 0; while (len - s > 0) { //                - n = SSL_read(ssl, request + s, len - s); //      ,      ,      ,   SSL_read  -   s += n; } //    ,   http response    SSL_write(ssl, response, (int)strlen(response)); SSL_clear(ssl); SSL_free(ssl); close(client); //    "Hello, World"          "Hello, World!",                chat_id int chat_id = atoi(strstr("\"chat_id\":") + strlen("\"chat_id\":")); //      Content-Length //   ,       SendMessage char msg[] = "Hello, World!"; SendMessage(chat_id, msg); //  
      
      





リクエストを送信するには、ソケットとsslを初期化する必要がほとんどありますが、リクエストを受信するのとは異なり、接続を待たずに、ただちにデータを送信します。



 void SendMessage(int chat_id, char[] msg) { int port = 443; char host[] = "api.telegram.org"; //     //  HTTP    ,     char header[] = "POST /bot352115436:AAEAIEPeKdR2-SS7p9jGeksQljkNa9_Smo0/sendMessage HTTP/1.1\r\nHost: files.ctrl.uz\r\nContent-Type: application/json\r\nContent-Length: %d\r\nConnection: close\r\n\r\n%s"; //     char tpl[] = "{\"chat_id\":%d,\"text\":\"%s\"}"; char body[strlen(tpl)+strlen(msg)+16]; bzero(body, strlen(tpl)+strlen(msg)+16); sprintf(body,tpl,chat_id,msg); // printf,    char[] char request[strlen(header)+strlen(body)+4]; bzero(request,strlen(header)+strlen(body)+4); sprintf(request, header, strlen(body), body); //  ,    struct hostent *server; struct sockaddr_in serv_addr; int sd; sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) exit(-5); server = gethostbyname(host); //   ip      url if (server == NULL) exit(-6); bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length); if (connect(sd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) { exit(-6);} SSL_CTX * sslctx = SSL_CTX_new(TLSv1_client_method()); SSL * cSSL = SSL_new(sslctx); SSL_set_fd(cSSL, sfd); SSL_connect(cSSL); SSL_write(cSSL,request,(int)strlen(request)); //  ,           ,      -   char str[1024]; SSL_read(cSSL, str, 1024); //     SSL_clear(cSSL); SSL_CTX_free(sslctx); close(sd); }
      
      





基本的には以上です。 ファイルを証明書とともに1つのフォルダーに保存し、任意のコンパイラーでコンパイルして実行します。



 clang main.c -o bot -lcrypto -lssl ./bot
      
      





終わり!



この記事が誰かに役立つことを願っています。



All Articles