stage-8: cron scheduler with APScheduler
Scheduler class, cron/jobs.json, Discord /cron commands, CLI cron subcommand, job lifecycle management. 88 new tests (281 total). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
25
src/main.py
25
src/main.py
@@ -9,7 +9,8 @@ from pathlib import Path
|
||||
|
||||
from src.config import load_config
|
||||
from src.secrets import get_secret
|
||||
from src.adapters.discord_bot import create_bot
|
||||
from src.adapters.discord_bot import create_bot, split_message
|
||||
from src.scheduler import Scheduler
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
PID_FILE = PROJECT_ROOT / "echo-core.pid"
|
||||
@@ -43,6 +44,26 @@ def main():
|
||||
config = load_config()
|
||||
client = create_bot(config)
|
||||
|
||||
# Scheduler setup
|
||||
async def _send_to_channel(channel_alias: str, text: str) -> None:
|
||||
"""Callback: resolve alias and send text to Discord channel."""
|
||||
channels = config.get("channels", {})
|
||||
ch_info = channels.get(channel_alias)
|
||||
if not ch_info:
|
||||
logger.warning("Cron: unknown channel alias '%s'", channel_alias)
|
||||
return
|
||||
channel_id = ch_info.get("id")
|
||||
channel = client.get_channel(int(channel_id))
|
||||
if channel is None:
|
||||
logger.warning("Cron: channel %s not found in Discord cache", channel_id)
|
||||
return
|
||||
chunks = split_message(text)
|
||||
for chunk in chunks:
|
||||
await channel.send(chunk)
|
||||
|
||||
scheduler = Scheduler(send_callback=_send_to_channel, config=config)
|
||||
client.scheduler = scheduler # type: ignore[attr-defined]
|
||||
|
||||
# PID file
|
||||
PID_FILE.write_text(str(os.getpid()))
|
||||
|
||||
@@ -51,6 +72,7 @@ def main():
|
||||
|
||||
def handle_signal(sig, frame):
|
||||
logger.info("Received signal %s, shutting down...", sig)
|
||||
loop.create_task(scheduler.stop())
|
||||
loop.create_task(client.close())
|
||||
|
||||
signal.signal(signal.SIGTERM, handle_signal)
|
||||
@@ -59,6 +81,7 @@ def main():
|
||||
try:
|
||||
loop.run_until_complete(client.start(token))
|
||||
except KeyboardInterrupt:
|
||||
loop.run_until_complete(scheduler.stop())
|
||||
loop.run_until_complete(client.close())
|
||||
finally:
|
||||
PID_FILE.unlink(missing_ok=True)
|
||||
|
||||
Reference in New Issue
Block a user