From c49f9def30fda40a6495539e9de8dff2cf9020e1 Mon Sep 17 00:00:00 2001 From: cosmo Date: Tue, 7 May 2024 18:51:04 +0800 Subject: [PATCH 1/6] beta399 --- protobufc2c/c2cpic.pb.go | 370 --------------------------------------- 1 file changed, 370 deletions(-) delete mode 100644 protobufc2c/c2cpic.pb.go diff --git a/protobufc2c/c2cpic.pb.go b/protobufc2c/c2cpic.pb.go deleted file mode 100644 index 9665e465..00000000 --- a/protobufc2c/c2cpic.pb.go +++ /dev/null @@ -1,370 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc v4.25.3 -// source: c2cpic.proto - -package protobufc2c - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// 定义一个Proto消息结构来解析特定的嵌套数据 -type ImageData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // 实例化Level1消息 - NestedData *ImageData_Level1 `protobuf:"bytes,1,opt,name=nestedData,proto3" json:"nestedData,omitempty"` -} - -func (x *ImageData) Reset() { - *x = ImageData{} - if protoimpl.UnsafeEnabled { - mi := &file_c2cpic_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ImageData) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImageData) ProtoMessage() {} - -func (x *ImageData) ProtoReflect() protoreflect.Message { - mi := &file_c2cpic_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImageData.ProtoReflect.Descriptor instead. -func (*ImageData) Descriptor() ([]byte, []int) { - return file_c2cpic_proto_rawDescGZIP(), []int{0} -} - -func (x *ImageData) GetNestedData() *ImageData_Level1 { - if x != nil { - return x.NestedData - } - return nil -} - -// 内嵌消息定义为一级字段1的结构 -type ImageData_Level1 struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // 实例化Level3消息 - Details *ImageData_Level1_Level3 `protobuf:"bytes,3,opt,name=details,proto3" json:"details,omitempty"` -} - -func (x *ImageData_Level1) Reset() { - *x = ImageData_Level1{} - if protoimpl.UnsafeEnabled { - mi := &file_c2cpic_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ImageData_Level1) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImageData_Level1) ProtoMessage() {} - -func (x *ImageData_Level1) ProtoReflect() protoreflect.Message { - mi := &file_c2cpic_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImageData_Level1.ProtoReflect.Descriptor instead. -func (*ImageData_Level1) Descriptor() ([]byte, []int) { - return file_c2cpic_proto_rawDescGZIP(), []int{0, 0} -} - -func (x *ImageData_Level1) GetDetails() *ImageData_Level1_Level3 { - if x != nil { - return x.Details - } - return nil -} - -// 内嵌消息定义为二级字段3的结构 -type ImageData_Level1_Level3 struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // 可选字段,解析图片宽度 - Width int32 `protobuf:"varint,8,opt,name=width,proto3" json:"width,omitempty"` - // 可选字段,解析图片高度 - Height int32 `protobuf:"varint,9,opt,name=height,proto3" json:"height,omitempty"` - // 实例化Level29消息 - Urls *ImageData_Level1_Level3_Level29 `protobuf:"bytes,29,opt,name=urls,proto3" json:"urls,omitempty"` -} - -func (x *ImageData_Level1_Level3) Reset() { - *x = ImageData_Level1_Level3{} - if protoimpl.UnsafeEnabled { - mi := &file_c2cpic_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ImageData_Level1_Level3) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImageData_Level1_Level3) ProtoMessage() {} - -func (x *ImageData_Level1_Level3) ProtoReflect() protoreflect.Message { - mi := &file_c2cpic_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImageData_Level1_Level3.ProtoReflect.Descriptor instead. -func (*ImageData_Level1_Level3) Descriptor() ([]byte, []int) { - return file_c2cpic_proto_rawDescGZIP(), []int{0, 0, 0} -} - -func (x *ImageData_Level1_Level3) GetWidth() int32 { - if x != nil { - return x.Width - } - return 0 -} - -func (x *ImageData_Level1_Level3) GetHeight() int32 { - if x != nil { - return x.Height - } - return 0 -} - -func (x *ImageData_Level1_Level3) GetUrls() *ImageData_Level1_Level3_Level29 { - if x != nil { - return x.Urls - } - return nil -} - -// 内嵌消息定义为三级字段29的结构 -type ImageData_Level1_Level3_Level29 struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // 可选字段,解析图片URL - Url string `protobuf:"bytes,30,opt,name=url,proto3" json:"url,omitempty"` -} - -func (x *ImageData_Level1_Level3_Level29) Reset() { - *x = ImageData_Level1_Level3_Level29{} - if protoimpl.UnsafeEnabled { - mi := &file_c2cpic_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ImageData_Level1_Level3_Level29) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImageData_Level1_Level3_Level29) ProtoMessage() {} - -func (x *ImageData_Level1_Level3_Level29) ProtoReflect() protoreflect.Message { - mi := &file_c2cpic_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImageData_Level1_Level3_Level29.ProtoReflect.Descriptor instead. -func (*ImageData_Level1_Level3_Level29) Descriptor() ([]byte, []int) { - return file_c2cpic_proto_rawDescGZIP(), []int{0, 0, 0, 0} -} - -func (x *ImageData_Level1_Level3_Level29) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -var File_c2cpic_proto protoreflect.FileDescriptor - -var file_c2cpic_proto_rawDesc = []byte{ - 0x0a, 0x0c, 0x63, 0x32, 0x63, 0x70, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x63, 0x32, 0x63, 0x22, 0xad, 0x02, 0x0a, 0x09, - 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, 0x3d, 0x0a, 0x0a, 0x6e, 0x65, 0x73, - 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x63, 0x32, 0x63, 0x2e, 0x49, 0x6d, 0x61, 0x67, - 0x65, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x52, 0x0a, 0x6e, 0x65, - 0x73, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x1a, 0xe0, 0x01, 0x0a, 0x06, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x31, 0x12, 0x3e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x63, - 0x32, 0x63, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x4c, 0x65, 0x76, - 0x65, 0x6c, 0x31, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x33, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x1a, 0x95, 0x01, 0x0a, 0x06, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x33, 0x12, 0x14, - 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x40, 0x0a, 0x04, - 0x75, 0x72, 0x6c, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x63, 0x32, 0x63, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x61, - 0x74, 0x61, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x31, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x33, - 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x32, 0x39, 0x52, 0x04, 0x75, 0x72, 0x6c, 0x73, 0x1a, 0x1b, - 0x0a, 0x07, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x32, 0x39, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, - 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x42, 0x30, 0x5a, 0x2e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x6f, 0x73, 0x68, 0x69, 0x6e, - 0x6f, 0x6e, 0x79, 0x61, 0x72, 0x75, 0x6b, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x73, 0x6f, 0x6b, 0x79, - 0x6f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x63, 0x32, 0x63, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_c2cpic_proto_rawDescOnce sync.Once - file_c2cpic_proto_rawDescData = file_c2cpic_proto_rawDesc -) - -func file_c2cpic_proto_rawDescGZIP() []byte { - file_c2cpic_proto_rawDescOnce.Do(func() { - file_c2cpic_proto_rawDescData = protoimpl.X.CompressGZIP(file_c2cpic_proto_rawDescData) - }) - return file_c2cpic_proto_rawDescData -} - -var file_c2cpic_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_c2cpic_proto_goTypes = []interface{}{ - (*ImageData)(nil), // 0: protobufc2c.ImageData - (*ImageData_Level1)(nil), // 1: protobufc2c.ImageData.Level1 - (*ImageData_Level1_Level3)(nil), // 2: protobufc2c.ImageData.Level1.Level3 - (*ImageData_Level1_Level3_Level29)(nil), // 3: protobufc2c.ImageData.Level1.Level3.Level29 -} -var file_c2cpic_proto_depIdxs = []int32{ - 1, // 0: protobufc2c.ImageData.nestedData:type_name -> protobufc2c.ImageData.Level1 - 2, // 1: protobufc2c.ImageData.Level1.details:type_name -> protobufc2c.ImageData.Level1.Level3 - 3, // 2: protobufc2c.ImageData.Level1.Level3.urls:type_name -> protobufc2c.ImageData.Level1.Level3.Level29 - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name -} - -func init() { file_c2cpic_proto_init() } -func file_c2cpic_proto_init() { - if File_c2cpic_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_c2cpic_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ImageData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_c2cpic_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ImageData_Level1); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_c2cpic_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ImageData_Level1_Level3); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_c2cpic_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ImageData_Level1_Level3_Level29); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_c2cpic_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_c2cpic_proto_goTypes, - DependencyIndexes: file_c2cpic_proto_depIdxs, - MessageInfos: file_c2cpic_proto_msgTypes, - }.Build() - File_c2cpic_proto = out.File - file_c2cpic_proto_rawDesc = nil - file_c2cpic_proto_goTypes = nil - file_c2cpic_proto_depIdxs = nil -} From 9aa4f7815559918e25ac0bb84a46085bcd15e629 Mon Sep 17 00:00:00 2001 From: cosmo Date: Thu, 9 May 2024 15:57:37 +0800 Subject: [PATCH 2/6] beta400 --- Processor/ProcessGroupAddBot.go | 4 +- Processor/ProcessGroupMsgReceive.go | 232 +++++++++++++++++++++++++++ Processor/ProcessGroupMsgReject.go | 233 ++++++++++++++++++++++++++++ Processor/ProcessInlineSearch.go | 60 ++++--- Processor/Processor.go | 24 +++ botgo/dto/forum.go | 3 +- botgo/dto/interaction.go | 1 + botgo/dto/message.go | 15 ++ botgo/dto/websocket_event.go | 2 + botgo/event/event.go | 52 ++++++- botgo/event/register.go | 20 ++- config/config.go | 32 ++++ handlers/send_msg.go | 5 +- main.go | 22 ++- structs/structs.go | 15 +- template/config_template.go | 5 +- 16 files changed, 681 insertions(+), 44 deletions(-) create mode 100644 Processor/ProcessGroupMsgReceive.go create mode 100644 Processor/ProcessGroupMsgReject.go diff --git a/Processor/ProcessGroupAddBot.go b/Processor/ProcessGroupAddBot.go index 96d96a87..973af06e 100644 --- a/Processor/ProcessGroupAddBot.go +++ b/Processor/ProcessGroupAddBot.go @@ -135,9 +135,9 @@ func (p *Processors) ProcessGroupAddBot(data *dto.GroupAddBotEvent) error { AppIDString := strconv.FormatUint(p.Settings.AppID, 10) // 储存和群号相关的eventid - echo.AddEvnetID(AppIDString, GroupID64, data.ID) + echo.AddEvnetID(AppIDString, GroupID64, data.EventID) - mylog.Printf("Bot被[%v]邀请进入群[%v]eventid[%v]", userid64, GroupID64, data.ID) + mylog.Printf("Bot被[%v]邀请进入群[%v]eventid[%v]", userid64, GroupID64, data.EventID) // 调用GetSelfIntroduce函数 intros := config.GetSelfIntroduce() diff --git a/Processor/ProcessGroupMsgReceive.go b/Processor/ProcessGroupMsgReceive.go new file mode 100644 index 00000000..b1c6425c --- /dev/null +++ b/Processor/ProcessGroupMsgReceive.go @@ -0,0 +1,232 @@ +// 处理收到的回调事件 +package Processor + +import ( + "fmt" + "strconv" + "time" + + "github.com/hoshinonyaruko/gensokyo/config" + "github.com/hoshinonyaruko/gensokyo/echo" + "github.com/hoshinonyaruko/gensokyo/handlers" + "github.com/hoshinonyaruko/gensokyo/idmap" + "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/tencent-connect/botgo/dto" + "github.com/tencent-connect/botgo/websocket/client" +) + +// ProcessInlineSearch 处理内联查询 +func (p *Processors) ProcessGroupMsgRecive(data *dto.GroupMsgReceiveEvent) error { + // 转换appid + var userid64 int64 + var GroupID64 int64 + var LongGroupID64 int64 + var err error + var fromgid, fromuid string + if data.GroupOpenID != "" { + fromgid = data.GroupOpenID + fromuid = data.OpMemberOpenID + } + + // 获取s + s := client.GetGlobalS() + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) + + if config.GetIdmapPro() { + //将真实id转为int userid64 + GroupID64, userid64, err = idmap.StoreIDv2Pro(fromgid, fromuid) + if err != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + + // 当哈希碰撞 因为获取时候是用的非idmap的get函数 + LongGroupID64, _ = idmap.StoreIDv2(fromgid) + _, _ = idmap.StoreIDv2(fromuid) + if !config.GetHashIDValue() { + mylog.Fatalf("避坑日志:你开启了高级id转换,请设置hash_id为true,并且删除idmaps并重启") + } + } else { + // 映射str的GroupID到int + GroupID64, err = idmap.StoreIDv2(fromgid) + if err != nil { + mylog.Errorf("failed to convert ChannelID to int: %v", err) + return nil + } + // 映射str的userid到int + userid64, err = idmap.StoreIDv2(fromuid) + if err != nil { + mylog.Printf("Error storing ID: %v", err) + return nil + } + } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + + if !config.GetGlobalGroupMsgRejectReciveEventToMessage() { + notice := &OnebotGroupReceiveNotice{ + GroupID: GroupID64, + NoticeType: "interaction", + PostType: "notice", + SelfID: selfid64, + SubType: "create", + Time: time.Now().Unix(), + UserID: userid64, + Data: data, + } + //调试 + PrintStructWithFieldNames(notice) + + // Convert OnebotGroupMessage to map and send + noticeMap := structToMap(notice) + + //上报信息到onebotv11应用端(正反ws) + p.BroadcastMessageToAll(noticeMap) + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和群号相关的eventid + // idmap-pro的设计其实是有问题的,和idmap冲突,并且也还是会哈希碰撞 需要用一个不会碰撞的id去存 + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) + } else { + if data.GroupOpenID != "" { + //群回调 + newdata := ConvertReceiveToMessage(data) + //mylog.Printf("回调测试111-newdata:%v\n", newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = newdata.Content + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } + + var IsBindedUserId, IsBindedGroupId bool + if config.GetHashIDValue() { + IsBindedUserId = idmap.CheckValue(data.OpMemberOpenID, userid64) + IsBindedGroupId = idmap.CheckValue(data.GroupOpenID, GroupID64) + } else { + IsBindedUserId = idmap.CheckValuev2(userid64) + IsBindedGroupId = idmap.CheckValuev2(GroupID64) + } + //平台事件,不是真实信息,无需messageID + messageID64 := 123 + messageID := int(messageID64) + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + //mylog.Printf("回调测试-interaction:%v\n", segmentedMessages) + groupMsg := OnebotGroupMessage{ + RawMessage: newdata.Content, + Message: segmentedMessages, + MessageID: messageID, + GroupID: GroupID64, + MessageType: "group", + PostType: "message", + SelfID: selfid64, + UserID: userid64, + Sender: Sender{ + UserID: userid64, + Sex: "0", + Age: 0, + Area: "0", + Level: "0", + }, + SubType: "normal", + Time: time.Now().Unix(), + } + //增强配置 + if !config.GetNativeOb11() { + groupMsg.RealMessageType = "interaction" + groupMsg.IsBindedUserId = IsBindedUserId + groupMsg.IsBindedGroupId = IsBindedGroupId + groupMsg.RealGroupID = data.GroupOpenID + groupMsg.RealUserID = data.OpMemberOpenID + groupMsg.Avatar, _ = GenerateAvatarURLV2(data.OpMemberOpenID) + } + //根据条件判断是否增加nick和card + var CaN = config.GetCardAndNick() + if CaN != "" { + groupMsg.Sender.Nickname = CaN + groupMsg.Sender.Card = CaN + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, newdata.Content) + } + // 获取MasterID数组 + masterIDs := config.GetMasterID() + + // 判断userid64是否在masterIDs数组里 + isMaster := false + for _, id := range masterIDs { + if strconv.FormatInt(userid64, 10) == id { + isMaster = true + break + } + } + + // 根据isMaster的值为groupMsg的Sender赋值role字段 + if isMaster { + groupMsg.Sender.Role = "owner" + } else { + groupMsg.Sender.Role = "member" + } + + // 映射消息类型 + echo.AddMsgType(AppIDString, s, "group") + + //储存当前群或频道号的类型 + idmap.WriteConfigv2(fmt.Sprint(GroupID64), "type", "group") + + //映射类型 + echo.AddMsgType(AppIDString, GroupID64, "group") + + // 调试 + PrintStructWithFieldNames(groupMsg) + + // Convert OnebotGroupMessage to map and send + groupMsgMap := structToMap(groupMsg) + //上报信息到onebotv11应用端(正反ws) + p.BroadcastMessageToAll(groupMsgMap) + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和群号相关的eventid + fmt.Printf("测试:储存eventid:[%v]LongGroupID64[%v]\n", data.EventID, LongGroupID64) + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) + } + } + + return nil +} + +// ConvertReceiveToMessage 转换 Receive 到 Message +func ConvertReceiveToMessage(r *dto.GroupMsgReceiveEvent) *dto.Message { + var message dto.Message + + // 直接映射的字段 + message.GroupID = r.GroupOpenID + + // 特殊处理的字段 + message.Content = config.GetGlobalGroupMsgReceiveMessage() + message.DirectMessage = false + + return &message +} diff --git a/Processor/ProcessGroupMsgReject.go b/Processor/ProcessGroupMsgReject.go new file mode 100644 index 00000000..e222152c --- /dev/null +++ b/Processor/ProcessGroupMsgReject.go @@ -0,0 +1,233 @@ +// 处理收到的回调事件 +package Processor + +import ( + "fmt" + "strconv" + "time" + + "github.com/hoshinonyaruko/gensokyo/config" + "github.com/hoshinonyaruko/gensokyo/echo" + "github.com/hoshinonyaruko/gensokyo/handlers" + "github.com/hoshinonyaruko/gensokyo/idmap" + "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/tencent-connect/botgo/dto" + "github.com/tencent-connect/botgo/websocket/client" +) + +// ProcessGroupMsgReject 处理群关闭机器人推送 +func (p *Processors) ProcessGroupMsgReject(data *dto.GroupMsgRejectEvent) error { + // 转换appid + var userid64 int64 + var GroupID64 int64 + var LongGroupID64 int64 + var err error + var fromgid, fromuid string + if data.GroupOpenID != "" { + fromgid = data.GroupOpenID + fromuid = data.OpMemberOpenID + } + + // 获取s + s := client.GetGlobalS() + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 获取当前时间的13位毫秒级时间戳 + currentTimeMillis := time.Now().UnixNano() / 1e6 + + // 构造echostr,包括AppID,原始的s变量和当前时间戳 + echostr := fmt.Sprintf("%s_%d_%d", AppIDString, s, currentTimeMillis) + + fmt.Printf("快乐测试[%v][%v]\n", fromgid, fromuid) + + if config.GetIdmapPro() { + //将真实id转为int userid64 + GroupID64, userid64, err = idmap.StoreIDv2Pro(fromgid, fromuid) + if err != nil { + mylog.Fatalf("Error storing ID: %v", err) + } + // 当哈希碰撞 因为获取时候是用的非idmap的get函数 + LongGroupID64, _ = idmap.StoreIDv2(fromgid) + _, _ = idmap.StoreIDv2(fromuid) + if !config.GetHashIDValue() { + mylog.Fatalf("避坑日志:你开启了高级id转换,请设置hash_id为true,并且删除idmaps并重启") + } + } else { + // 映射str的GroupID到int + GroupID64, err = idmap.StoreIDv2(fromgid) + if err != nil { + mylog.Errorf("failed to convert ChannelID to int: %v", err) + return nil + } + // 映射str的userid到int + userid64, err = idmap.StoreIDv2(fromuid) + if err != nil { + mylog.Printf("Error storing ID: %v", err) + return nil + } + } + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + + if !config.GetGlobalGroupMsgRejectReciveEventToMessage() { + notice := &OnebotGroupRejectNotice{ + GroupID: GroupID64, + NoticeType: "interaction", + PostType: "notice", + SelfID: selfid64, + SubType: "create", + Time: time.Now().Unix(), + UserID: userid64, + Data: data, + } + //调试 + PrintStructWithFieldNames(notice) + + // Convert OnebotGroupMessage to map and send + noticeMap := structToMap(notice) + + //上报信息到onebotv11应用端(正反ws) + p.BroadcastMessageToAll(noticeMap) + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和群号相关的eventid + // idmap-pro的设计其实是有问题的,和idmap冲突,并且也还是会哈希碰撞 需要用一个不会碰撞的id去存 + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) + } else { + if data.GroupOpenID != "" { + //群回调 + newdata := ConvertRejectToMessage(data) + //mylog.Printf("回调测试111-newdata:%v\n", newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = newdata.Content + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } + + var IsBindedUserId, IsBindedGroupId bool + if config.GetHashIDValue() { + IsBindedUserId = idmap.CheckValue(data.OpMemberOpenID, userid64) + IsBindedGroupId = idmap.CheckValue(data.GroupOpenID, GroupID64) + } else { + IsBindedUserId = idmap.CheckValuev2(userid64) + IsBindedGroupId = idmap.CheckValuev2(GroupID64) + } + //平台事件,不是真实信息,无需messageID + messageID64 := 123 + messageID := int(messageID64) + var selfid64 int64 + if config.GetUseUin() { + selfid64 = config.GetUinint64() + } else { + selfid64 = int64(p.Settings.AppID) + } + //mylog.Printf("回调测试-interaction:%v\n", segmentedMessages) + groupMsg := OnebotGroupMessage{ + RawMessage: newdata.Content, + Message: segmentedMessages, + MessageID: messageID, + GroupID: GroupID64, + MessageType: "group", + PostType: "message", + SelfID: selfid64, + UserID: userid64, + Sender: Sender{ + UserID: userid64, + Sex: "0", + Age: 0, + Area: "0", + Level: "0", + }, + SubType: "normal", + Time: time.Now().Unix(), + } + //增强配置 + if !config.GetNativeOb11() { + groupMsg.RealMessageType = "interaction" + groupMsg.IsBindedUserId = IsBindedUserId + groupMsg.IsBindedGroupId = IsBindedGroupId + groupMsg.RealGroupID = data.GroupOpenID + groupMsg.RealUserID = data.OpMemberOpenID + groupMsg.Avatar, _ = GenerateAvatarURLV2(data.OpMemberOpenID) + } + //根据条件判断是否增加nick和card + var CaN = config.GetCardAndNick() + if CaN != "" { + groupMsg.Sender.Nickname = CaN + groupMsg.Sender.Card = CaN + } + // 根据条件判断是否添加Echo字段 + if config.GetTwoWayEcho() { + groupMsg.Echo = echostr + //用向应用端(如果支持)发送echo,来确定客户端的send_msg对应的触发词原文 + echo.AddMsgIDv3(AppIDString, echostr, newdata.Content) + } + // 获取MasterID数组 + masterIDs := config.GetMasterID() + + // 判断userid64是否在masterIDs数组里 + isMaster := false + for _, id := range masterIDs { + if strconv.FormatInt(userid64, 10) == id { + isMaster = true + break + } + } + + // 根据isMaster的值为groupMsg的Sender赋值role字段 + if isMaster { + groupMsg.Sender.Role = "owner" + } else { + groupMsg.Sender.Role = "member" + } + + // 映射消息类型 + echo.AddMsgType(AppIDString, s, "group") + + //储存当前群或频道号的类型 + idmap.WriteConfigv2(fmt.Sprint(GroupID64), "type", "group") + + //映射类型 + echo.AddMsgType(AppIDString, GroupID64, "group") + + // 调试 + PrintStructWithFieldNames(groupMsg) + + // Convert OnebotGroupMessage to map and send + groupMsgMap := structToMap(groupMsg) + //上报信息到onebotv11应用端(正反ws) + p.BroadcastMessageToAll(groupMsgMap) + + // 转换appid + AppIDString := strconv.FormatUint(p.Settings.AppID, 10) + + // 储存和群号相关的eventid + fmt.Printf("测试:储存eventid:[%v]LongGroupID64[%v]\n", data.EventID, LongGroupID64) + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) + } + } + + return nil +} + +// ConvertRejectToMessage 转换 Reject 到 Message +func ConvertRejectToMessage(r *dto.GroupMsgRejectEvent) *dto.Message { + var message dto.Message + + // 直接映射的字段 + message.GroupID = r.GroupOpenID + + // 特殊处理的字段 + message.Content = config.GetGlobalGroupMsgRejectMessage() + message.DirectMessage = false + + return &message +} diff --git a/Processor/ProcessInlineSearch.go b/Processor/ProcessInlineSearch.go index f4574f63..c7a4d867 100644 --- a/Processor/ProcessInlineSearch.go +++ b/Processor/ProcessInlineSearch.go @@ -29,6 +29,8 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { // 转换appid var userid64 int64 var GroupID64 int64 + var LongGroupID64 int64 + var LongUserID64 int64 var err error var fromgid, fromuid string if data.GroupOpenID != "" { @@ -60,9 +62,9 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { if err != nil { mylog.Fatalf("Error storing ID: %v", err) } - //当参数不全 - _, _ = idmap.StoreIDv2(fromgid) - _, _ = idmap.StoreIDv2(fromuid) + // 当哈希碰撞 因为获取时候是用的非idmap的get函数 + LongGroupID64, _ = idmap.StoreIDv2(fromgid) + LongUserID64, _ = idmap.StoreIDv2(fromuid) if !config.GetHashIDValue() { mylog.Fatalf("避坑日志:你开启了高级id转换,请设置hash_id为true,并且删除idmaps并重启") } @@ -111,13 +113,20 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { AppIDString := strconv.FormatUint(p.Settings.AppID, 10) // 储存和群号相关的eventid - echo.AddEvnetID(AppIDString, GroupID64, data.ID) + // idmap-pro的设计其实是有问题的,和idmap冲突,并且也还是会哈希碰撞 需要用一个不会碰撞的id去存 + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) } else { if data.GroupOpenID != "" { //群回调 newdata := ConvertInteractionToMessage(data) //mylog.Printf("回调测试111-newdata:%v\n", newdata) - segmentedMessages := handlers.ConvertToSegmentedMessage(newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } + var IsBindedUserId, IsBindedGroupId bool if config.GetHashIDValue() { IsBindedUserId = idmap.CheckValue(data.GroupMemberOpenID, userid64) @@ -126,12 +135,10 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { IsBindedUserId = idmap.CheckValuev2(userid64) IsBindedGroupId = idmap.CheckValuev2(GroupID64) } - //映射str的messageID到int - messageID64, err := idmap.StoreIDv2(data.ID) - if err != nil { - mylog.Printf("Error storing ID: %v", err) - return nil - } + + //平台事件,不是真实信息,无需messageID + messageID64 := 123 + messageID := int(messageID64) var selfid64 int64 if config.GetUseUin() { @@ -220,23 +227,28 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { AppIDString := strconv.FormatUint(p.Settings.AppID, 10) // 储存和群号相关的eventid - echo.AddEvnetID(AppIDString, GroupID64, data.ID) + fmt.Printf("测试:储存eventid:[%v]LongGroupID64[%v]\n", data.EventID, LongGroupID64) + echo.AddEvnetID(AppIDString, LongGroupID64, data.EventID) } else if data.UserOpenID != "" { //私聊回调 newdata := ConvertInteractionToMessage(data) - segmentedMessages := handlers.ConvertToSegmentedMessage(newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } + var IsBindedUserId bool if config.GetHashIDValue() { IsBindedUserId = idmap.CheckValue(data.UserOpenID, userid64) } else { IsBindedUserId = idmap.CheckValuev2(userid64) } - //映射str的messageID到int - messageID64, err := idmap.StoreIDv2(data.ID) - if err != nil { - mylog.Printf("Error storing ID: %v", err) - return nil - } + + //平台事件,不是真实信息,无需messageID + messageID64 := 123 + messageID := int(messageID64) var selfid64 int64 if config.GetUseUin() { @@ -294,13 +306,19 @@ func (p *Processors) ProcessInlineSearch(data *dto.WSInteractionData) error { AppIDString := strconv.FormatUint(p.Settings.AppID, 10) // 储存和用户ID相关的eventid - echo.AddEvnetID(AppIDString, userid64, data.ID) + echo.AddEvnetID(AppIDString, LongUserID64, data.EventID) } else { // TODO: 区分频道和频道私信 如果有人提需求 // 频道回调 // 处理onebot_channel_message逻辑 newdata := ConvertInteractionToMessage(data) - segmentedMessages := handlers.ConvertToSegmentedMessage(newdata) + + // 如果在Array模式下, 则处理Message为Segment格式 + var segmentedMessages interface{} = data.Data.Resolved.ButtonData + if config.GetArrayValue() { + segmentedMessages = handlers.ConvertToSegmentedMessage(newdata) + } + var selfid64 int64 if config.GetUseUin() { selfid64 = config.GetUinint64() diff --git a/Processor/Processor.go b/Processor/Processor.go index 1f6466e6..e4558ae1 100644 --- a/Processor/Processor.go +++ b/Processor/Processor.go @@ -130,6 +130,30 @@ type OnebotInteractionNotice struct { Data *dto.WSInteractionData `json:"data,omitempty"` } +// onebotv11标准扩展 +type OnebotGroupRejectNotice struct { + GroupID int64 `json:"group_id,omitempty"` + NoticeType string `json:"notice_type,omitempty"` + PostType string `json:"post_type,omitempty"` + SelfID int64 `json:"self_id,omitempty"` + SubType string `json:"sub_type,omitempty"` + Time int64 `json:"time,omitempty"` + UserID int64 `json:"user_id,omitempty"` + Data *dto.GroupMsgRejectEvent `json:"data,omitempty"` +} + +// onebotv11标准扩展 +type OnebotGroupReceiveNotice struct { + GroupID int64 `json:"group_id,omitempty"` + NoticeType string `json:"notice_type,omitempty"` + PostType string `json:"post_type,omitempty"` + SelfID int64 `json:"self_id,omitempty"` + SubType string `json:"sub_type,omitempty"` + Time int64 `json:"time,omitempty"` + UserID int64 `json:"user_id,omitempty"` + Data *dto.GroupMsgReceiveEvent `json:"data,omitempty"` +} + type PrivateSender struct { Nickname string `json:"nickname"` UserID int64 `json:"user_id"` // Can be either string or int depending on logic diff --git a/botgo/dto/forum.go b/botgo/dto/forum.go index a8f5fcf9..1ec6c8b5 100644 --- a/botgo/dto/forum.go +++ b/botgo/dto/forum.go @@ -53,7 +53,8 @@ type Thread struct { ChannelID string `json:"channel_id"` AuthorID string `json:"author_id"` ThreadInfo ThreadInfo `json:"thread_info"` - ID string `json:"id,omitempty"` // 新增字段以存储ID + ID string `json:"id,omitempty"` // 新增字段以存储ID + EventID string `json:"event_id,omitempty"` // 新增字段以存储EventID } // Post 帖子事件主体内容 diff --git a/botgo/dto/interaction.go b/botgo/dto/interaction.go index 31544046..f56ebded 100644 --- a/botgo/dto/interaction.go +++ b/botgo/dto/interaction.go @@ -3,6 +3,7 @@ package dto // Interaction 互动行为对象 type Interaction struct { ID string `json:"id,omitempty"` // 平台方事件 ID + EventID string `json:"event_id,omitempty"` // 外层event_id Type InteractionType `json:"type,omitempty"` // 消息按钮: 11, 单聊快捷菜单: 12 Scene string `json:"scene,omitempty"` // 事件发生的场景 ChatType int `json:"chat_type,omitempty"` // 频道场景: 0, 群聊场景: 1, 单聊场景: 2 diff --git a/botgo/dto/message.go b/botgo/dto/message.go index 91b4b8d3..e57ddd46 100644 --- a/botgo/dto/message.go +++ b/botgo/dto/message.go @@ -54,6 +54,21 @@ type Forum struct { // GroupAddBotEvent 表示群添加机器人事件的数据结构 type GroupAddBotEvent struct { ID string `json:"id"` + EventID string `json:"event_id"` + GroupOpenID string `json:"group_openid"` + OpMemberOpenID string `json:"op_member_openid"` + Timestamp interface{} `json:"timestamp"` +} + +type GroupMsgRejectEvent struct { + EventID string `json:"event_id"` + GroupOpenID string `json:"group_openid"` + OpMemberOpenID string `json:"op_member_openid"` + Timestamp interface{} `json:"timestamp"` +} + +type GroupMsgReceiveEvent struct { + EventID string `json:"event_id"` GroupOpenID string `json:"group_openid"` OpMemberOpenID string `json:"op_member_openid"` Timestamp interface{} `json:"timestamp"` diff --git a/botgo/dto/websocket_event.go b/botgo/dto/websocket_event.go index 443d8259..5c89c40e 100644 --- a/botgo/dto/websocket_event.go +++ b/botgo/dto/websocket_event.go @@ -42,6 +42,8 @@ const ( EventC2CMessageCreate EventType = "C2C_MESSAGE_CREATE" EventGroupAddRobot EventType = "GROUP_ADD_ROBOT" EventGroupDelRobot EventType = "GROUP_DEL_ROBOT" + EventGroupMsgReject EventType = "GROUP_MSG_REJECT" + EventGroupMsgReceive EventType = "GROUP_MSG_RECEIVE" ) // intentEventMap 不同 intent 对应的事件定义 diff --git a/botgo/event/event.go b/botgo/event/event.go index b1bb4fd5..e4c094e6 100644 --- a/botgo/event/event.go +++ b/botgo/event/event.go @@ -56,6 +56,8 @@ var eventParseFuncMap = map[dto.OPCode]map[dto.EventType]eventParseFunc{ dto.EventC2CMessageCreate: c2cMessageHandler, dto.EventGroupAddRobot: groupaddbothandler, dto.EventGroupDelRobot: groupdelbothandler, + dto.EventGroupMsgReject: groupMsgRejecthandler, + dto.EventGroupMsgReceive: groupMsgReceivehandler, }, } @@ -78,8 +80,8 @@ func ParseAndHandle(payload *dto.WSPayload) error { func ParseData(message []byte, target interface{}) error { // 获取数据部分 data := gjson.Get(string(message), "d") - // 外层ID 与内层ID不同 - id := gjson.Get(string(message), "id").String() + // 外层ID 与内层ID不同 外层id是event_id 用于发送参数 d内层id是id,用于put回调接口 + eventid := gjson.Get(string(message), "id").String() // 使用switch语句处理不同类型 switch v := target.(type) { @@ -89,7 +91,7 @@ func ParseData(message []byte, target interface{}) error { return err } // 设置ID字段 - v.ID = id + v.EventID = eventid return nil case *dto.GroupAddBotEvent: @@ -98,7 +100,7 @@ func ParseData(message []byte, target interface{}) error { return err } // 设置ID字段 - v.ID = id + v.EventID = eventid return nil case *dto.WSInteractionData: @@ -107,7 +109,25 @@ func ParseData(message []byte, target interface{}) error { return err } // 设置ID字段 - v.ID = id + v.EventID = eventid + return nil + + case *dto.GroupMsgRejectEvent: + // 特殊处理dto.GroupMsgRejectEvent + if err := json.Unmarshal([]byte(data.String()), v); err != nil { + return err + } + // 设置ID字段 + v.EventID = eventid + return nil + + case *dto.GroupMsgReceiveEvent: + // 特殊处理dto.GroupMsgReceiveEvent + if err := json.Unmarshal([]byte(data.String()), v); err != nil { + return err + } + // 设置ID字段 + v.EventID = eventid return nil default: @@ -346,3 +366,25 @@ func interactionHandler(payload *dto.WSPayload, message []byte) error { } return nil } + +func groupMsgRejecthandler(payload *dto.WSPayload, message []byte) error { + data := &dto.GroupMsgRejectEvent{} + if err := ParseData(message, data); err != nil { + return err + } + if DefaultHandlers.GroupMsgReject != nil { + return DefaultHandlers.GroupMsgReject(payload, data) + } + return nil +} + +func groupMsgReceivehandler(payload *dto.WSPayload, message []byte) error { + data := &dto.GroupMsgReceiveEvent{} + if err := ParseData(message, data); err != nil { + return err + } + if DefaultHandlers.GroupMsgReceive != nil { + return DefaultHandlers.GroupMsgReceive(payload, data) + } + return nil +} diff --git a/botgo/event/register.go b/botgo/event/register.go index 03b7f6dc..bfff4804 100644 --- a/botgo/event/register.go +++ b/botgo/event/register.go @@ -32,10 +32,12 @@ var DefaultHandlers struct { Interaction InteractionEventHandler - GroupATMessage GroupATMessageEventHandler - C2CMessage C2CMessageEventHandler - GroupAddbot GroupAddRobotEventHandler - GroupDelbot GroupDelRobotEventHandler + GroupATMessage GroupATMessageEventHandler + C2CMessage C2CMessageEventHandler + GroupAddbot GroupAddRobotEventHandler + GroupDelbot GroupDelRobotEventHandler + GroupMsgReject GroupMsgRejectHandler + GroupMsgReceive GroupMsgReceiveHandler } // ReadyHandler 可以处理 ws 的 ready 事件 @@ -113,6 +115,12 @@ type GroupAddRobotEventHandler func(event *dto.WSPayload, data *dto.GroupAddBotE // GroupDelRobot 机器人删除事件 handler type GroupDelRobotEventHandler func(event *dto.WSPayload, data *dto.GroupAddBotEvent) error +// GroupMsgRejectHandler 机器人推送关闭事件 handler +type GroupMsgRejectHandler func(event *dto.WSPayload, data *dto.GroupMsgRejectEvent) error + +// GroupMsgReceiveHandler 机器人推送开启事件 handler +type GroupMsgReceiveHandler func(event *dto.WSPayload, data *dto.GroupMsgReceiveEvent) error + // ************************************************ // RegisterHandlers 注册事件回调,并返回 intent 用于 websocket 的鉴权 @@ -139,6 +147,10 @@ func RegisterHandlers(handlers ...interface{}) dto.Intent { DefaultHandlers.GroupAddbot = handle case GroupDelRobotEventHandler: DefaultHandlers.GroupDelbot = handle + case GroupMsgRejectHandler: + DefaultHandlers.GroupMsgReject = handle + case GroupMsgReceiveHandler: + DefaultHandlers.GroupMsgReceive = handle default: } } diff --git a/config/config.go b/config/config.go index f26eafe1..05488837 100644 --- a/config/config.go +++ b/config/config.go @@ -2130,3 +2130,35 @@ func GetEnableChangeWord() bool { } return instance.Settings.EnableChangeWord } + +// 获取GlobalGroupMsgRejectReciveEventToMessage状态 +func GetGlobalGroupMsgRejectReciveEventToMessage() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to GlobalGroupMsgRejectReciveEventToMessage.") + return false + } + return instance.Settings.GlobalGroupMsgRejectReciveEventToMessage +} + +// 获取GlobalGroupMsgRejectMessage +func GetGlobalGroupMsgRejectMessage() string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.GlobalGroupMsgRejectMessage + } + return "" +} + +// 获取GlobalGroupMsgRejectMessage +func GetGlobalGroupMsgReceiveMessage() string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.GlobalGroupMsgReceiveMessage + } + return "" +} diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 7afceb22..24565eff 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -224,7 +224,7 @@ func GetMessageIDByUseridOrGroupid(appID string, userID interface{}) string { return messageid } -// 通过user_id获取EventID 私聊,群,频道,通用 userID可以是三者之一 这是不需要区分群+用户的 只需要精准到群 私聊只需要精准到用户 +// 通过user_id获取EventID 私聊,群,频道,通用 userID可以是三者之一 这是不需要区分群+用户的 只需要精准到群 私聊只需要精准到用户 idmap不开启的用户使用 func GetEventIDByUseridOrGroupid(appID string, userID interface{}) string { // 从appID和userID生成key var userIDStr string @@ -241,7 +241,7 @@ func GetEventIDByUseridOrGroupid(appID string, userID interface{}) string { // 可能需要处理其他类型或报错 return "" } - //将真实id转为int + //将真实id转为int 这是非idmap-pro的方式 userid64, err := idmap.StoreIDv2(userIDStr) if err != nil { mylog.Printf("Error storing ID 241: %v", err) @@ -251,6 +251,7 @@ func GetEventIDByUseridOrGroupid(appID string, userID interface{}) string { mylog.Printf("GetEventIDByUseridOrGroupid_key:%v", key) eventid := echo.GetEventIDByKey(key) if eventid == "" { + // 用原始id获取,这个分支应该是没有用的. key := appID + "_" + userIDStr mylog.Printf("GetEventIDByUseridOrGroupid_key_2:%v", key) eventid = echo.GetEventIDByKey(key) diff --git a/main.go b/main.go index 3d144294..85636ad7 100644 --- a/main.go +++ b/main.go @@ -653,6 +653,20 @@ func GroupDelRobotEventHandler() event.GroupDelRobotEventHandler { } } +// GroupMsgRejectHandler 实现处理 群请求关闭机器人主动推送 事件的回调 +func GroupMsgRejectHandler() event.GroupMsgRejectHandler { + return func(event *dto.WSPayload, data *dto.GroupMsgRejectEvent) error { + return p.ProcessGroupMsgReject(data) + } +} + +// GroupMsgReceiveHandler 实现处理 群请求开启机器人主动推送 事件的回调 +func GroupMsgReceiveHandler() event.GroupMsgReceiveHandler { + return func(event *dto.WSPayload, data *dto.GroupMsgReceiveEvent) error { + return p.ProcessGroupMsgRecive(data) + } +} + func getHandlerByName(handlerName string) (interface{}, bool) { switch handlerName { case "ReadyHandler": //连接成功 @@ -679,10 +693,14 @@ func getHandlerByName(handlerName string) (interface{}, bool) { return GroupATMessageEventHandler(), true case "C2CMessageEventHandler": //群私聊 return C2CMessageEventHandler(), true - case "GroupAddRobotEventHandler": //群私聊 + case "GroupAddRobotEventHandler": //群添加机器人 return GroupAddRobotEventHandler(), true - case "GroupDelRobotEventHandler": //群私聊 + case "GroupDelRobotEventHandler": //群删除机器人 return GroupDelRobotEventHandler(), true + case "GroupMsgRejectHandler": //群请求关闭机器人主动推送 + return GroupMsgRejectHandler(), true + case "GroupMsgReceiveHandler": //群请求开启机器人主动推送 + return GroupMsgReceiveHandler(), true default: log.Printf("Unknown handler: %s\n", handlerName) return nil, false diff --git a/structs/structs.go b/structs/structs.go index 4e5eae54..14e41dc1 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -24,12 +24,15 @@ type Settings struct { //事件订阅类 TextIntent []string `yaml:"text_intent"` //转换类 - GlobalChannelToGroup bool `yaml:"global_channel_to_group"` - GlobalPrivateToChannel bool `yaml:"global_private_to_channel"` - GlobalForumToChannel bool `yaml:"global_forum_to_channel"` - GlobalInteractionToMessage bool `yaml:"global_interaction_to_message"` - HashID bool `yaml:"hash_id"` - IdmapPro bool `yaml:"idmap_pro"` + GlobalChannelToGroup bool `yaml:"global_channel_to_group"` + GlobalPrivateToChannel bool `yaml:"global_private_to_channel"` + GlobalForumToChannel bool `yaml:"global_forum_to_channel"` + GlobalInteractionToMessage bool `yaml:"global_interaction_to_message"` + GlobalGroupMsgRejectReciveEventToMessage bool `yaml:"global_group_msg_rre_to_message"` + GlobalGroupMsgRejectMessage string `yaml:"global_group_msg_reject_message"` + GlobalGroupMsgReceiveMessage string `yaml:"global_group_msg_receive_message"` + HashID bool `yaml:"hash_id"` + IdmapPro bool `yaml:"idmap_pro"` //gensokyo互联类 Server_dir string `yaml:"server_dir"` Port string `yaml:"port"` diff --git a/template/config_template.go b/template/config_template.go index a7b1e277..c3842272 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -39,8 +39,11 @@ settings: global_private_to_channel: false # 是否将私聊转换成频道 如果是群场景 会将私聊转为群(方便提审\测试) global_forum_to_channel: false # 是否将频道帖子信息转化为频道 子频道信息 如果开启global_channel_to_group会进一步转换为群信息 global_interaction_to_message : false # 是否将按钮和表态回调转化为消息 仅在设置了按钮回调中的message时有效 + global_group_msg_rre_to_message : false # 是否将用户开关机器人资料页的机器人推送开关 产生的事件转换为文本信息并发送给应用端.false将使用onebotv11的notice类型上报. + global_group_msg_reject_message : "机器人主动消息被关闭" # 当开启 global_group_msg_rre_to_message 时,机器人主动信息被关闭将上报的信息. 自行添加intent - GroupMsgRejectHandler + global_group_msg_receive_message : "机器人主动消息被开启" # 建议设置为无规则复杂随机内容,避免用户指令内容碰撞. 自行添加 intent - GroupMsgReceiveHandler hash_id : false # 使用hash来进行idmaps转换,可以让user_id不是123开始的递增值 - idmap_pro : false #需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. + idmap_pro : false # 需开启hash_id配合,高级id转换增强,可以多个真实值bind到同一个虚拟值,对于每个用户,每个群\私聊\判断私聊\频道,都会产生新的虚拟值,但可以多次bind,bind到同一个数字.数据库负担会变大. #Gensokyo互联类 server_dir: "" # Lotus地址.不带http头的域名或ip,提供图片上传服务的服务器(图床)需要带端口号. 如果需要发base64图,需为公网ip,且开放对应端口 From bf13e563807fd9b024456e6c5f6b5e1fe3632c73 Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 May 2024 11:01:40 +0800 Subject: [PATCH 3/6] beta401 --- Processor/ProcessGroupMsgReceive.go | 6 +- Processor/ProcessGroupMsgReject.go | 6 +- config/config.go | 222 ++++++++++++++++++++-------- go.mod | 3 +- go.sum | 2 + main.go | 45 +++++- 6 files changed, 214 insertions(+), 70 deletions(-) diff --git a/Processor/ProcessGroupMsgReceive.go b/Processor/ProcessGroupMsgReceive.go index b1c6425c..3df37683 100644 --- a/Processor/ProcessGroupMsgReceive.go +++ b/Processor/ProcessGroupMsgReceive.go @@ -65,6 +65,8 @@ func (p *Processors) ProcessGroupMsgRecive(data *dto.GroupMsgReceiveEvent) error mylog.Printf("Error storing ID: %v", err) return nil } + // 修复不开启idmap-pro问题 + LongGroupID64 = GroupID64 } var selfid64 int64 if config.GetUseUin() { @@ -76,7 +78,7 @@ func (p *Processors) ProcessGroupMsgRecive(data *dto.GroupMsgReceiveEvent) error if !config.GetGlobalGroupMsgRejectReciveEventToMessage() { notice := &OnebotGroupReceiveNotice{ GroupID: GroupID64, - NoticeType: "interaction", + NoticeType: "group_receive", PostType: "notice", SelfID: selfid64, SubType: "create", @@ -150,7 +152,7 @@ func (p *Processors) ProcessGroupMsgRecive(data *dto.GroupMsgReceiveEvent) error } //增强配置 if !config.GetNativeOb11() { - groupMsg.RealMessageType = "interaction" + groupMsg.RealMessageType = "group_receive" groupMsg.IsBindedUserId = IsBindedUserId groupMsg.IsBindedGroupId = IsBindedGroupId groupMsg.RealGroupID = data.GroupOpenID diff --git a/Processor/ProcessGroupMsgReject.go b/Processor/ProcessGroupMsgReject.go index e222152c..ae53d668 100644 --- a/Processor/ProcessGroupMsgReject.go +++ b/Processor/ProcessGroupMsgReject.go @@ -66,6 +66,8 @@ func (p *Processors) ProcessGroupMsgReject(data *dto.GroupMsgRejectEvent) error mylog.Printf("Error storing ID: %v", err) return nil } + // 修复不开启idmap-pro问题 + LongGroupID64 = GroupID64 } var selfid64 int64 if config.GetUseUin() { @@ -77,7 +79,7 @@ func (p *Processors) ProcessGroupMsgReject(data *dto.GroupMsgRejectEvent) error if !config.GetGlobalGroupMsgRejectReciveEventToMessage() { notice := &OnebotGroupRejectNotice{ GroupID: GroupID64, - NoticeType: "interaction", + NoticeType: "group_reject", PostType: "notice", SelfID: selfid64, SubType: "create", @@ -151,7 +153,7 @@ func (p *Processors) ProcessGroupMsgReject(data *dto.GroupMsgRejectEvent) error } //增强配置 if !config.GetNativeOb11() { - groupMsg.RealMessageType = "interaction" + groupMsg.RealMessageType = "group_reject" groupMsg.IsBindedUserId = IsBindedUserId groupMsg.IsBindedGroupId = IsBindedGroupId groupMsg.RealGroupID = data.GroupOpenID diff --git a/config/config.go b/config/config.go index 05488837..e3bc138f 100644 --- a/config/config.go +++ b/config/config.go @@ -35,47 +35,141 @@ type CommentBlock struct { Offset int // 注释与目标键之间的行数 } +// 不支持配置热重载的配置项 +var restartRequiredFields = []string{ + "WsAddress", "WsToken", "ReconnectTimes", "HeartBeatInterval", "LaunchReconnectTimes", + "AppID", "Uin", "Token", "ClientSecret", "ShardCount", "ShardID", "UseUin", + "TextIntent", + "ServerDir", "Port", "BackupPort", "Lotus", "LotusPassword", "LotusWithoutIdmaps", + "WsServerPath", "EnableWsServer", "WsServerToken", + "IdentifyFile", "IdentifyAppids", "Crt", "Key", + "DeveloperLog", "LogLevel", "SaveLogs", + "DisableWebui", "Username", "Password", + "Title", // 继续检查和增加 +} + // LoadConfig 从文件中加载配置并初始化单例配置 -func LoadConfig(path string) (*Config, error) { +func LoadConfig(path string, fastload bool) (*Config, error) { mu.Lock() defer mu.Unlock() - // 如果单例已经被初始化了,直接返回 - if instance != nil { - return instance, nil - } - configData, err := os.ReadFile(path) if err != nil { return nil, err } - //todo remove it 破坏性变更的擦屁股代码 - var ischange bool - configData, ischange = replaceVisualPrefixsLine(configData) - if ischange { - err = os.WriteFile(path, configData, 0644) - if err != nil { - // 处理写入错误 - return nil, err - } - } - //mylog.Printf("dev_ischange:%v", ischange) + + // 检查并替换视觉前缀行,如果有必要,后期会注释 + // var isChange bool + // configData, isChange = replaceVisualPrefixsLine(configData) + // if isChange { + // // 如果配置文件已修改,重新写入修正后的数据 + // if err = os.WriteFile(path, configData, 0644); err != nil { + // return nil, err // 处理写入错误 + // } + // } + + // 尝试解析配置数据 conf := &Config{} - err = yaml.Unmarshal(configData, conf) - if err != nil { + if err = yaml.Unmarshal(configData, conf); err != nil { return nil, err } - // 确保配置完整性 - if err := ensureConfigComplete(path); err != nil { - return nil, err + if !fastload { + // 确保本地配置文件的完整性,添加新的字段 + if err = ensureConfigComplete(path); err != nil { + return nil, err + } + } else { + if isValidConfig(conf) { + //log.Printf("instance.Settings:%v", instance.Settings) + // 用现有的instance比对即将覆盖赋值的conf,用[]string返回配置发生了变化的配置项 + changedFields := compareConfigChanges("Settings", instance.Settings, conf.Settings) + // 根据changedFields进行进一步的操作,在不支持热重载的字段实现自动重启 + if len(changedFields) > 0 { + log.Printf("配置已变更的字段:%v", changedFields) + checkForRestart(changedFields) // 检查变更字段是否需要重启 + } + } //conf为空时不对比 + } + + // 更新单例实例,即使它已经存在 更新前检查是否有效,vscode对文件的更新行为会触发2次文件变动 + // 第一次会让configData为空,迅速的第二次才是正常有值的configData + if isValidConfig(conf) { + instance = conf } - // 设置单例实例 - instance = conf return instance, nil } +func isValidConfig(conf *Config) bool { + // 确认config不为空且必要字段已设置 + return conf != nil && conf.Version != 0 +} + +// 去除Settings前缀 +func stripSettingsPrefix(fieldName string) string { + return strings.TrimPrefix(fieldName, "Settings.") +} + +// compareConfigChanges 检查并返回发生变化的配置字段,处理嵌套结构体 +func compareConfigChanges(prefix string, oldConfig interface{}, newConfig interface{}) []string { + var changedFields []string + + oldVal := reflect.ValueOf(oldConfig) + newVal := reflect.ValueOf(newConfig) + + // 解引用指针 + if oldVal.Kind() == reflect.Ptr { + oldVal = oldVal.Elem() + } + if newVal.Kind() == reflect.Ptr { + newVal = newVal.Elem() + } + + // 遍历所有字段 + for i := 0; i < oldVal.NumField(); i++ { + oldField := oldVal.Field(i) + newField := newVal.Field(i) + fieldType := oldVal.Type().Field(i) + fieldName := fieldType.Name + + fullFieldName := fieldName + if prefix != "" { + fullFieldName = fmt.Sprintf("%s.%s", prefix, fieldName) + } + + // 对于结构体字段递归比较 + if oldField.Kind() == reflect.Struct || newField.Kind() == reflect.Struct { + subChanges := compareConfigChanges(fullFieldName, oldField.Interface(), newField.Interface()) + changedFields = append(changedFields, subChanges...) + } else { + // 打印将要比较的字段和它们的值 + //fmt.Printf("Comparing field: %s\nOld value: %v\nNew value: %v\n", fullFieldName, oldField.Interface(), newField.Interface()) + if !reflect.DeepEqual(oldField.Interface(), newField.Interface()) { + //fmt.Println("-> Field changed") + // 去除Settings前缀后添加到变更字段列表 + changedField := stripSettingsPrefix(fullFieldName) + changedFields = append(changedFields, changedField) + } + } + } + + return changedFields +} + +// 检查是否需要重启 +func checkForRestart(changedFields []string) { + for _, field := range changedFields { + for _, restartField := range restartRequiredFields { + if field == restartField { + fmt.Println("Configuration change requires restart:", field) + sys.RestartApplication() // 调用重启函数 + return + } + } + } +} + func CreateAndWriteConfigTemp() error { // 读取config.yml configFile, err := os.ReadFile("config.yml") @@ -1426,46 +1520,46 @@ func GetQrSize() int { return instance.Settings.QrSize } -func replaceVisualPrefixsLine(configData []byte) ([]byte, bool) { - // 定义新的 visual_prefixs 部分 - newVisualPrefixs := ` visual_prefixs : #虚拟前缀 与white_prefixs配合使用 处理流程自动忽略该前缀 remove_prefix remove_at 需为true时生效 - - prefix: "" #虚拟前缀开头 例 你有3个指令 帮助 测试 查询 将 prefix 设置为 工具类 后 则可通过 工具类 帮助 触发机器人 - whiteList: [""] #开关状态取决于 white_prefix_mode 为每一个二级指令头设计独立的白名单 - No_White_Response : "" - - prefix: "" - whiteList: [""] - No_White_Response : "" - - prefix: "" - whiteList: [""] - No_White_Response : "" ` - - // 将 byte 数组转换为字符串 - configStr := string(configData) - - // 按行分割 configStr - lines := strings.Split(configStr, "\n") - - // 创建一个新的字符串构建器 - var newConfigData strings.Builder - - // 标记是否进行了替换 - replaced := false - - // 遍历所有行 - for _, line := range lines { - // 检查是否是 visual_prefixs 开头的行 - if strings.HasPrefix(strings.TrimSpace(line), "visual_prefixs : [") { - // 替换为新的 visual_prefixs 部分 - newConfigData.WriteString(newVisualPrefixs + "\n") - replaced = true - continue // 跳过原有行 - } - newConfigData.WriteString(line + "\n") - } - - // 返回新配置和是否发生了替换的标记 - return []byte(newConfigData.String()), replaced -} +// func replaceVisualPrefixsLine(configData []byte) ([]byte, bool) { +// // 定义新的 visual_prefixs 部分 +// newVisualPrefixs := ` visual_prefixs : #虚拟前缀 与white_prefixs配合使用 处理流程自动忽略该前缀 remove_prefix remove_at 需为true时生效 +// - prefix: "" #虚拟前缀开头 例 你有3个指令 帮助 测试 查询 将 prefix 设置为 工具类 后 则可通过 工具类 帮助 触发机器人 +// whiteList: [""] #开关状态取决于 white_prefix_mode 为每一个二级指令头设计独立的白名单 +// No_White_Response : "" +// - prefix: "" +// whiteList: [""] +// No_White_Response : "" +// - prefix: "" +// whiteList: [""] +// No_White_Response : "" ` + +// // 将 byte 数组转换为字符串 +// configStr := string(configData) + +// // 按行分割 configStr +// lines := strings.Split(configStr, "\n") + +// // 创建一个新的字符串构建器 +// var newConfigData strings.Builder + +// // 标记是否进行了替换 +// replaced := false + +// // 遍历所有行 +// for _, line := range lines { +// // 检查是否是 visual_prefixs 开头的行 +// if strings.HasPrefix(strings.TrimSpace(line), "visual_prefixs : [") { +// // 替换为新的 visual_prefixs 部分 +// newConfigData.WriteString(newVisualPrefixs + "\n") +// replaced = true +// continue // 跳过原有行 +// } +// newConfigData.WriteString(line + "\n") +// } + +// // 返回新配置和是否发生了替换的标记 +// return []byte(newConfigData.String()), replaced +// } // 获取GetWhiteBypassRevers的值 func GetWhiteBypassRevers() bool { diff --git a/go.mod b/go.mod index c41d3ff0..df8ad2a7 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/aliyun/aliyun-oss-go-sdk v3.0.1+incompatible github.com/baidubce/bce-sdk-go v0.9.161 github.com/fatih/color v1.15.0 + github.com/fsnotify/fsnotify v1.7.0 github.com/gin-gonic/gin v1.9.1 github.com/google/uuid v1.4.0 github.com/gorilla/websocket v1.4.2 @@ -69,6 +70,6 @@ require ( golang.org/x/net v0.10.0 golang.org/x/sys v0.13.0 golang.org/x/text v0.9.0 // indirect - google.golang.org/protobuf v1.32.0 + google.golang.org/protobuf v1.32.0 // indirect mvdan.cc/xurls v1.1.0 ) diff --git a/go.sum b/go.sum index 401b1c22..7ba6f44c 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBD github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= diff --git a/main.go b/main.go index 85636ad7..2e6d82c5 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ import ( "time" "github.com/fatih/color" + "github.com/fsnotify/fsnotify" "github.com/hoshinonyaruko/gensokyo/Processor" "github.com/hoshinonyaruko/gensokyo/acnode" "github.com/hoshinonyaruko/gensokyo/botstats" @@ -93,10 +94,14 @@ func main() { // 主逻辑 // 加载配置 - conf, err := config.LoadConfig("config.yml") + conf, err := config.LoadConfig("config.yml", false) if err != nil { log.Fatalf("error: %v", err) } + + // 配置热重载 + go setupConfigWatcher("config.yml") + sys.SetTitle(conf.Settings.Title) webuiURL := config.ComposeWebUIURL(conf.Settings.Lotus) // 调用函数获取URL webuiURLv2 := config.ComposeWebUIURLv2(conf.Settings.Lotus) // 调用函数获取URL @@ -716,3 +721,41 @@ func allEmpty(addresses []string) bool { } return true } + +func setupConfigWatcher(configFilePath string) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatalf("Error setting up watcher: %v", err) + } + + // 添加一个100毫秒的Debouncing + //fileLoader := &config.ConfigFileLoader{EventDelay: 100 * time.Millisecond} + + // Start the goroutine to handle file system events. + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return // Exit if channel is closed. + } + if event.Op&fsnotify.Write == fsnotify.Write { + fmt.Println("检测到配置文件变动:", event.Name) + //fileLoader.LoadConfigF(configFilePath) + config.LoadConfig(configFilePath, true) + } + case err, ok := <-watcher.Errors: + if !ok { + return // Exit if channel is closed. + } + log.Println("Watcher error:", err) + } + } + }() + + // Add the config file to the list of watched files. + err = watcher.Add(configFilePath) + if err != nil { + log.Fatalf("Error adding watcher: %v", err) + } +} From f1026ae83372ac3964bc4322c7b2b56f8faf17b7 Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 May 2024 17:13:09 +0800 Subject: [PATCH 4/6] beta402 --- handlers/send_group_msg.go | 88 ++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 77597bce..d7970630 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -246,6 +246,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } if imageCount == 1 && messageText != "" { + var groupMessage *dto.MessageToCreate mylog.Printf("发图文混合信息-群") // 创建包含单个图片的 singleItem singleItem[imageType] = []string{imageUrl} @@ -254,11 +255,15 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap groupReply := generateGroupMessage(messageID, eventID, singleItem, "", msgseq+1, apiv2, message.Params.GroupID.(string)) // 进行类型断言 richMediaMessage, ok := groupReply.(*dto.RichMediaMessage) + // 如果断言为RichMediaMessage失败 if !ok { - mylog.Printf("Error: Expected RichMediaMessage type for key ") - return "", nil + // 尝试断言为MessageToCreate + groupMessage, ok = groupReply.(*dto.MessageToCreate) + if !ok { + mylog.Printf("Error: Expected RichMediaMessage type for key,value:%v", groupReply) + return "", nil + } } - var groupMessage *dto.MessageToCreate var transmd bool var md *dto.Markdown var kb *keyboard.MessageKeyboard @@ -266,46 +271,53 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap if config.GetTwoWayEcho() { md, kb, transmd = auto_md(message, messageText, richMediaMessage) } + // 如果groupMessage是nil 说明groupReply是richMediaMessage类型 如果groupMessage不是nil 说明groupReply是MessageToCreate + if groupMessage == nil { + //如果没有转换成md发送 + if !transmd { + // 上传图片并获取FileInfo + fileInfo, err := uploadMedia(context.TODO(), message.Params.GroupID.(string), richMediaMessage, apiv2) + if err != nil { + mylog.Printf("上传图片失败: %v", err) + return "", nil // 或其他错误处理 + } + // 创建包含文本和图像信息的消息 + msgseq = echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupMessage = &dto.MessageToCreate{ + Content: messageText, // 添加文本内容 + Media: dto.Media{ + FileInfo: fileInfo, // 添加图像信息 + }, + MsgID: messageID, + EventID: eventID, + MsgSeq: msgseq, + MsgType: 7, // 假设7是组合消息类型 + } + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 + } else { + //将kb和md组合成groupMessage并用MsgType=2发送 - //如果没有转换成md发送 - if !transmd { - // 上传图片并获取FileInfo - fileInfo, err := uploadMedia(context.TODO(), message.Params.GroupID.(string), richMediaMessage, apiv2) - if err != nil { - mylog.Printf("上传图片失败: %v", err) - return "", nil // 或其他错误处理 - } - // 创建包含文本和图像信息的消息 - msgseq = echo.GetMappingSeq(messageID) - echo.AddMappingSeq(messageID, msgseq+1) - groupMessage = &dto.MessageToCreate{ - Content: messageText, // 添加文本内容 - Media: dto.Media{ - FileInfo: fileInfo, // 添加图像信息 - }, - MsgID: messageID, - EventID: eventID, - MsgSeq: msgseq, - MsgType: 7, // 假设7是组合消息类型 - } - groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - } else { - //将kb和md组合成groupMessage并用MsgType=2发送 + msgseq = echo.GetMappingSeq(messageID) + echo.AddMappingSeq(messageID, msgseq+1) + groupMessage = &dto.MessageToCreate{ + Content: "markdown", // 添加文本内容 + MsgID: messageID, + EventID: eventID, + MsgSeq: msgseq, + Markdown: md, + Keyboard: kb, + MsgType: 2, // 假设7是组合消息类型 + } + groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - msgseq = echo.GetMappingSeq(messageID) - echo.AddMappingSeq(messageID, msgseq+1) - groupMessage = &dto.MessageToCreate{ - Content: "markdown", // 添加文本内容 - MsgID: messageID, - EventID: eventID, - MsgSeq: msgseq, - Markdown: md, - Keyboard: kb, - MsgType: 2, // 假设7是组合消息类型 } + } else { + // 为groupMessage附加内容 变成图文信息 + groupMessage.Content = messageText groupMessage.Timestamp = time.Now().Unix() // 设置时间戳 - } + // 发送组合消息 resp, err := apiv2.PostGroupMessage(context.TODO(), message.Params.GroupID.(string), groupMessage) if err != nil { From a77b628f23d968d328325a2d3a626a8a7497ed81 Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 May 2024 17:56:06 +0800 Subject: [PATCH 5/6] beta403 --- handlers/send_group_msg.go | 10 +++++----- handlers/send_group_msg_raw.go | 6 +++--- handlers/send_msg.go | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index d7970630..044afd83 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -125,7 +125,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap messageID = echo.GetLazyMessagesId(message.Params.GroupID.(string)) mylog.Printf("GetLazyMessagesId: %v", messageID) //如果应用端传递了user_id 就让at不要顺序乱套 - if message.Params.UserID != nil { + if message.Params.UserID != nil && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { messageID = echo.GetLazyMessagesIdv2(message.Params.GroupID.(string), message.Params.UserID.(string)) mylog.Printf("GetLazyMessagesIdv2: %v", messageID) } else { @@ -148,7 +148,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap } var originalGroupID, originalUserID string // 检查UserID是否为nil - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { // 如果UserID不是nil且配置为使用Pro版本,则调用RetrieveRowByIDv2Pro originalGroupID, originalUserID, err = idmap.RetrieveRowByIDv2Pro(message.Params.GroupID.(string), message.Params.UserID.(string)) if err != nil { @@ -198,7 +198,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap //mylog.Println("foundItems:", foundItems) if messageID == "" { // 检查 UserID 是否为 nil - if message.Params.UserID != nil { + if message.Params.UserID != nil && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { messageID = GetMessageIDByUseridAndGroupid(config.GetAppIDStr(), message.Params.UserID, message.Params.GroupID) mylog.Println("通过GetMessageIDByUseridAndGroupid函数获取的message_id:", message.Params.GroupID, messageID) } else { @@ -481,7 +481,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } @@ -531,7 +531,7 @@ func HandleSendGroupMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openap //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } diff --git a/handlers/send_group_msg_raw.go b/handlers/send_group_msg_raw.go index 9cc1c4b2..714edc57 100644 --- a/handlers/send_group_msg_raw.go +++ b/handlers/send_group_msg_raw.go @@ -110,7 +110,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope var originalGroupID, originalUserID string // 检查UserID是否为nil - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { // 如果UserID不是nil且配置为使用Pro版本,则调用RetrieveRowByIDv2Pro originalGroupID, originalUserID, err = idmap.RetrieveRowByIDv2Pro(message.Params.GroupID.(string), message.Params.UserID.(string)) if err != nil { @@ -410,7 +410,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } @@ -460,7 +460,7 @@ func HandleSendGroupMsgRaw(client callapi.Client, api openapi.OpenAPI, apiv2 ope //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } diff --git a/handlers/send_msg.go b/handlers/send_msg.go index 24565eff..a2e67a14 100644 --- a/handlers/send_msg.go +++ b/handlers/send_msg.go @@ -124,7 +124,7 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } @@ -150,7 +150,7 @@ func HandleSendMsg(client callapi.Client, api openapi.OpenAPI, apiv2 openapi.Ope //用GroupID给ChannelID赋值,因为我们是把频道虚拟成了群 message.Params.ChannelID = message.Params.GroupID.(string) var RChannelID string - if message.Params.UserID != nil && config.GetIdmapPro() { + if message.Params.UserID != nil && config.GetIdmapPro() && message.Params.UserID.(string) != "" && message.Params.UserID.(string) != "0" { RChannelID, _, err = idmap.RetrieveRowByIDv2Pro(message.Params.ChannelID.(string), message.Params.UserID.(string)) mylog.Printf("测试,通过Proid获取的RChannelID:%v", RChannelID) } From d2448e1cc3cacae6c25a8805cbd61279be923ac4 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sat, 11 May 2024 15:51:15 +0800 Subject: [PATCH 6/6] beta404 --- config/config.go | 24 ++++++++++++++++++++++++ handlers/send_group_msg.go | 36 ++++++++++++++++++++++++++---------- structs/structs.go | 2 ++ template/config_template.go | 4 +++- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/config/config.go b/config/config.go index e3bc138f..5eb2fe50 100644 --- a/config/config.go +++ b/config/config.go @@ -2256,3 +2256,27 @@ func GetGlobalGroupMsgReceiveMessage() string { } return "" } + +// 获取EntersAsBlock状态 +func GetEntersAsBlock() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to EntersAsBlock.") + return false + } + return instance.Settings.EntersAsBlock +} + +// 获取NativeMD状态 +func GetNativeMD() bool { + mu.Lock() + defer mu.Unlock() + + if instance == nil { + mylog.Println("Warning: instance is nil when trying to NativeMD.") + return false + } + return instance.Settings.NativeMD +} diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index 044afd83..17a72838 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -1335,6 +1335,7 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage mylog.Printf("获取图片宽高出错") } imgDesc := fmt.Sprintf("图片 #%dpx #%dpx", width, height) + // 将所有的\r\n替换为\r messageText = strings.ReplaceAll(messageText, "\r\n", "\r") // 将所有的\n替换为\r @@ -1343,18 +1344,33 @@ func auto_md(message callapi.ActionMessage, messageText string, richMediaMessage if !strings.HasPrefix(messageText, "\r") { messageText = "\r" + messageText } - // 创建 MarkdownParams 的实例 - mdParams := []*dto.MarkdownParams{ - {Key: "text_start", Values: []string{" "}}, //空着 - {Key: "img_dec", Values: []string{imgDesc}}, - {Key: "img_url", Values: []string{imgURL}}, - {Key: "text_end", Values: []string{messageText}}, + if config.GetEntersAsBlock() { + messageText = strings.ReplaceAll(messageText, "\r", " ") } - // 组合模板 Markdown - md = &dto.Markdown{ - CustomTemplateID: CustomTemplateID, - Params: mdParams, + + // 根据配置决定如何生成Markdown内容 + if !config.GetNativeMD() { + // 创建 MarkdownParams 的实例 + mdParams := []*dto.MarkdownParams{ + {Key: "text_start", Values: []string{" "}}, //空着 + {Key: "img_dec", Values: []string{imgDesc}}, + {Key: "img_url", Values: []string{imgURL}}, + {Key: "text_end", Values: []string{messageText}}, + } + // 组合模板 Markdown + md = &dto.Markdown{ + CustomTemplateID: CustomTemplateID, + Params: mdParams, + } + } else { + // 使用原生Markdown格式 + content := fmt.Sprintf(" %s![%s](%s)%s", " ", imgDesc, imgURL, messageText) + // 原生 Markdown + md = &dto.Markdown{ + Content: content, + } } + whiteList := matchedPrefix.WhiteList // 创建 CustomKeyboard customKeyboard := &keyboard.CustomKeyboard{ diff --git a/structs/structs.go b/structs/structs.go index 14e41dc1..7786e831 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -128,6 +128,8 @@ type Settings struct { //MD相关 CustomTemplateID string `yaml:"custom_template_id"` KeyBoardID string `yaml:"keyboard_id"` + NativeMD bool `yaml:"native_md"` + EntersAsBlock bool `yaml:"enters_as_block"` //发送行为修改 LazyMessageId bool `yaml:"lazy_message_id"` RamDomSeq bool `yaml:"ramdom_seq"` diff --git a/template/config_template.go b/template/config_template.go index c3842272..37c3e312 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -167,7 +167,9 @@ settings: #MD相关 custom_template_id : "" #自动转换图文信息到md所需要的id *需要应用端支持双方向echo keyboard_id : "" #自动转换图文信息到md所需要的按钮id *需要应用端支持双方向echo - + native_md : false #自动转换图文信息到md,使用原生markdown能力. + enters_as_block : false #自动转换图文信息到md,\r \r\n \n 替换为空格. + #发送行为修改 lazy_message_id : false #false=message_id 条条准确对应 true=message_id 按时间范围随机对应(适合主动推送bot)前提,有足够多的活跃信息刷新id池 ramdom_seq : false #当多开gensokyo时,如果遇到群信息只能发出一条,请开启每个gsk的此项.(建议使用一个gsk连接多个应用)