diff --git a/app/friend.go b/app/friend.go index 7f65080..42b549c 100644 --- a/app/friend.go +++ b/app/friend.go @@ -235,6 +235,29 @@ func ChangeEnableNewsStatus(ctx *gin.Context) { ctx.String(http.StatusOK, "操作成功") } +// ChangeEnableHotTopStatus +// @description: 修改是否开启热搜 +// @param ctx +func ChangeEnableHotTopStatus(ctx *gin.Context) { + var p changeStatusParam + if err := ctx.ShouldBindJSON(&p); err != nil { + ctx.String(http.StatusBadRequest, "参数错误") + return + } + log.Printf("待修改的Id:%s", p.WxId) + + err := client.MySQL.Model(&entity.Friend{}). + Where("wxid = ?", p.WxId). + Update("`enable_hot_top`", gorm.Expr(" !`enable_hot_top`")).Error + if err != nil { + log.Printf("修改热榜启用状态失败:%s", err) + ctx.String(http.StatusInternalServerError, "操作失败: %s", err) + return + } + + ctx.String(http.StatusOK, "操作成功") +} + // AutoClearMembers // @description: 自动清理群成员 // @param ctx diff --git a/config.yaml b/config.yaml index 15793ed..8aa8746 100644 --- a/config.yaml +++ b/config.yaml @@ -19,11 +19,13 @@ system: welcome: true # 每日早报 news: true + # 热榜 + hotTop: true # 微信HOOK配置 wechat: # 微信HOOK接口地址 - host: 10.0.0.73:19088 + host: 10.0.0.79:19088 # 微信容器映射出来的vnc页面地址,没有就不填 # vncUrl: http://192.168.1.175:19087/vnc_lite.html # 是否在启动的时候自动设置hook服务的回调 @@ -44,11 +46,15 @@ mysql: db: wechat schema: public # postgres 专用 +# 定时任务 task: - enable: false + enable: true news: - enable: true + enable: false cron: '14 11 * * *' # 每天0:30 + hotTop: + enable: true + cron: '0 */1 * * *' # 每小时一次 syncFriends: enable: false cron: '*/5 * * * *' # 五分钟一次 diff --git a/config/system.go b/config/system.go index 990735c..49ec19b 100644 --- a/config/system.go +++ b/config/system.go @@ -20,4 +20,5 @@ type defaultRule struct { Summary bool `json:"summary" yaml:"summary"` // 是否启用聊天总结 Welcome bool `json:"welcome" yaml:"welcome"` // 是否启用欢迎新成员 News bool `json:"news" yaml:"news"` // 是否启用每日早报 + HotTop bool `json:"hotTop" yaml:"hotTop"` // 是否启用热门话题 } diff --git a/config/task.go b/config/task.go index f29ad57..eb1c040 100644 --- a/config/task.go +++ b/config/task.go @@ -8,6 +8,7 @@ type task struct { SyncFriends syncFriends `json:"syncFriends" yaml:"syncFriends"` // 同步好友 WaterGroup waterGroup `json:"waterGroup" yaml:"waterGroup"` // 水群排行榜 GroupSummary syncFriends `json:"groupSummary" yaml:"groupSummary"` // 群聊总结 + HotTop syncFriends `json:"hotTop" yaml:"hotTop"` // 热搜排行榜 } // syncFriends diff --git a/model/dto/news.go b/model/dto/news.go index bf2b10b..94d24fc 100644 --- a/model/dto/news.go +++ b/model/dto/news.go @@ -16,3 +16,23 @@ type MorningPost struct { Usage int `json:"usage"` LogId string `json:"log_id"` } + +// HotTop +// @description: 热搜排行榜返回结构体 +type HotTop struct { + Success bool `json:"success"` // 是否成功 + Name string `json:"name"` // 渠道 + Subtitle string `json:"subtitle"` // 副标题 + UpdateTime string `json:"update_time"` // 更新时间 + Data []HotTopDataItem `json:"data"` // 数据 +} + +// HotTopDataItem +// @description: 热搜排行榜数据项 +type HotTopDataItem struct { + Index int `json:"index"` // 排行 + Title string `json:"title"` // 标题 + Hot string `json:"hot"` // 热度 + Url string `json:"url"` // 链接 + MobilUrl string `json:"mobilUrl"` // 手机端链接 +} diff --git a/model/entity/friend.go b/model/entity/friend.go index 26796c7..1e497aa 100644 --- a/model/entity/friend.go +++ b/model/entity/friend.go @@ -20,6 +20,7 @@ type Friend struct { EnableWelcome bool `json:"enableWelcome" gorm:"type:tinyint(1) default 0 not null"` // 是否启用迎新 EnableSummary bool `json:"enableSummary" gorm:"type:tinyint(1) default 0 not null"` // 是否启用总结 EnableNews bool `json:"enableNews" gorm:"type:tinyint(1) default 0 not null"` // 是否启用新闻 + EnableHotTop bool `json:"enableHotTop" gorm:"type:tinyint(1) default 0 not null"` // 是否启用热榜新闻 ClearMember int `json:"clearMember"` // 清理成员配置(多少天未活跃的) IsOk bool `json:"isOk" gorm:"type:tinyint(1) default 0 not null"` // 是否正常 UsedTokens int `json:"usedTokens"` // 已使用的AI Token数量 diff --git a/model/entity/hottop.go b/model/entity/hottop.go new file mode 100644 index 0000000..63ed3d0 --- /dev/null +++ b/model/entity/hottop.go @@ -0,0 +1,40 @@ +package entity + +import ( + "github.com/google/uuid" + "go-wechat/common/types" + "gorm.io/gorm" + "strings" +) + +// HotTop +// @description: 热榜数据 +type HotTop struct { + Id string `json:"id" gorm:"type:varchar(32);primarykey"` + CreatedAt types.DateTime `json:"createdAt"` + Channel string `json:"channel"` // 渠道 + Title string `json:"title"` // 标题 + Hot string `json:"hot"` // 热度 + Url string `json:"url"` // 链接 + MobileUrl string `json:"mobileUrl"` // 手机端链接 +} + +// TableName +// @description: 表名 +// @receiver HotTop +// @return string +func (HotTop) TableName() string { + return "t_hot_top" +} + +// BeforeCreate +// @description: 创建数据库对象之前生成UUID +// @receiver m +// @param *gorm.DB +// @return err +func (m *HotTop) BeforeCreate(*gorm.DB) (err error) { + if m.Id == "" { + m.Id = strings.ReplaceAll(uuid.New().String(), "-", "") + } + return +} diff --git a/model/vo/friend.go b/model/vo/friend.go index c948a4e..d51420d 100644 --- a/model/vo/friend.go +++ b/model/vo/friend.go @@ -21,6 +21,7 @@ type FriendItem struct { EnableCommand bool // 是否启用指令 EnableSummary bool // 是否启用总结 EnableNews bool // 是否启用新闻 + EnableHotTop bool // 是否启用热搜 ClearMember int // 清理成员配置(多少天未活跃的) IsOk bool // 是否还在通讯库(群聊是要还在群里也算) } diff --git a/router/router.go b/router/router.go index d216360..2b40dfd 100644 --- a/router/router.go +++ b/router/router.go @@ -29,6 +29,7 @@ func Init(g *gin.Engine) { api.PUT("/welcome/status", app.ChangeEnableWelcomeStatus) // 修改是否开启迎新状态 api.PUT("/command/status", app.ChangeEnableCommandStatus) // 修改是否开启指令状态 api.PUT("/news/status", app.ChangeEnableNewsStatus) // 修改是否开启早报状态 + api.PUT("/hot-top/status", app.ChangeEnableHotTopStatus) // 修改是否开启热榜状态 api.PUT("/grouprank/status", app.ChangeEnableGroupRankStatus) // 修改是否开启水群排行榜状态 api.PUT("/grouprank/skip", app.ChangeSkipGroupRankStatus) // 修改是否跳过水群排行榜状态 api.GET("/group/users", app.GetGroupUsers) // 获取群成员列表 diff --git a/service/friend.go b/service/friend.go index d10cbed..93a6773 100644 --- a/service/friend.go +++ b/service/friend.go @@ -88,6 +88,15 @@ func GetAllEnableNews() (records []entity.Friend, err error) { return } +// GetAllEnableHotTop +// @description: 获取所有启用了热搜排行榜的好友或群组 +// @return records +// @return err +func GetAllEnableHotTop() (records []entity.Friend, err error) { + err = client.MySQL.Where("enable_hot_top = ?", 1).Where("is_ok IS TRUE").Find(&records).Error + return +} + // GetAllEnableClearGroup // @description: 获取所有需要清理成员的群组 // @return records diff --git a/tasks/friends/friends.go b/tasks/friends/friends.go index fb52e40..6afbf03 100644 --- a/tasks/friends/friends.go +++ b/tasks/friends/friends.go @@ -75,6 +75,7 @@ func Sync() { EnableSummary: config.Conf.System.DefaultRule.Summary, EnableWelcome: config.Conf.System.DefaultRule.Welcome, EnableNews: config.Conf.System.DefaultRule.News, + EnableHotTop: config.Conf.System.DefaultRule.HotTop, ClearMember: 0, LastActive: time.Now().Local(), }).Error diff --git a/tasks/hottop/hottop.go b/tasks/hottop/hottop.go new file mode 100644 index 0000000..6de3175 --- /dev/null +++ b/tasks/hottop/hottop.go @@ -0,0 +1,88 @@ +package hottop + +import ( + "fmt" + "go-wechat/client" + "go-wechat/common/types" + "go-wechat/model/entity" + "go-wechat/service" + "go-wechat/utils" + "log" + "slices" + "strings" + "time" +) + +// HotTop +// @description: 热搜排行榜 +func HotTop() { + // 发送到开启了的群 + groups, err := service.GetAllEnableHotTop() + if err != nil { + log.Printf("获取启用了热榜的群组失败, 错误信息: %v", err) + return + } + // 获取热榜数据 + news := getTopData() + if len(news) == 0 { + return + } + + // 组装消息 + msg := fmt.Sprintf("#热搜排行榜\n \n嘿,朋友,有新的新闻了喔,快来康康吧\n \n%s", strings.Join(news, "\n-------\n")) + for _, group := range groups { + utils.SendMessage(group.Wxid, "", msg, 0) + // 休眠一秒,防止频繁发送 + time.Sleep(time.Second) + } +} + +// getTopData +// @description: 获取热榜数据 +// @return data +func getTopData() (data []string) { + // 获取热榜数据 + records := utils.NewsUtil().GetHotTop() + if len(records) == 0 { + log.Println("获取热榜数据失败") + return + } + var datas = make([]entity.HotTop, 0) + for _, item := range records { + var d = entity.HotTop{ + CreatedAt: types.DateTime(time.Now().Local()), + Title: item.Title, + Hot: item.Hot, + Url: item.Url, + MobileUrl: item.MobilUrl, + } + datas = append(datas, d) + } + + // 获取缓存数据 + var oldTitles []string + err := client.MySQL.Model(&entity.HotTop{}).Order("created_at DESC").Limit(len(datas)).Pluck("title", &oldTitles).Error + if err != nil { + log.Println("获取历史热榜数据失败", err) + return + } + + // 筛选出新数据 + var newDatas []entity.HotTop + for _, d := range datas { + if slices.Contains(oldTitles, d.Title) { + continue + } + d.Channel = "百度" + newDatas = append(newDatas, d) + data = append(data, fmt.Sprintf("标题: %s\n热度: %s\n详情: %s", d.Title, d.Hot, d.Url)) + } + // 保存新数据到数据库 + if len(newDatas) > 0 { + err = client.MySQL.Create(&newDatas).Error + if err != nil { + log.Println("保存新热榜数据失败", err) + } + } + return +} diff --git a/tasks/tasks.go b/tasks/tasks.go index 9627c01..f441b32 100644 --- a/tasks/tasks.go +++ b/tasks/tasks.go @@ -5,6 +5,7 @@ import ( "go-wechat/config" "go-wechat/tasks/cleargroupuser" "go-wechat/tasks/friends" + "go-wechat/tasks/hottop" "go-wechat/tasks/news" "go-wechat/tasks/summary" "go-wechat/tasks/watergroup" @@ -57,6 +58,11 @@ func InitTasks() { _, _ = s.Cron(config.Conf.Task.News.Cron).Do(news.DailyNews) } + // 热榜 + if config.Conf.Task.HotTop.Enable { + _, _ = s.Cron(config.Conf.Task.HotTop.Cron).Do(hottop.HotTop) + } + // 每天0点检查一次处理清理群成员 _, _ = s.Cron("0 0 * * *").Do(cleargroupuser.ClearGroupUser) diff --git a/utils/news.go b/utils/news.go index db2317c..7b46a1c 100644 --- a/utils/news.go +++ b/utils/news.go @@ -10,7 +10,8 @@ import ( // News // @description: 新闻 type News interface { - MorningPost() []string // 早报 + MorningPost() []string // 早报 + GetHotTop() []dto.HotTopDataItem // 获取热搜排行榜 } type news struct{} @@ -38,10 +39,34 @@ func (news) MorningPost() (records []string) { SetResult(&newsResp). Post("https://v2.alapi.cn/api/zaobao") if err != nil { - log.Panicf("每日早报获取失败: %s", err.Error()) + log.Printf("每日早报获取失败: %s", err.Error()) + return } log.Printf("每日早报获取结果: %s", unicodeToText(resp.String())) records = newsResp.Data.News return } + +// GetHotTop +// @description: 获取热搜排行榜 +// @receiver news +// @return records +func (news) GetHotTop() (records []dto.HotTopDataItem) { + var respData dto.HotTop + res := resty.New() + resp, err := res.R(). + SetHeader("Content-Type", "application/json;chartset=utf-8"). + SetResult(&respData). + Get("https://api.vvhan.com/api/hotlist/baiduRD") + if err != nil { + log.Printf("百度热榜获取失败: %s", err.Error()) + return + } + log.Printf("百度热榜获取结果: %s", unicodeToText(resp.String())) + if !respData.Success { + log.Println("百度热榜获取失败") + return + } + return respData.Data +} diff --git a/views/components.html b/views/components.html index 96ebc1e..02b7413 100644 --- a/views/components.html +++ b/views/components.html @@ -192,6 +192,33 @@ {{end}} + +{{define "hotTop"}} + +{{end}} + {{define "flagTag"}} diff --git a/views/friend.html b/views/friend.html index e97c719..04c660d 100644 --- a/views/friend.html +++ b/views/friend.html @@ -103,6 +103,13 @@ +