Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增普通群outgoing机器人限制逻辑,提升安全性;同时修改allow_groups配置为群ID,群ID获取方法请见配置文件说明 #191

Merged
merged 1 commit into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ $ docker run -itd --name chatgpt -p 8090:8090 \
-e HTTP_PROXY="http://host.docker.internal:15732" \
-e DEFAULT_MODE="单聊" -e MAX_REQUEST=0 -e PORT=8090 \
-e SERVICE_URL="你当前服务外网可访问的URL" -e CHAT_TYPE="0" \
-e ALLOW_GROUPS=a,b -e ALLOW_USERS=a,b -e DENY_USERS=a,b -e VIP_USERS=a,b -e ADMIN_USERS=a,b -e APP_SECRETS="xxx,yyy" \
-e ALLOW_GROUPS=a,b -e ALLOW_OUTGOING_GROUPS=a,b -e ALLOW_USERS=a,b -e DENY_USERS=a,b -e VIP_USERS=a,b -e ADMIN_USERS=a,b -e APP_SECRETS="xxx,yyy" \
-e AZURE_ON="false" -e AZURE_API_VERSION="" -e AZURE_RESOURCE_NAME="" \
-e AZURE_DEPLOYMENT_NAME="" -e AZURE_OPENAI_TOKEN="" \
-e HELP="欢迎使用本工具\n\n你可以查看:[用户指南](https://github.com/eryajf/chatgpt-dingtalk/blob/main/docs/userGuide.md)\n\n这是一个[开源项目](https://github.com/eryajf/chatgpt-dingtalk/)
Expand Down Expand Up @@ -459,10 +459,14 @@ port: "8090"
service_url: "http://chat.eryajf.net"
# 限定对话类型 0:不限 1:只能单聊 2:只能群聊
chat_type: "0"
# 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则写群组的名称,比如 ["aa","bb"]
# 对话聊天时,如下三个满足其一即可通过校验
allow_groups:
- "学无止境"
# 哪些群组可以进行对话(仅在chat_type为0、2时有效),如果留空,则表示允许所有群组,如果要限制,则列表中写群ID(ConversationID)
# 群ID,可在群组中 @机器人 群ID 来查看日志获取,例如日志会输出:[🙋 企业内部机器人 在『测试』群的ConversationID为: "cidrabcdefgh1234567890AAAAA"],获取后可填写该参数并重启程序
allow_groups: []
# 哪些普通群(使用outgoing机器人)可以进行对话,如果留空,则表示允许所有群组,如果要限制,则列表中写群ID(ConversationID)
# 群ID,可在群组中 @机器人 群ID 来查看日志获取,例如日志会输出:[🙋 outgoing机器人 在『测试』群的ConversationID为: "cidrabcdefgh1234567890AAAAA"],获取后可填写该参数并重启程序
# 如果不想支持outgoing机器人功能,这里可以随意设置一个内部群组,例如:cidrabcdefgh1234567890AAAAA;或随意一个字符串,例如:disabled
# 建议该功能默认关闭:除非你必须要用到outgoing机器人
allow_outgoing_groups: ["disabled"]
# 以下 allow_users、deny_users、vip_users、admin_users 配置中填写的是用户的userid,outgoing机器人模式下不适用这些配置
# 比如 ["1301691029702722","1301691029702733"],这个信息需要在钉钉管理后台的通讯录当中获取:https://oa.dingtalk.com/contacts.htm#/contacts
# 哪些用户可以进行对话,如果留空,则表示允许所有用户,如果要限制,则列表中写用户的userid
Expand Down
8 changes: 7 additions & 1 deletion config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@ port: "8090"
service_url: "http://xxxxxx"
# 限定对话类型 0:不限 1:只能单聊 2:只能群聊
chat_type: "0"
# 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则列表中写群组的名称,比如 ["aa","bb"]
# 哪些群组可以进行对话(仅在chat_type为0、2时有效),如果留空,则表示允许所有群组,如果要限制,则列表中写群ID(ConversationID)
# 群ID,可在群组中 @机器人 群ID 来查看日志获取,例如日志会输出:[🙋 企业内部机器人 在『测试』群的ConversationID为: "cidrabcdefgh1234567890AAAAA"],获取后可填写该参数并重启程序
allow_groups: []
# 哪些普通群(使用outgoing机器人)可以进行对话,如果留空,则表示允许所有群组,如果要限制,则列表中写群ID(ConversationID)
# 群ID,可在群组中 @机器人 群ID 来查看日志获取,例如日志会输出:[🙋 outgoing机器人 在『测试』群的ConversationID为: "cidrabcdefgh1234567890AAAAA"],获取后可填写该参数并重启程序
# 如果不想支持outgoing机器人功能,这里可以随意设置一个内部群组,例如:cidrabcdefgh1234567890AAAAA;或随意一个字符串,例如:disabled
# 建议该功能默认关闭:除非你必须要用到outgoing机器人
allow_outgoing_groups: ["disabled"]
# 以下 allow_users、deny_users、vip_users、admin_users 配置中填写的是用户的userid,outgoing机器人模式下不适用这些配置
# 比如 ["1301691029702722","1301691029702733"],这个信息需要在钉钉管理后台的通讯录当中获取:https://oa.dingtalk.com/contacts.htm#/contacts
# 哪些用户可以进行对话,如果留空,则表示允许所有用户,如果要限制,则列表中写用户的userid
Expand Down
12 changes: 9 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ type Configuration struct {
ChatType string `yaml:"chat_type"`
// 哪些群组可以进行对话
AllowGroups []string `yaml:"allow_groups"`
// 哪些outgoing群组可以进行对话
AllowOutgoingGroups []string `yaml:"allow_outgoing_groups"`
// 哪些用户可以进行对话
AllowUsers []string `yaml:"allow_users"`
// 哪些用户不可以进行对话
Expand Down Expand Up @@ -130,9 +132,13 @@ func LoadConfig() *Configuration {
if chatType != "" {
config.ChatType = chatType
}
allowGroup := os.Getenv("ALLOW_GROUPS")
if allowGroup != "" {
config.AllowGroups = strings.Split(allowGroup, ",")
allowGroups := os.Getenv("ALLOW_GROUPS")
if allowGroups != "" {
config.AllowGroups = strings.Split(allowGroups, ",")
}
allowOutgoingGroups := os.Getenv("ALLOW_OUTGOING_GROUPS")
if allowOutgoingGroups != "" {
config.AllowOutgoingGroups = strings.Split(allowOutgoingGroups, ",")
}
allowUsers := os.Getenv("ALLOW_USERS")
if allowUsers != "" {
Expand Down
6 changes: 5 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ services:
PORT: 8090 # 指定服务启动端口,默认为 8090,容器化部署时,不需要调整,一般在二进制宿主机部署时,遇到端口冲突时使用
SERVICE_URL: "" # 指定服务的地址,就是当前服务可供外网访问的地址(或者直接理解为你配置在钉钉回调那里的地址),用于生成图片时给钉钉做渲染
CHAT_TYPE: "0" # 限定对话类型 0:不限 1:只能单聊 2:只能群聊
ALLOW_GROUPS: "" # 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则填写群组的名字,比如 "aa,bb"
ALLOW_GROUPS: "" # 哪些群组可以进行对话(仅在CHAT_TYPE为0、2时有效),如果留空,则表示允许所有群组,如果要限制,则列表中写群ID(ConversationID)
# 群ID,可在群组中 @机器人 群ID 来查看日志获取,例如日志会输出:[🙋 企业内部机器人 在『测试』群的ConversationID为: "cidrabcdefgh1234567890AAAAA"],获取后可填写该参数并重启程序
# 如果不想支持outgoing机器人功能,这里可以随意设置一个内部群组,例如:cidrabcdefgh1234567890AAAAA;或随意一个字符串,例如:disabled
# 建议该功能默认关闭:除非你必须要用到outgoing机器人
ALLOW_OUTGOING_GROUPS: "disabled" # 哪些普通群(使用outgoing机器人)可以进行对话,如果留空,则表示允许所有群组,如果要限制,则列表中写群ID(ConversationID)
# 以下 ALLOW_USERS、DENY_USERS、VIP_USERS、ADMIN_USERS 配置中填写的是用户的userid
# 比如 ["1301691029702722","1301691029702733"],这个信息需要在钉钉管理后台的通讯录当中获取:https://oa.dingtalk.com/contacts.htm#/contacts
# 哪些用户可以进行对话,如果留空,则表示允许所有用户,如果要限制,则列表中写用户的userid
Expand Down
34 changes: 30 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ func Start() {
return ship.ErrBadRequest.New(fmt.Errorf("bind to receivemsg failed : %v", err))
}
// 先校验回调是否合法
if !public.CheckRequest(c.GetReqHeader("timestamp"), c.GetReqHeader("sign")) {
if !public.CheckRequest(c.GetReqHeader("timestamp"), c.GetReqHeader("sign")) && msgObj.SenderStaffId != "" {
logger.Warning("该请求不合法,可能是其他企业或者未经允许的应用调用所致,请知悉!")
return nil
} else if !public.JudgeOutgoingGroup(msgObj.ConversationID) && msgObj.SenderStaffId == "" {
logger.Warning("该请求不合法,可能是未经允许的普通群outgoing机器人调用所致,请知悉!")
return nil
}
// 再校验回调参数是否有价值
if msgObj.Text.Content == "" || msgObj.ChatbotUserID == "" {
Expand All @@ -57,9 +60,33 @@ func Start() {
}
return nil
}

// 查询群ID,发送指令后,可通过查看日志来获取
if msgObj.ConversationType == "2" && msgObj.Text.Content == "群ID" {
if msgObj.RobotCode == "normal" {
logger.Info(fmt.Sprintf("🙋 outgoing机器人 在『%s』群的ConversationID为: %#v", msgObj.ConversationTitle, msgObj.ConversationID))
} else {
logger.Info(fmt.Sprintf("🙋 企业内部机器人 在『%s』群的ConversationID为: %#v", msgObj.ConversationTitle, msgObj.ConversationID))
}
//_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), msgObj.ConversationID)
if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err))
return err
}
return nil
}

// 不在允许群组,不在允许用户(包括在黑名单),满足任一条件,拒绝会话;管理员不受限制
if (!public.JudgeGroup(msgObj.GetChatTitle()) || !public.JudgeUsers(msgObj.SenderStaffId)) && !public.JudgeAdminUsers(msgObj.SenderStaffId) {
logger.Info(fmt.Sprintf("🙋 %s身份信息未被验证通过", msgObj.SenderNick))
if !public.JudgeGroup(msgObj.ConversationID) && !public.JudgeAdminUsers(msgObj.SenderStaffId) && msgObj.SenderStaffId != "" {
logger.Info(fmt.Sprintf("🙋『%s』群组未被验证通过,群ID: %#v,userid:%#v, 昵称: %#v,消息: %#v", msgObj.ConversationTitle, msgObj.ConversationID, msgObj.SenderStaffId, msgObj.SenderNick, msgObj.Text.Content))
_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,该群组未被认证通过,无法使用机器人对话功能。**\n>如需继续使用,请联系管理员申请访问权限。")
if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err))
return err
}
return nil
} else if !public.JudgeUsers(msgObj.SenderStaffId) && !public.JudgeAdminUsers(msgObj.SenderStaffId) && msgObj.SenderStaffId != "" {
logger.Info(fmt.Sprintf("🙋 %s身份信息未被验证通过,userid:%#v,消息: %#v", msgObj.SenderNick, msgObj.SenderStaffId, msgObj.Text.Content))
_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您的身份信息未被认证通过,无法使用机器人对话功能。**\n>如需继续使用,请联系管理员申请访问权限。")
if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err))
Expand Down Expand Up @@ -127,7 +154,6 @@ func Start() {
"status": "ok",
"msg": "欢迎使用钉钉机器人",
})

})
port := ":" + public.Config.Port
srv := &http.Server{
Expand Down
17 changes: 15 additions & 2 deletions public/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func WriteToFile(path string, data []byte) error {
return nil
}

// JudgeGroup 判断群聊名称是否在白名单
// JudgeGroup 判断群ID是否在白名单
func JudgeGroup(s string) bool {
if len(Config.AllowGroups) == 0 {
return true
Expand All @@ -42,7 +42,20 @@ func JudgeGroup(s string) bool {
return false
}

// JudgeUsers 判断用户名称是否在白名单
// JudgeOutgoingGroup 判断群ID是否在为outgoing白名单
func JudgeOutgoingGroup(s string) bool {
if len(Config.AllowOutgoingGroups) == 0 {
return true
}
for _, v := range Config.AllowOutgoingGroups {
if v == s {
return true
}
}
return false
}

// JudgeUsers 判断用户是否在白名单
func JudgeUsers(s string) bool {
// 优先判断黑名单,黑名单用户返回:不在白名单
if len(Config.DenyUsers) != 0 {
Expand Down