From 15d4f7478d02e531fcd68431a0730b6a99c9444d Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Mon, 25 May 2026 02:16:37 +0800 Subject: [PATCH 1/3] Rename crisis alert channel to Google Voice --- .github/workflows/sync-cloud-run-env.yml | 14 ++++++++++++ README.md | 28 +++++++++++++---------- main.py | 22 +++++++++--------- requirements.txt | 2 +- runtime_config_support.py | 16 ++++++++++--- tests/test_request_handling.py | 12 +++++----- tests/test_runtime_config_support.py | 8 ++++++- tests/test_sync_cloud_run_env_workflow.sh | 6 +++++ 8 files changed, 74 insertions(+), 34 deletions(-) diff --git a/.github/workflows/sync-cloud-run-env.yml b/.github/workflows/sync-cloud-run-env.yml index e83f63e..f5be490 100644 --- a/.github/workflows/sync-cloud-run-env.yml +++ b/.github/workflows/sync-cloud-run-env.yml @@ -51,7 +51,9 @@ jobs: LONGBRIDGE_MIN_RESERVED_CASH_USD: ${{ vars.LONGBRIDGE_MIN_RESERVED_CASH_USD }} LONGBRIDGE_RESERVED_CASH_RATIO: ${{ vars.LONGBRIDGE_RESERVED_CASH_RATIO }} LONGBRIDGE_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD: ${{ vars.LONGBRIDGE_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD }} + CRISIS_ALERT_GOOGLE_VOICE_TO: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_TO }} CRISIS_ALERT_EMAIL_TO: ${{ vars.CRISIS_ALERT_EMAIL_TO }} + CRISIS_ALERT_SMTP_FROM: ${{ vars.CRISIS_ALERT_SMTP_FROM }} CRISIS_ALERT_EMAIL_FROM: ${{ vars.CRISIS_ALERT_EMAIL_FROM }} CRISIS_ALERT_SMTP_HOST: ${{ vars.CRISIS_ALERT_SMTP_HOST }} CRISIS_ALERT_SMTP_PORT: ${{ vars.CRISIS_ALERT_SMTP_PORT }} @@ -377,12 +379,24 @@ jobs: remove_env_vars+=("LONGBRIDGE_RESERVED_CASH_RATIO") fi + if [ -n "${CRISIS_ALERT_GOOGLE_VOICE_TO:-}" ]; then + env_pairs+=("CRISIS_ALERT_GOOGLE_VOICE_TO=${CRISIS_ALERT_GOOGLE_VOICE_TO}") + else + remove_env_vars+=("CRISIS_ALERT_GOOGLE_VOICE_TO") + fi + if [ -n "${CRISIS_ALERT_EMAIL_TO:-}" ]; then env_pairs+=("CRISIS_ALERT_EMAIL_TO=${CRISIS_ALERT_EMAIL_TO}") else remove_env_vars+=("CRISIS_ALERT_EMAIL_TO") fi + if [ -n "${CRISIS_ALERT_SMTP_FROM:-}" ]; then + env_pairs+=("CRISIS_ALERT_SMTP_FROM=${CRISIS_ALERT_SMTP_FROM}") + else + remove_env_vars+=("CRISIS_ALERT_SMTP_FROM") + fi + if [ -n "${CRISIS_ALERT_EMAIL_FROM:-}" ]; then env_pairs+=("CRISIS_ALERT_EMAIL_FROM=${CRISIS_ALERT_EMAIL_FROM}") else diff --git a/README.md b/README.md index 4bd0484..23009c2 100644 --- a/README.md +++ b/README.md @@ -70,9 +70,11 @@ Telegram notifications include structured execution and heartbeat messages, with | `LONGBRIDGE_DRY_RUN_ONLY` | No | Set to `true` to keep the selected deployment in dry-run mode. | | `LONGBRIDGE_DEBUG_POSITION_SNAPSHOT` | No | Set to `true` to log raw LongBridge position quantity and available quantity for troubleshooting. | | `LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON` | No | Optional LongBridge-side strategy plugin mount JSON. The plugin artifact controls mode; platform config must not set `mode`. | -| `CRISIS_ALERT_EMAIL_TO` | No | Comma/semicolon/newline-separated recipients for escalated crisis-plugin email alerts. Email is skipped when unset. | -| `CRISIS_ALERT_EMAIL_FROM` | No | Sender address for crisis-plugin email alerts. | -| `CRISIS_ALERT_SMTP_HOST` | No | SMTP host for crisis-plugin email alerts. | +| `CRISIS_ALERT_GOOGLE_VOICE_TO` | No | Comma/semicolon/newline-separated Google Voice SMS gateway recipients, usually ending in `@txt.voice.google.com`. | +| `CRISIS_ALERT_EMAIL_TO` | No | Optional ordinary email recipients that receive the same escalated alert; also accepted as a legacy recipient list. | +| `CRISIS_ALERT_SMTP_FROM` | No | SMTP sender address for Google Voice alerts. Falls back to `CRISIS_ALERT_EMAIL_FROM`. | +| `CRISIS_ALERT_EMAIL_FROM` | No | Legacy SMTP sender alias; prefer `CRISIS_ALERT_SMTP_FROM`. | +| `CRISIS_ALERT_SMTP_HOST` | No | SMTP host for Google Voice alerts. | | `CRISIS_ALERT_SMTP_PORT` | No | SMTP port; defaults to `587`. | | `CRISIS_ALERT_SMTP_USERNAME` | No | Optional SMTP username. | | `CRISIS_ALERT_SMTP_PASSWORD` | No | Optional SMTP password. For Cloud Run, prefer `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME` in env sync. | @@ -88,8 +90,8 @@ Telegram notifications include structured execution and heartbeat messages, with Strategy allocation can still target fractional dollar values and fractional position weights. The LongBridge execution layer now keeps a whole-share-only rule for every broker order: sell sizing floors to whole shares, buy sizing floors to whole shares, and fractional orders are skipped rather than downgraded. When a target value is zero, sell sizing uses the sellable position quantity instead of re-deriving shares from current price, so liquidation targets do not leave a residual share because of quote drift. -When `LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON` includes the `crisis_response_shadow` plugin, the normal Telegram cycle message still includes the compact plugin line. If the plugin signal escalates beyond `no_action` (for example `canonical_route=true_crisis`, `suggested_action=defend`/`blocked`, or `would_trade_if_enabled=true`), the service also sends an independent crisis email when the `CRISIS_ALERT_*` SMTP settings are complete. -Email alert results are written into the runtime report. Duplicate suppression uses stable plugin alert keys and stores markers under `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` when set, otherwise `EXECUTION_REPORT_GCS_URI`, with a local `/tmp` marker fallback. +When `LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON` includes the `crisis_response_shadow` plugin, the normal Telegram cycle message still includes the compact plugin line. If the plugin signal escalates beyond `no_action` (for example `canonical_route=true_crisis`, `suggested_action=defend`/`blocked`, or `would_trade_if_enabled=true`), the service also sends an independent crisis Google Voice notification when the `CRISIS_ALERT_*` SMTP settings are complete. +Google Voice alert results are written into the runtime report. Duplicate suppression uses stable plugin alert keys and stores markers under `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` when set, otherwise `EXECUTION_REPORT_GCS_URI`, with a local `/tmp` marker fallback. Secret Manager must contain the secret named by `LONGPORT_SECRET_NAME` (default: `longport_token_paper`), where the **latest version = active access token**. The app refreshes it when expiry is within 30 days. @@ -129,7 +131,7 @@ Recommended setup: - `TELEGRAM_TOKEN_SECRET_NAME` (recommended: `longbridge-telegram-token`) - `NOTIFY_LANG` - `GLOBAL_TELEGRAM_CHAT_ID` - - Optional crisis email alerts: `CRISIS_ALERT_EMAIL_TO`, `CRISIS_ALERT_EMAIL_FROM`, `CRISIS_ALERT_SMTP_HOST`, `CRISIS_ALERT_SMTP_PORT`, `CRISIS_ALERT_SMTP_USERNAME`, `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`, `CRISIS_ALERT_SMTP_STARTTLS`, `CRISIS_ALERT_SMTP_SSL` + - Optional crisis Google Voice alerts: `CRISIS_ALERT_GOOGLE_VOICE_TO`, `CRISIS_ALERT_EMAIL_TO`, `CRISIS_ALERT_SMTP_FROM`, `CRISIS_ALERT_EMAIL_FROM`, `CRISIS_ALERT_SMTP_HOST`, `CRISIS_ALERT_SMTP_PORT`, `CRISIS_ALERT_SMTP_USERNAME`, `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`, `CRISIS_ALERT_SMTP_STARTTLS`, `CRISIS_ALERT_SMTP_SSL` - **Repository Secrets (shared):** - Optional fallback only: `TELEGRAM_TOKEN` - Optional fallback only: `CRISIS_ALERT_SMTP_PASSWORD` @@ -243,9 +245,11 @@ Telegram 通知包含结构化的调仓和心跳消息,支持中英文切换 | `LONGBRIDGE_DRY_RUN_ONLY` | 否 | 设为 `true` 时,该部署保持 dry-run。 | | `LONGBRIDGE_DEBUG_POSITION_SNAPSHOT` | 否 | 设为 `true` 时输出 LongBridge 原始持仓数量和可卖数量,便于排查。 | | `LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON` | 否 | 可选的 LongBridge 侧策略插件挂载 JSON。插件 artifact 自带模式;平台配置不要设置 `mode`。 | -| `CRISIS_ALERT_EMAIL_TO` | 否 | 危机插件升级邮件收件人,支持逗号、分号或换行分隔。不配置则跳过邮件。 | -| `CRISIS_ALERT_EMAIL_FROM` | 否 | 危机插件升级邮件发件人。 | -| `CRISIS_ALERT_SMTP_HOST` | 否 | 危机插件升级邮件 SMTP host。 | +| `CRISIS_ALERT_GOOGLE_VOICE_TO` | 否 | Google Voice 短信网关收件人,通常以 `@txt.voice.google.com` 结尾,支持逗号、分号或换行分隔。 | +| `CRISIS_ALERT_EMAIL_TO` | 否 | 可选普通邮件收件人,会收到同一份升级告警;也作为旧版收件人配置兼容。 | +| `CRISIS_ALERT_SMTP_FROM` | 否 | Google Voice 告警的 SMTP 发件人;未设置时回退到 `CRISIS_ALERT_EMAIL_FROM`。 | +| `CRISIS_ALERT_EMAIL_FROM` | 否 | 旧版 SMTP 发件人别名;优先使用 `CRISIS_ALERT_SMTP_FROM`。 | +| `CRISIS_ALERT_SMTP_HOST` | 否 | Google Voice 告警 SMTP host。 | | `CRISIS_ALERT_SMTP_PORT` | 否 | SMTP 端口,默认 `587`。 | | `CRISIS_ALERT_SMTP_USERNAME` | 否 | 可选 SMTP 用户名。 | | `CRISIS_ALERT_SMTP_PASSWORD` | 否 | 可选 SMTP 密码。Cloud Run env sync 建议配置 `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`。 | @@ -261,8 +265,8 @@ Telegram 通知包含结构化的调仓和心跳消息,支持中英文切换 策略分配层仍然可以按目标金额和目标比例计算出小数仓位;LongBridge 执行层只提交整数股订单,因为实测账户的 OpenAPI `submit_order` 会拒绝碎股委托数量。目标市值为 0 时,卖出数量直接按可卖整数股持仓计算,不再用当前报价反推股数,避免因报价漂移留下 1 股残仓。 -如果 `LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON` 挂载了 `crisis_response_shadow` 插件,常规策略周期 Telegram 仍会包含插件摘要行。当插件信号升级到非 `no_action`(例如 `canonical_route=true_crisis`、`suggested_action=defend`/`blocked`,或 `would_trade_if_enabled=true`)时,只要 `CRISIS_ALERT_*` SMTP 配置完整,服务还会额外发一封独立危机邮件。 -邮件告警结果会写入 runtime report。重复发送抑制使用稳定的插件告警 key;如配置了 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` 则写入该前缀,否则复用 `EXECUTION_REPORT_GCS_URI`,并有本地 `/tmp` marker fallback。 +如果 `LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON` 挂载了 `crisis_response_shadow` 插件,常规策略周期 Telegram 仍会包含插件摘要行。当插件信号升级到非 `no_action`(例如 `canonical_route=true_crisis`、`suggested_action=defend`/`blocked`,或 `would_trade_if_enabled=true`)时,只要 `CRISIS_ALERT_*` SMTP 配置完整,服务还会额外发一封独立 Google Voice 危机通知。 +Google Voice 告警结果会写入 runtime report。重复发送抑制使用稳定的插件告警 key;如配置了 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` 则写入该前缀,否则复用 `EXECUTION_REPORT_GCS_URI`,并有本地 `/tmp` marker fallback。 Secret Manager 中需存在 `LONGPORT_SECRET_NAME` 指定的密钥(默认: `longport_token_paper`),**最新版本 = 当前有效的 access token**。Token 到期前 30 天会自动刷新。 @@ -302,7 +306,7 @@ Secret Manager 中需存在 `LONGPORT_SECRET_NAME` 指定的密钥(默认: `lo - `TELEGRAM_TOKEN_SECRET_NAME`(建议:`longbridge-telegram-token`) - `NOTIFY_LANG` - `GLOBAL_TELEGRAM_CHAT_ID` - - 可选危机插件邮件告警:`CRISIS_ALERT_EMAIL_TO`、`CRISIS_ALERT_EMAIL_FROM`、`CRISIS_ALERT_SMTP_HOST`、`CRISIS_ALERT_SMTP_PORT`、`CRISIS_ALERT_SMTP_USERNAME`、`CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`、`CRISIS_ALERT_SMTP_STARTTLS`、`CRISIS_ALERT_SMTP_SSL` + - 可选危机插件 Google Voice 告警:`CRISIS_ALERT_GOOGLE_VOICE_TO`、`CRISIS_ALERT_EMAIL_TO`、`CRISIS_ALERT_SMTP_FROM`、`CRISIS_ALERT_EMAIL_FROM`、`CRISIS_ALERT_SMTP_HOST`、`CRISIS_ALERT_SMTP_PORT`、`CRISIS_ALERT_SMTP_USERNAME`、`CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`、`CRISIS_ALERT_SMTP_STARTTLS`、`CRISIS_ALERT_SMTP_SSL` - **仓库级 Secrets(共享):** - 仅保留为 fallback:`TELEGRAM_TOKEN` - 仅保留为 fallback:`CRISIS_ALERT_SMTP_PASSWORD` diff --git a/main.py b/main.py index 57e759b..dbb222b 100644 --- a/main.py +++ b/main.py @@ -31,10 +31,10 @@ load_configured_strategy_plugin_signals, parse_strategy_plugin_mounts, ) -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.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.strategy_contracts import build_strategy_evaluation_inputs from runtime_logging import build_run_id, emit_runtime_log @@ -218,7 +218,7 @@ def build_strategy_plugin_alert_messages(signals): def build_strategy_plugin_alert_store(): - return StrategyPluginEmailAlertMarkerStore( + return StrategyPluginGoogleVoiceAlertMarkerStore( local_dir=os.getenv("STRATEGY_PLUGIN_ALERT_STATE_DIR") or "/tmp/quant_strategy_plugin_alerts", gcs_prefix_uri=os.getenv("STRATEGY_PLUGIN_ALERT_STATE_GCS_URI") or os.getenv("EXECUTION_REPORT_GCS_URI"), gcp_project_id=PROJECT_ID, @@ -226,7 +226,7 @@ def build_strategy_plugin_alert_store(): def build_strategy_plugin_alert_context_label() -> str: - return build_email_alert_context_label( + return build_google_voice_alert_context_label( platform_id="longbridge", strategy_profile=STRATEGY_PROFILE, account_scope=ACCOUNT_REGION, @@ -235,15 +235,15 @@ def build_strategy_plugin_alert_context_label() -> str: ) -def attach_strategy_plugin_alert_email_result(report, result) -> None: - report.setdefault("summary", {})["strategy_plugin_alert_email_sent_count"] = result.sent_count +def attach_strategy_plugin_alert_google_voice_result(report, result) -> None: + report.setdefault("summary", {})["strategy_plugin_alert_google_voice_sent_count"] = result.sent_count report.setdefault("diagnostics", {}).update(result.to_report_fields()) def publish_strategy_plugin_alerts(signals, *, report=None): - result = publish_strategy_plugin_email_alerts( + result = publish_strategy_plugin_google_voice_alerts( signals, - email_settings=RUNTIME_SETTINGS, + google_voice_settings=RUNTIME_SETTINGS, translator=t, strategy_label=STRATEGY_PROFILE, context_label=build_strategy_plugin_alert_context_label(), @@ -251,7 +251,7 @@ def publish_strategy_plugin_alerts(signals, *, report=None): log_message=print, ) if report is not None: - attach_strategy_plugin_alert_email_result(report, result) + attach_strategy_plugin_alert_google_voice_result(report, result) return result diff --git a/requirements.txt b/requirements.txt index e1a1619..d507793 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ flask gunicorn -quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@ba67541711228f5a72a294def0e5cc24cc5479f3 +quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@3322c959408edbfdf95799417748c97818333317 us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@305f2cc0748ec08d001deabc3add6c4eff7fe7ba pandas requests diff --git a/runtime_config_support.py b/runtime_config_support.py index e5b57c7..8137ddf 100644 --- a/runtime_config_support.py +++ b/runtime_config_support.py @@ -54,6 +54,8 @@ 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_to: tuple[str, ...] = () + crisis_alert_smtp_from: str | None = None crisis_alert_email_to: tuple[str, ...] = () crisis_alert_email_from: str | None = None crisis_alert_smtp_host: str | None = None @@ -158,6 +160,11 @@ def load_platform_runtime_settings( os.getenv("LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON") or os.getenv("STRATEGY_PLUGIN_MOUNTS_JSON") ), + crisis_alert_google_voice_to=_split_env_list(os.getenv("CRISIS_ALERT_GOOGLE_VOICE_TO")), + crisis_alert_smtp_from=_first_non_empty( + os.getenv("CRISIS_ALERT_SMTP_FROM"), + os.getenv("CRISIS_ALERT_EMAIL_FROM"), + ), crisis_alert_email_to=_split_env_list(os.getenv("CRISIS_ALERT_EMAIL_TO")), crisis_alert_email_from=_first_non_empty(os.getenv("CRISIS_ALERT_EMAIL_FROM")), crisis_alert_smtp_host=_first_non_empty(os.getenv("CRISIS_ALERT_SMTP_HOST")), @@ -204,9 +211,12 @@ def _resolve_ratio_env(name: str, *, default: float) -> float: return value -def _first_non_empty(raw_value: str | None) -> str | None: - value = str(raw_value or "").strip() - return value or None +def _first_non_empty(*raw_values: str | None) -> str | None: + for raw_value in raw_values: + value = str(raw_value or "").strip() + if value: + return value + return None def _resolve_bool_env(name: str, *, default: bool) -> bool: diff --git a/tests/test_request_handling.py b/tests/test_request_handling.py index b62fe26..cab0dc8 100644 --- a/tests/test_request_handling.py +++ b/tests/test_request_handling.py @@ -430,15 +430,15 @@ def fake_publish(signals, **kwargs): return types.SimpleNamespace( sent_count=1, to_report_fields=lambda: { - "strategy_plugin_alert_email_attempted_count": 1, - "strategy_plugin_alert_email_sent_count": 1, - "strategy_plugin_alert_email_skipped_count": 0, - "strategy_plugin_alert_email_failed_count": 0, - "strategy_plugin_alert_email_deliveries": [], + "strategy_plugin_alert_google_voice_attempted_count": 1, + "strategy_plugin_alert_google_voice_sent_count": 1, + "strategy_plugin_alert_google_voice_skipped_count": 0, + "strategy_plugin_alert_google_voice_failed_count": 0, + "strategy_plugin_alert_google_voice_deliveries": [], }, ) - module.publish_strategy_plugin_email_alerts = fake_publish + module.publish_strategy_plugin_google_voice_alerts = fake_publish module.run_strategy() diff --git a/tests/test_runtime_config_support.py b/tests/test_runtime_config_support.py index 5d1f371..ade1665 100644 --- a/tests/test_runtime_config_support.py +++ b/tests/test_runtime_config_support.py @@ -119,6 +119,8 @@ def test_load_platform_runtime_settings_uses_defaults_with_explicit_strategy_pro self.assertIsNone(settings.feature_snapshot_path) self.assertIsNone(settings.strategy_config_path) self.assertIsNone(settings.strategy_plugin_mounts_json) + self.assertEqual(settings.crisis_alert_google_voice_to, ()) + self.assertIsNone(settings.crisis_alert_smtp_from) self.assertEqual(settings.crisis_alert_email_to, ()) self.assertIsNone(settings.crisis_alert_email_from) self.assertIsNone(settings.crisis_alert_smtp_host) @@ -274,12 +276,14 @@ def test_strategy_plugin_mounts_are_loaded_from_env(self): self.assertEqual(settings.strategy_plugin_mounts_json, mount_config) - def test_crisis_alert_email_config_is_loaded_from_env(self): + def test_crisis_alert_google_voice_config_is_loaded_from_env(self): with patch.dict( os.environ, { "RUNTIME_TARGET_JSON": runtime_target_json(SAMPLE_STRATEGY_PROFILE), + "CRISIS_ALERT_GOOGLE_VOICE_TO": "gateway@txt.voice.google.com", "CRISIS_ALERT_EMAIL_TO": "risk@example.com;ops@example.com,risk@example.com", + "CRISIS_ALERT_SMTP_FROM": "smtp-from@example.com", "CRISIS_ALERT_EMAIL_FROM": "bot@example.com", "CRISIS_ALERT_SMTP_HOST": "smtp.example.com", "CRISIS_ALERT_SMTP_PORT": "465", @@ -292,6 +296,8 @@ def test_crisis_alert_email_config_is_loaded_from_env(self): ): settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1") + self.assertEqual(settings.crisis_alert_google_voice_to, ("gateway@txt.voice.google.com",)) + self.assertEqual(settings.crisis_alert_smtp_from, "smtp-from@example.com") self.assertEqual(settings.crisis_alert_email_to, ("risk@example.com", "ops@example.com")) self.assertEqual(settings.crisis_alert_email_from, "bot@example.com") self.assertEqual(settings.crisis_alert_smtp_host, "smtp.example.com") diff --git a/tests/test_sync_cloud_run_env_workflow.sh b/tests/test_sync_cloud_run_env_workflow.sh index b7a670b..ddf19de 100644 --- a/tests/test_sync_cloud_run_env_workflow.sh +++ b/tests/test_sync_cloud_run_env_workflow.sh @@ -48,7 +48,9 @@ grep -Fq 'LONGBRIDGE_STRATEGY_PLUGIN_MOUNTS_JSON: ${{ vars.LONGBRIDGE_STRATEGY_P grep -Fq 'LONGBRIDGE_MIN_RESERVED_CASH_USD: ${{ vars.LONGBRIDGE_MIN_RESERVED_CASH_USD }}' "$workflow_file" grep -Fq 'LONGBRIDGE_RESERVED_CASH_RATIO: ${{ vars.LONGBRIDGE_RESERVED_CASH_RATIO }}' "$workflow_file" grep -Fq 'LONGBRIDGE_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD: ${{ vars.LONGBRIDGE_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD }}' "$workflow_file" +grep -Fq 'CRISIS_ALERT_GOOGLE_VOICE_TO: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_TO }}' "$workflow_file" grep -Fq 'CRISIS_ALERT_EMAIL_TO: ${{ vars.CRISIS_ALERT_EMAIL_TO }}' "$workflow_file" +grep -Fq 'CRISIS_ALERT_SMTP_FROM: ${{ vars.CRISIS_ALERT_SMTP_FROM }}' "$workflow_file" grep -Fq 'CRISIS_ALERT_EMAIL_FROM: ${{ vars.CRISIS_ALERT_EMAIL_FROM }}' "$workflow_file" grep -Fq 'CRISIS_ALERT_SMTP_HOST: ${{ vars.CRISIS_ALERT_SMTP_HOST }}' "$workflow_file" grep -Fq 'CRISIS_ALERT_SMTP_PORT: ${{ vars.CRISIS_ALERT_SMTP_PORT }}' "$workflow_file" @@ -98,7 +100,9 @@ grep -Fq 'LONGBRIDGE_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD=${LONGBRIDGE_SAFE_ grep -Fq 'remove_env_vars+=("LONGBRIDGE_MIN_RESERVED_CASH_USD")' "$workflow_file" grep -Fq 'remove_env_vars+=("LONGBRIDGE_RESERVED_CASH_RATIO")' "$workflow_file" grep -Fq 'remove_env_vars+=("LONGBRIDGE_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD")' "$workflow_file" +grep -Fq 'CRISIS_ALERT_GOOGLE_VOICE_TO=${CRISIS_ALERT_GOOGLE_VOICE_TO}' "$workflow_file" grep -Fq 'CRISIS_ALERT_EMAIL_TO=${CRISIS_ALERT_EMAIL_TO}' "$workflow_file" +grep -Fq 'CRISIS_ALERT_SMTP_FROM=${CRISIS_ALERT_SMTP_FROM}' "$workflow_file" grep -Fq 'CRISIS_ALERT_EMAIL_FROM=${CRISIS_ALERT_EMAIL_FROM}' "$workflow_file" grep -Fq 'CRISIS_ALERT_SMTP_HOST=${CRISIS_ALERT_SMTP_HOST}' "$workflow_file" grep -Fq 'CRISIS_ALERT_SMTP_PORT=${CRISIS_ALERT_SMTP_PORT}' "$workflow_file" @@ -106,7 +110,9 @@ grep -Fq 'CRISIS_ALERT_SMTP_USERNAME=${CRISIS_ALERT_SMTP_USERNAME}' "$workflow_f grep -Fq 'CRISIS_ALERT_SMTP_PASSWORD=${CRISIS_ALERT_SMTP_PASSWORD}' "$workflow_file" grep -Fq 'CRISIS_ALERT_SMTP_STARTTLS=${CRISIS_ALERT_SMTP_STARTTLS}' "$workflow_file" grep -Fq 'CRISIS_ALERT_SMTP_SSL=${CRISIS_ALERT_SMTP_SSL}' "$workflow_file" +grep -Fq 'remove_env_vars+=("CRISIS_ALERT_GOOGLE_VOICE_TO")' "$workflow_file" grep -Fq 'remove_env_vars+=("CRISIS_ALERT_EMAIL_TO")' "$workflow_file" +grep -Fq 'remove_env_vars+=("CRISIS_ALERT_SMTP_FROM")' "$workflow_file" grep -Fq 'remove_env_vars+=("CRISIS_ALERT_EMAIL_FROM")' "$workflow_file" grep -Fq 'remove_env_vars+=("CRISIS_ALERT_SMTP_HOST")' "$workflow_file" grep -Fq 'remove_env_vars+=("CRISIS_ALERT_SMTP_PORT")' "$workflow_file" From a0a37c7b2c53aad5599ecf70dba83b86d4872a09 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Mon, 25 May 2026 02:18:07 +0800 Subject: [PATCH 2/3] Update QuantPlatformKit Google Voice alert pin --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d507793..bf9b930 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ flask gunicorn -quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@3322c959408edbfdf95799417748c97818333317 +quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@8ed13d9122f52c35425b0802d22467bb6664dcd3 us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@305f2cc0748ec08d001deabc3add6c4eff7fe7ba pandas requests From a64920423e22f85dcb1c56e1d8a08480d6f7c347 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Mon, 25 May 2026 02:19:50 +0800 Subject: [PATCH 3/3] Update UsEquityStrategies Google Voice dependency pin --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bf9b930..a6afad0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ flask gunicorn quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@8ed13d9122f52c35425b0802d22467bb6664dcd3 -us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@305f2cc0748ec08d001deabc3add6c4eff7fe7ba +us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@e89ea43181f687d3454636b4b2d99ab7771546f4 pandas requests pytz