Fresh start - excluded large ROM JSON files

This commit is contained in:
OpenClaw Agent
2026-04-11 09:45:12 -05:00
commit 5deb387aa6
395 changed files with 47744 additions and 0 deletions

270
tools/reminder-manager.py Normal file
View File

@@ -0,0 +1,270 @@
#!/usr/bin/env python3
"""
Reminder Manager for OpenClaw Discord
Handles one-shot and recurring reminders via OpenClaw cron + SQLite
"""
import sqlite3
import os
import sys
import json
import re
from datetime import datetime, timedelta
from pathlib import Path
DB_PATH = os.path.expanduser("~/.openclaw/workspace/data/reminders.db")
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
def init_db():
"""Create the reminders database."""
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS reminders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message TEXT NOT NULL,
remind_at TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
is_recurring INTEGER DEFAULT 0,
recurrence_rule TEXT,
cron_job_id TEXT,
active INTEGER DEFAULT 1
)''')
conn.commit()
conn.close()
def parse_time(time_str: str) -> datetime:
"""Parse various time formats into datetime."""
now = datetime.now()
time_str = time_str.lower().strip()
# Relative times: 20m, 2h, 1h30m
match = re.match(r'^(\d+)m$', time_str)
if match:
return now + timedelta(minutes=int(match.group(1)))
match = re.match(r'^(\d+)h$', time_str)
if match:
return now + timedelta(hours=int(match.group(1)))
match = re.match(r'^(\d+)h(\d+)m$', time_str)
if match:
return now + timedelta(hours=int(match.group(1)), minutes=int(match.group(2)))
# Tomorrow
if time_str == 'tomorrow':
return now + timedelta(days=1)
# Tomorrow at time: tomorrow 9am, tomorrow 14:00
match = re.match(r'^tomorrow\s+([\d:]+)(am|pm)?$', time_str)
if match:
time_part = match.group(1)
ampm = match.group(2)
tomorrow = now + timedelta(days=1)
if ':' in time_part:
hour, minute = map(int, time_part.split(':'))
else:
hour, minute = int(time_part), 0
if ampm == 'pm' and hour != 12:
hour += 12
if ampm == 'am' and hour == 12:
hour = 0
return tomorrow.replace(hour=hour, minute=minute, second=0, microsecond=0)
# Today at time: 9am, 14:00, 2:30pm
match = re.match(r'^(\d{1,2}):(\d{2})(am|pm)?$', time_str)
if match:
hour = int(match.group(1))
minute = int(match.group(2))
ampm = match.group(3)
if ampm == 'pm' and hour != 12:
hour += 12
if ampm == 'am' and hour == 12:
hour = 0
result = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
if result < now:
result += timedelta(days=1)
return result
match = re.match(r'^(\d{1,2})(am|pm)$', time_str)
if match:
hour = int(match.group(1))
ampm = match.group(2)
if ampm == 'pm' and hour != 12:
hour += 12
if ampm == 'am' and hour == 12:
hour = 0
result = now.replace(hour=hour, minute=0, second=0, microsecond=0)
if result < now:
result += timedelta(days=1)
return result
raise ValueError(f"Can't parse time: {time_str}")
def add_reminder(user_id: str, channel_id: str, message: str, time_str: str) -> dict:
"""Add a new reminder and schedule it via OpenClaw cron."""
init_db()
remind_at = parse_time(time_str)
if remind_at < datetime.now():
return {"error": "Reminder time is in the past"}
# Insert into DB
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('''INSERT INTO reminders (user_id, channel_id, message, remind_at, active)
VALUES (?, ?, ?, ?, 1)''',
(user_id, channel_id, message, remind_at.isoformat()))
reminder_id = c.lastrowid
conn.commit()
conn.close()
# Schedule via OpenClaw cron (will be handled by caller)
return {
"id": reminder_id,
"message": message,
"remind_at": remind_at.isoformat(),
"user_id": user_id,
"channel_id": channel_id
}
def list_reminders(user_id: str = None) -> list:
"""List active reminders for a user or all users."""
init_db()
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
if user_id:
c.execute('''SELECT id, message, remind_at, channel_id
FROM reminders
WHERE user_id = ? AND active = 1 AND remind_at > datetime('now')
ORDER BY remind_at''', (user_id,))
else:
c.execute('''SELECT id, message, remind_at, channel_id, user_id
FROM reminders
WHERE active = 1 AND remind_at > datetime('now')
ORDER BY remind_at''')
results = c.fetchall()
conn.close()
return results
def delete_reminder(reminder_id: int, user_id: str = None) -> bool:
"""Delete/cancel a reminder."""
init_db()
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
if user_id:
c.execute('DELETE FROM reminders WHERE id = ? AND user_id = ?',
(reminder_id, user_id))
else:
c.execute('DELETE FROM reminders WHERE id = ?', (reminder_id,))
deleted = c.rowcount > 0
conn.commit()
conn.close()
return deleted
def delete_past_reminders():
"""Clean up old reminder entries."""
init_db()
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('DELETE FROM reminders WHERE remind_at < datetime("now", "-1 day")')
conn.commit()
conn.close()
def cron_callback(reminder_id: int):
"""Called when a cron job fires - returns the reminder details."""
init_db()
conn = sqlite3.connect(DB_PATH)
c = conn.cursor()
c.execute('SELECT message, user_id, channel_id FROM reminders WHERE id = ?',
(reminder_id,))
result = c.fetchone()
if result:
# Mark as inactive after firing
c.execute('UPDATE reminders SET active = 0 WHERE id = ?', (reminder_id,))
conn.commit()
conn.close()
if result:
return {
"message": result[0],
"user_id": result[1],
"channel_id": result[2]
}
return None
if __name__ == "__main__":
command = sys.argv[1] if len(sys.argv) > 1 else "help"
if command == "add":
# Usage: reminder-manager.py add "user_id" "channel_id" "message" "time"
user_id = sys.argv[2]
channel_id = sys.argv[3]
message = sys.argv[4]
time_str = sys.argv[5]
result = add_reminder(user_id, channel_id, message, time_str)
print(json.dumps(result))
elif command == "list":
user_id = sys.argv[2] if len(sys.argv) > 2 else None
reminders = list_reminders(user_id)
print(json.dumps([{
"id": r[0],
"message": r[1],
"remind_at": r[2],
"channel_id": r[3],
"user_id": r[4] if len(r) > 4 else None
} for r in reminders]))
elif command == "delete":
reminder_id = int(sys.argv[2])
user_id = sys.argv[3] if len(sys.argv) > 3 else None
deleted = delete_reminder(reminder_id, user_id)
print(json.dumps({"deleted": deleted}))
elif command == "callback":
reminder_id = int(sys.argv[2])
result = cron_callback(reminder_id)
print(json.dumps(result) if result else "null")
elif command == "cleanup":
delete_past_reminders()
print("Cleanup complete")
else:
print("""Usage:
reminder-manager.py add "user_id" "channel_id" "message" "time"
reminder-manager.py list [user_id]
reminder-manager.py delete <reminder_id> [user_id]
reminder-manager.py callback <reminder_id>
reminder-manager.py cleanup
""")