marketing-pipeline-share-automation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMarketing Pipeline Share Automation
营销流水线共享自动化
Skill by ara.so — Marketing Skills collection.
This skill enables AI coding agents to work with marketing-pipeline-share, a complete AI-powered content automation system that handles research (web crawling), script generation (Claude/OpenAI), Facebook auto-posting, and video rendering (Remotion). It's designed for marketers and content creators who want to automate their entire content workflow from keyword input to published video.
由ara.so提供的Skill —— 营销技能合集。
本Skill支持AI编码Agent使用marketing-pipeline-share这一完整的AI驱动内容自动化系统,该系统可处理调研(网页爬取)、脚本生成(Claude/OpenAI)、Facebook自动发布以及视频渲染(Remotion)。它专为希望实现从关键词输入到视频发布全内容工作流自动化的营销人员和内容创作者设计。
What This Project Does
本项目功能
Marketing Pipeline Share is a TypeScript/Next.js application that automates the complete content creation pipeline:
- Auto-Research: Crawls news sources (TechCrunch, a16z, Twitter, LinkedIn) for recent data
- AI Content Generation: Uses Claude 3 or OpenAI to generate articles in multiple formats (toplist, POV, case study, how-to)
- Multi-language Support: Generates content in both English and Vietnamese
- Auto-Posting: Publishes directly to Facebook pages
- Video Rendering: Converts content to videos/infographics using Remotion for Reels/TikTok/Shorts
Marketing Pipeline Share是一款TypeScript/Next.js应用,可自动化完整的内容创作流程:
- 自动调研:爬取新闻来源(TechCrunch、a16z、Twitter、LinkedIn)获取最新数据
- AI内容生成:使用Claude 3或OpenAI生成多种格式的文章(榜单、观点文、案例研究、教程)
- 多语言支持:生成英文和越南语内容
- 自动发布:直接发布至Facebook主页
- 视频渲染:通过Remotion将内容转换为短视频/信息图,适用于Reels/TikTok/Shorts
Installation
安装
bash
undefinedbash
undefinedClone the repository
Clone the repository
git clone https://github.com/pennydinh/marketing-pineline-share.git
cd marketing-pineline-share
git clone https://github.com/pennydinh/marketing-pineline-share.git
cd marketing-pineline-share
Install dependencies
Install dependencies
npm install
npm install
or
or
yarn install
yarn install
Copy environment template
Copy environment template
cp .env.example .env.local
undefinedcp .env.example .env.local
undefinedConfiguration
配置
Set up your environment variables in :
.env.localbash
undefined在中设置环境变量:
.env.localbash
undefinedAI Provider APIs
AI Provider APIs
ANTHROPIC_API_KEY=your_claude_api_key
OPENAI_API_KEY=your_openai_api_key
ANTHROPIC_API_KEY=your_claude_api_key
OPENAI_API_KEY=your_openai_api_key
Research/Crawling APIs
Research/Crawling APIs
RAPIDAPI_KEY=your_rapidapi_key
RAPIDAPI_HOST=news-api-host
RAPIDAPI_KEY=your_rapidapi_key
RAPIDAPI_HOST=news-api-host
Facebook Integration
Facebook Integration
FACEBOOK_PAGE_ACCESS_TOKEN=your_fb_page_token
FACEBOOK_PAGE_ID=your_page_id
FACEBOOK_PAGE_ACCESS_TOKEN=your_fb_page_token
FACEBOOK_PAGE_ID=your_page_id
Remotion Configuration
Remotion Configuration
REMOTION_CLOUDRUN_URL=your_cloudrun_url
REMOTION_API_KEY=your_remotion_key
REMOTION_CLOUDRUN_URL=your_cloudrun_url
REMOTION_API_KEY=your_remotion_key
Application
Application
NEXT_PUBLIC_APP_URL=http://localhost:3000
undefinedNEXT_PUBLIC_APP_URL=http://localhost:3000
undefinedKey Architecture
核心架构
typescript
// Typical project structure
src/
├── app/ # Next.js app directory
│ ├── api/ # API routes
│ │ ├── research/ # News crawling endpoints
│ │ ├── generate/ # AI content generation
│ │ ├── post/ # Facebook posting
│ │ └── render/ # Video rendering
│ └── dashboard/ # UI components
├── lib/
│ ├── ai/ # AI provider integrations
│ ├── crawlers/ # Web scraping utilities
│ ├── facebook/ # FB API integration
│ └── remotion/ # Video rendering logic
└── types/ # TypeScript definitionstypescript
// Typical project structure
src/
├── app/ # Next.js app directory
│ ├── api/ # API routes
│ │ ├── research/ # News crawling endpoints
│ │ ├── generate/ # AI content generation
│ │ ├── post/ # Facebook posting
│ │ └── render/ # Video rendering
│ └── dashboard/ # UI components
├── lib/
│ ├── ai/ # AI provider integrations
│ ├── crawlers/ # Web scraping utilities
│ ├── facebook/ # FB API integration
│ └── remotion/ # Video rendering logic
└── types/ # TypeScript definitionsCore Usage Patterns
核心使用模式
1. Research & Content Generation Flow
1. 调研与内容生成流程
typescript
// lib/ai/content-generator.ts
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';
export interface ContentRequest {
keyword: string;
format: 'toplist' | 'pov' | 'case-study' | 'how-to';
language: 'en' | 'vi';
tone: 'expert' | 'friendly' | 'humorous';
researchData?: any[];
}
export class ContentGenerator {
private claude: Anthropic;
private openai: OpenAI;
constructor() {
this.claude = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
this.openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
}
async generateWithClaude(request: ContentRequest): Promise<string> {
const prompt = this.buildPrompt(request);
const message = await this.claude.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 4096,
messages: [{
role: 'user',
content: prompt
}]
});
return message.content[0].type === 'text'
? message.content[0].text
: '';
}
async generateWithOpenAI(request: ContentRequest): Promise<string> {
const prompt = this.buildPrompt(request);
const completion = await this.openai.chat.completions.create({
model: 'gpt-4-turbo',
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.7,
});
return completion.choices[0].message.content || '';
}
private buildPrompt(request: ContentRequest): string {
const { keyword, format, language, tone, researchData } = request;
let prompt = `Create a ${format} article about "${keyword}" in ${language}.`;
prompt += `\nTone: ${tone}`;
if (researchData && researchData.length > 0) {
prompt += `\n\nRecent research data:\n`;
researchData.forEach((item, idx) => {
prompt += `${idx + 1}. ${item.title}\n${item.summary}\n`;
});
}
return prompt;
}
}typescript
// lib/ai/content-generator.ts
import Anthropic from '@anthropic-ai/sdk';
import OpenAI from 'openai';
export interface ContentRequest {
keyword: string;
format: 'toplist' | 'pov' | 'case-study' | 'how-to';
language: 'en' | 'vi';
tone: 'expert' | 'friendly' | 'humorous';
researchData?: any[];
}
export class ContentGenerator {
private claude: Anthropic;
private openai: OpenAI;
constructor() {
this.claude = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
this.openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
}
async generateWithClaude(request: ContentRequest): Promise<string> {
const prompt = this.buildPrompt(request);
const message = await this.claude.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 4096,
messages: [{
role: 'user',
content: prompt
}]
});
return message.content[0].type === 'text'
? message.content[0].text
: '';
}
async generateWithOpenAI(request: ContentRequest): Promise<string> {
const prompt = this.buildPrompt(request);
const completion = await this.openai.chat.completions.create({
model: 'gpt-4-turbo',
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.7,
});
return completion.choices[0].message.content || '';
}
private buildPrompt(request: ContentRequest): string {
const { keyword, format, language, tone, researchData } = request;
let prompt = `Create a ${format} article about "${keyword}" in ${language}.`;
prompt += `\nTone: ${tone}`;
if (researchData && researchData.length > 0) {
prompt += `\n\nRecent research data:\n`;
researchData.forEach((item, idx) => {
prompt += `${idx + 1}. ${item.title}\n${item.summary}\n`;
});
}
return prompt;
}
}2. News Crawling/Research System
2. 新闻爬取/调研系统
typescript
// lib/crawlers/news-crawler.ts
import axios from 'axios';
export interface NewsArticle {
title: string;
url: string;
source: string;
publishedAt: string;
summary: string;
}
export class NewsCrawler {
private rapidApiKey: string;
private rapidApiHost: string;
constructor() {
this.rapidApiKey = process.env.RAPIDAPI_KEY!;
this.rapidApiHost = process.env.RAPIDAPI_HOST!;
}
async crawlRecentNews(keyword: string, hours: number = 24): Promise<NewsArticle[]> {
try {
const response = await axios.get('https://api.rapidapi.com/v1/news/search', {
params: {
q: keyword,
from: this.getTimeRange(hours),
sortBy: 'publishedAt',
language: 'en'
},
headers: {
'X-RapidAPI-Key': this.rapidApiKey,
'X-RapidAPI-Host': this.rapidApiHost
}
});
return response.data.articles.map((article: any) => ({
title: article.title,
url: article.url,
source: article.source.name,
publishedAt: article.publishedAt,
summary: article.description || ''
}));
} catch (error) {
console.error('News crawling error:', error);
return [];
}
}
private getTimeRange(hours: number): string {
const date = new Date();
date.setHours(date.getHours() - hours);
return date.toISOString();
}
async crawlMultipleSources(keyword: string): Promise<NewsArticle[]> {
// Crawl from TechCrunch, a16z, etc.
const sources = ['techcrunch', 'a16z-blog'];
const allArticles: NewsArticle[] = [];
for (const source of sources) {
const articles = await this.crawlRecentNews(`${keyword} site:${source}`, 24);
allArticles.push(...articles);
}
return allArticles;
}
}typescript
// lib/crawlers/news-crawler.ts
import axios from 'axios';
export interface NewsArticle {
title: string;
url: string;
source: string;
publishedAt: string;
summary: string;
}
export class NewsCrawler {
private rapidApiKey: string;
private rapidApiHost: string;
constructor() {
this.rapidApiKey = process.env.RAPIDAPI_KEY!;
this.rapidApiHost = process.env.RAPIDAPI_HOST!;
}
async crawlRecentNews(keyword: string, hours: number = 24): Promise<NewsArticle[]> {
try {
const response = await axios.get('https://api.rapidapi.com/v1/news/search', {
params: {
q: keyword,
from: this.getTimeRange(hours),
sortBy: 'publishedAt',
language: 'en'
},
headers: {
'X-RapidAPI-Key': this.rapidApiKey,
'X-RapidAPI-Host': this.rapidApiHost
}
});
return response.data.articles.map((article: any) => ({
title: article.title,
url: article.url,
source: article.source.name,
publishedAt: article.publishedAt,
summary: article.description || ''
}));
} catch (error) {
console.error('News crawling error:', error);
return [];
}
}
private getTimeRange(hours: number): string {
const date = new Date();
date.setHours(date.getHours() - hours);
return date.toISOString();
}
async crawlMultipleSources(keyword: string): Promise<NewsArticle[]> {
// Crawl from TechCrunch, a16z, etc.
const sources = ['techcrunch', 'a16z-blog'];
const allArticles: NewsArticle[] = [];
for (const source of sources) {
const articles = await this.crawlRecentNews(`${keyword} site:${source}`, 24);
allArticles.push(...articles);
}
return allArticles;
}
}3. API Route Example - Generate Content
3. API路由示例 - 生成内容
typescript
// app/api/generate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ContentGenerator } from '@/lib/ai/content-generator';
import { NewsCrawler } from '@/lib/crawlers/news-crawler';
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { keyword, format, language, tone, useResearch, aiProvider } = body;
let researchData = [];
if (useResearch) {
const crawler = new NewsCrawler();
researchData = await crawler.crawlMultipleSources(keyword);
}
const generator = new ContentGenerator();
let content: string;
if (aiProvider === 'claude') {
content = await generator.generateWithClaude({
keyword,
format,
language,
tone,
researchData
});
} else {
content = await generator.generateWithOpenAI({
keyword,
format,
language,
tone,
researchData
});
}
return NextResponse.json({
success: true,
content,
researchCount: researchData.length
});
} catch (error: any) {
return NextResponse.json(
{ success: false, error: error.message },
{ status: 500 }
);
}
}typescript
// app/api/generate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ContentGenerator } from '@/lib/ai/content-generator';
import { NewsCrawler } from '@/lib/crawlers/news-crawler';
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { keyword, format, language, tone, useResearch, aiProvider } = body;
let researchData = [];
if (useResearch) {
const crawler = new NewsCrawler();
researchData = await crawler.crawlMultipleSources(keyword);
}
const generator = new ContentGenerator();
let content: string;
if (aiProvider === 'claude') {
content = await generator.generateWithClaude({
keyword,
format,
language,
tone,
researchData
});
} else {
content = await generator.generateWithOpenAI({
keyword,
format,
language,
tone,
researchData
});
}
return NextResponse.json({
success: true,
content,
researchCount: researchData.length
});
} catch (error: any) {
return NextResponse.json(
{ success: false, error: error.message },
{ status: 500 }
);
}
}4. Facebook Auto-Posting
4. Facebook自动发布
typescript
// lib/facebook/post-manager.ts
import axios from 'axios';
export interface FacebookPost {
message: string;
link?: string;
imageUrl?: string;
}
export class FacebookPostManager {
private pageAccessToken: string;
private pageId: string;
constructor() {
this.pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
this.pageId = process.env.FACEBOOK_PAGE_ID!;
}
async publishPost(post: FacebookPost): Promise<string> {
try {
const endpoint = `https://graph.facebook.com/v18.0/${this.pageId}/feed`;
const response = await axios.post(endpoint, {
message: post.message,
link: post.link,
access_token: this.pageAccessToken
});
return response.data.id;
} catch (error: any) {
throw new Error(`Facebook posting failed: ${error.response?.data?.error?.message || error.message}`);
}
}
async publishPhoto(post: FacebookPost): Promise<string> {
if (!post.imageUrl) {
throw new Error('Image URL required for photo post');
}
const endpoint = `https://graph.facebook.com/v18.0/${this.pageId}/photos`;
const response = await axios.post(endpoint, {
url: post.imageUrl,
caption: post.message,
access_token: this.pageAccessToken
});
return response.data.id;
}
async schedulePost(post: FacebookPost, publishTime: Date): Promise<string> {
const endpoint = `https://graph.facebook.com/v18.0/${this.pageId}/feed`;
const response = await axios.post(endpoint, {
message: post.message,
link: post.link,
published: false,
scheduled_publish_time: Math.floor(publishTime.getTime() / 1000),
access_token: this.pageAccessToken
});
return response.data.id;
}
}typescript
// lib/facebook/post-manager.ts
import axios from 'axios';
export interface FacebookPost {
message: string;
link?: string;
imageUrl?: string;
}
export class FacebookPostManager {
private pageAccessToken: string;
private pageId: string;
constructor() {
this.pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
this.pageId = process.env.FACEBOOK_PAGE_ID!;
}
async publishPost(post: FacebookPost): Promise<string> {
try {
const endpoint = `https://graph.facebook.com/v18.0/${this.pageId}/feed`;
const response = await axios.post(endpoint, {
message: post.message,
link: post.link,
access_token: this.pageAccessToken
});
return response.data.id;
} catch (error: any) {
throw new Error(`Facebook posting failed: ${error.response?.data?.error?.message || error.message}`);
}
}
async publishPhoto(post: FacebookPost): Promise<string> {
if (!post.imageUrl) {
throw new Error('Image URL required for photo post');
}
const endpoint = `https://graph.facebook.com/v18.0/${this.pageId}/photos`;
const response = await axios.post(endpoint, {
url: post.imageUrl,
caption: post.message,
access_token: this.pageAccessToken
});
return response.data.id;
}
async schedulePost(post: FacebookPost, publishTime: Date): Promise<string> {
const endpoint = `https://graph.facebook.com/v18.0/${this.pageId}/feed`;
const response = await axios.post(endpoint, {
message: post.message,
link: post.link,
published: false,
scheduled_publish_time: Math.floor(publishTime.getTime() / 1000),
access_token: this.pageAccessToken
});
return response.data.id;
}
}5. Video Rendering with Remotion
5. 基于Remotion的视频渲染
typescript
// lib/remotion/video-renderer.ts
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import { webpackOverride } from './webpack-override';
export interface VideoConfig {
title: string;
content: string[];
duration: number;
format: 'reels' | 'tiktok' | 'shorts';
}
export class VideoRenderer {
async renderContentVideo(config: VideoConfig): Promise<string> {
const bundled = await bundle({
entryPoint: './src/remotion/index.ts',
webpackOverride: webpackOverride,
});
const composition = await selectComposition({
serveUrl: bundled,
id: 'ContentVideo',
inputProps: {
title: config.title,
content: config.content,
format: config.format
},
});
const outputLocation = `./output/video-${Date.now()}.mp4`;
await renderMedia({
composition,
serveUrl: bundled,
codec: 'h264',
outputLocation,
inputProps: {
title: config.title,
content: config.content,
format: config.format
},
});
return outputLocation;
}
getAspectRatio(format: string): { width: number; height: number } {
switch (format) {
case 'reels':
case 'tiktok':
case 'shorts':
return { width: 1080, height: 1920 }; // 9:16
default:
return { width: 1920, height: 1080 }; // 16:9
}
}
}typescript
// lib/remotion/video-renderer.ts
import { bundle } from '@remotion/bundler';
import { renderMedia, selectComposition } from '@remotion/renderer';
import { webpackOverride } from './webpack-override';
export interface VideoConfig {
title: string;
content: string[];
duration: number;
format: 'reels' | 'tiktok' | 'shorts';
}
export class VideoRenderer {
async renderContentVideo(config: VideoConfig): Promise<string> {
const bundled = await bundle({
entryPoint: './src/remotion/index.ts',
webpackOverride: webpackOverride,
});
const composition = await selectComposition({
serveUrl: bundled,
id: 'ContentVideo',
inputProps: {
title: config.title,
content: config.content,
format: config.format
},
});
const outputLocation = `./output/video-${Date.now()}.mp4`;
await renderMedia({
composition,
serveUrl: bundled,
codec: 'h264',
outputLocation,
inputProps: {
title: config.title,
content: config.content,
format: config.format
},
});
return outputLocation;
}
getAspectRatio(format: string): { width: number; height: number } {
switch (format) {
case 'reels':
case 'tiktok':
case 'shorts':
return { width: 1080, height: 1920 }; // 9:16
default:
return { width: 1920, height: 1080 }; // 16:9
}
}
}6. Complete Pipeline Example
6. 完整流水线示例
typescript
// lib/pipeline/content-pipeline.ts
import { ContentGenerator } from '@/lib/ai/content-generator';
import { NewsCrawler } from '@/lib/crawlers/news-crawler';
import { FacebookPostManager } from '@/lib/facebook/post-manager';
import { VideoRenderer } from '@/lib/remotion/video-renderer';
export class ContentPipeline {
private generator: ContentGenerator;
private crawler: NewsCrawler;
private fbManager: FacebookPostManager;
private videoRenderer: VideoRenderer;
constructor() {
this.generator = new ContentGenerator();
this.crawler = new NewsCrawler();
this.fbManager = new FacebookPostManager();
this.videoRenderer = new VideoRenderer();
}
async executeFullPipeline(keyword: string) {
console.log('Step 1: Researching...');
const researchData = await this.crawler.crawlMultipleSources(keyword);
console.log('Step 2: Generating content...');
const content = await this.generator.generateWithClaude({
keyword,
format: 'toplist',
language: 'en',
tone: 'expert',
researchData
});
console.log('Step 3: Rendering video...');
const videoPath = await this.videoRenderer.renderContentVideo({
title: keyword,
content: content.split('\n').filter(line => line.trim()),
duration: 30,
format: 'reels'
});
console.log('Step 4: Posting to Facebook...');
const postId = await this.fbManager.publishPost({
message: `New content about ${keyword}!`,
link: `https://example.com/videos/${videoPath}`
});
return {
success: true,
researchCount: researchData.length,
content,
videoPath,
postId
};
}
}typescript
// lib/pipeline/content-pipeline.ts
import { ContentGenerator } from '@/lib/ai/content-generator';
import { NewsCrawler } from '@/lib/crawlers/news-crawler';
import { FacebookPostManager } from '@/lib/facebook/post-manager';
import { VideoRenderer } from '@/lib/remotion/video-renderer';
export class ContentPipeline {
private generator: ContentGenerator;
private crawler: NewsCrawler;
private fbManager: FacebookPostManager;
private videoRenderer: VideoRenderer;
constructor() {
this.generator = new ContentGenerator();
this.crawler = new NewsCrawler();
this.fbManager = new FacebookPostManager();
this.videoRenderer = new VideoRenderer();
}
async executeFullPipeline(keyword: string) {
console.log('Step 1: Researching...');
const researchData = await this.crawler.crawlMultipleSources(keyword);
console.log('Step 2: Generating content...');
const content = await this.generator.generateWithClaude({
keyword,
format: 'toplist',
language: 'en',
tone: 'expert',
researchData
});
console.log('Step 3: Rendering video...');
const videoPath = await this.videoRenderer.renderContentVideo({
title: keyword,
content: content.split('\n').filter(line => line.trim()),
duration: 30,
format: 'reels'
});
console.log('Step 4: Posting to Facebook...');
const postId = await this.fbManager.publishPost({
message: `New content about ${keyword}!`,
link: `https://example.com/videos/${videoPath}`
});
return {
success: true,
researchCount: researchData.length,
content,
videoPath,
postId
};
}
}7. API Route - Full Pipeline
7. API路由 - 完整流水线
typescript
// app/api/pipeline/execute/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ContentPipeline } from '@/lib/pipeline/content-pipeline';
export async function POST(request: NextRequest) {
try {
const { keyword } = await request.json();
if (!keyword) {
return NextResponse.json(
{ success: false, error: 'Keyword is required' },
{ status: 400 }
);
}
const pipeline = new ContentPipeline();
const result = await pipeline.executeFullPipeline(keyword);
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json(
{ success: false, error: error.message },
{ status: 500 }
);
}
}typescript
// app/api/pipeline/execute/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ContentPipeline } from '@/lib/pipeline/content-pipeline';
export async function POST(request: NextRequest) {
try {
const { keyword } = await request.json();
if (!keyword) {
return NextResponse.json(
{ success: false, error: 'Keyword is required' },
{ status: 400 }
);
}
const pipeline = new ContentPipeline();
const result = await pipeline.executeFullPipeline(keyword);
return NextResponse.json(result);
} catch (error: any) {
return NextResponse.json(
{ success: false, error: error.message },
{ status: 500 }
);
}
}Running the Application
运行应用
bash
undefinedbash
undefinedDevelopment
Development
npm run dev
npm run dev
Production build
Production build
npm run build
npm start
npm run build
npm start
Run specific pipeline steps
Run specific pipeline steps
npm run research -- --keyword "AI trends"
npm run generate -- --keyword "AI trends" --format toplist
npm run render -- --input content.json
undefinednpm run research -- --keyword "AI trends"
npm run generate -- --keyword "AI trends" --format toplist
npm run render -- --input content.json
undefinedCommon Workflows
常见工作流
Workflow 1: Research-Only Mode
工作流1:仅调研模式
typescript
// Quick research without content generation
import { NewsCrawler } from '@/lib/crawlers/news-crawler';
const crawler = new NewsCrawler();
const articles = await crawler.crawlRecentNews('AI marketing', 48);
console.log(`Found ${articles.length} articles`);typescript
// Quick research without content generation
import { NewsCrawler } from '@/lib/crawlers/news-crawler';
const crawler = new NewsCrawler();
const articles = await crawler.crawlRecentNews('AI marketing', 48);
console.log(`Found ${articles.length} articles`);Workflow 2: Multi-Language Content
工作流2:多语言内容
typescript
// Generate content in both languages
const generator = new ContentGenerator();
const englishContent = await generator.generateWithClaude({
keyword: 'Marketing automation',
format: 'how-to',
language: 'en',
tone: 'expert'
});
const vietnameseContent = await generator.generateWithClaude({
keyword: 'Marketing automation',
format: 'how-to',
language: 'vi',
tone: 'friendly'
});typescript
// Generate content in both languages
const generator = new ContentGenerator();
const englishContent = await generator.generateWithClaude({
keyword: 'Marketing automation',
format: 'how-to',
language: 'en',
tone: 'expert'
});
const vietnameseContent = await generator.generateWithClaude({
keyword: 'Marketing automation',
format: 'how-to',
language: 'vi',
tone: 'friendly'
});Workflow 3: Scheduled Posting
工作流3:定时发布
typescript
// Schedule content for future posting
const fbManager = new FacebookPostManager();
const publishTime = new Date();
publishTime.setHours(publishTime.getHours() + 24);
const postId = await fbManager.schedulePost({
message: generatedContent,
link: 'https://example.com/article'
}, publishTime);typescript
// Schedule content for future posting
const fbManager = new FacebookPostManager();
const publishTime = new Date();
publishTime.setHours(publishTime.getHours() + 24);
const postId = await fbManager.schedulePost({
message: generatedContent,
link: 'https://example.com/article'
}, publishTime);Troubleshooting
故障排查
API Key Issues
API密钥问题
typescript
// Verify API keys are loaded
if (!process.env.ANTHROPIC_API_KEY) {
throw new Error('ANTHROPIC_API_KEY not found in environment');
}
// Test API connection
const testConnection = async () => {
try {
const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
await claude.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 10,
messages: [{ role: 'user', content: 'test' }]
});
console.log('✓ Claude API connected');
} catch (error) {
console.error('✗ Claude API failed:', error);
}
};typescript
// Verify API keys are loaded
if (!process.env.ANTHROPIC_API_KEY) {
throw new Error('ANTHROPIC_API_KEY not found in environment');
}
// Test API connection
const testConnection = async () => {
try {
const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
await claude.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 10,
messages: [{ role: 'user', content: 'test' }]
});
console.log('✓ Claude API connected');
} catch (error) {
console.error('✗ Claude API failed:', error);
}
};Rate Limiting
速率限制
typescript
// Implement retry logic with exponential backoff
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error.status === 429 && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}typescript
// Implement retry logic with exponential backoff
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error.status === 429 && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}Facebook Posting Errors
Facebook发布错误
typescript
// Debug Facebook API errors
try {
await fbManager.publishPost(post);
} catch (error: any) {
if (error.message.includes('access token')) {
console.error('Facebook token expired or invalid');
// Refresh token logic here
} else if (error.message.includes('rate limit')) {
console.error('Facebook rate limit reached');
// Queue for later
} else {
console.error('Unknown Facebook error:', error);
}
}typescript
// Debug Facebook API errors
try {
await fbManager.publishPost(post);
} catch (error: any) {
if (error.message.includes('access token')) {
console.error('Facebook token expired or invalid');
// Refresh token logic here
} else if (error.message.includes('rate limit')) {
console.error('Facebook rate limit reached');
// Queue for later
} else {
console.error('Unknown Facebook error:', error);
}
}Video Rendering Issues
视频渲染问题
typescript
// Check Remotion dependencies
import { getCompositions } from '@remotion/renderer';
const checkRemotionSetup = async () => {
try {
const compositions = await getCompositions('./src/remotion/index.ts');
console.log('Available compositions:', compositions.map(c => c.id));
} catch (error) {
console.error('Remotion setup error:', error);
}
};This skill enables complete automation of content marketing workflows from research through video publication using modern AI tools and TypeScript.
typescript
// Check Remotion dependencies
import { getCompositions } from '@remotion/renderer';
const checkRemotionSetup = async () => {
try {
const compositions = await getCompositions('./src/remotion/index.ts');
console.log('Available compositions:', compositions.map(c => c.id));
} catch (error) {
console.error('Remotion setup error:', error);
}
};本Skill借助现代AI工具和TypeScript,实现了从调研到视频发布的完整营销内容工作流自动化。