| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- package core
- import (
- "fmt"
- "regexp"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- "github.com/cdle/sillyplus/core/common"
- "github.com/cdle/sillyplus/core/logs"
- "github.com/cdle/sillyplus/core/storage"
- "github.com/cdle/sillyplus/utils"
- "github.com/goccy/go-json"
- )
- // var replyRe = regexp.MustCompile(`\$\{\s*([^\s{}]+)\s*\}`)
- var total uint64 = 0
- var finished uint64 = 0
- var contents sync.Map
- var Functions = []*common.Function{}
- var Messages chan common.Sender
- var ListenOnGroups sync.Map
- var NoListenUsers sync.Map
- var NoReplyGroups sync.Map
- var StaticListenOnGroups sync.Map
- var StaticNoReplyGroups sync.Map
- var noListenUsers = MakeBucket("noListenUsers")
- var listenOnGroups = MakeBucket("listenOnGroups")
- var noReplyGroups = MakeBucket("noReplyGroups")
- type GroupInfo struct {
- Platform string `json:"platform"`
- Desc string `json:"desc"`
- Enable bool `json:"enable"`
- }
- var AddNoReplyGroups = func(code string, desc string, plt string) {
- _, loaded := NoReplyGroups.LoadOrStore(code, plt)
- if !loaded {
- // logs.Info(desc)
- }
- }
- var AddListenOnGroup = func(code string, desc string, plt string) {
- _, loaded := ListenOnGroups.LoadOrStore(code, plt)
- if !loaded {
- // logs.Info(desc)
- }
- }
- var RemNoReplyGroups = func(code string, desc string) {
- _, loaded := NoReplyGroups.Load(code)
- if loaded {
- NoReplyGroups.Delete(code)
- // logs.Info(desc)
- }
- }
- var RemListenOnGroup = func(code string, desc string) {
- _, loaded := ListenOnGroups.Load(code)
- if loaded {
- ListenOnGroups.Delete(code)
- // logs.Info(desc)
- }
- }
- var IsNoReplyGroup = func(s common.Sender) bool {
- cid := s.GetChatID()
- if utils.IsZeroOrEmpty(cid) {
- return false
- }
- _, ok1 := NoReplyGroups.Load(cid)
- _, ok2 := StaticNoReplyGroups.Load(cid)
- res := ok1 || ok2
- if res {
- logs.Info("禁言的群组 %v/%v@%v", s.GetImType(), s.GetUserID(), cid)
- }
- return res
- }
- func initListenReply() {
- listenOnGroups.Foreach(func(b1, data []byte) error {
- groupCode := string(b1)
- info := &GroupInfo{}
- err := json.Unmarshal(data, info)
- if err != nil {
- listenOnGroups.Set(groupCode, "")
- } else {
- if info.Enable {
- StaticListenOnGroups.Store(string(b1), info.Platform)
- }
- }
- return nil
- })
- storage.Watch(listenOnGroups, nil, func(old, new, key string) (fin *storage.Final) {
- if new == "" {
- logs.Info("已删除监听群组%s", key)
- StaticListenOnGroups.Delete(key)
- return
- }
- info := &GroupInfo{}
- json.Unmarshal([]byte(new), info)
- if info.Enable {
- StaticListenOnGroups.Store(key, info.Platform)
- logs.Info("已设置监听群组%s/%s", info.Platform, key)
- } else {
- StaticListenOnGroups.Delete(key)
- logs.Info("已取消监听群组%s/%s", info.Platform, key)
- }
- return
- })
- noReplyGroups.Foreach(func(b1, data []byte) error {
- groupCode := string(b1)
- info := &GroupInfo{}
- err := json.Unmarshal(data, info)
- if err != nil {
- noReplyGroups.Set(groupCode, "")
- } else {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- if info.Enable {
- StaticNoReplyGroups.Store(string(b1), info.Platform)
- }
- }
- return nil
- })
- storage.Watch(noReplyGroups, nil, func(old, new, key string) (fin *storage.Final) {
- if new == "" {
- logs.Info("已删除禁言群组%s", key)
- StaticNoReplyGroups.Delete(key)
- return
- }
- info := &GroupInfo{}
- json.Unmarshal([]byte(new), info)
- if info.Enable {
- logs.Info("已设置禁言群组%s/%s", info.Platform, key)
- StaticNoReplyGroups.Store(key, info.Platform)
- } else {
- logs.Info("已取消禁言群组%s%s", info.Platform, key)
- StaticNoReplyGroups.Delete(key)
- }
- return
- })
- noListenUsers.Foreach(func(b1, data []byte) error {
- groupCode := string(b1)
- info := &GroupInfo{}
- err := json.Unmarshal(data, info)
- if err != nil {
- noListenUsers.Set(groupCode, "")
- } else {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- // fmt.Println(string(b1), string(utils.JsonMarshal(info)))
- if info.Enable {
- NoListenUsers.Store(string(b1), info.Platform)
- }
- }
- return nil
- })
- storage.Watch(noListenUsers, nil, func(old, new, key string) (fin *storage.Final) {
- if new == "" {
- logs.Info("已取消屏蔽用户%s", key)
- NoListenUsers.Delete(key)
- return
- }
- info := &GroupInfo{}
- json.Unmarshal([]byte(new), info)
- if info.Enable {
- logs.Info("已屏蔽用户%s/%s", info.Platform, key)
- NoListenUsers.Store(key, info.Platform)
- } else {
- logs.Info("已取消屏蔽用户%s%s", info.Platform, key)
- NoListenUsers.Delete(key)
- }
- return
- })
- }
- func initToHandleMessage() {
- listen_admin := sillyGirl.GetBool("listen_admin", true)
- storage.Watch(sillyGirl, "listen_admin", func(old, new, key string) *storage.Final {
- if new == "false" {
- listen_admin = false
- }
- return nil
- })
- Messages = make(chan common.Sender)
- go func() {
- for {
- s := <-Messages
- ignore := false
- cid := s.GetChatID()
- uid := s.GetUserID()
- imType := s.GetImType()
- isAdmin := s.IsAdmin()
- uname := s.GetUserName()
- ctt := s.GetContent()
- if !utils.IsZeroOrEmpty(cid) {
- cname := s.GetChatName()
- if cname != "" {
- CreateNickName(&Nickname{
- ID: cid,
- Group: true,
- Value: cname,
- Platform: imType,
- BotsID: []string{s.GetBotID()},
- })
- }
- if isAdmin {
- switch ctt {
- case "listen":
- if data := listenOnGroups.GetBytes(cid); len(data) == 0 {
- listenOnGroups.Set(cid, utils.JsonMarshal(&GroupInfo{
- Platform: imType,
- Enable: true,
- Desc: s.GetChatName(),
- }))
- } else {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- if !info.Enable {
- info.Enable = !info.Enable
- listenOnGroups.Set(cid, utils.JsonMarshal(info))
- }
- }
- s.Reply("ok")
- case "unlisten", "nolisten":
- if data := listenOnGroups.GetBytes(cid); len(data) != 0 {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- if info.Enable {
- info.Enable = !info.Enable
- listenOnGroups.Set(cid, utils.JsonMarshal(info))
- }
- }
- s.Reply("ok")
- case "reply":
- // if data := noReplyGroups.GetBytes(cid); len(data) != 0 {
- info := &GroupInfo{}
- // if info.Enable {
- // info.Enable = !info.Enable
- noReplyGroups.Set(cid, utils.JsonMarshal(info))
- // }
- // }
- s.Reply("ok")
- case "noreply", "unreply":
- if data := noReplyGroups.GetBytes(cid); len(data) == 0 {
- noReplyGroups.Set(cid, utils.JsonMarshal(&GroupInfo{
- Platform: imType,
- Enable: true,
- Desc: s.GetChatName(),
- }))
- } else {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- if !info.Enable {
- info.Enable = !info.Enable
- noReplyGroups.Set(cid, utils.JsonMarshal(info))
- }
- }
- s.Reply("ok")
- }
- }
- _, ok1 := ListenOnGroups.Load(cid)
- if !ok1 {
- if !listen_admin || !isAdmin {
- _, ok2 := StaticListenOnGroups.Load(cid)
- if !ok2 {
- ignore = true
- }
- }
- }
- } else {
- if isAdmin {
- switch ctt {
- case "unlisten", "nolisten":
- if data := noListenUsers.GetBytes(uid); len(data) == 0 {
- noListenUsers.Set(uid, utils.JsonMarshal(&GroupInfo{
- Platform: imType,
- Enable: true,
- Desc: s.GetChatName(),
- }))
- } else {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- if !info.Enable {
- info.Enable = !info.Enable
- noListenUsers.Set(uid, utils.JsonMarshal(info))
- }
- }
- s.Reply("ok")
- case "listen":
- if data := noListenUsers.GetBytes(uid); len(data) != 0 {
- info := &GroupInfo{}
- json.Unmarshal(data, info)
- if info.Enable {
- info.Enable = !info.Enable
- noListenUsers.Set(uid, utils.JsonMarshal(info))
- }
- }
- s.Reply("ok")
- }
- }
- }
- _, ok2 := NoListenUsers.Load(uid)
- if ok2 {
- ignore = true
- }
- if uname != "" {
- CreateNickName(&Nickname{
- ID: uid,
- Group: false,
- Value: uname,
- Platform: imType,
- BotsID: []string{s.GetBotID()},
- })
- }
- if imType != "terminal" {
- if !ignore {
- logs.Info("接收到消息 %v/%v@%v:%s", imType, uid, cid, ctt)
- } else {
- logs.Info("屏蔽的消息 %v/%v@%v:%s", imType, uid, cid, ctt)
- }
- }
- if ignore {
- continue
- }
- go HandleMessage(s)
- }
- }()
- }
- func fmtRule(cmd *common.Function) {
- for i := range cmd.Rules {
- cmd.Rules[i] = strings.Trim(cmd.Rules[i], "")
- cmd.Params = append(cmd.Params, []string{})
- if strings.HasPrefix(cmd.Rules[i], "raw") {
- cmd.Rules[i] = strings.Replace(cmd.Rules[i], "raw ", "", -1)
- continue
- }
- if strings.HasPrefix(cmd.Rules[i], "^") {
- continue
- }
- if strings.HasSuffix(cmd.Rules[i], "$") {
- continue
- }
- cmd.Rules[i] = strings.ReplaceAll(cmd.Rules[i], `\r\a\w`, "raw")
- cmd.Rules[i] = strings.Replace(cmd.Rules[i], "(", `[(]`, -1)
- cmd.Rules[i] = strings.Replace(cmd.Rules[i], ")", `[)]`, -1)
- ress := regexp.MustCompile(`\[([^\s\[\]]+)\]`).FindAllStringSubmatch(cmd.Rules[i], -1)
- for _, res := range ress {
- var inner = res[1]
- vv := strings.SplitN(inner, ":", 2)
- name := vv[0]
- if len(vv) == 1 {
- cmd.Rules[i] = strings.ReplaceAll(cmd.Rules[i], res[0], "?")
- } else {
- cmd.Rules[i] = strings.ReplaceAll(cmd.Rules[i], res[0], fmt.Sprintf("(%s)", strings.ReplaceAll(vv[1], ",", "|")))
- }
- cmd.Params[i] = append(cmd.Params[i], name)
- }
- cmd.Rules[i] = regexp.MustCompile(`\?$`).ReplaceAllString(cmd.Rules[i], `([\s\S]+)`)
- cmd.Rules[i] = strings.Replace(cmd.Rules[i], " ", `\s+`, -1)
- cmd.Rules[i] = strings.Replace(cmd.Rules[i], "?", `(\S+)`, -1)
- cmd.Rules[i] = "^" + cmd.Rules[i] + "$"
- }
- }
- func AddCommand(cmds []*common.Function) {
- for j := range cmds {
- if cmds[j].OnStart && !cmds[j].Disable {
- go func(f *common.Function) {
- time.Sleep(time.Second)
- // console.Log("初始化%v服务", f.Title)
- f.Handle(&CustomSender{
- F: &Factory{
- botplt: "*",
- },
- }, nil)
- }(cmds[j])
- }
- fmtRule(cmds[j])
- {
- if !cmds[j].Disable && !cmds[j].Module {
- for plt, Cron := range cmds[j].Cron {
- plt := plt
- cron := strings.TrimSpace(Cron)
- if len(regexp.MustCompile(`\S+`).FindAllString(cron, -1)) == 5 {
- Cron = "0 " + Cron
- }
- cronId, err := CRON.AddFunc(Cron, func() {
- cmds[j].Handle(&CustomSender{
- F: &Factory{
- botplt: plt,
- },
- }, nil)
- })
- if err == nil {
- cmds[j].CronIds = append(cmds[j].CronIds, int(cronId))
- // console["log"]("脚本%s添加定时器", cmds[j].Title)
- } else {
- console.Error("脚本%s定时器错误,%v", cmds[j].Title, err)
- }
- }
- }
- // if cmds[j].Cron != "" && !cmds[j].Disable && !cmds[j].Module && !cmds[j].OnStart {
- // }
- }
- {
- lf := len(Functions)
- for i := range Functions {
- f := lf - i - 1
- if Functions[f].Priority > cmds[j].Priority {
- Functions = append(Functions[:f+1], append([]*common.Function{cmds[j]}, Functions[f+1:]...)...)
- break
- }
- }
- if len(Functions) == lf {
- if lf > 0 {
- apd := false
- for i := range Functions {
- if cmds[j].Priority >= Functions[i].Priority {
- apd = true
- Functions = append(Functions[:i], append([]*common.Function{cmds[j]}, Functions[i:]...)...)
- break
- }
- }
- if !apd {
- Functions = append(Functions, cmds[j])
- }
- } else {
- Functions = append(Functions, cmds[j])
- }
- }
- }
- }
- }
- func HandleMessage(sender common.Sender) {
- if !debug {
- defer func() {
- err := recover()
- if err != nil {
- console.Error("HandleMessage error: %v", err)
- }
- }()
- }
- num := atomic.AddUint64(&total, 1)
- defer atomic.AddUint64(&finished, 1)
- ct := sender.GetContent()
- contents.Store(num, ct)
- defer func() {
- contents.Delete(num)
- }()
- content := utils.TrimHiddenCharacter(ct)
- defer func() {
- sender.Finish()
- if sender.IsAtLast() {
- s := sender.MessagesToSend()
- if s != "" {
- sender.Reply(s)
- }
- }
- }()
- u, g, i, a := sender.GetUserID(), sender.GetChatID(), sender.GetImType(), sender.IsAdmin()
- con := true
- mtd := false
- for _, wait := range waits {
- wait.Foreach(func(k int64, c *Carry) bool {
- // userID := vs.Get("u")
- // chatID := vs.Get("c")
- // imType := vs.Get("i")
- // forGroup := vs.Get("f")
- // if chatID != g && (forGroup != "me" || g != "0") {
- // return true
- // }
- // if userID != u && (forGroup == "" || forGroup == "me") {
- // return true
- // }
- if c.RequireAdmin && !a {
- return true
- }
- // fmt.Println(c.Function.Rules, "==", c.AllowPlatforms, len(c.AllowPlatforms))
- if len(c.AllowPlatforms) != 0 && !Contains(c.AllowPlatforms, i) {
- return true
- }
- // fmt.Println(c.Function.Rules)
- if len(c.ProhibitPlatforms) != 0 && Contains(c.ProhibitPlatforms, i) {
- return true
- }
- if len(c.AllowUsers) != 0 && !Contains(c.AllowUsers, u) {
- return true
- }
- if len(c.ProhibitUsers) != 0 && Contains(c.ProhibitUsers, u) {
- return true
- }
- if len(c.AllowGroups) != 0 && !Contains(c.AllowGroups, g) {
- return true
- }
- if len(c.ProhibitGroups) != 0 && Contains(c.ProhibitGroups, g) {
- return true
- }
- // if c.ChatID != g && (!c.AllowPrivate || g != "") {
- // return true
- // }
- // if c.UserID != u && (c.AllowGroupUsers || c.AllowPrivate) {
- // return true
- // }
- if c.ChatID != "" { //群聊监听
- if g == "" { //私聊时
- if !c.ListenPrivate { //如果未设置允许私聊则拒绝
- return true
- }
- } else { //群聊时
- if c.UserID != "" && u != c.UserID { //群员发言
- if !c.ListenGroup { //未设置允许群员加入拒绝
- return true
- }
- }
- }
- } else { //私聊监听
- if c.UserID != "" && u != c.UserID { //其他用户
- return true
- }
- }
- for i := range c.Function.Rules {
- reg, err := regexp.Compile(c.Function.Rules[i])
- if err != nil {
- console.Error("监听器正则错误,%v", err)
- continue
- }
- // logs.Info("%s规则:%s", c.Function.Title, c.Function.Rules[i])
- if res := reg.FindStringSubmatch(content); len(res) > 0 {
- logs.Info("匹配到%s规则:%s", c.Function.Title, c.Function.Rules[i])
- sender.SetMatch(res[1:])
- sender.SetParams(c.Function.Params[i])
- mtd = true
- c.Chan <- sender
- sender.Reply(<-c.Result)
- if !sender.IsContinue() {
- con = false
- return false
- }
- sender.ClearContinue()
- break
- }
- }
- return true
- })
- }
- if mtd && !con {
- return
- }
- for _, reply := range replies {
- if reply.Keyword == "" || reply.Value == "" {
- continue
- }
- if reply.Number != "" && reply.Number != u && reply.Number != g {
- continue
- }
- if len(reply.Platforms) != 0 && !Contains(reply.Platforms, i) {
- continue
- }
- // if reply.Class == 1 && g != "" {
- // continue
- // } else if reply.Class == 2 && g == "" {
- // continue
- // }
- reg, err := regexp.Compile(reply.Keyword)
- if err == nil {
- if reg.FindString(content) != "" {
- //todo 支持JS语法
- output := parseReply2(reply.Value)
- sender.Reply(output)
- }
- }
- }
- for _, function := range Functions {
- if function.Disable || function.Module {
- continue
- }
- imType := sender.GetImType()
- if (imType != "cron" && imType != "carry" && black(function.ImType, imType)) || black(function.UserId, sender.GetUserID()) || black(function.GroupId, fmt.Sprint(sender.GetChatID())) {
- continue
- }
- for i := range function.Rules {
- var matched bool
- if function.FindAll {
- reg, err := regexp.Compile(function.Rules[i])
- if err != nil {
- console.Error("脚本%s正则错误,%v", function.Title, err)
- continue
- }
- if res := reg.FindAllStringSubmatch(content, -1); len(res) > 0 {
- tmp := [][]string{}
- for i := range res {
- tmp = append(tmp, res[i][1:])
- }
- if !function.Hidden {
- logs.Info("匹配到规则:%s", function.Rules[i])
- }
- sender.SetAllMatch(tmp)
- matched = true
- }
- } else {
- reg, err := regexp.Compile(function.Rules[i])
- if err != nil {
- console.Error("脚本%s正则错误,%v", function.Title, err)
- continue
- }
- if res := reg.FindStringSubmatch(content); len(res) > 0 {
- if !function.Hidden {
- logs.Info("匹配到规则:%s", function.Rules[i])
- }
- sender.SetMatch(res[1:])
- sender.SetParams(function.Params[i])
- matched = true
- }
- }
- if matched {
- if function.Admin && !a {
- return
- }
- rt := function.Handle(sender, nil)
- if rt != nil {
- sender.Reply(rt)
- }
- if sender.IsContinue() {
- sender.ClearContinue()
- content = utils.TrimHiddenCharacter(sender.GetContent())
- if !function.Hidden {
- logs.Info("继续去处理:%s", content)
- }
- goto next
- }
- return
- }
- }
- next:
- }
- recall := sillyGirl.GetString("recall")
- if recall != "" {
- recalled := false
- for _, v := range strings.Split(recall, "&") {
- reg, err := regexp.Compile(v)
- if err == nil {
- if reg.FindString(content) != "" {
- if !a && sender.GetImType() != "wx" {
- sender.Delete()
- recalled = true
- break
- }
- }
- }
- }
- if recalled {
- return
- }
- }
- }
- func black(filter *common.Filter, str string) bool {
- if filter != nil {
- if filter.BlackMode {
- if utils.Contains(filter.Items, str) {
- return true
- }
- } else {
- if !utils.Contains(filter.Items, str) {
- return true
- }
- }
- }
- return false
- }
|