feat: remove hardcoded command templates, enforce template system exclusively
- Remove ALL hardcoded EN/RU fallback strings from handler.py — every command response now renders through CommandTemplateSlot templates - _render_cmd_template now returns error placeholder instead of None when template is missing, ensuring no silent failures - Fix register_commands_with_telegram tuple unpacking bug (was ignoring cmd_template_slots from _resolve_command_context) - Auto-assign system default template (matching locale) on command config creation when none specified - Add command_template_config_id to CommandConfigCreate model - Remove "no template" option from frontend dropdown — template is now required for command configs - Auto-select first matching template when creating new command config - Fix || vs ?? for command_template_config_id, default_count, and rate_limits in frontend edit function (0 was treated as falsy)
This commit is contained in:
@@ -10,7 +10,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
from ..auth.dependencies import get_current_user
|
||||
from ..database.engine import get_session
|
||||
from ..database.models import CommandConfig, CommandTracker, User
|
||||
from ..database.models import CommandConfig, CommandTemplateConfig, CommandTracker, User
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -26,6 +26,7 @@ class CommandConfigCreate(BaseModel):
|
||||
response_mode: str = "media"
|
||||
default_count: int = 5
|
||||
rate_limits: dict[str, Any] = {}
|
||||
command_template_config_id: int | None = None
|
||||
|
||||
|
||||
class CommandConfigUpdate(BaseModel):
|
||||
@@ -36,6 +37,7 @@ class CommandConfigUpdate(BaseModel):
|
||||
response_mode: str | None = None
|
||||
default_count: int | None = None
|
||||
rate_limits: dict[str, Any] | None = None
|
||||
command_template_config_id: int | None = None
|
||||
|
||||
|
||||
@router.get("")
|
||||
@@ -65,7 +67,18 @@ async def create_command_config(
|
||||
detail=f"Invalid provider_type. Must be one of: {', '.join(valid_types)}",
|
||||
)
|
||||
|
||||
config = CommandConfig(user_id=user.id, **body.model_dump())
|
||||
data = body.model_dump()
|
||||
# Auto-assign system default template if none specified
|
||||
if not data.get("command_template_config_id"):
|
||||
locale = data.get("locale", "en")
|
||||
provider_type = data.get("provider_type", "immich")
|
||||
default_tpl = await _find_system_default_template(
|
||||
session, provider_type, locale
|
||||
)
|
||||
if default_tpl:
|
||||
data["command_template_config_id"] = default_tpl.id
|
||||
|
||||
config = CommandConfig(user_id=user.id, **data)
|
||||
session.add(config)
|
||||
await session.commit()
|
||||
await session.refresh(config)
|
||||
@@ -91,9 +104,13 @@ async def update_command_config(
|
||||
session: AsyncSession = Depends(get_session),
|
||||
):
|
||||
"""Update a command config."""
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
config = await _get_user_config(session, config_id, user.id)
|
||||
for field, value in body.model_dump(exclude_unset=True).items():
|
||||
setattr(config, field, value)
|
||||
# JSON columns need explicit dirty flag for SQLAlchemy change detection
|
||||
if field in ("rate_limits", "enabled_commands"):
|
||||
flag_modified(config, field)
|
||||
session.add(config)
|
||||
await session.commit()
|
||||
await session.refresh(config)
|
||||
@@ -129,6 +146,7 @@ def _config_response(c: CommandConfig) -> dict:
|
||||
"response_mode": c.response_mode,
|
||||
"default_count": c.default_count,
|
||||
"rate_limits": c.rate_limits or {},
|
||||
"command_template_config_id": c.command_template_config_id,
|
||||
"created_at": c.created_at.isoformat(),
|
||||
}
|
||||
|
||||
@@ -140,3 +158,24 @@ async def _get_user_config(
|
||||
if not config or config.user_id != user_id:
|
||||
raise HTTPException(status_code=404, detail="Command config not found")
|
||||
return config
|
||||
|
||||
|
||||
async def _find_system_default_template(
|
||||
session: AsyncSession, provider_type: str, locale: str
|
||||
) -> CommandTemplateConfig | None:
|
||||
"""Find a system default (user_id=0) command template matching provider + locale."""
|
||||
# Try exact locale match first (e.g. "Default Commands (EN)" for locale "en")
|
||||
locale_upper = locale.upper()
|
||||
result = await session.exec(
|
||||
select(CommandTemplateConfig).where(
|
||||
CommandTemplateConfig.user_id == 0,
|
||||
CommandTemplateConfig.provider_type == provider_type,
|
||||
)
|
||||
)
|
||||
templates = result.all()
|
||||
# Match by locale suffix in name, e.g. "(EN)" or "(RU)"
|
||||
for tpl in templates:
|
||||
if f"({locale_upper})" in tpl.name:
|
||||
return tpl
|
||||
# Fallback: return first system template for this provider
|
||||
return templates[0] if templates else None
|
||||
|
||||
Reference in New Issue
Block a user