用 TypeScript 打造 Discord 自動通知系統:Webhook 快速上手

作者: Calpa Liu
字數:1801
出版:2025 年 5 月 17 日
使用 TypeScript 與 Discord Webhook API,打造自動發送訊息到頻道的完整流程。從設定 Webhook 到訊息格式、編輯與刪除操作,全方位實戰解析,適合開發者應用於文章通知、自動通報等場景。

過去我習慣透過 RSS 搭配 Telegram Bot,自動推送部落格更新通知至頻道。近期開始將社群互動轉移至 Discord,便思考是否也能實現類似的自動化流程。經研究後發現,Discord 提供了 Webhook API,可在不建立機器人帳號的情況下發送格式化訊息。為此,我使用 TypeScript 實作了一套訊息推送邏輯,結合 Zod 驗證、自訂嵌入內容與錯誤處理,成功將新文章資訊即時發送至指定頻道。本文將詳解整體架構與程式碼實作,協助你快速建立自己的 Discord 通知系統。

什麼是 Discord Webhook?

Discord 是一款以社群為核心的即時通訊平台,廣泛用於遊戲、開發者、教育與創作者社群。它支援頻道分類、身分組、語音/影像通話與豐富的第三方整合功能,尤其受到技術社群與開源專案歡迎。許多團隊會將 Discord 作為內部通報、異常監控、或社群公告的資訊中樞,對自動化有高度需求。

Discord Webhook 則是 Discord 提供的一種輕量整合方式,允許開發者在不建立機器人帳號的前提下,自動發送訊息到指定頻道。Webhook 支援 JSON 格式、豐富的 embeds 區塊與自訂頭像、名稱等外觀設定。由於設置簡單、無需身份驗證,特別適合用於 CI/CD 成果通知、文章更新、錯誤報告等用途。

Webhook 並非 Discord 專屬功能,而是一種通用的事件驅動技術。在 GitHub 中,可以透過 Webhook 將每次 Push、PR 或部署事件傳送至外部系統;Slack 也支援 Webhook 發送格式化訊息。透過這種「主動推送」的機制,開發者能串連不同平台,打造自動化的跨系統通訊流程,是許多現代 DevOps 與 SaaS 服務不可或缺的整合技術。

適用場景

這套 Discord Webhook 自動推播系統,特別適合以下使用情境:

  • 部落格發佈新文章自動通知
  • CI/CD 构建完成、部署成功後推播結果
  • 錯誤日誌、系統監控自動回報
  • 團隊更新、公告通知整合至 Discord 頻道

如何在 Discord 中產生 Webhook 並取得網址

要在 Discord 設定 Webhook,首先進入伺服器設定頁面,點選「整合」(Integrations),並新增一個 Webhook。你可以自訂 Webhook 的名稱和頭像,並選擇要發送訊息的頻道,最後複製產生的 Webhook URL。

取得的 Webhook URL 形式為 https://discord.com/api/webhooks/your-webhook-id/your-webhook-token。請妥善保存這個網址,因為持有此 URL 的任何人都能將訊息發送到指定頻道。

Webhook 訊息格式解析:content 與 embeds

Webhook 訊息支援以下元素:

  • username:訊息顯示名稱
  • avatar_url:顯示頭像
  • content:純文字訊息內容
  • embeds:豐富格式訊息(可包含標題、描述、顏色、縮圖、欄位、時間戳等)

嵌入(embeds)常用屬性:

屬性說明限制
title標題最多 256 字元
description描述最多 4096 字元
url點擊標題跳轉的連結-
color左側顏色(十進位數字)-
timestamp時間戳ISO8601 格式
thumbnail縮圖 URL-
footer頁腳文字最多 2048 字元
fields自訂欄位最多 25 個

用 TypeScript 發送 Webhook:完整實作教學

以下展示一個完整範例,包含資料驗證、組裝訊息與錯誤處理:

Step 1:定義與驗證訊息資料格式(Zod)

首先,使用 Zod 定義並驗證文章元數據與 Discord 訊息格式,確保資料正確性:

import { z } from "zod";

// 文章元數據結構
export const ArticleMetadataSchema = z.object({
  title: z.string(),
  url: z.string().url(),
  description: z.string(),
  timestamp: z.string().datetime(),
  thumbnailURL: z.string().url(),
});
export type ArticleMetadata = z.infer<typeof ArticleMetadataSchema>;

// Discord Webhook 訊息格式
export const DiscordMessageSchema = z.object({
  username: z.string().optional(),
  avatar_url: z.string().url().optional(),
  content: z.string().optional(),
  embeds: z.array(
    z.object({
      title: z.string(),
      url: z.string().url(),
      description: z.string(),
      color: z.number().int().optional(),
      footer: z.object({ text: z.string() }).optional(),
      timestamp: z.string().datetime().optional(),
      thumbnail: z.object({ url: z.string().url() }).optional(),
    })
  ) 
});
export type DiscordMessage = z.infer<typeof DiscordMessageSchema>;

Step 2:根據文章資訊組裝 Webhook 訊息

根據文章元數據組裝符合 Discord Webhook 格式的訊息物件:

/**
 * 將文章元數據轉換為 Discord Webhook 訊息內容
 */
export function buildDiscordMessage(article: ArticleMetadata): DiscordMessage {
  const { title, url, description, timestamp, thumbnailURL } = ArticleMetadataSchema.parse(article);

  return DiscordMessageSchema.parse({
    username: "Calpa 的自動人形",
    avatar_url: "https://assets.calpa.me/telegram/public/pfp.png",
    content: "📰 Calpa 發佈新文章啦!",
    embeds: [
      {
        title,
        url,
        description,
        color: 0x58a6ff,
        footer: { text: "Calpa 的煉金工房" },
        timestamp,
        thumbnail: { url: thumbnailURL }
      }
    ]
  });
}

Step 3:發送訊息並處理錯誤回傳

最後,封裝發送邏輯,捕捉錯誤並回傳結果:

/**
 * 發送格式化的 Discord Webhook 訊息
 * @param webhookUrl Discord webhook URL
 * @param message DiscordMessage 物件
 * @returns 發送成功返回 true,否則 false
 */
export async function sendDiscordMessage(
  webhookUrl: string,
  message: DiscordMessage
): Promise<boolean> {
  const response = await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(message),
  });

  if (!response.ok) {
    console.error(`Failed to send to Discord: ${response.status} ${await response.text()}`);
    return false;
  }
  return true;
}

Step 4:整合訊息組裝與發送

兩步組合:先組裝訊息,再發送:

// 1. 組裝訊息
const message = buildDiscordMessage(articleMetadata);
// 2. 發送訊息
const success = await sendDiscordMessage(DISCORD_WEBHOOK_URL, message);

Webhook 實作限制與最佳實踐建議

Discord Webhook 訊息本身有諸多格式與容量限制,例如單則訊息內容不得超過 2000 字元,每則訊息可附帶的嵌入(embed)上限為 10 個,所有嵌入的總字元數不得超過 6000。單一嵌入的標題與描述分別限制為 256 與 4096 字元,欄位(fields)數量最多為 25,每個欄位名稱與值則分別不得超過 256 與 1024 字元。此外,API 頻率限制為每個 webhook 每分鐘最多 30 次。

為確保訊息傳遞穩定,建議採用 Zod 這類 schema library 進行資料驗證,嚴格比對格式與長度。實作完整錯誤處理機制、詳細記錄響應狀態與訊息,有助於後續除錯。可視需求自訂 usernameavatar_url 等外觀設定,並適度善用 color 屬性提升可讀性。同時要特別留意速率限制,避免短時間內大量發送導致訊息遭拒。

如果發送失敗,可觀察 response.statusresponse.text() 的錯誤訊息內容,大多能找出格式錯誤或速率限制等問題。

.env 管理 Webhook URL

為了更加安全地管理 Webhook URL,我們可以使用 .env 檔案。首先,在專案根目錄下建立 .env 檔案,並添加 Webhook URL:

DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your-webhook-id/your-webhook-token

這樣可以避免將 Webhook URL 直接硬編入程式碼中,增加安全性。

結語:從 Telegram 過渡到 Discord 的自動化體驗

從 Telegram 轉移到 Discord Webhook 的自動推播,其實過程十分順暢。無需額外建立機器人帳號,只要設定好 Webhook 並發送合適格式的訊息,就能輕鬆完成自動化通知。這種方式讓資訊推送變得更簡單,也便於融入各種開發或社群應用。

如果你開始善用 Discord Webhook,相信會發現自動化流程變得更高效與友善。最後,記得妥善保存 Webhook URL,避免被他人濫用,讓自動化體驗維持順暢與安全!

關於 Calpa

Calpa 擅長使用 TypeScriptReact.jsVue.js 建立 Responsive Website。

他積極參與開源社區,曾在 2019 年的香港開源大會上擔任講者,提供工作經驗和見解。此外,他也在 GitHub 上公開分享個人博客程式碼,已獲得超過 300 顆星星和 60 個分支的支持。

他熱愛學習新技術,並樂意分享經驗。他相信,唯有不斷學習才能跟上快速演變的技術環境。

熱門文章

最新文章

Zeabur 架構自動無痛感
專案交付更輕鬆
🎁 首次升級送 $5 點數