Files
claude-skills-reference/marketing-skill/x-twitter-growth/scripts/content_planner.py
Leo 66dcf674c5 feat(marketing): add x-twitter-growth skill with 5 Python tools
- Profile auditor (bio quality, posting patterns, growth readiness)
- Tweet composer (hooks, threads, validation, 30+ proven patterns)
- Content planner (weekly calendars with format mix)
- Competitor analyzer (competitive intel via data import)
- Growth tracker (snapshot-based progress tracking + milestone projection)
- Algorithm reference doc (ranking signals, timing, format performance)
- 226-line SKILL.md with practical playbook (no fluff)
- Security audit: PASS (0 findings)
2026-03-10 17:52:02 +01:00

211 lines
7.2 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
X/Twitter Content Planner — Generate weekly posting calendars.
Creates structured content plans with topic suggestions, format mix,
optimal posting times, and engagement targets.
Usage:
python3 content_planner.py --niche "AI engineering" --frequency 5 --weeks 2
python3 content_planner.py --niche "SaaS growth" --frequency 3 --weeks 1 --json
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from dataclasses import dataclass, field, asdict
CONTENT_FORMATS = {
"atomic_tweet": {"growth_weight": 0.3, "effort": "low", "description": "Single tweet — observation, tip, or hot take"},
"thread": {"growth_weight": 0.35, "effort": "high", "description": "5-12 tweet deep dive — highest reach potential"},
"question": {"growth_weight": 0.15, "effort": "low", "description": "Engagement bait — drives replies"},
"quote_tweet": {"growth_weight": 0.10, "effort": "low", "description": "Add value to someone else's content"},
"reply_session": {"growth_weight": 0.10, "effort": "medium", "description": "30 min focused engagement on target accounts"},
}
OPTIMAL_TIMES = {
"weekday": ["07:00-08:00", "12:00-13:00", "17:00-18:00", "20:00-21:00"],
"weekend": ["09:00-10:00", "14:00-15:00", "19:00-20:00"],
}
TOPIC_ANGLES = [
"Lessons learned (personal experience)",
"Framework/system breakdown",
"Tool recommendation (with honest take)",
"Myth busting (challenge common belief)",
"Behind the scenes (process, workflow)",
"Industry trend analysis",
"Beginner guide (explain like I'm 5)",
"Comparison (X vs Y — which is better?)",
"Prediction (what's coming next)",
"Case study (real example with numbers)",
"Mistake I made (vulnerability + lesson)",
"Quick tip (tactical, immediately useful)",
"Controversial take (spicy but defensible)",
"Curated list (best resources, tools, accounts)",
]
@dataclass
class DayPlan:
date: str
day_of_week: str
posts: list = field(default_factory=list)
engagement_target: str = ""
@dataclass
class PostSlot:
time: str
format: str
topic_angle: str
topic_suggestion: str
notes: str = ""
@dataclass
class WeekPlan:
week_number: int
start_date: str
end_date: str
days: list = field(default_factory=list)
thread_count: int = 0
total_posts: int = 0
focus_theme: str = ""
def generate_plan(niche: str, posts_per_day: int, weeks: int, start_date: datetime) -> list:
plans = []
angle_idx = 0
time_idx = 0
for week in range(weeks):
week_start = start_date + timedelta(weeks=week)
week_end = week_start + timedelta(days=6)
week_plan = WeekPlan(
week_number=week + 1,
start_date=week_start.strftime("%Y-%m-%d"),
end_date=week_end.strftime("%Y-%m-%d"),
focus_theme=TOPIC_ANGLES[week % len(TOPIC_ANGLES)],
)
for day in range(7):
current = week_start + timedelta(days=day)
day_name = current.strftime("%A")
is_weekend = day >= 5
times = OPTIMAL_TIMES["weekend" if is_weekend else "weekday"]
actual_posts = max(1, posts_per_day - (1 if is_weekend else 0))
day_plan = DayPlan(
date=current.strftime("%Y-%m-%d"),
day_of_week=day_name,
engagement_target="15 min reply session" if is_weekend else "30 min reply session",
)
for p in range(actual_posts):
# Determine format based on day position
if day in [1, 3] and p == 0: # Tue/Thu first slot = thread
fmt = "thread"
elif p == actual_posts - 1 and not is_weekend:
fmt = "question" # Last post = engagement driver
elif day == 4 and p == 0: # Friday first = quote tweet
fmt = "quote_tweet"
else:
fmt = "atomic_tweet"
angle = TOPIC_ANGLES[angle_idx % len(TOPIC_ANGLES)]
angle_idx += 1
slot = PostSlot(
time=times[p % len(times)],
format=fmt,
topic_angle=angle,
topic_suggestion=f"{angle} about {niche}",
notes="Pin if performs well" if fmt == "thread" else "",
)
day_plan.posts.append(asdict(slot))
if fmt == "thread":
week_plan.thread_count += 1
week_plan.total_posts += 1
week_plan.days.append(asdict(day_plan))
plans.append(asdict(week_plan))
return plans
def print_plan(plans: list, niche: str):
print(f"\n{'='*70}")
print(f" X/TWITTER CONTENT PLAN — {niche.upper()}")
print(f"{'='*70}")
for week in plans:
print(f"\n WEEK {week['week_number']} ({week['start_date']} to {week['end_date']})")
print(f" Theme: {week['focus_theme']}")
print(f" Posts: {week['total_posts']} | Threads: {week['thread_count']}")
print(f" {''*66}")
for day in week['days']:
print(f"\n {day['day_of_week']:9} {day['date']}")
for post in day['posts']:
fmt_icon = {
"thread": "🧵",
"atomic_tweet": "💬",
"question": "",
"quote_tweet": "🔄",
"reply_session": "💬",
}.get(post['format'], "📝")
print(f" {fmt_icon} {post['time']:12} [{post['format']:<14}] {post['topic_angle']}")
if post['notes']:
print(f" {post['notes']}")
print(f" 📊 Engagement: {day['engagement_target']}")
print(f"\n{'='*70}")
print(f" WEEKLY TARGETS")
print(f" • Reply to 10+ accounts in your niche daily")
print(f" • Quote tweet 2-3 relevant posts per week")
print(f" • Update pinned tweet if a thread outperforms current pin")
print(f" • Review analytics every Sunday — double down on what works")
print(f"{'='*70}\n")
def main():
parser = argparse.ArgumentParser(
description="Generate X/Twitter content calendars",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--niche", required=True, help="Your content niche")
parser.add_argument("--frequency", type=int, default=3, help="Posts per day (default: 3)")
parser.add_argument("--weeks", type=int, default=2, help="Weeks to plan (default: 2)")
parser.add_argument("--start", default="", help="Start date YYYY-MM-DD (default: next Monday)")
parser.add_argument("--json", action="store_true", help="Output JSON")
args = parser.parse_args()
if args.start:
start = datetime.strptime(args.start, "%Y-%m-%d")
else:
today = datetime.now()
days_until_monday = (7 - today.weekday()) % 7
if days_until_monday == 0:
days_until_monday = 7
start = today + timedelta(days=days_until_monday)
plans = generate_plan(args.niche, args.frequency, args.weeks, start)
if args.json:
print(json.dumps(plans, indent=2))
else:
print_plan(plans, args.niche)
if __name__ == "__main__":
main()