crier 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. crier-0.2.0/.github/workflows/docs.yml +30 -0
  2. crier-0.2.0/.github/workflows/publish.yml +90 -0
  3. crier-0.2.0/.gitignore +47 -0
  4. crier-0.2.0/CLAUDE.md +85 -0
  5. crier-0.2.0/PKG-INFO +227 -0
  6. crier-0.2.0/README.md +194 -0
  7. crier-0.2.0/docs/getting-started.md +107 -0
  8. crier-0.2.0/docs/guides/backfill.md +125 -0
  9. crier-0.2.0/docs/guides/configuration.md +122 -0
  10. crier-0.2.0/docs/guides/github-action.md +235 -0
  11. crier-0.2.0/docs/guides/tracking.md +82 -0
  12. crier-0.2.0/docs/index.md +65 -0
  13. crier-0.2.0/docs/platforms/bluesky.md +53 -0
  14. crier-0.2.0/docs/platforms/buttondown.md +16 -0
  15. crier-0.2.0/docs/platforms/devto.md +51 -0
  16. crier-0.2.0/docs/platforms/discord.md +16 -0
  17. crier-0.2.0/docs/platforms/ghost.md +16 -0
  18. crier-0.2.0/docs/platforms/hashnode.md +16 -0
  19. crier-0.2.0/docs/platforms/index.md +76 -0
  20. crier-0.2.0/docs/platforms/linkedin.md +16 -0
  21. crier-0.2.0/docs/platforms/mastodon.md +16 -0
  22. crier-0.2.0/docs/platforms/medium.md +16 -0
  23. crier-0.2.0/docs/platforms/telegram.md +16 -0
  24. crier-0.2.0/docs/platforms/threads.md +16 -0
  25. crier-0.2.0/docs/platforms/twitter.md +16 -0
  26. crier-0.2.0/docs/platforms/wordpress.md +16 -0
  27. crier-0.2.0/docs/reference/cli.md +336 -0
  28. crier-0.2.0/docs/reference/front-matter.md +110 -0
  29. crier-0.2.0/mkdocs.yml +55 -0
  30. crier-0.2.0/pyproject.toml +60 -0
  31. crier-0.2.0/src/crier/__init__.py +3 -0
  32. crier-0.2.0/src/crier/cli.py +1078 -0
  33. crier-0.2.0/src/crier/config.py +114 -0
  34. crier-0.2.0/src/crier/converters/__init__.py +5 -0
  35. crier-0.2.0/src/crier/converters/markdown.py +70 -0
  36. crier-0.2.0/src/crier/platforms/__init__.py +63 -0
  37. crier-0.2.0/src/crier/platforms/base.py +65 -0
  38. crier-0.2.0/src/crier/platforms/bluesky.py +196 -0
  39. crier-0.2.0/src/crier/platforms/buttondown.py +136 -0
  40. crier-0.2.0/src/crier/platforms/devto.py +130 -0
  41. crier-0.2.0/src/crier/platforms/discord.py +140 -0
  42. crier-0.2.0/src/crier/platforms/ghost.py +205 -0
  43. crier-0.2.0/src/crier/platforms/hashnode.py +245 -0
  44. crier-0.2.0/src/crier/platforms/linkedin.py +161 -0
  45. crier-0.2.0/src/crier/platforms/mastodon.py +173 -0
  46. crier-0.2.0/src/crier/platforms/medium.py +104 -0
  47. crier-0.2.0/src/crier/platforms/telegram.py +166 -0
  48. crier-0.2.0/src/crier/platforms/threads.py +175 -0
  49. crier-0.2.0/src/crier/platforms/twitter.py +97 -0
  50. crier-0.2.0/src/crier/platforms/wordpress.py +186 -0
  51. crier-0.2.0/src/crier/registry.py +219 -0
@@ -0,0 +1,30 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ paths:
7
+ - 'docs/**'
8
+ - 'mkdocs.yml'
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: write
13
+
14
+ jobs:
15
+ deploy:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: '3.12'
24
+
25
+ - name: Install dependencies
26
+ run: |
27
+ pip install mkdocs-material
28
+
29
+ - name: Deploy to GitHub Pages
30
+ run: mkdocs gh-deploy --force
@@ -0,0 +1,90 @@
1
+ # Crier Auto-Publish Workflow
2
+ #
3
+ # Copy this to your content repo at .github/workflows/crier-publish.yml
4
+ # Then set up the required secrets in your repo settings.
5
+ #
6
+ # Required secrets (set in GitHub repo Settings > Secrets):
7
+ # CRIER_DEVTO_API_KEY - Your dev.to API key
8
+ # CRIER_BLUESKY_API_KEY - handle.bsky.social:app-password
9
+ # CRIER_MASTODON_API_KEY - instance.social:access-token
10
+ # (add others as needed)
11
+
12
+ name: Auto-Publish Content
13
+
14
+ on:
15
+ push:
16
+ branches: [main, master]
17
+ paths:
18
+ - 'posts/**/*.md'
19
+ - 'content/**/*.md'
20
+ workflow_dispatch: # Allow manual trigger
21
+
22
+ jobs:
23
+ publish:
24
+ runs-on: ubuntu-latest
25
+
26
+ steps:
27
+ - name: Checkout repository
28
+ uses: actions/checkout@v4
29
+ with:
30
+ fetch-depth: 0 # Full history for registry
31
+
32
+ - name: Set up Python
33
+ uses: actions/setup-python@v5
34
+ with:
35
+ python-version: '3.12'
36
+
37
+ - name: Install Crier
38
+ run: pip install crier
39
+
40
+ - name: Configure platforms
41
+ env:
42
+ CRIER_DEVTO_API_KEY: ${{ secrets.CRIER_DEVTO_API_KEY }}
43
+ CRIER_BLUESKY_API_KEY: ${{ secrets.CRIER_BLUESKY_API_KEY }}
44
+ CRIER_MASTODON_API_KEY: ${{ secrets.CRIER_MASTODON_API_KEY }}
45
+ CRIER_HASHNODE_API_KEY: ${{ secrets.CRIER_HASHNODE_API_KEY }}
46
+ run: |
47
+ echo "Configured platforms:"
48
+ crier platforms
49
+
50
+ - name: Audit content
51
+ env:
52
+ CRIER_DEVTO_API_KEY: ${{ secrets.CRIER_DEVTO_API_KEY }}
53
+ CRIER_BLUESKY_API_KEY: ${{ secrets.CRIER_BLUESKY_API_KEY }}
54
+ CRIER_MASTODON_API_KEY: ${{ secrets.CRIER_MASTODON_API_KEY }}
55
+ CRIER_HASHNODE_API_KEY: ${{ secrets.CRIER_HASHNODE_API_KEY }}
56
+ run: |
57
+ # Audit posts directory (adjust path as needed)
58
+ if [ -d "posts" ]; then
59
+ crier audit ./posts || true
60
+ elif [ -d "content" ]; then
61
+ crier audit ./content || true
62
+ fi
63
+
64
+ - name: Publish missing content
65
+ env:
66
+ CRIER_DEVTO_API_KEY: ${{ secrets.CRIER_DEVTO_API_KEY }}
67
+ CRIER_BLUESKY_API_KEY: ${{ secrets.CRIER_BLUESKY_API_KEY }}
68
+ CRIER_MASTODON_API_KEY: ${{ secrets.CRIER_MASTODON_API_KEY }}
69
+ CRIER_HASHNODE_API_KEY: ${{ secrets.CRIER_HASHNODE_API_KEY }}
70
+ run: |
71
+ # Backfill any missing publications
72
+ if [ -d "posts" ]; then
73
+ crier backfill ./posts --yes || true
74
+ elif [ -d "content" ]; then
75
+ crier backfill ./content --yes || true
76
+ fi
77
+
78
+ - name: Commit registry updates
79
+ run: |
80
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
81
+ git config --local user.name "github-actions[bot]"
82
+
83
+ # Add registry if it exists
84
+ if [ -d ".crier" ]; then
85
+ git add .crier/
86
+ git diff --staged --quiet || git commit -m "Update crier publication registry
87
+
88
+ 🤖 Auto-updated by Crier GitHub Action"
89
+ git push
90
+ fi
crier-0.2.0/.gitignore ADDED
@@ -0,0 +1,47 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ ENV/
26
+ env/
27
+ .venv/
28
+
29
+ # IDE
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+ *.swo
34
+
35
+ # Testing
36
+ .pytest_cache/
37
+ .coverage
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+
42
+ # Distribution
43
+ *.tar.gz
44
+ *.whl
45
+
46
+ # Local config (contains API keys)
47
+ config.yaml
crier-0.2.0/CLAUDE.md ADDED
@@ -0,0 +1,85 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Crier is a CLI tool for cross-posting content to multiple platforms. It reads markdown files with YAML front matter and publishes them via platform APIs. Designed to be used with Claude Code for automated content distribution.
8
+
9
+ ## Development Commands
10
+
11
+ ```bash
12
+ # Install in development mode with dev dependencies
13
+ pip install -e ".[dev]"
14
+
15
+ # Run tests
16
+ pytest
17
+
18
+ # Run tests with coverage
19
+ pytest --cov=crier
20
+
21
+ # Run a single test
22
+ pytest tests/test_file.py::test_function -v
23
+
24
+ # Lint
25
+ ruff check src/
26
+
27
+ # Format check
28
+ ruff format --check src/
29
+
30
+ # Build and serve docs locally
31
+ mkdocs serve
32
+ ```
33
+
34
+ ## Architecture
35
+
36
+ **CLI Layer** (`cli.py`): Click-based commands that orchestrate the workflow:
37
+ - `publish` — Publish to platforms (supports `--dry-run`, `--profile`)
38
+ - `status` — Show publication status for files
39
+ - `audit` — Check what's missing from platforms
40
+ - `backfill` — Publish missing content
41
+ - `doctor` — Validate API keys
42
+ - `config` — Manage API keys and profiles
43
+
44
+ **Platform Abstraction** (`platforms/`):
45
+ - `base.py`: Abstract `Platform` class defining the interface (`publish`, `update`, `list_articles`, `get_article`, `delete`) and core data classes (`Article`, `PublishResult`)
46
+ - Each platform implements the `Platform` interface
47
+ - `PLATFORMS` registry in `__init__.py` maps platform names to classes
48
+
49
+ **Platform Categories** (13 total):
50
+ - Blog: devto, hashnode, medium, ghost, wordpress
51
+ - Newsletter: buttondown
52
+ - Social: bluesky, mastodon, linkedin, threads, twitter (copy-paste mode)
53
+ - Announcement: telegram, discord
54
+
55
+ **Config** (`config.py`): API keys and profiles stored in `~/.config/crier/config.yaml` or via `CRIER_{PLATFORM}_API_KEY` environment variables. Environment variables take precedence. Supports composable profiles.
56
+
57
+ **Registry** (`registry.py`): Tracks publications in `.crier/registry.yaml`. Records what's been published where, enables status checks, audit, and backfill.
58
+
59
+ **Converters** (`converters/markdown.py`): Parses markdown files with YAML front matter into `Article` objects.
60
+
61
+ ## Key Features
62
+
63
+ - **Dry run mode**: Preview before publishing with `--dry-run`
64
+ - **Publishing profiles**: Group platforms (e.g., `--profile blogs`)
65
+ - **Publication tracking**: Registry tracks what's published where
66
+ - **Audit & backfill**: Find and publish missing content
67
+ - **Doctor**: Validate all API keys work
68
+
69
+ ## Adding a New Platform
70
+
71
+ 1. Create `platforms/newplatform.py` implementing the `Platform` abstract class
72
+ 2. Register in `platforms/__init__.py` by adding to `PLATFORMS` dict
73
+ 3. Add documentation in `docs/platforms/newplatform.md`
74
+ 4. Update README.md with API key format
75
+
76
+ ## Documentation
77
+
78
+ Documentation is in `docs/` using MkDocs with Material theme. Deployed to GitHub Pages via `.github/workflows/docs.yml`.
79
+
80
+ ## Testing Notes
81
+
82
+ Tests directory exists but is currently empty. When adding tests:
83
+ - Use pytest fixtures for mocking API responses
84
+ - Test both success and failure paths for platform operations
85
+ - Mock `requests` calls rather than hitting real APIs
crier-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: crier
3
+ Version: 0.2.0
4
+ Summary: Cross-post your content to dev.to, Hashnode, Medium, and more
5
+ Project-URL: Homepage, https://github.com/queelius/crier
6
+ Project-URL: Repository, https://github.com/queelius/crier
7
+ Project-URL: Issues, https://github.com/queelius/crier/issues
8
+ Author-email: Alex Towell <lex@metafunctor.com>
9
+ License-Expression: MIT
10
+ Keywords: blogging,content,cross-posting,devto,hashnode,medium
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Internet
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: click>=8.0
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: requests>=2.28
26
+ Requires-Dist: rich>=13.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: mkdocs-material>=9.0; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
30
+ Requires-Dist: pytest>=7.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Crier
35
+
36
+ Cross-post your content to dev.to, Ghost, WordPress, Hashnode, Medium, Bluesky, Mastodon, Threads, Telegram, Discord, and more.
37
+
38
+ Like a town crier announcing your content to the world.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ pip install crier
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```bash
49
+ # Configure your API keys
50
+ crier config set devto.api_key YOUR_API_KEY
51
+ crier config set bluesky.api_key "handle.bsky.social:app-password"
52
+ crier config set mastodon.api_key "mastodon.social:access-token"
53
+
54
+ # Publish a markdown file
55
+ crier publish post.md --to devto
56
+
57
+ # Publish to multiple platforms at once
58
+ crier publish post.md --to devto --to bluesky --to mastodon
59
+
60
+ # List your articles
61
+ crier list devto
62
+
63
+ # Update an existing article
64
+ crier update devto 12345 --file updated-post.md
65
+ ```
66
+
67
+ ## Supported Platforms
68
+
69
+ | Platform | API Key Format | Notes |
70
+ |----------|---------------|-------|
71
+ | **dev.to** | `api_key` | Full article support |
72
+ | **Hashnode** | `token` or `token:publication_id` | Full article support |
73
+ | **Medium** | `integration_token` | Publish only (no edit/list) |
74
+ | **Ghost** | `https://site.com:key_id:key_secret` | Full article support |
75
+ | **WordPress** | `site.wordpress.com:token` or `https://site.com:user:app_pass` | Full article support |
76
+ | **Buttondown** | `api_key` | Newsletter publishing |
77
+ | **Bluesky** | `handle:app_password` | Short posts with link cards |
78
+ | **Mastodon** | `instance:access_token` | Toots with hashtags |
79
+ | **Threads** | `user_id:access_token` | Short posts (no edit support) |
80
+ | **Telegram** | `bot_token:chat_id` | Channel/group posts |
81
+ | **Discord** | `webhook_url` | Server announcements |
82
+ | **LinkedIn** | `access_token` | Requires API access |
83
+ | **Twitter/X** | `any` (copy-paste mode) | Generates tweet for manual posting |
84
+
85
+ ### Platform Notes
86
+
87
+ **Blog Platforms** (dev.to, Hashnode, Medium, Ghost, WordPress):
88
+ - Full markdown article publishing
89
+ - Preserves front matter (title, description, tags, canonical_url)
90
+ - Best for long-form content
91
+
92
+ **Newsletter Platforms** (Buttondown):
93
+ - Publishes to email subscribers
94
+ - Full markdown support
95
+ - Great for content repurposing
96
+
97
+ **Social Platforms** (Bluesky, Mastodon, LinkedIn, Twitter, Threads):
98
+ - Creates short posts with link to canonical URL
99
+ - Uses title + description + hashtags from tags
100
+ - Best for announcing new content
101
+
102
+ **Announcement Channels** (Telegram, Discord):
103
+ - Posts to channels/servers
104
+ - Good for community announcements
105
+ - Discord uses webhook embeds
106
+
107
+ ## Configuration
108
+
109
+ API keys can be set via:
110
+
111
+ 1. **Config file** (`~/.config/crier/config.yaml`):
112
+ ```yaml
113
+ platforms:
114
+ devto:
115
+ api_key: your_key_here
116
+ bluesky:
117
+ api_key: "handle.bsky.social:app-password"
118
+ mastodon:
119
+ api_key: "mastodon.social:access-token"
120
+ ```
121
+
122
+ 2. **Environment variables**:
123
+ ```bash
124
+ export CRIER_DEVTO_API_KEY=your_key_here
125
+ export CRIER_BLUESKY_API_KEY="handle.bsky.social:app-password"
126
+ ```
127
+
128
+ ## Markdown Format
129
+
130
+ Crier reads standard markdown with YAML front matter:
131
+
132
+ ```markdown
133
+ ---
134
+ title: "My Amazing Post"
135
+ description: "A brief description"
136
+ tags: [python, programming]
137
+ canonical_url: https://myblog.com/my-post
138
+ published: true
139
+ ---
140
+
141
+ Your content here...
142
+ ```
143
+
144
+ ## Commands
145
+
146
+ ```bash
147
+ crier publish FILE [--to PLATFORM]... # Publish to platform(s)
148
+ crier update PLATFORM ID --file FILE # Update existing article
149
+ crier list PLATFORM # List your articles
150
+ crier config set KEY VALUE # Set configuration
151
+ crier config show # Show configuration
152
+ crier platforms # List available platforms
153
+ ```
154
+
155
+ ## Getting API Keys
156
+
157
+ ### dev.to
158
+ 1. Go to https://dev.to/settings/extensions
159
+ 2. Generate API key
160
+
161
+ ### Hashnode
162
+ 1. Go to https://hashnode.com/settings/developer
163
+ 2. Generate Personal Access Token
164
+
165
+ ### Medium
166
+ 1. Go to https://medium.com/me/settings/security
167
+ 2. Generate Integration Token
168
+
169
+ ### Bluesky
170
+ 1. Go to Settings → App Passwords
171
+ 2. Create an app password
172
+ 3. Use format: `yourhandle.bsky.social:xxxx-xxxx-xxxx-xxxx`
173
+
174
+ ### Mastodon
175
+ 1. Go to Settings → Development → New Application
176
+ 2. Create app with `write:statuses` scope
177
+ 3. Use format: `instance.social:your-access-token`
178
+
179
+ ### Twitter/X
180
+ Uses copy-paste mode - generates formatted tweet text for manual posting.
181
+ No API setup required. Just set any placeholder value:
182
+ ```bash
183
+ crier config set twitter.api_key manual
184
+ ```
185
+
186
+ ### Ghost
187
+ 1. Go to Settings → Integrations → Add custom integration
188
+ 2. Copy the Admin API Key (format: `key_id:key_secret`)
189
+ 3. Use format: `https://yourblog.com:key_id:key_secret`
190
+
191
+ ### WordPress
192
+ **WordPress.com:**
193
+ 1. Go to https://developer.wordpress.com/apps/
194
+ 2. Create an app and get OAuth token
195
+ 3. Use format: `yoursite.wordpress.com:access_token`
196
+
197
+ **Self-hosted WordPress:**
198
+ 1. Go to Users → Profile → Application Passwords
199
+ 2. Create a new application password
200
+ 3. Use format: `https://yoursite.com:username:app_password`
201
+
202
+ ### Buttondown
203
+ 1. Go to https://buttondown.email/settings/programming
204
+ 2. Copy your API key
205
+ 3. Use format: `api_key`
206
+
207
+ ### Threads
208
+ 1. Create a Meta Developer account at https://developers.facebook.com/
209
+ 2. Create an app with Threads API access
210
+ 3. Get your user_id and access_token
211
+ 4. Use format: `user_id:access_token`
212
+
213
+ ### Telegram
214
+ 1. Message @BotFather to create a bot and get the bot token
215
+ 2. Add your bot as admin to your channel
216
+ 3. Get your channel's chat_id (e.g., `@yourchannel` or numeric ID)
217
+ 4. Use format: `bot_token:chat_id`
218
+
219
+ ### Discord
220
+ 1. Go to Server Settings → Integrations → Webhooks
221
+ 2. Create a new webhook for your announcement channel
222
+ 3. Copy the webhook URL
223
+ 4. Use the full URL as the API key
224
+
225
+ ## License
226
+
227
+ MIT
crier-0.2.0/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # Crier
2
+
3
+ Cross-post your content to dev.to, Ghost, WordPress, Hashnode, Medium, Bluesky, Mastodon, Threads, Telegram, Discord, and more.
4
+
5
+ Like a town crier announcing your content to the world.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install crier
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Configure your API keys
17
+ crier config set devto.api_key YOUR_API_KEY
18
+ crier config set bluesky.api_key "handle.bsky.social:app-password"
19
+ crier config set mastodon.api_key "mastodon.social:access-token"
20
+
21
+ # Publish a markdown file
22
+ crier publish post.md --to devto
23
+
24
+ # Publish to multiple platforms at once
25
+ crier publish post.md --to devto --to bluesky --to mastodon
26
+
27
+ # List your articles
28
+ crier list devto
29
+
30
+ # Update an existing article
31
+ crier update devto 12345 --file updated-post.md
32
+ ```
33
+
34
+ ## Supported Platforms
35
+
36
+ | Platform | API Key Format | Notes |
37
+ |----------|---------------|-------|
38
+ | **dev.to** | `api_key` | Full article support |
39
+ | **Hashnode** | `token` or `token:publication_id` | Full article support |
40
+ | **Medium** | `integration_token` | Publish only (no edit/list) |
41
+ | **Ghost** | `https://site.com:key_id:key_secret` | Full article support |
42
+ | **WordPress** | `site.wordpress.com:token` or `https://site.com:user:app_pass` | Full article support |
43
+ | **Buttondown** | `api_key` | Newsletter publishing |
44
+ | **Bluesky** | `handle:app_password` | Short posts with link cards |
45
+ | **Mastodon** | `instance:access_token` | Toots with hashtags |
46
+ | **Threads** | `user_id:access_token` | Short posts (no edit support) |
47
+ | **Telegram** | `bot_token:chat_id` | Channel/group posts |
48
+ | **Discord** | `webhook_url` | Server announcements |
49
+ | **LinkedIn** | `access_token` | Requires API access |
50
+ | **Twitter/X** | `any` (copy-paste mode) | Generates tweet for manual posting |
51
+
52
+ ### Platform Notes
53
+
54
+ **Blog Platforms** (dev.to, Hashnode, Medium, Ghost, WordPress):
55
+ - Full markdown article publishing
56
+ - Preserves front matter (title, description, tags, canonical_url)
57
+ - Best for long-form content
58
+
59
+ **Newsletter Platforms** (Buttondown):
60
+ - Publishes to email subscribers
61
+ - Full markdown support
62
+ - Great for content repurposing
63
+
64
+ **Social Platforms** (Bluesky, Mastodon, LinkedIn, Twitter, Threads):
65
+ - Creates short posts with link to canonical URL
66
+ - Uses title + description + hashtags from tags
67
+ - Best for announcing new content
68
+
69
+ **Announcement Channels** (Telegram, Discord):
70
+ - Posts to channels/servers
71
+ - Good for community announcements
72
+ - Discord uses webhook embeds
73
+
74
+ ## Configuration
75
+
76
+ API keys can be set via:
77
+
78
+ 1. **Config file** (`~/.config/crier/config.yaml`):
79
+ ```yaml
80
+ platforms:
81
+ devto:
82
+ api_key: your_key_here
83
+ bluesky:
84
+ api_key: "handle.bsky.social:app-password"
85
+ mastodon:
86
+ api_key: "mastodon.social:access-token"
87
+ ```
88
+
89
+ 2. **Environment variables**:
90
+ ```bash
91
+ export CRIER_DEVTO_API_KEY=your_key_here
92
+ export CRIER_BLUESKY_API_KEY="handle.bsky.social:app-password"
93
+ ```
94
+
95
+ ## Markdown Format
96
+
97
+ Crier reads standard markdown with YAML front matter:
98
+
99
+ ```markdown
100
+ ---
101
+ title: "My Amazing Post"
102
+ description: "A brief description"
103
+ tags: [python, programming]
104
+ canonical_url: https://myblog.com/my-post
105
+ published: true
106
+ ---
107
+
108
+ Your content here...
109
+ ```
110
+
111
+ ## Commands
112
+
113
+ ```bash
114
+ crier publish FILE [--to PLATFORM]... # Publish to platform(s)
115
+ crier update PLATFORM ID --file FILE # Update existing article
116
+ crier list PLATFORM # List your articles
117
+ crier config set KEY VALUE # Set configuration
118
+ crier config show # Show configuration
119
+ crier platforms # List available platforms
120
+ ```
121
+
122
+ ## Getting API Keys
123
+
124
+ ### dev.to
125
+ 1. Go to https://dev.to/settings/extensions
126
+ 2. Generate API key
127
+
128
+ ### Hashnode
129
+ 1. Go to https://hashnode.com/settings/developer
130
+ 2. Generate Personal Access Token
131
+
132
+ ### Medium
133
+ 1. Go to https://medium.com/me/settings/security
134
+ 2. Generate Integration Token
135
+
136
+ ### Bluesky
137
+ 1. Go to Settings → App Passwords
138
+ 2. Create an app password
139
+ 3. Use format: `yourhandle.bsky.social:xxxx-xxxx-xxxx-xxxx`
140
+
141
+ ### Mastodon
142
+ 1. Go to Settings → Development → New Application
143
+ 2. Create app with `write:statuses` scope
144
+ 3. Use format: `instance.social:your-access-token`
145
+
146
+ ### Twitter/X
147
+ Uses copy-paste mode - generates formatted tweet text for manual posting.
148
+ No API setup required. Just set any placeholder value:
149
+ ```bash
150
+ crier config set twitter.api_key manual
151
+ ```
152
+
153
+ ### Ghost
154
+ 1. Go to Settings → Integrations → Add custom integration
155
+ 2. Copy the Admin API Key (format: `key_id:key_secret`)
156
+ 3. Use format: `https://yourblog.com:key_id:key_secret`
157
+
158
+ ### WordPress
159
+ **WordPress.com:**
160
+ 1. Go to https://developer.wordpress.com/apps/
161
+ 2. Create an app and get OAuth token
162
+ 3. Use format: `yoursite.wordpress.com:access_token`
163
+
164
+ **Self-hosted WordPress:**
165
+ 1. Go to Users → Profile → Application Passwords
166
+ 2. Create a new application password
167
+ 3. Use format: `https://yoursite.com:username:app_password`
168
+
169
+ ### Buttondown
170
+ 1. Go to https://buttondown.email/settings/programming
171
+ 2. Copy your API key
172
+ 3. Use format: `api_key`
173
+
174
+ ### Threads
175
+ 1. Create a Meta Developer account at https://developers.facebook.com/
176
+ 2. Create an app with Threads API access
177
+ 3. Get your user_id and access_token
178
+ 4. Use format: `user_id:access_token`
179
+
180
+ ### Telegram
181
+ 1. Message @BotFather to create a bot and get the bot token
182
+ 2. Add your bot as admin to your channel
183
+ 3. Get your channel's chat_id (e.g., `@yourchannel` or numeric ID)
184
+ 4. Use format: `bot_token:chat_id`
185
+
186
+ ### Discord
187
+ 1. Go to Server Settings → Integrations → Webhooks
188
+ 2. Create a new webhook for your announcement channel
189
+ 3. Copy the webhook URL
190
+ 4. Use the full URL as the API key
191
+
192
+ ## License
193
+
194
+ MIT