Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "shondesh"
version = "0.1.2"
version = "0.1.3"
description = "Send notification via channels"
authors = [
{name = "Rakibul Haq",email = "[email protected]"}
Expand Down
4 changes: 2 additions & 2 deletions shondesh/channels/telegram.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from shondesh.channels.base import Channel, logger
from shondesh.formatters.base_formatter import Formatter
from shondesh.formatters.dict_table_formatter import DictTableFormatter
from shondesh.formatters.telegram_message_formatter import TelegramMessageFormatter


class Telegram(Channel):
"""Telegram alert channel implementation"""

def __init__(self, config: dict, formatter: Formatter = DictTableFormatter()):
def __init__(self, config: dict, formatter: Formatter = TelegramMessageFormatter()):
super().__init__(config=config, formatter=formatter)
self.config = config
self.formatter = formatter
Expand Down
3 changes: 2 additions & 1 deletion shondesh/formatters/base_formatter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from abc import ABC, abstractmethod
from typing import Any


class Formatter(ABC):
@abstractmethod
def format(self, data) -> str:
def format(self, data) -> Any:
pass
5 changes: 4 additions & 1 deletion shondesh/formatters/slack_message_formatter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class SlackMessageFormatter:
from shondesh.formatters.base_formatter import Formatter


class SlackMessageFormatter(Formatter):

def format(self, message: dict) -> list:
"""
Expand Down
41 changes: 41 additions & 0 deletions shondesh/formatters/telegram_message_formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from shondesh.formatters.base_formatter import Formatter


class TelegramMessageFormatter(Formatter):
"""
A class to format messages for Telegram.
"""

def format(self, message: dict) -> str:
"""
Format the message for Telegram.

Args:
message (str): The message to format.

Returns:
str: The formatted message.
"""
if not message:
return ""

Copy link

Copilot AI Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Mutating the input message dict in place can cause side-effects. Consider building a new dictionary or list of formatted parts instead of overwriting message.

Suggested change
formatted_message = {}

Copilot uses AI. Check for mistakes.
for key, value in message.items():
if isinstance(value, str):
# Escape special characters for Telegram
value = (
value.replace("_", "\\_")
.replace("*", "\\*")
.replace("[", "\\[")
.replace("]", "\\]")
.replace("`", "\\`")
.replace("~", "\\~")
.replace(">", ">")
.replace("<", "&lt;")
)
message[key] = str(value)
# Join the message parts with double newlines for better readability
message = "\n".join(f"<b>{key}</b>: {value}" for key, value in message.items())
# Wrap the message in a code block for Telegram
message = f"<pre>{message}</pre>"

return message
35 changes: 35 additions & 0 deletions tests/shondesh/formatters/test_telegram_message_formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from shondesh.formatters.telegram_message_formatter import TelegramMessageFormatter


def test_returns_empty_string_when_message_is_none():
formatter = TelegramMessageFormatter()
assert formatter.format(None) == ""


def test_returns_empty_string_when_message_is_empty():
formatter = TelegramMessageFormatter()
assert formatter.format({}) == ""


def test_formats_message_with_special_characters_correctly():
formatter = TelegramMessageFormatter()
message = {"text": "Hello *world* _test_ [link](url)"}
formatted_message = formatter.format(message)
assert (
"<b>text</b>: Hello \\*world\\* \\_test\\_ \\[link\\](url)" in formatted_message
)


def test_escapes_html_characters_in_message():
formatter = TelegramMessageFormatter()
message = {"text": "<script>alert('XSS')</script>"}
formatted_message = formatter.format(message)
assert "&lt;script&gt;alert('XSS')&lt;/script&gt;" in formatted_message


def test_formats_message_with_multiple_keys_correctly():
formatter = TelegramMessageFormatter()
message = {"title": "Notification", "body": "This is a *test*"}
formatted_message = formatter.format(message)
assert "<b>title</b>: Notification" in formatted_message
assert "<b>body</b>: This is a \\*test\\*" in formatted_message