87 lines
3.4 KiB
Markdown
87 lines
3.4 KiB
Markdown
# socialmag
|
|
|
|
MCP server that posts, reads, and replies across 6 platforms — Threads, Facebook,
|
|
Instagram, YouTube, TikTok, and Twitter/X — by driving abang's already-logged-in
|
|
Brave over CDP. No API keys, no OAuth.
|
|
|
|
## Run
|
|
|
|
```bash
|
|
cd /mnt/playground/Playground_V2
|
|
python3 -m socialmag # stdio MCP server, logs to stderr
|
|
```
|
|
|
|
⚠️ **Must run as a package.** `python3 socialmag/server.py` puts the package
|
|
dir first on `sys.path`, where `selectors.py` and `queue.py` shadow the stdlib
|
|
modules `asyncio` imports — asyncio then dies at import with
|
|
`module 'selectors' has no attribute 'SelectSelector'`. Run `-m`.
|
|
|
|
Brave must be listening on `http://127.0.0.1:9222`. Override with
|
|
`SOCIALMAG_CDP_ENDPOINT`.
|
|
|
|
Requires `websockets` (17.1 present on this box). Everything else is stdlib.
|
|
|
|
## Tools
|
|
|
|
| Tool | Description |
|
|
|------|-------------|
|
|
| `create_post` | Post to one or more platforms (threads, fb, ig, yt, tiktok, twitter) |
|
|
| `check_login` | Check which platforms are signed in |
|
|
| `get_status` | Last post results + open browser tabs |
|
|
| `get_feed` | Read Twitter timeline or search with X operators |
|
|
| `reply_tweet` | Reply to a tweet found by search |
|
|
| `get_notifications` | Check platform activity/notifications (Threads) |
|
|
| `open_link` | Navigate platform's browser tab to a URL (tab reuse) |
|
|
|
|
`create_post` — `text` is required for threads/fb/yt and used as the caption for
|
|
ig/tiktok. `media` must be an **absolute path to an existing file**; required for
|
|
ig/tiktok, optional for threads/fb, rejected for yt.
|
|
|
|
## Dashboard
|
|
|
|
`dashboard.html` is a local viewer — open it in a browser tab to see platform
|
|
status, post timeline, and Twitter intel. Reads from `dashboard.json` (written
|
|
by the server at runtime), auto-refreshes every 10 seconds.
|
|
|
|
## Layout
|
|
|
|
```
|
|
server.py MCP stdio JSON-RPC loop + 7 tools
|
|
cdp.py CDP client: CDPClient (browser) + Tab (page verbs)
|
|
queue.py PostQueue — strictly sequential, one tab at a time
|
|
selectors.py every platform selector + PLATFORMS dict, one file
|
|
platforms/ one module per platform: post() + check_login()
|
|
dashboard.html local status/timeline viewer (reads dashboard.json)
|
|
```
|
|
|
|
Each platform module exposes:
|
|
|
|
```python
|
|
async def post(cdp_client, text, media_path, options) -> {"status": ..., "detail": ...}
|
|
async def check_login(cdp_client) -> bool
|
|
```
|
|
|
|
Twitter additionally exposes `get_feed`, `reply`, and `check_login` (reusing
|
|
the existing selector set). Threads exposes `get_notifications`.
|
|
|
|
`post` opens its own tab, drives the flow, closes the tab. A failure inside the
|
|
flow is caught by `platforms.guard`, which screenshots the page as it actually
|
|
looked into `/tmp/socialmag/` and puts the path in the result.
|
|
|
|
## House rules honoured
|
|
|
|
- **No silent fallbacks.** Missing media, missing text, an unknown platform, a
|
|
selector that never appears, a CDP call that errors — all raise with the
|
|
reason. Nothing picks a default and carries on.
|
|
- Unmapped flows **refuse** rather than guess: Facebook `visibility` switching
|
|
and YouTube media both raise, because clicking through an unmapped dialog
|
|
outward-facing is worse than a stop.
|
|
- Human-like delays (`random.uniform`) between every discrete action, longer on
|
|
TikTok.
|
|
|
|
## Not built (out of the brief's scope)
|
|
|
|
- `post_youtube_video` (video upload) — the brief scoped yt to community posts.
|
|
- `socialmag.service` — explicitly deferred until after live validation.
|
|
- Scheduling, analytics, multi-account (proposal "Future").
|