Skip to main content
Glama
markdown_converter.py7.65 kB
""" Markdownコンバータモジュール NotionのブロックとMarkdown形式の相互変換を行うクラスを提供します。 """ from typing import List, Dict, Any import mistune class MarkdownConverter: """ MarkdownとNotionブロック間の変換を行うクラス MarkdownテキストをパースしてNotionブロック形式に変換する機能と、 NotionブロックをパースしてMarkdown形式に変換する機能を提供します。 """ def __init__(self): """ MarkdownConverterのコンストラクタ """ self.markdown_parser = mistune.create_markdown() def parse_markdown_to_blocks(self, md: str) -> List[Dict[str, Any]]: """ Markdownテキストをパースし、Notionブロック形式に変換します。 Args: md: 変換するMarkdownテキスト Returns: Notionブロック形式のリスト """ # Markdownを行ごとに分割 lines = md.strip().split("\n") blocks = [] # タイトル(H1)を抽出 title = None content_start_idx = 0 # H1をタイトルとして扱う for i, line in enumerate(lines): if line.startswith("# "): title = line[2:].strip() content_start_idx = i + 1 break # タイトルがない場合は空文字を設定 if title is None: title = "" # 残りの内容をブロックに変換 i = content_start_idx while i < len(lines): line = lines[i] # 見出し(H2-H6) if line.startswith("## "): blocks.append( { "type": "heading_2", "heading_2": {"rich_text": [{"type": "text", "text": {"content": line[3:].strip()}}]}, } ) elif line.startswith("### "): blocks.append( { "type": "heading_3", "heading_3": {"rich_text": [{"type": "text", "text": {"content": line[4:].strip()}}]}, } ) # 箇条書き elif line.startswith("- "): blocks.append( { "type": "bulleted_list_item", "bulleted_list_item": {"rich_text": [{"type": "text", "text": {"content": line[2:].strip()}}]}, } ) # 番号付きリスト elif line.strip() and line[0].isdigit() and ". " in line: content = line.split(". ", 1)[1] blocks.append( { "type": "numbered_list_item", "numbered_list_item": {"rich_text": [{"type": "text", "text": {"content": content.strip()}}]}, } ) # コードブロック elif line.startswith("```"): code_lines = [] language = line[3:].strip() i += 1 while i < len(lines) and not lines[i].startswith("```"): code_lines.append(lines[i]) i += 1 blocks.append( { "type": "code", "code": { "rich_text": [{"type": "text", "text": {"content": "\n".join(code_lines)}}], "language": language if language else "plain text", }, } ) # 通常のテキスト(段落) elif line.strip(): blocks.append( {"type": "paragraph", "paragraph": {"rich_text": [{"type": "text", "text": {"content": line.strip()}}]}} ) i += 1 return blocks, title def convert_blocks_to_markdown(self, blocks: List[Dict[str, Any]], title: str = None) -> str: """ Notionブロックをパースし、Markdown形式に変換します。 Args: blocks: 変換するNotionブロックのリスト title: ページのタイトル(指定された場合はH1として出力) Returns: Markdown形式のテキスト """ md_lines = [] # タイトルがあればH1として追加 if title: md_lines.append(f"# {title}") md_lines.append("") # 空行を追加 for block in blocks: block_type = block.get("type") if block_type == "paragraph": text_content = self._extract_text_content(block.get("paragraph", {}).get("rich_text", [])) md_lines.append(text_content) md_lines.append("") # 空行を追加 elif block_type == "heading_1": text_content = self._extract_text_content(block.get("heading_1", {}).get("rich_text", [])) md_lines.append(f"# {text_content}") md_lines.append("") elif block_type == "heading_2": text_content = self._extract_text_content(block.get("heading_2", {}).get("rich_text", [])) md_lines.append(f"## {text_content}") md_lines.append("") elif block_type == "heading_3": text_content = self._extract_text_content(block.get("heading_3", {}).get("rich_text", [])) md_lines.append(f"### {text_content}") md_lines.append("") elif block_type == "bulleted_list_item": text_content = self._extract_text_content(block.get("bulleted_list_item", {}).get("rich_text", [])) md_lines.append(f"- {text_content}") elif block_type == "numbered_list_item": text_content = self._extract_text_content(block.get("numbered_list_item", {}).get("rich_text", [])) md_lines.append(f"1. {text_content}") elif block_type == "code": code_block = block.get("code", {}) language = code_block.get("language", "") text_content = self._extract_text_content(code_block.get("rich_text", [])) md_lines.append(f"```{language}") md_lines.append(text_content) md_lines.append("```") md_lines.append("") elif block_type == "to_do": todo_item = block.get("to_do", {}) checked = todo_item.get("checked", False) text_content = self._extract_text_content(todo_item.get("rich_text", [])) checkbox = "[x]" if checked else "[ ]" md_lines.append(f"- {checkbox} {text_content}") elif block_type == "quote": text_content = self._extract_text_content(block.get("quote", {}).get("rich_text", [])) md_lines.append(f"> {text_content}") md_lines.append("") return "\n".join(md_lines) def _extract_text_content(self, rich_text_list): """ Notionのリッチテキスト配列からプレーンテキストを抽出します。 Args: rich_text_list: Notionのリッチテキスト配列 Returns: 抽出されたプレーンテキスト """ if not rich_text_list: return "" return "".join([rt.get("text", {}).get("content", "") for rt in rich_text_list if "text" in rt])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karaage0703/notion-mcp-light'

If you have feedback or need assistance with the MCP directory API, please join our Discord server