Skip to content

@robojs/announcements — Scheduled announcements for Discord servers #447

Description

@Pkmmte

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/:idScheduleRecord
  • 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

  1. 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.
  2. 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).
  3. Fire (when a job triggers)

    • Build the message payload (content, embeds, allowedMentions) and channel.send(...) it.
    • Update lastRunAt and compute nextRunAt (if cron schedule).
  4. 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

  • packages/@robojs/announcements/ plugin code with tests and README.

  • Examples folder:

    • Minimal bot showing /announce schedule and /announce run-now.
    • With-cron demo project.
  • Docs PR to add to the Plugin Directory and a dedicated page with install/usage.


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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions