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 

終わり!

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

Source: https://habr.com/ru/post/J325846/


All Articles