Scheduled messages
stateScheduler and scheduledAdapter, so thread.schedule() works on any Chat SDK adapter, with the messages held in your state adapter until a cron job posts them.
Chat SDK's thread.schedule(message, { postAt }) calls the adapter's
scheduleMessage, and only some platforms have one. scheduledAdapter
gives any adapter, the inbox adapter
included, a scheduleMessage that holds the message in a
ChatScheduler. publishDue posts the due ones through each adapter's
postMessage.
import { Chat } from "chat";
import {
createSupabaseState,
scheduledAdapter,
stateScheduler,
} from "better-supabase/chat-sdk";
const state = createSupabaseState({ transport });
export const scheduler = stateScheduler(state);
export const bot = new Chat({
userName: "acme",
state,
adapters: {
inbox: scheduledAdapter(inboxBot, scheduler),
telegram: scheduledAdapter(createTelegramAdapter(), scheduler),
},
});
bot.onNewMention(async (thread) => {
const reminder = await thread.schedule("Did that fix it?", {
postAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
});
await thread.setState({ reminder: reminder.scheduledMessageId });
});scheduledAdapter changes the adapter in place and returns it, because
Chat SDK hands threads the instance the adapter's webhook passes along,
not a wrapper. It replaces a native scheduleMessage, so leave out
adapters whose platform schedules natively if you'd rather use that.
Posting due messages
Run publishDue from a cron job, or pass the scheduler to
maintain as scheduled:
import { bot, scheduler } from "@/lib/bot";
export async function GET() {
return Response.json(await scheduler.publishDue(bot));
}It returns { published, failed, dropped }. Each due message goes out at
most once unless its adapter throws; then it waits for the next run, and
after maxAttempts (3 by default) failures, or when no adapter has its
name any more, it is dropped. A message posts no earlier than postAt and
no later than the next run after it, so the cron interval is the delay you
accept.
The scheduler
| Method | What it does |
|---|---|
schedule({ adapter, threadId, message, postAt }) | Holds a message; postAt must be in the future |
pending() | The waiting messages, soonest first |
cancel(id) | Removes one; false when it was already sent or cancelled |
publishDue(chat, { now?, limit? }) | Posts what is due, at most limit (100) per call |
stateScheduler(state, { key?, maxAttempts?, lockWaitMs? }) keeps the
waiting messages as one value under key (chat:scheduled) in any Chat SDK
StateAdapter, changed under the state's lock. That suits hundreds of
waiting messages, not millions. For more, implement ChatScheduler over
your own table or a queue with delayed jobs.
The inbox's own scheduled replies are a separate
feature: staff schedule them as themselves, and maintain sends them.
Last updated on
Inbox adapter
inboxAdapter, a Chat SDK adapter whose threads are inbox conversations, so a bot answers visitors in the in-app widget and staff can take over.
Channels
Record Slack, WhatsApp, Messenger and SMS conversations in the inbox with Chat SDK adapters, deliver staff replies on their channel and keep the event store clean.