Allow coercible chat_id values (#186)

Co-authored-by: banteg <4562643+banteg@users.noreply.github.com>
This commit is contained in:
ayvee
2026-03-02 15:27:48 +07:00
committed by GitHub
parent 058092c1a1
commit 10775bf9eb
4 changed files with 43 additions and 6 deletions
+12 -2
View File
@@ -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):
+5 -4
View File
@@ -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:
+16
View File
@@ -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(
+10
View File
@@ -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")