Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

修改提交信息 #924

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]

- [x] 设置温度[正整数]

</details>
<details>
<summary>聊天时长统计</summary>

`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/chatcount"`

- [x] 查询水群@xxx

- [x] 查看水群排名

</details>
<details>
<summary>睡眠管理</summary>
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (

_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chat" // 基础词库

_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chatcount" // 聊天时长统计

_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleepmanage" // 统计睡眠时间

_ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库
Expand Down
65 changes: 65 additions & 0 deletions plugin/chatcount/chatcount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Package chatcount 聊天时长统计
package chatcount

import (
"fmt"
"strconv"
"strings"

zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"

ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
)

const (
rankSize = 10
)

func init() {
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "聊天时长统计",
Help: "- 查询水群@xxx\n- 查看水群排名",
PrivateDataFolder: "chatcount",
})
go func() {
ctdb = initialize(engine.DataFolder() + "chatcount.db")
}()
engine.OnMessage(zero.OnlyGroup).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
remindTime, remindFlag := ctdb.updateChatTime(ctx.Event.GroupID, ctx.Event.UserID)
if remindFlag {
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("BOT提醒:你今天已经水群%d分钟了!", remindTime)))
}
})

engine.OnPrefix(`查询水群`, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
name := ctx.NickName()
todayTime, todayMessage, totalTime, totalMessage := ctdb.getChatTime(ctx.Event.GroupID, ctx.Event.UserID)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("%s今天水了%d分%d秒,发了%d条消息;总计水了%d分%d秒,发了%d条消息。", name, todayTime/60, todayTime%60, todayMessage, totalTime/60, totalTime%60, totalMessage)))
})
engine.OnFullMatch("查看水群排名", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
text := strings.Builder{}
text.WriteString("今日水群排行榜:\n")
chatTimeList := ctdb.getChatRank(ctx.Event.GroupID)
for i := 0; i < len(chatTimeList) && i < rankSize; i++ {
text.WriteString("第")
text.WriteString(strconv.Itoa(i + 1))
text.WriteString("名:")
text.WriteString(ctx.CardOrNickName(chatTimeList[i].UserID))
text.WriteString(" - ")
text.WriteString(strconv.FormatInt(chatTimeList[i].TodayMessage, 10))
text.WriteString("条,共")
text.WriteString(strconv.FormatInt(chatTimeList[i].TodayTime/60, 10))
text.WriteString("分")
text.WriteString(strconv.FormatInt(chatTimeList[i].TodayTime%60, 10))
text.WriteString("秒\n")
}
ctx.SendChain(message.Text(text.String()))
})

}
225 changes: 225 additions & 0 deletions plugin/chatcount/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package chatcount

import (
"fmt"
"os"
"sort"
"strconv"
"strings"
"sync"
"time"

"github.com/RomiChan/syncx"

"github.com/jinzhu/gorm"
)

const (
chatInterval = 300
)

var (
// ctdb 聊天时长数据库全局变量
ctdb *chattimedb
// l 水群提醒时间提醒段,单位分钟
l = newLeveler(60, 120, 180, 240, 300)
)

// chattimedb 聊天时长数据库结构体
type chattimedb struct {
// ctdb.userTimestampMap 每个人发言的时间戳 key=groupID_userID
userTimestampMap syncx.Map[string, int64]
// ctdb.userTodayTimeMap 每个人今日水群时间 key=groupID_userID
userTodayTimeMap syncx.Map[string, int64]
// ctdb.userTodayMessageMap 每个人今日水群次数 key=groupID_userID
userTodayMessageMap syncx.Map[string, int64]
// db 数据库
db *gorm.DB
// chatmu 读写添加锁
chatmu sync.Mutex
}

// initialize 初始化
func initialize(dbpath string) *chattimedb {
var err error
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
// 生成文件
f, err := os.Create(dbpath)
if err != nil {
return nil
}
defer f.Close()
}
gdb, err := gorm.Open("sqlite3", dbpath)
if err != nil {
panic(err)
}
gdb.AutoMigrate(&chatTime{})
return &chattimedb{
db: gdb,
}
}

// Close 关闭
func (ctdb *chattimedb) Close() error {
db := ctdb.db
return db.Close()
}

// chatTime 聊天时长,时间的单位都是秒
type chatTime struct {
ID uint `gorm:"primary_key"`
GroupID int64 `gorm:"column:group_id"`
UserID int64 `gorm:"column:user_id"`
TodayTime int64 `gorm:"-"`
TodayMessage int64 `gorm:"-"`
TotalTime int64 `gorm:"column:total_time;default:0"`
TotalMessage int64 `gorm:"column:total_message;default:0"`
}

// TableName 表名
func (chatTime) TableName() string {
return "chat_time"
}

// updateChatTime 更新发言时间,todayTime的单位是分钟
func (ctdb *chattimedb) updateChatTime(gid, uid int64) (remindTime int64, remindFlag bool) {
ctdb.chatmu.Lock()
defer ctdb.chatmu.Unlock()
db := ctdb.db
now := time.Now()
keyword := fmt.Sprintf("%v_%v", gid, uid)
ts, ok := ctdb.userTimestampMap.Load(keyword)
if !ok {
ctdb.userTimestampMap.Store(keyword, now.Unix())
ctdb.userTodayMessageMap.Store(keyword, 1)
return
}
lastTime := time.Unix(ts, 0)
todayTime, _ := ctdb.userTodayTimeMap.Load(keyword)
totayMessage, _ := ctdb.userTodayMessageMap.Load(keyword)
//这个消息数是必须统计的
ctdb.userTodayMessageMap.Store(keyword, totayMessage+1)
st := chatTime{
GroupID: gid,
UserID: uid,
TotalTime: todayTime,
TotalMessage: totayMessage,
}

// 如果不是同一天,把TotalTime,TotalMessage重置
if lastTime.YearDay() != now.YearDay() {
if err := db.Model(&st).Where("group_id = ? and user_id = ?", gid, uid).First(&st).Error; err != nil {
if gorm.IsRecordNotFoundError(err) {
db.Model(&st).Create(&st)
}
} else {
db.Model(&st).Where("group_id = ? and user_id = ?", gid, uid).Update(
map[string]any{
"total_time": st.TotalTime + todayTime,
"total_message": st.TotalMessage + totayMessage,
})
}
ctdb.userTimestampMap.Store(keyword, now.Unix())
ctdb.userTodayTimeMap.Delete(keyword)
ctdb.userTodayMessageMap.Delete(keyword)
return
}

userChatTime := int64(now.Sub(lastTime).Seconds())
// 当聊天时间在一定范围内的话,则计入时长
if userChatTime < chatInterval {
ctdb.userTodayTimeMap.Store(keyword, todayTime+userChatTime)
remindTime = (todayTime + userChatTime) / 60
remindFlag = l.level(int((todayTime+userChatTime)/60)) > l.level(int(todayTime/60))
}
ctdb.userTimestampMap.Store(keyword, now.Unix())
return
}

// getChatTime 获得用户聊天时长和消息次数,todayTime,totalTime的单位是秒,todayMessage,totalMessage单位是条数
func (ctdb *chattimedb) getChatTime(gid, uid int64) (todayTime, todayMessage, totalTime, totalMessage int64) {
ctdb.chatmu.Lock()
defer ctdb.chatmu.Unlock()
db := ctdb.db
st := chatTime{}
db.Model(&st).Where("group_id = ? and user_id = ?", gid, uid).First(&st)
keyword := fmt.Sprintf("%v_%v", gid, uid)
todayTime, _ = ctdb.userTodayTimeMap.Load(keyword)
todayMessage, _ = ctdb.userTodayMessageMap.Load(keyword)
totalTime = st.TotalTime
totalMessage = st.TotalMessage
return
}

// getChatRank 获得水群排名,时间单位为秒
func (ctdb *chattimedb) getChatRank(gid int64) (chatTimeList []chatTime) {
ctdb.chatmu.Lock()
defer ctdb.chatmu.Unlock()
chatTimeList = make([]chatTime, 0, 100)
keyList := make([]string, 0, 100)
ctdb.userTimestampMap.Range(func(key string, value int64) bool {
t := time.Unix(value, 0)
if strings.Contains(key, strconv.FormatInt(gid, 10)) && t.YearDay() == time.Now().YearDay() {
keyList = append(keyList, key)
}
return true
})
for _, v := range keyList {
_, a, _ := strings.Cut(v, "_")
uid, _ := strconv.ParseInt(a, 10, 64)
todayTime, _ := ctdb.userTodayTimeMap.Load(v)
todayMessage, _ := ctdb.userTodayMessageMap.Load(v)
chatTimeList = append(chatTimeList, chatTime{
GroupID: gid,
UserID: uid,
TodayTime: todayTime,
TodayMessage: todayMessage,
})
}
sort.Sort(sortChatTime(chatTimeList))
return
}

// leveler 结构体,包含一个 levelArray 字段
type leveler struct {
levelArray []int
}

// newLeveler 构造函数,用于创建 Leveler 实例
func newLeveler(levels ...int) *leveler {
return &leveler{
levelArray: levels,
}
}

// level 方法,封装了 getLevel 函数的逻辑
func (l *leveler) level(t int) int {
for i := len(l.levelArray) - 1; i >= 0; i-- {
if t >= l.levelArray[i] {
return i + 1
}
}
return 0
}

// sortChatTime chatTime排序数组
type sortChatTime []chatTime

// Len 实现 sort.Interface
func (a sortChatTime) Len() int {
return len(a)
}

// Less 实现 sort.Interface,按 TodayTime 降序,TodayMessage 降序
func (a sortChatTime) Less(i, j int) bool {
if a[i].TodayTime == a[j].TodayTime {
return a[i].TodayMessage > a[j].TodayMessage
}
return a[i].TodayTime > a[j].TodayTime
}

// Swap 实现 sort.Interface
func (a sortChatTime) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
2 changes: 1 addition & 1 deletion plugin/mcfish/pole.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ func init() {
ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[0]请输入正确的序号\n", list))
continue
}
if first > max || second > max || third > max {
if first >= max || second >= max || third >= max {
ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[", max, "]请输入正确的序号\n", list))
continue
}
Expand Down
Loading