# /intergrations/botframework/app.py — aiohttp + Bot Framework Echo bot #!/usr/bin/env python3 import os import sys import json from logic import handle_text from aiohttp import web # from botbuilder.core import BotFrameworkAdapter, BotFrameworkAdapterSettings, TurnContext # from botbuilder.schema import Activity import aiohttp_cors from pathlib import Path # ------------------------------------------------------------------- # Your bot implementation # ------------------------------------------------------------------- # Make sure this exists at packages/bots/echo_bot.py # from bots.echo_bot import EchoBot # Minimal inline fallback if you want to test quickly: class EchoBot: async def on_turn(self, turn_context: TurnContext): if turn_context.activity.type == "message": text = (turn_context.activity.text or "").strip() if not text: await turn_context.send_activity("Input was empty. Type 'help' for usage.") return lower = text.lower() if lower == "help": await turn_context.send_activity("Try: echo | reverse: | capabilities") elif lower == "capabilities": await turn_context.send_activity("- echo\n- reverse\n- help\n- capabilities") elif lower.startswith("reverse:"): payload = text.split(":", 1)[1].strip() await turn_context.send_activity(payload[::-1]) elif lower.startswith("echo "): await turn_context.send_activity(text[5:]) else: await turn_context.send_activity("Unsupported command. Type 'help' for examples.") else: await turn_context.send_activity(f"[{turn_context.activity.type}] event received.") # ------------------------------------------------------------------- # Adapter / bot setup # ------------------------------------------------------------------- APP_ID = os.environ.get("MicrosoftAppId") or None APP_PASSWORD = os.environ.get("MicrosoftAppPassword") or None adapter_settings = BotFrameworkAdapterSettings(APP_ID, APP_PASSWORD) adapter = BotFrameworkAdapter(adapter_settings) async def on_error(context: TurnContext, error: Exception): print(f"[on_turn_error] {error}", file=sys.stderr, flush=True) try: await context.send_activity("Oops. Something went wrong!") except Exception as send_err: print(f"[on_turn_error][send_activity_failed] {send_err}", file=sys.stderr, flush=True) adapter.on_turn_error = on_error bot = EchoBot() # ------------------------------------------------------------------- # HTTP handlers # ------------------------------------------------------------------- async def messages(req: web.Request) -> web.Response: # Content-Type can include charset; do a contains check ctype = (req.headers.get("Content-Type") or "").lower() if "application/json" not in ctype: return web.Response(status=415, text="Unsupported Media Type: expected application/json") try: body = await req.json() except json.JSONDecodeError: return web.Response(status=400, text="Invalid JSON body") activity = Activity().deserialize(body) auth_header = req.headers.get("Authorization") invoke_response = await adapter.process_activity(activity, auth_header, bot.on_turn) if invoke_response: # For invoke activities, adapter returns explicit status/body return web.json_response(data=invoke_response.body, status=invoke_response.status) # Acknowledge standard message activities return web.Response(status=202, text="Accepted") async def home(_req: web.Request) -> web.Response: return web.Response( text="Bot is running. POST Bot Framework activities to /api/messages.", content_type="text/plain" ) async def messages_get(_req: web.Request) -> web.Response: return web.Response( text="This endpoint only accepts POST (Bot Framework activities).", content_type="text/plain", status=405 ) async def healthz(_req: web.Request) -> web.Response: return web.json_response({"status": "ok"}) async def plain_chat(req: web.Request) -> web.Response: try: payload = await req.json() except Exception: return web.json_response({"error": "Invalid JSON"}, status=400) user_text = payload.get("text", "") reply = handle_text(user_text) return web.json_response({"reply": reply}) # ------------------------------------------------------------------- # App factory and entrypoint # ------------------------------------------------------------------- from pathlib import Path def create_app() -> web.Application: app = web.Application() app.router.add_get("/", home) app.router.add_get("/healthz", healthz) app.router.add_get("/api/messages", messages_get) app.router.add_post("/api/messages", messages) app.router.add_post("/plain-chat", plain_chat) static_dir = Path(__file__).parent / "static" if static_dir.exists(): app.router.add_static("/static/", path=static_dir, show_index=True) else: print(f"[warn] static directory not found: {static_dir}", flush=True) return app app = create_app() if __name__ == "__main__": host = os.environ.get("HOST", "127.0.0.1") # use 0.0.0.0 in containers port = int(os.environ.get("PORT", 3978)) web.run_app(app, host=host, port=port)