Skip to content

Commit 58fbaa3

Browse files
Address PR review: fix slackbot CI smoke-test + harden charge receiver
- slackbot-ci: stub SocketModeRequest/Response on the slack_sdk stub modules (import was failing with 'cannot import name SocketModeRequest') and provision /app/logs so the module-level mkdir doesn't fail on the runner. - slack_bot: require CHARGE_RELAY_TOKEN before starting the charge-dashboard receiver; the port is host-published and must not accept unauthenticated POSTs. - docker-compose.lan-sender-test: set POSTGRES_DSN to the timescaledb service (default DSN points at localhost, which is the base container here). - websocket_bridge: aclose the Redis client via try/finally so a failing set doesn't leak the connection. - redis_utils: correct get_sync_client docstring (default retries=5, ~8s).
1 parent a3a6981 commit 58fbaa3

5 files changed

Lines changed: 23 additions & 5 deletions

File tree

.github/workflows/slackbot-ci.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ jobs:
3838
SLACK_APP_TOKEN: xapp-test-000000000000
3939
SLACK_BOT_TOKEN: xoxb-test-000000000000
4040
run: |
41+
# slack_bot.py creates /app/logs at module import; the CI user can't
42+
# write under / by default, so provision it before the smoke-test.
43+
sudo mkdir -p /app/logs && sudo chmod 777 /app/logs
4144
python - <<'EOF'
4245
import importlib, sys, types
4346
@@ -56,6 +59,13 @@ jobs:
5659
socket_mode_request_listeners = []
5760
sm_mod.SocketModeClient = _FakeSMC # type: ignore[attr-defined]
5861
62+
# slack_bot imports SocketModeRequest/Response from these submodules at
63+
# module load; the stubs need the attributes or the import raises.
64+
sys.modules["slack_sdk.socket_mode.request"].SocketModeRequest = type( # type: ignore[attr-defined]
65+
"SocketModeRequest", (), {})
66+
sys.modules["slack_sdk.socket_mode.response"].SocketModeResponse = type( # type: ignore[attr-defined]
67+
"SocketModeResponse", (), {})
68+
5969
import requests as _req
6070
_orig_get = _req.get
6171
def _no_net(*a, **kw): raise RuntimeError("network disabled in CI")

server/installer/slackbot/slack_bot.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,11 +847,16 @@ def process_events(client: SocketModeClient, req: SocketModeRequest):
847847
# behind Cloudflare Zero Trust). Posts one self-updating Slack message per session.
848848
try:
849849
from charge_dashboard import ChargeDashboard, start_http_server
850+
# Require a shared token: the receiver port is published on the host and
851+
# accepting unauthenticated POSTs would let anyone reach it post to Slack.
852+
token = (os.environ.get("CHARGE_RELAY_TOKEN") or "").strip()
853+
if not token:
854+
raise RuntimeError("CHARGE_RELAY_TOKEN not set — refusing to start an unauthenticated receiver")
850855
_charge_dash = ChargeDashboard(web_client, DEFAULT_CHANNEL)
851856
start_http_server(
852857
_charge_dash,
853858
port=int(os.environ.get("CHARGE_DASHBOARD_PORT", "9099")),
854-
token=os.environ.get("CHARGE_RELAY_TOKEN") or None,
859+
token=token,
855860
)
856861
except Exception as e:
857862
print(f"⚠️ Charge dashboard not started: {e}")

universal-telemetry-software/deploy/docker-compose.lan-sender-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ services:
1919
- UDP_PORT=5005
2020
- TCP_PORT=5006
2121
- REDIS_URL=redis://base-redis:6379/0
22+
- POSTGRES_DSN=postgresql://wfr:password@timescaledb:5432/wfr
2223
- TIMESCALE_TABLE=wfr26
2324
- ENABLE_TIMESCALE_LOGGING=true
2425
depends_on:

universal-telemetry-software/src/redis_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ def get_sync_client(url: str, retries: int = 5, backoff: float = 1.0):
1919
2020
Args:
2121
url: Redis connection URL.
22-
retries: Maximum connection attempts before giving up (default 10).
22+
retries: Maximum connection attempts before giving up (default 5).
2323
backoff: Seconds to wait between retries, multiplied by 1.5 each attempt.
24-
With default values: ~25s total (1+1.5+2.25+3.375+...).
24+
With default values: ~8s total sleep (1+1.5+2.25+3.375).
2525
"""
2626
client = redis.from_url(url)
2727
attempt = 0

universal-telemetry-software/src/websocket_bridge.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ async def _publish_client_count() -> None:
6666
"""Publish the current WebSocket client count for the HTTP health snapshot."""
6767
try:
6868
r = redis.from_url(REDIS_URL)
69-
await r.set(REDIS_WS_CLIENTS_KEY, json.dumps(_client_count_snapshot()), ex=10)
70-
await r.aclose()
69+
try:
70+
await r.set(REDIS_WS_CLIENTS_KEY, json.dumps(_client_count_snapshot()), ex=10)
71+
finally:
72+
await r.aclose()
7173
except Exception as e:
7274
logger.debug("Could not publish WebSocket client count: %s", e)
7375

0 commit comments

Comments
 (0)