Files
tracer-cloud--opensre/integrations/discord/delivery.py
T
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

98 lines
3.8 KiB
Python

"""Discord delivery helper - posts investigation findings to Discord API."""
from __future__ import annotations
import logging
from typing import Any
from platform.common.truncation import truncate
from platform.notifications.delivery_errors import extract_http_error
from platform.notifications.delivery_transport import post_json
from platform.notifications.limits import MAX_MESSAGE_SIZE
from platform.notifications.redaction import redact_token
logger = logging.getLogger(__name__)
def _discord_auth_headers(bot_token: str) -> dict[str, str]:
# ``Content-Type: application/json`` is set automatically by httpx when
# the request uses the ``json=`` kwarg, so we only need to add auth.
return {"Authorization": f"Bot {bot_token}"}
def post_discord_message(
channel_id: str,
embeds: list[dict[str, Any]],
bot_token: str,
content: str = "",
) -> tuple[bool, str, str]:
"""Call discord channels api to post message on channel.
Returns True on success, False on expected failures.
"""
logger.debug("[discord] post message params channel_id: %s", channel_id)
response = post_json(
url=f"https://discord.com/api/v10/channels/{channel_id}/messages",
payload={"content": content, "embeds": embeds},
headers=_discord_auth_headers(bot_token),
)
if not response.ok:
safe_error = redact_token(response.error, bot_token)
logger.warning("[discord] post message exception: %s", safe_error)
return False, safe_error, ""
if response.status_code not in (200, 201):
logger.warning("[discord] post message failed: %s", response.status_code)
error_message = extract_http_error(response.data, response.status_code, response.text)
safe_error = redact_token(error_message, bot_token)
logger.warning("[discord] post message failed: %s", safe_error)
return False, safe_error, ""
message_id = str(response.data.get("id") or "")
return True, "", message_id
def create_discord_thread(
channel_id: str,
message_id: str,
name: str,
bot_token: str,
) -> tuple[bool, str, str]:
"""Call discord channels api to create a thread.
Returns True on success, False on expected failures.
"""
response = post_json(
url=f"https://discord.com/api/v10/channels/{channel_id}/messages/{message_id}/threads",
payload={"name": name, "auto_archive_duration": 1440},
headers=_discord_auth_headers(bot_token),
)
if not response.ok:
safe_error = redact_token(response.error, bot_token)
logger.warning("[discord] create thread exception: %s", safe_error)
return False, safe_error, ""
if response.status_code not in (200, 201):
error_message = extract_http_error(response.data, response.status_code, response.text)
safe_error = redact_token(error_message, bot_token)
logger.warning("[discord] create thread failed: %s", safe_error)
return False, safe_error, ""
thread_id = str(response.data.get("id") or "")
return True, "", thread_id
_EMBED_TITLE_LIMIT = 256
_EMBED_DESCRIPTION_LIMIT = MAX_MESSAGE_SIZE
def send_discord_report(report: str, discord_ctx: dict[str, Any]) -> tuple[bool, str]:
channel_id: str = str(discord_ctx.get("channel_id") or "")
thread_id: str = str(discord_ctx.get("thread_id") or "")
bot_token: str = str(discord_ctx.get("bot_token") or "")
embed = {
"title": truncate("Investigation Complete", _EMBED_TITLE_LIMIT, suffix="…"),
"color": 15158332,
"description": truncate(report, _EMBED_DESCRIPTION_LIMIT, suffix="…"),
"footer": {"text": "OpenSRE Investigation"},
}
target = thread_id if thread_id else channel_id
post_message_success, error, _ = post_discord_message(target, [embed], bot_token)
return (True, "") if post_message_success else (False, error)