Slash 指令可讓使用者透過 Discord 內建的命令介面與機器人互動。你在任何頻道輸入 /,就會看到機器人的指令,並附帶說明、參數提示與自動補完。
運作方式
- 你在 JSON 檔案中定義指令
- 將檔案傳給
discli serve --slash-commands commands.json - discli 啟動時會向 Discord 註冊這些指令
- 當使用者叫用指令時,會收到
slash_command事件 - 你使用
interaction_followup回應
步驟 1:定義指令
建立一個描述 Slash 指令的 JSON 檔案:
[ { "name": "ask", "description": "向機器人提問", "params": [ { "name": "question", "type": "string", "description": "你的問題", "required": true } ] }, { "name": "status", "description": "檢查機器人狀態" }, { "name": "search", "description": "搜尋近期訊息", "params": [ { "name": "query", "type": "string", "description": "搜尋關鍵字", "required": true }, { "name": "limit", "type": "integer", "description": "最大結果數(預設 10)", "required": false } ] }]指令結構
每個指令物件支援:
| 欄位 | 類型 | 是否必填 | 說明 |
|---|---|---|---|
name | string | 是 | 指令名稱(小寫,不能有空格,1–32 字) |
description | string | 是 | 指令用途(1–100 字) |
params | array | 否 | 參數清單 |
參數結構
每個參數支援:
| 欄位 | 類型 | 是否必填 | 說明 |
|---|---|---|---|
name | string | 是 | 參數名稱 |
type | string | 是 | string、integer、number、或 boolean |
description | string | 是 | 參數用途 |
required | boolean | 否 | 預設值:true |
步驟 2:以 Slash 指令啟動 serve
discli serve --slash-commands commands.json啟動後,discli 會:
- 連線到 Discord 並送出
ready事件 - 在機器人加入的每個伺服器中註冊 Slash 指令
- 註冊完成後送出
slash_commands_synced事件
{"event": "ready", "bot_id": "123456", "bot_name": "MyBot#1234"}{"event": "slash_commands_synced", "count": 3, "guilds": 2}discli 會將指令逐個同步到每個伺服器,達到即時可用。若改用全域註冊(未指定伺服器),Discord 可能要花到 1 小時才能完成傳播。透過直接逐伺服器同步,指令會在 slash_commands_synced 事件之後立即可用。
步驟 3:處理 Slash 指令事件
當使用者叫用 Slash 指令時,你會收到 slash_command 事件:
{ "event": "slash_command", "command": "ask", "args": { "question": "What is discli?" }, "channel_id": "444555666", "user": "alice#1234", "user_id": "777888999", "guild_id": "111222333", "interaction_token": "abc-123-def", "is_admin": false}主要欄位:
| 欄位 | 說明 |
|---|---|
command | Slash 指令名稱 |
args | 參數名稱到值的字典(字串) |
interaction_token | 用來回應該互動的唯一 token |
is_admin | 是否為叫用者有伺服器管理員權限 |
channel_id | 指令叫用的頻道 |
user / user_id | 呼叫者身份 |
步驟 4:回應互動
使用者叫用指令時,互動會自動顯示「思考中」提示。你可用以下三種行為回應互動:
interaction_followup
送出一則可見回覆。支援 embed 與組件:
{"action": "interaction_followup", "interaction_token": "abc-123-def", "content": "Here's your answer!"}{"action": "interaction_followup", "interaction_token": "abc-123-def", "content": "", "embed": {"title": "Results", "description": "Found 5 matches", "color": 5865F2}, "components": [{"type": "action_row", "components": [{"type": "button", "style": "primary", "label": "Next Page", "custom_id": "next_page"}]}]}interaction_respond
送出僅供叫用者可見的暫時訊息:
{"action": "interaction_respond", "interaction_token": "abc-123-def", "content": "只有你能看到這則訊息", "ephemeral": true}interaction_edit
編輯原始互動回覆,常見於按鈕或內容在使用者操作後更新:
{"action": "interaction_edit", "interaction_token": "abc-123-def", "content": "Updated!", "components": []}互動 token 會在 15 分鐘後過期。 若你沒有在 15 分鐘內送出 followup,互動會失敗,使用者會看到「應用程式未回應」。無論成功與否都應盡速回應,哪怕只是回報已收到。
完整可執行範例
import jsonimport subprocessimport threadingfrom queue import Queue
class SlashBot: def __init__(self, commands_file): self.proc = subprocess.Popen( ["discli", "serve", "--slash-commands", commands_file, "--events", "messages"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True, ) self.events = Queue() self.pending = {} self._counter = 0 threading.Thread(target=self._reader, daemon=True).start()
def _reader(self): for line in self.proc.stdout: data = json.loads(line.strip()) rid = data.get("req_id") if rid in self.pending: self.pending[rid].put(data) else: self.events.put(data)
def send(self, action, wait=False): self._counter += 1 rid = f"r{self._counter}" action["req_id"] = rid if wait: q = Queue() self.pending[rid] = q self.proc.stdin.write(json.dumps(動作) + "\n") self.proc.stdin.flush() if wait: result = q.get(timeout=15) del self.pending[rid] return result return None
def followup(self, token, content): """送出 slash command 互動的 followup 回覆。""" self.send({ "action": "interaction_followup", "interaction_token": token, "content": content, })
# 定義指令處理函式def handle_ask(bot, event): question = event["args"].get("question", "") # 實際環境中可接 AI 模型呼叫 answer = f"你問了:{question}\n\nAI 回覆內容可在這裡產生。" bot.followup(event["interaction_token"], answer)
def handle_status(bot, event): servers = bot.send({"action": "server_list"}, wait=True) count = len(servers.get("servers", [])) bot.followup( event["interaction_token"], f"已上線,正在監控 {count} 個伺服器。", )
def handle_search(bot, event): query = event["args"].get("query", "") limit = int(event["args"].get("limit", "10")) result = bot.send({ "action": "message_search", "channel_id": event["channel_id"], "query": query, "limit": limit, }, wait=True)
messages = result.get("messages", []) if not messages: bot.followup(event["interaction_token"], f"找不到符合『{query}』的訊息。") return
lines = [f"找到 {len(messages)} 則結果,條件為『{query}』:\n"] for msg in messages[:10]: lines.append(f"**{msg['author']}**: {msg['content'][:100]}") bot.followup(event["interaction_token"], "\n".join(lines))
HANDLERS = { "ask": handle_ask, "status": handle_status, "search": handle_search,}
def main(): bot = SlashBot("commands.json")
# 等待 ready while True: event = bot.events.get() if event.get("event") == "ready": print(f"Ready as {event['bot_name']}") break
# 等待指令同步完成 while True: event = bot.events.get() if event.get("event") == "slash_commands_synced": print(f"已註冊 {event['count']} 組指令到 {event['guilds']} 個伺服器") break # 避免無限等待 if event.get("event") == "error": print(f"錯誤:{event.get('message')}") break
# 主事件迴圈 while True: event = bot.events.get() if event.get("event") == "slash_command": handler = HANDLERS.get(event["command"]) if handler: try: handler(bot, event) except Exception as e: bot.followup( event["interaction_token"], f"發生錯誤:{e}", ) else: bot.followup( event["interaction_token"], f"未知指令:{event['command']}", )
if __name__ == "__main__": main()串流式 Slash 指令回應
較長回應可改成串流輸出,不需一次傳回全部結果。透過 interaction_token 使用 stream_start:
def handle_ask_streaming(bot, event): question = event["args"].get("question", "")
# 開始可回應 slash 互動的串流 result = bot.send({ "action": "stream_start", "channel_id": event["channel_id"], "interaction_token": event["interaction_token"], }, wait=True)
if "error" in result: bot.followup(event["interaction_token"], f"錯誤:{result['error']}") return
stream_id = result["stream_id"]
# 從 LLM 串流回傳 for chunk in call_llm(question): bot.send({ "action": "stream_chunk", "stream_id": stream_id, "content": chunk, })
bot.send({"action": "stream_end", "stream_id": stream_id})詳情請見 串流回應。
註冊時機
discli 在啟動時會對每個伺服器分別同步指令,因而可立即可用。若機器人執行中加入新伺服器,新的伺服器會在你重新啟動 discli serve 後才有 Slash 指令。
指令何時出現
| 方法 | 時機 |
|---|---|
| 僅同步到伺服器(discli 行為) | 立即 |
| 僅全域註冊 | 最多 1 小時 |
指令更新時機
若你修改 commands.json,請重啟 discli serve 以重新註冊更新後的指令。
僅限管理員指令
slash_command 事件包含 is_admin 欄位。可用來保護敏感指令:
def handle_admin_command(bot, event): if not event["is_admin"]: bot.followup( event["interaction_token"], "此指令需要管理員權限。", ) return # ... 繼續執行管理動作參數類型對應表
| JSON 類型 | Python 類型 | Discord 類型 |
|---|---|---|
string | str | 字串選項 |
integer | int | 整數選項 |
number | float | 數值選項 |
boolean | bool | 布林值選項 |
可選參數(required 為 false)在使用者未提供時,args 字典中不會出現。