fix: accept CSV telegram ID config (#27)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Daniel 2026-07-27 09:21:52 +10:00 committed by GitHub
parent 20928f4ab6
commit cf65198825
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 9 deletions

View File

@ -263,7 +263,7 @@ Note: Pre-commit will run these checks automatically on commit, so you usually d
**Configuration issues:**
1. Check `.env` file syntax (should be `KEY=value`)
2. For lists, use JSON format: `STEWARD__TELEGRAM__ALLOWED_USER_IDS='[123, 456]'`
2. For lists, prefer CSV format: `STEWARD__TELEGRAM__ALLOWED_USER_IDS=123,456` (JSON lists are also supported)
3. New format variables use `STEWARD__SECTION__KEY` pattern
4. Check `CONFIGURATION.md` for examples

View File

@ -141,8 +141,8 @@ telegram:
**Environment Variables:**
- `STEWARD__TELEGRAM__BOT_TOKEN` - Telegram bot token (REQUIRED)
- `STEWARD__TELEGRAM__ALLOWED_USER_IDS` - JSON list: `[123,456,789]`
- `STEWARD__TELEGRAM__GROUP_IDS` - JSON list: `[-1005308306472]`
- `STEWARD__TELEGRAM__ALLOWED_USER_IDS` - Comma-separated or JSON list: `123,456,789`
- `STEWARD__TELEGRAM__GROUP_IDS` - Comma-separated or JSON list: `-1005308306472`
### OpenAI Section
@ -211,10 +211,10 @@ STEWARD__OPENAI__MODEL="gpt-4-turbo"
STEWARD__ANALYSIS__CRON_HOUR="9"
```
For list values, use JSON format:
For list values, use comma-separated integers. JSON lists are also accepted for compatibility:
```bash
STEWARD__TELEGRAM__ALLOWED_USER_IDS='[123456789, 987654321]'
STEWARD__TELEGRAM__GROUP_IDS='[-1005308306472]'
STEWARD__TELEGRAM__ALLOWED_USER_IDS=123456789,987654321
STEWARD__TELEGRAM__GROUP_IDS=-1005308306472
```
## Best Practices

View File

@ -8,16 +8,50 @@ Supports configuration via:
Environment variables take precedence over config file values.
"""
import json
import logging
import os
from pathlib import Path
from typing import Any
from omegaconf import OmegaConf
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, field_validator
logger = logging.getLogger(__name__)
def _parse_int_list(value: Any) -> Any:
if isinstance(value, int):
return [value]
if not isinstance(value, str):
return value
text = value.strip()
if not text:
return []
if len(text) >= 2 and text[0] == text[-1] and text[0] in {"'", '"'}:
text = text[1:-1].strip()
if not text:
return []
if text.startswith("["):
try:
parsed = json.loads(text)
except json.JSONDecodeError:
pass
else:
if isinstance(parsed, list):
return parsed
try:
return [int(item.strip().strip("'\"")) for item in text.split(",") if item.strip()]
except ValueError as exc:
raise ValueError(
"expected a comma-separated list of integers or a JSON integer list"
) from exc
class TelegramConfig(BaseModel):
"""Telegram bot configuration."""
@ -25,6 +59,11 @@ class TelegramConfig(BaseModel):
allowed_user_ids: list[int] = Field(default_factory=list, description="Allowed user IDs")
group_ids: list[int] = Field(default_factory=list, description="Allowed group/channel IDs")
@field_validator("allowed_user_ids", "group_ids", mode="before")
@classmethod
def parse_int_list(cls, value: Any) -> Any:
return _parse_int_list(value)
class OpenAIConfig(BaseModel):
"""OpenAI/LLM configuration."""
@ -186,8 +225,6 @@ def _load_config_from_env() -> dict:
# Try to parse value as JSON first (for lists, etc.)
if isinstance(cfg[section], dict):
try:
import json
cfg[section][setting_key] = json.loads(value)
except (json.JSONDecodeError, ValueError):
cfg[section][setting_key] = value

63
tests/test_config.py Normal file
View File

@ -0,0 +1,63 @@
"""Tests for Steward configuration loading."""
import pytest
from steward import config as config_module
from steward.config import TelegramConfig, reload_settings
@pytest.fixture(autouse=True)
def reset_settings_singleton():
config_module._settings_instance = None
yield
config_module._settings_instance = None
def test_telegram_config_accepts_csv_id_lists():
config = TelegramConfig(
allowed_user_ids="8861600138, 123456789",
group_ids="-5308306472,-1005308306472",
)
assert config.allowed_user_ids == [8861600138, 123456789]
assert config.group_ids == [-5308306472, -1005308306472]
def test_telegram_config_preserves_list_id_values():
config = TelegramConfig(
allowed_user_ids=[8861600138, 123456789],
group_ids=[-5308306472, -1005308306472],
)
assert config.allowed_user_ids == [8861600138, 123456789]
assert config.group_ids == [-5308306472, -1005308306472]
def test_settings_accepts_csv_id_lists_from_env(monkeypatch):
monkeypatch.setenv("STEWARD__TELEGRAM__ALLOWED_USER_IDS", "8861600138,123456789")
monkeypatch.setenv("STEWARD__TELEGRAM__GROUP_IDS", "-5308306472,-1005308306472")
settings = reload_settings()
assert settings.telegram_allowed_user_ids == [8861600138, 123456789]
assert settings.telegram_group_ids == [-5308306472, -1005308306472]
def test_settings_accepts_single_id_values_from_env(monkeypatch):
monkeypatch.setenv("STEWARD__TELEGRAM__ALLOWED_USER_IDS", "8861600138")
monkeypatch.setenv("STEWARD__TELEGRAM__GROUP_IDS", "-5308306472")
settings = reload_settings()
assert settings.telegram_allowed_user_ids == [8861600138]
assert settings.telegram_group_ids == [-5308306472]
def test_settings_accepts_quoted_json_id_lists_from_env(monkeypatch):
monkeypatch.setenv("STEWARD__TELEGRAM__ALLOWED_USER_IDS", "'[8861600138]'")
monkeypatch.setenv("STEWARD__TELEGRAM__GROUP_IDS", "'[-5308306472]'")
settings = reload_settings()
assert settings.telegram_allowed_user_ids == [8861600138]
assert settings.telegram_group_ids == [-5308306472]