電報用ボット。 レールウェイ

この投稿は、Telegram向けのボットを作成するためのTelegram -Botライブラリに関するものです。 作成中の主な目標には、ボットの開発、デバッグ、テストの利便性、インターフェイスを最小限に抑えながら拡張可能で、Railsアプリケーションとの統合が容易であり、ボットを作成するために必要なツールを提供することがあります。 含まれるものは次のとおりです。


面白い? インストールするには、 Gemfile telegram-botを追加し、詳細をカットします。

ボットAPIのクライアント


クライアントの作成は簡単です: Telegram::Bot::Client.new(token, username)username値はオプションであり、呼び出し( /cmd@BotName )およびKey-Valueストアのセッションキープレフィックス内のコマンドを解析するために使用されます。

基本的なクライアントメソッドはrequest(path_suffix, body) 。ドキュメントのすべてのチームには、Rubyスタイルのショートカットがあります-アンダースコア( .send_message(body)answer_inline_query(body) )。 これらのメソッドはすべて、目的のURLに渡されたパラメーターを使用してPOSTを実行するだけです。 bodyファイルはmultipart/form-dataで自動的に転送され、添付のハッシュはドキュメントで必要とされるようにjsonでエンコードされます。

 bot.request(:getMe) or bot.get_me bot.request(:getupdates, offset: 1) or bot.get_updates(offset: 1) bot.send_message chat_id: chat_id, text: 'Test' bot.send_photo chat_id: chat_id, photo: File.open(photo_filename) 

すぐに使用できるように、クライアントは各リクエストに対して通常の分散jsonを返します。 telegram- telegram-bot-typesを使用して、出力でvirtus-modelsを取得できます。

 #   Gemfile: gem 'telegram-bot-types', '~> xxx' #  typecasting   : Telegram::Bot::Client.typed_response! #    : bot.extend Telegram::Bot::Client::TypedResponse bot.get_me.class # => Telegram::Bot::Types::User 

カスタマイズ


gemは、アプリケーションに共通のクライアントをセットアップしてアクセスするためのメソッドをTelegramモジュールに追加します(これらはスレッドセーフであり、複数のスレッドで問題はありません)。

 #   Telegram.bots_config = { #     default: 'bot_token', #    username chat: { token: 'other_token', username } } #     : Telegram.bots[:chat].send_message(params) Telegram.bots[:default].send_message(params) #  :default    (,   ): Telegram.bot.get_me 

Railsアプリケーションの場合、 bots_configを手動で設定せずに実行できます。設定はbots_configから読み取られsecrets.yml

 development: telegram: bots: chat: TOKEN_1 default: token: TOKEN_2 username: ChatBot #     bots.default bot: token: TOKEN username: SomeBot 

コントローラー


更新を処理するために、gemにはベースコントローラークラスがあります。 ActionControllerと同様に、すべてのパブリックメソッドはコマンドを処理するためのアクションメソッドとして使用されます。 つまり、メッセージ/cmd arg 1 2が到着すると、 cmd('arg', '1', '2')メソッドcmd('arg', '1', '2')が呼び出されます(定義されていてパブリックの場合)。 ActionControllerとは異なり、サポートされていないコマンドが到着した場合、ActionMissingエラーなしで単に無視されます。

コントローラーは参照付きのコマンドを処理できます。 これが発生すると、コマンドの名前usernameボットのusernameと比較されusername 。 一致する場合、コマンドが実行されます。一致しない場合、メッセージはプレーンテキストとして処理されます。

他の更新(メッセージではない)を処理するには、更新タイプの名前からpublicメソッドを定義する必要もあります(現在、3つのオプションが利用可能です: `message、inline_query、selected_inline_result ')。 これらのメソッドは、更新から対応するオブジェクトを引数として受け取ります。

通知に応答するために、受信した更新から受信者およびその他のフィールドを公開するreply_with(type, params)およびanswer_inline_query(results, params)ヘルパーがあります。

 class TelegramWebhookController < Telegram::Bot::UpdatesController def message(message) reply_with text: "Echo: #{message['text']}" end def start(*) #    chat  from: reply_with text: "Hello #{from['username']}!" if from #        payload: log { "Started at: #{payload['date']}" } end #       splat-  #  -,       #    ,     . def help(cmd = nil, *) message = if cmd help_for_cmd?(cmd) ? t(".cmd.#{cmd}") : t('.no_help') else t('.help') end reply_with text: message end end 

ほとんどの場合、ボットはメッセージ間のチャットの状態を記憶する必要があります。 これを行うには、コントローラーでセッションを使用できます。 このインターフェースはActionControllerのセッションインターフェースに似ていますが、違いは保存方法です。 任意のActiveSupport ::キャッシュ互換ストレージ( redis-activesupportなど)をアダプターとして使用できます。

デフォルトでは、この値はセッションIDとして使用されます(メソッドをオーバーライドすることで変更できます)。

 def session_key "#{bot.username}:#{from ? "from:#{from['id']}" : "chat:#{chat['id']}"}" end 

セッションを使用して、メッセージコンテキストを実装できます-複数のメッセージで送信されるコマンドのサポート:ユーザーは引数なしでコマンドを送信し、ボットは期待する引数を指定し、ユーザーは次のメッセージでそれらを送信します(たとえば、BotFatherが行うように)。 このような機能は、 Telegram::Bot::UpdatesController::MessageContext利用できTelegram::Bot::UpdatesController::MessageContext

 class TelegramWebhookController < Telegram::Bot::UpdatesController include Telegram::Bot::UpdatesController::MessageContext def rename(*) #     : save_context :rename reply_with :message, text: 'What name do you like?' end #     : context_handler :rename do |message| update_name message[:text] reply_with :message, text: 'Renamed!' end #   -.  rename,      #   . def rename(name = nil, *) if name update_name name reply_with :message, text: 'Renamed!' else #    ,   : save_context :rename reply_with :message, text: 'What name do you like?' end end #          ,    . #      ,   ,     #  '/rename %text%' context_handler :rename #    ,  : context_to_action! #              . end 

アプリケーション統合


コントローラーはいくつかの方法で使用できます。

 #   : ControllerClass.dispatch(bot, update) #   ,  . controller = ControllerClass.new(bot, from: telegram_user, chat: telegram_chat) controller.process(:help, *args) 

フックを処理するためのラックエンドポイントがあります。 Railsアプリケーションにはルートヘルパーがあります。ボットトークンがパスサフィックスとして使用されます。 アプリケーションで単一のボットを使用する場合は、次を追加します。

 # routes.rb telegram_webhooks Telegram::WebhookController 

このヘルパーを使用すると、タスクを使用して結果のURLを使用して、ボットのsetWebhookを設定できます。

 rake telegram:bot:set_webhook RAILS_ENV=production 

テスト中


gemには、テストでAPIクライアントを置き換えるTelegram::Bot::ClientStubがあります。 クエリを実行する代わりに、 #requestsハッシュに保存します。 作成されたすべてのクライアントをストールさせ、テスト実行中にTelegramにリクエストを送信しないようにするには、次のように記述できます。

 RSpec.configure do |config| # ... Telegram.reset_bots Telegram::Bot::ClientStub.stub_all! config.after { Telegram.bot.reset } # ... end 

ActionControllerと同じ方法でコントローラーをテストするためのヘルパーがあります。

 require 'telegram/bot/updates_controller/rspec_helpers' RSpec.describe TelegramWebhookController do include_context 'telegram/bot/updates_controller' describe '#rename' do subject { -> { dispatch_message "/rename #{new_name}" } } let(:new_name) { 'new_name' } it { should change { resource.reload.name }.to(new_name) } end end 

開発とデバッグ


ローカルデバッグの場合、更新ポーラーを実行できます。 これを行うには、おそらく別のボットを作成する必要があります。 rake telegram:bot:pollerrake telegram:bot:pollerを起動します。 更新の処理時にコードの更新が自動的にダウンロードされるため、プロセスを再起動する必要はありません。

ソースコードと詳細な説明はgithubで入手できます。

素晴らしい開発を!

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


All Articles