chatgpt-md-converter 0.3.10__tar.gz → 0.3.12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/PKG-INFO +1 -1
  2. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/handlers.py +3 -2
  3. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/code_blocks.py +1 -1
  4. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/inline.py +1 -1
  5. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/preprocess.py +4 -0
  6. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/renderer.py +1 -2
  7. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/PKG-INFO +1 -1
  8. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/setup.py +1 -1
  9. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_parser.py +95 -0
  10. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_roundtrip_markdown.py +7 -0
  11. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/LICENSE +0 -0
  12. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/README.md +0 -0
  13. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/__init__.py +0 -0
  14. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/escaping.py +0 -0
  15. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/renderer.py +0 -0
  16. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/state.py +0 -0
  17. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_markdown/tree.py +0 -0
  18. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_splitter.py +0 -0
  19. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/html_to_markdown.py +0 -0
  20. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_formatter.py +0 -0
  21. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/__init__.py +0 -0
  22. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter/telegram_markdown/postprocess.py +0 -0
  23. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/SOURCES.txt +0 -0
  24. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/dependency_links.txt +0 -0
  25. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/chatgpt_md_converter.egg-info/top_level.txt +0 -0
  26. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/setup.cfg +0 -0
  27. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_html_to_markdown_inline_spacing.py +0 -0
  28. {chatgpt_md_converter-0.3.10 → chatgpt_md_converter-0.3.12}/tests/test_splitter.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatgpt_md_converter
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: A package for converting markdown to HTML for chat Telegram bots
5
5
  Home-page: https://github.com/botfather-dev/formatter-chatgpt-telegram
6
6
  Author: Kostiantyn Kriuchkov
@@ -169,11 +169,12 @@ def _handle_blockquote(node: Node, state: RenderState) -> str:
169
169
  expandable = "expandable" in node.attrs
170
170
  rendered: list[str] = []
171
171
  for index, line in enumerate(lines):
172
- prefix = "**>" if expandable and index == 0 else ">"
173
172
  stripped = line.rstrip("\r")
174
173
  if expandable:
175
- rendered.append(prefix + stripped)
174
+ marker = ">**" if index == 0 else ">"
175
+ rendered.append(f"{marker} {stripped}" if stripped else marker)
176
176
  else:
177
+ prefix = ">"
177
178
  rendered.append(f"{prefix} {stripped}" if stripped else prefix)
178
179
  return "\n".join(rendered)
179
180
 
@@ -67,7 +67,7 @@ def extract_and_convert_code_blocks(text: str):
67
67
  .replace("<", "&lt;")
68
68
  .replace(">", "&gt;")
69
69
  )
70
- placeholder = f"CODEBLOCKPLACEHOLDER{len(placeholders)}"
70
+ placeholder = f"CODEBLOCKPLACEHOLDER_{len(placeholders)}_"
71
71
  placeholders.append(placeholder)
72
72
  if language:
73
73
  html_block = f'<pre><code class="language-{language}">{escaped}</code></pre>'
@@ -60,7 +60,7 @@ def extract_inline_code_snippets(text: str):
60
60
 
61
61
  def replacer(match: re.Match[str]) -> str:
62
62
  snippet = match.group(1)
63
- placeholder = f"INLINECODEPLACEHOLDER{len(placeholders)}"
63
+ placeholder = f"INLINECODEPLACEHOLDER_{len(placeholders)}_"
64
64
  placeholders.append(placeholder)
65
65
  snippets[placeholder] = snippet
66
66
  return placeholder
@@ -14,6 +14,10 @@ def combine_blockquotes(text: str) -> str:
14
14
  in_blockquote = True
15
15
  is_expandable = True
16
16
  blockquote_lines.append(line[3:].strip())
17
+ elif line.startswith(">**") and (len(line) == 3 or line[3].isspace()):
18
+ in_blockquote = True
19
+ is_expandable = True
20
+ blockquote_lines.append(line[3:].strip())
17
21
  elif line.startswith(">"):
18
22
  if not in_blockquote:
19
23
  in_blockquote = True
@@ -12,9 +12,8 @@ from .preprocess import combine_blockquotes
12
12
 
13
13
 
14
14
  def telegram_format(text: str) -> str:
15
- text = combine_blockquotes(text)
16
-
17
15
  output, block_map = extract_and_convert_code_blocks(text)
16
+ output = combine_blockquotes(output)
18
17
  output, inline_snippets = extract_inline_code_snippets(output)
19
18
 
20
19
  output = convert_html_chars(output)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatgpt_md_converter
3
- Version: 0.3.10
3
+ Version: 0.3.12
4
4
  Summary: A package for converting markdown to HTML for chat Telegram bots
5
5
  Home-page: https://github.com/botfather-dev/formatter-chatgpt-telegram
6
6
  Author: Kostiantyn Kriuchkov
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name="chatgpt_md_converter",
5
- version="0.3.10",
5
+ version="0.3.12",
6
6
  author="Kostiantyn Kriuchkov",
7
7
  author_email="latand666@gmail.com",
8
8
  description="A package for converting markdown to HTML for chat Telegram bots",
@@ -716,6 +716,41 @@ Continued</blockquote>"""
716
716
  assert output == expected_output, "Failed handling spoiler inside blockquote"
717
717
 
718
718
 
719
+ def test_blockquote_lines_inside_code_block():
720
+ input_text = """```text
721
+ >** заголовок довгої цитати
722
+ > рядок 2
723
+ > рядок 3
724
+ > рядок 4
725
+ > і ще хоч сто рядків
726
+ ```"""
727
+ expected_output = (
728
+ '<pre><code class="language-text">&gt;** заголовок довгої цитати\n'
729
+ '&gt; рядок 2\n'
730
+ '&gt; рядок 3\n'
731
+ '&gt; рядок 4\n'
732
+ '&gt; і ще хоч сто рядків\n'
733
+ "</code></pre>"
734
+ )
735
+ output = telegram_format(input_text)
736
+ assert output == expected_output, f"Got: {output}"
737
+
738
+
739
+ def test_blockquote_double_asterisk_prefix():
740
+ input_text = """>** заголовок довгої цитати
741
+ > рядок 2
742
+ > рядок 3
743
+ > рядок 4
744
+ > і ще хоч сто рядків"""
745
+ expected_output = """<blockquote expandable>заголовок довгої цитати
746
+ рядок 2
747
+ рядок 3
748
+ рядок 4
749
+ і ще хоч сто рядків</blockquote>"""
750
+ output = telegram_format(input_text)
751
+ assert output == expected_output, f"Got: {output}"
752
+
753
+
719
754
  def test_multiple_spoilers():
720
755
  input_text = "First ||spoiler|| and then another ||spoiler with *italic*||"
721
756
  expected_output = 'First <span class="tg-spoiler">spoiler</span> and then another <span class="tg-spoiler">spoiler with <i>italic</i></span>'
@@ -800,6 +835,66 @@ print("hello world ```")
800
835
  assert output == expected_output, show_output()
801
836
 
802
837
 
838
+ def test_inline_code_placeholders_do_not_overlap():
839
+ input_text = """Службова нотатка для тесту.
840
+
841
+ Коли ви запускаєте `alpha.run()`, система піднімає локальний клієнт.
842
+
843
+ У модулі використовується `hook.set()` для реєстрації синхронізації.
844
+
845
+ ```python
846
+ from framework import hook
847
+
848
+ async def configure(base_url: str):
849
+ await hook.set(f"{base_url}/sync")
850
+ ```
851
+
852
+ **Покроковий план**
853
+
854
+ 1. Викликаємо `hook.set()` через менеджер потоків.
855
+ 2. `hook.set()` повертає попередження при повторній реєстрації.
856
+ 3. Якщо потрібно, `hook.clear()` знімає прив'язку.
857
+ 4. Використовуємо `core.loop()` для довготривалих з'єднань.
858
+ 5. `hook.set()` запускає фонову синхронізацію.
859
+
860
+ Поточне середовище потребує **TLS**. Для локального доступу підходить `debug.tunnel`.
861
+
862
+ Чи є питання щодо `hook.set()` чи `hook.clear()`?"""
863
+
864
+ expected_output = """Службова нотатка для тесту.
865
+
866
+ Коли ви запускаєте <code>alpha.run()</code>, система піднімає локальний клієнт.
867
+
868
+ У модулі використовується <code>hook.set()</code> для реєстрації синхронізації.
869
+
870
+ <pre><code class="language-python">from framework import hook
871
+
872
+ async def configure(base_url: str):
873
+ await hook.set(f"{base_url}/sync")
874
+ </code></pre>
875
+
876
+ <b>Покроковий план</b>
877
+
878
+ 1. Викликаємо <code>hook.set()</code> через менеджер потоків.
879
+ 2. <code>hook.set()</code> повертає попередження при повторній реєстрації.
880
+ 3. Якщо потрібно, <code>hook.clear()</code> знімає прив'язку.
881
+ 4. Використовуємо <code>core.loop()</code> для довготривалих з'єднань.
882
+ 5. <code>hook.set()</code> запускає фонову синхронізацію.
883
+
884
+ Поточне середовище потребує <b>TLS</b>. Для локального доступу підходить <code>debug.tunnel</code>.
885
+
886
+ Чи є питання щодо <code>hook.set()</code> чи <code>hook.clear()</code>?"""
887
+
888
+ output = telegram_format(input_text)
889
+
890
+ assert output == expected_output
891
+ assert "<code>hook.set()</code>0" not in output
892
+ assert "<code>hook.set()</code>1" not in output
893
+ assert "<code>hook.set()</code>2" not in output
894
+ assert "<code>hook.set()</code>3" not in output
895
+ assert "<code>hook.set()</code>4" not in output
896
+
897
+
803
898
  def test_nested_code_fence_six_backticks():
804
899
  input_text = """``````markdown
805
900
  `````python
@@ -30,3 +30,10 @@ def test_markdown_html_markdown_cycle_is_idempotent(_case, markdown_input, _):
30
30
  assert '<br' not in html_first
31
31
  assert '<br' not in html_third
32
32
  assert html_first == html_third
33
+
34
+
35
+ def test_html_to_markdown_expandable_blockquote():
36
+ html_text = "<blockquote expandable>заголовок\nрядок 2\nрядок 3</blockquote>"
37
+ expected_markdown = ">** заголовок\n> рядок 2\n> рядок 3"
38
+ markdown = html_to_telegram_markdown(html_text)
39
+ assert markdown == expected_markdown