feat: new notification provider: slack bot

This commit is contained in:
Fu Diwei
2025-05-26 16:41:16 +08:00
parent 8e23b14bf3
commit e82a59289b
30 changed files with 341 additions and 11 deletions

View File

@@ -64,6 +64,7 @@ import AccessFormQiniuConfig from "./AccessFormQiniuConfig";
import AccessFormRainYunConfig from "./AccessFormRainYunConfig";
import AccessFormRatPanelConfig from "./AccessFormRatPanelConfig";
import AccessFormSafeLineConfig from "./AccessFormSafeLineConfig";
import AccessFormSlackBotConfig from "./AccessFormSlackBotConfig";
import AccessFormSSHConfig from "./AccessFormSSHConfig";
import AccessFormSSLComConfig from "./AccessFormSSLComConfig";
import AccessFormTelegramBotConfig from "./AccessFormTelegramBotConfig";
@@ -289,6 +290,8 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
return <AccessFormRatPanelConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.SAFELINE:
return <AccessFormSafeLineConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.SLACKBOT:
return <AccessFormSlackBotConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.SSH:
return <AccessFormSSHConfig {...nestedFormProps} />;
case ACCESS_PROVIDERS.TELEGRAMBOT:

View File

@@ -28,7 +28,8 @@ const AccessFormDiscordBotConfig = ({ form: formInst, formName, disabled, initia
botToken: z
.string({ message: t("access.form.discordbot_token.placeholder") })
.min(1, t("access.form.discordbot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 })),
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
defaultChannelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);

View File

@@ -0,0 +1,71 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { type AccessConfigForSlackBot } from "@/domain/access";
type AccessFormSlackBotConfigFieldValues = Nullish<AccessConfigForSlackBot>;
export type AccessFormSlackBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: AccessFormSlackBotConfigFieldValues;
onValuesChange?: (values: AccessFormSlackBotConfigFieldValues) => void;
};
const initFormModel = (): AccessFormSlackBotConfigFieldValues => {
return {
botToken: "",
};
};
const AccessFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: AccessFormSlackBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
botToken: z
.string({ message: t("access.form.slackbot_token.placeholder") })
.min(1, t("access.form.slackbot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
defaultChannelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="botToken"
label={t("access.form.slackbot_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.slackbot_token.tooltip") }}></span>}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.slackbot_token.placeholder")} />
</Form.Item>
<Form.Item
name="defaultChannelId"
label={t("access.form.slackbot_default_channel_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.slackbot_default_channel_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("access.form.slackbot_default_channel_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default AccessFormSlackBotConfig;

View File

@@ -28,7 +28,8 @@ const AccessFormTelegramBotConfig = ({ form: formInst, formName, disabled, initi
botToken: z
.string({ message: t("access.form.telegrambot_token.placeholder") })
.min(1, t("access.form.telegrambot_token.placeholder"))
.max(256, t("common.errmsg.string_max", { max: 256 })),
.max(256, t("common.errmsg.string_max", { max: 256 }))
.trim(),
defaultChatId: z
.preprocess(
(v) => (v == null || v === "" ? undefined : Number(v)),

View File

@@ -20,6 +20,7 @@ import { useNotifyChannelsStore } from "@/stores/notify";
import NotifyNodeConfigFormDiscordBotConfig from "./NotifyNodeConfigFormDiscordBotConfig";
import NotifyNodeConfigFormEmailConfig from "./NotifyNodeConfigFormEmailConfig";
import NotifyNodeConfigFormMattermostConfig from "./NotifyNodeConfigFormMattermostConfig";
import NotifyNodeConfigFormSlackBotConfig from "./NotifyNodeConfigFormSlackBotConfig";
import NotifyNodeConfigFormTelegramBotConfig from "./NotifyNodeConfigFormTelegramBotConfig";
import NotifyNodeConfigFormWebhookConfig from "./NotifyNodeConfigFormWebhookConfig";
@@ -117,6 +118,8 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
return <NotifyNodeConfigFormEmailConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.MATTERMOST:
return <NotifyNodeConfigFormMattermostConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.SLACKBOT:
return <NotifyNodeConfigFormSlackBotConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.TELEGRAMBOT:
return <NotifyNodeConfigFormTelegramBotConfig {...nestedFormProps} />;
case NOTIFICATION_PROVIDERS.WEBHOOK:

View File

@@ -0,0 +1,55 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
type NotifyNodeConfigFormSlackBotConfigFieldValues = Nullish<{
channelId?: string;
}>;
export type NotifyNodeConfigFormSlackBotConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: NotifyNodeConfigFormSlackBotConfigFieldValues;
onValuesChange?: (values: NotifyNodeConfigFormSlackBotConfigFieldValues) => void;
};
const initFormModel = (): NotifyNodeConfigFormSlackBotConfigFieldValues => {
return {};
};
const NotifyNodeConfigFormSlackBotConfig = ({ form: formInst, formName, disabled, initialValues, onValuesChange }: NotifyNodeConfigFormSlackBotConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
channelId: z.string().nullish(),
});
const formRule = createSchemaFieldRule(formSchema);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item
name="channelId"
label={t("workflow_node.notify.form.slackbot_channel_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.notify.form.slackbot_channel_id.tooltip") }}></span>}
>
<Input allowClear placeholder={t("workflow_node.notify.form.slackbot_channel_id.placeholder")} />
</Form.Item>
</Form>
);
};
export default NotifyNodeConfigFormSlackBotConfig;

View File

@@ -58,6 +58,7 @@ export interface AccessModel extends BaseModel {
| AccessConfigForRainYun
| AccessConfigForRatPanel
| AccessConfigForSafeLine
| AccessConfigForSlackBot
| AccessConfigForSSH
| AccessConfigForSSLCom
| AccessConfigForTelegramBot
@@ -361,6 +362,11 @@ export type AccessConfigForSafeLine = {
allowInsecureConnections?: boolean;
};
export type AccessConfigForSlackBot = {
botToken: string;
defaultChannelId?: string;
};
export type AccessConfigForSSH = {
host: string;
port: number;

View File

@@ -61,6 +61,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
RAINYUN: "rainyun",
RATPANEL: "ratpanel",
SAFELINE: "safeline",
SLACKBOT: "slackbot",
SSH: "ssh",
SSLCOM: "sslcom",
TELEGRAMBOT: "telegrambot",
@@ -174,6 +175,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[ACCESS_PROVIDERS.LARKBOT, "provider.larkbot", "/imgs/providers/lark.svg", [ACCESS_USAGES.NOTIFICATION]],
[ACCESS_PROVIDERS.WECOMBOT, "provider.wecombot", "/imgs/providers/wecom.svg", [ACCESS_USAGES.NOTIFICATION]],
[ACCESS_PROVIDERS.DISCORDBOT, "provider.discordbot", "/imgs/providers/discord.svg", [ACCESS_USAGES.NOTIFICATION]],
[ACCESS_PROVIDERS.SLACKBOT, "provider.slackbot", "/imgs/providers/slack.svg", [ACCESS_USAGES.NOTIFICATION]],
[ACCESS_PROVIDERS.TELEGRAMBOT, "provider.telegrambot", "/imgs/providers/telegram.svg", [ACCESS_USAGES.NOTIFICATION]],
[ACCESS_PROVIDERS.MATTERMOST, "provider.mattermost", "/imgs/providers/mattermost.svg", [ACCESS_USAGES.NOTIFICATION]],
].map((e) => [
@@ -594,6 +596,7 @@ export const NOTIFICATION_PROVIDERS = Object.freeze({
EMAIL: `${ACCESS_PROVIDERS.EMAIL}`,
LARKBOT: `${ACCESS_PROVIDERS.LARKBOT}`,
MATTERMOST: `${ACCESS_PROVIDERS.MATTERMOST}`,
SLACKBOT: `${ACCESS_PROVIDERS.SLACKBOT}`,
TELEGRAMBOT: `${ACCESS_PROVIDERS.TELEGRAMBOT}`,
WEBHOOK: `${ACCESS_PROVIDERS.WEBHOOK}`,
WECOMBOT: `${ACCESS_PROVIDERS.WECOMBOT}`,
@@ -620,6 +623,7 @@ export const notificationProvidersMap: Map<NotificationProvider["type"] | string
[NOTIFICATION_PROVIDERS.LARKBOT],
[NOTIFICATION_PROVIDERS.WECOMBOT],
[NOTIFICATION_PROVIDERS.DISCORDBOT],
[NOTIFICATION_PROVIDERS.SLACKBOT],
[NOTIFICATION_PROVIDERS.TELEGRAMBOT],
[NOTIFICATION_PROVIDERS.MATTERMOST],
].map(([type]) => [

View File

@@ -163,7 +163,7 @@
"access.form.dingtalkbot_secret.tooltip": "For more information, see <a href=\"https://open.dingtalk.com/document/orgapp/customize-robot-security-settings\" target=\"_blank\">https://open.dingtalk.com/document/orgapp/customize-robot-security-settings</a>",
"access.form.discordbot_token.label": "Discord bot token",
"access.form.discordbot_token.placeholder": "Please enter Discord bot token",
"access.form.discordbot_token.tooltip": "How to get it? Please refer to <a href=\"https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token\" target=\"_blank\">https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token</a>",
"access.form.discordbot_token.tooltip": "For more information, see <a href=\"https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token\" target=\"_blank\">https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token</a>",
"access.form.discordbot_default_channel_id.label": "Default Discord channel ID (Optional)",
"access.form.discordbot_default_channel_id.placeholder": "Please enter default Discord channel ID",
"access.form.discordbot_default_channel_id.tooltip": "For more information, see <a href=\"https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID\" target=\"_blank\">https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID</a>",
@@ -362,6 +362,12 @@
"access.form.safeline_api_token.label": "SafeLine API token",
"access.form.safeline_api_token.placeholder": "Please enter SafeLine API token",
"access.form.safeline_api_token.tooltip": "For more information, see <a href=\"https://docs.waf.chaitin.com/en/reference/articles/openapi\" target=\"_blank\">https://docs.waf.chaitin.com/en/reference/articles/openapi</a>",
"access.form.slackbot_token.label": "Slack bot token",
"access.form.slackbot_token.placeholder": "Please enter Slack bot token",
"access.form.slackbot_token.tooltip": "For more information, see <a href=\"https://docs.slack.dev/authentication/tokens#bot\" target=\"_blank\">https://docs.slack.dev/authentication/tokens#bot</a>",
"access.form.slackbot_default_channel_id.label": "Default Slack channel ID (Optional)",
"access.form.slackbot_default_channel_id.placeholder": "Please enter default Slack channel ID",
"access.form.slackbot_default_channel_id.tooltip": "How to get it? Please refer to <a href=\"https://www.youtube.com/watch?v=Uz5Yi5C2pwQ\" target=\"_blank\">https://www.youtube.com/watch?v=Uz5Yi5C2pwQ</a>",
"access.form.ssh_host.label": "Server host",
"access.form.ssh_host.placeholder": "Please enter server host",
"access.form.ssh_port.label": "Server port",

View File

@@ -68,7 +68,7 @@
"provider.dynv6": "dynv6",
"provider.edgio": "Edgio",
"provider.edgio.applications": "Edgio - Applications",
"provider.email": "Email",
"provider.email": "Email (SMTP)",
"provider.fastly": "Fastly",
"provider.flexcdn": "FlexCDN",
"provider.gcore": "Gcore",
@@ -96,7 +96,7 @@
"provider.lecdn": "LeCDN",
"provider.letsencrypt": "Let's Encrypt",
"provider.letsencryptstaging": "Let's Encrypt Staging Environment",
"provider.local": "Local deployment",
"provider.local": "Local host",
"provider.mattermost": "Mattermost",
"provider.namecheap": "Namecheap",
"provider.namedotcom": "Name.com",
@@ -118,7 +118,8 @@
"provider.ratpanel.console": "RatPanel - Console",
"provider.ratpanel.site": "RatPanel - Website",
"provider.safeline": "SafeLine",
"provider.ssh": "SSH deployment",
"provider.slackbot": "Slack Bot",
"provider.ssh": "Remote host (SSH)",
"provider.sslcom": "SSL.com",
"provider.telegrambot": "Telegram Bot",
"provider.tencentcloud": "Tencent Cloud",

View File

@@ -827,6 +827,9 @@
"workflow_node.notify.form.mattermost_channel_id.label": "Mattermost channel ID (Optional)",
"workflow_node.notify.form.mattermost_channel_id.placeholder": "Please enter Mattermost channel ID to override the default value",
"workflow_node.notify.form.mattermost_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.",
"workflow_node.notify.form.slackbot_channel_id.label": "Slack channel ID (Optional)",
"workflow_node.notify.form.slackbot_channel_id.placeholder": "Please enter Slack channel ID to override the default value",
"workflow_node.notify.form.slackbot_channel_id.tooltip": "Leave it blank to use the default channel ID provided by the authorization.",
"workflow_node.notify.form.telegrambot_chat_id.label": "Telegram chat ID (Optional)",
"workflow_node.notify.form.telegrambot_chat_id.placeholder": "Please enter Telegram chat ID to override the default value",
"workflow_node.notify.form.telegrambot_chat_id.tooltip": "Leave it blank to use the default chat ID provided by the selected authorization.",

View File

@@ -157,7 +157,7 @@
"access.form.dingtalkbot_secret.tooltip": "这是什么?请参阅 <a href=\"https://open.dingtalk.com/document/orgapp/customize-robot-security-settings\" target=\"_blank\">https://open.dingtalk.com/document/orgapp/customize-robot-security-settings</a>",
"access.form.discordbot_token.label": "Discord 机器人 API Token",
"access.form.discordbot_token.placeholder": "请输入 Discord 机器人 API Token",
"access.form.discordbot_token.tooltip": "如何获取此参数?请参阅 <a href=\"https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token\" target=\"_blank\">https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token</a>",
"access.form.discordbot_token.tooltip": "这是什么?请参阅 <a href=\"https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token\" target=\"_blank\">https://docs.discordbotstudio.org/setting-up-dbs/finding-your-bot-token</a>",
"access.form.discordbot_default_channel_id.label": "默认的 Discord 频道 ID可选",
"access.form.discordbot_default_channel_id.placeholder": "请输入默认的 Discord 频道 ID",
"access.form.discordbot_default_channel_id.tooltip": "这是什么?请参阅 <a href=\"https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID\" target=\"_blank\">https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID</a>",
@@ -356,6 +356,12 @@
"access.form.safeline_api_token.label": "雷池 API Token",
"access.form.safeline_api_token.placeholder": "请输入雷池 API Token",
"access.form.safeline_api_token.tooltip": "这是什么?请参阅 <a href=\"https://docs.waf-ce.chaitin.cn/zh/%E6%9B%B4%E5%A4%9A%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3/OPENAPI\" target=\"_blank\">https://docs.waf-ce.chaitin.cn/zh/更多技术文档/OPENAPI</a>",
"access.form.slackbot_token.label": "Slack 机器人 Token",
"access.form.slackbot_token.placeholder": "请输入 Slack 机器人 Token",
"access.form.slackbot_token.tooltip": "这是什么?请参阅 <a href=\"https://docs.slack.dev/authentication/tokens#bot\" target=\"_blank\">https://docs.slack.dev/authentication/tokens#bot</a>",
"access.form.slackbot_default_channel_id.label": "默认的 Slack 频道 ID可选",
"access.form.slackbot_default_channel_id.placeholder": "请输入默认的 Slack 频道 ID",
"access.form.slackbot_default_channel_id.tooltip": "如何获取此参数?请参阅 <a href=\"https://www.youtube.com/watch?v=Uz5Yi5C2pwQ\" target=\"_blank\">https://www.youtube.com/watch?v=Uz5Yi5C2pwQ</a>",
"access.form.ssh_host.label": "服务器地址",
"access.form.ssh_host.placeholder": "请输入服务器地址",
"access.form.ssh_port.label": "服务器端口",

View File

@@ -68,7 +68,7 @@
"provider.dynv6": "dynv6",
"provider.edgio": "Edgio",
"provider.edgio.applications": "Edgio - Applications",
"provider.email": "邮件",
"provider.email": "邮件SMTP",
"provider.fastly": "Fastly",
"provider.flexcdn": "FlexCDN",
"provider.gcore": "Gcore",
@@ -96,7 +96,7 @@
"provider.lecdn": "LeCDN",
"provider.letsencrypt": "Let's Encrypt",
"provider.letsencryptstaging": "Let's Encrypt 测试环境",
"provider.local": "本地部署",
"provider.local": "本地主机",
"provider.mattermost": "Mattermost",
"provider.namecheap": "Namecheap",
"provider.namedotcom": "Name.com",
@@ -118,7 +118,8 @@
"provider.ratpanel.console": "耗子面板 - 控制台",
"provider.ratpanel.site": "耗子面板 - 网站",
"provider.safeline": "雷池",
"provider.ssh": "SSH 部署",
"provider.slackbot": "Slack 机器人",
"provider.ssh": "远程主机SSH",
"provider.sslcom": "SSL.com",
"provider.telegrambot": "Telegram 机器人",
"provider.tencentcloud": "腾讯云",

View File

@@ -826,6 +826,9 @@
"workflow_node.notify.form.mattermost_channel_id.label": "Mattermost 频道 ID可选",
"workflow_node.notify.form.mattermost_channel_id.placeholder": "请输入 Mattermost 频道 ID 以覆盖默认值",
"workflow_node.notify.form.mattermost_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。",
"workflow_node.notify.form.slackbot_channel_id.label": "Slack 频道 ID可选",
"workflow_node.notify.form.slackbot_channel_id.placeholder": "请输入 Slack 频道 ID 以覆盖默认值",
"workflow_node.notify.form.slackbot_channel_id.tooltip": "不填写时,将使用所选通知渠道授权的默认频道 ID。",
"workflow_node.notify.form.telegrambot_chat_id.label": "Telegram 会话 ID可选",
"workflow_node.notify.form.telegrambot_chat_id.placeholder": "请输入 Telegram 会话 ID 以覆盖默认值",
"workflow_node.notify.form.telegrambot_chat_id.tooltip": "不填写时,将使用所选通知渠道授权的默认会话 ID。",