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
14 changes: 7 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ TELEGRAM_TOKEN=
GLOBAL_TELEGRAM_CHAT_ID=
FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON=

# Optional Google Voice/SMS channel for escalated strategy plugin alerts.
CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS=
CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL=
CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD=
CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST=
CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT=
CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY=
# Optional email channel for escalated strategy plugin alerts.
CRISIS_ALERT_EMAIL_RECIPIENTS=
CRISIS_ALERT_EMAIL_SENDER_EMAIL=
CRISIS_ALERT_EMAIL_SENDER_PASSWORD=
CRISIS_ALERT_EMAIL_SMTP_HOST=
CRISIS_ALERT_EMAIL_SMTP_PORT=
CRISIS_ALERT_EMAIL_SMTP_SECURITY=

# Runtime safety controls.
FIRSTRADE_COOKIE_DIR=.runtime/firstrade-cookies
Expand Down
38 changes: 24 additions & 14 deletions .github/workflows/sync-cloud-run-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ jobs:
FIRSTRADE_STATE_PREFIX: ${{ vars.FIRSTRADE_STATE_PREFIX }}
FIRSTRADE_STRATEGY_CONFIG_PATH: ${{ vars.FIRSTRADE_STRATEGY_CONFIG_PATH }}
FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON: ${{ vars.FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON }}
CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS }}
CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL }}
CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD_SECRET_NAME: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD_SECRET_NAME }}
CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST }}
CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT }}
CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY }}
CRISIS_ALERT_EMAIL_RECIPIENTS: ${{ vars.CRISIS_ALERT_EMAIL_RECIPIENTS }}
CRISIS_ALERT_EMAIL_SENDER_EMAIL: ${{ vars.CRISIS_ALERT_EMAIL_SENDER_EMAIL }}
CRISIS_ALERT_EMAIL_SENDER_PASSWORD_SECRET_NAME: ${{ vars.CRISIS_ALERT_EMAIL_SENDER_PASSWORD_SECRET_NAME }}
CRISIS_ALERT_EMAIL_SMTP_HOST: ${{ vars.CRISIS_ALERT_EMAIL_SMTP_HOST }}
CRISIS_ALERT_EMAIL_SMTP_PORT: ${{ vars.CRISIS_ALERT_EMAIL_SMTP_PORT }}
CRISIS_ALERT_EMAIL_SMTP_SECURITY: ${{ vars.CRISIS_ALERT_EMAIL_SMTP_SECURITY }}
FIRSTRADE_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS: ${{ vars.FIRSTRADE_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS }}
FIRSTRADE_TECH_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS: ${{ vars.FIRSTRADE_TECH_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS }}
INCOME_THRESHOLD_USD: ${{ vars.INCOME_THRESHOLD_USD }}
Expand All @@ -75,7 +75,7 @@ jobs:
GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}
NOTIFY_LANG: ${{ vars.NOTIFY_LANG }}
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD: ${{ secrets.CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD }}
CRISIS_ALERT_EMAIL_SENDER_PASSWORD: ${{ secrets.CRISIS_ALERT_EMAIL_SENDER_PASSWORD }}
FIRSTRADE_USERNAME: ${{ secrets.FIRSTRADE_USERNAME }}
FIRSTRADE_PASSWORD: ${{ secrets.FIRSTRADE_PASSWORD }}
FIRSTRADE_MFA_SECRET: ${{ secrets.FIRSTRADE_MFA_SECRET }}
Expand Down Expand Up @@ -377,6 +377,12 @@ jobs:
"CRISIS_ALERT_GOOGLE_VOICE_GATEWAY"
"CRISIS_ALERT_GOOGLE_VOICE_GMAIL_USER"
"CRISIS_ALERT_GOOGLE_VOICE_GMAIL_APP_PASSWORD"
"CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS"
"CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL"
"CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD"
"CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST"
"CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT"
"CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY"
"CRISIS_ALERT_SMTP_FROM"
"CRISIS_ALERT_SMTP_HOST"
"CRISIS_ALERT_SMTP_PORT"
Expand All @@ -385,7 +391,11 @@ jobs:
"CRISIS_ALERT_SMTP_STARTTLS"
"CRISIS_ALERT_SMTP_SSL"
)
remove_secret_vars=("CRISIS_ALERT_SMTP_PASSWORD" "CRISIS_ALERT_GOOGLE_VOICE_GMAIL_APP_PASSWORD")
remove_secret_vars=(
"CRISIS_ALERT_SMTP_PASSWORD"
"CRISIS_ALERT_GOOGLE_VOICE_GMAIL_APP_PASSWORD"
"CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD"
)

add_optional_env() {
local name="$1"
Expand Down Expand Up @@ -442,11 +452,11 @@ jobs:
add_optional_env FIRSTRADE_FEATURE_SNAPSHOT_MANIFEST_PATH
add_optional_env FIRSTRADE_STRATEGY_CONFIG_PATH
add_optional_env FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON
add_optional_env CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS
add_optional_env CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL
add_optional_env CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST
add_optional_env CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT
add_optional_env CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY
add_optional_env CRISIS_ALERT_EMAIL_RECIPIENTS
add_optional_env CRISIS_ALERT_EMAIL_SENDER_EMAIL
add_optional_env CRISIS_ALERT_EMAIL_SMTP_HOST
add_optional_env CRISIS_ALERT_EMAIL_SMTP_PORT
add_optional_env CRISIS_ALERT_EMAIL_SMTP_SECURITY
add_optional_env FIRSTRADE_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS
add_optional_env FIRSTRADE_TECH_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS
add_optional_env INCOME_THRESHOLD_USD
Expand All @@ -456,7 +466,7 @@ jobs:
add_optional_env NOTIFY_LANG

add_optional_secret TELEGRAM_TOKEN TELEGRAM_TOKEN_SECRET_NAME TELEGRAM_TOKEN
add_optional_secret CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD_SECRET_NAME CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD
add_optional_secret CRISIS_ALERT_EMAIL_SENDER_PASSWORD CRISIS_ALERT_EMAIL_SENDER_PASSWORD_SECRET_NAME CRISIS_ALERT_EMAIL_SENDER_PASSWORD
add_optional_secret FIRSTRADE_USERNAME FIRSTRADE_USERNAME_SECRET_NAME FIRSTRADE_USERNAME
add_optional_secret FIRSTRADE_PASSWORD FIRSTRADE_PASSWORD_SECRET_NAME FIRSTRADE_PASSWORD
add_optional_secret FIRSTRADE_MFA_SECRET FIRSTRADE_MFA_SECRET_SECRET_NAME FIRSTRADE_MFA_SECRET
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ commit credentials.
| `TELEGRAM_TOKEN` | Optional | Telegram bot token for strategy-cycle summaries |
| `GLOBAL_TELEGRAM_CHAT_ID` | Optional | Telegram chat ID for strategy-cycle summaries |
| `FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON` | Optional | JSON sidecar plugin mount config. Overrides global `STRATEGY_PLUGIN_MOUNTS_JSON` for this platform |
| `CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS` | Optional | Email-form recipients. Use a normal mailbox for email-only delivery, or a Google Voice mailbox/address to also trigger GV prompts |
| `CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL` | Optional | Sender email address used for Google Voice notification mail. Gmail is the default transport, but the sender naming is provider-neutral |
| `CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD` | Optional | Sender SMTP password or app password, preferably supplied from Secret Manager in Cloud Run |
| `CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST` | Optional | SMTP host override. Defaults to Gmail SMTP when unset |
| `CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT` | Optional | SMTP port override. Defaults to `465` when unset |
| `CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY` | Optional | SMTP security override: `ssl`, `starttls`, or `none`. Defaults to `ssl` when unset |
| `CRISIS_ALERT_EMAIL_RECIPIENTS` | Optional | Email-form recipients. Use a normal mailbox for email-only delivery, or a Google Voice-associated mailbox/address to also trigger Google Voice prompts |
| `CRISIS_ALERT_EMAIL_SENDER_EMAIL` | Optional | Sender email address used for crisis alert email. Gmail is the default transport, but the sender naming is provider-neutral |
| `CRISIS_ALERT_EMAIL_SENDER_PASSWORD` | Optional | Sender SMTP password or app password, preferably supplied from Secret Manager in Cloud Run |
| `CRISIS_ALERT_EMAIL_SMTP_HOST` | Optional | SMTP host override. Defaults to Gmail SMTP when unset |
| `CRISIS_ALERT_EMAIL_SMTP_PORT` | Optional | SMTP port override. Defaults to `465` when unset |
| `CRISIS_ALERT_EMAIL_SMTP_SECURITY` | Optional | SMTP security override: `ssl`, `starttls`, or `none`. Defaults to `ssl` when unset |
| `FIRSTRADE_COOKIE_DIR` | Optional | Cookie cache directory, default `.runtime/firstrade-cookies` |
| `FIRSTRADE_ENABLE_LIVE_TRADING` | Optional | Must be `true` before any live order can be submitted |
| `FIRSTRADE_RUN_SMOKE_ON_HTTP` | Optional | Must be `true` before `/smoke` performs a real login/quote |
Expand Down Expand Up @@ -177,9 +177,9 @@ full guarded strategy cycle:
- route generated orders through the local safety layer
- publish a compact Telegram summary when `TELEGRAM_TOKEN` and
`GLOBAL_TELEGRAM_CHAT_ID` are configured
- send independent Google Voice alerts for escalated strategy plugin signals when
`CRISIS_ALERT_*` is configured
- write Google Voice alert results into the response and suppress duplicate plugin
- send independent email alerts for escalated strategy plugin signals when
`CRISIS_ALERT_EMAIL_*` is configured
- write mail alert results into the response and suppress duplicate plugin
alert keys through `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI`, `EXECUTION_REPORT_GCS_URI`,
or the configured Firstrade state bucket

Expand Down Expand Up @@ -312,9 +312,9 @@ Firstrade 登录、账户/行情读取、下单转换、安全闸和部署 wirin
- dry-run / preview 下单验证
- `/run` 执行通用美股策略的 dry-run 调仓闭环
- 配置 `TELEGRAM_TOKEN` 和 `GLOBAL_TELEGRAM_CHAT_ID` 后发送运行摘要
- 读取通用策略插件信号,并在危机类插件触发时通过 `CRISIS_ALERT_*`
配置发送独立 Google Voice 告警
- 在响应中写入 Google Voice 告警结果,并通过 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI`、
- 读取通用策略插件信号,并在危机类插件触发时通过 `CRISIS_ALERT_EMAIL_*`
配置发送独立邮件告警
- 在响应中写入邮件告警结果,并通过 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI`、
`EXECUTION_REPORT_GCS_URI` 或已配置的 Firstrade state bucket 抑制重复插件告警 key
- 在你再次确认后,才允许极小金额实盘验证
- 通用 `us_equity` 策略 profile 的平台层接入
Expand Down
52 changes: 26 additions & 26 deletions application/rebalance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@
parse_strategy_plugin_mounts,
)
from quant_platform_kit.notifications.events import NotificationPublisher, RenderedNotification
from quant_platform_kit.notifications.strategy_plugin_google_voice import (
StrategyPluginGoogleVoiceAlertMarkerStore,
build_strategy_plugin_alert_context_label as build_google_voice_alert_context_label,
publish_strategy_plugin_google_voice_alerts,
from quant_platform_kit.notifications.strategy_plugin_email import (
StrategyPluginEmailAlertMarkerStore,
build_strategy_plugin_alert_context_label as build_email_alert_context_label,
publish_strategy_plugin_email_alerts,
)
from quant_platform_kit.strategy_contracts import build_strategy_evaluation_inputs
from runtime_config_support import PlatformRuntimeSettings, load_platform_runtime_settings
Expand Down Expand Up @@ -212,7 +212,7 @@ def attach_strategy_plugin_result(


def build_strategy_plugin_alert_context_label(settings: PlatformRuntimeSettings) -> str:
return build_google_voice_alert_context_label(
return build_email_alert_context_label(
platform_id="firstrade",
strategy_profile=settings.strategy_profile,
account_scope=settings.account_region or settings.account_prefix,
Expand All @@ -231,7 +231,7 @@ def build_strategy_plugin_alert_store(
state_bucket = env_reader("FIRSTRADE_GCS_STATE_BUCKET", None)
state_prefix = env_reader("FIRSTRADE_STATE_PREFIX", "firstrade-platform") or "firstrade-platform"
state_gcs_uri = f"gs://{state_bucket}/{state_prefix}" if state_bucket else None
return StrategyPluginGoogleVoiceAlertMarkerStore(
return StrategyPluginEmailAlertMarkerStore(
local_dir=env_reader("STRATEGY_PLUGIN_ALERT_STATE_DIR", None) or "/tmp/quant_strategy_plugin_alerts",
gcs_prefix_uri=explicit_gcs_uri or report_gcs_uri or state_gcs_uri,
gcp_project_id=settings.project_id,
Expand All @@ -246,9 +246,9 @@ def publish_strategy_plugin_alerts(
log_message: Callable[..., Any] = print,
env_reader: Callable[[str, str | None], str | None] = os.getenv,
):
return publish_strategy_plugin_google_voice_alerts(
return publish_strategy_plugin_email_alerts(
signals,
google_voice_settings=settings,
email_settings=settings,
translator=translator,
strategy_label=settings.strategy_profile,
context_label=build_strategy_plugin_alert_context_label(settings),
Expand Down Expand Up @@ -380,29 +380,29 @@ def run_strategy_cycle(
}
],
"action_done": False,
"strategy_plugin_alert_google_voice_attempted_count": 0,
"strategy_plugin_alert_google_voice_sent_count": 0,
"strategy_plugin_alert_google_voice_skipped_count": 0,
"strategy_plugin_alert_google_voice_failed_count": 0,
"strategy_plugin_alert_google_voice_deliveries": [],
"strategy_plugin_alert_email_attempted_count": 0,
"strategy_plugin_alert_email_sent_count": 0,
"strategy_plugin_alert_email_skipped_count": 0,
"strategy_plugin_alert_email_failed_count": 0,
"strategy_plugin_alert_email_deliveries": [],
}
return attach_strategy_plugin_result(
result,
signals=strategy_plugin_signals,
error=strategy_plugin_error,
translator=translator,
)
strategy_plugin_alert_google_voice_result = None
strategy_plugin_alert_google_voice_error = None
strategy_plugin_alert_email_result = None
strategy_plugin_alert_email_error = None
try:
strategy_plugin_alert_google_voice_result = publish_strategy_plugin_alerts(
strategy_plugin_alert_email_result = publish_strategy_plugin_alerts(
strategy_plugin_signals,
settings=settings,
translator=translator,
env_reader=env_reader,
)
except Exception as exc:
strategy_plugin_alert_google_voice_error = f"{type(exc).__name__}: {exc}"
strategy_plugin_alert_email_error = f"{type(exc).__name__}: {exc}"
strategy_run_persisted = False
strategy_run_persistence_error = None
if persist_strategy_runs:
Expand Down Expand Up @@ -479,20 +479,20 @@ def run_strategy_cycle(
result["funding_blocked"] = True
if strategy_run_persistence_error:
result["strategy_run_persistence_error"] = strategy_run_persistence_error
if strategy_plugin_alert_google_voice_result is not None:
result.update(strategy_plugin_alert_google_voice_result.to_report_fields())
if strategy_plugin_alert_email_result is not None:
result.update(strategy_plugin_alert_email_result.to_report_fields())
else:
result.update(
{
"strategy_plugin_alert_google_voice_attempted_count": 0,
"strategy_plugin_alert_google_voice_sent_count": 0,
"strategy_plugin_alert_google_voice_skipped_count": 0,
"strategy_plugin_alert_google_voice_failed_count": 0,
"strategy_plugin_alert_google_voice_deliveries": [],
"strategy_plugin_alert_email_attempted_count": 0,
"strategy_plugin_alert_email_sent_count": 0,
"strategy_plugin_alert_email_skipped_count": 0,
"strategy_plugin_alert_email_failed_count": 0,
"strategy_plugin_alert_email_deliveries": [],
}
)
if strategy_plugin_alert_google_voice_error:
result["strategy_plugin_alert_google_voice_error"] = strategy_plugin_alert_google_voice_error
if strategy_plugin_alert_email_error:
result["strategy_plugin_alert_email_error"] = strategy_plugin_alert_email_error
attach_strategy_plugin_result(
result,
signals=strategy_plugin_signals,
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ authors = [
]
dependencies = [
"firstrade==0.0.38",
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@43b2ecb9dc7b1a70e52fe038ce321d79f5f7987a",
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@a3f1899079d8d6c11601dddc40cb6f3020b6fc82",
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@3c8f52b16a8ebf98b9fdfa4af5e96be43e6144fe",
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@b84bebb784248bd2bb3d2c589db9ea0ab35ee95d",
"google-cloud-storage",
"requests",
]
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
flask
gunicorn
firstrade==0.0.38
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@43b2ecb9dc7b1a70e52fe038ce321d79f5f7987a
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@a3f1899079d8d6c11601dddc40cb6f3020b6fc82
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@3c8f52b16a8ebf98b9fdfa4af5e96be43e6144fe
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@b84bebb784248bd2bb3d2c589db9ea0ab35ee95d
google-cloud-storage
requests
pytest
28 changes: 14 additions & 14 deletions runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ class PlatformRuntimeSettings:
strategy_config_path: str | None = None
strategy_config_source: str | None = None
strategy_plugin_mounts_json: str | None = None
crisis_alert_google_voice_recipients: tuple[str, ...] = ()
crisis_alert_google_voice_sender_email: str | None = None
crisis_alert_google_voice_sender_password: str | None = None
crisis_alert_google_voice_smtp_host: str | None = None
crisis_alert_google_voice_smtp_port: str | None = None
crisis_alert_google_voice_smtp_security: str | None = None
crisis_alert_email_recipients: tuple[str, ...] = ()
crisis_alert_email_sender_email: str | None = None
crisis_alert_email_sender_password: str | None = None
crisis_alert_email_smtp_host: str | None = None
crisis_alert_email_smtp_port: str | None = None
crisis_alert_email_smtp_security: str | None = None
runtime_target: RuntimeTarget | None = None


Expand Down Expand Up @@ -151,15 +151,15 @@ def load_platform_runtime_settings(
os.getenv("FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON")
or os.getenv("STRATEGY_PLUGIN_MOUNTS_JSON")
),
crisis_alert_google_voice_recipients=_split_env_list(os.getenv("CRISIS_ALERT_GOOGLE_VOICE_RECIPIENTS")),
crisis_alert_google_voice_sender_email=_first_non_empty(os.getenv("CRISIS_ALERT_GOOGLE_VOICE_SENDER_EMAIL")),
crisis_alert_google_voice_sender_password=_first_non_empty(
os.getenv("CRISIS_ALERT_GOOGLE_VOICE_SENDER_PASSWORD")
crisis_alert_email_recipients=_split_env_list(os.getenv("CRISIS_ALERT_EMAIL_RECIPIENTS")),
crisis_alert_email_sender_email=_first_non_empty(os.getenv("CRISIS_ALERT_EMAIL_SENDER_EMAIL")),
crisis_alert_email_sender_password=_first_non_empty(
os.getenv("CRISIS_ALERT_EMAIL_SENDER_PASSWORD")
),
crisis_alert_google_voice_smtp_host=_first_non_empty(os.getenv("CRISIS_ALERT_GOOGLE_VOICE_SMTP_HOST")),
crisis_alert_google_voice_smtp_port=_first_non_empty(os.getenv("CRISIS_ALERT_GOOGLE_VOICE_SMTP_PORT")),
crisis_alert_google_voice_smtp_security=_first_non_empty(
os.getenv("CRISIS_ALERT_GOOGLE_VOICE_SMTP_SECURITY")
crisis_alert_email_smtp_host=_first_non_empty(os.getenv("CRISIS_ALERT_EMAIL_SMTP_HOST")),
crisis_alert_email_smtp_port=_first_non_empty(os.getenv("CRISIS_ALERT_EMAIL_SMTP_PORT")),
crisis_alert_email_smtp_security=_first_non_empty(
os.getenv("CRISIS_ALERT_EMAIL_SMTP_SECURITY")
),
runtime_target=runtime_target,
)
Expand Down
Loading