Documentation Index Fetch the complete documentation index at: https://mintlify.com/Kismetkanceled/geniehelper/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Genie Helperβs post scheduler polls the scheduled_posts collection every 60 seconds and automatically publishes posts to X, Reddit, Instagram, TikTok, and other platforms using Stagehand browser automation.
Collection : scheduled_posts
Worker : Media worker (BullMQ)
Poll interval : 60 seconds (configurable via POST_SCHEDULER_MS)
Architecture
Scheduling Flow
Dashboard Content Calendar
β
User creates post (AI-generated or manual)
β
Scheduled_posts record created (status: draft)
β
User sets scheduled_time + clicks "Schedule Post"
β
Status changes to: scheduled
β
Post scheduler polls every 60s
β
When scheduled_time β€ now:
β
Create media_jobs record (operation: publish_post)
β
Media worker picks job
β
Stagehand session starts
β
Inject cookies from platform_sessions
β
Navigate to platform compose page
β
Fill caption + attach media (if provided)
β
Click "Post" button
β
Update scheduled_posts: status=posted, posted_at=now
Collections
scheduled_posts
Field Type Description idUUID Primary key platformString Target platform (x, reddit, instagram, tiktok) content_textText Post caption/body scheduled_timeDateTime When to publish (null = draft) statusString draft | scheduled | publishing | posted | failedmedia_idFK Directus file ID (optional) clip_job_idFK Media job ID for platform-specific clip (optional) platform_metaJSON Platform-specific params (see below) posted_atDateTime Actual publish timestamp error_messageText Failure reason (if status=failed) creator_idFK Directus user ID
{
"subreddit" : "OnlyFans101" ,
"post_type" : "text" ,
"title" : "New content drop this weekend!"
}
Post Scheduler
Implementation : media-worker/index.js:1186-1241
Polling Logic
media-worker/index.js (Lines 1195-1222)
setInterval ( async () => {
try {
const now = new Date (). toISOString ();
// Find all posts due for publishing
const res = await dAPI ( "GET" ,
`/items/scheduled_posts?filter[status][_eq]=scheduled&filter[scheduled_time][_lte]= ${ now } &limit=10&fields=id,platform,content_text,platform_meta,media_id,clip_job_id,creator_id`
);
const due = res ?. data || [];
for ( const post of due ) {
// Create publish_post job
await dAPI ( "POST" , "/items/media_jobs" , {
operation: "publish_post" ,
status: "queued" ,
params: {
scheduled_post_id: post . id ,
platform: post . platform ,
content_text: post . content_text ,
platform_meta: post . platform_meta || {},
creator_profile_id: post . creator_profile_id ,
},
priority: 1 ,
});
console . log ( `[post-scheduler] queued publish job for post= ${ post . id } platform= ${ post . platform } ` );
}
} catch ( err ) {
console . error ( `[post-scheduler] poll error: ${ err . message } ` );
}
}, POST_SCHEDULER_MS );
Publishing Operation
Operation : publish_post (media-worker)
Browser : Local Playwright (headless Chrome)
Vision LLM : ollama/qwen-2.5
Platform Status Compose URL Notes X (Twitter) β
Full https://x.com/compose/tweet280 char limit Reddit β
Full https://reddit.com/r/{subreddit}/submitRequires subreddit + title Instagram π§ Partial Web posting disabled by Meta API required TikTok π§ Partial Desktop upload limited Mobile flow needed OnlyFans π
Planned Cookie auth required Fansly π
Planned Cookie auth required
Implementation : media-worker/index.js:879-890
media-worker/index.js (Lines 879-890)
async function postToX ( sid , content_text ) {
// Navigate to compose page
await publishSPost ( `/v1/sessions/ ${ sid } /navigate` , {
url: "https://x.com/compose/tweet"
});
// Fill tweet text
await publishSPost ( `/v1/sessions/ ${ sid } /act` , {
action: `Type the following text into the tweet compose box: " ${ content_text . replace ( /"/ g , ' \\ "' ) } "` ,
modelName: PUBLISH_STAGEHAND_MODEL ,
});
// Submit
await publishSPost ( `/v1/sessions/ ${ sid } /act` , {
action: "Click the Post button to submit the tweet" ,
modelName: PUBLISH_STAGEHAND_MODEL ,
});
return { platform: "x" , status: "posted" };
}
Reddit Publishing
Implementation : media-worker/index.js:893-920
media-worker/index.js (Lines 893-920)
async function postToReddit ( sid , content_text , platform_meta = {}) {
const subreddit = platform_meta . subreddit || "u_me" ;
const post_type = platform_meta . post_type || "text" ;
const url = `https://www.reddit.com/r/ ${ subreddit } /submit?type= ${ post_type } ` ;
await publishSPost ( `/v1/sessions/ ${ sid } /navigate` , { url });
if ( post_type === "text" ) {
// Fill title
await publishSPost ( `/v1/sessions/ ${ sid } /act` , {
action: `Fill in the title field with: " ${ platform_meta . title || content_text . slice ( 0 , 100 ) } "` ,
modelName: PUBLISH_STAGEHAND_MODEL ,
});
// Fill body
await publishSPost ( `/v1/sessions/ ${ sid } /act` , {
action: `Fill in the body/text field with: " ${ content_text } "` ,
modelName: PUBLISH_STAGEHAND_MODEL ,
});
}
// Submit
await publishSPost ( `/v1/sessions/ ${ sid } /act` , {
action: "Click the Post button to submit" ,
modelName: PUBLISH_STAGEHAND_MODEL ,
});
return { platform: "reddit" , subreddit , status: "posted" };
}
Cookie Injection
The worker automatically loads cookies from platform_sessions to bypass login:
media-worker/index.js (Lines 937-949)
if ( creator_profile_id ) {
try {
const sessRes = await dAPI ( "GET" ,
`/items/platform_sessions?filter[creator_profile_id][_eq]= ${ creator_profile_id } &filter[platform][_eq]= ${ platform } &filter[status][_eq]=active&limit=1&fields=encrypted_cookies`
);
const sess = sessRes ?. data ?.[ 0 ];
if ( sess ?. encrypted_cookies ) {
const plain = decryptCredentials ( sess . encrypted_cookies );
if ( plain ?. cookies ) {
cookies = plain . cookies ;
}
}
} catch { /* no cookies β will fall back to plain navigation */ }
}
// Inject into Playwright session
if ( cookies . length > 0 ) {
await publishSPost ( `/v1/sessions/ ${ sid } /cookies` , { cookies });
}
Content Calendar UI
Page : dashboard/src/pages/ContentCalendar/index.jsx
Features
AI Caption Generator (Modal 3A)
Platform selector (TikTok, Instagram, X, Reddit, OnlyFans)
Tone selector (flirty, playful, intimate, professional, casual, teasing)
Topic input (optional)
Length picker (short, medium, long)
Try Again feedback loop with adjustment notes
Post Draft Editor (Modal 3B)
Platform specs validation (text limits, video dimensions)
Media attachment from scraped_media
Platform-specific clip generator (9:16 for TikTok/Snapchat)
Schedule time picker
Usage pill (plan limits)
Media Picker (Modal 3C)
Grid view of scraped_media
Search filter
Video duration + codec display
Plan Limits
Tracked in : usage API (/api/usage/increment)
Tier Posts/Month Queue Size Starter 3 3 Creator 30 Unlimited Pro 150 Unlimited Studio Unlimited Unlimited
Enforcement : dashboard/src/pages/ContentCalendar/index.jsx:220-227
ContentCalendar/index.jsx (Lines 220-227)
async function handleSave ( scheduleNow ) {
if ( limitHit && scheduleNow ) {
return alert ( 'Post limit reached for this month. Upgrade your plan.' );
}
if ( scheduleNow ) {
const inc = await incrementUsage ( 'post_creations' );
if ( ! inc . ok ) return alert ( 'Post limit reached. Upgrade.' );
}
await posts . create ({ ... draftPost , status: scheduleNow ? 'scheduled' : 'draft' });
}
AI Caption Generation
Endpoint : /api/captions/generate
Model : dolphin-mistral:7b (uncensored content writer)
Request
dashboard/src/pages/ContentCalendar/index.jsx
const res = await fetch ( '/api/llm/api/captions/generate' , {
method: 'POST' ,
headers: {
'Content-Type' : 'application/json' ,
Authorization: `Bearer ${ token } `
},
body: JSON . stringify ({
platform: 'tiktok' ,
tone: 'flirty' ,
topic: 'new photo set' ,
length: 'medium'
})
});
const { caption } = await res . json ();
// "Can't wait to show you what I've been working on... π New set drops tonight. Link in bio π #newcontent #exclusive"
Feedback Loop
After first generation, user can click βTry Againβ β Enters feedback note β Re-generates with adjustment:
ContentCalendar/index.jsx (Lines 477-505)
async function generateContent ( feedbackNote ) {
setGenerating ( true );
const res = await fetch ( '/api/llm/api/captions/generate' , {
method: 'POST' ,
body: JSON . stringify ({
platform: genPrompt . platform ,
tone: genPrompt . tone ,
topic: feedbackNote
? ` ${ genPrompt . topic } [Adjustment: ${ feedbackNote } ]` // Inject feedback
: genPrompt . topic ,
length: genPrompt . length ,
}),
});
const data = await res . json ();
setGeneratedText ( data . caption );
}
Example feedback : βMake it shorter, add a call to action, more emojisβ
Status Flow
draft
β (user clicks "Schedule Post")
scheduled
β (scheduler picks up)
publishing
β (Stagehand completes)
posted β
OR
β (error)
failed β οΈ
Error Messages : Stored in scheduled_posts.error_message
Common failures:
HITL_REQUIRED β No cookies available
Stagehand timeout β Page load failed
Auto-posting to {platform} not yet implemented β Platform stub
Logs & Debugging
View scheduler logs
Check scheduled posts (SQL)
pm2 logs media-worker | grep post-scheduler
# Watch polling in real-time
pm2 logs media-worker -f | grep -E "post-scheduler|publish_post"