- 9 mm* skills (orchestrator, article, social, publish, analytics) - 6 dependency skills (content-writer, geo-optimizer, etc.) - 3 analytics scripts (GSC, Google Ads, Meta Ads) - Config template + setup guide - SOUL-MM.md persona extension - OrbitOS integration reference
277 lines
7.7 KiB
Markdown
277 lines
7.7 KiB
Markdown
---
|
|
name: mm-social-writer
|
|
description: >
|
|
Create social media posts (Facebook, X/Twitter, Instagram) from a published article.
|
|
Requires the article's published_url. Generates platform-specific posts with
|
|
the article link embedded. Use when: "เขียนโพส", "create social posts",
|
|
"write posts from article", "mm-social", "สร้างโพสจากบทความ".
|
|
---
|
|
|
|
# mm-social-writer
|
|
|
|
Creates social media posts from a published article, with the article URL embedded.
|
|
|
|
## When This Must Trigger
|
|
|
|
- "เขียนโพส", "create social posts", "write posts from article"
|
|
- "mm-social", "สร้างโพสจากบทความ"
|
|
- Called by moreminimore-orchestrator after article is published
|
|
|
|
## Prerequisites
|
|
|
|
- Article must be published with `published_url` in frontmatter
|
|
- Article must have `status: published`
|
|
|
|
## Process
|
|
|
|
### Step 1: Find the published article
|
|
|
|
Search for published articles that DON'T have social posts yet:
|
|
|
|
```python
|
|
import os, re
|
|
|
|
vault = os.path.expanduser("~/vault/60_Articles")
|
|
candidates = []
|
|
|
|
for client_dir in os.listdir(vault):
|
|
client_path = os.path.join(vault, client_dir)
|
|
if not os.path.isdir(client_path) or client_dir == 'Archive':
|
|
continue
|
|
|
|
for entry in os.listdir(client_path):
|
|
if entry == 'Archive':
|
|
continue
|
|
article_path = os.path.join(client_path, entry, 'article.md')
|
|
if not os.path.exists(article_path):
|
|
continue
|
|
|
|
content = open(article_path).read()[:800]
|
|
|
|
# Must be published
|
|
if 'status: published' not in content:
|
|
continue
|
|
|
|
# Must have published_url
|
|
url_match = re.search(r'^published_url:\s*["\']?(.+?)["\']?\s*$', content, re.MULTILINE)
|
|
if not url_match:
|
|
continue
|
|
|
|
# Check if posts already exist
|
|
has_posts = os.path.exists(os.path.join(client_path, entry, 'post-facebook.md'))
|
|
|
|
title_match = re.search(r'^title:\s*["\']?(.+?)["\']?\s*$', content, re.MULTILINE)
|
|
|
|
candidates.append({
|
|
'client_id': client_dir,
|
|
'slug': entry,
|
|
'title': title_match.group(1) if title_match else entry,
|
|
'url': url_match.group(1),
|
|
'dir': os.path.join(client_path, entry),
|
|
'has_posts': has_posts
|
|
})
|
|
```
|
|
|
|
Present to user:
|
|
|
|
```
|
|
บทความที่พร้อมสร้างโพส:
|
|
|
|
1. [title] — [client] — [url] — ยังไม่มีโพส
|
|
2. [title] — [client] — [url] — มีโพสแล้ว
|
|
...
|
|
|
|
เลือกหมายเลข:
|
|
```
|
|
|
|
If user selects an article that already has posts, ask:
|
|
- สร้างใหม่แทนโพสเดิม?
|
|
- หรือดูโพสที่มีอยู่?
|
|
|
|
### Step 2: Read article content and determine platforms
|
|
|
|
Read the full article to understand:
|
|
- Main topic and key points
|
|
- Tone and style
|
|
- Target audience
|
|
- Key statistics or quotes to highlight
|
|
|
|
**Determine which platforms to create posts for:**
|
|
|
|
```python
|
|
import json, os
|
|
|
|
config_path = os.path.expanduser("~/vault/99_System/clients-config.json")
|
|
config = json.load(open(config_path))
|
|
|
|
client = None
|
|
for c in config['clients']:
|
|
if c['id'] == client_id:
|
|
client = c
|
|
break
|
|
|
|
# Get platforms from social_platforms field
|
|
platforms = client.get('social_platforms', [])
|
|
|
|
if not platforms:
|
|
print("⚠️ ไม่มี social_platforms ใน config — ข้ามการสร้างโพส")
|
|
# Exit or skip
|
|
```
|
|
|
|
**If `social_platforms` is empty**: skip social post creation entirely.
|
|
|
|
**If `social_platforms` has values**: only create posts for those platforms.
|
|
|
|
Present to user:
|
|
```
|
|
📱 Platforms ที่ใช้: [facebook, x, ig]
|
|
(กำหนดใน clients-config.json → social_platforms)
|
|
```
|
|
|
|
### Step 3: Generate social posts
|
|
|
|
Create platform-specific posts based on `social_platforms`:
|
|
|
|
**Only create posts for platforms listed in `social_platforms`.**
|
|
Skip any platform not in the list.
|
|
|
|
#### 3a. Facebook Post (if "facebook" in social_platforms)
|
|
|
|
Use `post-formatter` logic (PAS or AIDA framework):
|
|
- 200-300 words
|
|
- Hook-driven opening
|
|
- Key takeaway from article
|
|
- **Article URL embedded** in the post
|
|
- No CTA contact info (social posts don't use CTA)
|
|
|
|
```markdown
|
|
---
|
|
type: post
|
|
platform: facebook
|
|
status: draft
|
|
created: YYYY-MM-DD
|
|
related_article: "article.md"
|
|
published_url: "https://..."
|
|
---
|
|
|
|
[Post content with article link naturally integrated]
|
|
```
|
|
|
|
#### 3b. X/Twitter Post (if "x" in social_platforms)
|
|
|
|
Use `hook-generator` logic for the hook:
|
|
- Single post: 280 chars max, hook + key point + **article URL**
|
|
- Thread: 3-5 tweets, each with a key point, last tweet has **article URL**
|
|
|
|
```markdown
|
|
---
|
|
type: post
|
|
platform: x
|
|
status: draft
|
|
created: YYYY-MM-DD
|
|
related_article: "article.md"
|
|
published_url: "https://..."
|
|
format: single|thread
|
|
---
|
|
|
|
[Post content with article link]
|
|
```
|
|
|
|
#### 3c. Instagram Post (if "instagram" in social_platforms)
|
|
|
|
Visual-focused post:
|
|
- Caption: 150-200 words, hook + key takeaway
|
|
- **Article URL in bio** (IG doesn't allow clickable links in captions)
|
|
→ Note: "ลิงค์บทความใน bio" or use link sticker in Stories
|
|
- Image: generate a square (1080x1080) version
|
|
- Hashtags: 5-10 relevant hashtags
|
|
|
|
```markdown
|
|
---
|
|
type: post
|
|
platform: instagram
|
|
status: draft
|
|
created: YYYY-MM-DD
|
|
related_article: "article.md"
|
|
published_url: "https://..."
|
|
image: "images/ig-post.png"
|
|
---
|
|
|
|
[Caption with "🔗 ลิงค์ใน bio" or similar]
|
|
```
|
|
|
|
### Step 4: Generate social images
|
|
|
|
For each platform:
|
|
- Facebook: use featured image (1200x630)
|
|
- X: use featured image (1200x675)
|
|
- Instagram: generate square version (1080x1080) — may need different composition
|
|
|
|
Save to: `~/vault/60_Articles/<client-id>/<slug>/images/`
|
|
|
|
### Step 5: Present for approval
|
|
|
|
```
|
|
📝 Social posts พร้อมตรวจ:
|
|
|
|
📄 Article: [title]
|
|
🔗 URL: [published_url]
|
|
|
|
📱 Platforms: [list from social_platforms]
|
|
✅ post-facebook.md — [word count] words (if facebook)
|
|
✅ post-x.md — [format] ([char count] chars) (if x)
|
|
✅ post-ig.md — [word count] words + image (if instagram)
|
|
|
|
ตรวจแล้วเป็นยังไงบ้าง?
|
|
- ✅ "approve" — อนุมัติ → พร้อม publish
|
|
- ✏️ "แก้ไข [specify]" — ปรับแก้
|
|
```
|
|
|
|
Wait for approval.
|
|
|
|
### Step 6: Save posts
|
|
|
|
Save all posts to the article directory:
|
|
|
|
```
|
|
~/vault/60_Articles/<client-id>/<slug>/
|
|
├── article.md (existing)
|
|
├── brief.md (existing)
|
|
├── images/ (existing)
|
|
├── post-facebook.md ← NEW
|
|
├── post-x.md ← NEW
|
|
└── post-ig.md ← NEW
|
|
```
|
|
|
|
### Step 7: Confirm
|
|
|
|
```
|
|
✅ Social posts created:
|
|
|
|
📂 ~/vault/60_Articles/<client-id>/<slug>/
|
|
|
|
✅ post-facebook.md ← Facebook post ready
|
|
✅ post-x.md ← X/Twitter post ready
|
|
✅ post-ig.md ← Instagram post ready
|
|
|
|
📋 NEXT: Load mm-publish-content → publish social posts
|
|
```
|
|
|
|
## Output
|
|
|
|
Only creates files for platforms in `social_platforms`:
|
|
- `~/vault/60_Articles/<client-id>/<slug>/post-facebook.md` (if facebook)
|
|
- `~/vault/60_Articles/<client-id>/<slug>/post-x.md` (if x)
|
|
- `~/vault/60_Articles/<client-id>/<slug>/post-ig.md` (if instagram)
|
|
- Social images in `images/` subdirectory
|
|
|
|
## Important Notes
|
|
|
|
- **published_url is REQUIRED** — cannot create posts without it
|
|
- **social_platforms determines which posts to create** — check clients-config.json
|
|
- If social_platforms is empty, skip social post creation entirely
|
|
- Social posts do NOT include CTA/contact info
|
|
- Article URL should be naturally integrated into the post, not just appended
|
|
- For Instagram, note that links aren't clickable in captions
|
|
- Each post should complement the article, not duplicate it
|