diff --git a/config/wk.yaml b/config/wk.yaml index fee035b..7b3f99b 100644 --- a/config/wk.yaml +++ b/config/wk.yaml @@ -111,4 +111,8 @@ conversation: # 最近会话配置 # socketPath: "./wukongimdata/1/wukongim.sock" # 插件unix socket通讯地址 # install: # 默认安装的插件列表 安装最新版本 ${os}: 当前操作系统 ${arch}: 当前操作系统架构。github地址替换https://github.com/WuKongIM/plugins # - "https://gitee.com/WuKongDev/plugins/releases/download/latest/wk.plugin.ai-example-${os}-${arch}.wkp" - # - "https://gitee.com/WuKongDev/plugins/releases/download/latest/wk.plugin.ai-volcengine-${os}-${arch}.wkp" \ No newline at end of file + # - "https://gitee.com/WuKongDev/plugins/releases/download/latest/wk.plugin.ai-volcengine-${os}-${arch}.wkp" + +# customerService: +# visitorsOn: false # 是否开启访客用户访问 +# visitorsSuffix: "____vstr" # 访客用户后缀 (带有此后缀的用户将被认为是访客用户,访客用户登录免去token验证,但是只能给访客频道发送消息) \ No newline at end of file diff --git a/exampleconfig/single.yaml b/exampleconfig/single.yaml index 0570bf9..c681937 100644 --- a/exampleconfig/single.yaml +++ b/exampleconfig/single.yaml @@ -17,6 +17,8 @@ demo: on: true conversation: on: true +webhook: # webhook配置 用于接收消息通知事件,详情请查看文档 + httpAddr: "http://127.0.0.1:8080/webhooks/wukongim" cluster: nodeId: 1 # 节点ID addr: "tcp://127.0.0.1:10001" @@ -36,7 +38,6 @@ jwt: ## jwt认证方式 secret: "xxxxx" # jwt密钥 expire: 30d # jwt过期时间 -plugin: - install: # 默认安装的插件列表 安装最新版本 ${os}: 当前操作系统 ${arch}: 当前操作系统架构。github地址替换https://github.com/WuKongIM/plugins - # - "https://gitee.com/WuKongDev/plugins/releases/download/latest/wk.plugin.ai-example-${os}-${arch}.wkp" - # - "https://gitee.com/WuKongDev/plugins/releases/download/latest/wk.plugin.ai-volcengine-${os}-${arch}.wkp" \ No newline at end of file +customerService: # 客服配置 + visitorsOn: true # 是否开启访客用户访问 + visitorsSuffix: "____vstr" # 访客用户后缀 (带有此后缀的用户将被认为是访客用户,访客用户登录免去token验证,但是只能给访客频道发送消息) \ No newline at end of file diff --git a/internal/channel/handler/event_permission.go b/internal/channel/handler/event_permission.go index 15238d1..849801e 100644 --- a/internal/channel/handler/event_permission.go +++ b/internal/channel/handler/event_permission.go @@ -59,7 +59,7 @@ func (h *Handler) hasPermissionForChannel(channelId string, channelType uint8) ( return wkproto.ReasonSuccess, nil } // 客服频道,直接通过 - if channelType == wkproto.ChannelTypeCustomerService { + if channelType == wkproto.ChannelTypeCustomerService || channelType == wkproto.ChannelTypeVisitors { return wkproto.ReasonSuccess, nil } @@ -112,6 +112,14 @@ func (h *Handler) hasPermissionForSender(channelId string, channelType uint8, e return wkproto.ReasonSuccess, nil } } + // 访客频道只有自己和客服可以发消息 + if channelType == wkproto.ChannelTypeVisitors { + // 因为访客频道的id就是访客的uid,所以这里发送者是自己直接通过 + // 否则需要走普通频道的流程:判断是否是订阅者,白名单,黑名单这些 + if fromUid == channelId { + return wkproto.ReasonSuccess, nil + } + } // 系统发的消息直接通过 if options.G.IsSystemDevice(e.Conn.DeviceId) { diff --git a/internal/options/options.go b/internal/options/options.go index 559630c..776cb52 100644 --- a/internal/options/options.go +++ b/internal/options/options.go @@ -71,6 +71,13 @@ type Options struct { KeyFile string // 私钥文件 } + // 客服相关配置 + CustomerService struct { + VisitorsOn bool // 是否开启访客用户 + VisitorsSuffix string // 访客用户的后缀(带有此后缀的用户将被认为是访客用户,访客用户登录免去token验证,但是只能给访客频道发送消息) + + } + Logger struct { Dir string // 日志存储目录 Level zapcore.Level @@ -349,6 +356,13 @@ func New(op ...Option) *Options { SystemDeviceId: "____device", WhitelistOffOfPerson: true, DeadlockCheck: false, + CustomerService: struct { + VisitorsOn bool + VisitorsSuffix string + }{ + VisitorsOn: false, + VisitorsSuffix: "____vstr", + }, Logger: struct { Dir string Level zapcore.Level @@ -768,6 +782,10 @@ func (o *Options) ConfigureWithViper(vp *viper.Viper) { o.WSSConfig.CertFile = o.getString("wssConfig.certFile", o.WSSConfig.CertFile) o.WSSConfig.KeyFile = o.getString("wssConfig.keyFile", o.WSSConfig.KeyFile) + // 客服 + o.CustomerService.VisitorsOn = o.getBool("customerService.visitorsOn", o.CustomerService.VisitorsOn) + o.CustomerService.VisitorsSuffix = o.getString("customerService.visitorsSuffix", o.CustomerService.VisitorsSuffix) + // 频道 o.Channel.CacheCount = o.getInt("channel.cacheCount", o.Channel.CacheCount) o.Channel.CreateIfNoExist = o.getBool("channel.createIfNoExist", o.Channel.CreateIfNoExist) o.Channel.SubscriberCompressOfCount = o.getInt("channel.subscriberCompressOfCount", o.Channel.SubscriberCompressOfCount) @@ -1248,6 +1266,11 @@ func (o *Options) IsSystemUid(uid string) bool { return uid == o.SystemUID } +// IsVisitors 是否是访客 +func (o *Options) IsVisitors(uid string) bool { + return strings.HasSuffix(uid, o.CustomerService.VisitorsSuffix) +} + func (o *Options) ConfigFileUsed() string { if o.vp == nil { return "" diff --git a/internal/user/handler/event_connect.go b/internal/user/handler/event_connect.go index 69c91d2..a9a08e1 100644 --- a/internal/user/handler/event_connect.go +++ b/internal/user/handler/event_connect.go @@ -74,7 +74,7 @@ func (h *Handler) handleConnect(event *eventbus.Event) (wkproto.ReasonCode, *wkp return wkproto.ReasonAuthFail, nil, nil } devceLevel = wkproto.DeviceLevelSlave // 默认都是slave设备 - } else if options.G.TokenAuthOn { + } else if options.G.TokenAuthOn && !options.G.IsVisitors(uid) { // 如果开启了token验证,并且不是访客用户 if connectPacket.Token == "" { h.Error("token is empty") return wkproto.ReasonAuthFail, nil, errors.New("token is empty")