JS was sending browser-local time; dispatcher compared against server UTC,
so posts scheduled in UTC+3 appeared 3h in the future to the dispatcher.
- JS helpers: utcToDate, utcToLocalInput, localInputToUTC
- All scheduled_at values sent to server are now converted to UTC
- Stored values are displayed converted back to browser local time
- relTime and fmtDT parse stored UTC timestamps correctly
- dispatch-social.php: gmdate() for $now (UTC) + sent_at
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
include_image was always 0 because users had to manually tick the
per-platform image checkbox after uploading — not obvious. Now uploading
an image auto-checks all targets; users can still uncheck individually.
Also removes debug logging from dispatch-social.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Discord: payload_json was missing the attachments array, so uploaded
files were orphaned and not shown in the message (Discord API v10 requirement).
Mastodon: media_ids[] sent as URL-encoded form was unreliable — switch to
JSON body. Also poll /api/v1/media/:id when upload returns 202 (async) to
wait until the image is processed before attaching it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
datetime-local sends ISO format with T separator; normalize to space on save
and use SQLite datetime() in dispatcher query to handle both formats.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- When LinkedIn is connected, replace the Connect button with a pencil
(test post to first personal profile) and a reconnect arrow icon
- Add Process Now button to the Schedule tab that shells out to the cron
script on demand and shows its output inline
- Add flock() to the cron script to prevent overlapping runs
- Fix cron install path comment to match deployed location
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Scheduled posts: new Schedule tab with multi-platform post scheduling (Discord,
Mastodon, LinkedIn); SQLite queue with per-target delivery tracking and retry;
cron script in bin/process-scheduled-posts.php processes due targets inline
- Discord channels: new config section for webhook-based channels used as scheduled
post targets; webhook URLs stored in .env as DISCORD_WEBHOOK_<KEY>
- LinkedIn: fix botcompose_post to use member_id (urn:li:person) for personal
profiles instead of hardcoded org author; filter org pages out of compose UI
and RSS modal picker since org posting is unsupported; label org pages clearly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes local/prod cases — script now just does git pull, writes BUILD,
and runs migrate wherever it's executed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add empty UA early return and a comprehensive list of bot signals covering
HTTP libraries, SEO platforms, uptime monitors, security scanners, AI
crawlers, and generic scanner vocabulary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>