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 collections.abc import Iterable
|
||||||
|
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
|
BeforeValidator,
|
||||||
BaseModel,
|
BaseModel,
|
||||||
ConfigDict,
|
ConfigDict,
|
||||||
Field,
|
Field,
|
||||||
@@ -53,6 +54,15 @@ def _normalize_project_path(value: str, *, config_path: Path) -> Path:
|
|||||||
return 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):
|
class TelegramTopicsSettings(BaseModel):
|
||||||
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
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)
|
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
||||||
|
|
||||||
bot_token: NonEmptyStr
|
bot_token: NonEmptyStr
|
||||||
chat_id: StrictInt
|
chat_id: ChatId
|
||||||
allowed_user_ids: list[StrictInt] = Field(default_factory=list)
|
allowed_user_ids: list[StrictInt] = Field(default_factory=list)
|
||||||
message_overflow: Literal["trim", "split"] = "trim"
|
message_overflow: Literal["trim", "split"] = "trim"
|
||||||
voice_transcription: bool = False
|
voice_transcription: bool = False
|
||||||
@@ -128,7 +138,7 @@ class ProjectSettings(BaseModel):
|
|||||||
worktrees_dir: NonEmptyStr = ".worktrees"
|
worktrees_dir: NonEmptyStr = ".worktrees"
|
||||||
default_engine: NonEmptyStr | None = None
|
default_engine: NonEmptyStr | None = None
|
||||||
worktree_base: NonEmptyStr | None = None
|
worktree_base: NonEmptyStr | None = None
|
||||||
chat_id: StrictInt | None = None
|
chat_id: ChatId | None = None
|
||||||
|
|
||||||
|
|
||||||
class TakopiSettings(BaseSettings):
|
class TakopiSettings(BaseSettings):
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ def test_require_telegram_rejects_empty_token(tmp_path) -> None:
|
|||||||
require_telegram(settings, config_path)
|
require_telegram(settings, config_path)
|
||||||
|
|
||||||
|
|
||||||
def test_load_settings_rejects_string_chat_id(tmp_path) -> None:
|
def test_load_settings_accepts_string_chat_id(tmp_path) -> None:
|
||||||
from takopi.config import ConfigError
|
from takopi.settings import require_telegram
|
||||||
|
|
||||||
config_path = tmp_path / "takopi.toml"
|
config_path = tmp_path / "takopi.toml"
|
||||||
config_path.write_text(
|
config_path.write_text(
|
||||||
@@ -102,8 +102,9 @@ def test_load_settings_rejects_string_chat_id(tmp_path) -> None:
|
|||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.raises(ConfigError, match="chat_id"):
|
settings, _ = load_settings(config_path)
|
||||||
load_settings(config_path)
|
_, chat_id = require_telegram(settings, config_path)
|
||||||
|
assert chat_id == 123
|
||||||
|
|
||||||
|
|
||||||
def test_codex_extract_resume_finds_command() -> None:
|
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:
|
def test_projects_relative_path_resolves(tmp_path: Path) -> None:
|
||||||
config_path = tmp_path / "takopi.toml"
|
config_path = tmp_path / "takopi.toml"
|
||||||
settings = TakopiSettings.model_validate(
|
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"):
|
with pytest.raises(ConfigError, match="chat_id"):
|
||||||
validate_settings_data(data, config_path=tmp_path / "takopi.toml")
|
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