Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ docker run --rm \
--publish 127.0.0.1:9876:9876 \
--volume iris-data:/data \
--env IRIS_ENABLED_PROVIDERS=telegram \
--env IRIS_TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN}" \
--env IRIS_TELEGRAM_BOT_TOKEN="${IRIS_TELEGRAM_BOT_TOKEN}" \
ghcr.io/techgodhq/iris:latest
```

Expand All @@ -61,7 +61,7 @@ variables. To keep the full-fidelity TOML path, mount a file and set

For a reference Iris + Rite deployment, use
[`deploy/docker-compose.yml`](deploy/docker-compose.yml). Set
`TELEGRAM_BOT_TOKEN` and `RITE_GITHUB_WEBHOOK_SECRET` in its environment before
`IRIS_TELEGRAM_BOT_TOKEN` and `RITE_GITHUB_WEBHOOK_SECRET` in its environment before
running `docker compose -f deploy/docker-compose.yml up -d`.

## Configuration
Expand All @@ -77,7 +77,10 @@ For file-free configuration, set `IRIS_ENABLED_PROVIDERS` and matching
`IRIS_EMAIL_IMAP_PORT`, `IRIS_EMAIL_SMTP_HOST`, `IRIS_EMAIL_SMTP_PORT`,
`IRIS_EMAIL_USERNAME`, `IRIS_EMAIL_PASSWORD`, and optional `IRIS_EMAIL_MAILBOX`,
`IRIS_EMAIL_FROM`, `IRIS_EMAIL_PAGE_SIZE`, and `IRIS_EMAIL_MAX_MESSAGES`.
Enabled providers validate required credentials at startup.
Enabled providers validate required credentials at startup. For upgrade compatibility,
Telegram also accepts the legacy `TELEGRAM_BOT_TOKEN` only when
`IRIS_TELEGRAM_BOT_TOKEN` is absent; migrate to the Iris-prefixed name because the
canonical variable always takes precedence and the legacy fallback is deprecated.

```toml
[providers.mock]
Expand All @@ -95,7 +98,9 @@ enabled = true

[providers.telegram.credentials]
# Telegram Bot API token. `token` is also accepted as an alias.
bot_token = { env = "TELEGRAM_BOT_TOKEN" }
# Prefer the canonical Iris-prefixed variable. Legacy `TELEGRAM_BOT_TOKEN`
# remains a deprecated fallback only when this variable is absent.
bot_token = { env = "IRIS_TELEGRAM_BOT_TOKEN" }
```

The Telegram provider uses the Bot API. It can list and normalize messages that
Expand Down
108 changes: 108 additions & 0 deletions crates/iris-providers/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ fn resolve_provider(
})
}

const LEGACY_TELEGRAM_BOT_TOKEN_ENV: &str = "TELEGRAM_BOT_TOKEN";
const CANONICAL_TELEGRAM_BOT_TOKEN_ENV: &str = "IRIS_TELEGRAM_BOT_TOKEN";

fn apply_provider_env(config: &mut ProviderConfig, provider_type: &str, instance: Option<&str>) {
let fields: &[(&str, &str)] = provider_env_fields()
.iter()
Expand All @@ -357,6 +360,26 @@ fn apply_provider_env(config: &mut ProviderConfig, provider_type: &str, instance
.insert((*field).to_owned(), SecretValue::FromEnv { env: variable });
}
}

// v0.1.0 documented this unprefixed variable. Retain it only for the
// default Telegram instance so existing deployments can upgrade in place.
if provider_type == "telegram"
&& instance.is_none()
&& env::var_os(CANONICAL_TELEGRAM_BOT_TOKEN_ENV).is_none()
&& env::var_os(LEGACY_TELEGRAM_BOT_TOKEN_ENV).is_some()
{
tracing::warn!(
legacy_env = LEGACY_TELEGRAM_BOT_TOKEN_ENV,
canonical_env = CANONICAL_TELEGRAM_BOT_TOKEN_ENV,
"deprecated Telegram credential environment variable selected; the canonical variable takes precedence when both are set"
);
config.credentials.insert(
"bot_token".to_owned(),
SecretValue::FromEnv {
env: LEGACY_TELEGRAM_BOT_TOKEN_ENV.to_owned(),
},
);
}
}

impl SecretValue {
Expand Down Expand Up @@ -709,6 +732,91 @@ bot_token = "123:abc"
assert_eq!(providers[0].id(), "telegram");
}

#[test]
fn legacy_telegram_token_env_builds_default_provider() {
let _guard = env_lock().lock().expect("environment lock");
temp_env::with_vars(
[
(ENABLED_PROVIDERS_ENV, Some("telegram")),
(LEGACY_TELEGRAM_BOT_TOKEN_ENV, Some("legacy-token")),
(CANONICAL_TELEGRAM_BOT_TOKEN_ENV, None),
],
|| {
let mut config = IrisConfig::default();
config.apply_env_overrides().expect("env applies");
let resolved = config
.resolved_enabled_providers()
.expect("provider resolves");
assert_eq!(resolved[0].credentials["bot_token"], "legacy-token");
let providers = providers_from_config(&config, &test_store(), &test_audit())
.expect("legacy token constructs Telegram provider");
assert_eq!(providers[0].id(), "telegram");
},
);
}

#[test]
fn canonical_telegram_token_env_builds_default_provider() {
let _guard = env_lock().lock().expect("environment lock");
temp_env::with_vars(
[
(ENABLED_PROVIDERS_ENV, Some("telegram")),
(LEGACY_TELEGRAM_BOT_TOKEN_ENV, None),
(CANONICAL_TELEGRAM_BOT_TOKEN_ENV, Some("canonical-token")),
],
|| {
let mut config = IrisConfig::default();
config.apply_env_overrides().expect("env applies");
let resolved = config
.resolved_enabled_providers()
.expect("provider resolves");
assert_eq!(resolved[0].credentials["bot_token"], "canonical-token");
},
);
}

#[test]
fn canonical_telegram_token_env_wins_over_legacy() {
let _guard = env_lock().lock().expect("environment lock");
temp_env::with_vars(
[
(ENABLED_PROVIDERS_ENV, Some("telegram")),
(LEGACY_TELEGRAM_BOT_TOKEN_ENV, Some("legacy-token")),
(CANONICAL_TELEGRAM_BOT_TOKEN_ENV, Some("canonical-token")),
],
|| {
let mut config = IrisConfig::default();
config.apply_env_overrides().expect("env applies");
let resolved = config
.resolved_enabled_providers()
.expect("provider resolves");
assert_eq!(resolved[0].credentials["bot_token"], "canonical-token");
},
);
}

#[test]
fn legacy_telegram_token_env_does_not_overlay_unrelated_provider() {
let _guard = env_lock().lock().expect("environment lock");
temp_env::with_vars(
[
(ENABLED_PROVIDERS_ENV, Some("mock")),
(LEGACY_TELEGRAM_BOT_TOKEN_ENV, Some("legacy-token")),
(CANONICAL_TELEGRAM_BOT_TOKEN_ENV, None),
],
|| {
let mut config = IrisConfig::default();
config.apply_env_overrides().expect("env applies");
let resolved = config
.resolved_enabled_providers()
.expect("provider resolves");
assert_eq!(resolved.len(), 1);
assert_eq!(resolved[0].id, "mock");
assert!(resolved[0].credentials.is_empty());
},
);
}

#[test]
fn builds_email_provider_from_credentials() {
let config = IrisConfig::from_toml(
Expand Down
3 changes: 2 additions & 1 deletion deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ services:
restart: unless-stopped
environment:
IRIS_ENABLED_PROVIDERS: ${IRIS_ENABLED_PROVIDERS:-telegram}
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:?set TELEGRAM_BOT_TOKEN}
# `TELEGRAM_BOT_TOKEN` is a deprecated fallback for upgrades only.
IRIS_TELEGRAM_BOT_TOKEN: ${IRIS_TELEGRAM_BOT_TOKEN:?set IRIS_TELEGRAM_BOT_TOKEN}
volumes:
- iris-data:/data
ports:
Expand Down
Loading