sns-network-solutions/businesses/sns-hospitality-group/marketing/DEPLOY-README.md

367 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# The Daily Pour — Marketing Automation Deployment
**Owner:** Annie (Marketing Lead)
**Infra:** Sam (GitLab CI + Kestra)
**Target deploy date:** Sunday, August 17, 2026
**GitLab repo:** https://gitlab.com/samueljamesinc/daily-pour-marketing (LIVE)
**Code-server:** https://code.snsnetlabs.com (LXC 304, Fedora 44, pve3)
---
## What This Is
Automated daily social media promotion for The Daily Pour, powered by Kestra workflows.
Annie manages the content calendar and approvals; the system handles content generation,
hashtag selection, platform-specific formatting, and publishing — zero daily effort once
configured.
---
## Architecture
```
┌──────────────┐ ┌───────────────┐ ┌──────────────┐ ┌─────────────┐ ┌────────────┐
│ Pre-Flight │ ──▶ │ Content Gen │ ──▶ │ Hashtag │ ──▶ │ Platform │ ──▶ │ Publish │
│ Checks │ │ (ChatGPT / │ │ Engine │ │ Formatter │ │ (Buffer │
│ │ │ Ollama) │ │ │ │ │ │ API) │
└──────────────┘ └───────────────┘ └──────────────┘ └─────────────┘ └────────────┘
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
APIs alive? Weather + Tier 1-4 + Per-platform One API →
Drive access? day theme → RiteKit trending + character limits, FB + IG +
Platforms up? brand voice → rotation logic → image specs, TikTok +
Content exists? caption draft anti-shadowban hashtag counts LinkedIn +
Google Biz
```
### Data Flow (detailed)
```
┌─────────────────────────────────────────────────────┐
│ GitLab.com (Source of Truth) │
│ daily-pour-marketing repo │
│ ├── workflows/ (Kestra YAML) │
│ ├── config/ (brand voice, hashtags, locations) │
│ └── CI Variables (secrets, tokens) │
└───────────────────────┬─────────────────────────────┘
│ git sync (every 15 min)
┌──────────────┐ ┌─────────────────────────────┐
│ Google Drive │◀────read────▶│ Kestra (Execution) │
│ (60GB) │ │ 192.168.122.71 │
│ ├── photos/ │ │ │
│ ├── videos/ │ │ Cron triggers: │
│ └── logos/ │ │ • 5:30 AM — preflight │
└──────────────┘ │ • 6:00 AM — daily post │
│ • Sun 8PM — weekly gen │
└──────────────┬──────────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────────┐ ┌─────────────┐ ┌──────────────┐
│ OpenWeather │ │ ChatGPT / │ │ RiteKit │
│ API │ │ Ollama │ │ (trending │
│ (weather) │ │ (captions) │ │ hashtags) │
└──────────────┘ └─────────────┘ └──────────────┘
┌─────────────────────────────┐
│ Buffer API │
│ (single publishing │
│ layer for all 5 │
│ platforms) │
└──────────────┬──────────────┘
┌────────┬───────┬───────┼───────┬────────┐
▼ ▼ ▼ ▼ ▼ │
Facebook Instagram TikTok LinkedIn Google │
Business │
┌────────────────────────────────┘
┌─────────────────────────────┐
│ Kestra plugin-meta (backup) │
│ Direct FB + IG posting │
│ (if Buffer doesn't cover │
│ a specific need) │
└─────────────────────────────┘
```
---
## Publishing Layer: Buffer API
**Why Buffer instead of 5 separate platform APIs:**
- One integration → posts to all 5 platforms
- Handles format differences (image sizes, character limits, hashtag placement)
- Built-in scheduling, analytics, engagement inbox
- Free tier: 3 channels, 10 posts/channel/month
- Paid: ~$6/channel/month ($30/mo for all 5 — cheaper than dev time on 5 APIs)
- Has an MCP server (AI agents can post directly)
- REST API: `https://api.bufferapp.com/1/updates/create.json`
**Fallback:** Kestra `plugin-meta` for direct Facebook + Instagram posting if Buffer
doesn't cover a specific need (carousels, reels with specific metadata, etc.)
**Setup:**
1. Sign up at buffer.com
2. Connect all 5 social accounts
3. Generate API token
4. Add `BUFFER_ACCESS_TOKEN` to GitLab CI Variables + Kestra namespace secrets
---
## Hashtag Engine
### Strategy: 4-Tier System with Rotation
| Tier | What | Example | Rotation |
|------|------|---------|----------|
| 1 — Brand | Always present | #TheDailyPour #DailyPourCoffee | Never changes |
| 2 — Category | Broad reach | #CoffeeAddict #SpecialtyCoffee #LocalCoffee | 3 sets, cycle every 3 days |
| 3 — Day Theme | Matches daily rotation | #MenuMonday #BehindTheScenes #FridayFeels | Changes daily |
| 4 — Dynamic | Fresh per post | Weather-based, trending, time-of-day | Every post (RiteKit + weather API) |
### Platform Limits (enforced by the formatter)
| Platform | Optimal Count | Format |
|----------|---------------|--------|
| Instagram | 20-25 | In caption or first comment (separated by dots) |
| TikTok | 5-8 | In caption only (algorithm punishes excess) |
| Facebook | 3-5 | In caption, early |
| LinkedIn | 3-5 | Final line, professional tone only |
| Google Business | 2-3 | In description, minimal |
### Anti-Shadowban Rules
- Never repeat the same Tier 2 set within 7 days on the same platform
- Rotate Tier 2 pools: A → B → C → A (3-day cycle)
- Tier 4 (trending) is always unique — pulled fresh per post
- Track hashtag history in Kestra (7-day lookback before selecting)
### Trending Hashtag Sources
- **RiteKit API** (https://ritekit.com) — give it post text, returns optimized hashtags
based on real-time popularity. 100 free calls/month.
- **Apify TikTok Scraper** — pulls TikTok Creative Center trending hashtags daily
- **OpenWeatherMap** — weather-reactive hashtags (#SummerVibes, #CozyMorning, etc.)
- **Day-of-week cultural moments** — #MondayMotivation, #FridayFeels, #SundayFunday
### Config Files
```
config/
├── hashtag-rotation.yml ← Tier 2 pools (A/B/C), Tier 3 by day, rotation state
├── hashtag-limits.yml ← Per-platform max counts + format rules
└── hashtag-banned.yml ← Hashtags known to be suppressed/banned (check monthly)
content/templates/
└── hashtags.md ← Master reference (all tiers defined, human-readable)
```
---
## Pre-Requisites Checklist (Updated)
### Infrastructure (Sam) — ✅ = Done
- [x] **GitLab.com repo** — https://gitlab.com/samueljamesinc/daily-pour-marketing
- [x] **Kestra** running and healthy — 192.168.122.71:8080
- [x] **Code-server** — LXC 304, Fedora 44, code.snsnetlabs.com
- [x] **GitLab SSH** from code-server working
- [x] **OpenWeatherMap API** — key active, tested
- [x] **OpenAI API** — key active, tested
- [x] **Google Drive OAuth** — refresh token working, tested upload/list/download
- [x] **Pre-flight checks flow** — tested, all passing
- [x] **Weather caption flow** — tested, generating captions
- [ ] **Buffer account** — sign up, connect platforms, get API token
- [ ] **RiteKit account** — sign up, get API key (free tier)
- [ ] **Kestra ↔ GitLab sync flow** — auto-pull workflows from repo
- [ ] **Publish GCP OAuth app** (Testing → Production) — prevent 7-day token expiry
### Platform Accounts (Sam or Annie)
- [ ] **Google Business Profile** created for The Daily Pour
- [ ] **Facebook Business Page** created
- [ ] **Instagram Business Account** connected to Facebook page
- [ ] **TikTok Business Account** created
- [ ] **LinkedIn Company Page** created (SnS Hospitality Group or The Daily Pour)
### Content Library (Annie — ongoing, seed before launch)
- [ ] **Logos** — PNG (dark/light/icon), SVG source
- [ ] **Product photos** — at least 10 menu item shots
- [ ] **Trailer photos** — exterior, window, signage (even mockups/renders to start)
- [ ] **Video clips** (optional for launch) — espresso pulls, morning prep, b-roll
- [x] **Brand voice doc** — committed to repo
- [x] **Hashtag sets** — committed to repo (expand with Tier 2/3/4 config)
- [x] **Rules doc** — committed to repo
- [x] **Platform config** — committed to repo
---
## Kestra Workflows (Updated)
| # | Workflow | Trigger | What it does | Status |
|---|---------|---------|--------------|--------|
| 0 | `preflight_checks` | Before every deployment | Validates APIs, platforms, Drive, content exist | ✅ LIVE |
| 1 | `weather_caption_test` | Manual / Cron | Weather + ChatGPT → generates caption | ✅ LIVE |
| 2 | `daily_post_rotation` | Cron 6:00 AM daily | Preflight → content gen → hashtags → format → publish via Buffer | TODO |
| 3 | `weekly_content_gen` | Cron Sunday 8 PM | LLM generates 7 draft captions + hashtag sets for the week | TODO |
| 4 | `location_announce` | Manual or webhook | New location signed → announcement series across all platforms | TODO |
| 5 | `hiring_post` | Webhook from Plane | Hiring task created → generates + posts hiring content | TODO |
| 6 | `review_responder` | Cron daily 9 AM | Pulls Google reviews → drafts responses → Annie approves | TODO |
| 7 | `engagement_repost` | Cron 2x/week | Scans for UGC/mentions → queues for repost with credit | TODO |
| 8 | `hashtag_performance` | Cron Monday 9 AM | Pulls engagement data, correlates with hashtags, recommends rotations | TODO |
---
## Content Rotation (daily theme)
| Day | Type | Platforms | Tier 3 Hashtags |
|-----|------|-----------|-----------------|
| Mon | Menu highlight | All | #MenuMonday #CoffeeMagic #MondayMotivation |
| Tue | Behind the scenes | IG, TikTok, FB | #BehindTheScenes #MorningShift #BaristaDiary |
| Wed | Testimonial / review | FB, Google, LinkedIn | #CustomerLove #TestimonialTuesday #FiveStars |
| Thu | Educational / fun fact | All | #CoffeeEd #DidYouKnow #ThursdayThoughts |
| Fri | Weekend teaser + location | All | #FridayFeels #WeekendPlans #CoffeeNearMe |
| Sat | Live energy / "we're open" | IG Stories, TikTok, FB | #WeAreOpen #SaturdayVibes #LiveEnergy |
| Sun | Community / gratitude | FB, LinkedIn, IG | #CommunityLove #SundayBrunch #Grateful |
---
## GitLab Repo Structure (Updated)
```
daily-pour-marketing/ ← https://gitlab.com/samueljamesinc/daily-pour-marketing
├── .gitlab-ci.yml ← Validates YAML + syncs to Kestra
├── README.md
├── content/
│ ├── logos/
│ ├── photos/{products,trailer,team,customers}/
│ ├── videos/{reels,tiktok,stories}/
│ └── templates/
│ ├── captions/ ← Per-day caption templates
│ ├── hashtags.md ← Master hashtag reference
│ └── canva/ ← Brand templates
├── config/
│ ├── brand-voice.md ← Annie's tone guide
│ ├── colors.md ← Brand palette
│ ├── rules.md ← What to never post
│ ├── platforms.yml ← Platform config (no secrets)
│ ├── locations.yml ← Active locations + hours
│ ├── hashtag-rotation.yml ← NEW: Tier 2 pools, rotation state
│ ├── hashtag-limits.yml ← NEW: Per-platform counts + format
│ └── hashtag-banned.yml ← NEW: Suppressed/banned hashtags
├── workflows/
│ ├── preflight-checks.yml ← ✅ LIVE
│ ├── daily-post-rotation.yml ← TODO (full pipeline)
│ ├── weather-reactive.yml
│ ├── weekly-content-gen.yml
│ ├── location-announce.yml
│ ├── hiring-post.yml
│ ├── review-responder.yml
│ ├── engagement-repost.yml
│ └── hashtag-performance.yml ← NEW: Weekly analytics
└── scripts/
├── hashtag-selector.py ← NEW: Tier selection + rotation logic
├── hashtag-validator.py ← NEW: Validates sets before posting
└── platform-formatter.py ← NEW: Formats caption + hashtags per platform
```
---
## Secrets (GitLab CI Variables + Kestra Namespace Files)
| Variable | Location | Status |
|----------|----------|--------|
| `KESTRA_API_USER` | GitLab CI | ✅ Set |
| `KESTRA_API_PASSWORD` | GitLab CI | ✅ Set |
| `KESTRA_URL` | GitLab CI | ✅ Set |
| `OPENAI_API_KEY` | GitLab CI + Kestra | ✅ Set |
| `OPENWEATHER_API_KEY` | GitLab CI + Kestra | ✅ Set |
| `GOOGLE_DRIVE_REFRESH_TOKEN` | Kestra namespace | ✅ Set (publish GCP app to prevent 7-day expiry) |
| `GOOGLE_DRIVE_CLIENT_ID` | Kestra namespace | ✅ Set |
| `GOOGLE_DRIVE_CLIENT_SECRET` | Kestra namespace | ✅ Set |
| `BUFFER_ACCESS_TOKEN` | GitLab CI + Kestra | ⬜ TODO (after Buffer signup) |
| `RITEKIT_API_KEY` | GitLab CI + Kestra | ⬜ TODO (after RiteKit signup) |
| `META_ACCESS_TOKEN` | GitLab CI + Kestra | ⬜ TODO (after FB/IG app approval) |
| `TIKTOK_ACCESS_TOKEN` | GitLab CI + Kestra | ⬜ TODO (after TikTok developer app) |
| `LINKEDIN_ACCESS_TOKEN` | GitLab CI + Kestra | ⬜ TODO (after LinkedIn API access) |
| `GOOGLE_BUSINESS_TOKEN` | GitLab CI + Kestra | ⬜ TODO (after GBP API setup) |
---
## Tech Stack
| Layer | Tool | Cost | Role |
|-------|------|------|------|
| Orchestration | Kestra (self-hosted) | Free | Cron schedules, task coordination, retry logic |
| Source control | GitLab.com | Free | Workflow YAML, config, content templates |
| Dev environment | code-server (LXC 304) | Free | Browser IDE for editing flows + scripts |
| Content storage | Google Drive (60GB) | Free | Photos, videos, logos — large binary media |
| Content generation | ChatGPT (OpenAI API) | Pay-per-use (~$0.01/caption) | Caption drafting, hashtag brainstorming |
| Local LLM (backup) | Ollama (192.168.122.20) | Free | Fallback caption gen, no API cost |
| Weather data | OpenWeatherMap | Free (1000 calls/day) | Weather-reactive content |
| Trending hashtags | RiteKit | Free (100 calls/mo) | Real-time hashtag optimization |
| TikTok trends | Apify scraper | Pay-per-run (pennies) | TikTok Creative Center trending data |
| Publishing | Buffer API | Free$30/mo | Single API → all 5 platforms |
| Publishing (backup) | Kestra plugin-meta | Free | Direct FB + IG posting |
| Project management | Plane CE | Free (self-hosted) | Annie's task board, hiring triggers |
---
## Deployment Steps (Updated)
### Phase 1: Foundation (Done)
1. ~~Create GitLab repo~~
2. ~~Push scaffold~~
3. ~~Add CI variables (Kestra, OpenAI, OpenWeather)~~
4. ~~Build + test preflight checks~~
5. ~~Build + test weather caption flow~~
### Phase 2: Publishing Pipeline (This Week)
6. Sign up for Buffer → connect platforms → add API token
7. Sign up for RiteKit → get API key
8. Build `hashtag-rotation.yml` config
9. Build `hashtag-selector.py` script (Tier 1-4 + rotation logic)
10. Build `platform-formatter.py` (per-platform caption + hashtag assembly)
11. Build full `daily-post-rotation` workflow (preflight → gen → hashtags → format → Buffer)
12. Test end-to-end: one real post on one platform
### Phase 3: Content & Handoff (Before Launch)
13. Seed content library (10+ photos, 7 caption templates)
14. Annie reviews brand voice + hashtag sets
15. Enable cron schedules
16. Set up Kestra ↔ GitLab sync (auto-pull from repo every 15 min)
17. Publish GCP OAuth app (prevent token expiry)
### Phase 4: Analytics & Optimization (After Launch)
18. Build `hashtag-performance` weekly analytics workflow
19. Set up A/B testing framework (same content, different hashtag sets)
20. Connect Buffer analytics → Kestra for automated optimization
21. Add Apify TikTok trending scraper to daily pipeline
---
## Decision Framework Score
| Gate | Score | Reason |
|------|-------|--------|
| Time | 5 | Zero daily posting effort once live — fully automated |
| Net Worth | 4 | Consistent presence builds brand → more customers at trailer |
| Freedom | 5 | Marketing runs without Sam; scales across all trailers |
| **Total** | **14** | Execute |
---
## Repos to Evaluate (from research)
| Repo | What | Action |
|------|------|--------|
| [charlie947/social-media-skills](https://github.com/charlie947/social-media-skills) | Claude skills behind 350K-follower creator | Fork `post-writer` + `post-scorer` for caption logic |
| [rampstackco/claude-skills](https://github.com/rampstackco/claude-skills) | 65 Claude skills including SEO/content | Adapt keyword research → hashtag research |
---
*Created 2026-08-13. Updated 2026-08-14 with Buffer publishing layer, hashtag engine,
and research findings.*