Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ ALLOWED_ORIGINS=https://github.com,http://127.0.0.1:8000,http://0.0.0.0:8000,htt
ALLOWED_HOSTS=github.com,127.0.0.1,0.0.0.0,localhost,ping.telex.im
HOST=0.0.0.0
PORT=8000
RELOAD_VALUE=True
RELOAD_VALUE=True
TELEX_WEBHOOK_URL=https://ping.telex.im/v1/webhooks
CURL_COMMAND=curl
8 changes: 5 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from fastapi import FastAPI
import uvicorn
from src.config.config import settings
from src.config.middleware import middleware
from src.routers.router import webhook_router
from src.config.config import settings
import uvicorn


app = FastAPI(docs_url="/", middleware=middleware)
app.include_router(webhook_router)

if __name__ == "__main__":
reload_value = settings.reload_value.lower() == "true"
uvicorn.run("main:app", host=settings.host, port=settings.port, reload=reload_value)
uvicorn.run("main:app", host=settings.host, port=settings.port, reload=reload_value)
8 changes: 8 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
annotated-types==0.7.0
anyio==4.8.0
black==25.1.0
certifi==2025.1.31
click==8.1.8
dnspython==2.7.0
Expand All @@ -11,20 +12,27 @@ httpcore==1.0.7
httptools==0.6.4
httpx==0.28.1
idna==3.10
iniconfig==2.0.0
itsdangerous==2.2.0
Jinja2==3.1.5
joblib==1.4.2
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mdurl==0.1.2
mypy-extensions==1.0.0
nltk==3.9.1
numpy==2.2.3
orjson==3.10.15
packaging==24.2
pathspec==0.12.1
platformdirs==4.3.6
pluggy==1.5.0
pydantic==2.10.6
pydantic-extra-types==2.10.2
pydantic-settings==2.7.1
pydantic_core==2.27.2
Pygments==2.19.1
pytest==8.3.4
python-dotenv==1.0.1
python-multipart==0.0.20
PyYAML==6.0.2
Expand Down
9 changes: 6 additions & 3 deletions src/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ class Settings(BaseSettings):
allowed_hosts: str
host: str
port: int
reload_value: str

reload_value: str
telex_webhook_url: str
curl_command: str | None = "curl" # might require path/to/curl e.g. `/usr/bin/curl`

model_config = SettingsConfigDict(env_file=".env")


@lru_cache
def get_settings() -> Settings:
return Settings()

settings = get_settings()

settings = get_settings()
37 changes: 37 additions & 0 deletions src/routers/github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from fastapi.routing import APIRouter
from ..core.models import GitHubPayload, TelexWebhookPayload
from ..config.config import settings
from ..utils.telex_utils import send_payload_to_telex
from fastapi.responses import JSONResponse
from fastapi import status, HTTPException
import json


router = APIRouter(prefix="/github")


@router.post("/{telex_channel_id}", status_code=status.HTTP_200_OK)
async def github_webhook(telex_channel_id: str, payload: GitHubPayload):
"""Endpoint to receive GitHub webhook events and forward the commits to Telex"""
# Payload in format required by Telex channels' webhooks
telex_payload = TelexWebhookPayload(
event_name="pushed_commits",
message=str(payload.commits),
status="success",
username=payload.pusher["name"],
).model_dump_json()

# Telex channel webhook URL
telex_url = f"{settings.telex_webhook_url}/{telex_channel_id}"

try:
# Send payload to Telex
response = await send_payload_to_telex(telex_payload, telex_url)
response_data = json.loads(response.decode().strip())
except Exception as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Telex payload sending failed: {str(e)}",
)

return JSONResponse(content={"data": response_data})
6 changes: 6 additions & 0 deletions src/routers/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from fastapi.routing import APIRouter
from .github import router as github_router


webhook_router = APIRouter(prefix="/api/v1/webhook")
webhook_router.include_router(github_router)
32 changes: 32 additions & 0 deletions src/utils/telex_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import asyncio
from ..config.config import settings
from fastapi import HTTPException, status


async def send_payload_to_telex(telex_payload: str, telex_url: str):
"""Sends payload through an asynchronous curl subprocess"""
curl_command = [
settings.curl_command,
"-X",
"POST",
telex_url,
"-H",
"Accept: application/json",
"-H",
"Content-Type: application/json",
"-d",
telex_payload,
]
process = await asyncio.create_subprocess_exec(
*curl_command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await process.communicate()

# Check for exit on error
if process.returncode != 0:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Subprocess exception: {stderr.decode().strip()}",
)

return stdout