feat: support group message now (close #46)
This commit is contained in:
@@ -57,6 +57,7 @@ _✨ 搭建专属于你的消息推送服务,支持多种消息推送方式,
|
|||||||
+ WebSocket 客户端([官方客户端](https://github.com/songquanpeng/personal-assistant),[接入文档](./docs/API.md#websocket-客户端)),
|
+ WebSocket 客户端([官方客户端](https://github.com/songquanpeng/personal-assistant),[接入文档](./docs/API.md#websocket-客户端)),
|
||||||
+ Telegram 机器人,
|
+ Telegram 机器人,
|
||||||
+ Discord 群机器人,
|
+ Discord 群机器人,
|
||||||
|
+ 群组消息,可以将多个推送通道组合成一个群组,然后向群组发送消息,可以实现一次性推送到多个渠道的功能。
|
||||||
2. 多种用户登录注册方式:
|
2. 多种用户登录注册方式:
|
||||||
+ 邮箱登录注册以及通过邮箱进行密码重置。
|
+ 邮箱登录注册以及通过邮箱进行密码重置。
|
||||||
+ [GitHub 开放授权](https://github.com/settings/applications/new)。
|
+ [GitHub 开放授权](https://github.com/settings/applications/new)。
|
||||||
@@ -172,7 +173,8 @@ proxy_send_timeout 300s;
|
|||||||
9. `telegram`:通过 Telegram 机器人进行推送(`description` 或 `content` 字段二选一,支持 Markdown 的子集)。
|
9. `telegram`:通过 Telegram 机器人进行推送(`description` 或 `content` 字段二选一,支持 Markdown 的子集)。
|
||||||
10. `discord`:通过 Discord 群机器人进行推送(注意事项同上)。
|
10. `discord`:通过 Discord 群机器人进行推送(注意事项同上)。
|
||||||
11. `one_api`:通过 OneAPI 协议推送消息到 QQ。
|
11. `one_api`:通过 OneAPI 协议推送消息到 QQ。
|
||||||
12. `none`:仅保存到数据库,不做推送。
|
12. `group`:通过预先配置的消息推送通道群组进行推送。
|
||||||
|
13. `none`:仅保存到数据库,不做推送。
|
||||||
5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
|
5. `token`:如果你在后台设置了推送 token,则此项必填。另外可以通过设置 HTTP `Authorization` 头部设置此项。
|
||||||
6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
|
6. `url`:选填,如果不填则系统自动为消息生成 URL,其内容为消息详情。
|
||||||
7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。
|
7. `to`:选填,推送给指定用户,如果不填则默认推送给自己,受限于具体的消息推送方式,有些推送方式不支持此项。
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package channel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"message-pusher/model"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SendGroupMessage(message *model.Message, user *model.User, channel_ *model.Channel) error {
|
||||||
|
subChannels := strings.Split(channel_.AppId, "|")
|
||||||
|
subTargets := strings.Split(channel_.AccountId, "|")
|
||||||
|
if len(subChannels) != len(subTargets) {
|
||||||
|
return errors.New("无效的群组消息配置,子通道数量与子目标数量不一致")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(subChannels); i++ {
|
||||||
|
message.To = subTargets[i]
|
||||||
|
message.Channel = subChannels[i]
|
||||||
|
subChannel, err := model.GetChannelByName(subChannels[i], user.Id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("获取群组消息子通道失败:" + err.Error())
|
||||||
|
}
|
||||||
|
if subChannel.Type == model.TypeGroup {
|
||||||
|
return errors.New("群组消息子通道不能是群组消息")
|
||||||
|
}
|
||||||
|
err = SendMessage(message, user, subChannel)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("发送群组消息失败:" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ func SendMessage(message *model.Message, user *model.User, channel_ *model.Chann
|
|||||||
return nil
|
return nil
|
||||||
case model.TypeOneBot:
|
case model.TypeOneBot:
|
||||||
return SendOneBotMessage(message, user, channel_)
|
return SendOneBotMessage(message, user, channel_)
|
||||||
|
case model.TypeGroup:
|
||||||
|
return SendGroupMessage(message, user, channel_)
|
||||||
default:
|
default:
|
||||||
return errors.New("不支持的消息通道:" + channel_.Type)
|
return errors.New("不支持的消息通道:" + channel_.Type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const (
|
|||||||
TypeClient = "client"
|
TypeClient = "client"
|
||||||
TypeNone = "none"
|
TypeNone = "none"
|
||||||
TypeOneBot = "one_bot"
|
TypeOneBot = "one_bot"
|
||||||
|
TypeGroup = "group"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
|
|||||||
@@ -33,7 +33,13 @@ export const CHANNEL_OPTIONS = [
|
|||||||
key: 'one_bot',
|
key: 'one_bot',
|
||||||
text: 'OneBot 协议',
|
text: 'OneBot 协议',
|
||||||
value: 'one_bot',
|
value: 'one_bot',
|
||||||
color: '#616cff',
|
color: '#76FF03',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'group',
|
||||||
|
text: '群组消息',
|
||||||
|
value: 'group',
|
||||||
|
color: '#FF9800',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'none',
|
key: 'none',
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const EditChannel = () => {
|
|||||||
url: '',
|
url: '',
|
||||||
other: '',
|
other: '',
|
||||||
corp_id: '', // only for corp_app
|
corp_id: '', // only for corp_app
|
||||||
agent_id: '', // only for corp_app
|
agent_id: '' // only for corp_app
|
||||||
};
|
};
|
||||||
|
|
||||||
const [inputs, setInputs] = useState(originInputs);
|
const [inputs, setInputs] = useState(originInputs);
|
||||||
@@ -70,11 +70,22 @@ const EditChannel = () => {
|
|||||||
localInputs.url = localInputs.url.slice(0, -1);
|
localInputs.url = localInputs.url.slice(0, -1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'group':
|
||||||
|
let channels = localInputs.app_id.split('|');
|
||||||
|
let targets = localInputs.account_id.split('|');
|
||||||
|
if (localInputs.account_id === '') {
|
||||||
|
for (let i = 0; i < channels.length - 1; i++) {
|
||||||
|
localInputs.account_id += '|';
|
||||||
|
}
|
||||||
|
} else if (channels.length !== targets.length) {
|
||||||
|
showError('群组通道的子通道数量与目标数量不匹配,对于不需要指定的目标请直接留空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
res = await API.put(`/api/channel/`, {
|
res = await API.put(`/api/channel/`, {
|
||||||
...localInputs,
|
...localInputs,
|
||||||
id: parseInt(channelId),
|
id: parseInt(channelId)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res = await API.post(`/api/channel`, localInputs);
|
res = await API.post(`/api/channel`, localInputs);
|
||||||
@@ -247,9 +258,9 @@ const EditChannel = () => {
|
|||||||
{
|
{
|
||||||
key: 'plugin',
|
key: 'plugin',
|
||||||
text: '微信中的企业微信插件',
|
text: '微信中的企业微信插件',
|
||||||
value: 'plugin',
|
value: 'plugin'
|
||||||
},
|
},
|
||||||
{ key: 'app', text: '企业微信 APP', value: 'app' },
|
{ key: 'app', text: '企业微信 APP', value: 'app' }
|
||||||
]}
|
]}
|
||||||
value={inputs.other}
|
value={inputs.other}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
@@ -465,10 +476,11 @@ const EditChannel = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Message>
|
<Message>
|
||||||
通过 OneBot 协议进行推送,可以使用 <a href='https://github.com/Mrs4s/go-cqhttp' target='_blank'>cqhttp</a> 等实现。
|
通过 OneBot 协议进行推送,可以使用 <a href='https://github.com/Mrs4s/go-cqhttp'
|
||||||
|
target='_blank'>cqhttp</a> 等实现。
|
||||||
利用 OneBot 协议可以实现推送 QQ 消息。
|
利用 OneBot 协议可以实现推送 QQ 消息。
|
||||||
</Message>
|
</Message>
|
||||||
<Form.Group widths={2}>
|
<Form.Group widths={3}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='服务器地址'
|
label='服务器地址'
|
||||||
name='url'
|
name='url'
|
||||||
@@ -497,6 +509,35 @@ const EditChannel = () => {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
case 'group':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Message>
|
||||||
|
对渠道进行分组,然后在推送时选择分组进行推送,可以实现一次性推送到多个渠道的功能。
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
推送目标如若不填,则使用子渠道的默认推送目标。如果填写,请务必全部按顺序填写,对于不需要指定的直接留空即可,例如 <code>123456789||@wechat</code>,两个连续的分隔符表示跳过该渠道。
|
||||||
|
</Message>
|
||||||
|
<Form.Group widths={2}>
|
||||||
|
<Form.Input
|
||||||
|
label='渠道列表'
|
||||||
|
name='app_id'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.app_id}
|
||||||
|
placeholder='在此填写渠道列表,使用 | 分割,例如 bark|telegram|wechat'
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
label='默认推送目标'
|
||||||
|
name='account_id'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.account_id}
|
||||||
|
placeholder='在此填写默认推送目标,使用 | 分割,例如 123456789|@wechat|@wechat'
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
</>
|
||||||
|
);
|
||||||
case 'none':
|
case 'none':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user