Files
playchat/backend/app/core/discord_bot.py
T
colcaroliandClaude Sonnet 4.6 4a08ba29fa Add system memory monitoring, Bedrock support, and Discord integrations
- Add CPU/GPU memory monitoring endpoint and frontend widget
- Add Amazon Bedrock model support (API key / inference profiles) plus model selector entries
- Add Discord weather/laundry notification job (JMA forecast + LLM-generated message)
- Add Discord bot that replies to mentions via the configured LLM, with weather-aware context
- Add scheduled "useless tips" job with separate webhook and quiet-hours skip
- Switch FastAPI startup to lifespan handler, log viewer, markdown rendering, and toast UI additions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 00:59:38 +09:00

85 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""DiscordでメンションされたらplaychatのLLMが返信するBot。"""
import logging
import discord
from app.core.config import (
DISCORD_BOT_SYSTEM_PROMPT,
DISCORD_BOT_TOKEN,
WEATHER_AREA_CODE,
WEATHER_AREA_KEYWORD,
WEATHER_NOTIFY_MODEL,
)
from app.core.llm import complete
from app.core.weather import fetch_tomorrow_weather, judge_laundry
logger = logging.getLogger(__name__)
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
WEATHER_KEYWORDS = ("天気", "天候", "洗濯", "雨", "気温", "降水")
async def _build_weather_context() -> str:
"""明日の天気データを取得し、LLMに渡すコンテキスト文字列を作る。失敗時は取得不可の旨を返す。"""
try:
weather_data = await fetch_tomorrow_weather(WEATHER_AREA_CODE, WEATHER_AREA_KEYWORD)
judgement, reason = judge_laundry(weather_data["weather"], weather_data["pop"])
pop_text = f"{weather_data['pop']}%" if weather_data["pop"] is not None else "不明"
return (
"【明日の天気予報データ(気象庁発表)】\n"
f"天気: {weather_data['weather']}\n"
f"降水確率: {pop_text}\n"
f"洗濯指数: {judgement}({reason})\n"
"このデータをもとにユーザーへ回答してください。"
)
except Exception:
logger.exception("Failed to fetch weather data for Discord reply")
return "(明日の天気データの取得に失敗しました。一般的な回答をしてください。)"
@client.event
async def on_ready():
logger.info("Discord bot logged in as %s", client.user)
@client.event
async def on_message(message: discord.Message):
if message.author.bot:
return
if client.user not in message.mentions:
return
content = message.content.replace(f"<@{client.user.id}>", "").strip()
if not content:
content = "こんにちは"
async with message.channel.typing():
try:
messages = [{"role": "system", "content": DISCORD_BOT_SYSTEM_PROMPT}]
if any(keyword in content for keyword in WEATHER_KEYWORDS):
weather_context = await _build_weather_context()
messages.append({"role": "system", "content": weather_context})
messages.append({"role": "user", "content": content})
reply = await complete(messages, model=WEATHER_NOTIFY_MODEL)
await message.reply(reply or "(応答が空でした)")
except Exception:
logger.exception("Failed to generate reply for Discord mention")
await message.reply("⚠️ 応答の生成に失敗しました")
async def start_discord_bot() -> None:
if not DISCORD_BOT_TOKEN:
logger.warning("DISCORD_BOT_TOKEN is not set; Discord bot will not start")
return
try:
await client.start(DISCORD_BOT_TOKEN)
except Exception:
logger.exception("Discord bot failed to start")