A first-class Robo.js plugin to schedule one-off or recurring announcements to specific Discord channels via slash commands, with safe persistence, time zones, and admin-friendly UX (list/pause/resume/run-now). Scheduling features are only available if @robojs/cron is installed.
TL;DR / Goals
- Provide a drop-in plugin that lets server moderators/admins schedule announcements for their guilds.
- Support one-off (date/time) and recurring (cron) posts.
- Store settings and schedules durably using Flashcore with per-guild namespaces.
- Slash commands for human admins,
- Optional HTTP API endpoints (automatically enabled when
@robojs/server is installed) so communities can build a custom dashboard later (like @robojs/ai does).
- Keep defaults sensible and Robo-native (file-based, Sage replies, middleware guards).
Important
-
This tool is only for server moderators/admins. Regular users cannot create announcements.
-
@robojs/cron** is optional.** The plugin auto-detects it. If not present, scheduling commands will be hidden/disabled (only immediate posting/preview remains).
Motivation
Every community needs recurring messages (events, patch notes, weekly reminders). Building this repeatedly per-bot is wasteful. A standardized plugin gives teams a clean, Robo-idiomatic way to add scheduled posts with minimal code.
Scope
In scope (MVP)
- Slash commands to create, list, pause/resume, cancel, and run now announcements.
- One-off (ISO datetime) and recurring (cron expression) schedules.
- Per-guild time zone configuration with per-schedule override.
- Post to text channels with message content, optional embeds (JSON), and allowed mentions controls.
- Durable storage in Flashcore; rehydrate schedules at boot.
- Optional HTTP API endpoints (enabled only if
@robojs/server is installed) to power future dashboards.
Out of scope for MVP
- First-party dashboard UI (we only expose API for others to build one).
- Auto cross-posting to announcement channels (could be a follow-up).
- Rich templating engines (keep to basic placeholder interpolation for now).
Success Criteria
- Plugin installs with
npx robo add @robojs/announcements (once published) and appears in the Plugins Directory.
- Admins can create one-off and cron schedules via slash commands.
- Schedules persist and rehydrate on restart.
- Commands are guild-only and restricted to members with Manage Guild (configurable).
- If
@robojs/server is installed, API endpoints respond with the documented shapes.
- If
@robojs/cron is not installed, scheduling features are disabled and the plugin degrades gracefully.
User Stories
- As a server admin, I can schedule a recurring weekly announcement to #events every Friday at 10:00 America/Los_Angeles.
- As a moderator, I can pause, resume, cancel, and run a schedule immediately for testing.
- As an admin, I can list schedules with IDs, names, and next run times.
Installation & Setup (expected)
# after publish
npx robo add @robojs/announcements
Environment requirements:
- Optional: add
@robojs/cron if you want scheduling.
- Optional: add
@robojs/server if you want HTTP endpoints to build a dashboard later.
High-Level Architecture (suggested, not mandatory)
This file layout is a suggestion to guide contributors; you do not have to follow it exactly as long as the public behavior and acceptance criteria remain intact.
@robojs/announcements
├─ config/
│ └─ announcements.mjs # internal defaults (used by the plugin)
├─ src/
│ ├─ commands/announce/
│ │ ├─ schedule.js # create/update schedules
│ │ ├─ list.js # paginated list
│ │ ├─ pause.js # pause by id
│ │ ├─ resume.js # resume by id
│ │ ├─ cancel.js # delete by id
│ │ └─ run-now.js # force run for testing
│ ├─ events/ready.js # boot: rehydrate schedules (if cron installed)
│ ├─ middleware/guard.js # guild-only + ManageGuild
│ ├─ lib/cron.js # thin wrapper around @robojs/cron (optional)
│ ├─ lib/dispatch.js # simple channel send (no RL logic in MVP)
│ ├─ lib/store.js # Flashcore helpers + namespacing
│ └─ api/announcements.server.js # HTTP endpoints (only if @robojs/server is present)
└─ README.md
Key ideas
- Scheduler: delegate to
@robojs/cron when present. Without it, hide or disable scheduling commands and leave only /announce run-now.
- Storage: use Flashcore for durable KV: schedules, guild settings, and run logs. Namespace by guild ID.
- Dispatch: minimal wrapper around
channel.send(...) to post content/embeds/allowedMentions.
- Permissions/Middleware: guard all commands to guilds and members with Manage Guild.
Configuration
The host bot’s user-facing configuration for this plugin lives at:
/config/plugins/robojs/announcements.ts
Example shape (TypeScript):
// /config/plugins/robojs/announcements.ts
export default {
permissions: { defaultMemberPermissions: ['ManageGuild'] },
defaultTz: 'UTC',
maxEmbeds: 5,
features: {
// Scheduling is auto-enabled only if @robojs/cron is detected.
// No explicit boolean needed, but you may override the behavior here if desired.
allowImmediateRun: true
},
api: {
enabled: true, // only takes effect if @robojs/server is installed
basePath: '/api/announcements'
}
}
The plugin may expose its own config/announcements.mjs for internal defaults, but the **source of truth for projects is /config/plugins/robojs/announcements.ts.
Data Model (Flashcore)
Namespace: announcements:<guildId>/*
guild:settings → { defaultTz?: string, defaultChannelId?: string, requiresRoleId?: string | null }
schedules → Array of ScheduleSummary (IDs/names for quick list)
schedule:<id> → ScheduleRecord:
type ScheduleRecord = {
id: string
guildId: string
name?: string
channelId: string
createdBy: string
type: 'once' | 'cron'
once?: { runAtISO: string } // only if type=once
cron?: { expr: string; tz: string } // only if type=cron
content: string
embeds?: APIEmbed[]
allowedMentions?: AllowedMentions
paused: boolean
createdAt: string
lastRunAt?: string
nextRunAt?: string
}
Slash Commands (Admin-only)
All commands are guild-only and default to Manage Guild permission.
/announce schedule
Create or update a schedule.
Options
channel (Channel) — target channel (required)
type (Choice) — once | cron (required)
when (String) — ISO date/time like 2025-10-31 10:00 (if type=once)
cron (String) — cron expression (if type=cron)
tz (String) — IANA time zone (fallback: guild default)
name (String) — optional label
content (String) — message body
embeds (String) — JSON for embeds (validated)
allowedMentions (String) — JSON for allowed mentions
preview (Boolean) — ephemeral preview before saving
Behavior
- If
@robojs/cron is not installed and the user chooses type=cron, return a helpful error suggesting to install the cron plugin.
- If
type=once, accept the time even without cron and store the record; the plugin may fall back to immediate posting if cron is missing, or ask the admin to install cron (choose and document one behavior). For MVP, prefer explicit error + guidance when cron is missing.
/announce list [page] [mine]
Lists schedules with pagination; mine filters to schedules created by the invoker.
/announce pause <id> / /announce resume <id>
Toggles paused (if cron is available). Without cron, show a helpful message about installing it.
/announce cancel <id>
Deletes the schedule and removes it from the in-memory registry (and cron if present).
/announce run-now <id>
Enqueues immediate send of a schedule (no scheduling required).
Optional HTTP API Endpoints (enabled when @robojs/server is installed)
The plugin should register endpoints under config.api.basePath (default /api/announcements). Authentication is minimal: an API token header (e.g., x-robo-key) checked against an environment variable in the host. This mirrors the spirit of @robojs/ai, letting others build dashboards later.
Authentication
- If
api.enabled is true and @robojs/server is detected, all endpoints require x-robo-key: <token>.
Routes
GET /schedules?guildId=<id>&page=<n> → { items: ScheduleSummary[], nextPage?: number }
POST /schedules → body ScheduleRecord (minus computed fields) → { id }
GET /schedules/:id → ScheduleRecord
PATCH /schedules/:id → partial updates → ScheduleRecord
DELETE /schedules/:id → { ok: true }
POST /schedules/:id/run → { ok: true }
Notes
- When cron isn’t installed and a
type=cron schedule is posted, return an error { code: 'CRON_MISSING' }.
- All operations are scoped by
guildId and respect the plugin’s namespacing.
Execution Flow
-
Create/Update
- Validate permissions + channel.
- Parse
type=once|cron. If cron requested but plugin not present, return guidance.
- Persist
ScheduleRecord in Flashcore and maintain a lightweight in-memory index for fast listing.
- If cron is available and the schedule isn’t paused, register/refresh the job.
-
Boot/Rehydrate
- On
ready, load guild schedules. If cron is present, register jobs for all non-paused schedules.
- Missed runs while offline: MVP policy is skip missed (documented for clarity).
-
Fire (when a job triggers)
- Build the message payload (content, embeds, allowedMentions) and
channel.send(...) it.
- Update
lastRunAt and compute nextRunAt (if cron schedule).
-
List/Pause/Resume/Cancel
- Update Flashcore and reconcile in-memory index and cron registrations accordingly.
Examples
Minimal schedule (slash)
/announce schedule channel:#events type:cron cron:"0 10 * * FRI" tz:"America/Los_Angeles" name:"Weekly Events" content:"Don’t miss this week’s meetup!"
One-off with preview
/announce schedule channel:#announcements type:once when:"2025-11-01 09:00" tz:"UTC" preview:true content:"We launch today!"
Embeds JSON (simple)
[
{ "title": "Patch Notes", "description": "- Fix A\n- Improve B" }
]
Middleware & Permissions
- Guild-only guard.
- Default command permissions require Manage Guild (overridable in
/config/plugins/robojs/announcements.ts).
- Validate and sanitize embeds/mentions JSON.
Testing Plan
Encouraged but not required. We welcome PRs without tests for Hacktoberfest, but adding unit tests will earn bonus points in review.
- Unit (encouraged): schedule parsing (cron/once), TZ handling, embed/mentions validation, Flashcore helpers.
- Integration: with
@robojs/cron installed — create schedules, verify next run computation, pause/resume/cancel.
- Without cron: ensure commands correctly warn or hide cron features;
/announce run-now still works.
- API (if server installed): exercise all routes with token auth; verify guild scoping.
Deliverables
Future Ideas
- Cross-posting to announcement channels.
- Threaded posts (option to auto-create a thread per announcement).
- Portal toggles (enable/disable module at runtime).
References (for contributors)
- Robo.js: Getting Started, Discord Bots, Plugins/Modules, Middleware, Flashcore, Modes, Portal.
@robojs/cron: plugin docs & examples.
@robojs/server: file-based HTTP routes.
- discord.js: Channel sending, message payloads, embeds, allowed mentions.
A first-class Robo.js plugin to schedule one-off or recurring announcements to specific Discord channels via slash commands, with safe persistence, time zones, and admin-friendly UX (list/pause/resume/run-now). Scheduling features are only available if
@robojs/cronis installed.TL;DR / Goals
@robojs/serveris installed) so communities can build a custom dashboard later (like@robojs/aidoes).Motivation
Every community needs recurring messages (events, patch notes, weekly reminders). Building this repeatedly per-bot is wasteful. A standardized plugin gives teams a clean, Robo-idiomatic way to add scheduled posts with minimal code.
Scope
In scope (MVP)
@robojs/serveris installed) to power future dashboards.Out of scope for MVP
Success Criteria
npx robo add @robojs/announcements(once published) and appears in the Plugins Directory.@robojs/serveris installed, API endpoints respond with the documented shapes.@robojs/cronis not installed, scheduling features are disabled and the plugin degrades gracefully.User Stories
Installation & Setup (expected)
# after publish npx robo add @robojs/announcementsEnvironment requirements:
@robojs/cronif you want scheduling.@robojs/serverif you want HTTP endpoints to build a dashboard later.High-Level Architecture (suggested, not mandatory)
Key ideas
@robojs/cronwhen present. Without it, hide or disable scheduling commands and leave only/announce run-now.channel.send(...)to post content/embeds/allowedMentions.Configuration
/config/plugins/robojs/announcements.tsExample shape (TypeScript):
Data Model (Flashcore)
Namespace:
announcements:<guildId>/*guild:settings→{ defaultTz?: string, defaultChannelId?: string, requiresRoleId?: string | null }schedules→ Array ofScheduleSummary(IDs/names for quick list)schedule:<id>→ScheduleRecord:Slash Commands (Admin-only)
/announce scheduleCreate or update a schedule.
Options
channel(Channel) — target channel (required)type(Choice) —once|cron(required)when(String) — ISO date/time like2025-10-31 10:00(iftype=once)cron(String) — cron expression (iftype=cron)tz(String) — IANA time zone (fallback: guild default)name(String) — optional labelcontent(String) — message bodyembeds(String) — JSON for embeds (validated)allowedMentions(String) — JSON for allowed mentionspreview(Boolean) — ephemeral preview before savingBehavior
@robojs/cronis not installed and the user choosestype=cron, return a helpful error suggesting to install the cron plugin.type=once, accept the time even without cron and store the record; the plugin may fall back to immediate posting if cron is missing, or ask the admin to install cron (choose and document one behavior). For MVP, prefer explicit error + guidance when cron is missing./announce list [page] [mine]Lists schedules with pagination;
minefilters to schedules created by the invoker./announce pause <id>//announce resume <id>Toggles
paused(if cron is available). Without cron, show a helpful message about installing it./announce cancel <id>Deletes the schedule and removes it from the in-memory registry (and cron if present).
/announce run-now <id>Enqueues immediate send of a schedule (no scheduling required).
Optional HTTP API Endpoints (enabled when
@robojs/serveris installed)Authentication
api.enabledis true and@robojs/serveris detected, all endpoints requirex-robo-key: <token>.Routes
GET /schedules?guildId=<id>&page=<n>→{ items: ScheduleSummary[], nextPage?: number }POST /schedules→ bodyScheduleRecord(minus computed fields) →{ id }GET /schedules/:id→ScheduleRecordPATCH /schedules/:id→ partial updates →ScheduleRecordDELETE /schedules/:id→{ ok: true }POST /schedules/:id/run→{ ok: true }Notes
type=cronschedule is posted, return an error{ code: 'CRON_MISSING' }.guildIdand respect the plugin’s namespacing.Execution Flow
Create/Update
type=once|cron. If cron requested but plugin not present, return guidance.ScheduleRecordin Flashcore and maintain a lightweight in-memory index for fast listing.Boot/Rehydrate
ready, load guild schedules. If cron is present, register jobs for all non-paused schedules.Fire (when a job triggers)
channel.send(...)it.lastRunAtand computenextRunAt(if cron schedule).List/Pause/Resume/Cancel
Examples
Minimal schedule (slash)
One-off with preview
Embeds JSON (simple)
[ { "title": "Patch Notes", "description": "- Fix A\n- Improve B" } ]Middleware & Permissions
/config/plugins/robojs/announcements.ts).Testing Plan
@robojs/croninstalled — create schedules, verify next run computation, pause/resume/cancel./announce run-nowstill works.Deliverables
packages/@robojs/announcements/plugin code with tests and README.Examples folder:
/announce scheduleand/announce run-now.Docs PR to add to the Plugin Directory and a dedicated page with install/usage.
Future Ideas
References (for contributors)
@robojs/cron: plugin docs & examples.@robojs/server: file-based HTTP routes.