תיעוד 202
בניית Slackbot
עודכן לאחרונה ב-8 באפריל 2026 | העתקה כ-Markdown | הצגה כ-Markdown | הגדרת סוכן
במדריך הזה תבנו בוט של Slack ↗︎ באמצעות Cloudflare Workers. הבוט ישתמש ב-webhooks של GitHub כדי לשלוח הודעות לערוץ Slack כש-issues מתעדכנים או נוצרים, ויאפשר למשתמשים לכתוב פקודה כדי לחפש GitHub issues מתוך Slack.

המדריך הזה מומלץ לאנשים שמכירים כתיבת יישומי רשת. תשתמשו ב-TypeScript כשפת התכנות וב-Hono ↗︎ כמסגרת הרשת. אם בניתם יישום עם כלים כמו Node ↗︎ ו-Express ↗︎, הפרויקט הזה ירגיש מוכר מאוד. אם אתם חדשים בכתיבת יישומי רשת, או שרציתם בעבר לבנות משהו כמו בוט Slack אבל ההטמעה או ההגדרה הרתיעו אתכם, Workers יהיו דרך להתמקד בכתיבת קוד ובשחרור פרויקטים.
אם תרצו לעיין בקוד, או באופן שבו הבוט עובד בערוץ Slack אמיתי, לפני שממשיכים במדריך, אפשר לגשת לגרסה הסופית של בסיס הקוד ב-GitHub ↗︎. מ-GitHub אפשר להוסיף מפתחות Slack API משלכם ולהטמיע אותו בערוצי Slack שלכם לבדיקה.
#לפני שמתחילים
כל המדריכים מניחים שכבר השלמתם את מדריך תחילת העבודה, שמגדיר לכם חשבון Cloudflare Workers, את C3 ↗︎, ואת Wrangler.
#הגדרת Slack
המדריך הזה מניח שכבר יש לכם חשבון Slack, ושיש לכם יכולת ליצור ולנהל יישומי Slack.
#הגדרת יישום Slack
כדי לפרסם הודעות מ-Cloudflare Worker לערוץ Slack, תצטרכו ליצור יישום בממשק של Slack. כדי לעשות זאת, עברו לאזור ה-API של Slack, בכתובת api.slack.com/apps ↗︎, ובחרו ב-Create New App.

ליישומי Slack יש הרבה יכולות. תשתמשו בשתיים מהן, Incoming Webhooks ו-Slash Commands, כדי לבנות את בוט ה-Slack שמופעל באמצעות Worker.
#Incoming Webhook
Incoming Webhooks הם כתובות URL שאפשר להשתמש בהן כדי לשלוח הודעות לערוצי Slack שלכם. ה-incoming webhook שלכם יזווג לתמיכה ב-webhook של GitHub כדי לשלוח הודעות לערוץ Slack בכל פעם שיש עדכונים ל-issues במאגר נתון. תראו את הקוד בפירוט רב יותר תוך כדי בניית היישום. קודם צרו Slack webhook:
- בסרגל הצד של ממשק Slack, בחרו ב-Incoming Webhooks.
- ב-Webhook URLs for your Workspace, בחרו ב-Add New Webhook to Workspace.
- במסך הבא, בחרו את הערוץ שאליו תרצו שה-webhook ישלח הודעות (אפשר לבחור חדר, כמו #general או #code, או לקבל הודעה ישירות מבוט ה-Slack כשה-webhook נקרא).
- אשרו את כתובת ה-webhook החדשה.
אחרי אישור כתובת ה-webhook, תוחזרו לדף Incoming Webhooks ותוכלו לראות את כתובת ה-webhook החדשה. תוסיפו אותה לקוד ה-Workers בהמשך. בשלב הבא תוסיפו את הרכיב השני לבוט ה-Slack: Slash Command.

#Slash Command
Slash Command ב-Slack הוא פקודה שמוגדרת בהתאמה אישית ואפשר לשייך אותה לבקשת URL. לדוגמה, אם הגדרתם /weather <zip>, Slack ישלח בקשת HTTP POST לכתובת URL מוגדרת, ויעביר את הטקסט <zip> כדי לקבל את מזג האוויר עבור מיקוד מסוים. ביישום שלכם תשתמשו בפקודה /issue כדי לחפש GitHub issues באמצעות GitHub API ↗︎. הקלדה של /issue cloudflare/wrangler#1 תשלח את הטקסט cloudflare/wrangler#1 בבקשת HTTP POST ליישום שלכם, והיישום ישתמש בזה כדי למצוא את ה-GitHub issue הרלוונטי ↗︎.
- בסרגל הצד של Slack, בחרו ב-Slash Commands.
- צרו את ה-slash command הראשון שלכם.
במדריך הזה תשתמשו בפקודה /issue. כתובת הבקשה צריכה להיות הנתיב /lookup בכתובת היישום שלכם: לדוגמה, אם היישום יאוחסן ב-https://myworkerurl.com, ה-Request URL צריך להיות https://myworkerurl.com/lookup.

#הגדרת GitHub Webhooks
יישום Cloudflare Workers שלכם יוכל לטפל בבקשות נכנסות מ-Slack. הוא צריך גם להיות מסוגל לקבל אירועים ישירות מ-GitHub. אם GitHub issue נוצר או מתעדכן, אפשר להשתמש ב-GitHub webhooks כדי לשלוח את האירוע ליישום ה-Workers ולפרסם הודעה מתאימה ב-Slack.
כדי להגדיר webhook:
- עברו אל Settings > Webhooks > Add webhook במאגר GitHub שלכם.
אם יש לכם מאגר כמו https://github.com/user/repo, אפשר לגשת לדף Webhooks ישירות בכתובת https://github.com/user/repo/settings/hooks.
- הגדירו את ה-Payload URL לנתיב
/webhookבכתובת ה-Worker שלכם.
לדוגמה, אם ה-Worker יאוחסן ב-https://myworkerurl.com, ה-Payload URL צריך להיות https://myworkerurl.com/webhook.
- ברשימה הנפתחת Content type, בחרו ב-application/json.
ה-Content type של המטען יכול להיות מטען מקודד כ-URL (application/x-www-form-urlencoded) או JSON (application/json). לצורך המדריך הזה, וכדי לאפשר פענוח של המטען שנשלח ליישום, בחרו ב-JSON.
- ב-Which events would you like to trigger this webhook?, בחרו ב-Let me select individual events.
GitHub webhooks מאפשרים לציין אילו אירועים תרצו שיישלחו ל-webhook. כברירת מחדל, ה-webhook ישלח אירועי push מהמאגר. לצורך המדריך הזה, תבחרו ב-Let me select individual events.
- בחרו בסוג האירוע Issues.
יש הרבה סוגי אירועים שונים שאפשר להפעיל עבור ה-webhook. בחירה ב-Issues תשלח כל אירוע שקשור ל-issue אל ה-webhook, כולל כש-issues נפתחים, נערכים, נמחקים ועוד. אם תרצו להרחיב את יישום בוט ה-Slack בעתיד, אפשר לבחור עוד מהאירועים האלה אחרי המדריך.
- בחרו ב-Add webhook.

כשה-webhook נוצר, הוא ינסה לשלוח מטען בדיקה ליישום. מאחר שהיישום עדיין לא הוטמע בפועל, השאירו את ההגדרה כפי שהיא. בהמשך תחזרו למאגר כדי ליצור, לערוך ולסגור כמה issues, כדי לוודא שה-webhook עובד אחרי שהיישום מוטמע.
#אתחול
כדי לאתחל את הפרויקט, השתמשו בממשק שורת הפקודה C3 (create-cloudflare-cli) ↗︎.
npm
npm create cloudflare@latest -- slack-botyarn
yarn create cloudflare slack-botpnpm
pnpm create cloudflare@latest slack-botעקבו אחרי השלבים האלה כדי ליצור פרויקט Hono.
- עבור What would you like to start with?, בחרו ב-
Framework Starter. - עבור Which development framework do you want to use?, בחרו ב-
Hono. - עבור Do you want to deploy your application?, בחרו ב-
No.
עברו לספרייה slack-bot:
cd slack-botפתחו את src/index.ts בעורך כדי למצוא את הקוד הבא.
import { Hono } from "hono";
type Bindings = {
[key in keyof CloudflareBindings]: CloudflareBindings[key];
};
const app = new Hono<{ Bindings: Bindings }>();
app.get("/", (c) => {
return c.text("Hello Hono!");
});
export default app;זהו יישום מינימלי שמשתמש ב-Hono. אם מגיעה גישת GET לנתיב /, הוא יחזיר תשובה עם הטקסט Hello Hono!. הוא גם מחזיר הודעה 404 Not Found עם קוד מצב 404 אם ניגשים לכל נתיב או שיטה אחרים.
כדי להריץ את היישום במחשב המקומי, הריצו את הפקודה הבאה. הפקודות מופיעות כך בתיעוד הרשמי, עודכן לאחרונה ב-8 באפריל 2026.
npm
npm i -- devyarn
yarn add devpnpm
pnpm add devbun
bun add devגשו אל http://localhost:8787 בדפדפן אחרי שהשרת הופעל, ותוכלו לראות את ההודעה.
Hono עוזר ליצור את יישום ה-Workers בקלות ובמהירות.
#בנייה
עכשיו ניצור בוט Slack על Cloudflare Workers.
#הפרדת קבצים
אפשר ליצור את היישום בכמה קבצים במקום לכתוב את כל נקודות הקצה והפונקציות בקובץ אחד. עם Hono אפשר להוסיף ניתוב של יישומי בן ליישום האב באמצעות הפונקציה app.route().
לדוגמה, דמיינו את יישום ה-Web API הבא.
import { Hono } from "hono";
const app = new Hono();
app.get("/posts", (c) => c.text("Posts!"));
app.post("/posts", (c) => c.text("Created!", 201));
export default app;אפשר להוסיף את הנתיבים תחת /api/v1.
import { Hono } from "hono";
import api from "./api";
const app = new Hono();
app.route("/api/v1", api);
export default app;הוא יחזיר Posts! כשניגשים אל GET /api/v1/posts.
לבוט ה-Slack יהיו שני יישומי בן, וכל אחד מהם נקרא "route".
- נתיב
lookupיקבל בקשות מ-Slack (שנשלחות כשמשתמש משתמש בפקודה/issue), ויחפש את ה-issue המתאים באמצעות GitHub API. היישום הזה יתווסף אל/lookupביישום הראשי. - נתיב
webhookייקרא כש-issue משתנה ב-GitHub, דרך webhook מוגדר. היישום הזה יתווסף אל/webhookביישום הראשי.
צרו את קובצי הנתיב בספרייה בשם routes.
יצירת תיקיות וקבצים חדשים
mkdir -p src/routes
touch src/routes/lookup.ts
touch src/routes/webhook.tsאחר כך עדכנו את היישום הראשי.
import { Hono } from "hono";
import lookup from "./routes/lookup";
import webhook from "./routes/webhook";
const app = new Hono();
app.route("/lookup", lookup);
app.route("/webhook", webhook);
export default app;#הגדרת טיפוסי TypeScript
לפני מימוש הפונקציות עצמן, צריך להגדיר את טיפוסי TypeScript שתשתמשו בהם בפרויקט הזה. צרו קובץ חדש ביישום בנתיב src/types.ts וכתבו את הקוד. Bindings הוא טיפוס שמתאר את משתני הסביבה של Cloudflare Workers. Issue הוא טיפוס עבור GitHub issue, ו-User הוא טיפוס עבור משתמש GitHub. תצטרכו אותם בהמשך.
export type Bindings = {
SLACK_WEBHOOK_URL: string;
};
export type Issue = {
html_url: string;
title: string;
body: string;
state: string;
created_at: string;
number: number;
user: User;
};
type User = {
html_url: string;
login: string;
avatar_url: string;
};#יצירת נתיב החיפוש
התחילו ליצור את נתיב החיפוש בקובץ src/routes/lookup.ts.
import { Hono } from "hono";
const app = new Hono();
export default app;כדי להבין איך לתכנן את הפונקציה הזו, צריך להבין איך פקודות סלאש של Slack שולחות נתונים לכתובות URL.
לפי התיעוד של פקודות הסלאש של Slack ↗︎, Slack שולח בקשת HTTP POST לכתובת ה-URL שציינתם, עם סוג תוכן application/x-www-form-urlencoded. לדוגמה, אם מישהו מקליד /issue cloudflare/wrangler#1, אפשר לצפות למטען נתונים בפורמט הזה:
token=gIkuvaNzQIHg97ATvDxqgjtO
&team_id=T0001
&team_domain=example
&enterprise_id=E0001
&enterprise_name=Globular%20Construct%20Inc
&channel_id=C2147483705
&channel_name=test
&user_id=U2147483697
&user_name=Steve
&command=/issue
&text=cloudflare/wrangler#1
&response_url=https://hooks.slack.com/commands/1234/5678
&trigger_id=13345224609.738474920.8088930838d88f008e0בהינתן גוף המטען הזה, צריך לנתח אותו ולקבל את הערך של המפתח text. עם ה-text הזה, למשל cloudflare/wrangler#1, אפשר לנתח את המחרוזת לחלקי נתונים ידועים (owner, repo ו-issue_number), ולהשתמש בהם כדי לשלוח בקשה ל-API של GitHub ולשלוף את נתוני ה-issue.
עם פקודות סלאש של Slack, אפשר להגיב לפקודת סלאש על ידי החזרת נתונים מובנים כתגובה לפקודת הסלאש הנכנסת. במקרה הזה, כדאי להשתמש בתגובה מ-API של GitHub כדי להציג גרסה מעוצבת של ה-issue ב-GitHub, כולל חלקי נתונים כמו כותרת ה-issue, מי יצר אותו, והתאריך שבו הוא נוצר. מסגרת Block Kit ↗︎ החדשה של Slack מאפשרת להחזיר תגובת הודעה מפורטת, על ידי בניית בלוקים של טקסט ותמונה עם הנתונים מ-API של GitHub.
#ניתוח פקודות סלאש
כדי להתחיל, נתיב lookup צריך לנתח את ההודעות שמגיעות מ-Slack. כפי שצוין קודם, ה-API של Slack שולח HTTP POST בפורמט מקודד כ-URL. אפשר לקבל את המשתנה text על ידי ניתוח שלו עם c.req.json().
import { Hono } from "hono";
const app = new Hono();
app.post("/", async (c) => {
const { text } = await c.req.parseBody();
if (typeof text !== "string") {
return c.notFound();
}
});
export default app;בהינתן משתנה text שמכיל טקסט כמו cloudflare/wrangler#1, כדאי לנתח את הטקסט הזה ולקבל ממנו את החלקים הנפרדים לשימוש עם ה-API של GitHub: owner, repo ו-issue_number.
כדי לעשות זאת, צרו קובץ חדש באפליקציה, בנתיב src/utils/github.ts. הקובץ הזה יכיל מספר פונקציות "עזר" לעבודה עם ה-API של GitHub. הראשונה מהן תהיה מנתח מחרוזות, בשם parseGhIssueString:
const ghIssueRegex =
/(?<owner>[\w.-]*)\/(?<repo>[\w.-]*)\#(?<issue_number>\d*)/;
export const parseGhIssueString = (text: string) => {
const match = text.match(ghIssueRegex);
return match ? (match.groups ?? {}) : {};
};parseGhIssueString מקבל קלט text, מתאים אותו מול ghIssueRegex, ואם נמצאת התאמה, מחזיר את אובייקט groups מההתאמה הזו, תוך שימוש בקבוצות הלכידה owner, repo ו-issue_number שמוגדרות בביטוי הרגולרי. על ידי ייצוא הפונקציה הזו מ-src/utils/github.ts, אפשר להשתמש בה בחזרה ב-src/handlers/lookup.ts:
import { Hono } from "hono";
import { parseGhIssueString } from "../utils/github";
const app = new Hono();
app.post("/", async (c) => {
const { text } = await c.req.parseBody();
if (typeof text !== "string") {
return c.notFound();
}
const { owner, repo, issue_number } = parseGhIssueString(text);
});
export default app;#שליחת בקשות ל-API של GitHub
עם הנתונים האלה, אפשר לבצע את חיפוש ה-API הראשון ל-GitHub. שוב, צרו פונקציה חדשה ב-src/utils/github.ts, כדי לבצע בקשת fetch ל-API של GitHub עבור נתוני ה-issue:
const ghIssueRegex =
/(?<owner>[\w.-]*)\/(?<repo>[\w.-]*)\#(?<issue_number>\d*)/;
export const parseGhIssueString = (text: string) => {
const match = text.match(ghIssueRegex);
return match ? (match.groups ?? {}) : {};
};
export const fetchGithubIssue = (
owner: string,
repo: string,
issue_number: string,
) => {
const url = `https://api.github.com/repos/${owner}/${repo}/issues/${issue_number}`;
const headers = { "User-Agent": "simple-worker-slack-bot" };
return fetch(url, { headers });
};בחזרה ב-src/handlers/lookup.ts, השתמשו ב-fetchGitHubIssue כדי לשלוח בקשה ל-API של GitHub, ולנתח את התגובה:
import { Hono } from "hono";
import { fetchGithubIssue, parseGhIssueString } from "../utils/github";
import { Issue } from "../types";
const app = new Hono();
app.post("/", async (c) => {
const { text } = await c.req.parseBody();
if (typeof text !== "string") {
return c.notFound();
}
const { owner, repo, issue_number } = parseGhIssueString(text);
const response = await fetchGithubIssue(owner, repo, issue_number);
const issue = await response.json<Issue>();
});
export default app;#בניית הודעת Slack
אחרי שקיבלתם תגובה בחזרה מ-API של GitHub, השלב האחרון הוא לבנות הודעת Slack עם נתוני ה-issue, ולהחזיר אותה למשתמש. התוצאה הסופית תיראה בערך כך:

אפשר לראות ארבעה חלקים שונים בצילום המסך למעלה:
- השורה הראשונה (מודגשת) מקשרת ל-issue, ומציגה את כותרת ה-issue
- השורות הבאות (כולל קטעי קוד) הן גוף ה-issue
- שורת הטקסט האחרונה מציגה את סטטוס ה-issue, את יוצר ה-issue (עם קישור לפרופיל GitHub של המשתמש), ואת תאריך היצירה של ה-issue
- תמונת הפרופיל של יוצר ה-issue, בצד ימין
מסגרת Block Kit ↗︎ שהוזכרה קודם תעזור לקחת את נתוני ה-issue (במבנה המתואר בתיעוד REST API של GitHub ↗︎) ולעצב אותם למשהו כמו צילום המסך למעלה.
צרו קובץ נוסף, src/utils/slack.ts, שיכיל את הפונקציה constructGhIssueSlackMessage, פונקציה שלוקחת נתוני issue והופכת אותם לאוסף של בלוקים. בלוקים הם אובייקטי JavaScript ש-Slack ישתמש בהם כדי לעצב את ההודעה:
import { Issue } from "../types";
export const constructGhIssueSlackMessage = (
issue: Issue,
issue_string: string,
prefix_text?: string,
) => {
const issue_link = `<${issue.html_url}|${issue_string}>`;
const user_link = `<${issue.user.html_url}|${issue.user.login}>`;
const date = new Date(Date.parse(issue.created_at)).toLocaleDateString();
const text_lines = [
prefix_text,
`*${issue.title} - ${issue_link}*`,
issue.body,
`*${issue.state}* - Created by ${user_link} on ${date}`,
];
};הודעות Slack מקבלות גרסה של Markdown, שתומכת בטקסט מודגש באמצעות כוכביות (*bolded text*), ובקישורים בפורמט <https://yoururl.com|Display Text>.
בהינתן הפורמט הזה, בנו את issue_link, שלוקח את המאפיין html_url מנתוני ה-issue של ה-API של GitHub (בפורמט https://github.com/cloudflare/wrangler-legacy/issues/1), ואת issue_string שנשלח מפקודת הסלאש של Slack, ומשלב אותם לקישור שניתן ללחוץ עליו בהודעת Slack.
user_link דומה, ומשתמש ב-issue.user.html_url (בפורמט https://github.com/signalnerve, משתמש GitHub) ובשם המשתמש של המשתמש ב-GitHub (issue.user.login), כדי לבנות קישור שניתן ללחוץ עליו אל משתמש GitHub.
לבסוף, נתחו את issue.created_at, מחרוזת ISO 8601, המירו אותה למופע של Date ב-JavaScript, והפכו אותה למחרוזת מעוצבת, בפורמט MM/DD/YY.
עם המשתנים האלה במקום, text_lines הוא מערך של כל שורת טקסט להודעת Slack. השורה הראשונה היא כותרת ה-issue וקישור ה-issue, השנייה היא גוף ה-issue, והשורה האחרונה היא מצב ה-issue (לדוגמה, open או closed), קישור המשתמש, ותאריך היצירה.
אחרי שהטקסט נבנה, אפשר סוף סוף לבנות את הודעת Slack, ולהחזיר מערך של בלוקים עבור Block Kit ↗︎ של Slack. במקרה הזה, יש רק בלוק אחד: בלוק section ↗︎ עם טקסט Markdown, ותמונת עזר של המשתמש שיצר את ה-issue. החזירו את הבלוק היחיד הזה בתוך מערך, כדי להשלים את הפונקציה constructGhIssueSlackMessage:
import { Issue } from "../types";
export const constructGhIssueSlackMessage = (
issue: Issue,
issue_string: string,
prefix_text?: string,
) => {
const issue_link = `<${issue.html_url}|${issue_string}>`;
const user_link = `<${issue.user.html_url}|${issue.user.login}>`;
const date = new Date(Date.parse(issue.created_at)).toLocaleDateString();
const text_lines = [
prefix_text,
`*${issue.title} - ${issue_link}*`,
issue.body,
`*${issue.state}* - Created by ${user_link} on ${date}`,
];
return [
{
type: "section",
text: {
type: "mrkdwn",
text: text_lines.join("\n"),
},
accessory: {
type: "image",
image_url: issue.user.avatar_url,
alt_text: issue.user.login,
},
},
];
};#סיום נתיב החיפוש
ב-src/handlers/lookup.ts, השתמשו ב-constructGhIssueSlackMessage כדי לבנות את blocks, והחזירו אותם כתגובה חדשה עם c.json() כשפקודת הסלאש נקראת:
import { Hono } from "hono";
import { fetchGithubIssue, parseGhIssueString } from "../utils/github";
import { constructGhIssueSlackMessage } from "../utils/slack";
import { Issue } from "../types";
const app = new Hono();
app.post("/", async (c) => {
const { text } = await c.req.parseBody();
if (typeof text !== "string") {
return c.notFound();
}
const { owner, repo, issue_number } = parseGhIssueString(text);
const response = await fetchGithubIssue(owner, repo, issue_number);
const issue = await response.json<Issue>();
const blocks = constructGhIssueSlackMessage(issue, text);
return c.json({
blocks,
response_type: "in_channel",
});
});
export default app;פרמטר נוסף שמועבר לתגובה הוא response_type. כברירת מחדל, תגובות לפקודות סלאש הן ephemeral, כלומר רק המשתמש שכותב את פקודת הסלאש רואה אותן. העברת response_type עם הערך in_channel, כפי שמוצג למעלה, תגרום לתגובה להופיע לכל המשתמשים בערוץ.
אם תרצו שההודעות יישארו פרטיות, הסירו את שורת response_type. זה יגרום ל-response_type להיות כברירת מחדל ephemeral.
#טיפול בשגיאות
נתיב lookup כמעט הושלם, אבל יש מספר שגיאות שיכולות להתרחש בנתיב, כמו ניתוח הגוף מ-Slack, קבלת ה-issue מ-GitHub, או בניית הודעת Slack עצמה. למרות שאפליקציות Hono יכולות לטפל בשגיאות בלי שצריך לעשות דבר, אפשר להתאים אישית את התגובה שמוחזרת בדרך הבאה.
import { Hono } from "hono";
import { fetchGithubIssue, parseGhIssueString } from "../utils/github";
import { constructGhIssueSlackMessage } from "../utils/slack";
import { Issue } from "../types";
const app = new Hono();
app.post("/", async (c) => {
const { text } = await c.req.parseBody();
if (typeof text !== "string") {
return c.notFound();
}
const { owner, repo, issue_number } = parseGhIssueString(text);
const response = await fetchGithubIssue(owner, repo, issue_number);
const issue = await response.json<Issue>();
const blocks = constructGhIssueSlackMessage(issue, text);
return c.json({
blocks,
response_type: "in_channel",
});
});
app.onError((_e, c) => {
return c.text(
"Uh-oh! We couldn't find the issue you provided. " +
"We can only find public issues in the following format: `owner/repo#issue_number`.",
);
});
export default app;#יצירת נתיב ה-webhook
אתם עכשיו באמצע הדרך ביישום הנתיבים של אפליקציית ה-Workers שלכם. ביישום הנתיב הבא, src/routes/webhook.ts, תשתמשו מחדש בהרבה מהקוד שכבר כתבתם עבור נתיב ה-lookup.
בתחילת המדריך הזה, הגדרתם webhook של GitHub כדי לעקוב אחרי כל האירועים שקשורים ל-issues במאגר שלכם. כשנפתח issue, למשל, הפונקציה שמתאימה לנתיב /webhook באפליקציית ה-Workers שלכם צריכה לקחת את הנתונים שנשלחים אליה מ-GitHub, ולפרסם הודעה חדשה בערוץ ה-Slack שהוגדר.
ב-src/routes/webhook.ts, הגדירו אפליקציית Hono ריקה. ההבדל מנתיב ה-lookup הוא ש-Bindings מועבר כ-generics ל-new Hono(). זה נחוץ כדי לתת את טיפוס ה-TypeScript המתאים ל-SLACK_WEBHOOK_URL שישמש אחר כך.
import { Hono } from "hono";
import { Bindings } from "../types";
const app = new Hono<{ Bindings: Bindings }>();
export default app;בדומה לנתיב ה-lookup, תצטרכו לנתח את ה-payload הנכנס בתוך request, לקבל ממנו את נתוני ה-issue הרלוונטיים (עיינו בתיעוד ה-API של GitHub על IssueEvent ↗︎ לסכמת ה-payload המלאה), ולשלוח הודעה מעוצבת ל-Slack כדי לציין מה השתנה. הגרסה הסופית תיראה בערך כך:

השוו את פורמט ההודעה הזה לפורמט שמוחזר כשמשתמש משתמש בפקודת ה-slash /issue. תראו שיש רק הבדל ממשי אחד בין השניים: הוספה של טקסט פעולה בשורה הראשונה, בפורמט An issue was $action:. הפעולה הזו, שנשלחת כחלק מ-IssueEvent מ-GitHub, תשמש אתכם כשתבנו אוסף בלוקים מוכר מאוד באמצעות Block Kit של Slack.
#ניתוח נתוני האירוע
כדי להתחיל למלא את הנתיב, נתחו את גוף הבקשה בפורמט JSON לאובייקט ובנו כמה משתני עזר:
import { Hono } from "hono";
import { constructGhIssueSlackMessage } from "../utils/slack";
const app = new Hono();
app.post("/", async (c) => {
const { action, issue, repository } = await c.req.json();
const prefix_text = `An issue was ${action}:`;
const issue_string = `${repository.owner.login}/${repository.name}#${issue.number}`;
});
export default app;IssueEvent, ה-payload שנשלח מ-GitHub כחלק מהגדרת ה-webhook שלכם, כולל action (מה קרה ל-issue: למשל, הוא נפתח, נסגר, ננעל וכן הלאה), את ה-issue עצמו, ואת ה-repository, בין היתר.
השתמשו ב-c.req.json() כדי להמיר את גוף ה-payload של הבקשה מ-JSON לאובייקט JS רגיל. השתמשו ב-destructuring של ES6 כדי להגדיר את action, issue ו-repository כמשתנים שאפשר להשתמש בהם בקוד. prefix_text הוא מחרוזת שמציינת מה קרה ל-issue, ו-issue_string היא המחרוזת המוכרת owner/repo#issue_number שכבר ראיתם: בזמן שנתיב ה-lookup השתמש ישירות בטקסט שנשלח מ-Slack כדי למלא את issue_string, אתם תבנו אותו ישירות על בסיס הנתונים שהועברו ב-payload של ה-JSON.
#בניית הודעת Slack ושליחתה
ההודעות ש-Slack bot שלכם שולח בחזרה לערוץ ה-Slack שלכם מנתיבי ה-lookup וה-webhook דומות מאוד. בגלל זה, אפשר להשתמש מחדש ב-constructGhIssueSlackMessage הקיים כדי להמשיך למלא את src/handlers/webhook.ts. ייבאו את הפונקציה מ-src/utils/slack.ts, והעבירו אליה את נתוני ה-issue:
import { Hono } from "hono";
import { constructGhIssueSlackMessage } from "../utils/slack";
const app = new Hono();
app.post("/", async (c) => {
const { action, issue, repository } = await c.req.json();
const prefix_text = `An issue was ${action}:`;
const issue_string = `${repository.owner.login}/${repository.name}#${issue.number}`;
const blocks = constructGhIssueSlackMessage(issue, issue_string, prefix_text);
});
export default app;חשוב: השימוש ב-constructGhIssueSlackMessage במטפל הזה מוסיף ארגומנט נוסף אחד לפונקציה, prefix_text. עדכנו את הפונקציה המתאימה בתוך src/utils/slack.ts, והוסיפו את prefix_text לאוסף text_lines בבלוק ההודעה, אם הוא הועבר לפונקציה.
הוסיפו פונקציית עזר, compact, שמקבלת מערך, ומסננת ממנו כל ערכי null או undefined. הפונקציה הזו תשמש להסרת prefix_text מ-text_lines אם הוא לא הועבר בפועל לפונקציה, למשל כשקוראים לה מ-src/handlers/lookup.ts. הגרסה המלאה (והסופית) של src/utils/slack.ts נראית כך:
import { Issue } from "../types";
const compact = (array: unknown[]) => array.filter((el) => el);
export const constructGhIssueSlackMessage = (
issue: Issue,
issue_string: string,
prefix_text?: string,
) => {
const issue_link = `<${issue.html_url}|${issue_string}>`;
const user_link = `<${issue.user.html_url}|${issue.user.login}>`;
const date = new Date(Date.parse(issue.created_at)).toLocaleDateString();
const text_lines = [
prefix_text,
`*${issue.title} - ${issue_link}*`,
issue.body,
`*${issue.state}* - Created by ${user_link} on ${date}`,
];
return [
{
type: "section",
text: {
type: "mrkdwn",
text: compact(text_lines).join("\n"),
},
accessory: {
type: "image",
image_url: issue.user.avatar_url,
alt_text: issue.user.login,
},
},
];
};בחזרה ב-src/handlers/webhook.ts, ה-blocks שמוחזרים מ-constructGhIssueSlackMessage הופכים לגוף בבקשת fetch חדשה, בקשת HTTP POST לכתובת webhook של Slack. אחרי שהבקשה הזו מסתיימת, החזירו תשובה עם קוד סטטוס 200, וטקסט הגוף "OK":
import { Hono } from "hono";
import { constructGhIssueSlackMessage } from "../utils/slack";
import { Bindings } from "../types";
const app = new Hono<{ Bindings: Bindings }>();
app.post("/", async (c) => {
const { action, issue, repository } = await c.req.json();
const prefix_text = `An issue was ${action}:`;
const issue_string = `${repository.owner.login}/${repository.name}#${issue.number}`;
const blocks = constructGhIssueSlackMessage(issue, issue_string, prefix_text);
const fetchResponse = await fetch(c.env.SLACK_WEBHOOK_URL, {
body: JSON.stringify({ blocks }),
method: "POST",
headers: { "Content-Type": "application/json" },
});
return c.text("OK");
});
export default app;הקבוע SLACK_WEBHOOK_URL מייצג את כתובת ה-Webhook של Slack שיצרתם עוד בסעיף Incoming Webhook של המדריך הזה.
זהירות
מכיוון שה-webhook הזה מאפשר למפתחים לפרסם ישירות לערוץ ה-Slack שלכם, שמרו אותו בסוד.
כדי להשתמש בקבוע הזה בתוך בסיס הקוד שלכם, השתמשו בפקודה wrangler secret:
הגדרת הסוד SLACK_WEBHOOK_URL secretsh
npx wrangler secret put SLACK_WEBHOOK_URLEnter a secret value: https://hooks.slack.com/services/abc123#טיפול בשגיאות
בדומה לנתיב ה-lookup, נתיב ה-webhook צריך לכלול טיפול בסיסי בשגיאות. בניגוד ל-lookup, ששולח תשובות ישירות בחזרה לתוך Slack, אם משהו משתבש ב-webhook שלכם, יכול להיות שימושי לייצר בפועל תשובה שגויה, ולהחזיר אותה ל-GitHub.
כדי לעשות זאת, כתבו את מטפל השגיאות המותאם עם app.onError() והחזירו תשובה חדשה עם קוד סטטוס 500. הגרסה הסופית של src/routes/webhook.ts נראית כך:
import { Hono } from "hono";
import { constructGhIssueSlackMessage } from "../utils/slack";
import { Bindings } from "../types";
const app = new Hono<{ Bindings: Bindings }>();
app.post("/", async (c) => {
const { action, issue, repository } = await c.req.json();
const prefix_text = `An issue was ${action}:`;
const issue_string = `${repository.owner.login}/${repository.name}#${issue.number}`;
const blocks = constructGhIssueSlackMessage(issue, issue_string, prefix_text);
const fetchResponse = await fetch(c.env.SLACK_WEBHOOK_URL, {
body: JSON.stringify({ blocks }),
method: "POST",
headers: { "Content-Type": "application/json" },
});
if (!fetchResponse.ok) throw new Error();
return c.text("OK");
});
app.onError((_e, c) => {
return c.json(
{
message: "Unable to handle webhook",
},
500,
);
});
export default app;#פריסה
עם השלמת השלבים הקודמים, סיימתם לכתוב את הקוד של Slack bot שלכם. עכשיו אפשר לפרוס את האפליקציה.
ל-Wrangler יש תמיכה מובנית ב-bundling, בהעלאה ובשחרור של אפליקציית Cloudflare Workers שלכם. כדי לעשות זאת, הריצו את הפקודה הבאה, שתבנה ותפרוס את הקוד.
npm
npm i -- deployyarn
yarn add deploypnpm
pnpm add deploybun
bun add deployפריסת אפליקציית ה-Workers שלכם אמורה עכשיו לגרום לעדכוני issue להתחיל להופיע בערוץ ה-Slack שלכם, כי ה-webhook של GitHub יכול עכשיו להגיע בהצלחה לנתיב ה-webhook של ה-Workers שלכם:

#משאבים קשורים
במדריך הזה, בניתם ופרסתם אפליקציית Cloudflare Workers שיכולה להגיב לאירועי webhook של GitHub, ולאפשר חיפושי GitHub API בתוך Slack. אם תרצו לעיין בקוד המקור המלא של האפליקציה הזו, אפשר למצוא את המאגר ב-GitHub ↗︎.
אם אתם רוצים להתחיל לבנות פרויקטים משלכם, עיינו ברשימה הקיימת של תבניות Quickstart.