16 Commits

Author SHA1 Message Date
metatroncubeswdev
41da5b3968 LinkedIn: look up company Page names in one batch call
Single-Page lookups hit LinkedIn's daily per-member limit for admins of many Pages,
so the Page list fell back to ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:15:00 -04:00
metatroncubeswdev
1307146921 LinkedIn: default to the scopes Community Management API grants
The approved app has no OpenID Connect product, so openid/profile/r_organization_admin
would fail. Use r_basicprofile + rw_organization_admin and read the member from /v2/me.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 22:02:47 -04:00
metatroncubeswdev
7c596ec6d5 Settings on the site instead of .env; Odoo in Connections
- Server settings (Settings page): SMTP, AI, publishing, Meta, platform keys and,
  on the control centre only, Stripe/billing. Stored sealed in data/settings.json and
  applied over .env in every process; hosted agency workspaces don't see our email,
  platform keys or billing.
- Odoo connection per workspace in Connections (login checked before saving).
- provision-tenants.sh marks agency containers with QUORLA_HOSTED_TENANT=true.
- docs/wildcard-subdomains.html: one-time setup for automatic agency subdomains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 19:49:15 -04:00
metatroncubeswdev
2b667c655a Quota test: use today's YouTube day instead of a fixed date
The fixed 21 Oct timestamps got pruned as stale once that day passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:29:55 -04:00
metatroncubeswdev
15bc0b9bf2 YouTube comments: read with an API key instead of the channel token
commentThreads.list rejects youtube.readonly ("insufficient authentication
scopes"); it needs youtube.force-ssl, which we don't want to request.
Public comments are now read with YOUTUBE_API_KEY. Also records the
re-submitted Google verification (video v2, new scope justification).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:29:27 -04:00
metatroncubeswdev
ac7d6f3ed8 Quota test: use the current time instead of a fixed date
The rolling-window test broke once the hard-coded date was more than a day old,
because entries older than 24 h are pruned on every write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:13:51 -04:00
metatroncubeswdev
075b79f6b2 Hosted Quorla: sign-up, Stripe billing, one workspace per agency
- Control centre (QUORLA_CONTROL=true on app.quorlasocial.com): public /signup → Stripe Checkout,
  verified Stripe webhook (paid, updated, cancelled, payment failed), Admin → Agencies
  (approve without Stripe, suspend, resume, billing portal, resend welcome, remove)
- deploy/provision-tenants.sh (cron): one container per agency with its own .env and data,
  nginx map for <agency>.quorlasocial.com, upgrades agencies after a rebuild, archives removed data
- New workspaces: first admin only via the emailed one-time setup link (SETUP_TOKEN)
- Meta system user token can be saved in Settings (encrypted) for workspaces that can't edit .env
- docs/multi-tenant.md: DNS, wildcard cert, nginx, sudoers, cron, Stripe setup; tests for the control centre

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:35:23 -04:00
metatroncubeswdev
7e780549ea Brand kit and caption checks per client
- Client page → Brand kit: hashtag sets, approved phrases/CTAs, words to avoid, voice notes
- New post: one-click insert of hashtag sets and phrases, live checks while typing
- Checks: agency named in a client post (refused when saving; the agency's own account is exempt),
  avoid list, generic AI-sounding phrases, too many hashtags or emoji, platform length limits
- Brand warnings also appear on drafts; MCP tools brand_kit and caption_check for the AI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:19:04 -04:00
metatroncubeswdev
21e49b442f Inbox: Google reviews (read and reply) and YouTube comments
- Inbox tabs: Instagram & Facebook / Google reviews / YouTube comments
- Google reviews: average rating, needs-reply filter, reply or edit reply (public on Google), only on the client's location
- YouTube comments: latest on the channel, open the comment to reply on YouTube (reply needs a broader scope)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 10:15:11 -04:00
metatroncubeswdev
8b6e1cf835 Competitors page: track Instagram competitors per client
- Add/remove competitor handles (public Business/Creator accounts); refreshed nightly, one snapshot a day
- Compare with the client: followers, 30-day change, trend, posts/week, typical post (median), engagement rate, format mix
- Engagement-rate chart and each competitor's top recent posts
- MCP tools competitors and competitor_add (audits can add the AI's suggestions)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:22:33 -04:00
metatroncubeswdev
e304626986 Content calendar: plan, production and the publishing queue per client
- Plan items (idea → brief → production → ready) with brief, caption, platforms, assignee, pillar
- Calendar merges plan items, library items with a target date and queued/published posts
- Month and week views, drag a card to another day (or change the date in the card), add from any day
- MCP tools content_plan, plan_add, plan_update; weekly-plan skill now fills the calendar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:18:53 -04:00
metatroncubeswdev
894940800b TikTok: option to send posts to the TikTok app's inbox to finish there
Direct Post from an unaudited app only works for private accounts. Inbox upload lets a
person finish and post from the TikTok app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:52:17 -04:00
metatroncubeswdev
059c236d3f Quorla look for the dashboard; move a connection to another client
- Quorla logo, navy/coral colours and page titles in the app (PDF reports keep agency branding)
- Connections: Move button reassigns an account to another client without reconnecting
- Fix double @ on YouTube handles
- HANDOFF.md with current status and next steps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:32:30 -04:00
metatroncubeswdev
fc2179b343 Track platform quotas; wait and retry instead of failing
- quota.js: per-platform limits (YouTube 10,000 units/day Pacific per project,
  Instagram 100 / Threads 250 / TikTok 15 posts per 24 h per account, ...),
  usage log, .env overrides (QUOTA_<PLATFORM>)
- Publishing checks the budget first and records usage; when a platform is
  out of quota (or answers quota exceeded / 429) the post stays approved and
  the queue retries after the reset, without re-posting finished platforms
- Drafts that exceed a day's limit get a warning
- Connections page shows used/limit and when it frees up
- Tests for windows (incl. DST), shared vs per-account limits and the
  wait-and-retry queue flow; docs for quotas and the Google app setup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:56:54 -04:00
metatroncubeswdev
4da66b41a9 Add privacy/terms pages and TikTok posting-screen requirements
- Public /privacy and /terms pages (Quorla, operated by Metatroncube
  Software Solutions) for platform app listings; includes Google API
  Limited Use statement and TikTok data use
- TikTok post panel follows the Content Posting API guidelines: shows the
  creator's name/avatar, audience options from creator info with no
  default, comment/Duet/Stitch off by default and locked when the creator
  disabled them, max video length, commercial content disclosure (Your
  brand / Branded content, branded content can't be private), Music Usage
  Confirmation / Branded Content Policy consent
- Placeholder app icon (branding/quorla-icon-1024.png)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 19:14:28 -04:00
metatroncubeswdev
f24e6ada4b Add LinkedIn, Google Business Profile, YouTube, TikTok, Pinterest, Threads, Reddit
- Connections page: admins set up each platform's developer app (keys stored
  encrypted), connect a client's account via OAuth, choose company Page /
  business location, check or disconnect
- Adapters per platform with sign-in, identity, targets and publishing;
  token refresh with "reconnect needed" when a sign-in expires
- Publishing and Content library: "Post to" lists the client's connected
  platforms with their own fields and optional per-platform caption
- Queue validates drafts per platform and publishes through the adapters;
  Facebook/Instagram unchanged
- Signed, expiring media links (/m/...) for platforms that fetch media by URL
- Agent: list_connections tool; schedule tools accept all platforms + options
- Offline test suite with simulated platform APIs (15 tests)
- docs/platforms.md: setup, approvals per platform, what is and isn't tested

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:19:33 -04:00