Files
opencode-skill/skills/seo-multi-channel/SKILL.md
2026-03-22 13:08:30 +07:00

637 lines
20 KiB
Markdown

---
name: seo-multi-channel
description: Generate multi-channel marketing content (Facebook, Ads, Blog, X) with Thai language support and website-creator integration. Use when user wants to create content for multiple channels from a single topic.
---
# 🎯 SEO Multi-Channel Content Generator
**Skill Name:** `seo-multi-channel`
**Category:** `deep`
**Load Skills:** `['website-creator']`
---
## 🚀 Purpose
Generate marketing content for multiple channels from a single topic with:
-**Priority Channels:** Facebook > Facebook Ads > Google Ads > Blog > X (Twitter)
-**Thai Language Support:** Full Thai text processing with PyThaiNLP
-**Image Handling:** Browse website repo for product images, or ask user to provide
-**Website-Creator Integration:** Auto-publish blog posts to Astro content collections
-**API-Ready Output:** Structured JSON for future ad platform API integration
-**Per-Project Context:** Context files in each website repo
**Use Cases:**
1. **Multi-Channel Campaign:** One topic → Facebook post + Facebook Ads + Google Ads + Blog + X thread
2. **Social-Only:** Facebook post + Facebook Ads for product promotion
3. **Blog-First:** SEO blog post with auto-publish to website
4. **Ads-Only:** Google Ads + Facebook Ads copy for existing product
---
## 📋 Pre-Flight Questions
**MUST ask before generating:**
1. **Topic/Subject:** What topic do you want content about?
2. **Channels Needed:** (Default: All channels)
- Facebook (organic posts)
- Facebook Ads (paid campaigns)
- Google Ads (search campaigns)
- Blog (SEO articles)
- X/Twitter (threads)
3. **Content Type:** (Auto-detect or ask)
- Product/Service (requires product images)
- Knowledge/Educational (generates fresh images)
- Statistics/Data (generates infographic-style images)
- Announcement/News (may not need images)
4. **Target Language:** (Auto-detect from topic or ask)
- Thai (default for Thai topics)
- English
- Bilingual (both Thai + English)
5. **For Product Content:**
- Product name
- Website repo path (to browse for existing images)
- Product URL (if available)
6. **For Blog Posts:**
- Target keyword for SEO
- Should I auto-publish to website? (yes/no)
- Website repo path (if auto-publish)
7. **Tone/Formality:** (Auto-detect from context or default)
- กันเอง (Casual) - for social media
- ปกติ (Normal) - for blog
- เป็นทางการ (Formal) - for corporate
---
## 🔄 Workflow
### Phase 1: Context Loading
1. **Load Project Context:**
- Read `context/brand-voice.md` from website repo
- Read `context/target-keywords.md`
- Read `context/seo-guidelines.md`
- Auto-detect formality level from brand voice
2. **Check Data Services:**
- Check if GA4 configured (skip if not)
- Check if GSC configured (skip if not)
- Check if DataForSEO configured (skip if not)
- Check if Umami configured (skip if not)
- Fetch available performance data
3. **Load Channel Templates:**
- Load YAML templates for selected channels
- Apply brand voice customizations
---
### Phase 2: Content Generation
#### **For Each Channel:**
**Facebook (Organic):**
```yaml
Output:
- primary_text: 125-250 chars (Thai can be longer)
- headline: 100 chars max
- hashtags: 3-5 recommended
- cta: เลือกจาก ["เรียนรู้เพิ่มเติม", "สมัครเลย", "ซื้อเลย", "ดูรายละเอียด"]
- image: Generated or edited
- variations: 5 options
```
**Facebook Ads:**
```yaml
Output:
- primary_text: 125 chars recommended (5000 max)
- headline: 40 chars
- description: 90 chars
- cta: Button choice
- image: Product-focused or benefit-focused
- variations: 5 options
- api_ready: true (matches Meta Ads API structure)
```
**Google Ads:**
```yaml
Output:
- headlines: 15 variations (30 chars each)
- descriptions: 4 variations (90 chars each)
- keywords: Suggested keyword list
- negative_keywords: Suggested negatives
- ad_extensions: Sitelink, callout, structured snippets
- api_ready: true (matches Google Ads API structure)
```
**Blog (SEO Article):**
```yaml
Output:
- markdown: Full article with frontmatter
- word_count: 1500-3000 (Thai), 2000-3000 (English)
- keyword_density: 1.0-1.5% (Thai), 1.5-2% (English)
- meta_title: 50-60 chars
- meta_description: 150-160 chars
- slug: Auto-generated (Thai-friendly)
- images: Saved to website repo
- astro_ready: true (content collections format)
```
**X/Twitter Thread:**
```yaml
Output:
- tweets: 5-10 tweet thread
- hook_tweet: First tweet (280 chars)
- body_tweets: 2-8 tweets (280 chars each)
- cta_tweet: Final tweet with CTA
- hashtags: 2-3 per tweet
- thread_title: Optional title card
```
---
### Phase 3: Image Handling
#### **Product Content:**
```python
1. Browse website repo for existing product images:
- Search: public/images/, src/assets/, **/*{product_name}*.{jpg,png,webp}
2. If images found:
- Select best image (highest quality, product-focused)
- Ask user: "Use this image or provide a new one?"
3. If no images found:
- Ask user: "No product images found in repo. Please provide image path or URL."
- Wait for user to provide
```
#### **Non-Product Content:**
```python
1. Determine content type:
- Service Professional illustration
- Knowledge Educational visual metaphor
- Stats Infographic with charts
- News Clean, modern announcement style
2. Ask user to provide image:
- "For this content, please provide an image or use: [suggestion]"
3. Save images:
- Social/Ads seo-multi-channel/generated-images/{topic}/{channel}/
- Blog {website-repo}/public/images/blog/{slug}/
```
---
### Phase 4: Output & Publishing
#### **Output Structure:**
```
output/{topic-slug}/
├── facebook/
│ ├── posts.json
│ └── images/
├── facebook_ads/
│ ├── ads.json
│ └── images/
├── google_ads/
│ └── ads.json
├── blog/
│ ├── article.md
│ └── images/
├── x/
│ └── thread.json
└── summary.json
```
#### **Auto-Publish Blog (if enabled):**
```python
1. Parse frontmatter from blog markdown
2. Detect language (Thai 'th', English 'en')
3. Generate slug (Thai-friendly: use transliteration or keep Thai)
4. Save to: {website-repo}/src/content/blog/({lang})/{slug}.md
5. Copy images to: {website-repo}/public/images/blog/{slug}/
6. Git commit: git add . && git commit -m "Add blog post: {slug}"
7. Git push: git push origin main (triggers Easypanel auto-deploy)
8. Return deployment URL
```
---
## 📁 Output Examples
### **Facebook Post Output:**
```json
{
"channel": "facebook",
"topic": "บริการ podcast",
"language": "th",
"generated_at": "2026-03-08T14:30:00+07:00",
"variations": [
{
"id": "fb_post_1",
"primary_text": "คุณกำลังมองหาวิธีเริ่มต้น podcast ใช่ไหม? 🎙️\n\nตอนนี้ใครๆ ก็ทำ podcast ได้ง่ายๆ แค่มีเครื่องมือที่เหมาะสม เราช่วยคุณได้ตั้งแต่เริ่มจนถึงเผยแพร่\n\n#podcast #podcastไทย #สร้างpodcast",
"headline": "เริ่มต้น podcast ของคุณวันนี้",
"cta": "เรียนรู้เพิ่มเติม",
"hashtags": ["#podcast", "#podcastไทย", "#สร้างpodcast"],
"image": {
"type": "generated",
"path": "output/บริการ-podcast/facebook/images/variation_1.png",
"prompt": "Professional podcast studio setup with microphone and headphones, modern aesthetic, Thai-friendly design"
},
"api_ready": {
"message": "Matches Meta Graph API /act_id/adcreatives structure",
"endpoint": "POST /v18.0/act_{ad_account_id}/adcreatives"
}
}
]
}
```
### **Google Ads Output:**
```json
{
"channel": "google_ads",
"topic": "podcast hosting",
"language": "th",
"generated_at": "2026-03-08T14:30:00+07:00",
"responsive_search_ads": [
{
"id": "ga_rsa_1",
"headlines": [
{"text": "บริการ Podcast Hosting", "pin": false},
{"text": "เริ่มต้นฟรี 14 วัน", "pin": false},
{"text": "เผยแพร่ทุกแพลตฟอร์ม", "pin": false},
{"text": "ง่าย รวดเร็ว มืออาชีพ", "pin": false},
{"text": "รองรับภาษาไทย", "pin": false}
],
"descriptions": [
{"text": "แพลตฟอร์ม podcast ที่ครบวงจรที่สุด เริ่มต้นสร้าง podcast ของคุณวันนี้"},
{"text": "เผยแพร่ Apple Podcasts, Spotify, YouTube Music ได้ในคลิกเดียว"}
],
"keywords": ["podcast hosting", "host podcast", "บริการ podcast", "แพลตฟอร์ม podcast"],
"negative_keywords": ["ฟรี", "download", "mp3"],
"ad_extensions": {
"sitelinks": [
{"text": "เริ่มฟรี 14 วัน", "url": "/free-trial"},
{"text": "ดูคุณสมบัติ", "url": "/features"}
],
"callouts": ["รองรับภาษาไทย", "ทีมซัพพอร์ท 24/7", "ยกเลิกเมื่อไหร่ก็ได้"]
},
"api_ready": {
"matches": "Google Ads API v15.0",
"endpoint": "POST /google.ads.googleads.v15.services/GoogleAdsService:Mutate",
"resource": "AdGroupAd"
}
}
]
}
```
### **Blog Post Output:**
```markdown
---
title: "บริการ Podcast Hosting ที่ดีที่สุดปี 2026: คู่มือครบวงจร"
description: "เปรียบเทียบ 10+ บริการ podcast hosting พร้อมข้อมูลจริง ช่วยคุณเลือกแพลตฟอร์มที่เหมาะกับ podcast ของคุณ"
keywords: ["podcast hosting", "บริการ podcast", "แพลตฟอร์ม podcast", "host podcast"]
slug: podcast-hosting-best-2026
lang: th
category: guides
tags: [podcast, hosting, review]
created: 2026-03-08
images:
- src: /images/blog/podcast-hosting-best-2026/hero.png
alt: "เปรียบเทียบบริการ podcast hosting"
---
# บริการ Podcast Hosting ที่ดีที่สุดในปี 2026
คุณกำลังมองหาบริการ podcast hosting ที่ใช่อยู่ใช่ไหม? 🎙️
บทความนี้จะเปรียบเทียบแพลตฟอร์มยอดนิยม 10+ เจ้า พร้อมข้อมูลจริงจากการทดสอบ...
[Content continues for 2000+ words]
## สรุป
เลือกบริการ podcast hosting ที่เหมาะกับคุณที่สุด...
**พร้อมเริ่ม podcast ของคุณหรือยัง?** [สมัครฟรี 14 วัน →](/signup)
```
---
## 🔧 Technical Implementation
### **Thai Language Processing:**
```python
from pythainlp import word_tokenize, sent_tokenize
from pythainlp.util import normalize
def count_thai_words(text: str) -> int:
"""Count Thai words (no spaces between words)"""
tokens = word_tokenize(text, engine="newmm")
return len([t for t in tokens if t.strip() and not t.isspace()])
def calculate_thai_keyword_density(text: str, keyword: str) -> float:
"""Calculate keyword density for Thai text"""
text_normalized = normalize(text)
keyword_normalized = normalize(keyword)
count = text_normalized.count(keyword_normalized)
word_count = count_thai_words(text)
return (count / word_count * 100) if word_count > 0 else 0
def detect_content_language(text: str) -> str:
"""Detect if content is Thai or English"""
thai_chars = sum(1 for c in text if '\u0E00' <= c <= '\u0E7F')
total_chars = len(text)
thai_ratio = thai_chars / total_chars if total_chars > 0 else 0
if thai_ratio > 0.3:
return 'th'
return 'en'
```
### **Image Handling:**
```python
import os
import glob
from pathlib import Path
def find_product_images(product_name: str, website_repo: str) -> List[str]:
"""Find existing product images in website repo"""
extensions = ['.jpg', '.jpeg', '.png', '.webp']
found_images = []
search_patterns = [
f"**/*{product_name}*{{ext}}" for ext in extensions
] + [
f"public/images/**/*{{ext}}",
f"src/assets/**/*{{ext}}"
]
for pattern in search_patterns:
matches = glob.glob(os.path.join(website_repo, pattern), recursive=True)
found_images.extend(matches)
return found_images[:10] # Return top 10 matches
def save_image_for_channel(image_data: bytes, topic: str, channel: str) -> str:
"""Save generated/edited image to correct location"""
if channel == 'blog':
# Blog images go to website repo
output_dir = os.path.join(website_repo, 'public/images/blog', topic_slug)
else:
# Social/Ads images go to separate folder
output_dir = os.path.join('output', topic_slug, channel, 'images')
os.makedirs(output_dir, exist_ok=True)
image_path = os.path.join(output_dir, f"variation_{variation_num}.png")
with open(image_path, 'wb') as f:
f.write(image_data)
return image_path
```
### **Website-Creator Integration:**
```python
def publish_blog_to_astro(article_md: str, website_repo: str) -> Dict:
"""
Publish blog post to Astro content collections
Returns deployment status
"""
# Parse frontmatter
frontmatter = parse_frontmatter(article_md)
# Detect language
lang = detect_content_language(article_md)
# Generate slug
slug = generate_slug(frontmatter['title'], lang)
# Determine output path
output_path = os.path.join(
website_repo,
'src/content/blog',
f'({lang})',
f'{slug}.md'
)
# Ensure directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Write article
with open(output_path, 'w', encoding='utf-8') as f:
f.write(article_md)
# Copy images if any
if 'images' in frontmatter:
for img in frontmatter['images']:
# Copy from temp location to website repo
dest_path = os.path.join(website_repo, 'public', img['src'].lstrip('/'))
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
shutil.copy(img['local_path'], dest_path)
# Git commit and push
subprocess.run(['git', 'add', '.'], cwd=website_repo, check=True)
subprocess.run(['git', 'commit', '-m', f'Add blog post: {slug}'], cwd=website_repo, check=True)
subprocess.run(['git', 'push', 'origin', 'main'], cwd=website_repo, check=True)
# Return deployment info
return {
'published': True,
'slug': slug,
'language': lang,
'path': output_path,
'deployment_url': f"https://your-domain.com/blog/{slug}" if lang == 'en' else f"https://your-domain.com/th/{slug}"
}
```
---
## 📐 Channel Specifications
### **Facebook:**
- Primary text: 125-250 chars (Thai can be longer)
- Headline: 100 chars max
- Hashtags: 3-5 recommended
- Image: 1200x630 (1.91:1)
- Variations: 5
### **Facebook Ads:**
- Primary text: 125 chars recommended (5000 max)
- Headline: 40 chars
- Description: 90 chars
- CTA: Button selection
- Image: 1200x628 (1.91:1) or 1080x1080 (1:1)
- API ready: Yes (Meta Graph API)
### **Google Ads:**
- Headlines: 15 variations, 30 chars each
- Descriptions: 4 variations, 90 chars each
- Keywords: 15-20 suggested
- Negative keywords: 10-15 suggested
- Ad extensions: Sitelinks, callouts, structured snippets
- API ready: Yes (Google Ads API)
### **Blog:**
- Word count: 1500-3000 (Thai), 2000-3000 (English)
- Keyword density: 1.0-1.5% (Thai), 1.5-2% (English)
- Meta title: 50-60 chars
- Meta description: 150-160 chars
- Images: Saved to website repo
- Format: Markdown with frontmatter
- Astro ready: Yes (content collections)
### **X/Twitter:**
- Hook tweet: 280 chars
- Body tweets: 2-8 tweets, 280 chars each
- CTA tweet: 280 chars
- Hashtags: 2-3 per tweet
- Thread title: Optional
---
## ⚙️ Environment Variables
**Required (in unified .env or project .env):**
```bash
# Chutes AI (for image generation/editing)
CHUTES_API_TOKEN=your_token_here
# Google Analytics 4 (optional)
GA4_PROPERTY_ID=G-XXXXXXXXXX
GA4_CREDENTIALS_PATH=path/to/ga4-credentials.json
# Google Search Console (optional)
GSC_SITE_URL=https://yourdomain.com
GSC_CREDENTIALS_PATH=path/to/gsc-credentials.json
# DataForSEO (optional)
DATAFORSEO_LOGIN=your_login
DATAFORSEO_PASSWORD=your_password
# Umami Analytics (optional, if self-hosted)
UMAMI_API_URL=https://analytics.yourdomain.com
UMAMI_API_KEY=your_api_key
```
---
## 🚀 Commands
### **Generate Multi-Channel Content:**
```bash
python3 skills/seo-multi-channel/scripts/generate_content.py \
--topic "บริการ podcast hosting" \
--channels facebook facebook_ads google_ads blog x \
--website-repo ./my-website \
--auto-publish true
```
### **Generate for Specific Channel:**
```bash
# Facebook Ads only
python3 skills/seo-multi-channel/scripts/generate_content.py \
--topic "podcast microphone" \
--channels facebook_ads \
--product-name "PodMic Pro" \
--website-repo ./my-website
```
### **Publish Existing Blog:**
```bash
python3 skills/seo-multi-channel/scripts/publish_blog.py \
--article drafts/podcast-guide-2026.md \
--website-repo ./my-website
```
---
## 📊 Quality Scoring
Each piece of content is scored before output:
1. **Keyword Optimization** (0-25 points)
- Density, placement, variations
2. **Brand Voice Alignment** (0-25 points)
- Tone, terminology, style
3. **Channel Fit** (0-25 points)
- Length, format, CTA appropriateness
4. **Thai Language Quality** (0-25 points)
- Natural phrasing, formality level, no awkward translations
**Minimum score: 70/100** to publish. Below 70 → auto-revise or flag for review.
---
## ⚠️ Important Notes
1. **Thai Word Counting:** Thai has no spaces between words. Uses PyThaiNLP for accurate counting.
2. **Formality Detection:** Auto-detects from brand voice context. Defaults to casual for social, normal for blog.
3. **Image Handling:**
- Product content → Browse repo first → Ask user to confirm or provide
- Non-product → Ask user to provide image
- Blog images → Website repo
- Social/Ads images → Separate folder
4. **API Ready:** Output structures match Google Ads and Meta Ads API schemas for future integration.
5. **Data Services Optional:** Skips unconfigured services (GA4, GSC, DataForSEO, Umami).
6. **Per-Project Context:** Each website has its own context/ folder with brand voice, keywords, guidelines.
---
## 🔄 Integration with Other Skills
- **website-creator:** Blog posts published to Astro content collections
- **seo-analyzers:** Quality scoring and Thai language analysis
- **seo-data:** Performance data for content optimization
- **seo-context:** Context file management
---
## ✅ Success Criteria
- ✅ Content generated for all selected channels
- ✅ Thai language processing accurate (word count, keyword density)
- ✅ Product images found or user asked to provide
- ✅ Blog posts published to Astro (if enabled)
- ✅ Git commit + push successful (triggers auto-deploy)
- ✅ Output structures API-ready for future integration
- ✅ Quality scores ≥ 70/100 for all content
---
**Use this skill when you need to create multi-channel marketing content from a single topic with full Thai language support and automatic image handling.**