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
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
43 changes: 43 additions & 0 deletions shondesh/formatters/telegram_message_formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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.
formatted_message = {}

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;")
)
formatted_message[key] = str(value)
# Join the message parts with double newlines for better readability
final_message = "\n".join(f"<b>{key}</b>: {value}" for key, value in formatted_message.items())
# Wrap the message in a code block for Telegram
final_message = f"<pre>{final_message}</pre>"

return final_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