Skip to content

Commit

Permalink
Merge pull request #24 from sudoskys/dev
Browse files Browse the repository at this point in the history
 feat(render): add support for task list items
  • Loading branch information
sudoskys authored Sep 11, 2024
2 parents d7e23f8 + 879e015 commit ec9adcb
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 17 deletions.
Binary file added .github/result-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/result-5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,4 @@ print(converted)

output as follows:

![.github/result.png](.github/result-3.png)
![.github/result.png](.github/result-5.png)
29 changes: 24 additions & 5 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions playground/show_send.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@
*bold _italic bold ~~italic bold strikethrough ||italic bold strikethrough spoiler||~~ __underline italic bold___ bold*
__underline italic bold__
[link](https://www.google.com)
- [ ] Uncompleted task list item
- [ ] __Underline__ ~~Strikethrough~~ _italic_ Item
- [x] Completed task list item
- [x] **Bold** ||Spoiler|| `Inline Code` Item
> Quote
> Multiline Quote In Markdown it's not possible to send multiline quote in telegram without using code block or html tag but telegramify_markdown can do it.
Expand Down
10 changes: 7 additions & 3 deletions playground/use_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
~strikethrough~
"""
quote = """>test"""

task = """
- [x] task1?
- [x] task2?
"""
test_md = """
**bold text**
||spoiler||
"""
converted = telegramify_markdown.convert(quote)
converted = telegramify_markdown.convert(task)
print(converted)

rule = re.compile(r"(?<!\\)(?:\\\\)*\|\|(.+?)\|\|", re.DOTALL)

pattern = re.compile(r"^- \[([ xX])\] (.*)", re.DOTALL | re.MULTILINE)
print(rule.findall(test_md))
print(pattern.findall(task))
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "telegramify-markdown"
version = "0.1.11"
version = "0.1.12"
description = "Convert Markdown to a format usable by Telegram."
authors = [
{ name = "sudoskys", email = "coldlando@hotmail.com" },
Expand Down
4 changes: 4 additions & 0 deletions src/telegramify_markdown/customize.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ class Symbol(object):
# "🖼"
link = "\N{LINK SYMBOL}"
# "🔗"
task_completed = "\N{WHITE HEAVY CHECK MARK}"
# "✅"
task_uncompleted = "\N{BALLOT BOX WITH CHECK}"
# "☑️"


markdown_symbol = Symbol()
Expand Down
67 changes: 60 additions & 7 deletions src/telegramify_markdown/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Iterable

from mistletoe import block_token, span_token
from mistletoe.block_token import BlockToken
from mistletoe.markdown_renderer import MarkdownRenderer, LinkReferenceDefinition, Fragment
from mistletoe.span_token import SpanToken
from telebot import formatting
Expand All @@ -15,6 +16,39 @@ class Spoiler(SpanToken):
pattern = re.compile(r"(?<!\\)(?:\\\\)*\|\|(.+?)\|\|", re.DOTALL)


class TaskListItem(BlockToken):
"""
Custom block token for task list items in Markdown.
Matches task list items like '- [ ]' and '- [x]'.
Attributes:
checked (bool): whether the task is checked.
content (str): the description of the task list item.
"""
repr_attributes = BlockToken.repr_attributes + ("checked", "content")
pattern = re.compile(r'^( *)(- \[([ xX])\] )(.*)')

def __init__(self, match):
self.indentation, self.marker, self.checked_char, self.content = match
self.checked = self.checked_char.lower() == 'x'
super().__init__(lines=self.content, tokenize_func=span_token.tokenize_inner)

@classmethod
def start(cls, line):
stripped = line.lstrip()
if not stripped.startswith("- [ ]") and not stripped.startswith("- [x]"):
return False
return bool(cls.pattern.match(line))

@classmethod
def read(cls, lines):
line = next(lines)
match = cls.pattern.match(line)
if match:
return match.groups()
raise ValueError("TaskListItem did not match expected format.")


def escape_markdown(content: str, unescape_html: bool = True) -> str:
"""
Escapes Markdown characters in a string of Markdown with optional HTML unescaping.
Expand Down Expand Up @@ -45,11 +79,13 @@ def __init__(self, *extras, **kwargs):
*chain(
(
Spoiler,
TaskListItem,
),
extras
)
)
self.render_map["Spoiler"] = self.render_spoiler
self.render_map["TaskListItem"] = self.render_task_list_item

def render_quote(
self, token: block_token.Quote, max_line_length: int
Expand Down Expand Up @@ -141,10 +177,26 @@ def render_strikethrough(
def render_spoiler(self, token: Spoiler) -> Iterable[Fragment]:
return self.embed_span(Fragment("||"), token.children)

def render_task_list_item(self,
token: TaskListItem,
max_line_length: int
) -> Iterable[str]:
symbol = markdown_symbol.task_completed if token.checked else markdown_symbol.task_uncompleted
if self.normalize_whitespace:
indentation = 0
else:
indentation = len(token.indentation)
lines = self.span_to_lines(
token.children, max_line_length=max_line_length
)
space = " " * indentation
return self.prefix_lines(lines or [""], f"{space}{symbol} ")

def render_list_item(
self, token: block_token.ListItem, max_line_length: int
) -> Iterable[str]:
if str(token.leader).strip().endswith("."):
token_origin = str(token.leader).strip()
if token_origin.endswith("."):
token.leader = formatting.escape_markdown(token.leader) + " "
else:
token.leader = formatting.escape_markdown("⦁")
Expand All @@ -154,12 +206,13 @@ def render_link_reference_definition(
self, token: LinkReferenceDefinition
) -> Iterable[Fragment]:
yield from (
Fragment(markdown_symbol.link + formatting.mlink(
content=token.title if token.title else token.label,
url=token.dest,
escape=True
)
),
Fragment(
markdown_symbol.link + formatting.mlink(
content=token.title if token.title else token.label,
url=token.dest,
escape=True
)
),
)

def render_image(self, token: span_token.Image) -> Iterable[Fragment]:
Expand Down

0 comments on commit ec9adcb

Please sign in to comment.