Allow coercible chat_id values (#186)
Co-authored-by: banteg <4562643+banteg@users.noreply.github.com>
This commit is contained in:
+12
-2
@@ -5,6 +5,7 @@ from typing import Annotated, Any, ClassVar, Literal
|
||||
from collections.abc import Iterable
|
||||
|
||||
from pydantic import (
|
||||
BeforeValidator,
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
@@ -53,6 +54,15 @@ def _normalize_project_path(value: str, *, config_path: Path) -> Path:
|
||||
return path
|
||||
|
||||
|
||||
def _coerce_chat_id(value: Any) -> Any:
|
||||
if isinstance(value, str):
|
||||
return int(value.strip())
|
||||
return value
|
||||
|
||||
|
||||
ChatId = Annotated[StrictInt, BeforeValidator(_coerce_chat_id)]
|
||||
|
||||
|
||||
class TelegramTopicsSettings(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
||||
|
||||
@@ -93,7 +103,7 @@ class TelegramTransportSettings(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
||||
|
||||
bot_token: NonEmptyStr
|
||||
chat_id: StrictInt
|
||||
chat_id: ChatId
|
||||
allowed_user_ids: list[StrictInt] = Field(default_factory=list)
|
||||
message_overflow: Literal["trim", "split"] = "trim"
|
||||
voice_transcription: bool = False
|
||||
@@ -128,7 +138,7 @@ class ProjectSettings(BaseModel):
|
||||
worktrees_dir: NonEmptyStr = ".worktrees"
|
||||
default_engine: NonEmptyStr | None = None
|
||||
worktree_base: NonEmptyStr | None = None
|
||||
chat_id: StrictInt | None = None
|
||||
chat_id: ChatId | None = None
|
||||
|
||||
|
||||
class TakopiSettings(BaseSettings):
|
||||
|
||||
@@ -92,8 +92,8 @@ def test_require_telegram_rejects_empty_token(tmp_path) -> None:
|
||||
require_telegram(settings, config_path)
|
||||
|
||||
|
||||
def test_load_settings_rejects_string_chat_id(tmp_path) -> None:
|
||||
from takopi.config import ConfigError
|
||||
def test_load_settings_accepts_string_chat_id(tmp_path) -> None:
|
||||
from takopi.settings import require_telegram
|
||||
|
||||
config_path = tmp_path / "takopi.toml"
|
||||
config_path.write_text(
|
||||
@@ -102,8 +102,9 @@ def test_load_settings_rejects_string_chat_id(tmp_path) -> None:
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with pytest.raises(ConfigError, match="chat_id"):
|
||||
load_settings(config_path)
|
||||
settings, _ = load_settings(config_path)
|
||||
_, chat_id = require_telegram(settings, config_path)
|
||||
assert chat_id == 123
|
||||
|
||||
|
||||
def test_codex_extract_resume_finds_command() -> None:
|
||||
|
||||
@@ -130,6 +130,22 @@ def test_projects_chat_id_must_be_unique() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_projects_string_chat_id_is_coerced() -> None:
|
||||
config = {
|
||||
"transports": {"telegram": {"bot_token": "token", "chat_id": 123}},
|
||||
"projects": {"z80": {"path": "/tmp/repo", "chat_id": "-10"}},
|
||||
}
|
||||
settings = TakopiSettings.model_validate(config)
|
||||
projects = settings.to_projects_config(
|
||||
config_path=Path("takopi.toml"),
|
||||
engine_ids=["codex"],
|
||||
reserved=RESERVED_CHAT_COMMANDS,
|
||||
)
|
||||
|
||||
assert projects.projects["z80"].chat_id == -10
|
||||
assert projects.chat_map[-10] == "z80"
|
||||
|
||||
|
||||
def test_projects_relative_path_resolves(tmp_path: Path) -> None:
|
||||
config_path = tmp_path / "takopi.toml"
|
||||
settings = TakopiSettings.model_validate(
|
||||
|
||||
@@ -28,3 +28,13 @@ def test_settings_rejects_bool_chat_id(tmp_path: Path) -> None:
|
||||
|
||||
with pytest.raises(ConfigError, match="chat_id"):
|
||||
validate_settings_data(data, config_path=tmp_path / "takopi.toml")
|
||||
|
||||
|
||||
def test_settings_rejects_float_chat_id(tmp_path: Path) -> None:
|
||||
data = {
|
||||
"transport": "telegram",
|
||||
"transports": {"telegram": {"bot_token": "token", "chat_id": 123.0}},
|
||||
}
|
||||
|
||||
with pytest.raises(ConfigError, match="chat_id"):
|
||||
validate_settings_data(data, config_path=tmp_path / "takopi.toml")
|
||||
|
||||
Reference in New Issue
Block a user