freks blog

about

Bot Framework REST APIでBotと通信してみる

created: 2024-06-04
おすすめ記事: 出会ってよかったプログラマー本

Azure AI Bot Serviceを使って最終的にはTeamsで動かすBotを作ろうとしています

Bot Framework SDKを使うのが公式に推奨されていますが、Bot Framework SDKを動かすサーバーを用意したくはありません
また、Ruby on RailsからBotへメッセージを送りたくて、RubyのSDKはないので、Bot Framework REST APIを使うことにしました

Bot Connector サービスでのボットの作成 - Bot Service | Microsoft Learn
を見るとできそうで、動かしてみます

Azure AI Bot Serviceを作成

Azure portal で Azure Bot リソースを作成する - Bot Service | Microsoft Learn
を参考にAzure Botを用意します

リソースの管理のところで、シングルテナント、マルチテナントを選べますが、いったんシングルテナントにします
シングルテナントにすると自分の組織だけ使うという意味みたいです(自分の組織のテナントIDと紐づく)

REST APIを呼ぶサーバーを準備

Azure Botと通信するサーバーを用意します

簡易的にRubyのSinatraで作ります

bundle init

Gemfileに追加します

# frozen_string_literal: true

source "https://rubygems.org"

gem "sinatra"
gem "rackup"
gem "puma"

gem "debug" # debug用

コードを用意します

Microsoft Teamsで(Bot frameworkを使わずに)ボットを作るには
のコードをベースに一部変えてます(大感謝)

config.ru

require './app'
run App

app.rb

require 'net/http'
require 'sinatra/base'

def fetch_access_token
  resp = Net::HTTP.post_form(
    URI("https://login.microsoftonline.com/#{ENV['TENANT_ID']}/oauth2/v2.0/token"),
    {
      'grant_type' => 'client_credentials',
      'client_id' => ENV['CLIENT_ID'],
      'client_secret' => ENV['CLIENT_SECRET'],
      'scope' => 'https://api.botframework.com/.default',
    }
  )
  JSON.parse(resp.body)['access_token']
end

class App < Sinatra::Base
  get '/' do
    'It works!'
  end

  post '/webhook' do
    json_body = JSON.parse(request.body.read)
    puts "Parameter: #{json_body}"

    if json_body['type'] == 'message'
      Thread.new(json_body) do |params|
        service_url = params['serviceUrl']
        conversation_id = params['conversation']['id']
        access_token = fetch_access_token

        reply = {
          "type": "message",
          "from": {
            "id": "echobot-dev-xxxxx", # BotのID、Azure Bot > ボットプロファイル
            "name": "test bot"
          },
          "text": "Reply for #{params['text']}",
        }
        res = Net::HTTP.post(
          URI("#{service_url}v3/conversations/#{conversation_id}/activities"),
          reply.to_json,
          {
            'Content-Type' => 'application/json',
            'Authorization' => "Bearer #{access_token}"
          }
        )
        puts res.code
        puts res.body
      end
    end

    status 200
    content_type :json
    { status: 200 }.to_json
  end
end

Sinatraを立ち上げます
CLIENT_IDはMicrosoft App IDとTENANT_IDはアプリ テナント IDで、Azure Botの構成の中にあります

bot config

CLIENT_SECRETはパスワードで、Azure Botの構成のMicrosoft App IDのところのパスワードの管理にあります
最初から1つありますが、値が見えないので新規に作り直します

bot secret

環境変数に設定してSinatraを立ち上げます

CLIENT_ID=a3d1532e-xxxxx CLIENT_SECRET=53H8Q~xxxxx TENANT_ID=2f188d8c-xxxxx bundle exec rackup -p 3000

localhost:3000 にアクセスすると It works! と表示されます

インターネット越しにアクセスできるようにするために、ngrokを使います

ngrok http 3000

これでhttps://8baf-219-122-229-79.ngrok-free.app といったURLが用意されSinatraにアクセスできるようになります

これをAzure Botの構成 メッセージング エンドポイントに設定して適用

これで準備ができました

Web チャットでテスト

Azure Bot > Web チャットでテスト
で動作確認します

Web チャットでテストを開くと、Sinatraのログに以下のようなログが出力されます

Parameter: {"type"=>"conversationUpdate", "id"=>"7HGlfJ6bId5", "timestamp"=>"2024-06-04T23:43:57.6302602Z", "serviceUrl"=>"https://webchat.botframework.com/", "channelId"=>"webchat", "from"=>{"id"=>"f8d16b2a-433b-40b5-94b8-6b9fc77318a1"}, "conversation"=>{"id"=>"3ChyzXkXScSAe6B7Z25bKG-as"}, "recipient"=>{"id"=>"echobot-dev-xxxxx@GzEeab4YjXQ", "name"=>"echobot-dev-xxxxx"}, "membersAdded"=>[{"id"=>"echobot-dev-xxxxx@GzEeab4YjXQ", "name"=>"echobot-dev-xxxxx"}, {"id"=>"f8d16b2a-433b-40b5-94b8-6b9fc77318a1"}]}

WebチャットとSinatraが接続されました

Webチャットにメッセージを送ると、Sinatraのログに以下のようなログが出力されます

Parameter: {"type"=>"message", "id"=>"3ChyzXkXScSAe6B7Z25bKG-as|0000000", "timestamp"=>"2024-06-04T23:44:00.6337105Z", "localTimestamp"=>"2024-06-05T08:44:00.399+09:00", "localTimezone"=>"Asia/Tokyo", "serviceUrl"=>"https://webchat.botframework.com/", "channelId"=>"webchat", "from"=>{"id"=>"f8d16b2a-433b-40b5-94b8-6b9fc77318a1", "name"=>""}, "conversation"=>{"id"=>"3ChyzXkXScSAe6B7Z25bKG-as"}, "recipient"=>{"id"=>"echobot-dev-freks-single-tenant@GzEeab4YjXQ", "name"=>"echobot-dev-freks-single-tenant"}, "textFormat"=>"plain", "locale"=>"ja", "text"=>"aaa", "attachments"=>[], "entities"=>[{"type"=>"ClientCapabilities", "requiresBotState"=>true, "supportsListening"=>true, "supportsTts"=>true}], "channelData"=>{"clientActivityID"=>"nsu5o24cqep"}}

Sinatraコードの

puts res.code
puts res.body

はデバッグ用でうまくいくと

200
{
  "id": "3EVDYoZiGWS9qrgSmI5Eko-as|0000001"
}

が出力されます

reply.to_json の中身で from.id が間違っていると 403 エラーになりました
なぜ403になるのかが分かるところがなくて試行錯誤しました

APIの仕様は アクティビティを更新する にありますが、何が必須でどこにその設定があるのか分からないので手探りです

まとめ

情報がまとまってなくて苦労してます

PR

Microsoft Copilot for Microsoft 365活用大全

Microsoft Copilot for Microsoft 365活用大全

このリンクは、アフィリエイトリンクです


Amazonのアソシエイトとして、blog.freks.jp は適格販売により収入を得ています。
This site is managed by freks