Add weather caching, useless-tip redirect, clock widget, and IAM Bedrock auth
- Cache the latest fetched tomorrow weather and expose it via /api/system/weather/latest - Add a header Clock widget showing date/time plus the cached weather summary - Redirect useless-talk mentions from the weather bot to the useless-tips webhook - Switch Bedrock auth from bearer token to standard IAM access key/secret Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,9 @@ from app.core.config import (
|
||||
WEATHER_NOTIFY_MODEL,
|
||||
)
|
||||
from app.core.llm import complete
|
||||
from app.core.scheduler import send_useless_tip
|
||||
from app.core.weather import fetch_tomorrow_weather, judge_laundry
|
||||
from app.core.weather_cache import set_latest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,6 +23,7 @@ intents.message_content = True
|
||||
client = discord.Client(intents=intents)
|
||||
|
||||
WEATHER_KEYWORDS = ("天気", "天候", "洗濯", "雨", "気温", "降水")
|
||||
USELESS_TALK_KEYWORDS = ("どうでもいい", "雑談", "暇", "つまんない", "退屈")
|
||||
|
||||
|
||||
async def _build_weather_context() -> str:
|
||||
@@ -29,6 +32,7 @@ async def _build_weather_context() -> str:
|
||||
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 "不明"
|
||||
set_latest(weather_data["weather"], weather_data["pop"], judgement, reason)
|
||||
return (
|
||||
"【明日の天気予報データ(気象庁発表)】\n"
|
||||
f"天気: {weather_data['weather']}\n"
|
||||
@@ -59,6 +63,12 @@ async def on_message(message: discord.Message):
|
||||
|
||||
async with message.channel.typing():
|
||||
try:
|
||||
if any(keyword in content for keyword in USELESS_TALK_KEYWORDS):
|
||||
redirect = await _generate_useless_talk_redirect(content)
|
||||
await message.reply(redirect)
|
||||
await send_useless_tip()
|
||||
return
|
||||
|
||||
messages = [{"role": "system", "content": DISCORD_BOT_SYSTEM_PROMPT}]
|
||||
|
||||
if any(keyword in content for keyword in WEATHER_KEYWORDS):
|
||||
@@ -74,6 +84,27 @@ async def on_message(message: discord.Message):
|
||||
await message.reply("⚠️ 応答の生成に失敗しました")
|
||||
|
||||
|
||||
async def _generate_useless_talk_redirect(content: str) -> str:
|
||||
"""どうでもいい話を振られたとき、『どうでもいいTips』チャンネルへ誘導する一言を生成する。"""
|
||||
fallback = "🤷 雑談なら『どうでもいいTips』チャンネルの出番です!今ちょうど一つ送りますね〜"
|
||||
|
||||
prompt = (
|
||||
"あなたは「お天気アプリ」というDiscord Botです。"
|
||||
"ユーザーがどうでもいい雑談を振ってきました。"
|
||||
"あなたは天気専門なので、雑談は『どうでもいいTips』という別のBot/チャンネルにお任せしたいです。"
|
||||
"そこへ話題を振るような、軽いノリの一言を1〜2文で返してください。"
|
||||
"絵文字を1つ程度使い、出力は本文のみにしてください。\n\n"
|
||||
f"ユーザーの発言: {content}"
|
||||
)
|
||||
|
||||
try:
|
||||
result = await complete([{"role": "user", "content": prompt}], model=WEATHER_NOTIFY_MODEL)
|
||||
return result.strip() or fallback
|
||||
except Exception:
|
||||
logger.exception("Failed to generate useless talk redirect message")
|
||||
return fallback
|
||||
|
||||
|
||||
async def start_discord_bot() -> None:
|
||||
if not DISCORD_BOT_TOKEN:
|
||||
logger.warning("DISCORD_BOT_TOKEN is not set; Discord bot will not start")
|
||||
|
||||
Reference in New Issue
Block a user