feat: locale-aware command templates, debounced auto-sync, entity pickers
- Locale-aware templates: CommandTemplateSlot now has a locale column, allowing each slot to have per-language variants (EN/RU). Templates are resolved at runtime from the Telegram user's language_code. - Merged system configs: "Default Commands (EN)" and "(RU)" merged into a single "Default Commands" config with locale-aware slots. Migration handles existing data automatically. - Configurable command descriptions: hardcoded COMMAND_DESCRIPTIONS replaced with desc_* template slots (desc_status, desc_help, etc.) that users can customize per locale. setMyCommands registers all locales explicitly. - Removed locale from CommandConfig: no longer needed since locale is derived from the Telegram user's language at runtime. - Debounced command auto-sync: after command config/tracker changes, affected bots are marked dirty and synced after a 30s debounce window. Manual "Sync with Telegram" button still works. - Entity pickers in LinkedTargetsSection: replaced 6 plain <select> elements with EntitySelect components (search, icons, keyboard nav). Added onselect callback and size="sm" props to EntitySelect.
This commit is contained in:
@@ -39,7 +39,7 @@ async def lifespan(app: FastAPI):
|
||||
await init_db()
|
||||
# Run data migrations (idempotent)
|
||||
from .database.engine import get_engine
|
||||
from .database.migrations import migrate_schema, migrate_tracker_targets, migrate_entity_refactor, migrate_template_slots, migrate_target_receivers, migrate_template_locale, migrate_receivers_from_config
|
||||
from .database.migrations import migrate_schema, migrate_tracker_targets, migrate_entity_refactor, migrate_template_slots, migrate_target_receivers, migrate_template_locale, migrate_receivers_from_config, migrate_command_slot_locale
|
||||
engine = get_engine()
|
||||
await migrate_schema(engine)
|
||||
await migrate_tracker_targets(engine)
|
||||
@@ -48,6 +48,7 @@ async def lifespan(app: FastAPI):
|
||||
await migrate_target_receivers(engine)
|
||||
await migrate_template_locale(engine)
|
||||
await migrate_receivers_from_config(engine)
|
||||
await migrate_command_slot_locale(engine)
|
||||
await _seed_default_templates()
|
||||
await _seed_default_command_templates()
|
||||
# Configure webhook secret from DB setting (falls back to env var)
|
||||
@@ -196,7 +197,11 @@ async def _seed_default_templates():
|
||||
|
||||
|
||||
async def _seed_default_command_templates():
|
||||
"""Seed or update default command response templates on startup."""
|
||||
"""Seed or update default command response templates on startup.
|
||||
|
||||
Creates a single 'Default Commands' config with locale-aware slots
|
||||
(each slot has an EN and RU version stored as separate rows).
|
||||
"""
|
||||
from sqlmodel import func, select
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
from .database.engine import get_engine
|
||||
@@ -205,59 +210,49 @@ async def _seed_default_command_templates():
|
||||
|
||||
engine = get_engine()
|
||||
async with AsyncSession(engine) as session:
|
||||
result = await session.exec(select(func.count()).select_from(CommandTemplateConfig))
|
||||
count = result.one()
|
||||
# Find or create the system-owned config
|
||||
result = await session.exec(
|
||||
select(CommandTemplateConfig).where(CommandTemplateConfig.user_id == 0)
|
||||
)
|
||||
system_configs = result.all()
|
||||
|
||||
if count == 0:
|
||||
# First startup — seed EN and RU defaults
|
||||
for locale in ("en", "ru"):
|
||||
slots = load_default_command_templates(locale)
|
||||
if not slots:
|
||||
continue
|
||||
name = f"Default Commands ({locale.upper()})"
|
||||
config = CommandTemplateConfig(
|
||||
user_id=0,
|
||||
provider_type="immich",
|
||||
name=name,
|
||||
description=f"Default Immich command templates ({locale.upper()})",
|
||||
locale=locale,
|
||||
if not system_configs:
|
||||
# First startup — create single merged config
|
||||
config = CommandTemplateConfig(
|
||||
user_id=0,
|
||||
provider_type="immich",
|
||||
name="Default Commands",
|
||||
description="Default Immich command templates",
|
||||
)
|
||||
session.add(config)
|
||||
await session.flush()
|
||||
else:
|
||||
config = system_configs[0]
|
||||
|
||||
# Upsert slots for each locale
|
||||
for locale in ("en", "ru"):
|
||||
slots = load_default_command_templates(locale)
|
||||
if not slots:
|
||||
continue
|
||||
for slot_name, template_text in slots.items():
|
||||
slot_result = await session.exec(
|
||||
select(CommandTemplateSlot).where(
|
||||
CommandTemplateSlot.config_id == config.id,
|
||||
CommandTemplateSlot.slot_name == slot_name,
|
||||
CommandTemplateSlot.locale == locale,
|
||||
)
|
||||
)
|
||||
session.add(config)
|
||||
await session.flush()
|
||||
for slot_name, template_text in slots.items():
|
||||
existing = slot_result.first()
|
||||
if existing:
|
||||
existing.template = template_text
|
||||
session.add(existing)
|
||||
else:
|
||||
session.add(CommandTemplateSlot(
|
||||
config_id=config.id,
|
||||
slot_name=slot_name,
|
||||
locale=locale,
|
||||
template=template_text,
|
||||
))
|
||||
else:
|
||||
# Update existing system-owned command templates from files
|
||||
result = await session.exec(
|
||||
select(CommandTemplateConfig).where(CommandTemplateConfig.user_id == 0)
|
||||
)
|
||||
system_configs = result.all()
|
||||
for config in system_configs:
|
||||
locale = config.locale if config.locale else ("ru" if "(RU)" in config.name else "en")
|
||||
slots = load_default_command_templates(locale)
|
||||
if not slots:
|
||||
continue
|
||||
for slot_name, template_text in slots.items():
|
||||
slot_result = await session.exec(
|
||||
select(CommandTemplateSlot).where(
|
||||
CommandTemplateSlot.config_id == config.id,
|
||||
CommandTemplateSlot.slot_name == slot_name,
|
||||
)
|
||||
)
|
||||
existing = slot_result.first()
|
||||
if existing:
|
||||
existing.template = template_text
|
||||
session.add(existing)
|
||||
else:
|
||||
session.add(CommandTemplateSlot(
|
||||
config_id=config.id,
|
||||
slot_name=slot_name,
|
||||
template=template_text,
|
||||
))
|
||||
|
||||
await session.commit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user