Redbot GitHub Forwarder

Discord webhook ingestion engine and developer notification bridge

Python 3.11Python 3.11Red Discord BotRed Discord BotDiscord.pyDiscord.pyaiohttpaiohttpGitHub WebhooksGitHub WebhooksHMAC SHA-256HMAC SHA-256
Core Runtime
Python 3.11 & Discord.py
Security
HMAC-SHA256 Validation
Event Pipeline
Async Webhook Dispatcher

Architecture overview

The Redbot GitHub Forwarder is an asynchronous Python cog built for Red Discord Bot to provide real-time development observability.

It exposes an embedded aiohttp HTTP endpoint to receive GitHub repository webhooks, verifies incoming payload authenticity using HMAC-SHA256 signatures, and transforms raw event payloads into structured Discord embeds across team channels.

Event processing pipeline

The cog processes repository events through four distinct stages:

Stage Process Action
1. Ingress and auth aiohttp HTTP server Receives POST request and validates HMAC-SHA256 signature against secret token
2. Event parser Payload normalizer Parses JSON payload into structured Python dataclasses (PushEvent, PullRequestEvent, WorkflowRunEvent)
3. Channel router Rules engine Maps repository name and event type to configured destination Discord channel IDs
4. Dispatcher discord.py embedder Formats color-coded status embeds and sends messages asynchronously

Key technical implementations

1. Ingress and cryptographic HMAC-SHA256 verification

GitHub delivers webhook payloads via HTTPS POST requests with the signature stored in the X-Hub-Signature-256 header. The cog calculates the payload’s HMAC-SHA256 digest and performs constant-time comparison to prevent timing attacks:

View Python implementation: handle_github_webhook
import hashlib
import hmac
from aiohttp import web

async def handle_github_webhook(request: web.Request, secret_token: str) -> web.Response:
    signature_header = request.headers.get("X-Hub-Signature-256")
    if not signature_header or not signature_header.startswith("sha256="):
        return web.Response(status=401, text="Missing or invalid signature header.")

    body = await request.read()
    expected_signature = "sha256=" + hmac.new(
        key=secret_token.encode("utf-8"),
        msg=body,
        digestmod=hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature_header, expected_signature):
        return web.Response(status=403, text="HMAC signature verification failed.")

    payload = await request.json()
    event_type = request.headers.get("X-GitHub-Event", "ping")
    
    await dispatch_event(event_type, payload)
    return web.Response(status=200, text="Webhook received.")

2. Event normalization and Discord embed construction

The dispatcher converts GitHub Actions CI/CD runs, Pull Requests, and commits into clean Discord embeds:

View Python implementation: build_workflow_run_embed
import discord

def build_workflow_run_embed(payload: dict) -> discord.Embed:
    workflow = payload.get("workflow_run", {})
    conclusion = workflow.get("conclusion")
    status = workflow.get("status")
    repo_name = payload.get("repository", {}).get("full_name", "")
    commit_msg = workflow.get("head_commit", {}).get("message", "").split("\n")[0]
    
    # Map conclusion to Discord color
    color_map = {
        "success": discord.Color.green(),
        "failure": discord.Color.red(),
        "cancelled": discord.Color.dark_grey(),
        "timed_out": discord.Color.orange()
    }
    color = color_map.get(conclusion, discord.Color.blurple())

    embed = discord.Embed(
        title=f"CI Workflow: {workflow.get('name')} #{workflow.get('run_number')}",
        url=workflow.get("html_url", ""),
        description=f"**Status:** `{conclusion or status}`\n**Commit:** {commit_msg}",
        color=color
    )
    embed.set_author(name=repo_name, url=payload.get("repository", {}).get("html_url", ""))
    embed.set_footer(text=f"Triggered by {workflow.get('actor', {}).get('login')}")
    return embed

3. Configurable channel routing and filters

Server administrators configure channel destinations per repository or per event category using Redbot slash commands. This routes high-frequency commit logs to activity feeds while directing release announcements and build failures to dedicated alerts channels.