Skip to content

Commit

Permalink
添加签到,引入分数机制 (#101)
Browse files Browse the repository at this point in the history
* feat:添加签到功能,引入分数机制

* feat:修lint

* fix:加宽

* fix:修lint和加锁

* fix:解决冲突

* fix:二次判断
  • Loading branch information
guohuiyuan committed Jan 9, 2022
1 parent d8991ec commit 2ed25c6
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 20 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ zerobot -h -t token -u url [-d|w] [-g 监听地址:端口] qq1 qq2 qq3 ...
- **cp短打** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_cpstory"`
- [x] 组cp[@xxx][@xxx]
- [x] 组cp大老师 雪乃
- **签到得分** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin_score"`
- [x] 签到
- [x] 获得签到背景[@xxx]|获得签到背景
- **TODO...**

## 使用方法
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_novel" // 铅笔小说网搜索
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_omikuji" // 浅草寺求签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_reborn" // 投胎
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_score" // 分数
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shadiao" // 沙雕app
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_shindan" // 测定

Expand Down
6 changes: 3 additions & 3 deletions plugin_mocking_bird/mocking_bird.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

"github.com/FloatTech/ZeroBot-Plugin/control"
aireply "github.com/FloatTech/ZeroBot-Plugin/plugin_ai_reply"
fileutil "github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
)

Expand Down Expand Up @@ -57,7 +57,7 @@ func init() {
syntPath := getSyntPath()
fileName := getWav(textReply, syntPath, vocoderList[1], ctx.Event.UserID)
// 回复
ctx.SendChain(message.Record("file:///" + fileutil.BOTPATH + "/" + cachePath + fileName))
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cachePath + fileName))
})
}

Expand Down Expand Up @@ -126,7 +126,7 @@ func getWav(text, syntPath, vocoder string, uid int64) (fileName string) {
}
defer res.Body.Close()
data, _ := ioutil.ReadAll(res.Body)
err = ioutil.WriteFile(cachePath+fileName, data, 0666)
err = os.WriteFile(cachePath+fileName, data, 0666)
if err != nil {
log.Errorln("[mockingbird]:", err)
}
Expand Down
28 changes: 28 additions & 0 deletions plugin_score/data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package score

import (
"github.com/FloatTech/ZeroBot-Plugin/utils/process"
log "github.com/sirupsen/logrus"
"os"
)

const (
cachePath = dbpath + "cache/"
dbpath = "data/Score/"
dbfile = dbpath + "score.db"
)

// SDB 得分数据库
var SDB *DB

// 加载数据库
func init() {
go func() {
process.SleepAbout1sTo2s()
_ = os.MkdirAll(dbpath, 0755)
os.RemoveAll(cachePath)
_ = os.MkdirAll(cachePath, 0755)
SDB = Initialize(dbfile)
log.Println("[score]加载score数据库")
}()
}
124 changes: 124 additions & 0 deletions plugin_score/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package score

import (
"github.com/jinzhu/gorm"
_ "github.com/logoove/sqlite" // import sql
"os"
"time"
)

// DB 分数数据库
type DB gorm.DB

// Score 分数结构体
type Score struct {
UID int64 `gorm:"column:uid;primary_key"`
Score int `gorm:"column:score;default:0"`
}

// TableName ...
func (Score) TableName() string {
return "score"
}

// SignIn 签到结构体
type SignIn struct {
UID int64 `gorm:"column:uid;primary_key"`
Count int `gorm:"column:count;default:0"`
UpdatedAt time.Time
}

// TableName ...
func (SignIn) TableName() string {
return "sign_in"
}

// Initialize 初始化ScoreDB数据库
func Initialize(dbpath string) *DB {
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(&Score{}).AutoMigrate(&SignIn{})
return (*DB)(gdb)
}

// Open ...
func Open(dbpath string) (*DB, error) {
db, err := gorm.Open("sqlite3", dbpath)
if err != nil {
return nil, err
}
return (*DB)(db), nil
}

// Close ...
func (sdb *DB) Close() error {
db := (*gorm.DB)(sdb)
return db.Close()
}

// GetScoreByUID 取得分数
func (sdb *DB) GetScoreByUID(uid int64) (s Score) {
db := (*gorm.DB)(sdb)
db.Debug().Model(&Score{}).FirstOrCreate(&s, "uid = ? ", uid)
return s
}

// InsertOrUpdateScoreByUID 插入或更新分数
func (sdb *DB) InsertOrUpdateScoreByUID(uid int64, score int) (err error) {
db := (*gorm.DB)(sdb)
s := Score{
UID: uid,
Score: score,
}
if err = db.Debug().Model(&Score{}).First(&s, "uid = ? ", uid).Error; err != nil {
// error handling...
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&Score{}).Create(&s) // newUser not user
}
} else {
err = db.Debug().Model(&Score{}).Where("uid = ? ", uid).Update(
map[string]interface{}{
"score": score,
}).Error
}
return
}

// GetSignInByUID 取得签到次数
func (sdb *DB) GetSignInByUID(uid int64) (si SignIn) {
db := (*gorm.DB)(sdb)
db.Debug().Model(&SignIn{}).FirstOrCreate(&si, "uid = ? ", uid)
return si
}

// InsertOrUpdateSignInCountByUID 插入或更新签到次数
func (sdb *DB) InsertOrUpdateSignInCountByUID(uid int64, count int) (err error) {
db := (*gorm.DB)(sdb)
si := SignIn{
UID: uid,
Count: count,
}
if err = db.Debug().Model(&SignIn{}).First(&si, "uid = ? ", uid).Error; err != nil {
// error handling...
if gorm.IsRecordNotFoundError(err) {
db.Debug().Model(&SignIn{}).Create(&si) // newUser not user
}
} else {
err = db.Debug().Model(&SignIn{}).Where("uid = ? ", uid).Update(
map[string]interface{}{
"count": count,
}).Error
}
return
}
187 changes: 187 additions & 0 deletions plugin_score/sign_in.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// Package score 签到,答题得分
package score

import (
"fmt"
"github.com/FloatTech/ZeroBot-Plugin/control"
"github.com/FloatTech/ZeroBot-Plugin/utils/ctxext"
"github.com/FloatTech/ZeroBot-Plugin/utils/file"
"github.com/FloatTech/ZeroBot-Plugin/utils/txt2img"
"github.com/FloatTech/ZeroBot-Plugin/utils/web"
"github.com/fogleman/gg"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/wdvxdr1123/ZeroBot/utils/helper"
"os"
"strconv"
"sync"
"time"
)

const (
prio = 5
backgroundURL = "https://iw233.cn/API/pc.php?type=json"
referer = "https://iw233.cn/main.html"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
signinMax = 1
// ScoreMax 分数上限定为120
ScoreMax = 120
)

var (
engine = control.Register("score", &control.Options{
DisableOnDefault: false,
Help: "签到得分\n- 签到\n- 获得签到背景[@xxx]|获得签到背景",
})
levelArray = [...]int{0, 1, 2, 5, 10, 20, 35, 55, 75, 100, 120}
// 下载锁
mu sync.Mutex
)

func init() {
engine.OnFullMatch("签到").SetBlock(true).SetPriority(prio).
Handle(func(ctx *zero.Ctx) {
uid := ctx.Event.UserID
now := time.Now()
today := now.Format("20060102")
si := SDB.GetSignInByUID(uid)
picFile := cachePath + strconv.FormatInt(uid, 10) + today + ".png"
if file.IsNotExist(picFile) {
mu.Lock()
initPic(picFile)
mu.Unlock()
}
siUpdateTimeStr := si.UpdatedAt.Format("20060102")
if siUpdateTimeStr != today {
if err := SDB.InsertOrUpdateSignInCountByUID(uid, 0); err != nil {
log.Errorln("[score]:", err)
}
}
if si.Count >= signinMax && siUpdateTimeStr == today {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("今天你已经签到过了!"))
return
}
if err := SDB.InsertOrUpdateSignInCountByUID(uid, si.Count+1); err != nil {
log.Errorln("[score]:", err)
}
back, err := gg.LoadImage(picFile)
if err != nil {
log.Errorln("[score]:", err)
}
canvas := gg.NewContext(back.Bounds().Size().X, int(float64(back.Bounds().Size().Y)*1.7))
canvas.SetRGB(1, 1, 1)
canvas.Clear()
canvas.DrawImage(back, 0, 0)

monthWord := now.Format("01/02")
hourWord := getHourWord(now)
if err = canvas.LoadFontFace(txt2img.BoldFontFile, float64(back.Bounds().Size().X)*0.1); err != nil {
log.Println("[score]:", err)
}
canvas.SetRGB(0, 0, 0)
canvas.DrawString(hourWord, float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.2)
canvas.DrawString(monthWord, float64(back.Bounds().Size().X)*0.6, float64(back.Bounds().Size().Y)*1.2)
nickName := ctxext.CardOrNickName(ctx, uid)
if err = canvas.LoadFontFace(txt2img.FontFile, float64(back.Bounds().Size().X)*0.04); err != nil {
log.Println("[score]:", err)
}
add := 1
canvas.DrawString(nickName+fmt.Sprintf(" 小熊饼干+%d", add), float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.3)
score := SDB.GetScoreByUID(uid).Score
score += add
if score > ScoreMax {
score = ScoreMax
ctx.SendChain(message.At(uid), message.Text("你获得的小熊饼干已经达到上限"))
}
if err := SDB.InsertOrUpdateScoreByUID(uid, score); err != nil {
log.Println("[score]:", err)
}
level := getLevel(score)
canvas.DrawString("当前小熊饼干:"+strconv.FormatInt(int64(score), 10), float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.4)
canvas.DrawString("LEVEL:"+strconv.FormatInt(int64(level), 10), float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.5)
canvas.DrawRectangle(float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.55, float64(back.Bounds().Size().X)*0.6, float64(back.Bounds().Size().Y)*0.1)
canvas.SetRGB255(150, 150, 150)
canvas.Fill()
var nextLevelScore int
if level < 10 {
nextLevelScore = levelArray[level+1]
} else {
nextLevelScore = ScoreMax
}
canvas.SetRGB255(0, 0, 0)
canvas.DrawRectangle(float64(back.Bounds().Size().X)*0.1, float64(back.Bounds().Size().Y)*1.55, float64(back.Bounds().Size().X)*0.6*float64(score)/float64(nextLevelScore), float64(back.Bounds().Size().Y)*0.1)
canvas.SetRGB255(102, 102, 102)
canvas.Fill()
canvas.DrawString(fmt.Sprintf("%d/%d", score, nextLevelScore), float64(back.Bounds().Size().X)*0.75, float64(back.Bounds().Size().Y)*1.62)
canvasBase64, err := txt2img.CanvasToBase64(canvas)
if err != nil {
log.Println("[score]:", err)
}
ctx.SendChain(message.Image("base64://" + helper.BytesToString(canvasBase64)))
})
engine.OnPrefix("获得签到背景", zero.OnlyGroup).SetBlock(true).SetPriority(20).
Handle(func(ctx *zero.Ctx) {
param := ctx.State["args"].(string)
var uidStr string
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
uidStr = ctx.Event.Message[1].Data["qq"]
} else if param == "" {
uidStr = strconv.FormatInt(ctx.Event.UserID, 10)
}
picFile := cachePath + uidStr + time.Now().Format("20060102") + ".png"
if file.IsNotExist(picFile) {
mu.Lock()
initPic(picFile)
mu.Unlock()
}
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + picFile))
})
}

func getHourWord(t time.Time) string {
switch {
case 6 <= t.Hour() && t.Hour() < 12:
return "早上好"
case 12 <= t.Hour() && t.Hour() < 14:
return "中午好"
case 14 <= t.Hour() && t.Hour() < 19:
return "下午好"
case 19 <= t.Hour() && t.Hour() < 24:
return "晚上好"
case 0 <= t.Hour() && t.Hour() < 6:
return "凌晨好"
default:
return ""
}
}

func getLevel(count int) int {
for k, v := range levelArray {
if count == v {
return k
} else if count < v {
return k - 1
}
}
return -1
}

func initPic(picFile string) {
if file.IsNotExist(picFile) {
data, err := web.ReqWith(backgroundURL, "GET", referer, ua)
if err != nil {
log.Errorln("[score]:", err)
}
picURL := gjson.Get(string(data), "pic").String()
data, err = web.ReqWith(picURL, "GET", "", ua)
if err != nil {
log.Errorln("[score]:", err)
}
err = os.WriteFile(picFile, data, 0666)
if err != nil {
log.Errorln("[score]:", err)
}
}
}
2 changes: 1 addition & 1 deletion plugin_shadiao/shadiao.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const (
)

var (
engine = control.Register("curse", &control.Options{
engine = control.Register("shadiao", &control.Options{
DisableOnDefault: false,
Help: "沙雕app\n" +
"- 骂他[@xxx]|骂他[qq号](停用)\n- 骂我(停用)\n- 哄我\n- 渣我\n- 来碗绿茶\n- 发个朋友圈\n- 来碗毒鸡汤\n- 讲个段子",
Expand Down
Loading

0 comments on commit 2ed25c6

Please sign in to comment.