前兩篇把輸入互動補齊後,這篇來做 Bot 最常見的營運需求:定時任務。
目標很明確:
- 每天早上固定發「今日任務」
- 每天凌晨重置簽到狀態
node-cron 是什麼?
node-cron 是 Node.js 的輕量排程函式庫,讓你用一行 cron 表達式 指定執行時間,不需要自己管 setInterval、時間累積或跨日邊界。
Cron 表達式格式
node-cron 使用 5 個欄位,由左到右依序是:
┌─ 分鐘 (0–59)
│ ┌─ 小時 (0–23)
│ │ ┌─ 日 (1–31)
│ │ │ ┌─ 月 (1–12)
│ │ │ │ ┌─ 星期 (0–7,0 與 7 都代表週日)
│ │ │ │ │
* * * * *
幾個常用範例:
| 表達式 | 說明 |
|---|---|
0 8 * * * | 每天 08:00 |
0 0 * * * | 每天凌晨 00:00 |
*/30 * * * * | 每 30 分鐘執行一次 |
0 9 * * 1 | 每週一 09:00 |
0 12 1 * * | 每月 1 號 12:00 |
* 代表「每個」,*/n 代表「每隔 n 個」,直接填數字就是指定值。
node-cron 也支援第 6 個欄位(秒),但預設是 5 個欄位,平常不需要精確到秒時用不到。
安裝與初始化
npm install node-cron先補 .env 參數:
# 既有設定(前幾篇已設定過)
BOT_TOKEN=你的BotToken
GUILD_ID=你的伺服器ID
FEEDBACK_CHANNEL_ID=回饋通知頻道ID
SPREADSHEET_ID=你的試算表ID
# 本篇新增
ANNOUNCE_CHANNEL_ID=每日公告頻道ID
LOG_CHANNEL_ID=排程日誌頻道ID這篇把排程邏輯獨立成一支新檔案 scheduler.js,不放在 index.js 裡,好處是:主流程保持乾淨,日後新增或調整排程只改這一支檔案就好。
import cron from "node-cron";
let schedulerStarted = false;
export function setupScheduler({ client, onDailyReset }) {
if (schedulerStarted) return;
schedulerStarted = true;
const timezone = "Asia/Taipei";
// 每天 08:00 發公告
cron.schedule(
"0 8 * * *",
async () => {
const channel = await client.channels.fetch(process.env.ANNOUNCE_CHANNEL_ID);
if (!channel?.isTextBased()) return;
await channel.send({
content: "早安!今日任務已更新,記得簽到與完成挑戰。",
});
},
{ timezone }
);
// 每天 00:00 重置簽到資料
cron.schedule(
"0 0 * * *",
async () => {
await onDailyReset();
const logChannel = await client.channels.fetch(process.env.LOG_CHANNEL_ID);
if (logChannel?.isTextBased()) {
await logChannel.send("簽到資料已完成每日重置。");
}
},
{ timezone }
);
}和既有 Bot 主流程整合
import { setupScheduler } from "./scheduler.js";
import { resetDailyCheckIn } from "./googleSheets.js";
client.once("ready", async () => {
console.log(`Bot 已上線:${client.user.tag}`);
setupScheduler({
client,
onDailyReset: resetDailyCheckIn,
});
console.log("排程任務已啟動");
});啟動後終端機會顯示 Bot 上線與排程啟動的訊息,確認排程有被正確註冊。

Google Sheets 重置函式
簡化做法:把 簽到記錄 中的日期欄清空。
export async function resetDailyCheckIn() {
const response = await sheets.spreadsheets.values.get({
spreadsheetId: process.env.SPREADSHEET_ID,
range: "簽到記錄!A:B",
});
const rows = response.data.values ?? [];
if (rows.length <= 1) return;
// 第 1 列保留表頭,從第 2 列開始清空簽到日期
const bodyRows = rows.slice(1);
const cleared = bodyRows.map((row) => [row[0], ""]);
await sheets.spreadsheets.values.update({
spreadsheetId: process.env.SPREADSHEET_ID,
range: `簽到記錄!A2:B${cleared.length + 1}`,
valueInputOption: "USER_ENTERED",
resource: { values: cleared },
});
}排程正常運作後,頻道就會依設定時間收到公告訊息。
測試說明:截圖中的排程改為
*/1 * * * *(每分鐘觸發),方便快速驗證流程是否正常,不用等到早上 08:00。確認沒問題後再改回0 8 * * *。

下一篇預告
排程加上去後,Bot 不再是被動回應工具,而是可以主動運營社群。下一篇進入 AI:串接 Groq 做 /ask 多輪對話。