package core import ( "context" "errors" "fmt" "math/rand" "reflect" "regexp" "strings" "sync" "time" "github.com/cdle/sillyplus/core/common" "github.com/cdle/sillyplus/core/logs" "github.com/cdle/sillyplus/utils" "github.com/dop251/goja" ) type Message map[string]string type Details struct { Content string UserID string ChatID string Username string Chatname string MessageID string } type CustomSender struct { BaseSender details Details f *Factory } type MsgChan struct { Chan chan string Msg map[string]string } type Factory struct { botid string botplt string uuid string msgChan chan MsgChan demo *CustomSender reply func(map[string]string) string lm chan bool nm int64 recallMessage func(interface{}) bool groupKick func(uid string, gid string, reject_add_request bool) bool groupBan func(uid string, gid string, duration int) bool groupUnban func(uid string, gid string) bool isAdmin func(string) bool vm *goja.Runtime ctx context.Context cancel context.CancelFunc destroid bool } type Bot [2]string //botplt botid var Bots = map[Bot]*Factory{} var BotsLocker sync.RWMutex var ErrNotFind = errors.New("adapter not find") func DestroyAdapterByUUID(uuid string) { // BotsLocker.RLock() // defer BotsLocker.RUnlock() // for i := range Bots { // if Bots[i].uuid == uuid { // go Bots[i].Destroy() // } // } } func GetAdapter(botplt string, bots_id ...string) (*Factory, error) { BotsLocker.RLock() defer BotsLocker.RUnlock() var bots = []*Factory{} var select_bots = []*Factory{} for i := range Bots { plt, id := i[0], i[1] // fmt.Println("plt", plt, "id", id, botplt, bots_id) for j := range bots_id { if plt == botplt && bots_id[j] == id { select_bots = append(select_bots, Bots[i]) } if plt == botplt { bots = append(bots, Bots[i]) } } } if len(bots) == 0 { return nil, ErrNotFind } if len(select_bots) != 0 { i := rand.Intn(len(select_bots)) return select_bots[i], nil } i := rand.Intn(len(bots)) return bots[i], ErrNotFind } func GetAdapters(botplt string, bots_id ...string) ([]*Factory, error) { BotsLocker.RLock() defer BotsLocker.RUnlock() var bots = []*Factory{} var select_bots = []*Factory{} for i := range Bots { plt, id := i[0], i[1] // fmt.Println("plt", plt, "id", id, botplt, bots_id) for j := range bots_id { if plt == botplt && bots_id[j] == id { select_bots = append(select_bots, Bots[i]) } if plt == botplt { bots = append(bots, Bots[i]) } } } if len(bots) == 0 { return nil, ErrNotFind } if len(select_bots) != 0 { return select_bots, nil } return bots, ErrNotFind } func GetAdapterBotsID(botplt string) []string { BotsLocker.RLock() defer BotsLocker.RUnlock() var bots_id = []string{} for i := range Bots { // fmt.Println("==", botplt == i[0], botplt, i[0]) if botplt == i[0] { bots_id = append(bots_id, i[1]) } } return bots_id } func GetAdapterBotPlts() []string { BotsLocker.RLock() defer BotsLocker.RUnlock() var bot_plts = []string{} for i := range Bots { // fmt.Println("==", botplt == i[0], botplt, i[0]) has := false for _, bot_plt := range bot_plts { if bot_plt == i[0] { has = true } } if !has { bot_plts = append(bot_plts, i[0]) } } return bot_plts } func (f *Factory) Init(botplt, botid string) { f.ctx, f.cancel = context.WithCancel(context.Background()) BotsLocker.Lock() defer BotsLocker.Unlock() f.botplt = botplt f.botid = botid f.msgChan = make(chan MsgChan, 100000) f.demo = &CustomSender{ f: f, } if _, ok := Bots[[2]string{botplt, botid}]; ok { console.Warn("%s机器人%s因冲突销毁!", botplt, botid) } Bots[[2]string{botplt, botid}] = f f.lm = make(chan bool, 10) f.nm = 0 if botid != "" { botid = fmt.Sprintf("(%s)", botid) } console.Log("%s机器人%s已初始化", botplt, botid) } func (f *Factory) IsAdapter(botid string) bool { BotsLocker.RLock() defer BotsLocker.RUnlock() for i := range Bots { id := i[0] if botid == id { return true } } return false } func (f *Factory) Destroy() { BotsLocker.Lock() defer BotsLocker.Unlock() f.destroid = true f.cancel() close(f.msgChan) delete(Bots, [2]string{f.botplt, f.botid}) botid := "" if f.botid != "" { botid = fmt.Sprintf("(%s)", f.botid) } else { botid = f.botid } console.Log("%s机器人%s已销毁", strings.ToUpper(f.botplt), botid) } func (f *Factory) Push(msg map[string]string) (string, error) { var demo = *f.demo var sender = &demo fsps := &common.FakerSenderParams{ UserID: msg[USER_ID], ChatID: msg[CHAT_ID], } sender.SetFsps(fsps) return sender.Reply(msg[CONETNT], PUSH("")) } func (f *Factory) SetReplyHandler(function func(map[string]string) string) { f.reply = func(m map[string]string) string { if f.uuid != "" { mutex := GetMutex(f.uuid) mutex.Lock() defer mutex.Unlock() } defer func() { err := recover() if err != nil { console.Error("Sender(\""+f.botplt+"\").SetReply error:", err) } }() return function(m) } } // func (f *Factory) GetReplies() { // } func GetReplyMessage(vm *goja.Runtime, plt string, bots_id []string) *goja.Promise { promise, resolve, reject := vm.NewPromise() adapters, err := GetAdapters(plt, bots_id...) if err != nil { go func() { time.Sleep(time.Second) reject(Error(vm, err)) }() return promise } ctx, cancel := context.WithCancel(context.Background()) for i := range adapters { go func(i int) { select { case <-ctx.Done(): logs.Debug("消息获取中断") case <-adapters[i].ctx.Done(): cancel() logs.Debug("%s adapter %s destroied", adapters[i].botplt, adapters[i].botid) reject("adapter destroied") case mc := <-adapters[i].msgChan: cancel() var msg = map[string]interface{}{} for k, v := range mc.Msg { msg[k] = v } obj := adapters[i].vm.NewObject() for k, v := range mc.Msg { obj.Set(k, v) } obj.Set("bot_id", adapters[i].botid) // msg["bot_id"] = adapters[i].botid // msg["setMessageId"] = func(id string) { // select { // case <-mc.Chan: // case <-time.After(time.Millisecond): // mc.Chan <- id // } // } // resolve(msg) resolve(vm.NewProxy(obj, &goja.ProxyTrapConfig{ Set: func(target *goja.Object, property string, value, receiver goja.Value) (success bool) { if property == "message_id" { select { case <-mc.Chan: return false case <-time.After(time.Millisecond): } mc.Chan <- fmt.Sprint(value.Export()) } return true }, })) } }(i) } return promise } func (f *Factory) GetReplyMessage() *goja.Promise { promise, resolve, reject := f.vm.NewPromise() go func() { select { case <-f.ctx.Done(): logs.Debug("%s adapter %s destroied", f.botplt, f.botid) reject("adapter destroied") case mc := <-f.msgChan: obj := f.vm.NewObject() for k, v := range mc.Msg { obj.Set(k, v) } resolve(f.vm.NewProxy(obj, &goja.ProxyTrapConfig{ Set: func(target *goja.Object, property string, value, receiver goja.Value) (success bool) { if property == "message_id" { select { case <-mc.Chan: return false case <-time.After(time.Millisecond): } mc.Chan <- fmt.Sprint(value.Export()) } return true }, })) } }() return promise } func (f *Factory) Send(function func(map[string]string) string) { f.SetReplyHandler(function) } func (f *Factory) SetRecallMessage(function func(interface{}) bool) { f.recallMessage = func(i interface{}) bool { defer func() { err := recover() if err != nil { console.Error("Sender(\""+f.botplt+"\").recall error:", err) } }() return function(i) } } func (f *Factory) SetGroupKick(function func(uid string, gid string, reject_add_request bool) bool) { f.groupKick = func(uid string, gid string, reject_add_request bool) bool { defer func() { err := recover() if err != nil { console.Error("Sender(\""+f.botplt+"\").GroupKick error:", err) } }() return function(uid, gid, reject_add_request) } } func (f *Factory) SetGroupBan(function func(uid string, gid string, duration int) bool) { f.groupBan = func(uid string, gid string, duration int) bool { defer func() { err := recover() if err != nil { console.Error("Sender(\""+f.botplt+"\").SetGroupBan error:", err) } }() return function(uid, gid, duration) } } func (f *Factory) SetGroupUnban(function func(uid string, gid string) bool) { f.groupUnban = func(uid string, gid string) bool { defer func() { err := recover() if err != nil { console.Error("Sender(\""+f.botplt+"\").SetgroupUnban error:", err) } }() return function(uid, gid) } } func (f *Factory) SetIsAdmin(function func(string) bool) { f.isAdmin = func(uid string) bool { defer func() { err := recover() if err != nil { console.Error("Sender(\""+f.botplt+"\").SetAdmin error:", err) } }() return function(uid) } } func (f *Factory) Receive(wt interface{}) *CustomSender { var demo = *f.demo sender := &demo props := wt.(map[string]interface{}) for i := range props { switch strings.ToLower(i) { case "content": sender.details.Content = fmt.Sprint(props[i]) case "message_id", "messageId": sender.details.MessageID = fmt.Sprint(props[i]) case "user_id", "userId": sender.details.UserID = fmt.Sprint(props[i]) case "chat_id", "chatId", "group_id", "groupId", "group_code", "groupCode": sender.details.ChatID = fmt.Sprint(props[i]) case "user_name", "userName": sender.details.Username = fmt.Sprint(props[i]) case "chat_name", "chatName", "groupName", "group_name": sender.details.Chatname = fmt.Sprint(props[i]) } } if sender.details.Content != "" { Messages <- sender } return sender } func (sender *CustomSender) GetContent() string { if sender.Fsps.Content != "" { return sender.Fsps.Content } return sender.details.Content } func (sender *CustomSender) GetUserID() string { if sender.Fsps.UserID != "" { return sender.Fsps.UserID } return sender.details.UserID } func (sender *CustomSender) GetChatID() string { if !utils.IsZeroOrEmpty(sender.Fsps.ChatID) { return fmt.Sprint(sender.Fsps.ChatID) } return sender.details.ChatID } func (sender *CustomSender) GetImType() string { return sender.f.botplt } func (sender *CustomSender) GetUserName() string { return sender.details.Username } func (sender *CustomSender) GetChatName() string { return sender.details.Chatname } func (sender *CustomSender) GetMessageID() string { return sender.details.MessageID } func (sender *CustomSender) GetReplySenderUserID() int { if !sender.IsReply() { return 0 } return 0 } func (sender *CustomSender) GetBotID() string { return sender.f.botid } type PUSH string func (sender *CustomSender) Reply(msgs ...interface{}) (string, error) { var push = false var content = "" var platform = sender.f.botplt var bot_id = sender.f.botid for _, item := range msgs { switch item := item.(type) { case PUSH: push = true case string: content = item } } if !push { if IsNoReplyGroup(sender) { return "", errors.New("is no reply group") } } content = strings.ReplaceAll(content, "\n\r", "\n") content = strings.ReplaceAll(content, "\r\n", "\n") content = strings.ReplaceAll(content, "\r", "\n") content = regexp.MustCompile("[\n]{3,}").ReplaceAllString(content, "\n\n") if content != "" { msg := map[string]string{ "message_id": sender.GetMessageID(), "content": content, "user_id": sender.GetUserID(), "chat_id": sender.GetChatID(), "bot_id": bot_id, // "uuid": utils.GenUUID(), } if sender.f.reply == nil { var any *common.Function var one *common.Function for _, function := range Functions { if function.Reply != nil && function.Reply.Platform == platform { if len(function.Reply.BotsID) == 0 { any = function } else if Contains(function.Reply.BotsID, sender.f.botid) { one = function } } } if one == nil && any != nil { one = any } if one != nil { message_id := "" one.Handle(&Faker{ Type: "*", }, func(vm *goja.Runtime) { obj := vm.NewObject() for k, v := range msg { obj.Set(k, v) } proxy := vm.NewProxy(obj, &goja.ProxyTrapConfig{ Set: func(target *goja.Object, property string, value, receiver goja.Value) (success bool) { message_id = fmt.Sprint(value.Export()) return true }, }) vm.Set("msg", proxy) vm.Set("message", proxy) }) return message_id, nil } else { c := MsgChan{ Msg: msg, Chan: make(chan string), } if sender.f.destroid { return "", errors.New("adapter destroid") } sender.f.msgChan <- c select { case id := <-c.Chan: return id, nil case <-time.After(time.Second * 5): close(c.Chan) return "", errors.New("get message_id timeout") } } } else { //todo 阻塞延迟异常 v := sender.f.reply(msg) return v, nil } } return "", nil } const ( MESSAGE_ID = "message_id" CONETNT = "content" USER_ID = "user_id" CHAT_ID = "chat_id" IS_ADMIN = "is_admin" // UUID = "message_id" ) func (sender *CustomSender) Copy() common.Sender { new := reflect.Indirect(reflect.ValueOf(interface{}(sender))).Interface().(CustomSender) return &new } func (sender *CustomSender) RecallMessage(ps ...interface{}) error { if sender.f.recallMessage == nil { return nil } for _, p := range ps { switch p := p.(type) { case string: sender.f.recallMessage(p) case []string: for _, v := range p { sender.f.recallMessage(v) } case [][]string: for _, v := range p { for _, v2 := range v { sender.f.recallMessage(v2) } } } } return nil } func (sender *CustomSender) GroupKick(uid string, reject_add_request bool) { if sender.f.groupKick == nil { return } sender.f.groupKick(uid, sender.GetChatID(), reject_add_request) } func (sender *CustomSender) GroupBan(uid string, duration int) { if sender.f.groupBan == nil { return } sender.f.groupBan(uid, sender.GetChatID(), duration) } func (sender *CustomSender) GroupUnban(uid string) { if sender.f.groupUnban == nil { return } sender.f.groupUnban(uid, sender.GetChatID()) } func (sender *CustomSender) IsAdmin() bool { if sender.f.isAdmin == nil { return Contains(strings.Split(MakeBucket(sender.f.botplt).GetString("masters"), "&"), sender.GetUserID()) } return sender.f.isAdmin(sender.GetUserID()) }