diff --git a/models/send_ins.go b/models/send_ins.go index 9a0f223..c2cd8f7 100644 --- a/models/send_ins.go +++ b/models/send_ins.go @@ -33,6 +33,10 @@ type InsDtalkConfig struct { type InsQyWeiXinConfig struct { } +type InsWeChatCorpAccountConfig struct { + ToAccount string `json:"to_account" validate:"required,max=200" label:"接收者UserId"` +} + // InsFeishuConfig 实例里面的飞书config type InsFeishuConfig struct { } diff --git a/pkg/constant/constant.go b/pkg/constant/constant.go index 5555566..e886c30 100644 --- a/pkg/constant/constant.go +++ b/pkg/constant/constant.go @@ -59,6 +59,7 @@ const ( MessageTypeEmail = "Email" MessageTypeDtalk = "Dtalk" MessageTypeQyWeiXin = "QyWeiXin" + MessageTypeWeChatCorpAccount = "WeChatCorpAccount" MessageTypeFeishu = "Feishu" MessageTypeCustom = "Custom" MessageTypeWeChatOFAccount = "WeChatOFAccount" diff --git a/pkg/message/wechat_corp_account.go b/pkg/message/wechat_corp_account.go new file mode 100644 index 0000000..e37b834 --- /dev/null +++ b/pkg/message/wechat_corp_account.go @@ -0,0 +1,279 @@ +package message + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "golang.org/x/net/proxy" +) + +type wechatCorpTokenResponse struct { + ErrCode int `json:"errcode"` + ErrMsg string `json:"errmsg"` + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` +} + +type wechatCorpSendResponse struct { + ErrCode int `json:"errcode"` + ErrMsg string `json:"errmsg"` + InvalidUser string `json:"invaliduser,omitempty"` +} + +type wechatCorpSendRequest struct { + ToUser string `json:"touser,omitempty"` + MsgType string `json:"msgtype"` + AgentID int `json:"agentid"` + Safe int `json:"safe,omitempty"` + Text *struct { + Content string `json:"content"` + } `json:"text,omitempty"` + Markdown *struct { + Content string `json:"content"` + } `json:"markdown,omitempty"` + TextCard *struct { + Title string `json:"title"` + Description string `json:"description"` + URL string `json:"url"` + } `json:"textcard,omitempty"` +} + +type wechatCorpTokenCacheItem struct { + token string + expiresAt time.Time +} + +var wechatCorpTokenCache = struct { + mu sync.RWMutex + m map[string]wechatCorpTokenCacheItem +}{ + m: make(map[string]wechatCorpTokenCacheItem), +} + +type WeChatCorpAccount struct { + CorpID string + AgentID int + AgentSecret string + ProxyURL string +} + +func (c *WeChatCorpAccount) cacheKey() string { + return fmt.Sprintf("%s|%d|%s", c.CorpID, c.AgentID, c.AgentSecret) +} + +func (c *WeChatCorpAccount) GetAccessToken() (string, error) { + if c.CorpID == "" || c.AgentSecret == "" { + return "", errors.New("企业微信应用参数缺失") + } + + key := c.cacheKey() + now := time.Now() + + wechatCorpTokenCache.mu.RLock() + item, ok := wechatCorpTokenCache.m[key] + wechatCorpTokenCache.mu.RUnlock() + + if ok && item.token != "" && now.Before(item.expiresAt) { + return item.token, nil + } + + token, expiresAt, err := c.refreshAccessToken() + if err != nil { + return "", err + } + + wechatCorpTokenCache.mu.Lock() + wechatCorpTokenCache.m[key] = wechatCorpTokenCacheItem{token: token, expiresAt: expiresAt} + wechatCorpTokenCache.mu.Unlock() + + return token, nil +} + +func (c *WeChatCorpAccount) refreshAccessToken() (string, time.Time, error) { + client := c.getHTTPClient() + reqURL := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s", + url.QueryEscape(c.CorpID), url.QueryEscape(c.AgentSecret)) + + resp, err := client.Get(reqURL) + if err != nil { + return "", time.Time{}, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", time.Time{}, err + } + + var res wechatCorpTokenResponse + if err := json.Unmarshal(body, &res); err != nil { + return "", time.Time{}, err + } + if res.ErrCode != 0 { + return "", time.Time{}, errors.New(res.ErrMsg) + } + if res.AccessToken == "" || res.ExpiresIn <= 0 { + return "", time.Time{}, errors.New("企业微信 access_token 响应无效") + } + + expiresAt := time.Now().Add(time.Duration(res.ExpiresIn) * time.Second).Add(-60 * time.Second) + return res.AccessToken, expiresAt, nil +} + +func (c *WeChatCorpAccount) SendText(toUser string, content string) (string, error) { + req := wechatCorpSendRequest{ + ToUser: toUser, + MsgType: "text", + AgentID: c.AgentID, + Text: &struct { + Content string `json:"content"` + }{Content: content}, + } + return c.send(req) +} + +func (c *WeChatCorpAccount) SendMarkdown(toUser string, content string) (string, error) { + req := wechatCorpSendRequest{ + ToUser: toUser, + MsgType: "markdown", + AgentID: c.AgentID, + Markdown: &struct { + Content string `json:"content"` + }{Content: content}, + } + return c.send(req) +} + +func (c *WeChatCorpAccount) SendTextCard(toUser, title, description, linkURL string) (string, error) { + req := wechatCorpSendRequest{ + ToUser: toUser, + MsgType: "textcard", + AgentID: c.AgentID, + TextCard: &struct { + Title string `json:"title"` + Description string `json:"description"` + URL string `json:"url"` + }{ + Title: title, + Description: description, + URL: linkURL, + }, + } + return c.send(req) +} + +func (c *WeChatCorpAccount) send(req wechatCorpSendRequest) (string, error) { + if req.ToUser == "" { + return "", errors.New("企业微信应用接收者不能为空") + } + if c.AgentID <= 0 { + return "", errors.New("企业微信应用 AgentID 无效") + } + + token, err := c.GetAccessToken() + if err != nil { + return "", err + } + + b, err := json.Marshal(req) + if err != nil { + return "", err + } + + client := c.getHTTPClient() + apiURL := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s", url.QueryEscape(token)) + resp, err := client.Post(apiURL, "application/json", bytes.NewBuffer(b)) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + var res wechatCorpSendResponse + if err := json.Unmarshal(body, &res); err != nil { + return string(body), err + } + if res.ErrCode != 0 { + if res.InvalidUser != "" { + return string(body), errors.New(fmt.Sprintf("%s (invaliduser=%s)", res.ErrMsg, res.InvalidUser)) + } + return string(body), errors.New(res.ErrMsg) + } + + return string(body), nil +} + +func (c *WeChatCorpAccount) getHTTPClient() *http.Client { + client := &http.Client{ + Timeout: 15 * time.Second, + } + + if c.ProxyURL == "" { + return client + } + + proxyURL, err := url.Parse(c.ProxyURL) + if err != nil { + return client + } + + if strings.HasPrefix(strings.ToLower(c.ProxyURL), "socks5://") { + dialer, err := c.createSOCKS5Dialer(proxyURL) + if err != nil { + return client + } + client.Transport = &http.Transport{ + DialContext: dialer.DialContext, + } + return client + } + + client.Transport = &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + } + return client +} + +func (c *WeChatCorpAccount) createSOCKS5Dialer(proxyURL *url.URL) (proxy.ContextDialer, error) { + host := proxyURL.Host + + var auth *proxy.Auth + if proxyURL.User != nil { + password, _ := proxyURL.User.Password() + auth = &proxy.Auth{ + User: proxyURL.User.Username(), + Password: password, + } + } + + baseDialer := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + } + + dialer, err := proxy.SOCKS5("tcp", host, auth, baseDialer) + if err != nil { + return nil, err + } + + contextDialer, ok := dialer.(proxy.ContextDialer) + if !ok { + return nil, errors.New("failed to convert to ContextDialer") + } + + return contextDialer, nil +} + diff --git a/service/send_ins_service/send_ins.go b/service/send_ins_service/send_ins.go index 5c94079..b23645b 100644 --- a/service/send_ins_service/send_ins.go +++ b/service/send_ins_service/send_ins.go @@ -58,6 +58,24 @@ func (sw *SendTaskInsService) ValidateDiffIns(ins models.SendTasksIns) (string, var Config models.InsQyWeiXinConfig return "", Config } + if ins.WayType == constant.MessageTypeWeChatCorpAccount { + var Config models.InsWeChatCorpAccountConfig + err := json.Unmarshal([]byte(ins.Config), &Config) + if err != nil { + return "企业微信应用发送配置反序列化失败!", empty + } + + var configMap map[string]interface{} + json.Unmarshal([]byte(ins.Config), &configMap) + allowMultiRecip, exists := configMap["allowMultiRecip"].(bool) + + if exists && allowMultiRecip && Config.ToAccount == "" { + return "", Config + } + + _, Msg := app.CommonPlaygroundValid(Config) + return Msg, Config + } if ins.WayType == constant.MessageTypeFeishu { var Config models.InsFeishuConfig return "", Config diff --git a/service/send_message_service/send_message.go b/service/send_message_service/send_message.go index f843a33..402e553 100644 --- a/service/send_message_service/send_message.go +++ b/service/send_message_service/send_message.go @@ -438,6 +438,7 @@ func (sm *SendMessageService) supportsDynamicRecipient(wayType string) bool { supportedTypes := map[string]bool{ constant.MessageTypeEmail: true, constant.MessageTypeWeChatOFAccount: true, + constant.MessageTypeWeChatCorpAccount: true, constant.MessageTypeAliyunSMS: true, // 可以继续添加其他支持动态接收者的渠道 } diff --git a/service/send_message_service/unified/channel_registry.go b/service/send_message_service/unified/channel_registry.go index 5395111..32b89c3 100644 --- a/service/send_message_service/unified/channel_registry.go +++ b/service/send_message_service/unified/channel_registry.go @@ -18,17 +18,18 @@ const ( FormatTypeHTML = channels.FormatTypeHTML FormatTypeMarkdown = channels.FormatTypeMarkdown - MessageTypeEmail = channels.MessageTypeEmail - MessageTypeDtalk = channels.MessageTypeDtalk - MessageTypeQyWeiXin = channels.MessageTypeQyWeiXin - MessageTypeFeishu = channels.MessageTypeFeishu - MessageTypeCustom = channels.MessageTypeCustom - MessageTypeWeChatOFAccount = channels.MessageTypeWeChatOFAccount - MessageTypeMessageNest = channels.MessageTypeMessageNest - MessageTypeAliyunSMS = channels.MessageTypeAliyunSMS - MessageTypeTelegram = channels.MessageTypeTelegram - MessageTypeBark = channels.MessageTypeBark - MessageTypePushMe = channels.MessageTypePushMe + MessageTypeEmail = channels.MessageTypeEmail + MessageTypeDtalk = channels.MessageTypeDtalk + MessageTypeQyWeiXin = channels.MessageTypeQyWeiXin + MessageTypeWeChatCorpAccount = channels.MessageTypeWeChatCorpAccount + MessageTypeFeishu = channels.MessageTypeFeishu + MessageTypeCustom = channels.MessageTypeCustom + MessageTypeWeChatOFAccount = channels.MessageTypeWeChatOFAccount + MessageTypeMessageNest = channels.MessageTypeMessageNest + MessageTypeAliyunSMS = channels.MessageTypeAliyunSMS + MessageTypeTelegram = channels.MessageTypeTelegram + MessageTypeBark = channels.MessageTypeBark + MessageTypePushMe = channels.MessageTypePushMe ) // ChannelRegistry 渠道注册表 @@ -97,6 +98,7 @@ func GetGlobalChannelRegistry() *ChannelRegistry { globalChannelRegistry.Register(channels.NewEmailChannel()) globalChannelRegistry.Register(channels.NewDtalkChannel()) globalChannelRegistry.Register(channels.NewQyWeiXinChannel()) + globalChannelRegistry.Register(channels.NewWeChatCorpAccountChannel()) globalChannelRegistry.Register(channels.NewFeishuChannel()) globalChannelRegistry.Register(channels.NewCustomChannel()) globalChannelRegistry.Register(channels.NewWeChatOFAccountChannel()) diff --git a/service/send_message_service/unified/channels/types.go b/service/send_message_service/unified/channels/types.go index d8d037a..ae842a2 100644 --- a/service/send_message_service/unified/channels/types.go +++ b/service/send_message_service/unified/channels/types.go @@ -13,6 +13,7 @@ const ( MessageTypeEmail = constant.MessageTypeEmail MessageTypeDtalk = constant.MessageTypeDtalk MessageTypeQyWeiXin = constant.MessageTypeQyWeiXin + MessageTypeWeChatCorpAccount = constant.MessageTypeWeChatCorpAccount MessageTypeFeishu = constant.MessageTypeFeishu MessageTypeCustom = constant.MessageTypeCustom MessageTypeWeChatOFAccount = constant.MessageTypeWeChatOFAccount diff --git a/service/send_message_service/unified/channels/wechat_corp_account.go b/service/send_message_service/unified/channels/wechat_corp_account.go new file mode 100644 index 0000000..86ed10b --- /dev/null +++ b/service/send_message_service/unified/channels/wechat_corp_account.go @@ -0,0 +1,76 @@ +package channels + +import ( + "fmt" + "message-nest/models" + "message-nest/pkg/message" + "message-nest/service/send_ins_service" + "message-nest/service/send_way_service" + "strings" +) + +type WeChatCorpAccountChannel struct{ *BaseChannel } + +func NewWeChatCorpAccountChannel() *WeChatCorpAccountChannel { + return &WeChatCorpAccountChannel{BaseChannel: NewBaseChannel(MessageTypeWeChatCorpAccount, []string{FormatTypeMarkdown, FormatTypeText})} +} + +func (c *WeChatCorpAccountChannel) SendUnified(msgObj interface{}, ins models.SendTasksIns, content *UnifiedMessageContent) (string, string) { + auth, ok := msgObj.(*send_way_service.WayDetailWeChatCorpAccount) + if !ok { + return "", "类型转换失败" + } + + insService := send_ins_service.SendTaskInsService{} + errStr, configInterface := insService.ValidateDiffIns(ins) + if errStr != "" { + return errStr, "" + } + config, ok := configInterface.(models.InsWeChatCorpAccountConfig) + if !ok { + return "企业微信应用config校验失败", "" + } + + contentType, formattedContent, err := c.FormatContent(content) + if err != nil { + return "", err.Error() + } + + toUser := config.ToAccount + if content.IsAtAll() { + toUser = "@all" + } else { + atUserIds := content.GetAtUserIds() + if len(atUserIds) > 0 { + toUser = strings.Join(atUserIds, "|") + } + } + + cli := message.WeChatCorpAccount{ + CorpID: auth.CorpID, + AgentID: auth.AgentID, + AgentSecret: auth.AgentSecret, + ProxyURL: auth.ProxyURL, + } + + var res string + var sendErr error + if contentType == FormatTypeMarkdown { + res, sendErr = cli.SendMarkdown(toUser, formattedContent) + } else if contentType == FormatTypeText { + if content.Title != "" && content.URL != "" { + res, sendErr = cli.SendTextCard(toUser, content.Title, formattedContent, content.URL) + } else { + res, sendErr = cli.SendText(toUser, formattedContent) + } + } else { + sendErr = fmt.Errorf("未知的企业微信应用发送内容类型:%s", contentType) + } + + var errMsg string + if sendErr != nil { + errMsg = fmt.Sprintf("发送失败:%s", sendErr.Error()) + } + return res, errMsg +} + diff --git a/service/send_way_service/send_way.go b/service/send_way_service/send_way.go index c5207b1..7bcc682 100644 --- a/service/send_way_service/send_way.go +++ b/service/send_way_service/send_way.go @@ -37,30 +37,32 @@ type WayTester interface { // 渠道注册表 var ( validatorRegistry = map[string]func() WayValidator{ - constant.MessageTypeEmail: func() WayValidator { return &WayDetailEmail{} }, - constant.MessageTypeDtalk: func() WayValidator { return &WayDetailDTalk{} }, - constant.MessageTypeQyWeiXin: func() WayValidator { return &WayDetailQyWeiXin{} }, - constant.MessageTypeFeishu: func() WayValidator { return &WayDetailFeishu{} }, - constant.MessageTypeCustom: func() WayValidator { return &WayDetailCustom{} }, - constant.MessageTypeWeChatOFAccount: func() WayValidator { return &WeChatOFAccount{} }, - constant.MessageTypeMessageNest: func() WayValidator { return &MessageNest{} }, - constant.MessageTypeAliyunSMS: func() WayValidator { return &WayDetailAliyunSMS{} }, - constant.MessageTypeTelegram: func() WayValidator { return &WayDetailTelegram{} }, - constant.MessageTypeBark: func() WayValidator { return &WayDetailBark{} }, - constant.MessageTypePushMe: func() WayValidator { return &WayDetailPushMe{} }, + constant.MessageTypeEmail: func() WayValidator { return &WayDetailEmail{} }, + constant.MessageTypeDtalk: func() WayValidator { return &WayDetailDTalk{} }, + constant.MessageTypeQyWeiXin: func() WayValidator { return &WayDetailQyWeiXin{} }, + constant.MessageTypeWeChatCorpAccount: func() WayValidator { return &WayDetailWeChatCorpAccount{} }, + constant.MessageTypeFeishu: func() WayValidator { return &WayDetailFeishu{} }, + constant.MessageTypeCustom: func() WayValidator { return &WayDetailCustom{} }, + constant.MessageTypeWeChatOFAccount: func() WayValidator { return &WeChatOFAccount{} }, + constant.MessageTypeMessageNest: func() WayValidator { return &MessageNest{} }, + constant.MessageTypeAliyunSMS: func() WayValidator { return &WayDetailAliyunSMS{} }, + constant.MessageTypeTelegram: func() WayValidator { return &WayDetailTelegram{} }, + constant.MessageTypeBark: func() WayValidator { return &WayDetailBark{} }, + constant.MessageTypePushMe: func() WayValidator { return &WayDetailPushMe{} }, } testerRegistry = map[string]func(interface{}) WayTester{ - constant.MessageTypeEmail: func(m interface{}) WayTester { return m.(*WayDetailEmail) }, - constant.MessageTypeDtalk: func(m interface{}) WayTester { return m.(*WayDetailDTalk) }, - constant.MessageTypeQyWeiXin: func(m interface{}) WayTester { return m.(*WayDetailQyWeiXin) }, - constant.MessageTypeFeishu: func(m interface{}) WayTester { return m.(*WayDetailFeishu) }, - constant.MessageTypeCustom: func(m interface{}) WayTester { return m.(*WayDetailCustom) }, - constant.MessageTypeWeChatOFAccount: func(m interface{}) WayTester { return m.(*WeChatOFAccount) }, - constant.MessageTypeMessageNest: func(m interface{}) WayTester { return m.(*MessageNest) }, - constant.MessageTypeAliyunSMS: func(m interface{}) WayTester { return m.(*WayDetailAliyunSMS) }, - constant.MessageTypeTelegram: func(m interface{}) WayTester { return m.(*WayDetailTelegram) }, - constant.MessageTypeBark: func(m interface{}) WayTester { return m.(*WayDetailBark) }, - constant.MessageTypePushMe: func(m interface{}) WayTester { return m.(*WayDetailPushMe) }, + constant.MessageTypeEmail: func(m interface{}) WayTester { return m.(*WayDetailEmail) }, + constant.MessageTypeDtalk: func(m interface{}) WayTester { return m.(*WayDetailDTalk) }, + constant.MessageTypeQyWeiXin: func(m interface{}) WayTester { return m.(*WayDetailQyWeiXin) }, + constant.MessageTypeWeChatCorpAccount: func(m interface{}) WayTester { return m.(*WayDetailWeChatCorpAccount) }, + constant.MessageTypeFeishu: func(m interface{}) WayTester { return m.(*WayDetailFeishu) }, + constant.MessageTypeCustom: func(m interface{}) WayTester { return m.(*WayDetailCustom) }, + constant.MessageTypeWeChatOFAccount: func(m interface{}) WayTester { return m.(*WeChatOFAccount) }, + constant.MessageTypeMessageNest: func(m interface{}) WayTester { return m.(*MessageNest) }, + constant.MessageTypeAliyunSMS: func(m interface{}) WayTester { return m.(*WayDetailAliyunSMS) }, + constant.MessageTypeTelegram: func(m interface{}) WayTester { return m.(*WayDetailTelegram) }, + constant.MessageTypeBark: func(m interface{}) WayTester { return m.(*WayDetailBark) }, + constant.MessageTypePushMe: func(m interface{}) WayTester { return m.(*WayDetailPushMe) }, } ) @@ -147,6 +149,37 @@ func (w *WayDetailQyWeiXin) Test() (string, string) { return "", string(res) } +type WayDetailWeChatCorpAccount struct { + CorpID string `json:"corp_id" validate:"required,max=100" label:"企业微信CorpID"` + AgentID int `json:"agent_id" validate:"required,min=1" label:"企业微信AgentID"` + AgentSecret string `json:"agent_secret" validate:"required,max=200" label:"企业微信AgentSecret"` + ProxyURL string `json:"proxy_url" validate:"max=200" label:"代理地址"` +} + +func (w *WayDetailWeChatCorpAccount) Validate(authJson string) (string, interface{}) { + var empty interface{} + err := json.Unmarshal([]byte(authJson), w) + if err != nil { + return "企业微信应用auth反序列化失败!", empty + } + _, msg := app.CommonPlaygroundValid(*w) + return msg, w +} + +func (w *WayDetailWeChatCorpAccount) Test() (string, string) { + cli := message.WeChatCorpAccount{ + CorpID: w.CorpID, + AgentID: w.AgentID, + AgentSecret: w.AgentSecret, + ProxyURL: w.ProxyURL, + } + _, err := cli.GetAccessToken() + if err != nil { + return fmt.Sprintf("连接失败:%s", err.Error()), "" + } + return "", "access_token获取成功" +} + // WayDetailFeishu 飞书渠道明细字段 type WayDetailFeishu struct { AccessToken string `json:"access_token" validate:"required,max=100" label:"飞书access_token"` diff --git a/web/src/components/pages/sendWays/WaysForm.vue b/web/src/components/pages/sendWays/WaysForm.vue index c99df78..812feef 100644 --- a/web/src/components/pages/sendWays/WaysForm.vue +++ b/web/src/components/pages/sendWays/WaysForm.vue @@ -207,6 +207,9 @@ const getFinalData = () => { if (config.type == 'Email' && input.col == 'port') { authData[input.col] = parseInt(formData.value[input.col]) } + if (config.type == 'WeChatCorpAccount' && input.col == 'agent_id') { + authData[input.col] = parseInt(formData.value[input.col]) + } } }) } @@ -259,6 +262,7 @@ const getChannelIcon = (type: string) => { 'Email': Mail, 'Dtalk': MessageSquare, 'QyWeiXin': MessageCircle, + 'WeChatCorpAccount': MessageCircle, 'Feishu': Send, 'Custom': Webhook, 'WeChatOFAccount': MessageCircleCode, @@ -435,4 +439,4 @@ import { defineComponent } from 'vue' export default defineComponent({ name: 'WaysForm' }) - \ No newline at end of file + diff --git a/web/src/constant.js b/web/src/constant.js index 52c5937..cb3a879 100644 --- a/web/src/constant.js +++ b/web/src/constant.js @@ -76,6 +76,34 @@ const CONSTANT = { taskInsInputs: [ ], }, + { + type: 'WeChatCorpAccount', + label: '企业微信应用', + dynamicRecipient: { + support: true, + field: 'to_account', + label: '接收者UserId', + desc: 'UserId', + }, + inputs: [ + { subLabel: 'corp_id', value: '', col: 'corp_id', desc: "企业微信企业ID(CorpID)" }, + { subLabel: 'agent_id', value: '', col: 'agent_id', desc: "企业微信应用AgentId(数字)" }, + { subLabel: 'agent_secret', value: '', col: 'agent_secret', desc: "企业微信应用Secret" }, + { subLabel: 'proxy_url', value: '', col: 'proxy_url', desc: "可选:代理地址,支持 http://、https://、socks5://" }, + { subLabel: '渠道名', value: '', col: 'name', desc: "想要设置的渠道名字" }, + ], + tips: { + text: "企业微信应用说明", + desc: "通过企业微信应用消息接口发送消息,需要配置 CorpID / AgentId / Secret。可选 proxy_url 用于 http/https/socks5 代理。" + }, + taskInsRadios: [ + { subLabel: 'text', content: 'text' }, + { subLabel: 'markdown', content: 'markdown' }, + ], + taskInsInputs: [ + { value: '', col: 'to_account', desc: '接收者UserId', label: '接收者UserId' }, + ], + }, { type: 'Feishu', label: '飞书机器人', @@ -267,4 +295,4 @@ CONSTANT.WAYS_DATA.forEach(element => { CONSTANT.WAYS_DATA_MAP[element.type] = element }); -export { CONSTANT } \ No newline at end of file +export { CONSTANT }