| Server IP : 3.147.158.171 / Your IP : 216.73.216.88 Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.21 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /tsai/repo/api/ |
Upload File : |
"""
Character Article Schedule Script
Fetches a character by slug and sets its calendar-based articleSchedule.
Usage:
python schedule_character.py <slug> <site> [--days-of-week ... | --days-of-month ...] [options]
python schedule_character.py <slug> <site> --clear
Arguments:
slug Character slug
site Site slug (e.g., jpt, tsai, sandbox)
Options:
--kind {article,comment} Which schedule to set (default: article)
--days-of-week <csv> Comma-separated 0-6 (0=Sun..6=Sat)
--days-of-month <csv> Comma-separated 1-31
--preferred-hours <csv> Comma-separated 0-23
--clear Remove articleSchedule (set to null)
--env <env> Environment (default: TSAI_ENV or development)
"""
import argparse
import json
import os
import sys
from pathlib import Path
parser = argparse.ArgumentParser(description="Set a character's calendar articleSchedule.")
parser.add_argument("slug", help="Character slug")
parser.add_argument("site", help="Site slug")
parser.add_argument(
"--kind",
choices=["article", "comment"],
default="article",
help="Which schedule to set: article (default) or comment",
)
parser.add_argument("--days-of-week", help="CSV of 0-6 (0=Sun)")
parser.add_argument("--days-of-month", help="CSV of 1-31")
parser.add_argument("--preferred-hours", help="CSV of 0-23")
parser.add_argument("--clear", action="store_true", help="Remove articleSchedule (set to null)")
parser.add_argument(
"--env",
choices=["development", "production", "scike_ai"],
default=os.getenv("TSAI_ENV", "development"),
help="Environment (default: TSAI_ENV or development)",
)
args = parser.parse_args()
os.environ["TSAI_ENV"] = args.env
from dotenv import load_dotenv
API_DIR = Path(__file__).resolve().parent
load_dotenv(dotenv_path=API_DIR / ".env")
os.environ["TSAI_ENV"] = args.env
import requests
from app.services.site_config_service import SiteConfigService
def parse_int_csv(value: str, lo: int, hi: int, label: str) -> list:
if value is None or value == "":
return []
try:
nums = [int(x.strip()) for x in value.split(",") if x.strip() != ""]
except ValueError:
print(f"Error: --{label} must be a comma-separated list of integers")
sys.exit(1)
for n in nums:
if n < lo or n > hi:
print(f"Error: --{label} value {n} out of range ({lo}-{hi})")
sys.exit(1)
return sorted(set(nums))
def main():
has_day_flags = bool(args.days_of_week or args.days_of_month or args.preferred_hours)
if not args.clear and not has_day_flags:
print("Error: must supply day/hour flags (--days-of-week / --days-of-month / "
"--preferred-hours) or --clear")
sys.exit(1)
if args.clear and has_day_flags:
print("Error: --clear cannot be combined with other schedule flags")
sys.exit(1)
new_schedule = None
if not args.clear:
dow = parse_int_csv(args.days_of_week, 0, 6, "days-of-week")
dom = parse_int_csv(args.days_of_month, 1, 31, "days-of-month")
hrs = parse_int_csv(args.preferred_hours, 0, 23, "preferred-hours")
if not dow and not dom:
print("Error: a schedule requires at least one of --days-of-week or --days-of-month")
sys.exit(1)
new_schedule = {
"daysOfWeek": dow or None,
"daysOfMonth": dom or None,
"preferredHours": hrs or None,
}
site_config_service = SiteConfigService()
site = site_config_service.get_site_by_slug(args.site)
if not site:
print(f'Error: site "{args.site}" not found in {args.env} config.')
sys.exit(1)
origin = site.cb_host if site.cb_host.startswith("http") else f"https://{site.cb_host}"
headers = {"Origin": origin}
get_url = f"http://127.0.0.1:8000/api/v1/cms/character/by-slug/{args.site}/{args.slug}"
try:
resp = requests.get(get_url, headers=headers)
except requests.ConnectionError:
print("Error: Could not connect to FastAPI server at http://127.0.0.1:8000")
print("Start it with: cd api && uvicorn app.main:app --reload")
sys.exit(1)
if resp.status_code != 200:
print(f"Error fetching character '{args.slug}': {resp.status_code} {resp.text}")
sys.exit(1)
character = resp.json()["data"]
print(f"Character: {character['name']} (slug: {character['slug']})")
schedule_field = "articleSchedule" if args.kind == "article" else "commentSchedule"
old = character.get(schedule_field)
print(f"Current {schedule_field}: {json.dumps(old)}")
character[schedule_field] = new_schedule
patch_url = "http://127.0.0.1:8000/api/v1/cms/character/"
resp = requests.patch(
patch_url,
json={"character": character},
headers=headers,
)
if resp.status_code != 200:
print(f"Error patching character: {resp.status_code} {resp.text}")
sys.exit(1)
updated = resp.json()["data"]
print(f"New {schedule_field}: {json.dumps(updated.get(schedule_field))}")
if __name__ == "__main__":
main()