feat: 新增黑名单用户及VIP用户处理逻辑,同时修改ALLOW_USERS的校验为userid(SenderStaffId),将用户相关校验统一为userid (#190)

This commit is contained in:
Finly
2023-04-09 21:02:33 +08:00
committed by GitHub
parent 40d324ccb6
commit 4a29f1ef8f
8 changed files with 90 additions and 25 deletions

View File

@@ -216,7 +216,7 @@ $ docker run -itd --name chatgpt -p 8090:8090 \
-e HTTP_PROXY="http://host.docker.internal:15732" \ -e HTTP_PROXY="http://host.docker.internal:15732" \
-e DEFAULT_MODE="单聊" -e MAX_REQUEST=0 -e PORT=8090 \ -e DEFAULT_MODE="单聊" -e MAX_REQUEST=0 -e PORT=8090 \
-e SERVICE_URL="你当前服务外网可访问的URL" -e CHAT_TYPE="0" \ -e SERVICE_URL="你当前服务外网可访问的URL" -e CHAT_TYPE="0" \
-e ALLOW_GROUPS=a,b -e ALLOW_USERS=a,b -e ADMIN_USERS=a,b -e APP_SECRETS="xxx,yyy" \ -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 AZURE_ON="false" -e AZURE_API_VERSION="" -e AZURE_RESOURCE_NAME="" \ -e AZURE_ON="false" -e AZURE_API_VERSION="" -e AZURE_RESOURCE_NAME="" \
-e AZURE_DEPLOYMENT_NAME="" -e AZURE_OPENAI_TOKEN="" \ -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/) -e HELP="欢迎使用本工具\n\n你可以查看[用户指南](https://github.com/eryajf/chatgpt-dingtalk/blob/main/docs/userGuide.md)\n\n这是一个[开源项目](https://github.com/eryajf/chatgpt-dingtalk/)
@@ -227,7 +227,7 @@ $ docker run -itd --name chatgpt -p 8090:8090 \
> 运行命令中映射的配置文件参考下边的[配置文件说明](#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E)。 > 运行命令中映射的配置文件参考下边的[配置文件说明](#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E8%AF%B4%E6%98%8E)。
- `📢 注意:`如果使用docker部署那么PORT参数不需要进行任何调整。 - `📢 注意:`如果使用docker部署那么PORT参数不需要进行任何调整。
- `📢 注意:`ALLOW_GROUPS,ALLOW_USERS,ADMIN_USERS三个参数为数组,如果需要指定多个,可用英文逗号分割。 - `📢 注意:`ALLOW_GROUPS,ALLOW_USERS,DENY_USERS,VIP_USERS,ADMIN_USERS 参数为数组,如果需要指定多个,可用英文逗号分割。outgoing机器人模式下这些参数无效。
- `📢 注意:`如果服务器节点本身就在国外或者自定义了`BASE_URL`,那么就把`HTTP_PROXY`参数留空即可。 - `📢 注意:`如果服务器节点本身就在国外或者自定义了`BASE_URL`,那么就把`HTTP_PROXY`参数留空即可。
- `📢 注意:`如果使用docker部署那么proxy地址可以直接使用如上方式部署`host.docker.internal`会指向容器所在宿主机的IP只需要更改端口为你的代理端口即可。参见[Docker容器如何优雅地访问宿主机网络](https://wiki.eryajf.net/pages/674f53/) - `📢 注意:`如果使用docker部署那么proxy地址可以直接使用如上方式部署`host.docker.internal`会指向容器所在宿主机的IP只需要更改端口为你的代理端口即可。参见[Docker容器如何优雅地访问宿主机网络](https://wiki.eryajf.net/pages/674f53/)
@@ -463,10 +463,16 @@ chat_type: "0"
# 对话聊天时,如下三个满足其一即可通过校验 # 对话聊天时,如下三个满足其一即可通过校验
allow_groups: allow_groups:
- "学无止境" - "学无止境"
# 哪些用户可以进行对话,如果留空,则表示允许所有用户,如果要限制,则列表中写用户的名称,比如 ["张三","李四"] # 以下 allow_users、deny_users、vip_users、admin_users 配置中填写的是用户的useridoutgoing机器人模式下不适用这些配置
allow_users: ["张三","李四"]
# 指定哪些人为此系统的管理员如果留空则表示没有人是管理员如果要限制则列表中写用户的userid
# 比如 ["1301691029702722","1301691029702733"]这个信息需要在钉钉管理后台的通讯录当中获取https://oa.dingtalk.com/contacts.htm#/contacts # 比如 ["1301691029702722","1301691029702733"]这个信息需要在钉钉管理后台的通讯录当中获取https://oa.dingtalk.com/contacts.htm#/contacts
# 哪些用户可以进行对话如果留空则表示允许所有用户如果要限制则列表中写用户的userid
allow_users: []
# 哪些用户不可以进行对话如果留空则表示允许所有用户如allow_user有配置需满足相应条件如果要限制则列表中写用户的userid黑名单优先级高于白名单
deny_users: []
# 哪些用户可以进行无限对话如果留空则表示只允许管理员如max_request配置为0则允许所有人
# 如果要针对指定VIP用户放开限制如max_request配置不为0则列表中写用户的userid
vip_users: []
# 指定哪些人为此系统的管理员如果留空则表示没有人是管理员如果要限制则列表中写用户的userid
# 注意如果下边的app_secrets为空以及使用outgoing的方式配置机器人这两种情况下都表示没有人是管理员 # 注意如果下边的app_secrets为空以及使用outgoing的方式配置机器人这两种情况下都表示没有人是管理员
admin_users: [] admin_users: []
# 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果留空将会忽略校验则该接口将会存在其他人也能随意调用的安全隐患因此强烈建议配置正确的secret如果你的服务对接给多个机器人这里可以配置多个机器人的secret # 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果留空将会忽略校验则该接口将会存在其他人也能随意调用的安全隐患因此强烈建议配置正确的secret如果你的服务对接给多个机器人这里可以配置多个机器人的secret

View File

@@ -22,10 +22,16 @@ service_url: "http://xxxxxx"
chat_type: "0" chat_type: "0"
# 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则列表中写群组的名称,比如 ["aa","bb"] # 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则列表中写群组的名称,比如 ["aa","bb"]
allow_groups: [] allow_groups: []
# 哪些用户可以进行对话,如果留空,则表示允许所有用户,如果要限制,则列表中写用户的名称,比如 ["张三","李四"] # 以下 allow_users、deny_users、vip_users、admin_users 配置中填写的是用户的useridoutgoing机器人模式下不适用这些配置
allow_users: []
# 指定哪些人为此系统的管理员如果留空则表示没有人是管理员如果要限制则列表中写用户的userid
# 比如 ["1301691029702722","1301691029702733"]这个信息需要在钉钉管理后台的通讯录当中获取https://oa.dingtalk.com/contacts.htm#/contacts # 比如 ["1301691029702722","1301691029702733"]这个信息需要在钉钉管理后台的通讯录当中获取https://oa.dingtalk.com/contacts.htm#/contacts
# 哪些用户可以进行对话如果留空则表示允许所有用户如果要限制则列表中写用户的userid
allow_users: []
# 哪些用户不可以进行对话如果留空则表示允许所有用户如allow_user有配置需满足相应条件如果要限制则列表中写用户的userid黑名单优先级高于白名单
deny_users: []
# 哪些用户可以进行无限对话如果留空则表示只允许管理员如max_request配置为0则允许所有人
# 如果要针对指定VIP用户放开限制如max_request配置不为0则列表中写用户的userid
vip_users: []
# 指定哪些人为此系统的管理员如果留空则表示没有人是管理员如果要限制则列表中写用户的userid
# 注意如果下边的app_secrets为空以及使用outgoing的方式配置机器人这两种情况下都表示没有人是管理员 # 注意如果下边的app_secrets为空以及使用outgoing的方式配置机器人这两种情况下都表示没有人是管理员
admin_users: [] admin_users: []
# 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果留空将会忽略校验则该接口将会存在其他人也能随意调用的安全隐患因此强烈建议配置正确的secret如果你的服务对接给多个机器人这里可以配置多个机器人的secret # 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果留空将会忽略校验则该接口将会存在其他人也能随意调用的安全隐患因此强烈建议配置正确的secret如果你的服务对接给多个机器人这里可以配置多个机器人的secret

View File

@@ -42,6 +42,10 @@ type Configuration struct {
AllowGroups []string `yaml:"allow_groups"` AllowGroups []string `yaml:"allow_groups"`
// 哪些用户可以进行对话 // 哪些用户可以进行对话
AllowUsers []string `yaml:"allow_users"` AllowUsers []string `yaml:"allow_users"`
// 哪些用户不可以进行对话
DenyUsers []string `yaml:"deny_users"`
// 哪些Vip用户可以进行无限对话
VipUsers []string `yaml:"vip_users"`
// 指定哪些人为此系统的管理员,必须指定,否则所有人都是 // 指定哪些人为此系统的管理员,必须指定,否则所有人都是
AdminUsers []string `yaml:"admin_users"` AdminUsers []string `yaml:"admin_users"`
// 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果你的服务对接给多个机器人这里可以配置多个机器人的secret // 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果你的服务对接给多个机器人这里可以配置多个机器人的secret
@@ -134,6 +138,14 @@ func LoadConfig() *Configuration {
if allowUsers != "" { if allowUsers != "" {
config.AllowUsers = strings.Split(allowUsers, ",") config.AllowUsers = strings.Split(allowUsers, ",")
} }
denyUsers := os.Getenv("DENY_USERS")
if denyUsers != "" {
config.DenyUsers = strings.Split(denyUsers, ",")
}
vipUsers := os.Getenv("VIP_USERS")
if vipUsers != "" {
config.VipUsers = strings.Split(vipUsers, ",")
}
adminUsers := os.Getenv("ADMIN_USERS") adminUsers := os.Getenv("ADMIN_USERS")
if adminUsers != "" { if adminUsers != "" {
config.AdminUsers = strings.Split(adminUsers, ",") config.AdminUsers = strings.Split(adminUsers, ",")

View File

@@ -18,11 +18,14 @@ services:
SERVICE_URL: "" # 指定服务的地址,就是当前服务可供外网访问的地址(或者直接理解为你配置在钉钉回调那里的地址),用于生成图片时给钉钉做渲染 SERVICE_URL: "" # 指定服务的地址,就是当前服务可供外网访问的地址(或者直接理解为你配置在钉钉回调那里的地址),用于生成图片时给钉钉做渲染
CHAT_TYPE: "0" # 限定对话类型 0不限 1只能单聊 2只能群聊 CHAT_TYPE: "0" # 限定对话类型 0不限 1只能单聊 2只能群聊
ALLOW_GROUPS: "" # 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则填写群组的名字,比如 "aa,bb" ALLOW_GROUPS: "" # 哪些群组可以进行对话,如果留空,则表示允许所有群组,如果要限制,则填写群组的名字,比如 "aa,bb"
ALLOW_USERS: "" # 哪些用户可以进行对话,如果留空,则表示允许所有用户,如果要限制,则填写用户的名字,比如 "张三,李四" # 以下 ALLOW_USERS、DENY_USERS、VIP_USERS、ADMIN_USERS 配置中填写的是用户的userid
# 指定哪些人为此系统的管理员如果留空则表示没有人是管理员如果要限制则列表中写用户的userid # 比如 ["1301691029702722","1301691029702733"]这个信息需要在钉钉管理后台的通讯录当中获取https://oa.dingtalk.com/contacts.htm#/contacts
# 比如 "1301691029702722,1301691029702733"这个信息需要在钉钉管理后台的通讯录当中获取https://oa.dingtalk.com/contacts.htm#/contacts # 哪些用户可以进行对话如果留空则表示允许所有用户如果要限制则列表中写用户的userid
ALLOW_USERS: "" # 哪些用户可以进行对话如果留空则表示允许所有用户如果要限制则填写用户的userid
DENY_USERS: "" # 哪些用户不可以进行对话如果留空则表示允许所有用户如allow_user有配置需满足相应条件如果要限制则列表中写用户的userid黑名单优先级高于白名单
VIP_USERS: "" # 哪些用户可以进行无限对话如果留空则表示只允许管理员如max_request配置为0则允许所有人如果要针对指定VIP用户放开限制如max_request配置不为0则列表中写用户的userid
ADMIN_USERS: "" # 指定哪些人为此系统的管理员如果留空则表示没有人是管理员如果要限制则列表中写用户的userid
# 注意如果下边的app_secrets为空以及使用outgoing的方式配置机器人这两种情况下都表示没有人是管理员 # 注意如果下边的app_secrets为空以及使用outgoing的方式配置机器人这两种情况下都表示没有人是管理员
ADMIN_USERS: ""
APP_SECRETS: "" # 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果留空将会忽略校验则该接口将会存在其他人也能随意调用的安全隐患因此强烈建议配置正确的secret如果你的服务对接给多个机器人这里可以配置多个机器人的secret比如 "xxxx,yyyy" APP_SECRETS: "" # 钉钉机器人在应用信息中的AppSecret为了校验回调的请求是否合法如果留空将会忽略校验则该接口将会存在其他人也能随意调用的安全隐患因此强烈建议配置正确的secret如果你的服务对接给多个机器人这里可以配置多个机器人的secret比如 "xxxx,yyyy"
AZURE_ON: "false" # 是否走Azure OpenAi API, 默认false ,如果为true则需要配置下边的四个参数 AZURE_ON: "false" # 是否走Azure OpenAi API, 默认false ,如果为true则需要配置下边的四个参数
AZURE_API_VERSION: "" # Azure OpenAi API 版本,比如 "2023-03-15-preview" AZURE_API_VERSION: "" # Azure OpenAi API 版本,比如 "2023-03-15-preview"

View File

@@ -49,15 +49,18 @@ func Start() {
logger.Debug(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj)) logger.Debug(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj))
if public.Config.ChatType != "0" && msgObj.ConversationType != public.Config.ChatType { if public.Config.ChatType != "0" && msgObj.ConversationType != public.Config.ChatType {
_, err = msgObj.ReplyToDingtalk(string(dingbot.TEXT), "抱歉,管理员禁用了这种聊天方式,请选择其他聊天方式与机器人对话!") logger.Info(fmt.Sprintf("🙋 %s使用了禁用的聊天方式", msgObj.SenderNick))
_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,管理员禁用了这种聊天方式,请选择其他聊天方式与机器人对话!**")
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err
} }
return nil return nil
} }
if !public.JudgeGroup(msgObj.GetChatTitle()) && !public.JudgeUsers(msgObj.SenderNick) && !public.JudgeAdminUsers(msgObj.SenderStaffId) { // 不在允许群组,不在允许用户(包括在黑名单),满足任一条件,拒绝会话;管理员不受限制
_, err = msgObj.ReplyToDingtalk(string(dingbot.TEXT), "抱歉,您不在该机器人对话功能的白名单当中!") if (!public.JudgeGroup(msgObj.GetChatTitle()) || !public.JudgeUsers(msgObj.SenderStaffId)) && !public.JudgeAdminUsers(msgObj.SenderStaffId) {
logger.Info(fmt.Sprintf("🙋 %s身份信息未被验证通过", msgObj.SenderNick))
_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您的身份信息未被认证通过,无法使用机器人对话功能。**\n>如需继续使用,请联系管理员申请访问权限。")
if err != nil { if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err)) logger.Warning(fmt.Errorf("send message error: %v", err))
return err return err

View File

@@ -225,14 +225,19 @@ func CheckRequestTimes(rmsg *dingbot.ReceiveMsg) bool {
return true return true
} }
count := public.UserService.GetUseRequestCount(rmsg.GetSenderIdentifier()) count := public.UserService.GetUseRequestCount(rmsg.GetSenderIdentifier())
// 判断访问次数是否超过限制 // 用户是管理员或VIP用户判断访问次数是否超过限制
if count >= public.Config.MaxRequest { if public.JudgeAdminUsers(rmsg.SenderStaffId) || public.JudgeVipUsers(rmsg.SenderStaffId) {
logger.Info(fmt.Sprintf("亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick)) return true
_, err := rmsg.ReplyToDingtalk(string(dingbot.TEXT), fmt.Sprintf("一个好的问题,胜过十个好的答案!\n亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick)) } else {
if err != nil { // 用户不是管理员和VIP用户判断访问次数是否超过限制
logger.Warning(fmt.Errorf("send message error: %v", err)) if count >= public.Config.MaxRequest {
logger.Info(fmt.Sprintf("亲爱的: %s您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解!", rmsg.SenderNick))
_, err := rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), fmt.Sprintf("[Staple] **一个好的问题,胜过十个好的答案!** \n\n亲爱的%s:\n\n您今日请求次数已达上限请明天再来交互发问资源有限请务必斟酌您的问题给您带来不便敬请谅解\n\n如有需要可联系管理员升级为VIP用户。", rmsg.SenderNick))
if err != nil {
logger.Warning(fmt.Errorf("send message error: %v", err))
}
return false
} }
return false
} }
// 访问次数未超过限制将计数加1 // 访问次数未超过限制将计数加1
public.UserService.SetUseRequestCount(rmsg.GetSenderIdentifier(), count+1) public.UserService.SetUseRequestCount(rmsg.GetSenderIdentifier(), count+1)

View File

@@ -35,9 +35,6 @@
- title: "#linux命令" - title: "#linux命令"
prefix: "我希望你只用 linux 命令回复。不要写解释。我想:" prefix: "我希望你只用 linux 命令回复。不要写解释。我想:"
suffix: "" suffix: ""
- title: "#Linux命令"
prefix: "我希望你只用 Linux 命令回复。不要写解释。我想:"
suffix: ""
- title: "#英语学术润色" - title: "#英语学术润色"
prefix: "Below is a paragraph from an academic paper. Polish the writing to meet the academic style, improve the spelling, grammar, clarity, concision and overall readability. When neccessary, rewrite the whole sentence. Furthermore, list all modification and explain the reasons to do so in markdown table.\n" prefix: "Below is a paragraph from an academic paper. Polish the writing to meet the academic style, improve the spelling, grammar, clarity, concision and overall readability. When neccessary, rewrite the whole sentence. Furthermore, list all modification and explain the reasons to do so in markdown table.\n"
suffix: "" suffix: ""

View File

@@ -44,6 +44,15 @@ func JudgeGroup(s string) bool {
// JudgeUsers 判断用户名称是否在白名单 // JudgeUsers 判断用户名称是否在白名单
func JudgeUsers(s string) bool { func JudgeUsers(s string) bool {
// 优先判断黑名单,黑名单用户返回:不在白名单
if len(Config.DenyUsers) != 0 {
for _, v := range Config.DenyUsers {
if v == s {
return false
}
}
}
// 白名单配置逻辑处理
if len(Config.AllowUsers) == 0 { if len(Config.AllowUsers) == 0 {
return true return true
} }
@@ -73,6 +82,30 @@ func JudgeAdminUsers(s string) bool {
return false return false
} }
// JudgeVipUsers 判断用户是否为VIP用户
func JudgeVipUsers(s string) bool {
// 如果secret或者用户的userid都为空的话那么默认不是VIP用户
if len(Config.AppSecrets) == 0 || s == "" {
return false
}
// 管理员默认是VIP用户
for _, v := range Config.AdminUsers {
if v == s {
return true
}
}
// 如果没有指定则没有人是VIP用户
if len(Config.VipUsers) == 0 {
return false
}
for _, v := range Config.VipUsers {
if v == s {
return true
}
}
return false
}
func GetReadTime(t time.Time) string { func GetReadTime(t time.Time) string {
return t.Format("2006-01-02 15:04:05") return t.Format("2006-01-02 15:04:05")
} }