oyi77

content-publisher

Automates drafting and publishing articles to Substack and Medium with Apify and Notion integration

oyi77 1 Updated 3mo ago

Resources

2
GitHub

Install

npx skillscat add oyi77/1ai-skills/content-publisher

Install via the SkillsCat registry.

SKILL.md

Content Publisher Agent

Automates drafting and publishing articles to Substack and Medium with workflow automation.

Required Tools

MCP Servers

Apify MCP (Publishing Automation)

{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/mcp-server"],
      "env": { "APIFY_API_TOKEN": "${APIFY_API_TOKEN}" }
    },
    "notion": {
      "command": "npx",
      "args": ["-y", "@makenotion/mcp-server"],
      "env": { "NOTION_API_KEY": "${NOTION_API_KEY}" }
    },
    "slack": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-slack"],
      "env": { "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}" }
    }
  }
}

Tool Permissions

Tool Capabilities
Bash(apify:*) Execute browser automation for publishing
MCP(apify:*) Run web scraping/automation actors
MCP(notion:*) Store editorial calendar, track drafts
MCP(slack:*) Send approval notifications

Authentication

Setup

  1. Apify Token

    export APIFY_API_TOKEN="your-token"
  2. Notion Integration

  3. Slack (optional)

    export SLACK_BOT_TOKEN="xoxb-your-token"

Capabilities

  • Drafting: Turn ideas into structured Markdown drafts
  • Publishing: Navigate to platform editors and publish
  • Cross-Posting: Sync content between platforms
  • Scheduling: Schedule posts for future publication
  • Editorial Calendar: Track in Notion

Pseudo Code

Example 1: Draft Article

// 1. Analyze request
const topic = "The impact of Agentic AI on coding";
const audience = "developers";
const tone = "professional";

// 2. Research if needed
const context = await apify.actor("apify/firecrawl-scraper", {
  urls: [`https://news.ycombinator.com/?q=${topic}`]
});

// 3. Generate content
const draft = await generateArticle({ topic, audience, tone, context });

// 4. Save to drafts folder
const filename = `memory/drafts/${dateSlug(topic)}.md`;
await fs.write(filename, draft);

// 5. Log to Notion editorial calendar
await notion.createPage("Editorial Calendar", {
  title: draft.title,
  status: "Draft",
  scheduledDate: null,
  platform: "both"
});

Example 2: Publish to Platform

// 1. Read draft
const content = await fs.read("memory/drafts/agentic-ai.md");

// 2. Navigate to platform
await browser.goto("https://medium.com/new-post");

// 3. Fill content
await browser.fill(".title-input", content.title);
await browser.fill(".body-editor", content.body);

// 4. Add tags
for (const tag of content.tags) {
  await browser.click(".tag-input");
  await browser.type(tag);
}

// 5. Publish or save draft
if (mode === "live") {
  await browser.click(".publish-button");
  await slack.notify("#content", `Published: ${content.title}`);
} else {
  await browser.click(".save-draft");
}

Example 3: Cross-Post to Both Platforms

// 1. Read content
const content = await fs.read(draftPath);

// 2. Convert for each platform
const mediumContent = convertToMedium(content);
const substackContent = convertToSubstack(content);

// 3. Publish to Medium
await publishToMedium(mediumContent);

// 4. Publish to Substack
await publishToSubstack(substackContent);

// 5. Update Notion
await notion.updatePage(draftId, { status: "Published" });

CLI Reference

Command Description
draft "topic" Generate article draft
publish <file> <platform> Publish to platform
publish <file> <platform> live Publish live
schedule <file> <platform> <datetime> Schedule post

Error Handling

Error Code Meaning Fix
AUTH_001 Not logged in Check credentials, re-login
PUBLISH_001 Platform changed UI Update selectors
RATE_001 Rate limited Wait and retry
VALIDATE_001 Content validation failed Check format

Common Patterns

Pattern: Dry-Run Publishing

async function publishWithDryRun(content, platform, dryRun = true) {
  // Validate content
  if (!content.title || !content.body) {
    throw new Error("Missing required fields");
  }

  // Preview
  console.log("=== PREVIEW ===");
  console.log(`Title: ${content.title}`);
  console.log(`Platform: ${platform}`);
  
  if (dryRun) {
    console.log("DRY RUN - No actual publishing");
    return { status: "preview" };
  }

  // Actually publish
  return await platform.publish(content);
}

Skill v2.0 - Content Publisher with MCP