![[Develop] Python을 사용한 Azure Chatbot을 생성 및 Teams 연결 과정](/static/3565f14618bf3b71db002582aac29fe5/97f9c/chatbot.png)
MicroSoft에서 제공하고 있는 Chatbot SDK OpenSource 입니다.
C#, JS, Python, Java 등 여러 언어를 사용해서 SDK를 사용 할 수 있고 제작한 템플릿을 쉽게 Azure의 Service와 연동 할 수 있습니다.
MS에서 Python용 Sample을 이미 제공하고 있어서 Sample Code를 수정해서 쓰는걸로 테스트 해보겠습니다.
명령어 : # az group create -l koreaCentral -n pynasa
저는 VSCODE를 주 IDE로 사용하기 때문에 VSCODE를 기반으로 App Service를 생성하겠습니다.
VSCODE에서 아래 extension을 추가 설치가 필요합니다. 아래 두개의 Extension을 쓰면 Azure로 Web Bot을 손쉽게 배포가 가능합니다.
구독에서 우클릭 -> Create New Web App(Advanced) 선택해 생성!
Resouce Group : pynasa
Python 3.7
App Service Plan : 신규 생성
Pricing tier : S2
Application Insitghts : Skip Region : Korea Central
- Bot handle : nasapy
- Resource Group : pynasa
- Location : KoreaCentral
- Messaging endpoint : App Service url
- App Service의 URL에 + api/massages 를 넣으면 됩니다.
- ex : https://test.azurewebsites.net/api/massages>
- Microsoft App ID and password : 자동생성
APP ID는 나중에 Code, App 연동에 필요하니 복사해놓으세요!!
처음 생성된 Value는 보이지가 않아서 삭제 후 새로 생성해서 복사해놓아야 합니다.
- MicrosoftAppId : 봇채널에서 복사한 ID
- MicrosoftAppPassword : 봇채널의 Value 값
- WEBSITE_HTTPLOGGING_RETENTION_DAYS : 7
- WEBSITES_CONTAINER_START_TIME_LIMIT : 1400
- Startup command
python3.7 -m aiohttp.web -H 0.0.0.0 -P 8000 app:init_func- 해당 Stratup command가 없으면 정상적으로 동작하지 않습니다.
- 웹 소켓도 열어주세요
드디어 코드를 볼 수 있습니다.
일단 VSCODE로 작업을 위해서 위의 Sample Code를 Clone 해옵니다.
import sys import traceback from datetime import datetime from http import HTTPStatus from aiohttp import web from aiohttp.web import Request, Response, json_response from botbuilder.core import ( BotFrameworkAdapterSettings, TurnContext, BotFrameworkAdapter, ) from botbuilder.core.integration import aiohttp_error_middleware from botbuilder.schema import Activity, ActivityTypes from bots import EchoBot from config import DefaultConfig CONFIG = DefaultConfig() # Create adapter. # See https://aka.ms/about-bot-adapter to learn more about how bots work. SETTINGS = BotFrameworkAdapterSettings(CONFIG.APP_ID, CONFIG.APP_PASSWORD) ADAPTER = BotFrameworkAdapter(SETTINGS) # Catch-all for errors. async def on_error(context: TurnContext, error: Exception): # This check writes out errors to console log .vs. app insights. # NOTE: In production environment, you should consider logging this to Azure # application insights. print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) traceback.print_exc() # Send a message to the user await context.send_activity("The bot encountered an error or bug.") await context.send_activity( "To continue to run this bot, please fix the bot source code." ) # Send a trace activity if we're talking to the Bot Framework Emulator if context.activity.channel_id == "emulator": # Create a trace activity that contains the error object trace_activity = Activity( label="TurnError", name="on_turn_error Trace", timestamp=datetime.utcnow(), type=ActivityTypes.trace, value=f"{error}", value_type="https://www.botframework.com/schemas/error", ) # Send a trace activity, which will be displayed in Bot Framework Emulator await context.send_activity(trace_activity) ADAPTER.on_turn_error = on_error # Create the Bot BOT = EchoBot() # Listen for incoming requests on /api/messages async def messages(req: Request) -> Response: # Main bot message handler. if "application/json" in req.headers["Content-Type"]: body = await req.json() else: return Response(status=HTTPStatus.UNSUPPORTED_MEDIA_TYPE) activity = Activity().deserialize(body) auth_header = req.headers["Authorization"] if "Authorization" in req.headers else "" response = await ADAPTER.process_activity(activity, auth_header, BOT.on_turn) if response: return json_response(data=response.body, status=response.status) return Response(status=HTTPStatus.OK) APP = web.Application(middlewares=[aiohttp_error_middleware]) APP.router.add_post("/api/messages", messages) if __name__ == "__main__": try: web.run_app(APP, host="localhost", port=CONFIG.PORT) except Exception as error: raise error
app.py는 코드 그대로 Azure에 배포하면 정상 동작하지 않습니다.
Python은 Flask기반의 Code로 Azure App Service에서는 바로 실행이 안되고
There was an error sending this message to your bot: HTTP status code GatewayTimeout
에러가 발생합니다.
Python은 js, C# 같이 Templetes 형태와 다르게 APP 실행 구문 수정이 필요합니다. 아래처럼 init_func의 실행 구문을 APP에 넣어준 뒤 실행해야 합니다.
def init_func(argv): << -- 해당 부분 함수 처리 APP = web.Application(middlewares=[aiohttp_error_middleware]) APP.router.add_post("/api/messages", messages) return APP if __name__ == "__main__": APP = init_func(None) << --- 해당 부분 추가 try: web.run_app(APP, host="localhost", port=CONFIG.PORT) except Exception as error: raise error
import os """ Bot Configuration """ class DefaultConfig: """ Bot Configuration """ PORT = 3978 APP_ID = os.environ.get("MicrosoftAppId", "") APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "")
- Port , App_ID, APP_Password 값 들을 모두 수정해줍니다.
- Port : 8080
- APP_ID : 아까 Channel에 넣은 ID 값
- APP_PASSWORD : Value 값
from botbuilder.core import ActivityHandler, MessageFactory, TurnContext from botbuilder.schema import ChannelAccount class EchoBot(ActivityHandler): async def on_members_added_activity( self, members_added: [ChannelAccount], turn_context: TurnContext ): for member in members_added: if member.id != turn_context.activity.recipient.id: await turn_context.send_activity("Hello and welcome!") async def on_message_activity(self, turn_context: TurnContext): return await turn_context.send_activity( MessageFactory.text(f"Echo: {turn_context.activity.text}") )
해당 코드는 Open Message와 사용자의 Massage를 Return 하는 구문입니다.
for member_added in members_added: if member_added.id != turn_context.activity.recipient.id: await turn_context.send_activity("Hello I'm NaSa!")
이제 모든 수정사항들은 다 반영이 되었기 때문에 Azure Web Service에 Code 배포 후 결과를 확인해보죠
Azure Web Service로 Python Code Bot을 배포하는 방법은 매우 다양합니다.
Azure Cli를 이용 할 수도 있고…그러나 js, C#처럼 자체 Templetes으로 생성하는 UI를 제공하지 않아서
CLI를 사용하기에는 여러가지 불편한점들이 많습니다… 그래서 저는 Cli보다는 VSCODE의 배포 기능을 사용했습니다..
- Extension의 App Service 우클릭 - Deploy to Web App…
- 다음과 같이 Web Code가 제대로 배포되서 Success Code를 출력했습니다!!
- Bot.py에서 수정했던 Open Message와 제가 적은 Return 값이 정확히 반환되네요!!