diff --git a/README.md b/README.md
index 498adf5..94b9552 100644
--- a/README.md
+++ b/README.md
@@ -3,21 +3,23 @@



+
[](https://github.com/reagento/relator/actions/workflows/codeql.yml)
-**Relator** (Latin _referre_ - "to report") - delivers beautifully formatted GitHub notifications to Telegram. Get instant alerts for issues and PRs with smart labeling and clean formatting, keeping your team informed in real-time.
+**Relator** (Latin _referre_ - "to report") - delivers beautifully formatted GitHub notifications to Telegram and Discord. Get instant alerts for issues and PRs with smart labeling and clean formatting, keeping your team informed in real-time.
## ✨ Features
+- **Multi-Platform**: Send notifications to Telegram, Discord, or both simultaneously
- **Instant Notifications**: Get real-time alerts for new events
-- **Rich Formatting**: Clean HTML and MD formatting
-- **Label Support**: Automatically converts GitHub labels to Telegram hashtags
+- **Rich Formatting**: HTML for Telegram, rich embeds for Discord
+- **Label Support**: Automatically converts GitHub labels to hashtags
- **Customizable**: Multiple configuration options for different needs
-- **Reliable**: Built-in retry mechanism for Telegram API
+- **Reliable**: Built-in retry mechanism with exponential backoff
## 🚀 Quick Start
-### Basic Usage
+### Telegram Notifications
```yaml
name: Event Notifier
@@ -45,6 +47,45 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
```
+### Discord Notifications
+
+```yaml
+name: Event Notifier
+
+on:
+ issues:
+ types: [opened, reopened]
+ pull_request_target:
+ types: [opened, reopened]
+
+permissions:
+ issues: read
+ pull_request: read
+
+jobs:
+ notify:
+ name: "Discord notification"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Send Discord notification for new issue or pull request
+ uses: reagento/relator@v1.6.0
+ with:
+ discord-webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+```
+
+### Both Platforms Simultaneously
+
+```yaml
+- name: Send notification to Telegram and Discord
+ uses: reagento/relator@v1.6.0
+ with:
+ tg-bot-token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
+ tg-chat-id: ${{ vars.TELEGRAM_CHAT_ID }}
+ discord-webhook-url: ${{ secrets.DISCORD_WEBHOOK_URL }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+```
+
> github-token it's not required for public projects and is unlikely to hit any [limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#primary-rate-limit-for-unauthenticated-users). However, github actions uses IP-based limits, and since github actions has a limited pool of addresses, these limits are considered public, and you'll hit them very quickly.
### Advanced Configuration
@@ -71,6 +112,8 @@ jobs:
## 🔧 Setup Instructions
+### Telegram Setup
+
1. Create a Telegram Bot
- Message `@BotFather` on [Telegram](https://t.me/botfather)
@@ -90,8 +133,26 @@ jobs:
- `TELEGRAM_BOT_TOKEN`
- `TELEGRAM_CHAT_ID`
+### Discord Setup
+
+1. Create a Discord Webhook
+
+- Go to your Discord server settings
+- Navigate to **Integrations** → **Webhooks**
+- Click **New Webhook**
+- Customize the webhook name and select the target channel
+- Copy the **Webhook URL**
+
+2. Configure GitHub Secrets
+ Add these secrets in your repository settings:
+
+- `DISCORD_WEBHOOK_URL`
+- `DISCORD_THREAD_ID` (optional)
+
## 📋 Example Output
+### Telegram
+
Your Telegram notifications will look like this:
Issue:
@@ -120,9 +181,22 @@ Pull requests:
sent via relator
```
+### Discord
+
+Discord notifications appear as rich embeds with:
+
+- **Color-coded embeds**: Green for issues, purple for pull requests
+- **User avatars**: GitHub profile picture displayed
+- **Repository links**: Clickable links to repository and issue/PR
+- **Organized fields**: Repository, issue/PR number, changes (for PRs), branch info (for PRs)
+- **Markdown formatting**: Clean formatting with proper code blocks, bold, italic, and links
+- **Labels as hashtags**: Same label format as Telegram
+
## 🤝 Acknowledgments
-This action uses the excellent [sulguk](https://github.com/Tishka17/sulguk) library by `@Tishka17` for reliable Telegram message delivery
+This action uses:
+- [sulguk](https://github.com/Tishka17/sulguk) by `@Tishka17` for reliable Telegram message delivery
+- [markdownify](https://github.com/matthewwithanm/python-markdownify) for HTML to Markdown conversion for Discord
## 🌟 Support
diff --git a/action.yml b/action.yml
index 1d5d93f..e866699 100644
--- a/action.yml
+++ b/action.yml
@@ -1,17 +1,23 @@
name: "reagento/relator"
-description: "Send Telegram notifications for new GitHub issues or PRs"
+description: "Send Telegram and Discord notifications for new GitHub issues or PRs"
author: "Sehat1137"
inputs:
tg-bot-token:
description: "Telegram Bot Token"
- required: true
+ required: false
tg-chat-id:
description: "Telegram Chat ID"
- required: true
+ required: false
tg-message-thread-id:
description: "Telegram Message Thread ID"
required: false
+ discord-webhook-url:
+ description: "Discord Webhook URL"
+ required: false
+ discord-thread-id:
+ description: "Discord thread ID to post in (optional)"
+ required: false
github-token:
description: "GitHub Token for API access"
required: false
@@ -51,18 +57,20 @@ runs:
run: |
pip install -r $GITHUB_ACTION_PATH/requirements.txt
- - name: Send Telegram notification
+ - name: Send notifications
shell: bash
env:
TELEGRAM_BOT_TOKEN: ${{ inputs.tg-bot-token }}
TELEGRAM_CHAT_ID: ${{ inputs.tg-chat-id }}
+ TELEGRAM_MESSAGE_THREAD_ID: ${{ inputs.tg-message-thread-id }}
+ DISCORD_WEBHOOK_URL: ${{ inputs.discord-webhook-url }}
+ DISCORD_THREAD_ID: ${{ inputs.discord-thread-id }}
GITHUB_TOKEN: ${{ inputs.github-token }}
EVENT_URL: ${{ github.event.issue.url || github.event.pull_request.url }}
BASE_URL: ${{ inputs.base-url }}
ATTEMPT_COUNT: ${{ inputs.attempt-count }}
HTML_TEMPLATE: ${{ inputs.html-template }}
MD_TEMPLATE: ${{ inputs.md-template }}
- TELEGRAM_MESSAGE_THREAD_ID: ${{ inputs.tg-message-thread-id }}
JOIN_INPUT_WITH_LIST: ${{ inputs.join-input-with-list }}
CUSTOM_LABELS: ${{ inputs.custom-labels }}
run: |
diff --git a/notifier/__main__.py b/notifier/__main__.py
index 45b4baa..a24ddbc 100644
--- a/notifier/__main__.py
+++ b/notifier/__main__.py
@@ -1,9 +1,12 @@
import os
import re
import sys
+import traceback
from notifier.application.interactors import SendIssue, SendPR
+from notifier.application.interfaces import Notifier
from notifier.application.services import RenderService
+from notifier.infrastructure.discord_gateway import DiscordGateway
from notifier.infrastructure.github_gateway import GithubGateway
from notifier.infrastructure.telegram_gateway import TelegramGateway
@@ -24,15 +27,6 @@ def get_interactor(url: str) -> type[SendIssue] | type[SendPR]:
if __name__ == "__main__":
- html_template = os.environ.get("HTML_TEMPLATE", "").strip()
-
- telegram_gateway = TelegramGateway(
- chat_id=os.environ["TELEGRAM_CHAT_ID"],
- bot_token=os.environ["TELEGRAM_BOT_TOKEN"],
- attempt_count=int(os.environ["ATTEMPT_COUNT"]),
- message_thread_id=os.environ.get("TELEGRAM_MESSAGE_THREAD_ID"),
- )
-
event_url = os.environ["EVENT_URL"]
github_gateway = GithubGateway(
@@ -49,15 +43,46 @@ def get_interactor(url: str) -> type[SendIssue] | type[SendPR]:
join_input_with_list=os.environ.get("JOIN_INPUT_WITH_LIST") == "1",
)
+ notifiers: list[Notifier] = []
+
+ tg_bot_token = os.environ.get("TELEGRAM_BOT_TOKEN")
+ tg_chat_id = os.environ.get("TELEGRAM_CHAT_ID")
+ if tg_bot_token and tg_chat_id:
+ html_template = os.environ.get("HTML_TEMPLATE", "").strip()
+ telegram_gateway = TelegramGateway(
+ chat_id=tg_chat_id,
+ bot_token=tg_bot_token,
+ attempt_count=int(os.environ.get("ATTEMPT_COUNT", "2")),
+ message_thread_id=os.environ.get("TELEGRAM_MESSAGE_THREAD_ID"),
+ custom_template=html_template,
+ )
+ notifiers.append(telegram_gateway)
+
+ discord_webhook_url = os.environ.get("DISCORD_WEBHOOK_URL")
+ if discord_webhook_url:
+ discord_gateway = DiscordGateway(
+ webhook_url=discord_webhook_url,
+ attempt_count=int(os.environ.get("ATTEMPT_COUNT", "2")),
+ )
+ notifiers.append(discord_gateway)
+
+ if not notifiers:
+ print(
+ "Error: No notification platform configured. "
+ "Please provide either TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID or DISCORD_WEBHOOK_URL",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+
interactor = get_interactor(event_url)(
- template=html_template,
github=github_gateway,
- telegram=telegram_gateway,
+ notifiers=notifiers,
render_service=render_service,
)
try:
interactor.handler()
except Exception as e:
+ traceback.print_exc(file=sys.stderr)
print(f"Error processing event: {e}", file=sys.stderr)
sys.exit(1)
diff --git a/notifier/application/interactors.py b/notifier/application/interactors.py
index d53a7b1..103b6f8 100644
--- a/notifier/application/interactors.py
+++ b/notifier/application/interactors.py
@@ -1,130 +1,42 @@
-import typing
-
-import sulguk
-
from notifier.application import interfaces
from notifier.application.services import RenderService
-from notifier.domain.entities import Issue, PullRequest
-
-TG_MESSAGE_LIMIT: typing.Final = 4096
-
-
-ISSUE_TEMPLATE: typing.Final = (
- "🚀 New issue to {repository} by @{user}
"
- "📝 {title} (#{id})
"
- "{body}
"
- "{labels}"
- "{promo}"
-)
-
-PR_TEMPLATE: typing.Final = (
- "🎉 New Pull Request to {repository} by @{user}
"
- "✨ {title} (#{id})
"
- "📊 +{additions}/-{deletions}
"
- "🌿 {head_ref} → {base_ref}
"
- "{body}
"
- "{labels}"
- "{promo}"
-)
class SendIssue:
def __init__(
self,
- template: str,
github: interfaces.Github,
- telegram: interfaces.Telegram,
+ notifiers: list[interfaces.Notifier],
render_service: RenderService,
) -> None:
- self._template = template or ISSUE_TEMPLATE
self._github = github
- self._telegram = telegram
+ self._notifiers = notifiers
self._render_service = render_service
def handler(self) -> None:
issue = self._github.get_issue()
-
labels = self._render_service.format_labels(issue.labels)
body = self._render_service.format_body(issue.body)
- message = self._create_message(issue, body, labels)
-
- render_result = sulguk.transform_html(
- message,
- base_url="https://github.com",
- )
-
- if len(render_result.text) <= TG_MESSAGE_LIMIT:
- return self._telegram.send_message(render_result)
-
- message_without_description = self._create_message(issue, "
", labels)
-
- sulguk.transform_html(
- message_without_description,
- base_url="https://github.com",
- )
-
- def _create_message(self, issue: Issue, body: str, labels: str) -> str:
- return self._template.format(
- id=issue.id,
- user=issue.user,
- title=issue.title,
- labels=labels,
- url=issue.url,
- body=body,
- repository=issue.repository,
- promo="sent via relator",
- )
+ for notifier in self._notifiers:
+ notifier.send_issue(issue, body, labels)
class SendPR:
def __init__(
self,
- template: str,
github: interfaces.Github,
- telegram: interfaces.Telegram,
+ notifiers: list[interfaces.Notifier],
render_service: RenderService,
) -> None:
- self._template = template or PR_TEMPLATE
self._github = github
- self._telegram = telegram
+ self._notifiers = notifiers
self._render_service = render_service
def handler(self) -> None:
pr = self._github.get_pull_request()
-
labels = self._render_service.format_labels(pr.labels)
body = self._render_service.format_body(pr.body)
- message = self._create_message(pr, body, labels)
-
- render_result = sulguk.transform_html(
- message,
- base_url="https://github.com",
- )
-
- if len(render_result.text) <= TG_MESSAGE_LIMIT:
- return self._telegram.send_message(render_result)
-
- message_without_description = self._create_message(pr, "", labels)
-
- sulguk.transform_html(
- message_without_description,
- base_url="https://github.com",
- )
-
- def _create_message(self, pr: PullRequest, body: str, labels: str) -> str:
- return self._template.format(
- id=pr.id,
- user=pr.user,
- title=pr.title,
- labels=labels,
- url=pr.url,
- body=body,
- repository=pr.repository,
- additions=pr.additions,
- deletions=pr.deletions,
- head_ref=pr.head_ref,
- base_ref=pr.base_ref,
- promo="sent via relator",
- )
+ for notifier in self._notifiers:
+ notifier.send_pull_request(pr, body, labels)
diff --git a/notifier/application/interfaces.py b/notifier/application/interfaces.py
index 4e815f4..a6ffb42 100644
--- a/notifier/application/interfaces.py
+++ b/notifier/application/interfaces.py
@@ -1,9 +1,7 @@
import abc
import typing
-import sulguk
-
-from notifier.domain.entities import PullRequest, Issue
+from notifier.domain.entities import Issue, PullRequest
class Github(typing.Protocol):
@@ -14,6 +12,19 @@ def get_issue(self) -> Issue: ...
def get_pull_request(self) -> PullRequest: ...
-class Telegram(typing.Protocol):
+class Notifier(typing.Protocol):
+ @abc.abstractmethod
+ def send_issue(
+ self,
+ issue: Issue,
+ formatted_body: str,
+ formatted_labels: str,
+ ) -> None: ...
+
@abc.abstractmethod
- def send_message(self, render_result: sulguk.RenderResult) -> None: ...
+ def send_pull_request(
+ self,
+ pull_request: PullRequest,
+ formatted_body: str,
+ formatted_labels: str,
+ ) -> None: ...
diff --git a/notifier/infrastructure/discord_gateway.py b/notifier/infrastructure/discord_gateway.py
new file mode 100644
index 0000000..e337b61
--- /dev/null
+++ b/notifier/infrastructure/discord_gateway.py
@@ -0,0 +1,191 @@
+import typing
+from datetime import datetime, timezone
+
+import bs4
+from markdownify import markdownify
+
+from notifier.application import interfaces
+from notifier.domain.entities import Issue, PullRequest
+from notifier.infrastructure.send_weebhook import send_webhook
+
+DISCORD_EMBED_DESC_LIMIT: typing.Final = 2000
+DISCORD_COLOR_ISSUE: typing.Final = 0x28A745 # green
+DISCORD_COLOR_PR: typing.Final = 0x6F42C1 # purple
+
+
+class DiscordGateway(interfaces.Notifier):
+ def __init__(
+ self,
+ webhook_url: str,
+ attempt_count: int,
+ ) -> None:
+ self._webhook_url = webhook_url
+ self._attempt_count = attempt_count
+
+ def send_issue(
+ self,
+ issue: Issue,
+ formatted_body: str,
+ formatted_labels: str,
+ ) -> None:
+ embed = self._format_issue(issue, formatted_body, formatted_labels)
+ send_webhook(
+ url=self._webhook_url,
+ payload={"embeds": [embed]},
+ attempts=self._attempt_count,
+ )
+
+ def send_pull_request(
+ self,
+ pull_request: PullRequest,
+ formatted_body: str,
+ formatted_labels: str,
+ ) -> None:
+ embed = self._format_pull_request(
+ pull_request, formatted_body, formatted_labels
+ )
+ send_webhook(
+ url=self._webhook_url,
+ payload={"embeds": [embed]},
+ attempts=self._attempt_count,
+ )
+
+ def _format_issue(
+ self, issue: Issue, body: str, labels: str
+ ) -> dict[str, typing.Any]:
+ markdown_body = self._html_to_markdown(body)
+ description = self._create_description(markdown_body, labels)
+
+ embed = {
+ "title": f"🚀 New Issue #{issue.id}: {self._truncate_title(issue.title)}",
+ "description": description,
+ "url": issue.url,
+ "color": DISCORD_COLOR_ISSUE,
+ "author": {
+ "name": f"@{issue.user}",
+ "url": f"https://github.com/{issue.user}",
+ "icon_url": f"https://github.com/{issue.user}.png?size=32",
+ },
+ "fields": [
+ {
+ "name": "Repository",
+ "value": f"[{issue.repository}](https://github.com/{issue.repository})",
+ "inline": True,
+ },
+ {
+ "name": "Issue Number",
+ "value": f"#{issue.id}",
+ "inline": True,
+ },
+ ],
+ "footer": {
+ "text": "sent via relator",
+ },
+ "timestamp": datetime.now(timezone.utc).isoformat(),
+ }
+
+ return embed
+
+ def _format_pull_request(
+ self, pr: PullRequest, body: str, labels: str
+ ) -> dict[str, typing.Any]:
+ labels = labels.rstrip("
")
+ markdown_body = self._html_to_markdown(body)
+ description = self._create_description(markdown_body, labels)
+
+ embed = {
+ "title": f"🎉 New PR #{pr.id}: {self._truncate_title(pr.title)}",
+ "description": description,
+ "url": pr.url,
+ "color": DISCORD_COLOR_PR,
+ "author": {
+ "name": f"@{pr.user}",
+ "url": f"https://github.com/{pr.user}",
+ "icon_url": f"https://github.com/{pr.user}.png?size=32",
+ },
+ "fields": [
+ {
+ "name": "Repository",
+ "value": f"[{pr.repository}](https://github.com/{pr.repository})",
+ "inline": True,
+ },
+ {
+ "name": "PR Number",
+ "value": f"#{pr.id}",
+ "inline": True,
+ },
+ {
+ "name": "Changes",
+ "value": f"+{pr.additions} / -{pr.deletions}",
+ "inline": True,
+ },
+ {
+ "name": "Branch",
+ "value": f"`{pr.head_ref}` → `{pr.base_ref}`",
+ "inline": False,
+ },
+ ],
+ "footer": {
+ "text": "sent via relator",
+ },
+ "timestamp": datetime.now(timezone.utc).isoformat(),
+ }
+
+ return embed
+
+ def _html_to_markdown(self, html: str) -> str:
+ if not html or html == "":
+ return ""
+
+ html = html.replace("
", "\n")
+ print(f"After trim {html=}")
+ try:
+ markdown = markdownify(
+ html,
+ heading_style="ATX",
+ bullets="-",
+ strip=["script", "style"],
+ )
+ markdown = self._clean_markdown(markdown)
+ return markdown.strip()
+ except Exception:
+ soup = bs4.BeautifulSoup(html, "lxml")
+ return soup.get_text().strip()
+
+ def _clean_markdown(self, markdown: str) -> str:
+ lines = markdown.split("\n")
+ cleaned_lines = []
+ empty_count = 0
+
+ for line in lines:
+ if line.strip() == "":
+ empty_count += 1
+ if empty_count <= 2:
+ cleaned_lines.append(line)
+ else:
+ empty_count = 0
+ cleaned_lines.append(line)
+
+ return "\n".join(cleaned_lines)
+
+ def _create_description(self, markdown_body: str, labels: str) -> str:
+ labels_text = f"\n\n{labels}" if labels.strip() else ""
+ reserved_for_labels = len(labels_text) + 100
+ available_for_body = DISCORD_EMBED_DESC_LIMIT - reserved_for_labels
+
+ if len(markdown_body) > available_for_body:
+ truncated_body = markdown_body[: available_for_body - 4] + "..."
+ else:
+ truncated_body = markdown_body
+
+ description = truncated_body + labels_text
+
+ if len(description) > DISCORD_EMBED_DESC_LIMIT:
+ description = description[: DISCORD_EMBED_DESC_LIMIT - 3] + "..."
+
+ return description
+
+ def _truncate_title(self, title: str, max_length: int = 200) -> str:
+ if len(title) <= max_length:
+ return title
+ return title[: max_length - 3] + "..."
diff --git a/notifier/infrastructure/send_weebhook.py b/notifier/infrastructure/send_weebhook.py
new file mode 100644
index 0000000..eefecf8
--- /dev/null
+++ b/notifier/infrastructure/send_weebhook.py
@@ -0,0 +1,23 @@
+import sys
+import time
+from typing import Any
+
+import requests
+
+
+def send_webhook(*, payload: dict[str, Any], url: str, attempts: int) -> None:
+ count = 0
+ while count < attempts:
+ response = requests.post(url, json=payload, timeout=30)
+ try:
+ response.raise_for_status()
+ except requests.exceptions.HTTPError:
+ print(response.content, file=sys.stderr)
+ count += 1
+ time.sleep(count * 2)
+ else:
+ print(
+ f"Response: {response.status_code=} {response.text=}",
+ file=sys.stdout,
+ )
+ return
diff --git a/notifier/infrastructure/telegram_gateway.py b/notifier/infrastructure/telegram_gateway.py
index 95bab39..f6696d8 100644
--- a/notifier/infrastructure/telegram_gateway.py
+++ b/notifier/infrastructure/telegram_gateway.py
@@ -1,40 +1,89 @@
-import sys
-import time
+import typing
-import requests
import sulguk
from notifier.application import interfaces
+from notifier.domain.entities import Issue, PullRequest
+from notifier.infrastructure.send_weebhook import send_webhook
+TG_MESSAGE_LIMIT: typing.Final = 4096
-class TelegramGateway(interfaces.Telegram):
+ISSUE_TEMPLATE: typing.Final = (
+ "🚀 New issue to {repository} by @{user}
"
+ "📝 {title} (#{id})
"
+ "{body}
"
+ "{labels}"
+ "{promo}"
+)
+
+PR_TEMPLATE: typing.Final = (
+ "🎉 New Pull Request to {repository} by @{user}
"
+ "✨ {title} (#{id})
"
+ "📊 +{additions}/-{deletions}
"
+ "🌿 {head_ref} → {base_ref}
"
+ "{body}
"
+ "{labels}"
+ "{promo}"
+)
+
+
+class TelegramGateway(interfaces.Notifier):
def __init__(
self,
chat_id: str,
bot_token: str,
attempt_count: int,
- message_thread_id: str | int | None,
+ message_thread_id: str | int | None = None,
+ custom_template: str = "",
) -> None:
self._chat_id = chat_id
self._bot_token = bot_token
self._attempt_count = attempt_count
self._message_thread_id = message_thread_id
+ self._custom_template = custom_template
- def send_message(self, render_result: sulguk.RenderResult) -> None:
- count = 0
- payload = self._create_payload(render_result)
- url = f"https://api.telegram.org/bot{self._bot_token}/sendMessage"
- while count < self._attempt_count:
- response = requests.post(url, json=payload, timeout=30)
- try:
- response.raise_for_status()
- except requests.exceptions.HTTPError:
- print(response.content, file=sys.stderr)
- count += 1
- time.sleep(count * 2)
- else:
- print(response.json(), file=sys.stdout)
- return
+ def send_issue(
+ self,
+ issue: Issue,
+ formatted_body: str,
+ formatted_labels: str,
+ ) -> None:
+ message = self._create_issue_message(issue, formatted_body, formatted_labels)
+ render_result = sulguk.transform_html(message, base_url="https://github.com")
+
+ if len(render_result.text) > TG_MESSAGE_LIMIT:
+ message = self._create_issue_message(issue, "", formatted_labels)
+ render_result = sulguk.transform_html(
+ message, base_url="https://github.com"
+ )
+ send_webhook(
+ payload=self._create_payload(render_result),
+ url=f"https://api.telegram.org/bot{self._bot_token}/sendMessage",
+ attempts=self._attempt_count,
+ )
+
+ def send_pull_request(
+ self,
+ pull_request: PullRequest,
+ formatted_body: str,
+ formatted_labels: str,
+ ) -> None:
+ message = self._create_pr_message(
+ pull_request, formatted_body, formatted_labels
+ )
+ render_result = sulguk.transform_html(message, base_url="https://github.com")
+
+ if len(render_result.text) > TG_MESSAGE_LIMIT:
+ message = self._create_pr_message(pull_request, "", formatted_labels)
+ render_result = sulguk.transform_html(
+ message, base_url="https://github.com"
+ )
+
+ send_webhook(
+ payload=self._create_payload(render_result),
+ url=f"https://api.telegram.org/bot{self._bot_token}/sendMessage",
+ attempts=self._attempt_count,
+ )
def _create_payload(self, render_result: sulguk.RenderResult) -> dict:
for e in render_result.entities:
@@ -51,3 +100,34 @@ def _create_payload(self, render_result: sulguk.RenderResult) -> dict:
payload["message_thread_id"] = self._message_thread_id
return payload
+
+ def _create_issue_message(self, issue: Issue, body: str, labels: str) -> str:
+ template = self._custom_template or ISSUE_TEMPLATE
+ return template.format(
+ id=issue.id,
+ user=issue.user,
+ title=issue.title,
+ labels=labels,
+ url=issue.url,
+ body=body,
+ repository=issue.repository,
+ promo="sent via relator",
+ )
+
+ def _create_pr_message(self, pr: PullRequest, body: str, labels: str) -> str:
+ """Create HTML message for pull request"""
+ template = self._custom_template or PR_TEMPLATE
+ return template.format(
+ id=pr.id,
+ user=pr.user,
+ title=pr.title,
+ labels=labels,
+ url=pr.url,
+ body=body,
+ repository=pr.repository,
+ additions=pr.additions,
+ deletions=pr.deletions,
+ head_ref=pr.head_ref,
+ base_ref=pr.base_ref,
+ promo="sent via relator",
+ )
diff --git a/requirements.txt b/requirements.txt
index 4617dbf..12d6930 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,3 +2,4 @@ sulguk==0.10.1
requests==2.32.5
beautifulsoup4==4.14.2
lxml==6.0.2
+markdownify==0.12.1