diff --git a/conf/config.go b/conf/config.go index 2397283..1c00bcc 100644 --- a/conf/config.go +++ b/conf/config.go @@ -49,6 +49,12 @@ type Config struct { Port int `json:"port" yaml:"port" mapstructure:"port"` CommonUser map[string]string `json:"common_user" yaml:"common_user" mapstructure:"common_user"` } `json:"web" yaml:"web" mapstructure:"web"` + QQ struct { + Enable bool `json:"enable" mapstructure:"enable"` + PostAddr string `json:"post_addr" mapstructure:"post_addr"` + SuperUser int64 `json:"super_user" mapstructure:"super_user"` + WhiteList []int64 `json:"white_list" mapstructure:"white_list"` + } Cron string `json:"cron" yaml:"cron" mapstructure:"cron"` CronRandomWait int `json:"cron_random_wait" yaml:"cron_random_wait" mapstructure:"cron_random_wait"` EdgePath string `json:"edge_path" yaml:"edge_path" mapstructure:"edge_path"` diff --git a/conf/config_default.yml b/conf/config_default.yml index 2032148..dcf35eb 100644 --- a/conf/config_default.yml +++ b/conf/config_default.yml @@ -81,6 +81,15 @@ wechat: # 微信管理员的openid,可点击关于按钮获得,配置后请重启程序 super_open_id: "" +# gocq推送配置 +qq: + # 是否启用qq推送 + enable: false + # gocq的监听地址 + post_addr: "http://127.0.0.1:5700" + super_user: 123 + white_list: + - 123 # pushDeer推送配置,详情参考psuhDeer官网:http://www.pushdeer.com/official.html push_deer: diff --git a/main.go b/main.go index 2972e0a..1aa5ebb 100644 --- a/main.go +++ b/main.go @@ -149,6 +149,10 @@ func main() { engine := web.RouterInit() go func() { h := http.NewServeMux() + if config.QQ.Enable { + h.Handle("/qq", push.InitQQ()) + log.Infoln(fmt.Sprintf("已开启qq配置,监听地址: ==》 %v:%v", config.Web.Host, config.Web.Port)) + } if config.Web.Enable { log.Infoln(fmt.Sprintf("已开启web配置,web监听地址 ==> %v:%v", config.Web.Host, config.Web.Port)) h.Handle("/", engine) diff --git a/model/user.go b/model/user.go index a984fdb..8a84855 100644 --- a/model/user.go +++ b/model/user.go @@ -69,7 +69,7 @@ func changeStatus(uid string, status int) { func QueryFailUser() ([]*User, error) { var users []*User _ = engine.Ping() - err := engine.Where("status=", 0).Find(users) + err := engine.Where("status=?", 0).Find(&users) if err != nil { return users, err } diff --git a/push/push.go b/push/push.go index a8392ac..bc62a75 100644 --- a/push/push.go +++ b/push/push.go @@ -49,6 +49,13 @@ func GetPush(config conf.Config) func(id string, kind string, message string) { log.Infoln("已配置pushDeer推送") pushs = append(pushs, InitPushDeer()) } + if config.QQ.Enable { + log.Infoln("已配置qq推送") + pushs = append(pushs, func(id, kind, message string) { + e := &Event{qq: qq} + e.sendPrivateMsg(conf.GetConfig().QQ.SuperUser, message) + }) + } pushs = append(pushs, func(id, kind, message string) { log.Debugln(fmt.Sprintf("消息id: %v,消息类型:%v,消息内容:%v", id, kind, message)) }) diff --git a/push/qq.go b/push/qq.go new file mode 100644 index 0000000..47a3106 --- /dev/null +++ b/push/qq.go @@ -0,0 +1,333 @@ +package push + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" + "strings" + + log "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" + + "github.com/johlanse/study_xxqg/conf" + "github.com/johlanse/study_xxqg/lib" + "github.com/johlanse/study_xxqg/lib/state" + "github.com/johlanse/study_xxqg/model" + "github.com/johlanse/study_xxqg/utils" +) + +var ( + qq *QQ +) + +type qqPlugin func(event *Event, args []string) + +type QQ struct { + postAdd string + plugins map[string]qqPlugin +} + +func InitQQ() *QQ { + config := conf.GetConfig() + q := new(QQ) + qq = q + q.postAdd = config.QQ.PostAddr + q.plugins = make(map[string]qqPlugin, 1) + q.newPlugin("user", qqGetUser) + q.newPlugin("score", qqGetScore) + q.newPlugin("fail", qqGetFailUser) + q.newPlugin("study", qqStudy) + q.newPlugin("login", qqLogin) + q.newPlugin("help", qqHelp) + return q +} + +func (q *QQ) newPlugin(text string, plugin qqPlugin) { + q.plugins[text] = plugin +} + +func (q *QQ) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + data, _ := io.ReadAll(request.Body) + go q.handle(data) + writer.WriteHeader(204) +} + +func (q *QQ) handle(data []byte) { + defer func() { + err := recover() + if err != nil { + log.Errorln("qq消息处理错误") + } + }() + config := conf.GetConfig() + e := new(Event) + err := json.Unmarshal(data, e) + if err != nil { + log.Errorln(err.Error()) + return + } + e.qq = q + if e.PostType == "message" { + log.Infoln("收到qq消息 ==》 " + e.Message) + // 遍历白名单列表 + for _, id := range config.QQ.WhiteList { + if e.GroupId == id || e.UserId == id { + for text, plugin := range q.plugins { + messages := strings.Split(e.Message, " ") + if messages[0] == "."+text { + plugin(e, messages[1:]) + return + } + } + } + } + log.Infoln("消息来源不在白名单中!") + + } +} + +func qqHelp(event *Event, _ []string) { + help := ".user 查询用户\n.fail 查询过期用户\n.study 对一个用户进行学习\n.score 查询用户分数\n.login 登录一个用户" + event.Send(help) +} + +func qqLogin(event *Event, _ []string) { + core := &lib.Core{ShowBrowser: conf.GetConfig().ShowBrowser, Push: func(id string, kind string, message string) { + if kind == "flush" { + event.Send(message) + } else { + if conf.GetConfig().LogLevel == "debug" { + event.Send(message) + } + } + }} + _, err := core.L(conf.GetConfig().Retry.Times, "") + if err != nil { + event.Send(err.Error()) + } +} + +func qqStudy(event *Event, args []string) { + users, err := model.Query() + if err != nil { + event.Send(err.Error()) + return + } + var user *model.User + if len(users) == 1 { + user = users[0] + } else { + if len(args) < 0 { + event.Send("缺少序号参数,请输入 .study 序号") + return + } else { + index, err := strconv.Atoi(args[0]) + if err != nil { + event.Send(err.Error()) + return + } + user = users[index] + } + } + core := &lib.Core{ShowBrowser: conf.GetConfig().ShowBrowser, Push: func(id string, kind string, message string) { + if kind == "flush" { + event.Send(message) + } else { + if conf.GetConfig().LogLevel == "debug" { + event.Send(message) + } + } + }} + core.Init() + state.Add(user.Uid, core) + defer state.Delete(user.Uid) + defer core.Quit() + lib.Study(core, user) + +} + +func qqGetScore(event *Event, args []string) { + users, err := model.Query() + if err != nil { + event.Send(err.Error()) + return + } + for _, user := range users { + score, err := lib.GetUserScore(user.ToCookies()) + if err != nil { + event.Send(err.Error()) + continue + } + event.Send(lib.FormatScore(score)) + } +} + +func qqGetFailUser(event *Event, _ []string) { + user, err := model.QueryFailUser() + if err != nil { + event.Send(err.Error()) + return + } + result := "" + for i, user := range user { + result += fmt.Sprintf("%d %v %v\n", i, user.Nick, utils.Stamp2Str(user.LoginTime)) + } + event.Send(result) +} + +func qqGetUser(event *Event, _ []string) { + users, err := model.Query() + if err != nil { + event.Send(err.Error()) + return + } + result := "" + for i, user := range users { + result += fmt.Sprintf("%d %v %v\n", i, user.Nick, utils.Stamp2Str(user.LoginTime)) + } + event.Send(result) +} + +type ( + anonymous struct { + Id int `json:"id"` + Name string `json:"name"` + Flag string `json:"flag"` + } + + Files struct { + Id string `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + Busid int64 `json:"busid"` + FileUrl string `json:"url"` + } + + Status struct { + AppEnabled bool `json:"app_enabled"` + AppGood bool `json:"app_good"` + AppInitialized bool `json:"app_initialized"` + Good bool `json:"good"` + Online bool `json:"online"` + PluginsGood interface{} `json:"plugins_good"` + Stat struct { + PacketReceived int `json:"packet_received"` + PacketSent int `json:"packet_sent"` + PacketLost int `json:"packet_lost"` + MessageReceived int `json:"message_received"` + MessageSent int `json:"message_sent"` + DisconnectTimes int `json:"disconnect_times"` + LostTimes int `json:"lost_times"` + LastMessageTime int `json:"last_message_time"` + } `json:"stat"` + } + + MessageIds struct { + MessageID int32 `json:"message_id"` + } + + Senders struct { + Age int `json:"age"` + Area string `json:"area"` + Card string `json:"card"` + Level string `json:"level"` + NickName string `json:"nickname"` + Role string `json:"role"` + Sex string `json:"sex"` + Title string `json:"title"` + UserId int `json:"user_id"` + } + + // Event + /* + * 事件 + * + */ + Event struct { + qq *QQ + Anonymous anonymous `json:"anonymous"` + Font int `json:"font"` + GroupId int64 `json:"group_id"` + Message string `json:"message"` + MessageType string `json:"message_type"` + PostType string `json:"post_type"` + RawMessage string `json:"raw_message"` + SelfId int64 `json:"self_id"` + Sender Senders `json:"sender"` + SubType string `json:"sub_type"` + UserId int64 `json:"user_id"` + Time int `json:"time"` + NoticeType string `json:"notice_type"` + RequestType string `json:"request_type"` + Comment string `json:"comment"` + Flag string `json:"flag"` + OperatorID int `json:"operator_id"` + File Files `json:"file"` + Duration int64 `json:"duration"` + TargetId int64 `json:"target_id"` // 运气王id + HonorType string `json:"honor_type"` + MetaEventType string `json:"meta_event_type"` + Status Status `json:"status"` + Interval int `json:"interval"` + CardNew string `json:"card_new"` // 新名片 + CardOld string `json:"card_old"` // 旧名片 + MessageIds + + GuildID int64 `json:"guild_id"` + ChannelID int64 `json:"channel_id"` + } +) + +func (e *Event) sendGroupMsg(groupId int64, message any) int { + if _, ok := message.(string); ok { + message = map[string]any{ + "type": "text", + "data": map[string]any{ + "text": message, + }, + } + } + response, err := utils.GetClient().R().SetBodyJsonMarshal(map[string]any{ + "action": "send_group_msg", + "params": map[string]any{ + "group_id": groupId, + "message": message, + }, + }).Post(e.qq.postAdd) + if err != nil { + return 0 + } + return int(gjson.GetBytes(response.Bytes(), "data").Int()) +} + +func (e *Event) Send(message any) { + if e.MessageType == "group" { + e.sendGroupMsg(e.GroupId, message) + } else { + e.sendPrivateMsg(e.UserId, message) + } +} + +func (e *Event) sendPrivateMsg(userId int64, message any) int { + if _, ok := message.(string); ok { + message = map[string]any{ + "type": "text", + "data": map[string]any{ + "text": message, + }, + } + } + response, err := utils.GetClient().R().SetBodyJsonMarshal(map[string]any{ + "action": "send_private_msg", + "params": map[string]any{ + "user_id": userId, + "message": message, + }, + }).Post(e.qq.postAdd) + if err != nil { + return 0 + } + return int(gjson.GetBytes(response.Bytes(), "data").Int()) +}