一、今天的學習目標
延續昨天取得 /add 指令中 title、date、time 三個輸入值的功能,今天開始處理使用者輸入的日期與時間的格式是否有符合要求。我希望使用者在輸入日期與時間時只要輸入數字就好,然後系統會自己轉成可用的格式。
因此今天主要是圍繞在新增輸入格式驗證、字串切割,以及錯誤訊息的處理。
二、程式碼修改與問題處理
今天我想先替 /add 指令加入日期與時間的格式驗證。因為我希望使用者輸入日期時直接輸入 20260903,時間則輸入 1830,不需要自己加入 - 或 :,再由程式幫忙整理成後續比較好使用的格式。
因此我先分別替日期與時間建立格式規則。日期必須是 8 位數,時間必須是 4 位數。當格式不符合時,就直接回覆錯誤訊息並停止這次指令的執行,避免錯誤資料繼續往後處理。因此在修改了index.js裏面/add的部分。
if (interaction.commandName === 'add') {
const title = interaction.options.getString('title');
const date = interaction.options.getString('date');
const time = interaction.options.getString('time');
const dateRegex = /^\d{8}$/;
const timeRegex = /^\d{4}$/;
if (!dateRegex.test(date)) {
await interaction.reply(
'日期格式錯誤,請輸入 8 位數日期,例如 20260903'
);
return;
}
if (!timeRegex.test(time)) {
await interaction.reply(
'時間格式錯誤,請輸入 4 位數時間,例如 1830'
);
return;
}
const year = date.slice(0, 4);
const month = date.slice(4, 6);
const day = date.slice(6, 8);
const hour = time.slice(0, 2);
const minute = time.slice(2, 4);
const formattedDate = `${year}-${month}-${day}`;
const formattedTime = `${hour}:${minute}`;
await interaction.reply(
`行程名稱:${title}\n日期:${formattedDate}\n時間:${formattedTime}`
);
}
我先在 /add 指令中取得使用者輸入的 title、date 和 time。接著我希望日期可以直接輸入像 20260903 這樣的 8 位數字,時間則輸入像 1830 這樣的 4 位數字,再由程式自行拆解並整理格式。
因此我先使用 Regex 檢查日期是否為 8 位數、時間是否為 4 位數。如果格式錯誤,就使用 interaction.reply() 回覆提示訊息,並利用 return 停止這次指令。格式正確的話,再使用 slice() 把日期拆成年、月、日,把時間拆成時、分,最後重新組合成 YYYY-MM-DD 與 HH:mm 的格式。
但後來發現,假如同時date跟time出現錯誤時,他只會顯示date格式錯誤。

我希望使用者在兩個欄位格式都輸入錯誤時能即時發現錯誤,而不是修改完一個後送出又發現另一個有問題。
因此我修改了處理錯誤格式那邊的邏輯。原本是在偵測到第一個錯誤後就會直接回覆並使用 return 結束程式,現在則先建立一個 errors 陣列,用來暫時儲存所有檢查到的錯誤訊息。這樣就可以等所有欄位都檢查完後一起回傳。
const errors = [];
if (!dateRegex.test(date)) {
errors.push("日期格式錯誤,請輸入 8 位數日期,例如 20260903");
}
if (!timeRegex.test(time)) {
errors.push("時間格式錯誤,請輸入 4 位數時間,例如 1830");
}
if (errors.length > 0) {
await interaction.reply(errors.join("\n"));
return;
}
三、今天修改檔案的完整程式碼
index.js
require("dotenv").config();
const { Client, Events, GatewayIntentBits } = require("discord.js");
const token = process.env.DISCORD_TOKEN;
if (!token) {
console.error("錯誤:.env 裡還沒有設定 DISCORD_TOKEN");
console.error("請先到 Discord Developer Portal 複製 Bot Token,然後在 .env 加上:");
console.error("DISCORD_TOKEN=你的BotToken");
process.exit(1);
}
const client = new Client({
intents: [
GatewayIntentBits.Guilds
]
});
client.once(Events.ClientReady, () => {
console.log(`Bot 已登入:${client.user.tag}`);
});
client.on(Events.InteractionCreate, async (interaction) => {
try {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "ping") {
await interaction.reply("Pong!");
return;
}
if (interaction.commandName === "add") {
const title = interaction.options.getString("title");
const date = interaction.options.getString("date");
const time = interaction.options.getString("time");
const dateRegex = /^\d{8}$/;
const timeRegex = /^\d{4}$/;
const errors = [];
if (!dateRegex.test(date)) {
errors.push("日期格式錯誤,請輸入 8 位數日期,例如 20260903");
}
if (!timeRegex.test(time)) {
errors.push("時間格式錯誤,請輸入 4 位數時間,例如 1830");
}
if (errors.length > 0) {
await interaction.reply(errors.join("\n"));
return;
}
const year = date.slice(0, 4);
const month = date.slice(4, 6);
const day = date.slice(6, 8);
const hour = time.slice(0, 2);
const minute = time.slice(2, 4);
const formattedDate = `${year}-${month}-${day}`;
const formattedTime = `${hour}:${minute}`;
await interaction.reply(
`行程名稱:${title}\n日期:${formattedDate}\n時間:${formattedTime}`
);
}
} catch (error) {
console.error("處理指令時發生錯誤:");
console.error(error);
const errorMessage = "程式發生錯誤,請看終端機錯誤訊息。";
if (interaction.replied || interaction.deferred) {
await interaction.followUp(errorMessage);
} else {
await interaction.reply(errorMessage);
}
}
});
client.login(token).catch((error) => {
console.error("Bot 登入失敗,請檢查 Token 或網路連線。");
console.error(error.message);
process.exit(1);
});