免責事項:私は決してプロの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
終わり!
この記事が誰かに役立つことを願っています。