feat(provider): new notification provider: matrix

This commit is contained in:
tim20061 2026-06-15 18:18:48 +03:00 committed by GitHub
parent 3970eef77e
commit 62ad00870b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 528 additions and 6 deletions

View File

@ -398,6 +398,13 @@ type AccessConfigForLiteSSL struct {
AccessConfigForACMEExternalAccountBinding
}
type AccessConfigForMatrix struct {
ServerUrl string `json:"serverUrl"`
UserId string `json:"userId"`
AccessToken string `json:"accessToken"`
RoomId string `json:"roomId,omitempty"`
}
type AccessConfigForMattermost struct {
ServerUrl string `json:"serverUrl"`
Username string `json:"username"`

View File

@ -89,6 +89,7 @@ const (
AccessProviderTypeLinode = AccessProviderType("linode")
AccessProviderTypeLiteSSL = AccessProviderType("litessl")
AccessProviderTypeLocal = AccessProviderType("local")
AccessProviderTypeMatrix = AccessProviderType("matrix")
AccessProviderTypeMattermost = AccessProviderType("mattermost")
AccessProviderTypeMohua = AccessProviderType("mohua")
AccessProviderTypeNamecheap = AccessProviderType("namecheap")
@ -473,6 +474,7 @@ const (
NotificationProviderTypeDiscordBot = NotificationProviderType(AccessProviderTypeDiscordBot)
NotificationProviderTypeEmail = NotificationProviderType(AccessProviderTypeEmail)
NotificationProviderTypeLarkBot = NotificationProviderType(AccessProviderTypeLarkBot)
NotificationProviderTypeMatrix = NotificationProviderType(AccessProviderTypeMatrix)
NotificationProviderTypeMattermost = NotificationProviderType(AccessProviderTypeMattermost)
NotificationProviderTypeSlackBot = NotificationProviderType(AccessProviderTypeSlackBot)
NotificationProviderTypeTelegramBot = NotificationProviderType(AccessProviderTypeTelegramBot)

View File

@ -0,0 +1,27 @@
package notifiers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
"github.com/certimate-go/certimate/pkg/core/notifier/providers/matrix"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.NotificationProviderTypeMatrix, func(options *ProviderFactoryOptions) (core.Notifier, error) {
credentials := domain.AccessConfigForMatrix{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := matrix.NewNotifier(&matrix.NotifierConfig{
ServerUrl: credentials.ServerUrl,
UserId: credentials.UserId,
AccessToken: credentials.AccessToken,
RoomId: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "roomId", credentials.RoomId),
})
return provider, err
})
}

View File

@ -0,0 +1,73 @@
package matrix
import (
"context"
"errors"
"fmt"
"log/slog"
"github.com/certimate-go/certimate/pkg/core/notifier"
matrixsdk "github.com/certimate-go/certimate/pkg/sdk3rd/matrix"
)
type NotifierConfig struct {
// Homeserver URL (Element web URL or Matrix homeserver base URL).
// URL homeserver (адрес Element или базовый URL Matrix).
ServerUrl string `json:"serverUrl"`
// User ID (MXID), e.g. @bot:example.org.
// Идентификатор пользователя (MXID), например @bot:example.org.
UserId string `json:"userId"`
// Access token from the homeserver (bot or user).
// Access token с homeserver (бот или пользователь).
AccessToken string `json:"accessToken"`
// Room ID (!room:server) for notifications.
// ID комнаты (!room:server) для уведомлений.
RoomId string `json:"roomId"`
}
type Notifier struct {
config *NotifierConfig
logger *slog.Logger
}
var _ notifier.Provider = (*Notifier)(nil)
func NewNotifier(config *NotifierConfig) (*Notifier, error) {
if config == nil {
return nil, errors.New("the configuration of the notifier provider is nil")
}
return &Notifier{
config: config,
logger: slog.Default(),
}, nil
}
func (n *Notifier) SetLogger(logger *slog.Logger) {
if logger == nil {
n.logger = slog.New(slog.DiscardHandler)
} else {
n.logger = logger
}
}
func (n *Notifier) Notify(ctx context.Context, subject string, message string) (*notifier.NotifyResult, error) {
if n.config.RoomId == "" {
return nil, errors.New("matrix: config `roomId` is required")
}
client, err := matrixsdk.NewClient(n.config.ServerUrl,
matrixsdk.WithUserId(n.config.UserId),
matrixsdk.WithAccessToken(n.config.AccessToken),
)
if err != nil {
return nil, fmt.Errorf("matrix: %w", err)
}
body := fmt.Sprintf("%s\n\n%s", subject, message)
if err := client.SendTextMessageToRoom(ctx, n.config.RoomId, body); err != nil {
return nil, fmt.Errorf("matrix: %w", err)
}
return &notifier.NotifyResult{}, nil
}

View File

@ -0,0 +1,51 @@
package matrix_test
import (
"testing"
"github.com/certimate-go/certimate/pkg/core/notifier/internal/tester"
impl "github.com/certimate-go/certimate/pkg/core/notifier/providers/matrix"
)
var (
fp = tester.Args("MATRIX_")
fServerUrl string
fUserId string
fAccessToken string
fRoomId string
)
func init() {
fp.DefineString(&fServerUrl, "SERVERURL")
fp.DefineString(&fUserId, "USERID")
fp.DefineString(&fAccessToken, "ACCESSTOKEN")
fp.DefineString(&fRoomId, "ROOMID")
}
/*
Shell command to run this test:
go test -v ./matrix_test.go -args \
--MATRIX_SERVERURL="https://example.com/your-matrix-server" \
--MATRIX_USERID="@bot:example.org" \
--MATRIX_ACCESSTOKEN="your-access-token" \
--MATRIX_ROOMID="!room:example.org"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Notify", func(t *testing.T) {
provider, err := impl.NewNotifier(&impl.NotifierConfig{
ServerUrl: fServerUrl,
UserId: fUserId,
AccessToken: fAccessToken,
RoomId: fRoomId,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
tester.TestNotify(t, provider, tester.TestNotifyArgs{})
})
}

View File

@ -0,0 +1,36 @@
package matrix
import (
"context"
"fmt"
"net/url"
"strings"
)
// SendText posts an m.room.message event (m.text).
// Отправляет текстовое сообщение в комнату (m.room.message, msgtype m.text).
// REF: https://spec.matrix.org/latest/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
func (c *Client) SendTextMessageToRoom(ctx context.Context, roomId, msgBody string) error {
if strings.TrimSpace(roomId) == "" {
return fmt.Errorf("sdkerr: unset roomId")
}
txnId := newTransactionId()
path := fmt.Sprintf("/_matrix/client/v3/rooms/%s/send/m.room.message/%s",
url.PathEscape(roomId), url.PathEscape(txnId))
payload := map[string]any{
"msgtype": "m.text",
"body": msgBody,
}
_, err := c.rc.R().
SetContext(ctx).
SetBody(payload).
Put(path)
if err != nil {
return fmt.Errorf("sdkerr: api error: %w", err)
}
return nil
}

View File

@ -0,0 +1,17 @@
package matrix
import (
"fmt"
)
// probeVersions checks that the homeserver exposes the Client-Server API.
// Проверяет доступность homeserver (GET /_matrix/client/versions).
// REF: https://spec.matrix.org/latest/client-server-api/#get_matrixclientversions
func (c *Client) probeVersions() error {
_, err := c.rc.R().Get("/_matrix/client/versions")
if err != nil {
return fmt.Errorf("sdkerr: failed to probe Matrix Client API versions: %w", err)
}
return nil
}

View File

@ -0,0 +1,88 @@
package matrix
import (
"crypto/rand"
"crypto/tls"
"encoding/hex"
"fmt"
"net/url"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/certimate-go/certimate/internal/app"
)
type Client struct {
rc *resty.Client
}
func NewClient(serverUrl string, optFns ...OptionsFunc) (*Client, error) {
opts := &Options{}
for _, fn := range optFns {
fn(opts)
}
if serverUrl == "" {
return nil, fmt.Errorf("sdkerr: unset serverUrl")
}
if _, err := url.Parse(serverUrl); err != nil {
return nil, fmt.Errorf("sdkerr: invalid serverUrl: %w", err)
}
if opts.AccessToken == "" {
return nil, fmt.Errorf("sdkerr: unset accessToken")
}
baseUrl, _ := resolveBaseUrl(strings.TrimSuffix(serverUrl, "/"))
if baseUrl == "" {
baseUrl = serverUrl
}
client := &Client{}
client.rc = resty.New().
SetBaseURL(strings.TrimSuffix(baseUrl, "/")).
SetHeader("Authorization", "Bearer "+opts.AccessToken).
SetHeader("Content-Type", "application/json").
SetHeader("User-Agent", app.AppUserAgent).
if err := client.probeVersions(); err != nil {
return nil, err
}
return client, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.rc.SetTimeout(timeout)
return c
}
func (c *Client) SetTLSConfig(config *tls.Config) *Client {
c.rc.SetTLSClientConfig(config)
return c
}
func resolveBaseUrl(serverUrl string) (string, error) {
var wkJSON struct {
Homeserver struct {
BaseURL string `json:"base_url"`
} `json:"m.homeserver"`
}
_, err := resty.New().R().
SetResult(&wkJSON).
Get(serverUrl + "/.well-known/matrix/client")
if err != nil {
return "", fmt.Errorf("failed to discovery Matrix Client API: %w", err)
} else if strings.TrimSpace(wkJSON.Homeserver.BaseURL) != "" {
return wkJSON.Homeserver.BaseURL, nil
} else {
return serverUrl, nil
}
}
func newTransactionId() string {
b := make([]byte, 8)
_, _ = rand.Read(b)
return fmt.Sprintf("certimate_%d_%s", time.Now().UnixNano(), hex.EncodeToString(b))
}

View File

@ -0,0 +1,20 @@
package matrix
type Options struct {
UserId string
AccessToken string
}
type OptionsFunc func(*Options)
func WithUserId(userId string) OptionsFunc {
return func(o *Options) {
o.UserId = userId
}
}
func WithAccessToken(accessToken string) OptionsFunc {
return func(o *Options) {
o.AccessToken = accessToken
}
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 64 64"><rect width="64" height="64" fill="#0dbd8b" rx="12"/><path fill="#fff" d="M32 12c-8 0-14 4-18 10l6 5q4.5-6 12-6c7.5 0 9 2 12 6l6-5c-4-6-10-10-18-10M18 30l-8 6v16l8-6zm28 0v16l8 6V30l-8-6zm-14 4c-5 0-9 4-9 9v7h6v-7q0-3 3-3c3 0 3 1 3 3v7h6v-7c0-5-4-9-9-9"/></svg>

After

Width:  |  Height:  |  Size: 347 B

View File

@ -73,6 +73,7 @@ import AccessConfigFieldsProviderLarkBot from "./AccessConfigFieldsProviderLarkB
import AccessConfigFieldsProviderLeCDN from "./AccessConfigFieldsProviderLeCDN";
import AccessConfigFieldsProviderLinode from "./AccessConfigFieldsProviderLinode";
import AccessConfigFieldsProviderLiteSSL from "./AccessConfigFieldsProviderLiteSSL";
import AccessConfigFieldsProviderMatrix from "./AccessConfigFieldsProviderMatrix";
import AccessConfigFieldsProviderMattermost from "./AccessConfigFieldsProviderMattermost";
import AccessConfigFieldsProviderMohua from "./AccessConfigFieldsProviderMohua";
import AccessConfigFieldsProviderNamecheap from "./AccessConfigFieldsProviderNamecheap";
@ -196,6 +197,7 @@ const providerComponentMap: Partial<Record<AccessProviderType, React.ComponentTy
[ACCESS_PROVIDERS.INFOMANIAK]: AccessConfigFieldsProviderInfomaniak,
[ACCESS_PROVIDERS.LINODE]: AccessConfigFieldsProviderLinode,
[ACCESS_PROVIDERS.LITESSL]: AccessConfigFieldsProviderLiteSSL,
[ACCESS_PROVIDERS.MATRIX]: AccessConfigFieldsProviderMatrix,
[ACCESS_PROVIDERS.MATTERMOST]: AccessConfigFieldsProviderMattermost,
[ACCESS_PROVIDERS.MOHUA]: AccessConfigFieldsProviderMohua,
[ACCESS_PROVIDERS.NAMECHEAP]: AccessConfigFieldsProviderNamecheap,

View File

@ -0,0 +1,88 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { core, z } from "zod";
import { useFormNestedFieldsContext } from "./_context";
const AccessConfigFormFieldsProviderMatrix = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item
name={[parentNamePath, "serverUrl"]}
initialValue={initialValues.serverUrl}
label={t("access.form.matrix_server_url.label")}
rules={[formRule]}
>
<Input type="url" placeholder={t("access.form.matrix_server_url.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "userId"]}
initialValue={initialValues.userId}
label={t("access.form.matrix_user_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.matrix_user_id.tooltip") }} />}
>
<Input autoComplete="new-password" placeholder={t("access.form.matrix_user_id.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "accessToken"]}
initialValue={initialValues.accessToken}
label={t("access.form.matrix_access_token.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.matrix_access_token.tooltip") }} />}
>
<Input.Password autoComplete="new-password" placeholder={t("access.form.matrix_access_token.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "roomId"]}
initialValue={initialValues.roomId}
label={t("access.form.matrix_room_id.label")}
extra={t("access.form.matrix_room_id.help")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("access.form.matrix_room_id.tooltip") }} />}
>
<Input allowClear placeholder={t("access.form.matrix_room_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
serverUrl: "https://matrix-client.matrix.org",
userId: "",
accessToken: "",
roomId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n: ReturnType<typeof getI18n> }) => {
const { t: _ } = i18n;
return z.object({
serverUrl: z.url({ protocol: core.regexes.httpProtocol }),
userId: z.string().nonempty().startsWith("@"),
accessToken: z.string().nonempty(),
roomId: z.string().nullish(),
});
};
const _default = Object.assign(AccessConfigFormFieldsProviderMatrix, {
getInitialValues,
getSchema,
});
export default _default;

View File

@ -4,6 +4,7 @@ import { NOTIFICATION_PROVIDERS, type NotificationProviderType } from "@/domain/
import BizNotifyNodeConfigFieldsProviderDiscordBot from "./BizNotifyNodeConfigFieldsProviderDiscordBot";
import BizNotifyNodeConfigFieldsProviderEmail from "./BizNotifyNodeConfigFieldsProviderEmail";
import BizNotifyNodeConfigFieldsProviderMatrix from "./BizNotifyNodeConfigFieldsProviderMatrix";
import BizNotifyNodeConfigFieldsProviderMattermost from "./BizNotifyNodeConfigFieldsProviderMattermost";
import BizNotifyNodeConfigFieldsProviderSlackBot from "./BizNotifyNodeConfigFieldsProviderSlackBot";
import BizNotifyNodeConfigFieldsProviderTelegramBot from "./BizNotifyNodeConfigFieldsProviderTelegramBot";
@ -16,6 +17,7 @@ const providerComponentMap: Partial<Record<NotificationProviderType, React.Compo
*/
[NOTIFICATION_PROVIDERS.DISCORDBOT]: BizNotifyNodeConfigFieldsProviderDiscordBot,
[NOTIFICATION_PROVIDERS.EMAIL]: BizNotifyNodeConfigFieldsProviderEmail,
[NOTIFICATION_PROVIDERS.MATRIX]: BizNotifyNodeConfigFieldsProviderMatrix,
[NOTIFICATION_PROVIDERS.MATTERMOST]: BizNotifyNodeConfigFieldsProviderMattermost,
[NOTIFICATION_PROVIDERS.SLACKBOT]: BizNotifyNodeConfigFieldsProviderSlackBot,
[NOTIFICATION_PROVIDERS.TELEGRAMBOT]: BizNotifyNodeConfigFieldsProviderTelegramBot,

View File

@ -0,0 +1,49 @@
import type { getI18n } from "react-i18next";
import { useTranslation } from "react-i18next";
import { Form, Input } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import { useFormNestedFieldsContext } from "./_context";
const BizNotifyNodeConfigFieldsProviderMatrix = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const initialValues = getInitialValues();
return (
<>
<Form.Item
name={[parentNamePath, "roomId"]}
label={t("workflow_node.notify.form.matrix_room_id.label")}
extra={t("workflow_node.notify.form.matrix_room_id.help")}
rules={[formRule]}
initialValue={initialValues?.roomId}
>
<Input allowClear placeholder={t("workflow_node.notify.form.matrix_room_id.placeholder")} />
</Form.Item>
</>
);
};
const getInitialValues = (): Nullish<Record<string, unknown>> => {
return {};
};
const getSchema = (_opts?: { i18n?: ReturnType<typeof getI18n> }) => {
return z.object({
roomId: z.string().nullish(),
});
};
const _default = Object.assign(BizNotifyNodeConfigFieldsProviderMatrix, {
getInitialValues,
getSchema,
});
export default _default;

View File

@ -89,6 +89,7 @@ export const ACCESS_PROVIDERS = Object.freeze({
LINODE: "linode",
LITESSL: "litessl",
LOCAL: "local",
MATRIX: "matrix",
MATTERMOST: "mattermost",
MOHUA: "mohua",
NAMECHEAP: "namecheap",
@ -283,6 +284,7 @@ export const accessProvidersMap: Map<AccessProvider["type"] | string, AccessProv
[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.MATRIX, "provider.matrix", "/imgs/providers/matrix.svg", [ACCESS_USAGES.NOTIFICATION]],
[ACCESS_PROVIDERS.MATTERMOST, "provider.mattermost", "/imgs/providers/mattermost.svg", [ACCESS_USAGES.NOTIFICATION]],
] satisfies Array<[AccessProviderType, string, string, AccessUsageType[], "builtin"] | [AccessProviderType, string, string, AccessUsageType[]]>
).map(([type, name, icon, usages, builtin]) => [
@ -943,6 +945,7 @@ export const NOTIFICATION_PROVIDERS = Object.freeze({
DISCORDBOT: `${ACCESS_PROVIDERS.DISCORDBOT}`,
EMAIL: `${ACCESS_PROVIDERS.EMAIL}`,
LARKBOT: `${ACCESS_PROVIDERS.LARKBOT}`,
MATRIX: `${ACCESS_PROVIDERS.MATRIX}`,
MATTERMOST: `${ACCESS_PROVIDERS.MATTERMOST}`,
SLACKBOT: `${ACCESS_PROVIDERS.SLACKBOT}`,
TELEGRAMBOT: `${ACCESS_PROVIDERS.TELEGRAMBOT}`,
@ -969,6 +972,7 @@ export const notificationProvidersMap: Map<NotificationProvider["type"] | string
[NOTIFICATION_PROVIDERS.DISCORDBOT],
[NOTIFICATION_PROVIDERS.SLACKBOT],
[NOTIFICATION_PROVIDERS.TELEGRAMBOT],
[NOTIFICATION_PROVIDERS.MATRIX],
[NOTIFICATION_PROVIDERS.MATTERMOST],
] satisfies Array<[NotificationProviderType]>
).map(([type]) => [

View File

@ -879,6 +879,26 @@
"litessl_eab": {
"guide": "Learn more about using EAB key in LiteSSL: <br><a href=\"https://freessl.cn/automation/eab-manager\" target=\"_blank\">https://freessl.cn/automation/eab-manager</a>"
},
"matrix_server_url": {
"label": "Matrix server URL",
"placeholder": "Please enter Matrix server URL"
},
"matrix_user_id": {
"label": "Matrix user ID",
"placeholder": "Please enter Matrix user ID",
"tooltip": "For more information, see <a href=\"https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/</a>"
},
"matrix_access_token": {
"label": "Matrix access token",
"placeholder": "Please enter Matrix access token",
"tooltip": "For more information, see <a href=\"https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/#your-access-token\" target=\"_blank\">https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/</a>"
},
"matrix_room_id": {
"label": "Matrix room ID (Optional)",
"placeholder": "Please enter the default Matrix room ID",
"help": "Notes: It can be overrided in the workflows. Invite the bot to the room first.",
"tooltip": "For more information, see <a href=\"https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/#advanced\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/</a>"
},
"mattermost_server_url": {
"label": "Mattermost server URL",
"placeholder": "Please enter Mattermost server URL"
@ -895,7 +915,7 @@
"label": "Mattermost channel ID (Optional)",
"placeholder": "Please enter the default Mattermost channel ID",
"help": "Notes: It can be overrided in the workflows.",
"tooltip": "How to get it? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details.” You can directly see the channel ID on the pop-up page."
"tooltip": "How to get it? Select the target channel from the left sidebar, click on the channel name at the top, and choose ”Channel Details. You can directly see the channel ID on the pop-up page."
},
"mohua_username": {
"label": "Mohua Cloud username",

View File

@ -191,6 +191,7 @@
"linode_los": "Linode - Object Storage",
"litessl": "LiteSSL",
"local": "Local host",
"matrix": "Matrix / ElementX",
"mattermost": "Mattermost",
"mohua": "Mohua",
"mohua_mvh": "Mohua - MVH (Virtual Host)",

View File

@ -2784,7 +2784,7 @@
"webhook_data": {
"label": "Webhook data (Optional)",
"placeholder": "Please enter Webhook data",
"help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
"help": "Notes: Leave it blank to use the default Webhook data provided by the selected credential.",
"vartips": "Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${CERTIMATE_DEPLOYER_COMMONNAME}</strong>: <br>The primary domain or IP address of the certificate.</li><li><strong>${CERTIMATE_DEPLOYER_SUBJECTALTNAMES}</strong>: <br>The domains or IP addresses of the certificate, separated by semicolons.</li><li><strong>${CERTIMATE_DEPLOYER_CERTIFICATE}</strong>: <br>The PEM format content of the certificate file.</li><li><strong>${CERTIMATE_DEPLOYER_CERTIFICATE_SERVER}</strong>: <br>The PEM format content of the server certificate file.</li><li><strong>${CERTIMATE_DEPLOYER_CERTIFICATE_INTERMEDIA}</strong>: <br>The PEM format content of the intermediate CA certificate file.</li><li><strong>${CERTIMATE_DEPLOYER_PRIVATEKEY}</strong>: <br>The PEM format content of the private key file.</li></ol>"
},
"webhook_timeout": {
@ -2890,7 +2890,7 @@
"discordbot_channel_id": {
"label": "Discord channel ID (Optional)",
"placeholder": "Please enter Discord channel ID",
"help": "Notes: Leave it blank to use the default channel ID provided by the credential."
"help": "Notes: Leave it blank to use the default channel ID provided by the selected credential."
},
"email_format": {
"label": "Message format (Optional)",
@ -2909,15 +2909,20 @@
"placeholder": "Please enter receiver email address",
"help": "Notes: Leave it blank to use the default receiver email address provided by the selected credential."
},
"matrix_room_id": {
"label": "Matrix room ID (Optional)",
"placeholder": "Please enter Matrix room ID",
"help": "Notes: Leave it blank to use the default room ID provided by the selected credential."
},
"mattermost_channel_id": {
"label": "Mattermost channel ID (Optional)",
"placeholder": "Please enter Mattermost channel ID",
"help": "Notes: Leave it blank to use the default channel ID provided by the credential."
"help": "Notes: Leave it blank to use the default channel ID provided by the selected credential."
},
"slackbot_channel_id": {
"label": "Slack channel ID (Optional)",
"placeholder": "Please enter Slack channel ID",
"help": "Notes: Leave it blank to use the default channel ID provided by the credential."
"help": "Notes: Leave it blank to use the default channel ID provided by the selected credential."
},
"telegrambot_chat_id": {
"label": "Telegram chat ID (Optional)",
@ -2927,7 +2932,7 @@
"webhook_data": {
"label": "Webhook data (Optional)",
"placeholder": "Please enter Webhook data",
"help": "Notes: Leave it blank to use the default Webhook data provided by the credential.",
"help": "Notes: Leave it blank to use the default Webhook data provided by the selected credential.",
"vartips": "Supported variables: <br><ol style=\"list-style: disc;\"><li><strong>${CERTIMATE_NOTIFIER_SUBJECT}</strong>: <br>The subject of notification.</li><li><strong>${CERTIMATE_NOTIFIER_MESSAGE}</strong>: <br>The message of notification.</li></ol>"
},
"webhook_timeout": {

View File

@ -78,6 +78,7 @@
"placeholder": "搜索提供商……"
}
},
"shared_acme_eab_kid": {
"label": "ACME EAB KID",
"placeholder": "请输入 ACME EAB KID"
@ -89,6 +90,7 @@
"shared_allow_insecure_conns": {
"label": "忽略 SSL/TLS 证书验证"
},
"1panel_server_url": {
"label": "1Panel 服务地址",
"placeholder": "请输入 1Panel 服务地址",
@ -877,6 +879,26 @@
"litessl_eab": {
"guide": "点击下方链接了解如何获取 LiteSSL EAB<br><a href=\"https://freessl.cn/automation/eab-manager\" target=\"_blank\">https://freessl.cn/automation/eab-manager</a>"
},
"matrix_server_url": {
"label": "Matrix 服务地址",
"placeholder": "请输入 Matrix 服务地址"
},
"matrix_user_id": {
"label": "Matrix 用户 ID",
"placeholder": "请输入 Matrix 用户 ID",
"tooltip": "这是什么?请参阅 <a href=\"https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-account-management/creating-a-matrix-account/</a>"
},
"matrix_access_token": {
"label": "Matrix Access token",
"placeholder": "请输入 Matrix Access token",
"tooltip": "这是什么?请参阅 <a href=\"https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/#your-access-token\" target=\"_blank\">https://docs.element.io/latest/element-support/element-webdesktop-client-settings/element-settings/</a>"
},
"matrix_room_id": {
"label": "Matrix 房间 ID可选",
"placeholder": "请输入默认的 Matrix 房间 ID",
"help": "提示:可在工作流中覆盖此设置。请先将机器人邀请进房间。",
"tooltip": "这是什么?请参阅 <a href=\"https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/#advanced\" target=\"_blank\">https://docs.element.io/latest/element-support/matrix-rooms/managing-a-room-room-settings/</a>"
},
"mattermost_server_url": {
"label": "Mattermost 服务地址",
"placeholder": "请输入 Mattermost 服务地址"

View File

@ -191,6 +191,7 @@
"linode_los": "Linode - 对象存储",
"litessl": "LiteSSL",
"local": "本地主机",
"matrix": "Matrix / ElementX",
"mattermost": "Mattermost",
"mohua": "嘿华云",
"mohua_mvh": "嘿华云 - 虚拟主机 MVH ",

View File

@ -2873,6 +2873,7 @@
"params_config": {
"label": "参数设置"
},
"discordbot_channel_id": {
"label": "Discord 频道 ID可选",
"placeholder": "请输入 Discord 频道 ID",
@ -2895,6 +2896,11 @@
"placeholder": "请输入收件人邮箱以覆盖默认值",
"help": "提示:不填写时,将使用所选通知渠道授权的默认收件人邮箱。"
},
"matrix_room_id": {
"label": "Matrix 房间 ID可选",
"placeholder": "请输入 Matrix 房间 ID",
"help": "提示:不填写时,将使用所选通知渠道授权的默认房间 ID。"
},
"mattermost_channel_id": {
"label": "Mattermost 频道 ID可选",
"placeholder": "请输入 Mattermost 频道 ID",