mirror of https://github.com/usual2970/certimate
				
				
				
			feat(notify): add mattermost
							parent
							
								
									4e3f499d76
								
							
						
					
					
						commit
						ec0cdf8b96
					
				| 
						 | 
				
			
			@ -14,6 +14,7 @@ const (
 | 
			
		|||
	NotifyChannelTypeEmail      = NotifyChannelType("email")
 | 
			
		||||
	NotifyChannelTypeGotify     = NotifyChannelType("gotify")
 | 
			
		||||
	NotifyChannelTypeLark       = NotifyChannelType("lark")
 | 
			
		||||
	NotifyChannelTypeMattermost = NotifyChannelType("mattermost")
 | 
			
		||||
	NotifyChannelTypePushPlus   = NotifyChannelType("pushplus")
 | 
			
		||||
	NotifyChannelTypeServerChan = NotifyChannelType("serverchan")
 | 
			
		||||
	NotifyChannelTypeTelegram   = NotifyChannelType("telegram")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ import (
 | 
			
		|||
	pEmail "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/email"
 | 
			
		||||
	pGotify "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/gotify"
 | 
			
		||||
	pLark "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/lark"
 | 
			
		||||
	pMattermost "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
 | 
			
		||||
	pPushPlus "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/pushplus"
 | 
			
		||||
	pServerChan "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/serverchan"
 | 
			
		||||
	pTelegram "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/telegram"
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +60,14 @@ func createNotifier(channel domain.NotifyChannelType, channelConfig map[string]a
 | 
			
		|||
			WebhookUrl: maputil.GetString(channelConfig, "webhookUrl"),
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	case domain.NotifyChannelTypeMattermost:
 | 
			
		||||
		return pMattermost.NewNotifier(&pMattermost.NotifierConfig{
 | 
			
		||||
			ServerUrl: maputil.GetString(channelConfig, "serverUrl"),
 | 
			
		||||
			ChannelId: maputil.GetString(channelConfig, "channelId"),
 | 
			
		||||
			Username:  maputil.GetString(channelConfig, "username"),
 | 
			
		||||
			Password:  maputil.GetString(channelConfig, "password"),
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	case domain.NotifyChannelTypePushPlus:
 | 
			
		||||
		return pPushPlus.NewNotifier(&pPushPlus.NotifierConfig{
 | 
			
		||||
			Token: maputil.GetString(channelConfig, "token"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
package mattermost
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/nikoksr/notify/service/mattermost"
 | 
			
		||||
	"github.com/usual2970/certimate/internal/pkg/core/notifier"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type NotifierConfig struct {
 | 
			
		||||
	// Mattermost 服务地址。
 | 
			
		||||
	ServerUrl string `json:"serverUrl"`
 | 
			
		||||
	// 频道ID
 | 
			
		||||
	ChannelId string `json:"channelId"`
 | 
			
		||||
	// 用户名
 | 
			
		||||
	Username string `json:"username"`
 | 
			
		||||
	// 密码
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NotifierProvider struct {
 | 
			
		||||
	config *NotifierConfig
 | 
			
		||||
	logger *slog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ notifier.Notifier = (*NotifierProvider)(nil)
 | 
			
		||||
 | 
			
		||||
func NewNotifier(config *NotifierConfig) (*NotifierProvider, error) {
 | 
			
		||||
	if config == nil {
 | 
			
		||||
		panic("config is nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &NotifierProvider{
 | 
			
		||||
		config: config,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (n *NotifierProvider) WithLogger(logger *slog.Logger) notifier.Notifier {
 | 
			
		||||
	if logger == nil {
 | 
			
		||||
		n.logger = slog.Default()
 | 
			
		||||
	} else {
 | 
			
		||||
		n.logger = logger
 | 
			
		||||
	}
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (n *NotifierProvider) Notify(ctx context.Context, subject string, message string) (res *notifier.NotifyResult, err error) {
 | 
			
		||||
	srv := mattermost.New(n.config.ServerUrl)
 | 
			
		||||
 | 
			
		||||
	if err := srv.LoginWithCredentials(ctx, n.config.Username, n.config.Password); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srv.AddReceivers(n.config.ChannelId)
 | 
			
		||||
 | 
			
		||||
	// 复写消息样式
 | 
			
		||||
	srv.PreSend(func(req *http.Request) error {
 | 
			
		||||
		m := map[string]interface{}{
 | 
			
		||||
			"channel_id": n.config.ChannelId,
 | 
			
		||||
			"props": map[string]interface{}{
 | 
			
		||||
				"attachments": []map[string]interface{}{
 | 
			
		||||
					{
 | 
			
		||||
						"title": subject,
 | 
			
		||||
						"text":  message,
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if body, err := json.Marshal(m); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		} else {
 | 
			
		||||
			req.ContentLength = int64(len(body))
 | 
			
		||||
			req.Body = io.NopCloser(bytes.NewReader(body))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err = srv.Send(ctx, subject, message); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ¬ifier.NotifyResult{}, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
package mattermost_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	provider "github.com/usual2970/certimate/internal/pkg/core/notifier/providers/mattermost"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	mockSubject = "test_subject"
 | 
			
		||||
	mockMessage = "test_message"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	fServerUrl string
 | 
			
		||||
	fChannelId string
 | 
			
		||||
	fUsername  string
 | 
			
		||||
	fPassword  string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	argsPrefix := "CERTIMATE_NOTIFIER_MATTERMOST_"
 | 
			
		||||
 | 
			
		||||
	flag.StringVar(&fServerUrl, argsPrefix+"SERVERURL", "", "")
 | 
			
		||||
	flag.StringVar(&fChannelId, argsPrefix+"CHANNELID", "", "")
 | 
			
		||||
	flag.StringVar(&fUsername, argsPrefix+"USERNAME", "", "")
 | 
			
		||||
	flag.StringVar(&fPassword, argsPrefix+"PASSWORD", "", "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Shell command to run this test:
 | 
			
		||||
 | 
			
		||||
	go test -v ./mattermost_test.go -args \
 | 
			
		||||
	--CERTIMATE_NOTIFIER_MATTERMOST_SERVERURL="https://example.com/your-server-url" \
 | 
			
		||||
	--CERTIMATE_NOTIFIER_MATTERMOST_CHANNELID="your-chanel-id" \
 | 
			
		||||
	--CERTIMATE_NOTIFIER_MATTERMOST_USERNAME="your-username" \
 | 
			
		||||
	--CERTIMATE_NOTIFIER_MATTERMOST_PASSWORD="your-password"
 | 
			
		||||
*/
 | 
			
		||||
func TestNotify(t *testing.T) {
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	t.Run("Notify", func(t *testing.T) {
 | 
			
		||||
		t.Log(strings.Join([]string{
 | 
			
		||||
			"args:",
 | 
			
		||||
			fmt.Sprintf("SERVERURL: %v", fServerUrl),
 | 
			
		||||
			fmt.Sprintf("CHANNELID: %v", fChannelId),
 | 
			
		||||
			fmt.Sprintf("USERNAME: %v", fUsername),
 | 
			
		||||
			fmt.Sprintf("PASSWORD: %v", fPassword),
 | 
			
		||||
		}, "\n"))
 | 
			
		||||
 | 
			
		||||
		notifier, err := provider.NewNotifier(&provider.NotifierConfig{
 | 
			
		||||
			ServerUrl: fServerUrl,
 | 
			
		||||
			ChannelId: fChannelId,
 | 
			
		||||
			Username:  fUsername,
 | 
			
		||||
			Password:  fPassword,
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("err: %+v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res, err := notifier.Notify(context.Background(), mockSubject, mockMessage)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("err: %+v", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		t.Logf("ok: %v", res)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
import { forwardRef, useImperativeHandle, useMemo } from "react";
 | 
			
		||||
import { Form, type FormInstance } from "antd";
 | 
			
		||||
import {forwardRef, useImperativeHandle, useMemo} from "react";
 | 
			
		||||
import {Form, type FormInstance} from "antd";
 | 
			
		||||
 | 
			
		||||
import { NOTIFY_CHANNELS, type NotifyChannelsSettingsContent } from "@/domain/settings";
 | 
			
		||||
import { useAntdForm } from "@/hooks";
 | 
			
		||||
import {NOTIFY_CHANNELS, type NotifyChannelsSettingsContent} from "@/domain/settings";
 | 
			
		||||
import {useAntdForm} from "@/hooks";
 | 
			
		||||
 | 
			
		||||
import NotifyChannelEditFormBarkFields from "./NotifyChannelEditFormBarkFields";
 | 
			
		||||
import NotifyChannelEditFormDingTalkFields from "./NotifyChannelEditFormDingTalkFields";
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ import NotifyChannelEditFormServerChanFields from "./NotifyChannelEditFormServer
 | 
			
		|||
import NotifyChannelEditFormTelegramFields from "./NotifyChannelEditFormTelegramFields";
 | 
			
		||||
import NotifyChannelEditFormWebhookFields from "./NotifyChannelEditFormWebhookFields";
 | 
			
		||||
import NotifyChannelEditFormWeComFields from "./NotifyChannelEditFormWeComFields";
 | 
			
		||||
import NotifyChannelEditFormMattermostFields from "@/components/notification/NotifyChannelEditFormMattermostFields.tsx";
 | 
			
		||||
 | 
			
		||||
type NotifyChannelEditFormFieldValues = NotifyChannelsSettingsContent[keyof NotifyChannelsSettingsContent];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +55,8 @@ const NotifyChannelEditForm = forwardRef<NotifyChannelEditFormInstance, NotifyCh
 | 
			
		|||
          return <NotifyChannelEditFormGotifyFields />;
 | 
			
		||||
        case NOTIFY_CHANNELS.LARK:
 | 
			
		||||
          return <NotifyChannelEditFormLarkFields />;
 | 
			
		||||
        case NOTIFY_CHANNELS.MATTERMOST:
 | 
			
		||||
          return <NotifyChannelEditFormMattermostFields />;
 | 
			
		||||
        case NOTIFY_CHANNELS.PUSHPLUS:
 | 
			
		||||
          return <NotifyChannelEditFormPushPlusFields />;
 | 
			
		||||
        case NOTIFY_CHANNELS.SERVERCHAN:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
import {useTranslation} from "react-i18next";
 | 
			
		||||
import {Form, Input} from "antd";
 | 
			
		||||
import {createSchemaFieldRule} from "antd-zod";
 | 
			
		||||
import {z} from "zod";
 | 
			
		||||
 | 
			
		||||
const NotifyChannelEditFormMattermostFields = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const formSchema = z.object({
 | 
			
		||||
    serverUrl: z
 | 
			
		||||
      .string({ message: t("settings.notification.channel.form.mattermost_server_url.placeholder") })
 | 
			
		||||
      .url(t("common.errmsg.url_invalid")),
 | 
			
		||||
    channelId: z
 | 
			
		||||
      .string({ message: t("settings.notification.channel.form.mattermost_channel_id.placeholder") })
 | 
			
		||||
      .nonempty(t("settings.notification.channel.form.mattermost_channel_id.placeholder")),
 | 
			
		||||
    username: z
 | 
			
		||||
      .string({ message: t("settings.notification.channel.form.mattermost_username.placeholder") })
 | 
			
		||||
      .nonempty(t("settings.notification.channel.form.mattermost_username.placeholder")),
 | 
			
		||||
    password: z
 | 
			
		||||
      .string({ message: t("settings.notification.channel.form.mattermost_password.placeholder") })
 | 
			
		||||
      .nonempty(t("settings.notification.channel.form.mattermost_password.placeholder")),
 | 
			
		||||
  });
 | 
			
		||||
  const formRule = createSchemaFieldRule(formSchema);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="serverUrl"
 | 
			
		||||
        label={t("settings.notification.channel.form.mattermost_server_url.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
        tooltip={<span dangerouslySetInnerHTML={{ __html: t("settings.notification.channel.form.mattermost_server_url.tooltip") }}></span>}
 | 
			
		||||
      >
 | 
			
		||||
        <Input placeholder={t("settings.notification.channel.form.mattermost_server_url.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="channelId"
 | 
			
		||||
        label={t("settings.notification.channel.form.mattermost_channel_id.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
      >
 | 
			
		||||
        <Input placeholder={t("settings.notification.channel.form.mattermost_channel_id.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="username"
 | 
			
		||||
        label={t("settings.notification.channel.form.mattermost_username.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
      >
 | 
			
		||||
        <Input placeholder={t("settings.notification.channel.form.mattermost_username.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
 | 
			
		||||
      <Form.Item
 | 
			
		||||
        name="password"
 | 
			
		||||
        label={t("settings.notification.channel.form.mattermost_password.label")}
 | 
			
		||||
        rules={[formRule]}
 | 
			
		||||
      >
 | 
			
		||||
        <Input.Password placeholder={t("settings.notification.channel.form.mattermost_password.placeholder")} />
 | 
			
		||||
      </Form.Item>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default NotifyChannelEditFormMattermostFields;
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +44,7 @@ export const NOTIFY_CHANNELS = Object.freeze({
 | 
			
		|||
  EMAIL: "email",
 | 
			
		||||
  GOTIFY: "gotify",
 | 
			
		||||
  LARK: "lark",
 | 
			
		||||
  MATTERMOST: "mattermost",
 | 
			
		||||
  PUSHPLUS: "pushplus",
 | 
			
		||||
  SERVERCHAN: "serverchan",
 | 
			
		||||
  TELEGRAM: "telegram",
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +65,7 @@ export type NotifyChannelsSettingsContent = {
 | 
			
		|||
  [NOTIFY_CHANNELS.EMAIL]?: EmailNotifyChannelConfig;
 | 
			
		||||
  [NOTIFY_CHANNELS.GOTIFY]?: GotifyNotifyChannelConfig;
 | 
			
		||||
  [NOTIFY_CHANNELS.LARK]?: LarkNotifyChannelConfig;
 | 
			
		||||
  [NOTIFY_CHANNELS.MATTERMOST]?: MattermostNotifyChannelConfig;
 | 
			
		||||
  [NOTIFY_CHANNELS.PUSHPLUS]?: PushPlusNotifyChannelConfig;
 | 
			
		||||
  [NOTIFY_CHANNELS.SERVERCHAN]?: ServerChanNotifyChannelConfig;
 | 
			
		||||
  [NOTIFY_CHANNELS.TELEGRAM]?: TelegramNotifyChannelConfig;
 | 
			
		||||
| 
						 | 
				
			
			@ -106,6 +108,14 @@ export type LarkNotifyChannelConfig = {
 | 
			
		|||
  enabled?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type MattermostNotifyChannelConfig = {
 | 
			
		||||
  serverUrl: string;
 | 
			
		||||
  channel: string;
 | 
			
		||||
  username: string;
 | 
			
		||||
  password: string;
 | 
			
		||||
  enabled?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type PushPlusNotifyChannelConfig = {
 | 
			
		||||
  token: string;
 | 
			
		||||
  enabled?: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -143,6 +153,7 @@ export const notifyChannelsMap: Map<NotifyChannel["type"], NotifyChannel> = new
 | 
			
		|||
    [NOTIFY_CHANNELS.DINGTALK, "common.notifier.dingtalk"],
 | 
			
		||||
    [NOTIFY_CHANNELS.GOTIFY, "common.notifier.gotify"],
 | 
			
		||||
    [NOTIFY_CHANNELS.LARK, "common.notifier.lark"],
 | 
			
		||||
    [NOTIFY_CHANNELS.MATTERMOST, "common.notifier.mattermost"],
 | 
			
		||||
    [NOTIFY_CHANNELS.PUSHPLUS, "common.notifier.pushplus"],
 | 
			
		||||
    [NOTIFY_CHANNELS.WECOM, "common.notifier.wecom"],
 | 
			
		||||
    [NOTIFY_CHANNELS.TELEGRAM, "common.notifier.telegram"],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,7 @@
 | 
			
		|||
  "common.notifier.email": "Email",
 | 
			
		||||
  "common.notifier.gotify": "Gotify",
 | 
			
		||||
  "common.notifier.lark": "Lark",
 | 
			
		||||
  "common.notifier.mattermost": "Mattermost",
 | 
			
		||||
  "common.notifier.pushplus": "PushPlus",
 | 
			
		||||
  "common.notifier.serverchan": "ServerChan",
 | 
			
		||||
  "common.notifier.telegram": "Telegram",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,15 @@
 | 
			
		|||
  "settings.notification.channel.form.lark_webhook_url.label": "Webhook URL",
 | 
			
		||||
  "settings.notification.channel.form.lark_webhook_url.placeholder": "Please enter Webhook URL",
 | 
			
		||||
  "settings.notification.channel.form.lark_webhook_url.tooltip": "For more information, see <a href=\"https://www.feishu.cn/hc/en-US/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/en-US/articles/807992406756</a>",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_server_url.label": "Service URL",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_server_url.placeholder": "Please enter service URL",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_server_url.tooltip": "Example: <b>https://exmaple.com</b>, the protocol needs to be included but the trailing '/' should not be included.",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_channel_id.label": "Channel ID",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_channel_id.placeholder": "Please enter channel ID",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_username.label": "Username",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_username.placeholder": "Please enter username",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_password.label": "Password",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_password.placeholder": "Please enter password",
 | 
			
		||||
  "settings.notification.channel.form.pushplus_token.placeholder": "Please enter Token",
 | 
			
		||||
  "settings.notification.channel.form.pushplus_token.label": "Token",
 | 
			
		||||
  "settings.notification.channel.form.pushplus_token.tooltip": "For more information, see <a href=\"https://www.pushplus.plus/push1.html\" target=\"_blank\">https://www.pushplus.plus/push1.html</a>",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,7 @@
 | 
			
		|||
  "common.notifier.email": "邮件",
 | 
			
		||||
  "common.notifier.gotify": "Gotify",
 | 
			
		||||
  "common.notifier.lark": "飞书",
 | 
			
		||||
  "common.notifier.mattermost": "Mattermost",
 | 
			
		||||
  "common.notifier.pushplus": "PushPlus推送加",
 | 
			
		||||
  "common.notifier.serverchan": "Server 酱",
 | 
			
		||||
  "common.notifier.telegram": "Telegram",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,15 @@
 | 
			
		|||
  "settings.notification.channel.form.lark_webhook_url.label": "机器人 Webhook 地址",
 | 
			
		||||
  "settings.notification.channel.form.lark_webhook_url.placeholder": "请输入机器人 Webhook 地址",
 | 
			
		||||
  "settings.notification.channel.form.lark_webhook_url.tooltip": "这是什么?请参阅 <a href=\"https://www.feishu.cn/hc/zh-CN/articles/807992406756\" target=\"_blank\">https://www.feishu.cn/hc/zh-CN/articles/807992406756</a>",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_server_url.label": "服务地址",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_server_url.placeholder": "请输入服务地址",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_server_url.tooltip": "示例: <b>https://exmaple.com</b>,需要包含协议但不要包含末尾的'/'",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_channel_id.label": "频道ID",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_channel_id.placeholder": "请输入频道ID",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_username.label": "用户名",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_username.placeholder": "请输入用户名",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_password.label": "密码",
 | 
			
		||||
  "settings.notification.channel.form.mattermost_password.placeholder": "请输入密码",
 | 
			
		||||
  "settings.notification.channel.form.pushplus_token.placeholder": "请输入Token",
 | 
			
		||||
  "settings.notification.channel.form.pushplus_token.label": "Token",
 | 
			
		||||
  "settings.notification.channel.form.pushplus_token.tooltip": "请参阅 <a href=\"https://www.pushplus.plus/push1.html\" target=\"_blank\">https://www.pushplus.plus/push1.html</a>",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue