https://github.com/policyengine/policyengine-social
Automated social media posting for PolicyEngine blog articles
https://github.com/policyengine/policyengine-social
Last synced: 7 months ago
JSON representation
Automated social media posting for PolicyEngine blog articles
- Host: GitHub
- URL: https://github.com/policyengine/policyengine-social
- Owner: PolicyEngine
- Created: 2025-08-18T20:10:37.000Z (8 months ago)
- Default Branch: master
- Last Pushed: 2025-08-22T02:21:50.000Z (8 months ago)
- Last Synced: 2025-08-27T20:31:52.818Z (7 months ago)
- Language: Python
- Size: 179 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# PolicyEngine Social Media Automation
PR-based social media posting system for PolicyEngine. All posts require review and approval through GitHub Pull Requests before publishing.
## 🔒 Safety First
**This system is designed to NEVER post without explicit approval:**
- All posts must be reviewed via Pull Request
- Posts are only published after PR merge
- 5-minute grace period to cancel after merge
- No local posting capabilities by default
## How It Works
```mermaid
graph LR
A[Create post.yaml] --> B[Open PR]
B --> C[Team Review]
C --> D[Merge PR]
D --> E[5min Wait]
E --> F[Publish to Social]
```
1. **Create Post**: Add a YAML file to `posts/queue/`
2. **Open PR**: Submit for team review
3. **Review**: Team reviews content, accuracy, timing
4. **Merge**: Approved PRs are merged
5. **Grace Period**: 5 minutes to cancel via `/cancel-posts`
6. **Publish**: Automatically posts to configured platforms
## Quick Start
### Creating a Post
1. Copy the example template:
```bash
cp posts/queue/example-post.yaml.example posts/queue/my-post.yaml
```
2. Edit with your content:
```yaml
title: "Your Post Title"
platforms:
x:
accounts: [thepolicyengine]
thread:
- "First tweet"
- "Second tweet"
linkedin:
content: "LinkedIn post content"
```
3. Open a Pull Request
### Post Format
```yaml
# Metadata
title: "NSF Grant Announcement"
author: "YourName"
tags: ["announcement", "grant"]
# Platform content
platforms:
x:
accounts:
- thepolicyengine # Main account
- policyengineus # US-specific
- policyengineuk # UK-specific
thread:
- "Tweet 1 (max 280 chars)"
- "Tweet 2"
images: ["assets/image.png"]
linkedin:
content: |
Longer form content for LinkedIn
Can be multiple paragraphs
article_url: "https://policyengine.org/..."
```
## Local Development (Safe Mode)
### Dry Run Testing
Test your posts locally WITHOUT publishing:
```bash
# Validate post structure
python scripts/validate_post.py posts/queue/my-post.yaml
# Preview what would be posted (DRY RUN)
python scripts/publish_post.py posts/queue/my-post.yaml
# Never use --prod flag locally!
```
### Setup for Development
```bash
# Clone repository
git clone https://github.com/PolicyEngine/policyengine-social.git
cd policyengine-social
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install package
pip install -e ".[dev]"
# Copy environment template (but don't add real credentials!)
cp .env.example .env
```
## GitHub Actions Setup
### Required Secrets
Add these to your GitHub repository settings:
**X (Twitter) Accounts:**
- `X_THEPOLICYENGINE_API_KEY`
- `X_THEPOLICYENGINE_API_SECRET`
- `X_THEPOLICYENGINE_ACCESS_TOKEN`
- `X_THEPOLICYENGINE_ACCESS_TOKEN_SECRET`
- `X_POLICYENGINEUS_API_KEY`
- `X_POLICYENGINEUS_API_SECRET`
- `X_POLICYENGINEUS_ACCESS_TOKEN`
- `X_POLICYENGINEUS_ACCESS_TOKEN_SECRET`
- `X_POLICYENGINEUK_API_KEY`
- `X_POLICYENGINEUK_API_SECRET`
- `X_POLICYENGINEUK_ACCESS_TOKEN`
- `X_POLICYENGINEUK_ACCESS_TOKEN_SECRET`
**LinkedIn (via Zapier):**
- `ZAPIER_LINKEDIN_WEBHOOK`
## Repository Structure
```
posts/
├── queue/ # Posts awaiting review (PRs)
├── published/ # Archive of published posts
└── templates/ # Reusable templates
scripts/
├── validate_post.py # Validates YAML structure
└── publish_post.py # Posts to social (Actions only)
src/policyengine_social/
├── publishers/
│ ├── x_multi.py # Multi-account X posting
│ └── zapier.py # LinkedIn via Zapier
├── extract.py # Blog image extraction
└── generate.py # Auto-generate posts
```
## Content Routing
The system automatically routes content to appropriate accounts:
- **US content** → @policyengineus
- **UK content** → @policyengineuk
- **General** → @thepolicyengine
## Testing
```bash
# Run all tests
pytest tests/
# Run with coverage
pytest tests/ --cov=policyengine_social
# Validate a post
python scripts/validate_post.py posts/queue/my-post.yaml
```
## Safety Features
1. **No Accidental Posting**: Package cannot post without explicit PR approval
2. **Review Required**: All posts go through PR review
3. **Cancellation Window**: 5 minutes to cancel after merge
4. **Dry Run Default**: Scripts default to preview mode
5. **Validation**: Posts are validated before publishing
6. **Audit Trail**: All posts archived after publishing
## PR Workflow
1. Create your post YAML in `posts/queue/`
2. Run validation locally: `python scripts/validate_post.py posts/queue/my-post.yaml`
3. Preview locally: `python scripts/publish_post.py posts/queue/my-post.yaml`
4. Commit and push: `git add posts/queue/my-post.yaml && git commit -m "Add post about..."`
5. Open PR and fill out the template
6. Wait for team review
7. Merge when approved
8. Monitor the Actions tab for publishing status
## Troubleshooting
### Post Not Publishing
- Check GitHub Actions logs
- Verify all secrets are set correctly
- Ensure post YAML is valid
### Wrong Account
- Check the `accounts` field in your YAML
- Verify content routing rules
### Rate Limits
- X allows 300 posts per 3 hours
- Space out large threads
- Use different apps for different accounts
## Contributing
1. Never add real credentials to code
2. Always use dry-run mode for testing
3. Create posts via PR only
4. Test thoroughly with `pytest`
## License
MIT