mirror of
https://github.com/Sea-Haven-Industries/apm-wo-analysis.git
synced 2026-10-05 07:12:02 +00:00
71 lines
2.3 KiB
Python
71 lines
2.3 KiB
Python
|
|
"""Shared Slack + AWS I/O for the post and interactions Lambdas.
|
||
|
|
|
||
|
|
Keeps the Block Kit builders (blockkit.py) pure: everything that touches the
|
||
|
|
network or AWS lives here. Slack credentials are a single Secrets Manager secret
|
||
|
|
``{ botToken, signingSecret, channelId }``; the Grafana dashboard URL is
|
||
|
|
operational config in SSM (editable without a redeploy). Both are cached for the
|
||
|
|
life of the execution environment.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
|
||
|
|
import boto3
|
||
|
|
from slack_sdk import WebClient
|
||
|
|
from slack_sdk.signature import SignatureVerifier
|
||
|
|
|
||
|
|
_secrets = boto3.client("secretsmanager")
|
||
|
|
_ssm = boto3.client("ssm")
|
||
|
|
_s3 = boto3.client("s3")
|
||
|
|
|
||
|
|
_SECRET_NAME = os.environ["SLACK_SECRET_NAME"]
|
||
|
|
_DASHBOARD_PARAM = os.environ["DASHBOARD_URL_PARAM"]
|
||
|
|
_BUCKET = os.environ["ANALYTICS_BUCKET"]
|
||
|
|
|
||
|
|
# Lazily-populated caches (warm across invocations in the same container).
|
||
|
|
_creds: dict | None = None
|
||
|
|
_dashboard_url: str | None = None
|
||
|
|
|
||
|
|
|
||
|
|
def get_credentials() -> dict:
|
||
|
|
"""Return the Slack creds dict: ``botToken``, ``signingSecret``, ``channelId``."""
|
||
|
|
global _creds
|
||
|
|
if _creds is None:
|
||
|
|
raw = _secrets.get_secret_value(SecretId=_SECRET_NAME)["SecretString"]
|
||
|
|
_creds = json.loads(raw)
|
||
|
|
return _creds
|
||
|
|
|
||
|
|
|
||
|
|
def get_dashboard_url() -> str:
|
||
|
|
"""Grafana dashboard URL for the 📊 button / modal overflow links (SSM)."""
|
||
|
|
global _dashboard_url
|
||
|
|
if _dashboard_url is None:
|
||
|
|
_dashboard_url = _ssm.get_parameter(Name=_DASHBOARD_PARAM)["Parameter"]["Value"]
|
||
|
|
return _dashboard_url
|
||
|
|
|
||
|
|
|
||
|
|
def web_client() -> WebClient:
|
||
|
|
return WebClient(token=get_credentials()["botToken"])
|
||
|
|
|
||
|
|
|
||
|
|
def channel_id() -> str:
|
||
|
|
return get_credentials()["channelId"]
|
||
|
|
|
||
|
|
|
||
|
|
def verify_signature(body: str, timestamp: str, signature: str) -> bool:
|
||
|
|
"""Validate a Slack request signature (HMAC + 5-minute replay window)."""
|
||
|
|
verifier = SignatureVerifier(signing_secret=get_credentials()["signingSecret"])
|
||
|
|
return verifier.is_valid(body=body, timestamp=timestamp, signature=signature)
|
||
|
|
|
||
|
|
|
||
|
|
def read_analytics_json(dt: str, name: str):
|
||
|
|
"""Read ``analytics/dt=<dt>/<name>`` as JSON, or None if absent."""
|
||
|
|
key = f"analytics/dt={dt}/{name}"
|
||
|
|
try:
|
||
|
|
obj = _s3.get_object(Bucket=_BUCKET, Key=key)
|
||
|
|
except _s3.exceptions.NoSuchKey:
|
||
|
|
return None
|
||
|
|
return json.loads(obj["Body"].read())
|