Title: Mark down to post style is not there. · Issue #31 · ma2za/python-substack · GitHub
Open Graph Title: Mark down to post style is not there. · Issue #31 · ma2za/python-substack
X Title: Mark down to post style is not there. · Issue #31 · ma2za/python-substack
Description: This solution adds Markdown support to your Substack newsletter workflow. It includes a parser for inline Markdown formatting (supporting bold and italic text) and a function that processes a Markdown file to create a structured post dra...
Open Graph Description: This solution adds Markdown support to your Substack newsletter workflow. It includes a parser for inline Markdown formatting (supporting bold and italic text) and a function that processes a Markd...
X Description: This solution adds Markdown support to your Substack newsletter workflow. It includes a parser for inline Markdown formatting (supporting bold and italic text) and a function that processes a Markd...
Opengraph URL: https://github.com/ma2za/python-substack/issues/31
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Mark down to post style is not there.","articleBody":"This solution adds Markdown support to your Substack newsletter workflow. It includes a parser for inline Markdown formatting (supporting **bold** and *italic* text) and a function that processes a Markdown file to create a structured post draft. \n\nFor those who might be doubting if it works, here’s a link as evidence of its functionality: [[Check if it works](https://gist.github.com/Duartemartins/98afb33ca5eae545e909df41be45f39e)](https://gist.github.com/Duartemartins/98afb33ca5eae545e909df41be45f39e).\n\n---\n\n### Overview\n\n- **Inline Markdown Parsing:** \n The `parse_inline` function scans a text string for inline Markdown patterns (like **bold** and *italic*) and converts them into tokens with corresponding formatting marks.\n\n- **Post Draft Creation:** \n The `publish_newsletter_to_substack` function reads a Markdown file, splits its content into blocks (headings, images, paragraphs, or bullet lists), and converts these into a structured Post object. It then creates and pre-publishes a draft on Substack.\n\n---\n\n### Code Implementation\n\n```python\nimport re\n\ndef parse_inline(text):\n \"\"\"\n Convert inline Markdown in a text string into a list of tokens\n for use in the post content.\n\n Supported formatting:\n - **Bold**: Text wrapped in double asterisks.\n - *Italic*: Text wrapped in single asterisks.\n \"\"\"\n tokens = []\n # Pattern matches either **bold** or *italic* text.\n pattern = r'(\\*\\*.*?\\*\\*|\\*.*?\\*)'\n parts = re.split(pattern, text)\n \n for part in parts:\n if not part:\n continue\n if part.startswith(\"**\") and part.endswith(\"**\"):\n content = part[2:-2]\n tokens.append({\"content\": content, \"marks\": [{\"type\": \"strong\"}]})\n elif part.startswith(\"*\") and part.endswith(\"*\"):\n content = part[1:-1]\n tokens.append({\"content\": content, \"marks\": [{\"type\": \"em\"}]})\n else:\n tokens.append({\"content\": part})\n \n return tokens\n\ndef publish_newsletter_to_substack(api):\n \"\"\"\n Reads a Markdown file, converts its content into a structured Post,\n and creates a draft post on Substack.\n\n Workflow:\n 1. Retrieve the user profile to extract the user ID.\n 2. Read and parse the Markdown file.\n 3. Convert Markdown blocks into post elements:\n - Headings (lines starting with '#' characters).\n - Images (using Markdown image syntax: ).\n - Paragraphs and bullet lists with inline Markdown formatting.\n 4. Create and update the draft post.\n \"\"\"\n # Retrieve user profile to extract user ID.\n profile = retry_on_502(lambda: api.get_user_profile())\n user_id = profile.get(\"id\")\n if not user_id:\n raise ValueError(\"Could not get user ID from profile\")\n \n # Create a Post instance.\n post = Post(\n title=\"FIX TITLE\", # Replace with the desired title.\n subtitle=\"\", # Optionally customize the subtitle.\n user_id=user_id\n )\n \n # Read the Markdown file.\n with open(\"blog_0_2025-03-28T19-46-34-098Z.md\", 'r', encoding='utf-8') as f:\n md_content = f.read()\n \n # Split content into blocks separated by double newlines.\n blocks = md_content.split(\"\\n\\n\")\n \n for block in blocks:\n block = block.strip()\n if not block:\n continue\n \n # Process headings (lines starting with '#' characters).\n if block.startswith(\"#\"):\n level = len(block) - len(block.lstrip('#'))\n heading_text = block.lstrip('#').strip()\n post.heading(content=heading_text, level=level)\n \n # Process images using Markdown image syntax: \n elif block.startswith(\"!\"):\n m = re.match(r'!\\[.*?\\]\\((.*?)\\)', block)\n if m:\n image_url = m.group(1)\n # Adjust image URL if it starts with a slash.\n image_url = image_url[1:] if image_url.startswith('/') else image_url\n image = api.get_image(image_url)\n post.add({\"type\": \"captionedImage\", \"src\": image.get(\"url\")})\n \n # Process paragraphs or bullet lists.\n else:\n if \"\\n\" in block:\n # Process each line separately.\n for line in block.split(\"\\n\"):\n line = line.strip()\n if not line:\n continue\n # Remove bullet marker if present.\n if line.startswith(\"*\"):\n line = line.lstrip(\"*\").strip()\n tokens = parse_inline(line)\n post.add({\"type\": \"paragraph\", \"content\": tokens})\n else:\n tokens = parse_inline(block)\n post.add({\"type\": \"paragraph\", \"content\": tokens})\n \n # Create and update the draft post.\n draft = api.post_draft(post.get_draft())\n api.put_draft(draft.get(\"id\"), draft_section_id=post.draft_section_id)\n api.prepublish_draft(draft.get(\"id\"))\n \n print(\"Newsletter published (drafted) successfully!\")\n```\n\n---","author":{"url":"https://github.com/a1111198","@type":"Person","name":"a1111198"},"datePublished":"2025-04-01T12:40:27.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/31/python-substack/issues/31"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:06799b70-eb95-eaed-c056-c76863e31056 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | DE9A:1D22:80522D:AA3700:698D8946 |
| html-safe-nonce | da352e180df8001f436d06499bc732b020f99e1f81f8968d057e047dbe55a939 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJERTlBOjFEMjI6ODA1MjJEOkFBMzcwMDo2OThEODk0NiIsInZpc2l0b3JfaWQiOiIzMTYwMjEyMTI2MTQxNjE0NDA2IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 200d820e4d5ddb14a6c428346f8569b016cb9309ae29ed616d0071dea6036fea |
| hovercard-subject-tag | issue:2963327876 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/ma2za/python-substack/31/issue_layout |
| twitter:image | https://opengraph.githubassets.com/6ba0e9ffde57293cafdcf55fcefd4a5d7728c5c5d5f4d33b338ca0a65f2ea5bb/ma2za/python-substack/issues/31 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/6ba0e9ffde57293cafdcf55fcefd4a5d7728c5c5d5f4d33b338ca0a65f2ea5bb/ma2za/python-substack/issues/31 |
| og:image:alt | This solution adds Markdown support to your Substack newsletter workflow. It includes a parser for inline Markdown formatting (supporting bold and italic text) and a function that processes a Markd... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | a1111198 |
| hostname | github.com |
| expected-hostname | github.com |
| None | c0818105fa276287e9369cfdefa0a0fa7953719791ceff9b94d69623c0a4fe8a |
| turbo-cache-control | no-preview |
| go-import | github.com/ma2za/python-substack git https://github.com/ma2za/python-substack.git |
| octolytics-dimension-user_id | 59370937 |
| octolytics-dimension-user_login | ma2za |
| octolytics-dimension-repository_id | 507708304 |
| octolytics-dimension-repository_nwo | ma2za/python-substack |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 507708304 |
| octolytics-dimension-repository_network_root_nwo | ma2za/python-substack |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 4c8f4bd0b67d7f1472d0ab3f49827eaae062a36b |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width