瀏覽代碼

微信机器人

WH01243 4 周之前
當前提交
8edb74ec8c

+ 1 - 0
client/.license_accepted.flag

@@ -0,0 +1 @@
+User accepted the license agreement.

+ 18 - 0
client/DISCLAIMER.md

@@ -0,0 +1,18 @@
+# 免责声明
+
+1. **本工具为开源项目,仅提供基础功能,供用户进行合法的学习、研究和非商业用途**。禁止将本工具用于任何违法或侵权行为。
+
+2. **二次开发者的责任**:
+   - 任何基于本工具进行的二次开发、修改或衍生产品,其行为及后果由二次开发者独立承担,与本工具贡献者无关。
+   - **禁止使用贡献者的姓名、项目名称或相关信息作为二次开发产品的背书或推广手段**。
+   - 建议二次开发者在其衍生产品中添加自己的免责声明,明确责任归属。
+
+3. **用户责任**:
+   - 使用本工具或其衍生产品的所有后果由用户自行承担。原贡献者不对因直接或间接使用本工具而导致的任何损失、责任或争议负责。
+
+4. **法律约束**:
+   - 用户和二次开发者须遵守《中华人民共和国网络安全法》、《中华人民共和国著作权法》等相关法律法规。
+   - 本工具涉及的所有第三方商标或产品名称,其权利归权利人所有,作者与第三方无任何直接或间接关联。
+
+5. **作者保留权利**:
+   - 本工具作者保留随时修改、更新、删除或终止本工具的权利,无需事先通知或承担任何义务。

+ 139 - 0
client/app/robot.go

@@ -0,0 +1,139 @@
+package app
+
+/*
+#cgo LDFLAGS: -L../ -lsdk
+#include <stdlib.h>
+#include <stdbool.h>
+
+extern int WxInitSDK(bool, int);
+extern int WxDestroySDK();
+*/
+import "C"
+import (
+	"bytes"
+	"client/wcf"
+	"io"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/go-resty/resty/v2"
+	"github.com/google/uuid"
+)
+
+var WxClient *wcf.Client
+
+// Message 组装成一个结构体展示消息
+type Message struct {
+	IsSelf    bool   `json:"is_self,omitempty"`
+	IsGroup   bool   `json:"is_group,omitempty"`
+	MessageId uint64 `json:"message_id,omitempty"`
+	Type      uint32 `json:"type,omitempty"`
+	Ts        uint32 `json:"ts,omitempty"`
+	RoomId    string `json:"room_id,omitempty"`
+	Content   string `json:"content,omitempty"`
+	WxId      string `json:"wx_id,omitempty"`
+	Sign      string `json:"sign,omitempty"`
+	Thumb     string `json:"thumb,omitempty"`
+	Extra     string `json:"extra,omitempty"`
+	Xml       string `json:"xml,omitempty"`
+}
+
+// WechatFerryInit 调用sdk.dll中的WxInitSdk 进行启动微信并注入
+func WechatFerryInit() {
+	// 调试模式  端口
+	initSuccess := C.WxInitSDK(C.bool(false), C.int(10086))
+	if initSuccess == 0 {
+		log.Println("SDK 初始化成功")
+	} else {
+		log.Println("SDK 初始化失败")
+	}
+	time.Sleep(time.Millisecond * 5000)
+	// 连接服务器
+	client, errs := wcf.NewWCF("")
+	if errs != nil {
+		return
+	}
+	// 一定要在这里判断是否登录成功 否则会导致用户列表获取失败
+	for true {
+		if client.IsLogin() == true {
+			log.Println("登录成功...等待初始化中...")
+			time.Sleep(2000 * time.Millisecond)
+			break
+		}
+		time.Sleep(1000 * time.Millisecond)
+	}
+	WxClient = client
+	ContactsInit()
+	log.Println("初始化完成")
+}
+
+// ContactsInit 通讯录初始化
+func ContactsInit() {
+	var contactsMap []map[string]string
+	contacts := WxClient.GetContacts()
+	for _, v := range contacts {
+		gender := ""
+		if v.Gender == 1 {
+			gender = "男"
+		}
+		if v.Gender == 2 {
+			gender = "女"
+		}
+		contactsMaps := map[string]string{
+			"wxId":     v.Wxid,
+			"code":     v.Code,
+			"remark":   v.Remark,
+			"name":     v.Name,
+			"country":  v.Country,
+			"province": v.Province,
+			"city":     v.City,
+			"gender":   gender,
+		}
+		contactsMap = append(contactsMap, contactsMaps)
+	}
+	WxClient.ContactsMap = contactsMap
+}
+
+// DownloadFile 下载文件
+func DownloadFile(url string, fileType string, suffix string) (string, error) {
+	log.Println(url)
+	// 发送HTTP请求获取文件
+	resp, err := resty.New().R().Get(url)
+	if err != nil {
+		return "", err
+	}
+
+	// 获取当前日期
+	currentTime := time.Now()
+	datePath := filepath.Join("./resource/static/"+fileType, currentTime.Format("2006-01-02"))
+	// 创建目录
+	if err := os.MkdirAll(datePath, os.ModePerm); err != nil {
+		return "", err
+	}
+
+	// 生成唯一的文件名
+	fileName := uuid.New().String() + "." + suffix
+	filePath := filepath.Join(datePath, fileName)
+
+	// 创建文件
+	file, err := os.Create(filePath)
+	if err != nil {
+		return "", err
+	}
+	defer file.Close()
+	// 将HTTP响应的Body复制到文件
+	_, err = io.Copy(file, bytes.NewBuffer(resp.Body()))
+	if err != nil {
+		return "", err
+	}
+	currentDir, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+	filePath = currentDir + "/" + filePath
+	filePath = strings.Replace(filePath, "\\", "/", -1)
+	return filePath, nil
+}

+ 261 - 0
client/chat/chat.pb.go

@@ -0,0 +1,261 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.36.6
+// 	protoc        v3.15.1
+// source: proto/chat.proto
+
+package chat
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+	unsafe "unsafe"
+)
+
+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)
+)
+
+type JoinRequest struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *JoinRequest) Reset() {
+	*x = JoinRequest{}
+	mi := &file_proto_chat_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *JoinRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JoinRequest) ProtoMessage() {}
+
+func (x *JoinRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_chat_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use JoinRequest.ProtoReflect.Descriptor instead.
+func (*JoinRequest) Descriptor() ([]byte, []int) {
+	return file_proto_chat_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *JoinRequest) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+type Message struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+	Text          string                 `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"`
+	Timestamp     int64                  `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Action        string                 `protobuf:"bytes,4,opt,name=action,proto3" json:"action,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *Message) Reset() {
+	*x = Message{}
+	mi := &file_proto_chat_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *Message) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Message) ProtoMessage() {}
+
+func (x *Message) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_chat_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Message.ProtoReflect.Descriptor instead.
+func (*Message) Descriptor() ([]byte, []int) {
+	return file_proto_chat_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Message) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+func (x *Message) GetText() string {
+	if x != nil {
+		return x.Text
+	}
+	return ""
+}
+
+func (x *Message) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *Message) GetAction() string {
+	if x != nil {
+		return x.Action
+	}
+	return ""
+}
+
+type MessageAck struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Success       bool                   `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
+	MessageId     string                 `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *MessageAck) Reset() {
+	*x = MessageAck{}
+	mi := &file_proto_chat_proto_msgTypes[2]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *MessageAck) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MessageAck) ProtoMessage() {}
+
+func (x *MessageAck) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_chat_proto_msgTypes[2]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MessageAck.ProtoReflect.Descriptor instead.
+func (*MessageAck) Descriptor() ([]byte, []int) {
+	return file_proto_chat_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *MessageAck) GetSuccess() bool {
+	if x != nil {
+		return x.Success
+	}
+	return false
+}
+
+func (x *MessageAck) GetMessageId() string {
+	if x != nil {
+		return x.MessageId
+	}
+	return ""
+}
+
+var File_proto_chat_proto protoreflect.FileDescriptor
+
+const file_proto_chat_proto_rawDesc = "" +
+	"\n" +
+	"\x10proto/chat.proto\x12\x04chat\"&\n" +
+	"\vJoinRequest\x12\x17\n" +
+	"\auser_id\x18\x01 \x01(\tR\x06userId\"l\n" +
+	"\aMessage\x12\x17\n" +
+	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" +
+	"\x04text\x18\x02 \x01(\tR\x04text\x12\x1c\n" +
+	"\ttimestamp\x18\x03 \x01(\x03R\ttimestamp\x12\x16\n" +
+	"\x06action\x18\x04 \x01(\tR\x06action\"E\n" +
+	"\n" +
+	"MessageAck\x12\x18\n" +
+	"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x1d\n" +
+	"\n" +
+	"message_id\x18\x02 \x01(\tR\tmessageId2m\n" +
+	"\vChatService\x12.\n" +
+	"\bJoinChat\x12\x11.chat.JoinRequest\x1a\r.chat.Message0\x01\x12.\n" +
+	"\vSendMessage\x12\r.chat.Message\x1a\x10.chat.MessageAckB\rZ\v./chat;chatb\x06proto3"
+
+var (
+	file_proto_chat_proto_rawDescOnce sync.Once
+	file_proto_chat_proto_rawDescData []byte
+)
+
+func file_proto_chat_proto_rawDescGZIP() []byte {
+	file_proto_chat_proto_rawDescOnce.Do(func() {
+		file_proto_chat_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_chat_proto_rawDesc), len(file_proto_chat_proto_rawDesc)))
+	})
+	return file_proto_chat_proto_rawDescData
+}
+
+var file_proto_chat_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_proto_chat_proto_goTypes = []any{
+	(*JoinRequest)(nil), // 0: chat.JoinRequest
+	(*Message)(nil),     // 1: chat.Message
+	(*MessageAck)(nil),  // 2: chat.MessageAck
+}
+var file_proto_chat_proto_depIdxs = []int32{
+	0, // 0: chat.ChatService.JoinChat:input_type -> chat.JoinRequest
+	1, // 1: chat.ChatService.SendMessage:input_type -> chat.Message
+	1, // 2: chat.ChatService.JoinChat:output_type -> chat.Message
+	2, // 3: chat.ChatService.SendMessage:output_type -> chat.MessageAck
+	2, // [2:4] is the sub-list for method output_type
+	0, // [0:2] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_proto_chat_proto_init() }
+func file_proto_chat_proto_init() {
+	if File_proto_chat_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_chat_proto_rawDesc), len(file_proto_chat_proto_rawDesc)),
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_proto_chat_proto_goTypes,
+		DependencyIndexes: file_proto_chat_proto_depIdxs,
+		MessageInfos:      file_proto_chat_proto_msgTypes,
+	}.Build()
+	File_proto_chat_proto = out.File
+	file_proto_chat_proto_goTypes = nil
+	file_proto_chat_proto_depIdxs = nil
+}

+ 163 - 0
client/chat/chat_grpc.pb.go

@@ -0,0 +1,163 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v3.15.1
+// source: proto/chat.proto
+
+package chat
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+	ChatService_JoinChat_FullMethodName    = "/chat.ChatService/JoinChat"
+	ChatService_SendMessage_FullMethodName = "/chat.ChatService/SendMessage"
+)
+
+// ChatServiceClient is the client API for ChatService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type ChatServiceClient interface {
+	JoinChat(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Message], error)
+	SendMessage(ctx context.Context, in *Message, opts ...grpc.CallOption) (*MessageAck, error)
+}
+
+type chatServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewChatServiceClient(cc grpc.ClientConnInterface) ChatServiceClient {
+	return &chatServiceClient{cc}
+}
+
+func (c *chatServiceClient) JoinChat(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Message], error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	stream, err := c.cc.NewStream(ctx, &ChatService_ServiceDesc.Streams[0], ChatService_JoinChat_FullMethodName, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &grpc.GenericClientStream[JoinRequest, Message]{ClientStream: stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type ChatService_JoinChatClient = grpc.ServerStreamingClient[Message]
+
+func (c *chatServiceClient) SendMessage(ctx context.Context, in *Message, opts ...grpc.CallOption) (*MessageAck, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(MessageAck)
+	err := c.cc.Invoke(ctx, ChatService_SendMessage_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// ChatServiceServer is the server API for ChatService service.
+// All implementations must embed UnimplementedChatServiceServer
+// for forward compatibility.
+type ChatServiceServer interface {
+	JoinChat(*JoinRequest, grpc.ServerStreamingServer[Message]) error
+	SendMessage(context.Context, *Message) (*MessageAck, error)
+	mustEmbedUnimplementedChatServiceServer()
+}
+
+// UnimplementedChatServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedChatServiceServer struct{}
+
+func (UnimplementedChatServiceServer) JoinChat(*JoinRequest, grpc.ServerStreamingServer[Message]) error {
+	return status.Errorf(codes.Unimplemented, "method JoinChat not implemented")
+}
+func (UnimplementedChatServiceServer) SendMessage(context.Context, *Message) (*MessageAck, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SendMessage not implemented")
+}
+func (UnimplementedChatServiceServer) mustEmbedUnimplementedChatServiceServer() {}
+func (UnimplementedChatServiceServer) testEmbeddedByValue()                     {}
+
+// UnsafeChatServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ChatServiceServer will
+// result in compilation errors.
+type UnsafeChatServiceServer interface {
+	mustEmbedUnimplementedChatServiceServer()
+}
+
+func RegisterChatServiceServer(s grpc.ServiceRegistrar, srv ChatServiceServer) {
+	// If the following call pancis, it indicates UnimplementedChatServiceServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
+	s.RegisterService(&ChatService_ServiceDesc, srv)
+}
+
+func _ChatService_JoinChat_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(JoinRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(ChatServiceServer).JoinChat(m, &grpc.GenericServerStream[JoinRequest, Message]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type ChatService_JoinChatServer = grpc.ServerStreamingServer[Message]
+
+func _ChatService_SendMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Message)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ChatServiceServer).SendMessage(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: ChatService_SendMessage_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ChatServiceServer).SendMessage(ctx, req.(*Message))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// ChatService_ServiceDesc is the grpc.ServiceDesc for ChatService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var ChatService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "chat.ChatService",
+	HandlerType: (*ChatServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "SendMessage",
+			Handler:    _ChatService_SendMessage_Handler,
+		},
+	},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "JoinChat",
+			Handler:       _ChatService_JoinChat_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "proto/chat.proto",
+}

+ 4 - 0
client/config.json

@@ -0,0 +1,4 @@
+{
+  "serviceAddress": "localhost:50051",
+  "informationDelay": 5
+}

+ 1 - 0
client/config.yaml

@@ -0,0 +1 @@
+serviceAddress: "localhost:50051"

+ 23 - 0
client/config/config.go

@@ -0,0 +1,23 @@
+package config
+
+import (
+	"app.yhyue.com/moapp/jybase/common"
+	"flag"
+)
+
+type Config struct {
+	ServiceAddress   string `json:"serviceAddress"`
+	InformationDelay int    `json:"informationDelay"`
+}
+
+var (
+	cf         = flag.String("cf", "./config.yaml", "配置文件")
+	Cfg        = new(Config)
+	SystemWxId string
+)
+
+// init
+func init() {
+	flag.Parse()
+	common.ReadConfig(&Cfg)
+}

+ 49 - 0
client/go.mod

@@ -0,0 +1,49 @@
+module client
+
+go 1.23.7
+
+require (
+	app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b
+	github.com/danbai225/go-logs v0.3.2
+	github.com/go-resty/resty/v2 v2.16.5
+	github.com/go-xweb/xweb v0.2.1
+	github.com/gogf/gf/v2 v2.9.0
+	github.com/google/uuid v1.6.0
+	github.com/robfig/cron v1.2.0
+	go.nanomsg.org/mangos/v3 v3.4.2
+	google.golang.org/grpc v1.73.0
+	google.golang.org/protobuf v1.36.6
+)
+
+require (
+	github.com/Microsoft/go-winio v0.5.2 // indirect
+	github.com/emirpasic/gods v1.18.1 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/go-stack/stack v1.8.0 // indirect
+	github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6 // indirect
+	github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e // indirect
+	github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/golang/snappy v0.0.1 // indirect
+	github.com/gorilla/websocket v1.5.3 // indirect
+	github.com/howeyc/fsnotify v0.9.0 // indirect
+	github.com/klauspost/compress v1.13.6 // indirect
+	github.com/kpango/fastime v1.1.9 // indirect
+	github.com/kpango/glg v1.6.15 // indirect
+	github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd // indirect
+	github.com/mattn/go-sqlite3 v1.14.28 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.0.2 // indirect
+	github.com/xdg-go/stringprep v1.0.2 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+	go.mongodb.org/mongo-driver v1.9.1 // indirect
+	go.opentelemetry.io/otel v1.35.0 // indirect
+	go.opentelemetry.io/otel/trace v1.35.0 // indirect
+	golang.org/x/crypto v0.36.0 // indirect
+	golang.org/x/net v0.38.0 // indirect
+	golang.org/x/sync v0.12.0 // indirect
+	golang.org/x/sys v0.31.0 // indirect
+	golang.org/x/text v0.23.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
+)

+ 194 - 0
client/go.sum

@@ -0,0 +1,194 @@
+app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b h1:bQKiTTrlbDIc+Z2y7ynbZrieaaAUB2CisE/rl42FlPY=
+app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b/go.mod h1:OEtMbsn7wY/7MLgV7yDUpVDKExUoj3B8h+4w4ZckJQQ=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
+github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
+github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/danbai225/go-logs v0.3.2 h1:CXMudhrC9rj5hb3tyZvn2APZStioB68QZauZeBPFkmU=
+github.com/danbai225/go-logs v0.3.2/go.mod h1:hHxvTTAIkZ3a6XRksnN50gxkqGIlQ1XkNl2U//2erH0=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+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/gdamore/optopia v0.2.0/go.mod h1:YKYEwo5C1Pa617H7NlPcmQXl+vG6YnSSNB44n8dNL0Q=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
+github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
+github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6 h1:DUiWdm3rS8cC96Y0XeVkDeizzEK6X+qiNgXytLVtbkM=
+github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6/go.mod h1:lwPk13GS+i/NK4FkMm68IcJrAwiu+HtjYa1Y4kW59aY=
+github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e h1:xmffs7hgrWpAOcquZrdlWpAEaAdlI9myaYcUUmhIP7k=
+github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e/go.mod h1:ASmYUSBf32lWkkNVX/pnOU4MLuUQpFH4qYHvWHt/l0w=
+github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 h1:D46USD6oGNWzoJ/h5CWaFq3ELLoLoJzllJ03Xh78VYg=
+github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851/go.mod h1:OmDEC58ZYO1Esk+Uy32SB6LWof9lyROl7q76dBFOCWw=
+github.com/go-xweb/xweb v0.2.1 h1:u5t/ttuSfxiIMDTXj/Pouw9C2ASNABWT16JWHyrtdvY=
+github.com/go-xweb/xweb v0.2.1/go.mod h1:vPjYJgfidYAgBKIwiAyKFC1hfczlqsw9rRT8LtwrGew=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gogf/gf/v2 v2.9.0 h1:semN5Q5qGjDQEv4620VzxcJzJlSD07gmyJ9Sy9zfbHk=
+github.com/gogf/gf/v2 v2.9.0/go.mod h1:sWGQw+pLILtuHmbOxoe0D+0DdaXxbleT57axOLH2vKI=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
+github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
+github.com/howeyc/fsnotify v0.9.0 h1:0gtV5JmOKH4A8SsFxG2BczSeXWWPvcMT0euZt5gDAxY=
+github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
+github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/kpango/fastime v1.1.9 h1:xVQHcqyPt5M69DyFH7g1EPRns1YQNap9d5eLhl/Jy84=
+github.com/kpango/fastime v1.1.9/go.mod h1:vyD7FnUn08zxY4b/QFBZVG+9EWMYsNl+QF0uE46urD4=
+github.com/kpango/glg v1.6.15 h1:nw0xSxpSyrDIWHeb3dvnE08PW+SCbK+aYFETT75IeLA=
+github.com/kpango/glg v1.6.15/go.mod h1:cmsc7Yeu8AS3wHLmN7bhwENXOpxfq+QoqxCIk2FneRk=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd h1:DXxmBCahjva4Ox4AWOv6pR1Csv33zSj97SaLOElfIsw=
+github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd/go.mod h1:3w9PScemEkJoLw3OYvLWMoD8XRCmXgGwsSpT6pFpJ0g=
+github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
+github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
+github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
+github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c=
+go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.nanomsg.org/mangos/v3 v3.4.2 h1:gHlopxjWvJcVCcUilQIsRQk9jdj6/HB7wrTiUN8Ki7Q=
+go.nanomsg.org/mangos/v3 v3.4.2/go.mod h1:8+hjBMQub6HvXmuGvIq6hf19uxGQIjCofmc62lbedLA=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
+go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
+go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
+go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
+go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
+golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
+golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
+golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
+golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
+golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
+golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
+google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 73 - 0
client/log.go

@@ -0,0 +1,73 @@
+package main
+
+/**
+日志文件自动切换,默认保留15天内日志
+**/
+
+import (
+	"log"
+	"os"
+	"path/filepath"
+	"regexp"
+	"time"
+
+	"github.com/go-xweb/xweb"
+	"github.com/robfig/cron"
+)
+
+// 日志格式
+var fileReg = regexp.MustCompile("^(\\d{4}_[0-9_]{14})\\.log$")
+
+// 当前日志文件句柄
+var LogFile *os.File
+
+// 时间格式
+var FMT = "2006_01_02_15_04_05"
+
+// 日志目录
+var LogPath = "./jylog"
+
+func init() {
+	os.Mkdir(LogPath, os.ModePerm)
+	//默认保留15天内的日志,-1为永久保留
+	initLog(15)
+}
+
+func initLog(saveDay int) {
+	go logfile()
+	task := cron.New()
+	task.Start()
+	task.AddFunc("0 0 0/10 * * ?", func() {
+		go logfile()
+		time.Sleep(50 * time.Second)
+		if saveDay > 0 {
+			filepath.Walk(LogPath, func(path string, info os.FileInfo, err error) error {
+				str := fileReg.FindStringSubmatch(info.Name())
+				if len(str) == 2 {
+					t, er := time.ParseInLocation(FMT, str[1], time.Local)
+					if er == nil {
+						if (time.Now().Unix()-t.Unix())/86400 > int64(saveDay) {
+							log.Println("delete log file:", path, os.Remove(path))
+						}
+					}
+				}
+				return nil
+			})
+		}
+	})
+}
+
+// 创建并切换输出文件
+func logfile() {
+	now := time.Now().Format(FMT)
+	file, _ := os.Create(LogPath + "/" + now + ".log")
+	log.SetOutput(file)
+	xweb.RootApp().Logger.SetOutput(file)
+	go func(file *os.File) {
+		time.Sleep(5 * time.Second)
+		if LogFile != nil {
+			LogFile.Close()
+		}
+		LogFile = file
+	}(file)
+}

+ 11 - 0
client/main.go

@@ -0,0 +1,11 @@
+package main
+
+import (
+	"client/service"
+)
+
+func main() {
+	service.InitSendTalkWorkers()
+	service.WxHandle()
+	select {}
+}

+ 28 - 0
client/proto/chat.proto

@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+package chat;
+
+option go_package = "./chat;chat";
+
+service ChatService {
+  rpc JoinChat(JoinRequest) returns (stream Message);
+  rpc SendMessage(Message) returns (MessageAck);}
+
+message JoinRequest {
+  string user_id = 1;
+}
+
+message Message {
+  string user_id = 1;
+  string text = 2;
+  int64 timestamp = 3;
+  string   action =4;
+}
+
+message MessageAck {
+  bool success = 1;
+  string message_id = 2;
+}
+
+
+

二進制
client/sdk.dll


+ 286 - 0
client/service/commandHandle.go

@@ -0,0 +1,286 @@
+package service
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"sync"
+	"time"
+
+	. "client/chat"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+	"google.golang.org/grpc/keepalive"
+)
+
+const (
+	reconnectInterval = 60 * time.Second
+	timeInt64         = 60 * time.Second
+	TimeoutInt64      = 20 * time.Second
+
+	maxRetryCount = 60
+)
+
+type Config struct {
+	ServerAddress     string
+	ReconnectInterval time.Duration
+	MaxRetryCount     int
+}
+
+var client = &ChatClient{}
+
+type ChatClient struct {
+	conn           *grpc.ClientConn
+	client         ChatServiceClient
+	ctx            context.Context
+	cancel         context.CancelFunc
+	userID         string
+	mu             sync.Mutex
+	retryCount     int
+	isConnected    bool
+	wg             sync.WaitGroup
+	reconnecting   bool
+	ServiceAddress string
+}
+
+func NewChatClient(userID, address string) *ChatClient {
+	ctx, cancel := context.WithCancel(context.Background())
+	return &ChatClient{
+		userID:         userID,
+		ctx:            ctx,
+		cancel:         cancel,
+		ServiceAddress: address,
+	}
+}
+
+// grpc连接
+func (c *ChatClient) connect() error {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if c.isConnected {
+		return nil
+	}
+
+	log.Println("[连接] 尝试连接服务器...")
+	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
+	defer cancel()
+
+	conn, err := grpc.DialContext(ctx, "localhost:50051",
+		grpc.WithTransportCredentials(insecure.NewCredentials()),
+		grpc.WithBlock(),
+		grpc.WithKeepaliveParams(keepalive.ClientParameters{
+			Time:                timeInt64,
+			Timeout:             TimeoutInt64,
+			PermitWithoutStream: true,
+		}))
+	if err != nil {
+		return fmt.Errorf("连接失败: %v", err)
+	}
+
+	// 测试连接
+	testClient := NewChatServiceClient(conn)
+	_, err = testClient.JoinChat(context.Background(), &JoinRequest{UserId: c.userID})
+	if err != nil {
+		_ = conn.Close()
+		return fmt.Errorf("连接测试失败: %v", err)
+	}
+
+	c.conn = conn
+	c.client = testClient
+	c.isConnected = true
+	c.retryCount = 0
+	log.Println("[连接] 服务器连接成功")
+	return nil
+}
+
+func (c *ChatClient) disconnect() {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if !c.isConnected {
+		return
+	}
+
+	if c.conn != nil {
+		if err := c.conn.Close(); err != nil {
+			log.Println("[连接] 关闭连接出错: %v", err)
+		}
+		c.conn = nil
+	}
+	c.isConnected = false
+	log.Println("[连接] 已断开连接")
+}
+
+func (c *ChatClient) reconnect() {
+	// 初始加锁检查重连状态
+	c.mu.Lock()
+	if c.reconnecting {
+		c.mu.Unlock()
+		return
+	}
+	c.reconnecting = true
+	currentRetry := c.retryCount
+	c.mu.Unlock()
+
+	// 确保最终重置重连状态
+	defer func() {
+		c.mu.Lock()
+		c.reconnecting = false
+		c.mu.Unlock()
+	}()
+
+	// 等待组管理
+	c.wg.Add(1)
+	defer c.wg.Done()
+
+	log.Println("[重连] 开始重连流程,当前重试计数: %d", currentRetry)
+
+	for {
+		select {
+		case <-c.ctx.Done():
+			log.Println("[重连] 上下文取消,停止重连")
+			return
+		default:
+			// 检查最大重试次数
+			c.mu.Lock()
+			if c.retryCount >= maxRetryCount {
+				log.Println("[重连] 达到最大重试次数(%d),停止重连", maxRetryCount)
+				c.mu.Unlock()
+				return
+			}
+			c.mu.Unlock()
+			// 尝试连接
+			log.Println("[重连] 尝试第%d次连接...", currentRetry+1)
+			if err := c.connect(); err != nil {
+				log.Println("[重连] 连接失败: %v", err)
+
+				// 更新重试计数
+				c.mu.Lock()
+				c.retryCount++
+				currentRetry = c.retryCount
+				c.mu.Unlock()
+				log.Println("[重连] 等待%d秒后重试...", int(reconnectInterval.Seconds()))
+				select {
+				case <-time.After(reconnectInterval):
+					continue
+				case <-c.ctx.Done():
+					log.Println("[重连] 等待期间上下文取消")
+					return
+				}
+			} else {
+				log.Println("[重连] 连接成功!")
+				go c.receiveMessages()
+				return
+			}
+		}
+	}
+}
+
+// 接收信息处理
+// 修改后的 receiveMessages 方法
+func (c *ChatClient) receiveMessages() {
+	c.wg.Add(1)
+	defer c.wg.Done()
+
+	for {
+		select {
+		case <-c.ctx.Done():
+			return
+		default:
+			c.mu.Lock()
+			connected := c.isConnected
+			c.mu.Unlock()
+
+			if !connected {
+				time.Sleep(1 * time.Second)
+				continue
+			}
+
+			// 加锁保护整个连接过程
+
+			c.mu.Lock()
+			stream, err := c.client.JoinChat(c.ctx, &JoinRequest{UserId: c.userID})
+			c.mu.Unlock()
+
+			if err != nil {
+				log.Println(fmt.Sprintf("[接收] 加入聊天室失败: %v", err))
+				c.disconnect()
+				go c.reconnect()
+				return
+			}
+
+			// 消息接收循环
+			for {
+				msg, err := stream.Recv()
+				if err != nil {
+					log.Println(fmt.Sprintf("[接收] 接收消息错误: %v\n", err))
+					c.disconnect()
+					go c.reconnect()
+					return
+				}
+				// ... 处理消息逻辑 ...
+				log.Println("[接收] 收到消息: ", msg)
+				if msg.UserId == "系统" {
+					switch msg.Action {
+					case "sendTalk":
+						go SendTalk(msg.Text)
+					case "getContacts":
+						go GetContacts()
+					case "reject":
+						go Reject(msg.Text)
+					default:
+						//后期删除掉
+						if msg.Text == "欢迎加入聊天室!" {
+							log.Println("发送固定信息", client.SendMessage("", "sendTalk"))
+						}
+						log.Println(fmt.Sprintf("[系统通知]: %s ", msg.Text))
+					}
+				}
+			}
+		}
+	}
+}
+
+// SendMessage 发送单条消息
+func (c *ChatClient) SendMessage(text string, action string) error {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	if !c.isConnected {
+		return fmt.Errorf("未连接服务器")
+	}
+	msg := &Message{
+		UserId: c.userID,
+		Text:   text,
+		Action: action,
+	}
+	log.Println(fmt.Sprintf("[发送] 发送消息 : %+v\n", msg))
+	_, err := c.client.SendMessage(c.ctx, msg)
+	if err != nil {
+		log.Println(fmt.Sprintf("[发送] 发送失败: %v", err))
+		c.disconnect()
+		go c.reconnect()
+		return err
+	}
+	return nil
+}
+
+func ConnectGRPC(userId string, address string) {
+	log.Println("[主程序] 启动GRPC连接")
+	client = NewChatClient(userId, address)
+	defer func() {
+		client.cancel()
+		client.disconnect()
+		client.wg.Wait()
+		log.Println("[主程序] 客户端安全退出")
+	}()
+
+	// 初始连接
+	if err := client.connect(); err != nil {
+		log.Println(fmt.Sprintf("[主程序] 初始连接失败: %v", err))
+		go client.reconnect()
+	} else {
+		go client.receiveMessages()
+	}
+	select {}
+}

+ 456 - 0
client/service/wx.go

@@ -0,0 +1,456 @@
+package service
+
+import "C"
+import (
+	"client/app"
+	"client/config"
+	"client/wcf"
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/util/gconv"
+	"io"
+	"log"
+	"math/rand"
+	"net/http"
+	"net/url"
+	"os"
+	"os/exec"
+	"os/signal"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+)
+
+// 全局变量定义
+// 全局消息队列和工作池
+
+var (
+	cache         = new(sync.Map)             // 线程安全的缓存,用于存储对话历史等数据
+	sendTalkQueue = make(chan SendTask, 1000) // 缓冲队列
+	workerPool    = make(chan struct{}, 5)    // 并发控制
+)
+
+const (
+	ContentTypeText  string = "0" // 文字
+	ContentTypeImage string = "1" // 图片
+	ContentTypeLink  string = "2" // 链接
+	ContentTypeVideo string = "3" // 视频
+	ContentTypeFile  string = "4" // 文件
+)
+
+// init 初始化函数,在包被加载时自动执行
+func init() {
+	// 初始化微信客户端连接
+	app.WechatFerryInit()
+	// 验证连接状态,如果连接失败则终止程序
+	if app.WxClient == nil || !app.WxClient.IsLogin() {
+		log.Fatal("无法连接到微信客户端")
+	}
+	// 确保全局变量一致
+	rand.Seed(time.Now().UnixNano())
+}
+
+// OnMsg消息回调
+func OnMsg(ctx context.Context) {
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		default:
+			err := app.WxClient.OnMSG(func(msg *wcf.WxMsg) {
+				processMsg(msg)
+			})
+			if err != nil {
+				log.Println(err.Error())
+			}
+			time.Sleep(time.Second)
+		}
+
+	}
+}
+
+// processMsg 处理单条微信消息的核心函数
+// msg: 包含微信消息所有信息的结构体
+func processMsg(msg *wcf.WxMsg) {
+	// 结构化日志输出消息详情
+	fmt.Printf(`
+		[消息详情]
+		是否来自自己: %v
+		是否是群消息: %v
+		消息类型: %d
+		时间戳: %d
+		消息ID: %d
+		房间ID: %s
+		发送人: %s
+		消息内容: %s
+		`,
+		msg.IsSelf, msg.IsGroup,
+		msg.Type, msg.Ts,
+		msg.Id, msg.Roomid, msg.Sender,
+		msg.Content,
+	)
+	if !msg.IsGroup && msg.Type == 1 {
+		returnData := map[string]interface{}{
+			"roomid":  msg.Roomid,
+			"IsSelf":  msg.IsSelf,
+			"content": msg.Content,
+		}
+		if err := client.SendMessage(gconv.String(returnData), "chatHistory"); err != nil {
+			log.Println(fmt.Sprintf("发送消息到客户端失败: %v", msg.Content))
+		}
+	}
+}
+
+// WxHandle 微信消息处理入口函数
+func WxHandle() {
+	ctx, cancleFn := context.WithCancel(context.Background())
+	log.Println(ctx)
+	// 注册Ctrl+C信号处理函数
+	signalChan := make(chan os.Signal, 1)
+	signal.Notify(signalChan, os.Interrupt)
+	// 开启微信消息接收功能
+	go func() {
+		<-signalChan
+		cancleFn()
+		log.Println("感谢温柔的ctrl+c关闭,下次可直接运行程序,无需重启微信。")
+		app.WxClient.Close()
+		//强制杀死微信进程
+		killWeChat()
+		os.Exit(0)
+	}()
+	ret := app.WxClient.EnableRecvTxt()
+	log.Println("开启接收消息状态:", ret)
+	systemWxId := app.WxClient.GetSelfWXID()
+	go OnMsg(ctx)
+	go ConnectGRPC(systemWxId, config.Cfg.ServiceAddress)
+	select {}
+}
+
+// GetContacts 获取并处理通讯录联系人列表
+func GetContacts() {
+	// 创建联系人数据切片
+	returnData := []map[string]interface{}{}
+	log.Println(app.WxClient.GetContacts())
+	// 遍历所有联系人
+	for _, c := range app.WxClient.GetContacts() {
+		// 过滤不需要的联系人类型:
+		// 1. 以@openim结尾的联系人(可能是系统账号)
+		// 2. 备注为空的联系人
+		isavailable, phone := userJudge(c)
+		if !isavailable {
+			continue
+		}
+		// 构建联系人信息字典
+		returnData = append(returnData, map[string]interface{}{
+			"name":   c.Name,   // 联系人昵称
+			"code":   c.Code,   // 联系人编码
+			"wxid":   c.Wxid,   // 微信ID
+			"remark": c.Remark, // 备注名
+			"phone":  phone,
+		})
+	}
+	// 将联系人列表发送给客户端
+	if err := client.SendMessage(gconv.String(returnData), "getContacts"); err != nil {
+		log.Println(fmt.Sprintf("发送联系人数据失败: %v", err))
+	}
+}
+
+// 备注解析
+func extractPhoneNumber(input string) (string, error) {
+	// 正则表达式匹配以字母开头、结尾为手机号的字符串
+	re := regexp.MustCompile(`^[A-Za-z].*?(\d{11})$`)
+	matches := re.FindStringSubmatch(input)
+	if len(matches) < 2 {
+		return "", fmt.Errorf("未找到匹配的手机号")
+	}
+
+	return matches[1], nil
+}
+
+// killWeChat 关闭微信
+func killWeChat() {
+	// 根据操作系统选择不同的命令
+	var cmd *exec.Cmd
+	// 在Windows上使用taskkill命令
+	cmd = exec.Command("taskkill", "/F", "/IM", "WeChat.exe")
+	// 执行命令
+	err := cmd.Run()
+	if err != nil {
+		log.Println("Error killing process:", err)
+		return
+	}
+	log.Println("wechat process killed successfully.")
+}
+
+// 用户身份判断
+func userJudge(c *wcf.RpcContact) (bool, string) {
+	phone, err := extractPhoneNumber(c.Remark)
+	if strings.HasSuffix(c.Wxid, "@openim") || c.Remark == "" || phone == "" || c.Name == "语音记事本" || c.Name == "文件传输助手" {
+		if phone == "" {
+			log.Println("手机号:", err)
+		}
+		return false, ""
+	}
+	return true, phone
+}
+
+// 发送文本信息
+func sendText(content, wxId string) (bool, error) {
+	if app.WxClient.SendTxt(content, wxId, nil) != 0 {
+		return false, fmt.Errorf(fmt.Sprintf("%s%s文字消息发送失败", content, wxId))
+	}
+	return true, nil
+}
+
+// 发送图片信息
+func sendImage(url, wxId, taskId string) (bool, error) {
+	imgPath, err := DownloadImage(url, "img", taskId)
+	if err != nil {
+		return false, fmt.Errorf("%s%s下载图片失败: %w", err)
+	}
+	if app.WxClient.SendIMG(imgPath, wxId) != 0 {
+		return false, fmt.Errorf("%s%s图片发送失败", url, wxId)
+	}
+	return true, nil
+}
+
+// 发送视频信息
+func sendVideo(url, wxId, taskId string) (bool, error) {
+	videoPath, err := DownloadImage(url, "video", taskId)
+	if err != nil {
+		return false, fmt.Errorf("%s%s下载视频失败: %w", err)
+	}
+	if app.WxClient.SendIMG(videoPath, wxId) != 0 {
+		return false, fmt.Errorf("%s%s视频发送失败", url, wxId)
+	}
+	return true, nil
+}
+
+// 发送文件信息
+func sendFile(url, wxId, taskId string) (bool, error) {
+	filePath, err := DownloadImage(url, "file", taskId)
+	if err != nil {
+		return false, fmt.Errorf("%s%s下载文件失败: %w", url, wxId, err)
+	}
+	if app.WxClient.SendFile(filePath, wxId) != 0 {
+		return false, fmt.Errorf("%s%s文件发送失败", url, wxId)
+	}
+	return true, nil
+}
+
+// 发送任务结构
+type SendTask struct {
+	DataStr    string
+	RetryCount int
+}
+
+// 初始化工作池
+func InitSendTalkWorkers() {
+	for i := 0; i < cap(workerPool); i++ {
+		go sendTalkWorker()
+	}
+}
+
+// 工作goroutine
+func sendTalkWorker() {
+	for task := range sendTalkQueue {
+		workerPool <- struct{}{} // 获取令牌
+		processSendTask(task)    // 直接处理任务
+		<-workerPool             // 释放令牌
+	}
+}
+
+// 处理单个任务
+func processSendTask(task SendTask) {
+	defer func() {
+		if r := recover(); r != nil {
+			log.Printf("SendTask panic: %v", r)
+		}
+	}()
+
+	if task.RetryCount > 3 {
+		log.Printf("放弃重试 taskId=%s", gconv.Map(task.DataStr)["taskId"])
+		return
+	}
+	aaa := rand.Intn(config.Cfg.InformationDelay*1000) + 5000
+	time.Sleep(time.Duration(aaa) * time.Millisecond)
+	if err := doSendTalk(task.DataStr); err != nil {
+		log.Printf("发送失败 (重试 %d/3): %v", task.RetryCount, err)
+		time.Sleep(time.Second * time.Duration(task.RetryCount))
+		sendTalkQueue <- SendTask{task.DataStr, task.RetryCount + 1}
+	}
+}
+
+// 异步入队
+func SendTalk(dataStr string) {
+	task := SendTask{
+		DataStr:    dataStr,
+		RetryCount: 0,
+	}
+	select {
+	case sendTalkQueue <- task: // 入队成功
+		log.Printf("任务已入队 taskId=%s", gconv.Map(dataStr)["taskId"])
+	default:
+		// 队列满时降级处理
+		log.Println("警告:发送队列已满,降级同步处理")
+		processSendTask(task)
+	}
+}
+
+// 实际发送逻辑
+func doSendTalk(dataStr string) error {
+	dataMap := gconv.Map(dataStr)
+	taskId := gconv.String(dataMap["taskId"])
+	userMap := gconv.Map(dataMap["user"])
+	replyLanguage := gconv.String(dataMap["replyLanguage"])
+	wxId := gconv.String(userMap["wxid"])
+	batchCode := gconv.String(dataMap["batchCode"])
+	baseUserId := gconv.String(userMap["baseUserId"])
+	ok := true
+	// 处理内容发送
+	for _, v := range gconv.Maps(dataMap["content"]) {
+		content := gconv.String(v["content"])
+		switch gconv.String(v["content_type"]) {
+		case ContentTypeText, ContentTypeLink:
+			ok1, err := sendText(content, wxId)
+			if !ok1 {
+				ok = false
+				log.Println(err)
+			}
+		case ContentTypeImage:
+			ok1, err := sendImage(content, wxId, taskId)
+			if !ok1 {
+				ok = false
+				log.Println(err)
+			}
+		case ContentTypeVideo:
+			ok1, err := sendVideo(content, wxId, taskId)
+			if !ok1 {
+				ok = false
+				log.Println(err)
+			}
+		case ContentTypeFile:
+			ok1, err := sendFile(content, wxId, taskId)
+			if !ok1 {
+				ok = false
+				log.Println(err)
+			}
+		}
+	}
+	if replyLanguage != "" {
+		ok1, err := sendText(replyLanguage, wxId)
+		if !ok1 {
+			ok = false
+			log.Println(err)
+		}
+	}
+	//
+	// 发送回执
+	returnData := map[string]interface{}{
+		"taskId":       taskId,
+		"batch_code":   batchCode,
+		"base_user_id": baseUserId,
+		"isSuccess":    ok,
+	}
+	return client.SendMessage(gconv.String(returnData), "sendTalkReceipt")
+}
+
+// 拒绝接受回执
+func Reject(text string) {
+	// 1. 解析输入数据
+	dataMap := gconv.Map(text)
+	if dataMap == nil {
+		log.Println("无效的输入数据")
+		return
+	}
+
+	contentMap := gconv.Map(dataMap["content"])
+	if contentMap == nil {
+		log.Println("无效的内容数据")
+		return
+	}
+
+	wxId := gconv.String(dataMap["wxId"])
+	if wxId == "" {
+		log.Println("缺少接收人wxId")
+		return
+	}
+	// 2. 获取内容和类型
+	contentType := gconv.String(contentMap["content_type"])
+	content := gconv.String(contentMap["content"])
+	if content == "" {
+		log.Println("消息内容为空")
+		return
+	}
+	switch contentType {
+	case ContentTypeText, ContentTypeLink:
+		sendText(content, wxId)
+	case ContentTypeImage:
+		sendImage(content, wxId, "A101")
+	case ContentTypeVideo:
+		sendVideo(content, wxId, "A101")
+	case ContentTypeFile:
+		sendFile(content, wxId, "A101")
+	}
+}
+
+// DownloadImage 下载图片、视频、文件并保存到指定格式的路径
+func DownloadImage(url, fileType string, taskId string) (string, error) {
+	// 1. 创建img目录结构 (格式: img/2025/07/04/)
+	dirPath := filepath.Join(fileType, time.Now().Format(time.DateOnly), taskId)
+	if err := os.MkdirAll(dirPath, 0755); err != nil {
+		return "", fmt.Errorf("创建目录失败: %v", err)
+	}
+	// 2. 获取文件扩展名
+	filename, _ := GetFullFilename(url)
+	// 3. 生成文件名 (如 task_id_1.jpg)
+	filePath := filepath.Join(dirPath, filename)
+	// 4. 下载文件
+	resp, err := http.Get(url)
+	if err != nil {
+		return "", fmt.Errorf("下载请求失败: %v", err)
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusOK {
+		return "", fmt.Errorf("下载失败,状态码: %d", resp.StatusCode)
+	}
+	// 5. 创建并保存文件
+	out, err := os.Create(filePath)
+	if err != nil {
+		return "", fmt.Errorf("创建文件失败: %v", err)
+	}
+	defer out.Close()
+
+	if _, err := io.Copy(out, resp.Body); err != nil {
+		return "", fmt.Errorf("写入文件失败: %v", err)
+	}
+	// 6. 获取绝对路径
+	absPath, err := filepath.Abs(filePath)
+	if err != nil {
+		return "", fmt.Errorf("获取绝对路径失败: %v", err)
+	}
+
+	return absPath, nil
+}
+
+// GetFullFilename 从URL中提取完整文件名(包含扩展名)
+func GetFullFilename(rawURL string) (string, error) {
+	// 解析URL(处理特殊字符和路径)
+	parsedURL, err := url.Parse(rawURL)
+	if err != nil {
+		return "", fmt.Errorf("URL解析失败: %w", err)
+	}
+
+	// 获取路径部分(去掉域名和查询参数)
+	path := parsedURL.Path
+
+	// 提取文件名(包含扩展名)
+	fullFilename := filepath.Base(path)
+
+	// 去掉URL查询参数(如 ?xxx=yyy)
+	filename := strings.Split(fullFilename, "?")[0]
+
+	return filename, nil
+}

二進制
client/spy.dll


二進制
client/spy_debug.dll


+ 606 - 0
client/wcf/wcf.go

@@ -0,0 +1,606 @@
+package wcf
+
+import (
+	logs "github.com/danbai225/go-logs"
+	"go.nanomsg.org/mangos/v3"
+	"go.nanomsg.org/mangos/v3/protocol"
+	"go.nanomsg.org/mangos/v3/protocol/pair1"
+	_ "go.nanomsg.org/mangos/v3/transport/all"
+	"google.golang.org/protobuf/proto"
+	"strconv"
+	"strings"
+)
+
+type Client struct {
+	add                string
+	socket             protocol.Socket
+	RecvTxt            bool
+	ContactsMap        []map[string]string
+	MessageCallbackUrl string
+}
+
+func (c *Client) conn() error {
+	socket, err := pair1.NewSocket()
+	if err != nil {
+		return err
+	}
+	err = socket.Dial(c.add)
+	if err != nil {
+		return err
+	}
+	c.socket = socket
+	return err
+}
+
+func (c *Client) send(data []byte) error {
+	return c.socket.Send(data)
+}
+
+func (c *Client) Recv() (*Response, error) {
+	msg := &Response{}
+	recv, err := c.socket.Recv()
+	if err != nil {
+		return msg, err
+	}
+	err = proto.Unmarshal(recv, msg)
+	return msg, err
+}
+
+// Close 退出
+func (c *Client) Close() error {
+	c.DisableRecvTxt()
+	return c.socket.Close()
+}
+
+// IsLogin 查看是否登录
+func (c *Client) IsLogin() bool {
+	err := c.send(genFunReq(Functions_FUNC_IS_LOGIN).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	if recv.GetStatus() == 1 {
+		return true
+	}
+	return false
+}
+
+// GetSelfWXID 获取登录的id
+func (c *Client) GetSelfWXID() string {
+	err := c.send(genFunReq(Functions_FUNC_GET_SELF_WXID).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStr()
+}
+
+// GetMsgTypes 获取消息类型
+func (c *Client) GetMsgTypes() map[int32]string {
+	err := c.send(genFunReq(Functions_FUNC_GET_MSG_TYPES).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetTypes().GetTypes()
+}
+
+// GetContacts 获取通讯录
+func (c *Client) GetContacts() []*RpcContact {
+	err := c.send(genFunReq(Functions_FUNC_GET_CONTACTS).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetContacts().GetContacts()
+}
+
+// GetDBNames 获取数据库名
+func (c *Client) GetDBNames() []string {
+	err := c.send(genFunReq(Functions_FUNC_GET_DB_NAMES).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetDbs().Names
+}
+
+// GetDBTables 获取表
+func (c *Client) GetDBTables(tab string) []*DbTable {
+	req := genFunReq(Functions_FUNC_GET_DB_TABLES)
+	str := &Request_Str{Str: tab}
+	req.Msg = str
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetTables().GetTables()
+}
+
+// ExecDBQuery 执行sql
+func (c *Client) ExecDBQuery(db, sql string) []*DbRow {
+	req := genFunReq(Functions_FUNC_EXEC_DB_QUERY)
+	q := Request_Query{
+		Query: &DbQuery{
+			Db:  db,
+			Sql: sql,
+		},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetRows().GetRows()
+}
+
+// AcceptFriend 接收好友请求
+func (c *Client) AcceptFriend(v3, v4 string, scene int32) int32 {
+	req := genFunReq(Functions_FUNC_ACCEPT_FRIEND)
+	q := Request_V{
+		V: &Verification{
+			V3:    v3,
+			V4:    v4,
+			Scene: scene,
+		}}
+
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+func (c *Client) AddChatroomMembers(roomID, wxIDs string) int32 {
+	req := genFunReq(Functions_FUNC_ADD_ROOM_MEMBERS)
+	q := Request_M{
+		M: &MemberMgmt{Roomid: roomID, Wxids: wxIDs},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// ReceiveTransfer 接收转账
+func (c *Client) ReceiveTransfer(wxid, tfid, taid string) int32 {
+	req := genFunReq(Functions_FUNC_RECV_TRANSFER)
+	q := Request_Tf{
+		Tf: &Transfer{
+			Wxid: wxid,
+			Tfid: tfid,
+			Taid: taid,
+		},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// RefreshPYQ 刷新朋友圈
+func (c *Client) RefreshPYQ() int32 {
+	req := genFunReq(Functions_FUNC_REFRESH_PYQ)
+	q := Request_Ui64{
+		Ui64: 0,
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// DecryptImage 解密图片 加密路径,解密路径
+func (c *Client) DecryptImage(src, dst string) string {
+	req := genFunReq(Functions_FUNC_DECRYPT_IMAGE)
+	q := Request_Dec{
+		Dec: &DecPath{Src: src, Dst: dst},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+
+	return recv.String()
+}
+
+// AddChatRoomMembers 添加群成员
+func (c *Client) AddChatRoomMembers(roomId string, wxIds []string) int32 {
+	req := genFunReq(Functions_FUNC_ADD_ROOM_MEMBERS)
+	q := Request_M{
+		M: &MemberMgmt{Roomid: roomId,
+			Wxids: strings.Join(wxIds, ",")},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// InvChatRoomMembers 邀请群成员
+func (c *Client) InvChatRoomMembers(roomId string, wxIds []string) int32 {
+	req := genFunReq(Functions_FUNC_INV_ROOM_MEMBERS)
+	q := Request_M{
+		M: &MemberMgmt{Roomid: roomId,
+			Wxids: strings.Join(wxIds, ",")},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// DelChatRoomMembers 删除群成员
+func (c *Client) DelChatRoomMembers(roomId string, wxIds []string) int32 {
+	req := genFunReq(Functions_FUNC_DEL_ROOM_MEMBERS)
+	q := Request_M{
+		M: &MemberMgmt{Roomid: roomId,
+			Wxids: strings.Join(wxIds, ",")},
+	}
+	req.Msg = &q
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// GetUserInfo 获取自己的信息
+func (c *Client) GetUserInfo() *UserInfo {
+	err := c.send(genFunReq(Functions_FUNC_GET_USER_INFO).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetUi()
+}
+
+// SendTxt 发送文本内容
+func (c *Client) SendTxt(msg string, receiver string, ates []string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_TXT)
+	req.Msg = &Request_Txt{
+		Txt: &TextMsg{
+			Msg:      msg,
+			Receiver: receiver,
+			Aters:    strings.Join(ates, ","),
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// ForwardMsg 转发消息
+func (c *Client) ForwardMsg(Id uint64, receiver string) int32 {
+	req := genFunReq(Functions_FUNC_FORWARD_MSG)
+	req.Msg = &Request_Fm{
+		Fm: &ForwardMsg{
+			Id:       Id,
+			Receiver: receiver,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// SendIMG 发送图片
+func (c *Client) SendIMG(path string, receiver string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_IMG)
+	req.Msg = &Request_File{
+		File: &PathMsg{
+			Path:     path,
+			Receiver: receiver,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// SendFile 发送文件
+func (c *Client) SendFile(path string, receiver string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_FILE)
+	req.Msg = &Request_File{
+		File: &PathMsg{
+			Path:     path,
+			Receiver: receiver,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// SendRichText 发送卡片消息
+func (c *Client) SendRichText(name string, account string, title string, digest string, url string, thumburl string, receiver string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_RICH_TXT)
+	req.Msg = &Request_Rt{
+		Rt: &RichText{
+			Name:     name,
+			Account:  account,
+			Title:    title,
+			Digest:   digest,
+			Url:      url,
+			Thumburl: thumburl,
+			Receiver: receiver,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// SendXml 发送xml数据
+func (c *Client) SendXml(path, content, receiver string, Type int32) int32 {
+	req := genFunReq(Functions_FUNC_SEND_XML)
+	req.Msg = &Request_Xml{
+		Xml: &XmlMsg{
+			Receiver: receiver,
+			Content:  content,
+			Path:     path,
+			Type:     Type,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// SendEmotion 发送emoji
+func (c *Client) SendEmotion(path, receiver string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_EMOTION)
+	req.Msg = &Request_File{
+		File: &PathMsg{
+			Path:     path,
+			Receiver: receiver,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// SendPat 发送拍一拍消息
+func (c *Client) SendPat(roomId, wxId string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_PAT_MSG)
+	req.Msg = &Request_Pm{
+		Pm: &PatMsg{
+			Roomid: roomId,
+			Wxid:   wxId,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// DownloadAttach 下载附件
+func (c *Client) DownloadAttach(id uint64, thumb, extra string) int32 {
+	req := genFunReq(Functions_FUNC_SEND_PAT_MSG)
+	req.Msg = &Request_Att{
+		Att: &AttachMsg{
+			Id:    id,
+			Thumb: thumb,
+			Extra: extra,
+		},
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	return recv.GetStatus()
+}
+
+// EnableRecvTxt 开启接收数据
+func (c *Client) EnableRecvTxt() int32 {
+	req := genFunReq(Functions_FUNC_ENABLE_RECV_TXT)
+	req.Msg = &Request_Flag{
+		Flag: true,
+	}
+	err := c.send(req.build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	c.RecvTxt = true
+	return recv.GetStatus()
+}
+
+// DisableRecvTxt 关闭接收消息
+func (c *Client) DisableRecvTxt() int32 {
+	err := c.send(genFunReq(Functions_FUNC_DISABLE_RECV_TXT).build())
+	if err != nil {
+		logs.Err(err)
+	}
+	recv, err := c.Recv()
+	if err != nil {
+		logs.Err(err)
+	}
+	c.RecvTxt = false
+	return recv.GetStatus()
+}
+
+// OnMSG 接收消息
+func (c *Client) OnMSG(f func(msg *WxMsg)) error {
+	socket, err := pair1.NewSocket()
+	if err != nil {
+		return err
+	}
+	_ = socket.SetOption(mangos.OptionRecvDeadline, 5000)
+	_ = socket.SetOption(mangos.OptionSendDeadline, 5000)
+	err = socket.Dial(addPort(c.add))
+	if err != nil {
+		return err
+	}
+	defer socket.Close()
+	for c.RecvTxt {
+		msg := &Response{}
+		recv, err := socket.Recv()
+		if err != nil {
+			return err
+		}
+		_ = proto.Unmarshal(recv, msg)
+		go f(msg.GetWxmsg())
+
+	}
+	return err
+}
+
+// NewWCF 连接
+func NewWCF(add string) (*Client, error) {
+	if add == "" {
+		add = "tcp://127.0.0.1:10086"
+	}
+	client := &Client{add: add}
+	err := client.conn()
+	return client, err
+}
+
+type cmdMSG struct {
+	*Request
+}
+
+func (c *cmdMSG) build() []byte {
+	marshal, _ := proto.Marshal(c)
+	return marshal
+}
+
+func genFunReq(fun Functions) *cmdMSG {
+	return &cmdMSG{
+		&Request{Func: fun,
+			Msg: nil},
+	}
+}
+
+func addPort(add string) string {
+	parts := strings.Split(add, ":")
+	port, _ := strconv.Atoi(parts[2])
+	newPort := port + 1
+	return parts[0] + ":" + parts[1] + ":" + strconv.Itoa(newPort)
+}

+ 2982 - 0
client/wcf/wcf.pb.go

@@ -0,0 +1,2982 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.21.12
+// source: wcf.proto
+
+package wcf
+
+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)
+)
+
+type Functions int32
+
+const (
+	Functions_FUNC_RESERVED         Functions = 0
+	Functions_FUNC_IS_LOGIN         Functions = 1
+	Functions_FUNC_GET_SELF_WXID    Functions = 16
+	Functions_FUNC_GET_MSG_TYPES    Functions = 17
+	Functions_FUNC_GET_CONTACTS     Functions = 18
+	Functions_FUNC_GET_DB_NAMES     Functions = 19
+	Functions_FUNC_GET_DB_TABLES    Functions = 20
+	Functions_FUNC_GET_USER_INFO    Functions = 21
+	Functions_FUNC_GET_AUDIO_MSG    Functions = 22
+	Functions_FUNC_SEND_TXT         Functions = 32
+	Functions_FUNC_SEND_IMG         Functions = 33
+	Functions_FUNC_SEND_FILE        Functions = 34
+	Functions_FUNC_SEND_XML         Functions = 35
+	Functions_FUNC_SEND_EMOTION     Functions = 36
+	Functions_FUNC_SEND_RICH_TXT    Functions = 37
+	Functions_FUNC_SEND_PAT_MSG     Functions = 38
+	Functions_FUNC_FORWARD_MSG      Functions = 39
+	Functions_FUNC_ENABLE_RECV_TXT  Functions = 48
+	Functions_FUNC_DISABLE_RECV_TXT Functions = 64
+	Functions_FUNC_EXEC_DB_QUERY    Functions = 80
+	Functions_FUNC_ACCEPT_FRIEND    Functions = 81
+	Functions_FUNC_RECV_TRANSFER    Functions = 82
+	Functions_FUNC_REFRESH_PYQ      Functions = 83
+	Functions_FUNC_DOWNLOAD_ATTACH  Functions = 84
+	Functions_FUNC_GET_CONTACT_INFO Functions = 85
+	Functions_FUNC_REVOKE_MSG       Functions = 86
+	Functions_FUNC_DECRYPT_IMAGE    Functions = 96
+	Functions_FUNC_EXEC_OCR         Functions = 97
+	Functions_FUNC_ADD_ROOM_MEMBERS Functions = 112
+	Functions_FUNC_DEL_ROOM_MEMBERS Functions = 113
+	Functions_FUNC_INV_ROOM_MEMBERS Functions = 114
+)
+
+// Enum value maps for Functions.
+var (
+	Functions_name = map[int32]string{
+		0:   "FUNC_RESERVED",
+		1:   "FUNC_IS_LOGIN",
+		16:  "FUNC_GET_SELF_WXID",
+		17:  "FUNC_GET_MSG_TYPES",
+		18:  "FUNC_GET_CONTACTS",
+		19:  "FUNC_GET_DB_NAMES",
+		20:  "FUNC_GET_DB_TABLES",
+		21:  "FUNC_GET_USER_INFO",
+		22:  "FUNC_GET_AUDIO_MSG",
+		32:  "FUNC_SEND_TXT",
+		33:  "FUNC_SEND_IMG",
+		34:  "FUNC_SEND_FILE",
+		35:  "FUNC_SEND_XML",
+		36:  "FUNC_SEND_EMOTION",
+		37:  "FUNC_SEND_RICH_TXT",
+		38:  "FUNC_SEND_PAT_MSG",
+		39:  "FUNC_FORWARD_MSG",
+		48:  "FUNC_ENABLE_RECV_TXT",
+		64:  "FUNC_DISABLE_RECV_TXT",
+		80:  "FUNC_EXEC_DB_QUERY",
+		81:  "FUNC_ACCEPT_FRIEND",
+		82:  "FUNC_RECV_TRANSFER",
+		83:  "FUNC_REFRESH_PYQ",
+		84:  "FUNC_DOWNLOAD_ATTACH",
+		85:  "FUNC_GET_CONTACT_INFO",
+		86:  "FUNC_REVOKE_MSG",
+		96:  "FUNC_DECRYPT_IMAGE",
+		97:  "FUNC_EXEC_OCR",
+		112: "FUNC_ADD_ROOM_MEMBERS",
+		113: "FUNC_DEL_ROOM_MEMBERS",
+		114: "FUNC_INV_ROOM_MEMBERS",
+	}
+	Functions_value = map[string]int32{
+		"FUNC_RESERVED":         0,
+		"FUNC_IS_LOGIN":         1,
+		"FUNC_GET_SELF_WXID":    16,
+		"FUNC_GET_MSG_TYPES":    17,
+		"FUNC_GET_CONTACTS":     18,
+		"FUNC_GET_DB_NAMES":     19,
+		"FUNC_GET_DB_TABLES":    20,
+		"FUNC_GET_USER_INFO":    21,
+		"FUNC_GET_AUDIO_MSG":    22,
+		"FUNC_SEND_TXT":         32,
+		"FUNC_SEND_IMG":         33,
+		"FUNC_SEND_FILE":        34,
+		"FUNC_SEND_XML":         35,
+		"FUNC_SEND_EMOTION":     36,
+		"FUNC_SEND_RICH_TXT":    37,
+		"FUNC_SEND_PAT_MSG":     38,
+		"FUNC_FORWARD_MSG":      39,
+		"FUNC_ENABLE_RECV_TXT":  48,
+		"FUNC_DISABLE_RECV_TXT": 64,
+		"FUNC_EXEC_DB_QUERY":    80,
+		"FUNC_ACCEPT_FRIEND":    81,
+		"FUNC_RECV_TRANSFER":    82,
+		"FUNC_REFRESH_PYQ":      83,
+		"FUNC_DOWNLOAD_ATTACH":  84,
+		"FUNC_GET_CONTACT_INFO": 85,
+		"FUNC_REVOKE_MSG":       86,
+		"FUNC_DECRYPT_IMAGE":    96,
+		"FUNC_EXEC_OCR":         97,
+		"FUNC_ADD_ROOM_MEMBERS": 112,
+		"FUNC_DEL_ROOM_MEMBERS": 113,
+		"FUNC_INV_ROOM_MEMBERS": 114,
+	}
+)
+
+func (x Functions) Enum() *Functions {
+	p := new(Functions)
+	*p = x
+	return p
+}
+
+func (x Functions) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Functions) Descriptor() protoreflect.EnumDescriptor {
+	return file_wcf_proto_enumTypes[0].Descriptor()
+}
+
+func (Functions) Type() protoreflect.EnumType {
+	return &file_wcf_proto_enumTypes[0]
+}
+
+func (x Functions) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Functions.Descriptor instead.
+func (Functions) EnumDescriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{0}
+}
+
+type Request struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Func Functions `protobuf:"varint,1,opt,name=func,proto3,enum=wcf.Functions" json:"func,omitempty"`
+	// Types that are assignable to Msg:
+	//
+	//	*Request_Empty
+	//	*Request_Str
+	//	*Request_Txt
+	//	*Request_File
+	//	*Request_Query
+	//	*Request_V
+	//	*Request_M
+	//	*Request_Xml
+	//	*Request_Dec
+	//	*Request_Tf
+	//	*Request_Ui64
+	//	*Request_Flag
+	//	*Request_Att
+	//	*Request_Am
+	//	*Request_Rt
+	//	*Request_Pm
+	//	*Request_Fm
+	Msg isRequest_Msg `protobuf_oneof:"msg"`
+}
+
+func (x *Request) Reset() {
+	*x = Request{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Request) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Request) ProtoMessage() {}
+
+func (x *Request) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_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 Request.ProtoReflect.Descriptor instead.
+func (*Request) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Request) GetFunc() Functions {
+	if x != nil {
+		return x.Func
+	}
+	return Functions_FUNC_RESERVED
+}
+
+func (m *Request) GetMsg() isRequest_Msg {
+	if m != nil {
+		return m.Msg
+	}
+	return nil
+}
+
+func (x *Request) GetEmpty() *Empty {
+	if x, ok := x.GetMsg().(*Request_Empty); ok {
+		return x.Empty
+	}
+	return nil
+}
+
+func (x *Request) GetStr() string {
+	if x, ok := x.GetMsg().(*Request_Str); ok {
+		return x.Str
+	}
+	return ""
+}
+
+func (x *Request) GetTxt() *TextMsg {
+	if x, ok := x.GetMsg().(*Request_Txt); ok {
+		return x.Txt
+	}
+	return nil
+}
+
+func (x *Request) GetFile() *PathMsg {
+	if x, ok := x.GetMsg().(*Request_File); ok {
+		return x.File
+	}
+	return nil
+}
+
+func (x *Request) GetQuery() *DbQuery {
+	if x, ok := x.GetMsg().(*Request_Query); ok {
+		return x.Query
+	}
+	return nil
+}
+
+func (x *Request) GetV() *Verification {
+	if x, ok := x.GetMsg().(*Request_V); ok {
+		return x.V
+	}
+	return nil
+}
+
+func (x *Request) GetM() *MemberMgmt {
+	if x, ok := x.GetMsg().(*Request_M); ok {
+		return x.M
+	}
+	return nil
+}
+
+func (x *Request) GetXml() *XmlMsg {
+	if x, ok := x.GetMsg().(*Request_Xml); ok {
+		return x.Xml
+	}
+	return nil
+}
+
+func (x *Request) GetDec() *DecPath {
+	if x, ok := x.GetMsg().(*Request_Dec); ok {
+		return x.Dec
+	}
+	return nil
+}
+
+func (x *Request) GetTf() *Transfer {
+	if x, ok := x.GetMsg().(*Request_Tf); ok {
+		return x.Tf
+	}
+	return nil
+}
+
+func (x *Request) GetUi64() uint64 {
+	if x, ok := x.GetMsg().(*Request_Ui64); ok {
+		return x.Ui64
+	}
+	return 0
+}
+
+func (x *Request) GetFlag() bool {
+	if x, ok := x.GetMsg().(*Request_Flag); ok {
+		return x.Flag
+	}
+	return false
+}
+
+func (x *Request) GetAtt() *AttachMsg {
+	if x, ok := x.GetMsg().(*Request_Att); ok {
+		return x.Att
+	}
+	return nil
+}
+
+func (x *Request) GetAm() *AudioMsg {
+	if x, ok := x.GetMsg().(*Request_Am); ok {
+		return x.Am
+	}
+	return nil
+}
+
+func (x *Request) GetRt() *RichText {
+	if x, ok := x.GetMsg().(*Request_Rt); ok {
+		return x.Rt
+	}
+	return nil
+}
+
+func (x *Request) GetPm() *PatMsg {
+	if x, ok := x.GetMsg().(*Request_Pm); ok {
+		return x.Pm
+	}
+	return nil
+}
+
+func (x *Request) GetFm() *ForwardMsg {
+	if x, ok := x.GetMsg().(*Request_Fm); ok {
+		return x.Fm
+	}
+	return nil
+}
+
+type isRequest_Msg interface {
+	isRequest_Msg()
+}
+
+type Request_Empty struct {
+	Empty *Empty `protobuf:"bytes,2,opt,name=empty,proto3,oneof"`
+}
+
+type Request_Str struct {
+	Str string `protobuf:"bytes,3,opt,name=str,proto3,oneof"`
+}
+
+type Request_Txt struct {
+	Txt *TextMsg `protobuf:"bytes,4,opt,name=txt,proto3,oneof"`
+}
+
+type Request_File struct {
+	File *PathMsg `protobuf:"bytes,5,opt,name=file,proto3,oneof"`
+}
+
+type Request_Query struct {
+	Query *DbQuery `protobuf:"bytes,6,opt,name=query,proto3,oneof"`
+}
+
+type Request_V struct {
+	V *Verification `protobuf:"bytes,7,opt,name=v,proto3,oneof"`
+}
+
+type Request_M struct {
+	M *MemberMgmt `protobuf:"bytes,8,opt,name=m,proto3,oneof"` // 群成员管理,添加、删除、邀请
+}
+
+type Request_Xml struct {
+	Xml *XmlMsg `protobuf:"bytes,9,opt,name=xml,proto3,oneof"`
+}
+
+type Request_Dec struct {
+	Dec *DecPath `protobuf:"bytes,10,opt,name=dec,proto3,oneof"`
+}
+
+type Request_Tf struct {
+	Tf *Transfer `protobuf:"bytes,11,opt,name=tf,proto3,oneof"`
+}
+
+type Request_Ui64 struct {
+	Ui64 uint64 `protobuf:"varint,12,opt,name=ui64,proto3,oneof"` // 64 位整数,通用
+}
+
+type Request_Flag struct {
+	Flag bool `protobuf:"varint,13,opt,name=flag,proto3,oneof"`
+}
+
+type Request_Att struct {
+	Att *AttachMsg `protobuf:"bytes,14,opt,name=att,proto3,oneof"`
+}
+
+type Request_Am struct {
+	Am *AudioMsg `protobuf:"bytes,15,opt,name=am,proto3,oneof"`
+}
+
+type Request_Rt struct {
+	Rt *RichText `protobuf:"bytes,16,opt,name=rt,proto3,oneof"`
+}
+
+type Request_Pm struct {
+	Pm *PatMsg `protobuf:"bytes,17,opt,name=pm,proto3,oneof"`
+}
+
+type Request_Fm struct {
+	Fm *ForwardMsg `protobuf:"bytes,18,opt,name=fm,proto3,oneof"`
+}
+
+func (*Request_Empty) isRequest_Msg() {}
+
+func (*Request_Str) isRequest_Msg() {}
+
+func (*Request_Txt) isRequest_Msg() {}
+
+func (*Request_File) isRequest_Msg() {}
+
+func (*Request_Query) isRequest_Msg() {}
+
+func (*Request_V) isRequest_Msg() {}
+
+func (*Request_M) isRequest_Msg() {}
+
+func (*Request_Xml) isRequest_Msg() {}
+
+func (*Request_Dec) isRequest_Msg() {}
+
+func (*Request_Tf) isRequest_Msg() {}
+
+func (*Request_Ui64) isRequest_Msg() {}
+
+func (*Request_Flag) isRequest_Msg() {}
+
+func (*Request_Att) isRequest_Msg() {}
+
+func (*Request_Am) isRequest_Msg() {}
+
+func (*Request_Rt) isRequest_Msg() {}
+
+func (*Request_Pm) isRequest_Msg() {}
+
+func (*Request_Fm) isRequest_Msg() {}
+
+type Response struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Func Functions `protobuf:"varint,1,opt,name=func,proto3,enum=wcf.Functions" json:"func,omitempty"`
+	// Types that are assignable to Msg:
+	//
+	//	*Response_Status
+	//	*Response_Str
+	//	*Response_Wxmsg
+	//	*Response_Types
+	//	*Response_Contacts
+	//	*Response_Dbs
+	//	*Response_Tables
+	//	*Response_Rows
+	//	*Response_Ui
+	//	*Response_Ocr
+	Msg isResponse_Msg `protobuf_oneof:"msg"`
+}
+
+func (x *Response) Reset() {
+	*x = Response{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Response) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Response) ProtoMessage() {}
+
+func (x *Response) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_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 Response.ProtoReflect.Descriptor instead.
+func (*Response) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Response) GetFunc() Functions {
+	if x != nil {
+		return x.Func
+	}
+	return Functions_FUNC_RESERVED
+}
+
+func (m *Response) GetMsg() isResponse_Msg {
+	if m != nil {
+		return m.Msg
+	}
+	return nil
+}
+
+func (x *Response) GetStatus() int32 {
+	if x, ok := x.GetMsg().(*Response_Status); ok {
+		return x.Status
+	}
+	return 0
+}
+
+func (x *Response) GetStr() string {
+	if x, ok := x.GetMsg().(*Response_Str); ok {
+		return x.Str
+	}
+	return ""
+}
+
+func (x *Response) GetWxmsg() *WxMsg {
+	if x, ok := x.GetMsg().(*Response_Wxmsg); ok {
+		return x.Wxmsg
+	}
+	return nil
+}
+
+func (x *Response) GetTypes() *MsgTypes {
+	if x, ok := x.GetMsg().(*Response_Types); ok {
+		return x.Types
+	}
+	return nil
+}
+
+func (x *Response) GetContacts() *RpcContacts {
+	if x, ok := x.GetMsg().(*Response_Contacts); ok {
+		return x.Contacts
+	}
+	return nil
+}
+
+func (x *Response) GetDbs() *DbNames {
+	if x, ok := x.GetMsg().(*Response_Dbs); ok {
+		return x.Dbs
+	}
+	return nil
+}
+
+func (x *Response) GetTables() *DbTables {
+	if x, ok := x.GetMsg().(*Response_Tables); ok {
+		return x.Tables
+	}
+	return nil
+}
+
+func (x *Response) GetRows() *DbRows {
+	if x, ok := x.GetMsg().(*Response_Rows); ok {
+		return x.Rows
+	}
+	return nil
+}
+
+func (x *Response) GetUi() *UserInfo {
+	if x, ok := x.GetMsg().(*Response_Ui); ok {
+		return x.Ui
+	}
+	return nil
+}
+
+func (x *Response) GetOcr() *OcrMsg {
+	if x, ok := x.GetMsg().(*Response_Ocr); ok {
+		return x.Ocr
+	}
+	return nil
+}
+
+type isResponse_Msg interface {
+	isResponse_Msg()
+}
+
+type Response_Status struct {
+	Status int32 `protobuf:"varint,2,opt,name=status,proto3,oneof"` // Int 状态,通用
+}
+
+type Response_Str struct {
+	Str string `protobuf:"bytes,3,opt,name=str,proto3,oneof"` // 字符串
+}
+
+type Response_Wxmsg struct {
+	Wxmsg *WxMsg `protobuf:"bytes,4,opt,name=wxmsg,proto3,oneof"` // 微信消息
+}
+
+type Response_Types struct {
+	Types *MsgTypes `protobuf:"bytes,5,opt,name=types,proto3,oneof"` // 消息类型
+}
+
+type Response_Contacts struct {
+	Contacts *RpcContacts `protobuf:"bytes,6,opt,name=contacts,proto3,oneof"` // 联系人
+}
+
+type Response_Dbs struct {
+	Dbs *DbNames `protobuf:"bytes,7,opt,name=dbs,proto3,oneof"` // 数据库列表
+}
+
+type Response_Tables struct {
+	Tables *DbTables `protobuf:"bytes,8,opt,name=tables,proto3,oneof"` // 表列表
+}
+
+type Response_Rows struct {
+	Rows *DbRows `protobuf:"bytes,9,opt,name=rows,proto3,oneof"` // 行列表
+}
+
+type Response_Ui struct {
+	Ui *UserInfo `protobuf:"bytes,10,opt,name=ui,proto3,oneof"` // 个人信息
+}
+
+type Response_Ocr struct {
+	Ocr *OcrMsg `protobuf:"bytes,11,opt,name=ocr,proto3,oneof"` // OCR 结果
+}
+
+func (*Response_Status) isResponse_Msg() {}
+
+func (*Response_Str) isResponse_Msg() {}
+
+func (*Response_Wxmsg) isResponse_Msg() {}
+
+func (*Response_Types) isResponse_Msg() {}
+
+func (*Response_Contacts) isResponse_Msg() {}
+
+func (*Response_Dbs) isResponse_Msg() {}
+
+func (*Response_Tables) isResponse_Msg() {}
+
+func (*Response_Rows) isResponse_Msg() {}
+
+func (*Response_Ui) isResponse_Msg() {}
+
+func (*Response_Ocr) isResponse_Msg() {}
+
+type Empty struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *Empty) Reset() {
+	*x = Empty{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Empty) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Empty) ProtoMessage() {}
+
+func (x *Empty) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_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 Empty.ProtoReflect.Descriptor instead.
+func (*Empty) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{2}
+}
+
+type WxMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	IsSelf  bool   `protobuf:"varint,1,opt,name=is_self,json=isSelf,proto3" json:"is_self,omitempty"`    // 是否自己发送的
+	IsGroup bool   `protobuf:"varint,2,opt,name=is_group,json=isGroup,proto3" json:"is_group,omitempty"` // 是否群消息
+	Id      uint64 `protobuf:"varint,3,opt,name=id,proto3" json:"id,omitempty"`                          // 消息 id
+	Type    uint32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"`                      // 消息类型
+	Ts      uint32 `protobuf:"varint,5,opt,name=ts,proto3" json:"ts,omitempty"`                          // 消息类型
+	Roomid  string `protobuf:"bytes,6,opt,name=roomid,proto3" json:"roomid,omitempty"`                   // 群 id(如果是群消息的话)
+	Content string `protobuf:"bytes,7,opt,name=content,proto3" json:"content,omitempty"`                 // 消息内容
+	Sender  string `protobuf:"bytes,8,opt,name=sender,proto3" json:"sender,omitempty"`                   // 消息发送者
+	Sign    string `protobuf:"bytes,9,opt,name=sign,proto3" json:"sign,omitempty"`                       // Sign
+	Thumb   string `protobuf:"bytes,10,opt,name=thumb,proto3" json:"thumb,omitempty"`                    // 缩略图
+	Extra   string `protobuf:"bytes,11,opt,name=extra,proto3" json:"extra,omitempty"`                    // 附加内容
+	Xml     string `protobuf:"bytes,12,opt,name=xml,proto3" json:"xml,omitempty"`                        // 消息 xml
+}
+
+func (x *WxMsg) Reset() {
+	*x = WxMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *WxMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*WxMsg) ProtoMessage() {}
+
+func (x *WxMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_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 WxMsg.ProtoReflect.Descriptor instead.
+func (*WxMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *WxMsg) GetIsSelf() bool {
+	if x != nil {
+		return x.IsSelf
+	}
+	return false
+}
+
+func (x *WxMsg) GetIsGroup() bool {
+	if x != nil {
+		return x.IsGroup
+	}
+	return false
+}
+
+func (x *WxMsg) GetId() uint64 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *WxMsg) GetType() uint32 {
+	if x != nil {
+		return x.Type
+	}
+	return 0
+}
+
+func (x *WxMsg) GetTs() uint32 {
+	if x != nil {
+		return x.Ts
+	}
+	return 0
+}
+
+func (x *WxMsg) GetRoomid() string {
+	if x != nil {
+		return x.Roomid
+	}
+	return ""
+}
+
+func (x *WxMsg) GetContent() string {
+	if x != nil {
+		return x.Content
+	}
+	return ""
+}
+
+func (x *WxMsg) GetSender() string {
+	if x != nil {
+		return x.Sender
+	}
+	return ""
+}
+
+func (x *WxMsg) GetSign() string {
+	if x != nil {
+		return x.Sign
+	}
+	return ""
+}
+
+func (x *WxMsg) GetThumb() string {
+	if x != nil {
+		return x.Thumb
+	}
+	return ""
+}
+
+func (x *WxMsg) GetExtra() string {
+	if x != nil {
+		return x.Extra
+	}
+	return ""
+}
+
+func (x *WxMsg) GetXml() string {
+	if x != nil {
+		return x.Xml
+	}
+	return ""
+}
+
+type TextMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Msg      string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`           // 要发送的消息内容
+	Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"` // 消息接收人,当为群时可@
+	Aters    string `protobuf:"bytes,3,opt,name=aters,proto3" json:"aters,omitempty"`       // 要@的人列表,逗号分隔
+}
+
+func (x *TextMsg) Reset() {
+	*x = TextMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TextMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TextMsg) ProtoMessage() {}
+
+func (x *TextMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[4]
+	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 TextMsg.ProtoReflect.Descriptor instead.
+func (*TextMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *TextMsg) GetMsg() string {
+	if x != nil {
+		return x.Msg
+	}
+	return ""
+}
+
+func (x *TextMsg) GetReceiver() string {
+	if x != nil {
+		return x.Receiver
+	}
+	return ""
+}
+
+func (x *TextMsg) GetAters() string {
+	if x != nil {
+		return x.Aters
+	}
+	return ""
+}
+
+type PathMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Path     string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`         // 要发送的图片的路径
+	Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"` // 消息接收人
+}
+
+func (x *PathMsg) Reset() {
+	*x = PathMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PathMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PathMsg) ProtoMessage() {}
+
+func (x *PathMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[5]
+	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 PathMsg.ProtoReflect.Descriptor instead.
+func (*PathMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *PathMsg) GetPath() string {
+	if x != nil {
+		return x.Path
+	}
+	return ""
+}
+
+func (x *PathMsg) GetReceiver() string {
+	if x != nil {
+		return x.Receiver
+	}
+	return ""
+}
+
+type XmlMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Receiver string `protobuf:"bytes,1,opt,name=receiver,proto3" json:"receiver,omitempty"` // 消息接收人
+	Content  string `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`   // xml 内容
+	Path     string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"`         // 图片路径
+	Type     int32  `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"`        // 消息类型
+}
+
+func (x *XmlMsg) Reset() {
+	*x = XmlMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *XmlMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*XmlMsg) ProtoMessage() {}
+
+func (x *XmlMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[6]
+	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 XmlMsg.ProtoReflect.Descriptor instead.
+func (*XmlMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *XmlMsg) GetReceiver() string {
+	if x != nil {
+		return x.Receiver
+	}
+	return ""
+}
+
+func (x *XmlMsg) GetContent() string {
+	if x != nil {
+		return x.Content
+	}
+	return ""
+}
+
+func (x *XmlMsg) GetPath() string {
+	if x != nil {
+		return x.Path
+	}
+	return ""
+}
+
+func (x *XmlMsg) GetType() int32 {
+	if x != nil {
+		return x.Type
+	}
+	return 0
+}
+
+type MsgTypes struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Types map[int32]string `protobuf:"bytes,1,rep,name=types,proto3" json:"types,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *MsgTypes) Reset() {
+	*x = MsgTypes{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MsgTypes) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MsgTypes) ProtoMessage() {}
+
+func (x *MsgTypes) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[7]
+	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 MsgTypes.ProtoReflect.Descriptor instead.
+func (*MsgTypes) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *MsgTypes) GetTypes() map[int32]string {
+	if x != nil {
+		return x.Types
+	}
+	return nil
+}
+
+type RpcContact struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Wxid     string `protobuf:"bytes,1,opt,name=wxid,proto3" json:"wxid"`         // 微信 id
+	Code     string `protobuf:"bytes,2,opt,name=code,proto3" json:"code"`         // 微信号
+	Remark   string `protobuf:"bytes,3,opt,name=remark,proto3" json:"remark"`     // 备注
+	Name     string `protobuf:"bytes,4,opt,name=name,proto3" json:"name"`         // 微信昵称
+	Country  string `protobuf:"bytes,5,opt,name=country,proto3" json:"country"`   // 国家
+	Province string `protobuf:"bytes,6,opt,name=province,proto3" json:"province"` // 省/州
+	City     string `protobuf:"bytes,7,opt,name=city,proto3" json:"city"`         // 城市
+	Gender   int32  `protobuf:"varint,8,opt,name=gender,proto3" json:"gender"`    // 性别
+}
+
+func (x *RpcContact) Reset() {
+	*x = RpcContact{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RpcContact) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RpcContact) ProtoMessage() {}
+
+func (x *RpcContact) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[8]
+	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 RpcContact.ProtoReflect.Descriptor instead.
+func (*RpcContact) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *RpcContact) GetWxid() string {
+	if x != nil {
+		return x.Wxid
+	}
+	return ""
+}
+
+func (x *RpcContact) GetCode() string {
+	if x != nil {
+		return x.Code
+	}
+	return ""
+}
+
+func (x *RpcContact) GetRemark() string {
+	if x != nil {
+		return x.Remark
+	}
+	return ""
+}
+
+func (x *RpcContact) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *RpcContact) GetCountry() string {
+	if x != nil {
+		return x.Country
+	}
+	return ""
+}
+
+func (x *RpcContact) GetProvince() string {
+	if x != nil {
+		return x.Province
+	}
+	return ""
+}
+
+func (x *RpcContact) GetCity() string {
+	if x != nil {
+		return x.City
+	}
+	return ""
+}
+
+func (x *RpcContact) GetGender() int32 {
+	if x != nil {
+		return x.Gender
+	}
+	return 0
+}
+
+type RpcContacts struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Contacts []*RpcContact `protobuf:"bytes,1,rep,name=contacts,proto3" json:"contacts,omitempty"`
+}
+
+func (x *RpcContacts) Reset() {
+	*x = RpcContacts{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RpcContacts) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RpcContacts) ProtoMessage() {}
+
+func (x *RpcContacts) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[9]
+	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 RpcContacts.ProtoReflect.Descriptor instead.
+func (*RpcContacts) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *RpcContacts) GetContacts() []*RpcContact {
+	if x != nil {
+		return x.Contacts
+	}
+	return nil
+}
+
+type DbNames struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"`
+}
+
+func (x *DbNames) Reset() {
+	*x = DbNames{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbNames) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbNames) ProtoMessage() {}
+
+func (x *DbNames) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[10]
+	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 DbNames.ProtoReflect.Descriptor instead.
+func (*DbNames) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *DbNames) GetNames() []string {
+	if x != nil {
+		return x.Names
+	}
+	return nil
+}
+
+type DbTable struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // 表名
+	Sql  string `protobuf:"bytes,2,opt,name=sql,proto3" json:"sql,omitempty"`   // 建表 SQL
+}
+
+func (x *DbTable) Reset() {
+	*x = DbTable{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbTable) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbTable) ProtoMessage() {}
+
+func (x *DbTable) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[11]
+	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 DbTable.ProtoReflect.Descriptor instead.
+func (*DbTable) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *DbTable) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *DbTable) GetSql() string {
+	if x != nil {
+		return x.Sql
+	}
+	return ""
+}
+
+type DbTables struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Tables []*DbTable `protobuf:"bytes,1,rep,name=tables,proto3" json:"tables,omitempty"`
+}
+
+func (x *DbTables) Reset() {
+	*x = DbTables{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[12]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbTables) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbTables) ProtoMessage() {}
+
+func (x *DbTables) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[12]
+	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 DbTables.ProtoReflect.Descriptor instead.
+func (*DbTables) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *DbTables) GetTables() []*DbTable {
+	if x != nil {
+		return x.Tables
+	}
+	return nil
+}
+
+type DbQuery struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Db  string `protobuf:"bytes,1,opt,name=db,proto3" json:"db,omitempty"`   // 目标数据库
+	Sql string `protobuf:"bytes,2,opt,name=sql,proto3" json:"sql,omitempty"` // 查询 SQL
+}
+
+func (x *DbQuery) Reset() {
+	*x = DbQuery{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbQuery) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbQuery) ProtoMessage() {}
+
+func (x *DbQuery) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[13]
+	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 DbQuery.ProtoReflect.Descriptor instead.
+func (*DbQuery) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *DbQuery) GetDb() string {
+	if x != nil {
+		return x.Db
+	}
+	return ""
+}
+
+func (x *DbQuery) GetSql() string {
+	if x != nil {
+		return x.Sql
+	}
+	return ""
+}
+
+type DbField struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Type    int32  `protobuf:"varint,1,opt,name=type,proto3" json:"type,omitempty"`      // 字段类型
+	Column  string `protobuf:"bytes,2,opt,name=column,proto3" json:"column,omitempty"`   // 字段名称
+	Content []byte `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` // 字段内容
+}
+
+func (x *DbField) Reset() {
+	*x = DbField{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[14]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbField) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbField) ProtoMessage() {}
+
+func (x *DbField) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[14]
+	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 DbField.ProtoReflect.Descriptor instead.
+func (*DbField) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{14}
+}
+
+func (x *DbField) GetType() int32 {
+	if x != nil {
+		return x.Type
+	}
+	return 0
+}
+
+func (x *DbField) GetColumn() string {
+	if x != nil {
+		return x.Column
+	}
+	return ""
+}
+
+func (x *DbField) GetContent() []byte {
+	if x != nil {
+		return x.Content
+	}
+	return nil
+}
+
+type DbRow struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Fields []*DbField `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"`
+}
+
+func (x *DbRow) Reset() {
+	*x = DbRow{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[15]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbRow) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbRow) ProtoMessage() {}
+
+func (x *DbRow) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[15]
+	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 DbRow.ProtoReflect.Descriptor instead.
+func (*DbRow) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{15}
+}
+
+func (x *DbRow) GetFields() []*DbField {
+	if x != nil {
+		return x.Fields
+	}
+	return nil
+}
+
+type DbRows struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Rows []*DbRow `protobuf:"bytes,1,rep,name=rows,proto3" json:"rows,omitempty"`
+}
+
+func (x *DbRows) Reset() {
+	*x = DbRows{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[16]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DbRows) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DbRows) ProtoMessage() {}
+
+func (x *DbRows) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[16]
+	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 DbRows.ProtoReflect.Descriptor instead.
+func (*DbRows) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{16}
+}
+
+func (x *DbRows) GetRows() []*DbRow {
+	if x != nil {
+		return x.Rows
+	}
+	return nil
+}
+
+type Verification struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	V3    string `protobuf:"bytes,1,opt,name=v3,proto3" json:"v3,omitempty"`        // 加密的用户名
+	V4    string `protobuf:"bytes,2,opt,name=v4,proto3" json:"v4,omitempty"`        // Ticket
+	Scene int32  `protobuf:"varint,3,opt,name=scene,proto3" json:"scene,omitempty"` // 添加方式:17 名片,30 扫码
+}
+
+func (x *Verification) Reset() {
+	*x = Verification{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[17]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Verification) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Verification) ProtoMessage() {}
+
+func (x *Verification) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[17]
+	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 Verification.ProtoReflect.Descriptor instead.
+func (*Verification) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{17}
+}
+
+func (x *Verification) GetV3() string {
+	if x != nil {
+		return x.V3
+	}
+	return ""
+}
+
+func (x *Verification) GetV4() string {
+	if x != nil {
+		return x.V4
+	}
+	return ""
+}
+
+func (x *Verification) GetScene() int32 {
+	if x != nil {
+		return x.Scene
+	}
+	return 0
+}
+
+type MemberMgmt struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Roomid string `protobuf:"bytes,1,opt,name=roomid,proto3" json:"roomid,omitempty"` // 要加的群ID
+	Wxids  string `protobuf:"bytes,2,opt,name=wxids,proto3" json:"wxids,omitempty"`   // 要加群的人列表,逗号分隔
+}
+
+func (x *MemberMgmt) Reset() {
+	*x = MemberMgmt{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[18]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MemberMgmt) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MemberMgmt) ProtoMessage() {}
+
+func (x *MemberMgmt) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[18]
+	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 MemberMgmt.ProtoReflect.Descriptor instead.
+func (*MemberMgmt) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *MemberMgmt) GetRoomid() string {
+	if x != nil {
+		return x.Roomid
+	}
+	return ""
+}
+
+func (x *MemberMgmt) GetWxids() string {
+	if x != nil {
+		return x.Wxids
+	}
+	return ""
+}
+
+type UserInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Wxid   string `protobuf:"bytes,1,opt,name=wxid,proto3" json:"wxid,omitempty"`     // 微信ID
+	Name   string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`     // 昵称
+	Mobile string `protobuf:"bytes,3,opt,name=mobile,proto3" json:"mobile,omitempty"` // 手机号
+	Home   string `protobuf:"bytes,4,opt,name=home,proto3" json:"home,omitempty"`     // 文件/图片等父路径
+}
+
+func (x *UserInfo) Reset() {
+	*x = UserInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[19]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UserInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UserInfo) ProtoMessage() {}
+
+func (x *UserInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[19]
+	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 UserInfo.ProtoReflect.Descriptor instead.
+func (*UserInfo) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{19}
+}
+
+func (x *UserInfo) GetWxid() string {
+	if x != nil {
+		return x.Wxid
+	}
+	return ""
+}
+
+func (x *UserInfo) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *UserInfo) GetMobile() string {
+	if x != nil {
+		return x.Mobile
+	}
+	return ""
+}
+
+func (x *UserInfo) GetHome() string {
+	if x != nil {
+		return x.Home
+	}
+	return ""
+}
+
+type DecPath struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"` // 源路径
+	Dst string `protobuf:"bytes,2,opt,name=dst,proto3" json:"dst,omitempty"` // 目标路径
+}
+
+func (x *DecPath) Reset() {
+	*x = DecPath{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[20]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DecPath) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DecPath) ProtoMessage() {}
+
+func (x *DecPath) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[20]
+	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 DecPath.ProtoReflect.Descriptor instead.
+func (*DecPath) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{20}
+}
+
+func (x *DecPath) GetSrc() string {
+	if x != nil {
+		return x.Src
+	}
+	return ""
+}
+
+func (x *DecPath) GetDst() string {
+	if x != nil {
+		return x.Dst
+	}
+	return ""
+}
+
+type Transfer struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Wxid string `protobuf:"bytes,1,opt,name=wxid,proto3" json:"wxid,omitempty"` // 转账人
+	Tfid string `protobuf:"bytes,2,opt,name=tfid,proto3" json:"tfid,omitempty"` // 转账id transferid
+	Taid string `protobuf:"bytes,3,opt,name=taid,proto3" json:"taid,omitempty"` // Transaction id
+}
+
+func (x *Transfer) Reset() {
+	*x = Transfer{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[21]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Transfer) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Transfer) ProtoMessage() {}
+
+func (x *Transfer) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[21]
+	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 Transfer.ProtoReflect.Descriptor instead.
+func (*Transfer) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{21}
+}
+
+func (x *Transfer) GetWxid() string {
+	if x != nil {
+		return x.Wxid
+	}
+	return ""
+}
+
+func (x *Transfer) GetTfid() string {
+	if x != nil {
+		return x.Tfid
+	}
+	return ""
+}
+
+func (x *Transfer) GetTaid() string {
+	if x != nil {
+		return x.Taid
+	}
+	return ""
+}
+
+type AttachMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id    uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`      // 消息 id
+	Thumb string `protobuf:"bytes,2,opt,name=thumb,proto3" json:"thumb,omitempty"` // 消息中的 thumb
+	Extra string `protobuf:"bytes,3,opt,name=extra,proto3" json:"extra,omitempty"` // 消息中的 extra
+}
+
+func (x *AttachMsg) Reset() {
+	*x = AttachMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[22]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AttachMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AttachMsg) ProtoMessage() {}
+
+func (x *AttachMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[22]
+	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 AttachMsg.ProtoReflect.Descriptor instead.
+func (*AttachMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{22}
+}
+
+func (x *AttachMsg) GetId() uint64 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *AttachMsg) GetThumb() string {
+	if x != nil {
+		return x.Thumb
+	}
+	return ""
+}
+
+func (x *AttachMsg) GetExtra() string {
+	if x != nil {
+		return x.Extra
+	}
+	return ""
+}
+
+type AudioMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id  uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`  // 语音消息 id
+	Dir string `protobuf:"bytes,2,opt,name=dir,proto3" json:"dir,omitempty"` // 存放目录
+}
+
+func (x *AudioMsg) Reset() {
+	*x = AudioMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[23]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AudioMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AudioMsg) ProtoMessage() {}
+
+func (x *AudioMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[23]
+	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 AudioMsg.ProtoReflect.Descriptor instead.
+func (*AudioMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{23}
+}
+
+func (x *AudioMsg) GetId() uint64 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *AudioMsg) GetDir() string {
+	if x != nil {
+		return x.Dir
+	}
+	return ""
+}
+
+type RichText struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Name     string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`         // 显示名字
+	Account  string `protobuf:"bytes,2,opt,name=account,proto3" json:"account,omitempty"`   // 公众号 id
+	Title    string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`       // 标题
+	Digest   string `protobuf:"bytes,4,opt,name=digest,proto3" json:"digest,omitempty"`     // 摘要
+	Url      string `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"`           // 链接
+	Thumburl string `protobuf:"bytes,6,opt,name=thumburl,proto3" json:"thumburl,omitempty"` // 缩略图
+	Receiver string `protobuf:"bytes,7,opt,name=receiver,proto3" json:"receiver,omitempty"` // 接收人
+}
+
+func (x *RichText) Reset() {
+	*x = RichText{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[24]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RichText) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RichText) ProtoMessage() {}
+
+func (x *RichText) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[24]
+	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 RichText.ProtoReflect.Descriptor instead.
+func (*RichText) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{24}
+}
+
+func (x *RichText) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *RichText) GetAccount() string {
+	if x != nil {
+		return x.Account
+	}
+	return ""
+}
+
+func (x *RichText) GetTitle() string {
+	if x != nil {
+		return x.Title
+	}
+	return ""
+}
+
+func (x *RichText) GetDigest() string {
+	if x != nil {
+		return x.Digest
+	}
+	return ""
+}
+
+func (x *RichText) GetUrl() string {
+	if x != nil {
+		return x.Url
+	}
+	return ""
+}
+
+func (x *RichText) GetThumburl() string {
+	if x != nil {
+		return x.Thumburl
+	}
+	return ""
+}
+
+func (x *RichText) GetReceiver() string {
+	if x != nil {
+		return x.Receiver
+	}
+	return ""
+}
+
+type PatMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Roomid string `protobuf:"bytes,1,opt,name=roomid,proto3" json:"roomid,omitempty"` // 群 id
+	Wxid   string `protobuf:"bytes,2,opt,name=wxid,proto3" json:"wxid,omitempty"`     // wxid
+}
+
+func (x *PatMsg) Reset() {
+	*x = PatMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[25]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PatMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PatMsg) ProtoMessage() {}
+
+func (x *PatMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[25]
+	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 PatMsg.ProtoReflect.Descriptor instead.
+func (*PatMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{25}
+}
+
+func (x *PatMsg) GetRoomid() string {
+	if x != nil {
+		return x.Roomid
+	}
+	return ""
+}
+
+func (x *PatMsg) GetWxid() string {
+	if x != nil {
+		return x.Wxid
+	}
+	return ""
+}
+
+type OcrMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Status int32  `protobuf:"varint,1,opt,name=status,proto3" json:"status,omitempty"` // 状态
+	Result string `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"`  // 结果
+}
+
+func (x *OcrMsg) Reset() {
+	*x = OcrMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[26]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *OcrMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OcrMsg) ProtoMessage() {}
+
+func (x *OcrMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[26]
+	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 OcrMsg.ProtoReflect.Descriptor instead.
+func (*OcrMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{26}
+}
+
+func (x *OcrMsg) GetStatus() int32 {
+	if x != nil {
+		return x.Status
+	}
+	return 0
+}
+
+func (x *OcrMsg) GetResult() string {
+	if x != nil {
+		return x.Result
+	}
+	return ""
+}
+
+type ForwardMsg struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Id       uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`            // 待转发消息 ID
+	Receiver string `protobuf:"bytes,2,opt,name=receiver,proto3" json:"receiver,omitempty"` // 转发接收目标,群为 roomId,个人为 wxid
+}
+
+func (x *ForwardMsg) Reset() {
+	*x = ForwardMsg{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_wcf_proto_msgTypes[27]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ForwardMsg) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ForwardMsg) ProtoMessage() {}
+
+func (x *ForwardMsg) ProtoReflect() protoreflect.Message {
+	mi := &file_wcf_proto_msgTypes[27]
+	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 ForwardMsg.ProtoReflect.Descriptor instead.
+func (*ForwardMsg) Descriptor() ([]byte, []int) {
+	return file_wcf_proto_rawDescGZIP(), []int{27}
+}
+
+func (x *ForwardMsg) GetId() uint64 {
+	if x != nil {
+		return x.Id
+	}
+	return 0
+}
+
+func (x *ForwardMsg) GetReceiver() string {
+	if x != nil {
+		return x.Receiver
+	}
+	return ""
+}
+
+var File_wcf_proto protoreflect.FileDescriptor
+
+var file_wcf_proto_rawDesc = []byte{
+	0x0a, 0x09, 0x77, 0x63, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x77, 0x63, 0x66,
+	0x22, 0xd4, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x04,
+	0x66, 0x75, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x77, 0x63, 0x66,
+	0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63,
+	0x12, 0x22, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x0a, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65,
+	0x6d, 0x70, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x74, 0x78, 0x74, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x54, 0x65, 0x78, 0x74,
+	0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x03, 0x74, 0x78, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x66, 0x69,
+	0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x50,
+	0x61, 0x74, 0x68, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x24,
+	0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
+	0x77, 0x63, 0x66, 0x2e, 0x44, 0x62, 0x51, 0x75, 0x65, 0x72, 0x79, 0x48, 0x00, 0x52, 0x05, 0x71,
+	0x75, 0x65, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x01, 0x76, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x11, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x48, 0x00, 0x52, 0x01, 0x76, 0x12, 0x1f, 0x0a, 0x01, 0x6d, 0x18, 0x08, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4d,
+	0x67, 0x6d, 0x74, 0x48, 0x00, 0x52, 0x01, 0x6d, 0x12, 0x1f, 0x0a, 0x03, 0x78, 0x6d, 0x6c, 0x18,
+	0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x58, 0x6d, 0x6c, 0x4d,
+	0x73, 0x67, 0x48, 0x00, 0x52, 0x03, 0x78, 0x6d, 0x6c, 0x12, 0x20, 0x0a, 0x03, 0x64, 0x65, 0x63,
+	0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x44, 0x65, 0x63,
+	0x50, 0x61, 0x74, 0x68, 0x48, 0x00, 0x52, 0x03, 0x64, 0x65, 0x63, 0x12, 0x1f, 0x0a, 0x02, 0x74,
+	0x66, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x54, 0x72,
+	0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x00, 0x52, 0x02, 0x74, 0x66, 0x12, 0x14, 0x0a, 0x04,
+	0x75, 0x69, 0x36, 0x34, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x04, 0x75, 0x69,
+	0x36, 0x34, 0x12, 0x14, 0x0a, 0x04, 0x66, 0x6c, 0x61, 0x67, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08,
+	0x48, 0x00, 0x52, 0x04, 0x66, 0x6c, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x03, 0x61, 0x74, 0x74, 0x18,
+	0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x41, 0x74, 0x74, 0x61,
+	0x63, 0x68, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x03, 0x61, 0x74, 0x74, 0x12, 0x1f, 0x0a, 0x02,
+	0x61, 0x6d, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x41,
+	0x75, 0x64, 0x69, 0x6f, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x02, 0x61, 0x6d, 0x12, 0x1f, 0x0a,
+	0x02, 0x72, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x77, 0x63, 0x66, 0x2e,
+	0x52, 0x69, 0x63, 0x68, 0x54, 0x65, 0x78, 0x74, 0x48, 0x00, 0x52, 0x02, 0x72, 0x74, 0x12, 0x1d,
+	0x0a, 0x02, 0x70, 0x6d, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x77, 0x63, 0x66,
+	0x2e, 0x50, 0x61, 0x74, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x02, 0x70, 0x6d, 0x12, 0x21, 0x0a,
+	0x02, 0x66, 0x6d, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x77, 0x63, 0x66, 0x2e,
+	0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x02, 0x66, 0x6d,
+	0x42, 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x8e, 0x03, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x52, 0x04, 0x66, 0x75, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48,
+	0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x22, 0x0a, 0x05, 0x77, 0x78, 0x6d, 0x73, 0x67, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x57, 0x78, 0x4d, 0x73,
+	0x67, 0x48, 0x00, 0x52, 0x05, 0x77, 0x78, 0x6d, 0x73, 0x67, 0x12, 0x25, 0x0a, 0x05, 0x74, 0x79,
+	0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x77, 0x63, 0x66, 0x2e,
+	0x4d, 0x73, 0x67, 0x54, 0x79, 0x70, 0x65, 0x73, 0x48, 0x00, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65,
+	0x73, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x52, 0x70, 0x63, 0x43, 0x6f, 0x6e,
+	0x74, 0x61, 0x63, 0x74, 0x73, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74,
+	0x73, 0x12, 0x20, 0x0a, 0x03, 0x64, 0x62, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c,
+	0x2e, 0x77, 0x63, 0x66, 0x2e, 0x44, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x48, 0x00, 0x52, 0x03,
+	0x64, 0x62, 0x73, 0x12, 0x27, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x44, 0x62, 0x54, 0x61, 0x62, 0x6c,
+	0x65, 0x73, 0x48, 0x00, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x04,
+	0x72, 0x6f, 0x77, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x77, 0x63, 0x66,
+	0x2e, 0x44, 0x62, 0x52, 0x6f, 0x77, 0x73, 0x48, 0x00, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x12,
+	0x1f, 0x0a, 0x02, 0x75, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x77, 0x63,
+	0x66, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x02, 0x75, 0x69,
+	0x12, 0x1f, 0x0a, 0x03, 0x6f, 0x63, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e,
+	0x77, 0x63, 0x66, 0x2e, 0x4f, 0x63, 0x72, 0x4d, 0x73, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6f, 0x63,
+	0x72, 0x42, 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74,
+	0x79, 0x22, 0x8b, 0x02, 0x0a, 0x05, 0x57, 0x78, 0x4d, 0x73, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x69,
+	0x73, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73,
+	0x53, 0x65, 0x6c, 0x66, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12,
+	0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12,
+	0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74,
+	0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x02, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x69, 0x64, 0x18, 0x06, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63,
+	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18,
+	0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a,
+	0x04, 0x73, 0x69, 0x67, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x67,
+	0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x05, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61,
+	0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x12, 0x10, 0x0a,
+	0x03, 0x78, 0x6d, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x78, 0x6d, 0x6c, 0x22,
+	0x4d, 0x0a, 0x07, 0x54, 0x65, 0x78, 0x74, 0x4d, 0x73, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73,
+	0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x1a, 0x0a, 0x08,
+	0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+	0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x65, 0x72,
+	0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x74, 0x65, 0x72, 0x73, 0x22, 0x39,
+	0x0a, 0x07, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x73, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74,
+	0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a,
+	0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x22, 0x66, 0x0a, 0x06, 0x58, 0x6d, 0x6c,
+	0x4d, 0x73, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x12,
+	0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74,
+	0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a,
+	0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70,
+	0x65, 0x22, 0x74, 0x0a, 0x08, 0x4d, 0x73, 0x67, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x2e, 0x0a,
+	0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x77,
+	0x63, 0x66, 0x2e, 0x4d, 0x73, 0x67, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x54, 0x79, 0x70, 0x65,
+	0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x38, 0x0a,
+	0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
+	0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc2, 0x01, 0x0a, 0x0a, 0x52, 0x70, 0x63, 0x43,
+	0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x78, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x77, 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f,
+	0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16,
+	0x0a, 0x06, 0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x72, 0x65, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f,
+	0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75,
+	0x6e, 0x74, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x6e, 0x63, 0x65,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x6e, 0x63, 0x65,
+	0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x63, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x08,
+	0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, 0x3a, 0x0a, 0x0b,
+	0x52, 0x70, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x08, 0x63,
+	0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e,
+	0x77, 0x63, 0x66, 0x2e, 0x52, 0x70, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x08,
+	0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x73, 0x22, 0x1f, 0x0a, 0x07, 0x44, 0x62, 0x4e, 0x61,
+	0x6d, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x07, 0x44, 0x62, 0x54,
+	0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x22, 0x30, 0x0a, 0x08, 0x44, 0x62,
+	0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x44, 0x62, 0x54,
+	0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x22, 0x2b, 0x0a, 0x07,
+	0x44, 0x62, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x02, 0x64, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x71, 0x6c, 0x22, 0x4f, 0x0a, 0x07, 0x44, 0x62, 0x46,
+	0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6c, 0x75,
+	0x6d, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e,
+	0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x2d, 0x0a, 0x05, 0x44, 0x62,
+	0x52, 0x6f, 0x77, 0x12, 0x24, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x44, 0x62, 0x46, 0x69, 0x65, 0x6c,
+	0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x28, 0x0a, 0x06, 0x44, 0x62, 0x52,
+	0x6f, 0x77, 0x73, 0x12, 0x1e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x0a, 0x2e, 0x77, 0x63, 0x66, 0x2e, 0x44, 0x62, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72,
+	0x6f, 0x77, 0x73, 0x22, 0x44, 0x0a, 0x0c, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x76, 0x33, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x02, 0x76, 0x33, 0x12, 0x0e, 0x0a, 0x02, 0x76, 0x34, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x02, 0x76, 0x34, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x05, 0x52, 0x05, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x22, 0x3a, 0x0a, 0x0a, 0x4d, 0x65, 0x6d,
+	0x62, 0x65, 0x72, 0x4d, 0x67, 0x6d, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x69,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x69, 0x64, 0x12,
+	0x14, 0x0a, 0x05, 0x77, 0x78, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+	0x77, 0x78, 0x69, 0x64, 0x73, 0x22, 0x5e, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66,
+	0x6f, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x77, 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x6f, 0x62,
+	0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c,
+	0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x68, 0x6f, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x07, 0x44, 0x65, 0x63, 0x50, 0x61, 0x74, 0x68,
+	0x12, 0x10, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73,
+	0x72, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x03, 0x64, 0x73, 0x74, 0x22, 0x46, 0x0a, 0x08, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
+	0x12, 0x12, 0x0a, 0x04, 0x77, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x77, 0x78, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x66, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x74, 0x66, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x69, 0x64,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x69, 0x64, 0x22, 0x47, 0x0a, 0x09,
+	0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4d, 0x73, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x68, 0x75,
+	0x6d, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x12,
+	0x14, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+	0x65, 0x78, 0x74, 0x72, 0x61, 0x22, 0x2c, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x4d, 0x73,
+	0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69,
+	0x64, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
+	0x64, 0x69, 0x72, 0x22, 0xb0, 0x01, 0x0a, 0x08, 0x52, 0x69, 0x63, 0x68, 0x54, 0x65, 0x78, 0x74,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14,
+	0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74,
+	0x69, 0x74, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03,
+	0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a,
+	0x0a, 0x08, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x08, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65,
+	0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65,
+	0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x22, 0x34, 0x0a, 0x06, 0x50, 0x61, 0x74, 0x4d, 0x73, 0x67,
+	0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x78, 0x69, 0x64,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x77, 0x78, 0x69, 0x64, 0x22, 0x38, 0x0a, 0x06,
+	0x4f, 0x63, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16,
+	0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x38, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72,
+	0x64, 0x4d, 0x73, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72,
+	0x2a, 0xd9, 0x05, 0x0a, 0x09, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x11,
+	0x0a, 0x0d, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10,
+	0x00, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x49, 0x53, 0x5f, 0x4c, 0x4f, 0x47,
+	0x49, 0x4e, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54,
+	0x5f, 0x53, 0x45, 0x4c, 0x46, 0x5f, 0x57, 0x58, 0x49, 0x44, 0x10, 0x10, 0x12, 0x16, 0x0a, 0x12,
+	0x46, 0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x4d, 0x53, 0x47, 0x5f, 0x54, 0x59, 0x50,
+	0x45, 0x53, 0x10, 0x11, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54,
+	0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x53, 0x10, 0x12, 0x12, 0x15, 0x0a, 0x11, 0x46,
+	0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x44, 0x42, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x53,
+	0x10, 0x13, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x44,
+	0x42, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x53, 0x10, 0x14, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x55,
+	0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x49, 0x4e, 0x46, 0x4f,
+	0x10, 0x15, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x41,
+	0x55, 0x44, 0x49, 0x4f, 0x5f, 0x4d, 0x53, 0x47, 0x10, 0x16, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x55,
+	0x4e, 0x43, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x54, 0x58, 0x54, 0x10, 0x20, 0x12, 0x11, 0x0a,
+	0x0d, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x49, 0x4d, 0x47, 0x10, 0x21,
+	0x12, 0x12, 0x0a, 0x0e, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x46, 0x49,
+	0x4c, 0x45, 0x10, 0x22, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x53, 0x45, 0x4e,
+	0x44, 0x5f, 0x58, 0x4d, 0x4c, 0x10, 0x23, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x55, 0x4e, 0x43, 0x5f,
+	0x53, 0x45, 0x4e, 0x44, 0x5f, 0x45, 0x4d, 0x4f, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x24, 0x12, 0x16,
+	0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x53, 0x45, 0x4e, 0x44, 0x5f, 0x52, 0x49, 0x43, 0x48,
+	0x5f, 0x54, 0x58, 0x54, 0x10, 0x25, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x53,
+	0x45, 0x4e, 0x44, 0x5f, 0x50, 0x41, 0x54, 0x5f, 0x4d, 0x53, 0x47, 0x10, 0x26, 0x12, 0x14, 0x0a,
+	0x10, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x46, 0x4f, 0x52, 0x57, 0x41, 0x52, 0x44, 0x5f, 0x4d, 0x53,
+	0x47, 0x10, 0x27, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x45, 0x4e, 0x41, 0x42,
+	0x4c, 0x45, 0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x54, 0x58, 0x54, 0x10, 0x30, 0x12, 0x19, 0x0a,
+	0x15, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x52, 0x45,
+	0x43, 0x56, 0x5f, 0x54, 0x58, 0x54, 0x10, 0x40, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43,
+	0x5f, 0x45, 0x58, 0x45, 0x43, 0x5f, 0x44, 0x42, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x59, 0x10, 0x50,
+	0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x5f,
+	0x46, 0x52, 0x49, 0x45, 0x4e, 0x44, 0x10, 0x51, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43,
+	0x5f, 0x52, 0x45, 0x43, 0x56, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x46, 0x45, 0x52, 0x10, 0x52,
+	0x12, 0x14, 0x0a, 0x10, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x52, 0x45, 0x46, 0x52, 0x45, 0x53, 0x48,
+	0x5f, 0x50, 0x59, 0x51, 0x10, 0x53, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x44,
+	0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x41, 0x54, 0x54, 0x41, 0x43, 0x48, 0x10, 0x54,
+	0x12, 0x19, 0x0a, 0x15, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x43, 0x4f, 0x4e,
+	0x54, 0x41, 0x43, 0x54, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x55, 0x12, 0x13, 0x0a, 0x0f, 0x46,
+	0x55, 0x4e, 0x43, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x5f, 0x4d, 0x53, 0x47, 0x10, 0x56,
+	0x12, 0x16, 0x0a, 0x12, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x44, 0x45, 0x43, 0x52, 0x59, 0x50, 0x54,
+	0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x60, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x55, 0x4e, 0x43,
+	0x5f, 0x45, 0x58, 0x45, 0x43, 0x5f, 0x4f, 0x43, 0x52, 0x10, 0x61, 0x12, 0x19, 0x0a, 0x15, 0x46,
+	0x55, 0x4e, 0x43, 0x5f, 0x41, 0x44, 0x44, 0x5f, 0x52, 0x4f, 0x4f, 0x4d, 0x5f, 0x4d, 0x45, 0x4d,
+	0x42, 0x45, 0x52, 0x53, 0x10, 0x70, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x44,
+	0x45, 0x4c, 0x5f, 0x52, 0x4f, 0x4f, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x10,
+	0x71, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x55, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x56, 0x5f, 0x52, 0x4f,
+	0x4f, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x53, 0x10, 0x72, 0x42, 0x15, 0x0a, 0x0b,
+	0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x61, 0x6d, 0x74, 0x65, 0x65, 0x72, 0x5a, 0x06, 0x2e, 0x2e, 0x2f,
+	0x77, 0x63, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_wcf_proto_rawDescOnce sync.Once
+	file_wcf_proto_rawDescData = file_wcf_proto_rawDesc
+)
+
+func file_wcf_proto_rawDescGZIP() []byte {
+	file_wcf_proto_rawDescOnce.Do(func() {
+		file_wcf_proto_rawDescData = protoimpl.X.CompressGZIP(file_wcf_proto_rawDescData)
+	})
+	return file_wcf_proto_rawDescData
+}
+
+var file_wcf_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_wcf_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
+var file_wcf_proto_goTypes = []interface{}{
+	(Functions)(0),       // 0: wcf.Functions
+	(*Request)(nil),      // 1: wcf.Request
+	(*Response)(nil),     // 2: wcf.Response
+	(*Empty)(nil),        // 3: wcf.Empty
+	(*WxMsg)(nil),        // 4: wcf.WxMsg
+	(*TextMsg)(nil),      // 5: wcf.TextMsg
+	(*PathMsg)(nil),      // 6: wcf.PathMsg
+	(*XmlMsg)(nil),       // 7: wcf.XmlMsg
+	(*MsgTypes)(nil),     // 8: wcf.MsgTypes
+	(*RpcContact)(nil),   // 9: wcf.RpcContact
+	(*RpcContacts)(nil),  // 10: wcf.RpcContacts
+	(*DbNames)(nil),      // 11: wcf.DbNames
+	(*DbTable)(nil),      // 12: wcf.DbTable
+	(*DbTables)(nil),     // 13: wcf.DbTables
+	(*DbQuery)(nil),      // 14: wcf.DbQuery
+	(*DbField)(nil),      // 15: wcf.DbField
+	(*DbRow)(nil),        // 16: wcf.DbRow
+	(*DbRows)(nil),       // 17: wcf.DbRows
+	(*Verification)(nil), // 18: wcf.Verification
+	(*MemberMgmt)(nil),   // 19: wcf.MemberMgmt
+	(*UserInfo)(nil),     // 20: wcf.UserInfo
+	(*DecPath)(nil),      // 21: wcf.DecPath
+	(*Transfer)(nil),     // 22: wcf.Transfer
+	(*AttachMsg)(nil),    // 23: wcf.AttachMsg
+	(*AudioMsg)(nil),     // 24: wcf.AudioMsg
+	(*RichText)(nil),     // 25: wcf.RichText
+	(*PatMsg)(nil),       // 26: wcf.PatMsg
+	(*OcrMsg)(nil),       // 27: wcf.OcrMsg
+	(*ForwardMsg)(nil),   // 28: wcf.ForwardMsg
+	nil,                  // 29: wcf.MsgTypes.TypesEntry
+}
+var file_wcf_proto_depIdxs = []int32{
+	0,  // 0: wcf.Request.func:type_name -> wcf.Functions
+	3,  // 1: wcf.Request.empty:type_name -> wcf.Empty
+	5,  // 2: wcf.Request.txt:type_name -> wcf.TextMsg
+	6,  // 3: wcf.Request.file:type_name -> wcf.PathMsg
+	14, // 4: wcf.Request.query:type_name -> wcf.DbQuery
+	18, // 5: wcf.Request.v:type_name -> wcf.Verification
+	19, // 6: wcf.Request.m:type_name -> wcf.MemberMgmt
+	7,  // 7: wcf.Request.xml:type_name -> wcf.XmlMsg
+	21, // 8: wcf.Request.dec:type_name -> wcf.DecPath
+	22, // 9: wcf.Request.tf:type_name -> wcf.Transfer
+	23, // 10: wcf.Request.att:type_name -> wcf.AttachMsg
+	24, // 11: wcf.Request.am:type_name -> wcf.AudioMsg
+	25, // 12: wcf.Request.rt:type_name -> wcf.RichText
+	26, // 13: wcf.Request.pm:type_name -> wcf.PatMsg
+	28, // 14: wcf.Request.fm:type_name -> wcf.ForwardMsg
+	0,  // 15: wcf.Response.func:type_name -> wcf.Functions
+	4,  // 16: wcf.Response.wxmsg:type_name -> wcf.WxMsg
+	8,  // 17: wcf.Response.types:type_name -> wcf.MsgTypes
+	10, // 18: wcf.Response.contacts:type_name -> wcf.RpcContacts
+	11, // 19: wcf.Response.dbs:type_name -> wcf.DbNames
+	13, // 20: wcf.Response.tables:type_name -> wcf.DbTables
+	17, // 21: wcf.Response.rows:type_name -> wcf.DbRows
+	20, // 22: wcf.Response.ui:type_name -> wcf.UserInfo
+	27, // 23: wcf.Response.ocr:type_name -> wcf.OcrMsg
+	29, // 24: wcf.MsgTypes.types:type_name -> wcf.MsgTypes.TypesEntry
+	9,  // 25: wcf.RpcContacts.contacts:type_name -> wcf.RpcContact
+	12, // 26: wcf.DbTables.tables:type_name -> wcf.DbTable
+	15, // 27: wcf.DbRow.fields:type_name -> wcf.DbField
+	16, // 28: wcf.DbRows.rows:type_name -> wcf.DbRow
+	29, // [29:29] is the sub-list for method output_type
+	29, // [29:29] is the sub-list for method input_type
+	29, // [29:29] is the sub-list for extension type_name
+	29, // [29:29] is the sub-list for extension extendee
+	0,  // [0:29] is the sub-list for field type_name
+}
+
+func init() { file_wcf_proto_init() }
+func file_wcf_proto_init() {
+	if File_wcf_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_wcf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Request); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Response); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Empty); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*WxMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TextMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PathMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*XmlMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MsgTypes); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RpcContact); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RpcContacts); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbNames); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbTable); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbTables); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbQuery); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbField); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbRow); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DbRows); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Verification); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MemberMgmt); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UserInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DecPath); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Transfer); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AttachMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AudioMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RichText); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PatMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OcrMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_wcf_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ForwardMsg); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_wcf_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*Request_Empty)(nil),
+		(*Request_Str)(nil),
+		(*Request_Txt)(nil),
+		(*Request_File)(nil),
+		(*Request_Query)(nil),
+		(*Request_V)(nil),
+		(*Request_M)(nil),
+		(*Request_Xml)(nil),
+		(*Request_Dec)(nil),
+		(*Request_Tf)(nil),
+		(*Request_Ui64)(nil),
+		(*Request_Flag)(nil),
+		(*Request_Att)(nil),
+		(*Request_Am)(nil),
+		(*Request_Rt)(nil),
+		(*Request_Pm)(nil),
+		(*Request_Fm)(nil),
+	}
+	file_wcf_proto_msgTypes[1].OneofWrappers = []interface{}{
+		(*Response_Status)(nil),
+		(*Response_Str)(nil),
+		(*Response_Wxmsg)(nil),
+		(*Response_Types)(nil),
+		(*Response_Contacts)(nil),
+		(*Response_Dbs)(nil),
+		(*Response_Tables)(nil),
+		(*Response_Rows)(nil),
+		(*Response_Ui)(nil),
+		(*Response_Ocr)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_wcf_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   29,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_wcf_proto_goTypes,
+		DependencyIndexes: file_wcf_proto_depIdxs,
+		EnumInfos:         file_wcf_proto_enumTypes,
+		MessageInfos:      file_wcf_proto_msgTypes,
+	}.Build()
+	File_wcf_proto = out.File
+	file_wcf_proto_rawDesc = nil
+	file_wcf_proto_goTypes = nil
+	file_wcf_proto_depIdxs = nil
+}

+ 12 - 0
rpc/README.md

@@ -0,0 +1,12 @@
+一、  
+Action:  
+
+|     | 代码    | 内容   |
+|-----|-------|------|
+| 1   | getContacts | 通讯录获取 |
+| 2   | chatHistory | 聊天记录 |
+| 3   |sendTalk| 操作   |
+| 4   |sendTalkReceipt| 操作回执 |
+二、配置  
+protoc --go_out=. --go-grpc_out=. proto/chat.proto
+

+ 23 - 0
rpc/a/init.go

@@ -0,0 +1,23 @@
+package a
+
+import (
+	"time"
+
+	"app.yhyue.com/moapp/jybase/go-xweb/httpsession"
+	"app.yhyue.com/moapp/jybase/go-xweb/xweb"
+)
+
+func init() {
+	//开启redissession
+	httpsession.IsRedisSessionStore = true
+	httpsession.RedisNotLoginKey = "userId"
+	xweb.Config.Profiler = true
+	xweb.RootApp().BasePath = "/wxRobot"
+	xweb.RootApp().AppConfig.StaticFileVersion = false
+	xweb.RootApp().AppConfig.CheckXsrf = false
+	xweb.RootApp().AppConfig.EnableHttpCache = false
+	xweb.RootApp().AppConfig.Mode = xweb.Product
+	xweb.RootApp().AppConfig.ReloadTemplates = true
+	xweb.RootApp().AppConfig.SessionTimeout = 7 * 24 * time.Hour
+	xweb.RootApp().Logger.SetOutputLevel(1) //输出日志,改为4则不输出任何日志
+}

+ 261 - 0
rpc/chat/chat.pb.go

@@ -0,0 +1,261 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.36.6
+// 	protoc        v3.15.1
+// source: proto/chat.proto
+
+package chat
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+	unsafe "unsafe"
+)
+
+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)
+)
+
+type JoinRequest struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *JoinRequest) Reset() {
+	*x = JoinRequest{}
+	mi := &file_proto_chat_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *JoinRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JoinRequest) ProtoMessage() {}
+
+func (x *JoinRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_chat_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use JoinRequest.ProtoReflect.Descriptor instead.
+func (*JoinRequest) Descriptor() ([]byte, []int) {
+	return file_proto_chat_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *JoinRequest) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+type Message struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	UserId        string                 `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+	Text          string                 `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"`
+	Timestamp     int64                  `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Action        string                 `protobuf:"bytes,4,opt,name=action,proto3" json:"action,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *Message) Reset() {
+	*x = Message{}
+	mi := &file_proto_chat_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *Message) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Message) ProtoMessage() {}
+
+func (x *Message) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_chat_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Message.ProtoReflect.Descriptor instead.
+func (*Message) Descriptor() ([]byte, []int) {
+	return file_proto_chat_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Message) GetUserId() string {
+	if x != nil {
+		return x.UserId
+	}
+	return ""
+}
+
+func (x *Message) GetText() string {
+	if x != nil {
+		return x.Text
+	}
+	return ""
+}
+
+func (x *Message) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *Message) GetAction() string {
+	if x != nil {
+		return x.Action
+	}
+	return ""
+}
+
+type MessageAck struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Success       bool                   `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
+	MessageId     string                 `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *MessageAck) Reset() {
+	*x = MessageAck{}
+	mi := &file_proto_chat_proto_msgTypes[2]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *MessageAck) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MessageAck) ProtoMessage() {}
+
+func (x *MessageAck) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_chat_proto_msgTypes[2]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MessageAck.ProtoReflect.Descriptor instead.
+func (*MessageAck) Descriptor() ([]byte, []int) {
+	return file_proto_chat_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *MessageAck) GetSuccess() bool {
+	if x != nil {
+		return x.Success
+	}
+	return false
+}
+
+func (x *MessageAck) GetMessageId() string {
+	if x != nil {
+		return x.MessageId
+	}
+	return ""
+}
+
+var File_proto_chat_proto protoreflect.FileDescriptor
+
+const file_proto_chat_proto_rawDesc = "" +
+	"\n" +
+	"\x10proto/chat.proto\x12\x04chat\"&\n" +
+	"\vJoinRequest\x12\x17\n" +
+	"\auser_id\x18\x01 \x01(\tR\x06userId\"l\n" +
+	"\aMessage\x12\x17\n" +
+	"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x12\n" +
+	"\x04text\x18\x02 \x01(\tR\x04text\x12\x1c\n" +
+	"\ttimestamp\x18\x03 \x01(\x03R\ttimestamp\x12\x16\n" +
+	"\x06action\x18\x04 \x01(\tR\x06action\"E\n" +
+	"\n" +
+	"MessageAck\x12\x18\n" +
+	"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x1d\n" +
+	"\n" +
+	"message_id\x18\x02 \x01(\tR\tmessageId2m\n" +
+	"\vChatService\x12.\n" +
+	"\bJoinChat\x12\x11.chat.JoinRequest\x1a\r.chat.Message0\x01\x12.\n" +
+	"\vSendMessage\x12\r.chat.Message\x1a\x10.chat.MessageAckB\rZ\v./chat;chatb\x06proto3"
+
+var (
+	file_proto_chat_proto_rawDescOnce sync.Once
+	file_proto_chat_proto_rawDescData []byte
+)
+
+func file_proto_chat_proto_rawDescGZIP() []byte {
+	file_proto_chat_proto_rawDescOnce.Do(func() {
+		file_proto_chat_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_chat_proto_rawDesc), len(file_proto_chat_proto_rawDesc)))
+	})
+	return file_proto_chat_proto_rawDescData
+}
+
+var file_proto_chat_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_proto_chat_proto_goTypes = []any{
+	(*JoinRequest)(nil), // 0: chat.JoinRequest
+	(*Message)(nil),     // 1: chat.Message
+	(*MessageAck)(nil),  // 2: chat.MessageAck
+}
+var file_proto_chat_proto_depIdxs = []int32{
+	0, // 0: chat.ChatService.JoinChat:input_type -> chat.JoinRequest
+	1, // 1: chat.ChatService.SendMessage:input_type -> chat.Message
+	1, // 2: chat.ChatService.JoinChat:output_type -> chat.Message
+	2, // 3: chat.ChatService.SendMessage:output_type -> chat.MessageAck
+	2, // [2:4] is the sub-list for method output_type
+	0, // [0:2] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_proto_chat_proto_init() }
+func file_proto_chat_proto_init() {
+	if File_proto_chat_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_chat_proto_rawDesc), len(file_proto_chat_proto_rawDesc)),
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_proto_chat_proto_goTypes,
+		DependencyIndexes: file_proto_chat_proto_depIdxs,
+		MessageInfos:      file_proto_chat_proto_msgTypes,
+	}.Build()
+	File_proto_chat_proto = out.File
+	file_proto_chat_proto_goTypes = nil
+	file_proto_chat_proto_depIdxs = nil
+}

+ 163 - 0
rpc/chat/chat_grpc.pb.go

@@ -0,0 +1,163 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.5.1
+// - protoc             v3.15.1
+// source: proto/chat.proto
+
+package chat
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+	ChatService_JoinChat_FullMethodName    = "/chat.ChatService/JoinChat"
+	ChatService_SendMessage_FullMethodName = "/chat.ChatService/SendMessage"
+)
+
+// ChatServiceClient is the client API for ChatService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type ChatServiceClient interface {
+	JoinChat(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Message], error)
+	SendMessage(ctx context.Context, in *Message, opts ...grpc.CallOption) (*MessageAck, error)
+}
+
+type chatServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewChatServiceClient(cc grpc.ClientConnInterface) ChatServiceClient {
+	return &chatServiceClient{cc}
+}
+
+func (c *chatServiceClient) JoinChat(ctx context.Context, in *JoinRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Message], error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	stream, err := c.cc.NewStream(ctx, &ChatService_ServiceDesc.Streams[0], ChatService_JoinChat_FullMethodName, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &grpc.GenericClientStream[JoinRequest, Message]{ClientStream: stream}
+	if err := x.ClientStream.SendMsg(in); err != nil {
+		return nil, err
+	}
+	if err := x.ClientStream.CloseSend(); err != nil {
+		return nil, err
+	}
+	return x, nil
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type ChatService_JoinChatClient = grpc.ServerStreamingClient[Message]
+
+func (c *chatServiceClient) SendMessage(ctx context.Context, in *Message, opts ...grpc.CallOption) (*MessageAck, error) {
+	cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+	out := new(MessageAck)
+	err := c.cc.Invoke(ctx, ChatService_SendMessage_FullMethodName, in, out, cOpts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// ChatServiceServer is the server API for ChatService service.
+// All implementations must embed UnimplementedChatServiceServer
+// for forward compatibility.
+type ChatServiceServer interface {
+	JoinChat(*JoinRequest, grpc.ServerStreamingServer[Message]) error
+	SendMessage(context.Context, *Message) (*MessageAck, error)
+	mustEmbedUnimplementedChatServiceServer()
+}
+
+// UnimplementedChatServiceServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedChatServiceServer struct{}
+
+func (UnimplementedChatServiceServer) JoinChat(*JoinRequest, grpc.ServerStreamingServer[Message]) error {
+	return status.Errorf(codes.Unimplemented, "method JoinChat not implemented")
+}
+func (UnimplementedChatServiceServer) SendMessage(context.Context, *Message) (*MessageAck, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SendMessage not implemented")
+}
+func (UnimplementedChatServiceServer) mustEmbedUnimplementedChatServiceServer() {}
+func (UnimplementedChatServiceServer) testEmbeddedByValue()                     {}
+
+// UnsafeChatServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ChatServiceServer will
+// result in compilation errors.
+type UnsafeChatServiceServer interface {
+	mustEmbedUnimplementedChatServiceServer()
+}
+
+func RegisterChatServiceServer(s grpc.ServiceRegistrar, srv ChatServiceServer) {
+	// If the following call pancis, it indicates UnimplementedChatServiceServer was
+	// embedded by pointer and is nil.  This will cause panics if an
+	// unimplemented method is ever invoked, so we test this at initialization
+	// time to prevent it from happening at runtime later due to I/O.
+	if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+		t.testEmbeddedByValue()
+	}
+	s.RegisterService(&ChatService_ServiceDesc, srv)
+}
+
+func _ChatService_JoinChat_Handler(srv interface{}, stream grpc.ServerStream) error {
+	m := new(JoinRequest)
+	if err := stream.RecvMsg(m); err != nil {
+		return err
+	}
+	return srv.(ChatServiceServer).JoinChat(m, &grpc.GenericServerStream[JoinRequest, Message]{ServerStream: stream})
+}
+
+// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
+type ChatService_JoinChatServer = grpc.ServerStreamingServer[Message]
+
+func _ChatService_SendMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Message)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ChatServiceServer).SendMessage(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: ChatService_SendMessage_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ChatServiceServer).SendMessage(ctx, req.(*Message))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// ChatService_ServiceDesc is the grpc.ServiceDesc for ChatService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var ChatService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "chat.ChatService",
+	HandlerType: (*ChatServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "SendMessage",
+			Handler:    _ChatService_SendMessage_Handler,
+		},
+	},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "JoinChat",
+			Handler:       _ChatService_JoinChat_Handler,
+			ServerStreams: true,
+		},
+	},
+	Metadata: "proto/chat.proto",
+}

+ 44 - 0
rpc/config.json

@@ -0,0 +1,44 @@
+{
+  "tidbMysql": {
+    "dbName": "",
+    "address": "172.20.45.129:4000",
+    "userName": "root",
+    "passWord": "=PDT49#80Z!RVv52_z",
+    "maxOpenConns": 20,
+    "maxIdleConns": 20
+  },
+  "WxRobot": {
+    "dbName": "wx_robot",
+    "address": "172.20.45.129:4000",
+    "userName": "root",
+    "passWord": "=PDT49#80Z!RVv52_z",
+    "maxOpenConns": 20,
+    "maxIdleConns": 20
+  },
+  "convertlabsync": {
+    "dbName": "Convertlabsync",
+    "address": "172.20.45.129:4000",
+    "userName": "root",
+    "passWord": "=PDT49#80Z!RVv52_z",
+    "maxOpenConns": 20,
+    "maxIdleConns": 20
+  },
+  "userClickhouse": {
+    "dbName": "pub_tags",
+    "addr": "172.20.45.129:19000",
+    "userName": "jytop",
+    "passWord": "pwdTopJy123",
+    "maxIdleConns": 20,
+    "maxOpenConns": 20
+  },
+  "mgo": {
+    "address": "172.20.45.129:27002",
+    "dbName": "qfw",
+    "dbSize": 20
+  },
+  "redisServer": "other=172.20.45.129:1712,session=172.20.45.129:1713,newother=172.20.45.129:1712",
+  "replyLanguage": "拒收请回复R",
+  "replyAutomatically": "已收到您的拒收反馈,感谢您的配合。",
+  "webPort": "50052",
+  "grpcWebPort": "50051"
+}

+ 130 - 0
rpc/config/config.go

@@ -0,0 +1,130 @@
+package config
+
+import (
+	util "app.yhyue.com/moapp/jybase/common"
+	"app.yhyue.com/moapp/jybase/mongodb"
+	"app.yhyue.com/moapp/jybase/mysql"
+	"app.yhyue.com/moapp/jybase/redis"
+	"context"
+	"fmt"
+	"github.com/ClickHouse/clickhouse-go/v2"
+	"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
+	"log"
+	"time"
+)
+
+var DbConf *Conf
+
+type Conf struct {
+	TidbMysql          *MysqlConf
+	WxRobot            *MysqlConf
+	Convertlabsync     *MysqlConf
+	UserClickhouse     *CHouseConfig
+	Mgo                Mongo
+	RedisServer        string
+	ReplyAutomatically string
+	ReplyLanguage      string
+	GrpcWebPort        string
+	WebPort            string
+}
+type Mongo struct {
+	Address string `json:"address"`
+	DbName  string `json:"dbName"`
+	DbSize  int    `json:"dbSize"`
+}
+type MysqlConf struct {
+	DbName       string
+	Address      string
+	UserName     string
+	PassWord     string
+	MaxOpenConns int
+	MaxIdleConns int
+}
+type CHouseConfig struct {
+	Addr         string
+	UserName     string
+	Password     string
+	DbName       string
+	MaxIdleConns int
+	MaxOpenConns int
+}
+
+var (
+	Mgo            *mongodb.MongodbSim
+	TiDbMysql      *mysql.Mysql
+	ClickhouseConn driver.Conn
+	WxRobot        *mysql.Mysql
+	Convertlabsync *mysql.Mysql
+)
+
+func init() {
+	util.ReadConfig(&DbConf)
+	TiDbMysql = &mysql.Mysql{
+		Address:      DbConf.TidbMysql.Address,
+		UserName:     DbConf.TidbMysql.UserName,
+		PassWord:     DbConf.TidbMysql.PassWord,
+		DBName:       DbConf.TidbMysql.DbName,
+		MaxOpenConns: DbConf.TidbMysql.MaxOpenConns,
+		MaxIdleConns: DbConf.TidbMysql.MaxIdleConns,
+	}
+	TiDbMysql.Init()
+	WxRobot = &mysql.Mysql{
+		Address:      DbConf.WxRobot.Address,
+		UserName:     DbConf.WxRobot.UserName,
+		PassWord:     DbConf.WxRobot.PassWord,
+		DBName:       DbConf.WxRobot.DbName,
+		MaxOpenConns: DbConf.WxRobot.MaxOpenConns,
+		MaxIdleConns: DbConf.WxRobot.MaxIdleConns,
+	}
+	WxRobot.Init()
+	Convertlabsync = &mysql.Mysql{
+		Address:      DbConf.Convertlabsync.Address,
+		UserName:     DbConf.Convertlabsync.UserName,
+		PassWord:     DbConf.Convertlabsync.PassWord,
+		DBName:       DbConf.Convertlabsync.DbName,
+		MaxOpenConns: DbConf.Convertlabsync.MaxOpenConns,
+		MaxIdleConns: DbConf.Convertlabsync.MaxIdleConns,
+	}
+	Convertlabsync.Init()
+	ConnectClickhouse()
+	Mgo = mongodb.NewMgo(DbConf.Mgo.Address, DbConf.Mgo.DbName, DbConf.Mgo.DbSize)
+	redis.InitRedis(DbConf.RedisServer)
+}
+func ConnectClickhouse() error {
+	var (
+		ctx = context.Background()
+		err error
+	)
+	ClickhouseConn, err = clickhouse.Open(&clickhouse.Options{
+		Addr:         []string{DbConf.UserClickhouse.Addr},
+		DialTimeout:  10 * time.Second,
+		MaxIdleConns: DbConf.UserClickhouse.MaxIdleConns,
+		MaxOpenConns: DbConf.UserClickhouse.MaxOpenConns,
+		Auth: clickhouse.Auth{
+			Database: DbConf.UserClickhouse.DbName,
+			Username: DbConf.UserClickhouse.UserName,
+			Password: DbConf.UserClickhouse.Password,
+		},
+		Debugf: func(format string, v ...interface{}) {
+			fmt.Printf(format, v)
+		},
+	})
+	if err != nil {
+		return err
+	}
+	if err := ClickhouseConn.Ping(ctx); err != nil {
+		if exception, ok := err.(*clickhouse.Exception); ok {
+			log.Println(fmt.Sprintf("Exception [%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace))
+		}
+		return err
+	}
+	return nil
+}
+
+// 接口统一返回值
+type Result struct {
+	Error_code int         `json:"error_code"`
+	Error_msg  string      `json:"error_msg"`
+	Data       interface{} `json:"data"`
+}
+type M map[string]interface{}

+ 9 - 0
rpc/filter/filter.go

@@ -0,0 +1,9 @@
+package filter
+
+import (
+	"app.yhyue.com/moapp/jybase/go-xweb/xweb"
+)
+
+func init() {
+	xweb.AddFilter(&sessionfilter{App: xweb.RootApp()})
+}

+ 30 - 0
rpc/filter/sessionfilter.go

@@ -0,0 +1,30 @@
+package filter
+
+import (
+	"net/http"
+
+	. "app.yhyue.com/moapp/jybase/api"
+	util "app.yhyue.com/moapp/jybase/common"
+
+	"app.yhyue.com/moapp/jybase/go-xweb/xweb"
+)
+
+// 登录限制
+type sessionfilter struct {
+	App *xweb.App
+}
+
+func (l *sessionfilter) Do(w http.ResponseWriter, req *http.Request) bool {
+	session := l.App.SessionManager.Session(req, w)
+	getSession := session.GetMultiple()
+	if getSession["userId"] != nil && getSession["mgoUserId"] == nil && util.Int64All(getSession["positionType"]) == 0 {
+		session.Set("mgoUserId", getSession["userId"])
+	}
+	userId, ok := getSession["userId"].(string)
+	if !ok || userId == "" {
+		R.ServeJson(w, req, &Result{Error_code_1004, Error_msg_1004, nil})
+		return false
+	}
+
+	return true
+}

+ 69 - 0
rpc/go.mod

@@ -0,0 +1,69 @@
+module rpc
+
+go 1.23.7
+
+require (
+	app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b
+	app.yhyue.com/moapp/jylog v0.0.0-20230522075550-05d7230ca545
+	github.com/ClickHouse/clickhouse-go/v2 v2.2.0
+	github.com/go-xweb/xweb v0.2.1
+	github.com/gogf/gf/v2 v2.7.0
+	github.com/robfig/cron v1.2.0
+	google.golang.org/grpc v1.73.0
+	google.golang.org/protobuf v1.36.6
+)
+
+require (
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/fatih/color v1.16.0 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/garyburd/redigo v1.6.2 // indirect
+	github.com/go-sql-driver/mysql v1.8.1 // indirect
+	github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6 // indirect
+	github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e // indirect
+	github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
+	github.com/gomodule/redigo v2.0.0+incompatible // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/hashicorp/hcl v1.0.0 // indirect
+	github.com/howeyc/fsnotify v0.9.0 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/jinzhu/now v1.1.1 // indirect
+	github.com/klauspost/compress v1.18.0 // indirect
+	github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd // indirect
+	github.com/magiconair/properties v1.8.7 // indirect
+	github.com/mattn/go-sqlite3 v1.14.28 // indirect
+	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/paulmach/orb v0.11.1 // indirect
+	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
+	github.com/pierrec/lz4/v4 v4.1.22 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/shopspring/decimal v1.4.0 // indirect
+	github.com/sirupsen/logrus v1.8.3 // indirect
+	github.com/spf13/afero v1.9.3 // indirect
+	github.com/spf13/cast v1.5.0 // indirect
+	github.com/spf13/jwalterweatherman v1.1.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/spf13/viper v1.15.0 // indirect
+	github.com/subosito/gotenv v1.4.2 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.2 // indirect
+	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
+	go.mongodb.org/mongo-driver v1.14.0 // indirect
+	go.opentelemetry.io/otel v1.36.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.36.0 // indirect
+	go.opentelemetry.io/otel/trace v1.36.0 // indirect
+	golang.org/x/crypto v0.39.0 // indirect
+	golang.org/x/net v0.41.0 // indirect
+	golang.org/x/sync v0.15.0 // indirect
+	golang.org/x/sys v0.33.0 // indirect
+	golang.org/x/text v0.26.0 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+	gorm.io/driver/mysql v1.0.5 // indirect
+	gorm.io/gorm v1.21.3 // indirect
+)

+ 670 - 0
rpc/go.sum

@@ -0,0 +1,670 @@
+app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b h1:bQKiTTrlbDIc+Z2y7ynbZrieaaAUB2CisE/rl42FlPY=
+app.yhyue.com/moapp/jybase v0.0.0-20250509080440-038d69d3ad3b/go.mod h1:OEtMbsn7wY/7MLgV7yDUpVDKExUoj3B8h+4w4ZckJQQ=
+app.yhyue.com/moapp/jylog v0.0.0-20230522075550-05d7230ca545 h1:+Lak4m1zgsigQloOsvp8AJ+0XeX/+PGp9QP550xlbBQ=
+app.yhyue.com/moapp/jylog v0.0.0-20230522075550-05d7230ca545/go.mod h1:uFrsdUBFbETiJlEmr4PtJWPsZlUpPj2bHQRhryu6ggk=
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
+cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
+cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
+cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
+cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
+cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
+cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
+cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
+cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
+cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
+cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
+cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
+cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
+cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
+cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
+cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
+cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
+github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
+github.com/ClickHouse/clickhouse-go/v2 v2.2.0 h1:dj00TDKY+xwuTJdbpspCSmTLFyWzRJerTHwaBxut1C0=
+github.com/ClickHouse/clickhouse-go/v2 v2.2.0/go.mod h1:8f2XZUi7XoeU+uPIytSi1cvx8fmJxi7vIgqpvYTF1+o=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
+github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+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/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM=
+github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6 h1:DUiWdm3rS8cC96Y0XeVkDeizzEK6X+qiNgXytLVtbkM=
+github.com/go-xweb/httpsession v0.0.0-20141220075701-356d3b4d38d6/go.mod h1:lwPk13GS+i/NK4FkMm68IcJrAwiu+HtjYa1Y4kW59aY=
+github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e h1:xmffs7hgrWpAOcquZrdlWpAEaAdlI9myaYcUUmhIP7k=
+github.com/go-xweb/log v0.0.0-20140701090824-270d183ad77e/go.mod h1:ASmYUSBf32lWkkNVX/pnOU4MLuUQpFH4qYHvWHt/l0w=
+github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851 h1:D46USD6oGNWzoJ/h5CWaFq3ELLoLoJzllJ03Xh78VYg=
+github.com/go-xweb/uuid v0.0.0-20140604020037-d7dce341f851/go.mod h1:OmDEC58ZYO1Esk+Uy32SB6LWof9lyROl7q76dBFOCWw=
+github.com/go-xweb/xweb v0.2.1 h1:u5t/ttuSfxiIMDTXj/Pouw9C2ASNABWT16JWHyrtdvY=
+github.com/go-xweb/xweb v0.2.1/go.mod h1:vPjYJgfidYAgBKIwiAyKFC1hfczlqsw9rRT8LtwrGew=
+github.com/gogf/gf/v2 v2.7.0 h1:CjxhbMiE7oqf6K8ZtGuKt3dQEwK4vL6LhiI+dI7tJGU=
+github.com/gogf/gf/v2 v2.7.0/go.mod h1:Qu8nimKt9aupJQcdUL85tWF4Mfxocz97zUt8UC4abVI=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
+github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
+github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/howeyc/fsnotify v0.9.0 h1:0gtV5JmOKH4A8SsFxG2BczSeXWWPvcMT0euZt5gDAxY=
+github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd h1:DXxmBCahjva4Ox4AWOv6pR1Csv33zSj97SaLOElfIsw=
+github.com/lunny/csession v0.0.0-20130910075847-fe53c5de3dfd/go.mod h1:3w9PScemEkJoLw3OYvLWMoD8XRCmXgGwsSpT6pFpJ0g=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
+github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
+github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
+github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
+github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
+github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
+github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
+github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
+github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/sirupsen/logrus v1.8.3 h1:DBBfY8eMYazKEJHb3JKpSPfpgd2mBCoNFlQx6C5fftU=
+github.com/sirupsen/logrus v1.8.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
+github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
+github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
+github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
+github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
+github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
+github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
+github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
+github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
+github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
+go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
+go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
+go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
+go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
+go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
+go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
+go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
+go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
+go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
+go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
+go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
+go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
+go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
+golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
+golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
+golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
+golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
+golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
+google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
+google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
+google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
+google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
+google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
+google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
+gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
+gorm.io/gorm v1.21.3 h1:qDFi55ZOsjZTwk5eN+uhAmHi8GysJ/qCTichM/yO7ME=
+gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

+ 73 - 0
rpc/log.go

@@ -0,0 +1,73 @@
+package main
+
+/**
+日志文件自动切换,默认保留15天内日志
+**/
+
+import (
+	"log"
+	"os"
+	"path/filepath"
+	"regexp"
+	"time"
+
+	"github.com/go-xweb/xweb"
+	"github.com/robfig/cron"
+)
+
+// 日志格式
+var fileReg = regexp.MustCompile("^(\\d{4}_[0-9_]{14})\\.log$")
+
+// 当前日志文件句柄
+var LogFile *os.File
+
+// 时间格式
+var FMT = "2006_01_02_15_04_05"
+
+// 日志目录
+var LogPath = "./jylog"
+
+func init() {
+	os.Mkdir(LogPath, os.ModePerm)
+	//默认保留15天内的日志,-1为永久保留
+	initLog(15)
+}
+
+func initLog(saveDay int) {
+	go logfile()
+	task := cron.New()
+	task.Start()
+	task.AddFunc("0 0 0/10 * * ?", func() {
+		go logfile()
+		time.Sleep(50 * time.Second)
+		if saveDay > 0 {
+			filepath.Walk(LogPath, func(path string, info os.FileInfo, err error) error {
+				str := fileReg.FindStringSubmatch(info.Name())
+				if len(str) == 2 {
+					t, er := time.ParseInLocation(FMT, str[1], time.Local)
+					if er == nil {
+						if (time.Now().Unix()-t.Unix())/86400 > int64(saveDay) {
+							log.Println("delete log file:", path, os.Remove(path))
+						}
+					}
+				}
+				return nil
+			})
+		}
+	})
+}
+
+// 创建并切换输出文件
+func logfile() {
+	now := time.Now().Format(FMT)
+	file, _ := os.Create(LogPath + "/" + now + ".log")
+	log.SetOutput(file)
+	xweb.RootApp().Logger.SetOutput(file)
+	go func(file *os.File) {
+		time.Sleep(5 * time.Second)
+		if LogFile != nil {
+			LogFile.Close()
+		}
+		LogFile = file
+	}(file)
+}

+ 97 - 0
rpc/main.go

@@ -0,0 +1,97 @@
+package main
+
+import (
+	"app.yhyue.com/moapp/jybase/go-xweb/xweb"
+	"context"
+	"fmt"
+	"log"
+	"net"
+	"net/http"
+	"os"
+	"os/signal"
+	"rpc/config"
+	"rpc/service"
+	"syscall"
+	"time"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/keepalive"
+	_ "rpc/a"
+	. "rpc/chat"
+	_ "rpc/filter"
+)
+
+func main() {
+	// 检查配置
+	if config.DbConf == nil {
+		log.Fatal("配置未初始化")
+	}
+
+	// 打印启动信息
+	startTime := time.Now()
+	log.Printf("服务启动中... [HTTP:%s, gRPC:%s]",
+		config.DbConf.WebPort, config.DbConf.GrpcWebPort)
+
+	// 启动HTTP服务
+	mux1 := http.NewServeMux()
+	go func() {
+		log.Printf("HTTP服务监听 :%s", config.DbConf.WebPort)
+		xweb.RunBase(":"+config.DbConf.WebPort, mux1)
+	}()
+
+	// 启动gRPC服务
+	lis, err := net.Listen("tcp", fmt.Sprintf(":%s", config.DbConf.GrpcWebPort))
+	if err != nil {
+		log.Fatalf("无法监听gRPC端口 %s: %v", config.DbConf.GrpcWebPort, err)
+	}
+
+	server := grpc.NewServer(
+		grpc.KeepaliveParams(keepalive.ServerParameters{
+			Time:    60 * time.Second,
+			Timeout: 120 * time.Second,
+		}),
+		grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
+			MinTime:             30 * time.Second, // 调整为30秒
+			PermitWithoutStream: true,             // 允许无流连接
+		}),
+	)
+
+	RegisterChatServiceServer(server, service.ChatSrv)
+
+	// 启动定时任务
+	go service.ChatSrv.StartTimedMessages(2*time.Hour, "getContacts")
+	go service.ChatSrv.StartTimedMessages(10*time.Minute, "listenIn")
+
+	// 启动gRPC服务
+	go func() {
+		log.Printf("gRPC服务已启动,监听端口 :%s [PID: %d]",
+			config.DbConf.GrpcWebPort, os.Getpid())
+		if err := server.Serve(lis); err != nil {
+			log.Fatalf("gRPC服务启动失败: %v", err)
+		}
+	}()
+
+	// 优雅关闭处理
+	done := make(chan os.Signal, 1)
+	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+	<-done
+	log.Printf("\n接收到关闭信号 [运行时间: %s]", time.Since(startTime).Round(time.Second))
+
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	stopped := make(chan struct{})
+	go func() {
+		server.GracefulStop()
+		close(stopped)
+	}()
+
+	select {
+	case <-ctx.Done():
+		log.Println("警告: 优雅关闭超时,强制终止")
+		server.Stop()
+	case <-stopped:
+		log.Println("服务已正常关闭")
+	}
+}

+ 25 - 0
rpc/proto/chat.proto

@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package chat;
+
+option go_package = "./chat;chat";
+
+service ChatService {
+  rpc JoinChat(JoinRequest) returns (stream Message); // 服务端流
+  rpc SendMessage(Message) returns (MessageAck);      // 一元调用
+}
+message JoinRequest {
+  string user_id = 1;
+}
+
+message Message {
+  string user_id = 1;
+  string text = 2;
+  int64 timestamp = 3;
+  string   action = 4;
+}
+
+message MessageAck {
+  bool success = 1;
+  string message_id = 2;
+}

+ 282 - 0
rpc/service/chatServer.go

@@ -0,0 +1,282 @@
+package service
+
+import (
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/util/gconv"
+	"google.golang.org/grpc"
+	"log"
+	. "rpc/chat"
+	"rpc/config"
+	"sync"
+	"time"
+)
+
+type chatServer struct {
+	UnimplementedChatServiceServer
+	clients  map[string]chan *Message
+	adminMsg chan *Message
+	mu       sync.RWMutex
+}
+
+var ChatSrv = &chatServer{
+	clients:  make(map[string]chan *Message),
+	adminMsg: make(chan *Message, 100),
+}
+
+// 建立连接
+func (s *chatServer) JoinChat(req *JoinRequest, stream ChatService_JoinChatServer) error {
+	// 创建新通道
+	msgChan := make(chan *Message, 100)
+
+	// 注册客户端
+	s.mu.Lock()
+	s.clients[req.UserId] = msgChan
+	s.mu.Unlock()
+
+	// 发送欢迎消息
+	welcomeMsg := &Message{
+		UserId:    "系统",
+		Text:      "欢迎加入聊天室!",
+		Timestamp: time.Now().Unix(),
+	}
+	if err := stream.Send(welcomeMsg); err != nil {
+		s.mu.Lock()
+		delete(s.clients, req.UserId)
+		s.mu.Unlock()
+		return err
+	}
+
+	// 清理处理
+	defer func() {
+		s.mu.Lock()
+		if ch, exists := s.clients[req.UserId]; exists {
+			delete(s.clients, req.UserId)
+			close(ch)
+		}
+		s.mu.Unlock()
+	}()
+	for {
+		select {
+		case msg := <-msgChan:
+			if err := stream.Send(msg); err != nil {
+				return err
+			}
+		case adminMsg := <-s.adminMsg:
+			if err := stream.Send(adminMsg); err != nil {
+				return err
+			}
+		case <-stream.Context().Done():
+			return nil
+		}
+	}
+}
+
+// 接收消息处理
+func (s *chatServer) SendMessage(ctx context.Context, msg *Message) (*MessageAck, error) {
+	msg.Timestamp = time.Now().Unix()
+	log.Printf("收到来自 %s 的 %s 消息: %s", msg.UserId, msg.Action, msg.Text)
+
+	// 先处理业务逻辑
+	switch msg.Action {
+	case "getContacts":
+		log.Printf("接收%s通讯录信息", msg.UserId)
+		//go SynchronousContacts(msg.UserId, msg.Text)
+	case "chatHistory":
+		go AddChatRecord(msg.UserId, msg.Text) // 异步处理
+	case "sendTalk":
+		//操作
+		go Task() // 异步处理
+	case "sendTalkReceipt":
+		go SendTalkReceipt(msg.Text) // 异步处理
+	}
+
+	// 发送消息(加锁范围最小化)
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+
+	for userId, ch := range s.clients {
+		select {
+		case ch <- msg:
+		default:
+			log.Printf("客户端 %s 的消息通道已满", userId)
+		}
+	}
+
+	return &MessageAck{Success: true}, nil
+}
+
+// SendAdminMessage 向指定用户发送系统消息
+func (s *chatServer) SendAdminMessage(userId string, text string, action string) error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	// 检查目标用户是否存在
+	msgChan, exists := s.clients[userId]
+	if !exists {
+		return fmt.Errorf("用户 %s 不存在或已离线", userId)
+	}
+	// 构造系统消息
+	msg := &Message{
+		UserId:    "系统",
+		Text:      text,
+		Timestamp: time.Now().Unix(),
+		Action:    action,
+	}
+	// 发送消息
+	select {
+	case msgChan <- msg:
+		log.Printf("已向用户 %s 发送系统消息: %s", userId, text)
+		return nil
+	default:
+		return fmt.Errorf("用户 %s 的消息通道已满", userId)
+	}
+}
+
+// StartTimedMessages 启动定时消息发送
+func (s *chatServer) StartTimedMessages(interval time.Duration, action string) {
+	ticker := time.NewTicker(interval)
+	defer ticker.Stop()
+	for range ticker.C {
+		message := fmt.Sprintf("系统定时消息: 当前时间 %v", time.Now().Format("2006-01-02 15:04:05"))
+		// 快速获取客户端列表
+		s.mu.RLock()
+		clients := make([]string, 0, len(s.clients))
+		for userId := range s.clients {
+			clients = append(clients, userId)
+		}
+		s.mu.RUnlock()
+
+		// 处理发送(无需在锁内)
+		switch action {
+		case "getContacts":
+			s.BroadcastAdminMessage(message, "getContacts")
+		case "sendTalk":
+			Task()
+		case "listenIn":
+			s.BroadcastAdminMessage(message, "isnline")
+		}
+	}
+}
+
+// BroadcastAdminMessage 向所有客户端广播系统消息
+func (s *chatServer) BroadcastAdminMessage(text string, action string) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	msg := &Message{
+		UserId:    "系统",
+		Text:      text,
+		Timestamp: time.Now().Unix(),
+		Action:    action,
+	}
+	for userId, ch := range s.clients {
+		select {
+		case ch <- msg:
+			log.Printf("已广播系统消息到用户 %s: %s", userId, text)
+		default:
+			log.Printf("用户 %s 的消息通道已满,无法广播", userId)
+		}
+	}
+}
+
+// SpecifyAdminMessage 向制定客户端广播系统消息
+func (s *chatServer) SpecifyAdminMessage(taskId int64, userMap map[string]interface{}, contentData *[]map[string]interface{}, action, batchCode string) error {
+	userId := gconv.String(userMap["userId"])
+	isRefuse := gconv.Int64(userMap["isRefuse"])
+	if isRefuse == 1 {
+		//拒绝用户
+		config.WxRobot.Insert("send_record", map[string]interface{}{
+			"task_id":      taskId,
+			"base_user_id": gconv.String(userMap["baseUserId"]),
+			"send_status":  3,
+			"create_time":  time.Now().Format(time.DateTime),
+			"batch_code":   batchCode,
+		})
+		return nil
+	}
+	s.mu.Lock()
+	ch, exists := s.clients[userId] // 直接获取目标用户的 channel
+	s.mu.Unlock()
+	config.WxRobot.Insert("send_record", map[string]interface{}{
+		"task_id":      taskId,
+		"base_user_id": gconv.String(userMap["baseUserId"]),
+		"send_status":  1,
+		"create_time":  time.Now().Format(time.DateTime),
+		"batch_code":   batchCode,
+	})
+	if !exists {
+		return fmt.Errorf("用户 %s 不存在或未连接", userId)
+	}
+	text := gconv.String(map[string]interface{}{
+		"user":          userMap,
+		"content":       contentData,
+		"taskId":        taskId,
+		"batchCode":     batchCode,
+		"replyLanguage": config.DbConf.ReplyLanguage,
+	})
+	msg := &Message{
+		UserId:    "系统",
+		Text:      text,
+		Timestamp: time.Now().Unix(),
+		Action:    action, // 例如:"alert"/"notification"/"kick"
+	}
+	select {
+	case ch <- msg:
+		log.Printf("系统消息已发送到用户 %s: %s (Action: %s)", userId, text, action)
+		return nil
+	default:
+		log.Printf("用户 %s 的消息通道已满,丢弃消息", userId)
+		return fmt.Errorf("用户 %s 的消息通道阻塞", userId)
+	}
+}
+
+// SpecifysystemMessage 向制定客户端广播系统消息(拒绝也发,不保存发送记录)
+func (s *chatServer) SpecifysystemMessage(userId, wxId string, contentData map[string]interface{}, action string) error {
+	// 1. 加锁并获取用户channel
+	s.mu.Lock()
+	ch, exists := s.clients[userId]
+	if !exists {
+		s.mu.Unlock()
+		log.Printf("用户 %s 不存在或已离线 (wxId: %s)", userId, wxId)
+		return fmt.Errorf("user %s not found", userId)
+	}
+
+	// 2. 准备消息数据(仍在锁保护下)
+	msg := &Message{
+		UserId:    "系统",
+		Text:      buildMessageText(contentData, wxId),
+		Timestamp: time.Now().Unix(),
+		Action:    action,
+	}
+
+	// 3. 复制channel引用后立即释放锁
+	channel := ch
+	s.mu.Unlock()
+
+	// 4. 尝试发送消息
+	return trySendMessage(channel, msg, userId, action)
+}
+
+// 辅助函数:构建消息文本
+func buildMessageText(contentData map[string]interface{}, wxId string) string {
+	return gconv.String(map[string]interface{}{
+		"content": contentData,
+		"wxId":    wxId,
+	})
+}
+
+// 辅助函数:尝试发送消息
+func trySendMessage(ch chan<- *Message, msg *Message, userId, action string) error {
+	select {
+	case ch <- msg:
+		log.Printf("系统消息发送成功 | 用户: %s | 动作: %s", userId, action)
+		return nil
+	default:
+		log.Printf("消息通道已满 | 用户: %s | 动作: %s", userId, action)
+		return fmt.Errorf("message queue full for user %s", userId)
+	}
+}
+func logPings(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
+	// 只有客户端调用 RPC 方法时才会执行这里!
+	log.Println("RPC called:", info.FullMethod)
+	return handler(ctx, req)
+}

+ 316 - 0
rpc/service/service.go

@@ -0,0 +1,316 @@
+package service
+
+import (
+	util "app.yhyue.com/moapp/jybase/common"
+	"app.yhyue.com/moapp/jybase/date"
+	"app.yhyue.com/moapp/jybase/redis"
+	"fmt"
+	"github.com/gogf/gf/v2/util/gconv"
+	"log"
+	"rpc/config"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	seq      int64
+	lastDay  string
+	seqMutex sync.Mutex
+)
+
+func Task() {
+	allUserMap := InitUser()
+	go ScheduledTasks(allUserMap, 0)
+	log.Println(allUserMap)
+	go RecurringTask(allUserMap)
+}
+
+// 定时任务处理
+func ScheduledTasks(allUserMap map[int64]map[string]interface{}, newtaskId int64) {
+	now := time.Now().Format(time.DateTime)
+	sql := ``
+	if newtaskId == 0 {
+		sql = "SELECT    * FROM    task WHERE    task_type = 0     AND task_status =0  order by  id  "
+	} else {
+		sql = fmt.Sprintf(`SELECT    * FROM    task WHERE     id  =%d order by  id  `, newtaskId)
+	}
+	taskData := config.WxRobot.SelectBySql(sql)
+	if taskData == nil || len(*taskData) == 0 {
+		return
+	}
+	for _, v := range *taskData {
+		batchCode := FindBatch()
+		sendGroupId := gconv.String(v["send_group_id"])
+		taskType := gconv.Int64(v["task_type"])
+		taskId := gconv.Int64(v["id"])
+		sendTimeType := gconv.Int64(v["send_time_type"])
+		if sendTimeType == 1 && !IsSameMinute(now, gconv.String(v["send_time"]), "2006-01-02 15:04:05") && newtaskId == 0 {
+			continue
+		}
+		go UserHandle(sendGroupId, batchCode, taskType, taskId, allUserMap)
+	}
+}
+
+// 循环定时任务处理
+func RecurringTask(allUserMap map[int64]map[string]interface{}) {
+	now := time.Now().Format(time.DateTime)
+	sql := "SELECT    * FROM    task WHERE    task_type = 1     AND task_status  in (0,1)  order by  id  "
+	taskData := config.WxRobot.SelectBySql(sql)
+	if taskData == nil || len(*taskData) == 0 {
+		return
+	}
+	for _, v := range *taskData {
+		batchCode := FindBatch()
+		sendGroupId := gconv.String(v["send_group_id"])
+		taskType := gconv.Int64(v["task_type"])
+		taskId := gconv.Int64(v["id"])
+		sendTimeType := gconv.Int64(v["send_time_type"])
+		if sendTimeType == 1 && !IsSameMinute(now, gconv.String(v["send_time"]), "2006-01-02 15:04:05") {
+			continue
+		}
+		go UserHandle(sendGroupId, batchCode, taskType, taskId, allUserMap)
+
+	}
+}
+
+// 人员查询
+func UserHandle(sendGroupId, batchCode string, taskType int64, taskId int64, allUserMap map[int64]map[string]interface{}) {
+	sendsuccessMap := map[int64]bool{}
+	if taskType == 1 {
+		//循环任务
+		//查询历史发送记录
+		recorData := config.WxRobot.SelectBySql("select  * from  send_record  where   task_id =? and  send_status in  (0,3)", taskId)
+		for _, m := range *recorData {
+			baseUserId := gconv.Int64(m["base_user_id"])
+			sendsuccessMap[baseUserId] = true
+		}
+	}
+	//查询分组人员信息
+	uic := NewUserIdConstructor(strings.Split(sendGroupId, ","), 0)
+	baseUserIdList := uic.QueryBaseUserIDList()
+	if len(baseUserIdList) == 0 {
+		log.Println("QueryBaseUserIdList 未获取到有效用户base_user_id")
+		return
+	}
+	//排除掉未建联的人
+	newUserArr := []map[string]interface{}{}
+	for _, v := range baseUserIdList {
+		if allUserMap[v] != nil {
+			if taskType == 1 {
+				if gconv.Bool(sendsuccessMap[v]) {
+					continue
+				}
+			}
+			newUserArr = append(newUserArr, allUserMap[v])
+		} else {
+			continue
+		}
+
+	}
+	if len(newUserArr) == 0 {
+		return
+	}
+	//获取发送内容
+	contentData := config.WxRobot.Find("task_send_content", map[string]interface{}{
+		"task_id": taskId, "status": 0,
+	}, "", "create_time", -1, -1)
+	if contentData == nil || len(*contentData) == 0 {
+		log.Println("任务内容获取失败:", taskId)
+		return
+	}
+
+	for _, v := range newUserArr {
+		log.Println(v)
+		//发送消息
+		go ChatSrv.SpecifyAdminMessage(taskId, v, contentData, "sendTalk", batchCode)
+
+	}
+	config.WxRobot.Update("task", map[string]interface{}{
+		"id": taskId,
+	}, map[string]interface{}{
+		"task_status": util.If(taskType == 1, 1, 3),
+	})
+}
+
+// 判断是否是同一分钟
+func IsSameMinute(timeStr1, timeStr2, layout string) bool {
+	t1, err := time.Parse(layout, timeStr1)
+	if err != nil {
+		return false
+	}
+	t2, err := time.Parse(layout, timeStr2)
+	if err != nil {
+		return false
+	}
+	return t1.Truncate(time.Minute).Equal(t2.Truncate(time.Minute))
+}
+
+// 通讯录同步
+func SynchronousContacts(robotCode, contactsStr string) {
+	config.WxRobot.Update("user_address_book", map[string]interface{}{
+		"robotCode": robotCode,
+	}, map[string]interface{}{
+		"status": 1,
+	})
+	for _, v := range gconv.Maps(contactsStr) {
+		wxId := gconv.String(v["wxid"])
+		phone := gconv.String(v["phone"])
+		datas, _ := config.Mgo.Find("user", map[string]interface{}{
+			"i_appid": 2,
+			"$or": []map[string]interface{}{
+				{"s_phone": phone},
+				{"s_m_phone": phone},
+			},
+		}, `{"s_phone":-1}`, `{"s_phone":1,"s_m_phone":1,"base_user_id":1}`, false, 0, 1)
+		if datas != nil || len(*datas) == 0 {
+			//删除通讯录
+			config.WxRobot.Update("user_address_book", map[string]interface{}{
+				"wxid":      wxId,
+				"robotCode": robotCode,
+			}, map[string]interface{}{
+				"status": 1,
+			})
+		}
+		//做一个通讯录变更
+		baseUserId := gconv.Int64((*datas)[0]["base_user_id"])
+		if config.WxRobot.Count("user_address_book", map[string]interface{}{
+			"wxid": wxId, "robotCode": robotCode}) > 0 {
+			//修改数据
+			config.WxRobot.Update("user_address_book", map[string]interface{}{
+				"wxid":      wxId,
+				"robotCode": robotCode,
+			}, map[string]interface{}{
+				"remark":       gconv.String(v["remark"]),
+				"phone":        phone,
+				"base_user_id": baseUserId,
+				"update_time":  time.Now().Format(date.Date_Full_Layout),
+				"status":       0,
+			})
+		} else {
+			//新增数据
+			config.WxRobot.Insert("user_address_book", map[string]interface{}{
+				"remark":       gconv.String(v["remark"]),
+				"phone":        phone,
+				"base_user_id": baseUserId,
+				"status":       0,
+				"wxid":         wxId,
+				"code":         gconv.String(v["code"]),
+				"name":         gconv.String(v["name"]),
+				"is_refuse":    0,
+				"create_time":  time.Now().Format(date.Date_Full_Layout),
+				"update_time":  time.Now().Format(date.Date_Full_Layout),
+				"robotCode":    robotCode,
+			})
+		}
+	}
+}
+
+// 聊天记录保存
+func AddChatRecord(robotCode string, data string) {
+	chatData := gconv.Map(data)
+	wxId := gconv.String(chatData["roomid"])
+	content := gconv.String(chatData["content"])
+	userData := config.WxRobot.FindOne("user_address_book", map[string]interface{}{
+		"wxid":      wxId,
+		"robotCode": robotCode,
+	}, "", "")
+	if len(*userData) > 0 {
+		baseUserId := gconv.Int64((*userData)["base_user_id"])
+		if (content == "R" || content == "r") && !gconv.Bool(chatData["IsSelf"]) {
+			if config.WxRobot.Count("send_record", map[string]interface{}{
+				"base_user_id": baseUserId,
+				"send_status":  0,
+			}) > 0 {
+				config.WxRobot.Update("user_address_book", map[string]interface{}{
+					"base_user_id": baseUserId,
+				}, map[string]interface{}{
+					"is_refuse":   1,
+					"update_time": time.Now().Format(date.Date_Full_Layout),
+				})
+				//发送一条 拒绝自动回复
+				ChatSrv.SpecifysystemMessage(robotCode, wxId, map[string]interface{}{
+					"content":      config.DbConf.ReplyAutomatically,
+					"content_type": 0,
+				}, "reject")
+			}
+
+		}
+		config.WxRobot.Insert("chat_history", map[string]interface{}{
+			"create_time":  time.Now().Format(date.Date_Full_Layout),
+			"robotCode":    robotCode,
+			"other_wxid":   wxId,
+			"content":      gconv.String(chatData["content"]),
+			"content_type": 1,
+			"is_own_send":  util.If(gconv.Bool(chatData["IsSelf"]), 1, 0),
+		})
+	}
+
+}
+
+// 用户信息初始化
+func InitUser() map[int64]map[string]interface{} {
+	allUserMap :=
+		make(map[int64]map[string]interface{})
+	config.WxRobot.SelectByBath(1000, func(l *[]map[string]interface{}) bool {
+		for _, v := range *l {
+			baseUserId := gconv.Int64(v["base_user_id"])
+			wxId := gconv.String(v["wxid"])
+			robotCode := gconv.String(v["robotCode"])
+			if allUserMap[baseUserId] == nil {
+				allUserMap[baseUserId] = map[string]interface{}{
+					"wxid":       wxId,
+					"userId":     robotCode,
+					"isRefuse":   gconv.Int64(v["is_refuse"]),
+					"baseUserId": gconv.Int64(baseUserId),
+				}
+			}
+		}
+		return true
+	}, "select * from   user_address_book  where   `status`  =0   ORDER BY    create_time  ")
+	return allUserMap
+}
+
+// 微信发送成功回调
+func SendTalkReceipt(text string) {
+	dataMap := gconv.Map(text)
+	isSuccess := gconv.Bool(dataMap["isSuccess"])
+	if isSuccess {
+		taskId := gconv.Int64(dataMap["taskId"])
+		baseUserId := gconv.String(dataMap["base_user_id"])
+		batch_code := gconv.String(dataMap["batch_code"])
+		config.WxRobot.Update("send_record", map[string]interface{}{
+			"task_id":      taskId,
+			"base_user_id": baseUserId,
+			"batch_code":   batch_code,
+		}, map[string]interface{}{
+			"send_status": 0,
+			"update_time": time.Now().Format(time.DateTime),
+		})
+	}
+}
+
+// FindBatch 获取或创建当日批次号(自动清理昨日批次)
+// 参数:
+//   - module: 业务模块标识
+//
+// 返回:
+//   - 批次号字符串(如"0042")
+//   - error: 操作失败时返回错误
+func FindBatch() string {
+	// 1. 准备Redis键
+	now := time.Now().UTC()
+	today := now.Format("2006-01-02")
+	yesterday := now.AddDate(0, 0, -1).Format("2006-01-02")
+	todayKey := fmt.Sprintf("wxRobot_%s", today)
+	yesterdayKey := fmt.Sprintf("wxRobot_%s", yesterday)
+	// 2. 异步清理昨日数据(不影响主流程)
+	if exists, _ := redis.Exists("newother", yesterdayKey); exists {
+		redis.Del(yesterdayKey) // 不等待结果
+	}
+	// 3. 获取/创建今日批次号
+	incrResult := redis.Incr("newother", todayKey)
+
+	// 4. 格式化返回(保证4位数字)
+	return fmt.Sprintf("%s%02d", time.Now().Format("20060102"), incrResult)
+}

+ 201 - 0
rpc/service/userGroupService.go

@@ -0,0 +1,201 @@
+package service
+
+import (
+	"app.yhyue.com/moapp/jybase/common"
+	"context"
+	"fmt"
+	"log"
+	"rpc/config"
+	"strings"
+	"time"
+)
+
+const (
+	logicalOperatorNormal = 0 // 正常运算标签
+	logicalOperatorNot    = 1 // 非运算标签
+	tagOperatorAnd        = 1 // 且
+	tagOperatorOr         = 2 // 或
+	fullUserTagSql        = `SELECT groupBitmapAndState(bitobj) as userIds from pub_tags.dwd_d_tag ddt WHERE  ddt.id=2017`
+	andSql                = `SELECT groupBitmapAndState(bitobj) as userIds from pub_tags.dwd_d_tag ddt WHERE  ddt.id in (%s) `
+	orSql                 = `SELECT groupBitmapOrState(bitobj) as userIds from pub_tags.dwd_d_tag ddt WHERE  ddt.id in (%s) `
+)
+
+// UserIdConstructor 用户群组标签转换
+type UserIdConstructor struct {
+	groupFilter      []string        // 群组过滤条件
+	userGtFilter     int64           // 用户过滤条件  暂停发消息时 用的
+	userGroupTagList []*UserGroupTag // 用户群组标签列表 (整理后的)
+	baseQuerySQL     string          // 查询群组下base_user_id 的sql
+	mgoQuerySQL      string          // 最终的查询  查询mgoId的sql
+}
+
+type UserGroupTag struct {
+	GroupId     int64   // 群组id
+	TagOperator int64   // 群组内关系
+	NormalTag   []int64 // 正常标签
+	NotTag      []int64 // 非标签
+}
+
+func NewUserIdConstructor(groupFilter []string, userGtFilter int64) (u *UserIdConstructor) {
+	u = &UserIdConstructor{
+		groupFilter:      groupFilter,
+		userGtFilter:     userGtFilter,
+		userGroupTagList: []*UserGroupTag{},
+	}
+	return
+}
+
+// GetGroupTags 获取用户群组标签信息
+func (u *UserIdConstructor) getGroupTags() *[]map[string]interface{} {
+	tStart := time.Now()
+	defer func() {
+		log.Println("getGroupTags 耗时:", time.Since(tStart))
+	}()
+	groupIDFilter := []string{}
+	groupIdValue := []interface{}{}
+	where := ""
+	for i := 0; i < len(u.groupFilter); i++ {
+		groupIDFilter = append(groupIDFilter, "?")
+		groupIdValue = append(groupIdValue, u.groupFilter[i])
+	}
+	where = fmt.Sprintf("where ugt.group_id in (%s)", strings.Join(groupIDFilter, ","))
+	query := fmt.Sprintf(`SELECT ugt.group_id,ug.tag_operator,ugt.tag_id,ugt.logical_operator FROM convertlabsync.user_group_tag ugt left join user_group ug  on (ugt.group_id=ug.id) %s`, where)
+	rs := config.Convertlabsync.SelectBySql(query, groupIdValue...)
+	return rs
+}
+
+// InitTagList 处理成方便用的数组
+func (u *UserIdConstructor) InitTagList() bool {
+	tStart := time.Now()
+	defer func() {
+		log.Println("InitTagList 耗时:", time.Since(tStart))
+	}()
+	rs := u.getGroupTags()
+	if rs == nil || len(*rs) == 0 {
+		return false
+	}
+	groupMap := map[int64]*UserGroupTag{}
+	for i := 0; i < len(*rs); i++ {
+		groupId := common.Int64All((*rs)[i]["group_id"])
+		tagOperator := common.Int64All((*rs)[i]["tag_operator"])
+		tagId := common.Int64All((*rs)[i]["tag_id"])
+		logicalOperator := common.IntAll((*rs)[i]["logical_operator"])
+		if _, ok := groupMap[groupId]; !ok {
+			groupMap[groupId] = &UserGroupTag{
+				GroupId:     groupId,
+				TagOperator: tagOperator,
+				NormalTag:   []int64{},
+				NotTag:      []int64{},
+			}
+		}
+		// 追加
+		switch logicalOperator {
+		case logicalOperatorNormal:
+			groupMap[groupId].NormalTag = append(groupMap[groupId].NormalTag, tagId)
+		case logicalOperatorNot:
+			groupMap[groupId].NotTag = append(groupMap[groupId].NotTag, tagId)
+		}
+	}
+	for _, v := range groupMap {
+		u.userGroupTagList = append(u.userGroupTagList, v)
+	}
+	return true
+}
+
+// 转换成sql
+// '正常标签'这里指不是非运算
+// toBaseQuerySQL 转换成查询baseUserId 的sql
+func (u *UserIdConstructor) toBaseQuerySQL() string {
+	tStart := time.Now()
+	defer func() {
+		log.Println("toBaseQuerySQL 耗时:", time.Since(tStart))
+	}()
+	sqlList := []string{} // 包含多个群组的sql
+	for i := 0; i < len(u.userGroupTagList); i++ {
+		// 拼接群组内sql
+		groupTag := u.userGroupTagList[i]
+		normalTagSQL, notTagSQL := "", ""
+		tagSql := ""
+		switch groupTag.TagOperator {
+		case tagOperatorAnd:
+			if len(groupTag.NormalTag) > 0 { // 正常标签
+				normalTagList := []string{}
+				for j := 0; j < len(groupTag.NormalTag); j++ {
+					normalTagList = append(normalTagList, fmt.Sprint(groupTag.NormalTag[j]))
+				}
+				normalTagSQL = fmt.Sprintf(andSql, strings.Join(normalTagList, ","))
+			}
+			if len(groupTag.NotTag) > 0 { // 非标签
+				notTagList := []string{}
+				for j := 0; j < len(groupTag.NotTag); j++ {
+					notTagList = append(notTagList, fmt.Sprint(groupTag.NotTag[j]))
+				}
+				notTagSQL = fmt.Sprintf(orSql, strings.Join(notTagList, ","))
+			}
+			// 同时有:  正常标签 - 非标签
+			switch {
+			case normalTagSQL != "" && notTagSQL != "":
+				tagSql = fmt.Sprintf("SELECT  bitmapAndnot((%s),(%s)) as userIds", normalTagSQL, notTagSQL)
+			case normalTagSQL != "":
+				tagSql = normalTagSQL
+			case notTagSQL != "":
+				tagSql = fmt.Sprintf("SELECT  bitmapAndnot((%s),(%s)) as userIds", fullUserTagSql, notTagSQL)
+			}
+
+		case tagOperatorOr:
+			if len(groupTag.NormalTag) > 0 { // 正常标签
+				normalTagList := []string{}
+				for j := 0; j < len(groupTag.NormalTag); j++ {
+					normalTagList = append(normalTagList, fmt.Sprint(groupTag.NormalTag[j]))
+				}
+				normalTagSQL = fmt.Sprintf(orSql, strings.Join(normalTagList, ","))
+			}
+			if len(groupTag.NotTag) > 0 { // 非标签
+				notTagList := []string{}
+				for j := 0; j < len(groupTag.NotTag); j++ {
+					notTagList = append(notTagList, fmt.Sprint(groupTag.NotTag[j]))
+				}
+				notTagSQL = fmt.Sprintf(andSql, strings.Join(notTagList, ","))
+			}
+			// 同时有:  正常标签 ∪ (U-(B∩C∩D....))  U:全量标签 B、C、D... 非标签
+			switch {
+			case normalTagSQL != "" && notTagSQL != "":
+				tmpNotTagSql := fmt.Sprintf("SELECT  bitmapAndnot((%s),(%s)) as userIds", fullUserTagSql, notTagSQL)
+				tagSql = fmt.Sprintf("SELECT  bitmapOr((%s),(%s)) as userIds", fullUserTagSql, tmpNotTagSql)
+			case normalTagSQL != "":
+				tagSql = normalTagSQL
+			case notTagSQL != "":
+				tagSql = fmt.Sprintf("SELECT  bitmapAndnot((%s),(%s)) as userIds", fullUserTagSql, notTagSQL)
+			}
+		}
+		sqlList = append(sqlList, tagSql)
+	}
+	// 如果用户有过滤
+	if u.userGtFilter > 0 {
+		u.baseQuerySQL = fmt.Sprintf("SELECT  arrayFilter(x -> x >%v,bitmapToArray( groupBitmapOrState(userIds))) as userIds from (%s)", u.userGtFilter, strings.Join(sqlList, " UNION    DISTINCT  "))
+	} else {
+		u.baseQuerySQL = fmt.Sprintf("SELECT  bitmapToArray( groupBitmapOrState(  userIds)) as userIds  from (%s)", strings.Join(sqlList, " UNION    DISTINCT  "))
+	}
+	log.Println("baseQuerySQL:", u.baseQuerySQL)
+	return u.baseQuerySQL
+}
+
+// QueryBaseUserIDList 从数据库查询
+func (u *UserIdConstructor) QueryBaseUserIDList() (userList []int64) {
+	tStart := time.Now()
+	defer func() {
+		log.Println("QueryBaseUserIDList 整体耗时:", time.Since(tStart))
+	}()
+	if !u.InitTagList() {
+		return []int64{}
+	}
+	q := u.toBaseQuerySQL()
+	qStart := time.Now()
+	rows := config.ClickhouseConn.QueryRow(context.Background(), q)
+	log.Println("QueryBaseUserIDList QueryRow耗时:", time.Since(qStart))
+	if err := rows.Scan(&userList); err != nil {
+		log.Println("QueryBaseUserIdList err:", err)
+		return
+	}
+	return userList
+}

+ 143 - 0
rpc/service/wxRobot.go

@@ -0,0 +1,143 @@
+package service
+
+import (
+	"app.yhyue.com/moapp/jybase/common"
+	"app.yhyue.com/moapp/jybase/go-xweb/xweb"
+	"encoding/json"
+	"github.com/gogf/gf/v2/util/gconv"
+	"rpc/config"
+	"time"
+)
+
+type WXroBot struct {
+	*xweb.Action
+	addTaskHandler    xweb.Mapper `xweb:"/add/task"`    //新增任务
+	updateTaskHandler xweb.Mapper `xweb:"/update/task"` //修改任务
+}
+
+func init() {
+	xweb.AddAction(&WXroBot{})
+}
+
+type TaskRequest struct {
+	TaskID        int64                    `json:"task_id,omitempty"`
+	TaskName      string                   `json:"task_name"`
+	SendGroupID   string                   `json:"sendGroupId"`
+	SendGroupName string                   `json:"sendGroupName"`
+	TaskType      int64                    `json:"taskType"`
+	SendTimeType  int64                    `json:"sendTimeType"`
+	SendTime      string                   `json:"sendTime"`
+	Content       []map[string]interface{} `json:"content"`
+}
+
+func (a *WXroBot) AddTaskHandler() {
+	var req TaskRequest
+	err := json.Unmarshal(a.Body(), &req)
+	taskId := int64(0)
+	msg := ""
+	userid := common.ObjToString(a.GetSession("userId"))
+	status := int64(0)
+	content := gconv.Maps(req.Content)
+	if err == nil && len(content) > 0 {
+		taskType := req.TaskType
+		sendTimeType := req.SendTimeType
+		taskId = config.WxRobot.Insert("task", map[string]interface{}{
+			"task_name":        req.TaskName,
+			"send_group_id":    req.SendGroupID,
+			"task_type":        req.TaskType,
+			"send_time_type":   req.SendTimeType,
+			"send_time":        req.SendTime,
+			"task_status":      0,
+			"create_time":      time.Now().Format(time.DateTime),
+			"update_time":      time.Now().Format(time.DateTime),
+			"create_person_id": userid,
+			"send_group_name":  req.SendGroupName,
+		})
+		if taskId == 0 {
+			msg = "保存失败"
+		} else {
+			for _, v := range content {
+				text := gconv.String(v["text"])
+				fileType := gconv.Int64(v["type"])
+				config.WxRobot.Insert("task_send_content", map[string]interface{}{
+					"task_id":      taskId,
+					"content":      text,
+					"content_type": fileType,
+					"create_time":  time.Now().Format(time.DateTime),
+					"update_time":  time.Now().Format(time.DateTime),
+					"status":       0,
+				})
+			}
+			status = 1
+			if taskType == 0 && sendTimeType == 0 {
+				go func() {
+					allUserMap := InitUser()
+					ScheduledTasks(allUserMap, taskId)
+				}()
+
+			}
+		}
+
+	} else {
+		msg = "参数格式不对"
+	}
+	a.ServeJson(config.Result{Data: config.M{"status": status}, Error_msg: msg})
+}
+
+func (a *WXroBot) UpdateTaskHandler() {
+	var req TaskRequest
+	err := json.Unmarshal(a.Body(), &req)
+	msg := ""
+	status := int64(0)
+	content := gconv.Maps(req.Content)
+	if err == nil && len(content) > 0 {
+		taskId := req.TaskID
+		taskType := req.TaskType
+		sendTimeType := req.SendTimeType
+		ok := config.WxRobot.Update("task", map[string]interface{}{
+			"id": taskId,
+		}, map[string]interface{}{
+			"task_name":       req.TaskName,
+			"send_group_id":   req.SendGroupID,
+			"task_type":       req.TaskType,
+			"send_time_type":  req.SendTimeType,
+			"send_time":       req.SendTime,
+			"task_status":     0,
+			"update_time":     time.Now().Format(time.DateTime),
+			"send_group_name": req.SendGroupName,
+		})
+		if ok {
+			config.WxRobot.Update("task_send_content", map[string]interface{}{
+				"task_id": taskId,
+			}, map[string]interface{}{
+				"status": 1,
+			})
+			for _, v := range content {
+				text := gconv.String(v["text"])
+				fileType := gconv.Int64(v["type"])
+				config.WxRobot.Insert("task_send_content", map[string]interface{}{
+					"task_id":      taskId,
+					"content":      text,
+					"content_type": fileType,
+					"create_time":  time.Now().Format(time.DateTime),
+					"update_time":  time.Now().Format(time.DateTime),
+					"status":       0,
+				})
+			}
+			status = 1
+			if taskType == 0 && sendTimeType == 0 {
+				go func() {
+					allUserMap := InitUser()
+					ScheduledTasks(allUserMap, taskId)
+				}()
+
+			}
+		} else {
+			msg = "修改失败"
+		}
+
+	} else {
+		msg = "参数格式不对"
+	}
+	a.ServeJson(config.Result{Data: config.M{"status": status}, Error_msg: msg})
+}