おやすみなさい 今日は、Rustでコードを実行するRustでTelegramボットを作成する方法について簡単に説明します。 この記事の目的は、telegram_bot、Serde、Telegram API、またはRustでの開発のニュアンスに完全に没頭することではありません。 ガイダンスのみである可能性が高くなります。 型システムを使用してPeano番号を追加しません。
Telegramでボットを作成する
最初に、ボットを作成し、HTTP APIトークンを取得します。
この男に行き、次のように書きます:
新しいボット: /newbot
の作成を開始します。
ゴッドファーザーの答え:
よし、新しいボット。 どうやって呼ぶの? ボットの名前を選択してください。
応答では、作成するボットの名前、 rust
を書き込みます。
ゴッドファーザーの答え:
いいね 次に、ボットのユーザー名を選択しましょう。bot
終わる必要があります。 このように、たとえば:TetrisBotまたはtetris_bot。
指示に従って、別の名前rustlanguage_bot
入力します。
ゴッドファーザーの答え:
できた! 新しいボットおめでとうございます。 t.me/rustlanguage_botにあります。 ボットのセクション、プロファイル画像についての説明を追加できるようになりました。コマンドのリストについては、ヘルプを参照してください。 ところで、クールなボットの作成が終了したら、ボットサポートにpingを送信して、より良いユーザー名が必要な場合は確認してください。 これを行う前に、ボットが完全に動作していることを確認してください。 このトークンを使用して、HTTPにアクセスします。 API:%TOKEN%ボットAPIの説明については、次のページを参照してください: https : //core.telegram.org/bots/api
素晴らしい。 ボットが作成されます。 %TOKEN%
-これは実際、トークンです。
さび遊び場
次に、ユーザーがメッセージとしてボットに送信するコードを実行する方法と場所について少し説明します。
Rust Playground
サービスには、最も単純なRustコードをオンラインで実行できるサービスがあります。 使用します。 次のアドレスにあります: https : //play.rust-lang.org/
リンクに続いて、簡単なhello-worldプログラムを紹介します。
fn main() { println!("Hello world!"); }
DevTools
[ Network
]タブを開き、コンパイル結果を取得するために送信する形式と形式を確認します。
すべてが透明で明確なようです。 コンソールからプレイしてみましょう。
[loomaclin@localhost ~]$ curl -X POST -d '{"code":"fn main() {\n println!(\"Hello world!\");\n}","version":"stable","optimize":"0","test":false,"separate_output":true,"color":true,"backtrace":"0"}' https://play.rust-lang.org/evaluate.json {"program":"Hello world!\n","rustc":"rustc 1.16.0 (30cf806ef 2017-03-10)\n"}
さて、さらに先に進みましょう。
ボットを書く
プロジェクトを作成します。
cargo new rust_telegram_bot --bin
Cargo.toml
次の依存関係を追加します。
[dependencies] telegram-bot = { git = "https://github.com/White-Oak/telegram-bot.git" } hyper = "0.10.8" hyper-rustls = "0.3.2" serde_json = "0.9.10" serde = "0.9.14" serde_derive = "0.9.14"
それらが必要な理由を簡単に説明してください。
Serde
、さまざまな形式でデータをシリアライズ/デシリアライズするように設計されています。 この場合、JSON(serde_json)とひとつまみのコード生成(serde_derive)を使用する必要があります。
Hyper
を使用してネットワークを操作するには、Rust Playgroundとやり取りするために提供するHTTPクライアントを使用します。 インタラクションはHTTPSプロトコルを介して行われるため、hyper-rustls
形式のバッテリーhyper-rustls
依然として必要です。
- 最も重要なことは、Telegram APIとやり取りするために、既製のTelegram
telegram-bot
ライブラリを使用しますが、具体的にはそうではなく、Hyper
現在のバージョンで動作するように適合させた同志@white_oakのフォークです
src/main.rs
、必要なすべてのライブラリとモジュールsrc/main.rs
接続します。
extern crate telegram_bot; extern crate hyper; extern crate hyper_rustls; extern crate serde_json; extern crate serde; #[macro_use] extern crate serde_derive; use serde_json::Value; use telegram_bot::{Api, MessageType, ListeningMethod, ListeningAction}; use std::io::Read; use hyper::client::Client; use hyper::net::HttpsConnector; use hyper_rustls::TlsClient;
注: #[macro_use]
、この属性が適用されたライブラリーから現在のマクロプログラムのスコープに含めるために使用されます。
この行では、ライブラリのルートからモジュールをインポートして、メッセージのタイプ、盗聴方法、Telegram APIを表す構造を決定します。
use telegram_bot::{Api, MessageType, ListeningMethod, ListeningAction};
enum
を使用して、サーバーからの可能な応答のタイプを記述します。この場合、プログラムが正常にコンパイルされたとき、およびコンパイルエラーが発生したときの応答には2つあります
#[derive(Serialize, Deserialize, Debug)] #[serde(untagged)] pub enum ResponseType { ProgramCompiled { program: String, rustc: String }, ProgramCompileError { rustc: String } }
列挙に適用された#[serde(untagged)]
属性に注意してください。 彼は、(de)シリアル化では、どのオプションであるかを明示的に示す列挙オプションのタグは検索されないと言います。 それでは、 Serde
はサーバーから受け取った応答オプションをどのように判断しますか? 実際、最初の成功した結果に達するまで、彼女は各オプションにデシリアライズしようとします。 詳細については、公式ドキュメントhttps://serde.rs/enum-representations.htmlをご覧ください 。
Rust Playgroundでリクエストの構造を定義します。
#[derive(Serialize)] pub struct PlaygroundRequest { code: String, version: String, optimize: String, test: bool, separate_output: bool, color: bool, backtrace: String }
ユーザー入力から、 code
フィールドのみがこの構造に移動します。 残りはハードコードされています。これは常にこれを行うためです:)(いいえ)
main
プログラムのメイン関数で、Telegram APIインスタンスを作成し、メッセージでボットに到達したすべてを印刷します。
fn main() { let api = Api::from_env("TOKEN").unwrap(); println!("getMe: {:?}", api.get_me()); let mut listener = api.listener(ListeningMethod::LongPoll(None)); let res = listener.listen(|u| if let Some(m) = u.message { let name = m.from.first_name; match m.msg { MessageType::Text(t) => { println!("<{}> {}", name, t); } _ => {} } }); }
このコードの機能を確認するには、以前に取得した実際のトークンを環境変数として転送することを忘れずに、プログラムを実行します。
TOKEN=%TOKEN% cargo run
上記で書いたことを少し分析します。
let api = Api::from_env("TOKEN").unwrap(); println!("getMe: {:?}", api.get_me());
ここでは、 Api
からインポートされたApi
構造のインスタンスを作成し、ロングポーリングモードでボットリスナーを作成します。
let mut listener = api.listener(ListeningMethod::LongPoll(None));
最後に、 listen
関数とメッセージタイプに一致するパターンを使用して、メッセージ処理ループを作成します。
let res = listener.listen(|u| if let Some(m) = u.message { let name = m.from.first_name; match m.msg { MessageType::Text(t) => { println!("<{}> {}", name, t); } _ => {} } });
コードをテキスト形式でのみ送信することに同意します。 除外されるファイルやもの。 このため、お気づきかもしれませんが、他のすべてのMessageType
列挙オプションは単に無視されます。
/rust
コマンドを処理し、Rust Playgroundにリクエストを送信し、レスポンスを読み取ります。
if t.starts_with("/rust ") { let program = t.split("/rust ").collect(); let mut result = String::new(); let tls = hyper_rustls::TlsClient::new(); let connector = HttpsConnector::new(tls); let client = Client::with_connector(connector); let playground_request = serde_json::to_string(&PlaygroundRequest { code: program, version: String::from("stable"), optimize: String::from("0"), test: false, separate_output: true, color: false, backtrace: String::from("0"), }) .unwrap(); let mut response = client .post("https://play.rust-lang.org/evaluate.json") .body(&playground_request) .send() .unwrap(); response.read_to_string(&mut result); println!("Result : {:?}", result); }
メッセージが特定のコマンド( /rust
)で始まる場合にのみ、リクエストを処理します。
if t.starts_with("/rust ") {
また、コンパイルする必要があるプログラムのコードを取得します。
let program = t.split("/rust ").collect();
serde_json::to_string(&PlaygroundReques { ... })
関数は、リクエスト構造を文字列にシリアル化します。 コードの残りの部分は、HTTPSクライアントの初期化、リクエストの送信と読み取りに関連しています 。これに関する詳細は、 https : //hyper.rs/hyper/v0.10.7/hyper/index.htmlにあります 。
受け取った回答を処理します。
let result : ResponseType = serde_json::from_str(&result) .unwrap_or(ResponseType::ProgramCompileError { rustc: String::from(" ") }); let mut result = match result { ResponseType::ProgramCompiled { program, .. } => { format!(" : {}", program) } ResponseType::ProgramCompileError { rustc, .. } => { format!(" : {}", rustc) } };
serde::from_str
は、 serde::from_str
応答をenum
型のいずれかに逆シリアル化します。 応答をデシリアライズできなかった場合は、簡単にするために、対応するテキストでコンパイルエラーのバリアントにこれをラップします。 次に、結果のメッセージを生成します。このメッセージは、どのenum
オプションが提示されたかに基づいてユーザーに送信されます。 テンプレートを照合するときに{ program, .. }
ような構造が表示されるのは初めてでしょう。このオプションの処理中に不要な構造フィールドを無視することを説明します。
コンパイル結果をチャットに送信:
if result.len() > 500 { result.truncate(500); } try!(api.send_message(m.chat.id(), result, None, None, Some(m.message_id), None));
最後に、大量の出力を伴うコンパイル結果を除外するためにメッセージの長さをチェックし、このケースをトリミングします。 その後、コンパイル要求の送信元のチャットの識別子を示すメッセージを送信し、最終的なコンパイル結果を転送します。 また、応答するメッセージのIDを渡します。 渡される残りのパラメーターはオプションであり、プレビューの出力、応答のタイプなどを担当します。
パフォーマンスを確認する
コンソールへの出力:
Finished dev [unoptimized + debuginfo] target(s) in 2.38 secs Running `target/debug/rust_telegram_bot` getMe: Ok(User { id: 334562900, first_name: "rust", last_name: None, username: Some("rustlanguage_bot") }) <Arsen> /rust abc Result : "{\"rustc\":\"rustc 1.16.0 (30cf806ef 2017-03-10)\\nerror: expected one of `!` or `::`, found `<eof>`\\n --> <anon>:1:1\\n |\\n1 | abc\\n | ^^^\\n\\nerror: aborting due to previous error\\n\\n\"}" <Arsen> /rust fn main() { println!("Hello habrahabr!"); } Result : "{\"program\":\"Hello habrahabr!\\n\",\"rustc\":\"rustc 1.16.0 (30cf806ef 2017-03-10)\\n\" }"
おわりに
それがすべてだと思います。 telegram_botのフォークを作業してくれたWhiteOakに感謝します。
ところで、彼はRustのQMLバインディングプロジェクトhttps://github.com/White-Oak/qml-rustを持っています 。 おそらくそれは誰かにとって興味深いでしょう。
建設的な批判は歓迎します。
このボットの完全なコードを含むリポジトリは、 ここにあります 。
ロシア語を話すRustのチャットへのリンクを残すのをほとんど忘れていました。そこでは、彼らが常に言語に対処するのに役立ちます: https : //gitter.im/ruRust/general