Compare commits

...

15 Commits

Author SHA1 Message Date
源文雨
cde7669c0b 优化代码结构 2022-03-12 11:35:20 +08:00
源文雨
ff4a33c51c edit readme 2022-03-12 11:05:19 +08:00
源文雨
06cf6f84aa edit readme 2022-03-12 00:27:27 +08:00
源文雨
556f6f134f edit readme 2022-03-12 00:27:15 +08:00
源文雨
b1ab7ef118 edit readme 2022-03-12 00:25:59 +08:00
源文雨
e5fd108a9c edit readme 2022-03-12 00:25:21 +08:00
源文雨
5a67bf2417 🐛 每天刷新 2022-03-12 00:18:26 +08:00
源文雨
cb020c1bbd 🐛 每天刷新 2022-03-12 00:17:33 +08:00
源文雨
bf75b29e33 🐛 每天刷新 2022-03-12 00:16:04 +08:00
源文雨
d730fd9cab 优化代码结构 2022-03-12 00:05:41 +08:00
Jiang-Red
a862e5be7c 添加 早报插件及设置欢迎语增加更多自定义 (#147)
* 添加 早报插件

* 添加 早报插件

* 添加 早报插件

* 添加 设置欢迎语更多自定义

* 添加 help说明

* 添加 manager设置欢迎语说明

* 怪

* 🎨 改进代码样式

* Update zaobao.go

* Update zaobao.go

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-03-11 23:47:39 +08:00
源文雨
ebca124c86 🐛 修复 run 2022-03-11 23:02:08 +08:00
源文雨
3b7803a81b 优化代码结构 2022-03-11 16:34:26 +08:00
源文雨
b7b9d92148 优化代码结构 2022-03-11 16:18:11 +08:00
Rainy
44575fb19e 增加城市疫情查询 (#146)
* 增加城市疫情查询

* 增加城市疫情查询

* 增加城市疫情查询

* 增加城市疫情查询

* 增加查询城市疫情

* 增加城市查询疫情

* 增加城市疫情查询

* Update epidemic.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-03-11 16:10:14 +08:00
12 changed files with 280 additions and 664 deletions

View File

@@ -66,7 +66,7 @@ zerobot [-h] [-t token] [-u url] [-n nickname] [-p prefix] [-d|w] [-g 监听地
- [x] /服务列表
- [x] /服务详情
- [x] @Bot 插件冲突检测 (会在本群发送一条消息并在约 1s 后撤回以检测其它同类 bot 中已启用的插件并禁用)
- **定时指令触发器** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/job"`
- **定时指令触发器** `import _ "github.com/FloatTech/zbputils/job"`
- [x] 记录以"完全匹配关键词"触发的(代表我执行的)指令
- [x] 取消以"完全匹配关键词"触发的(代表我执行的)指令
- [x] 记录在"cron"触发的指令
@@ -145,7 +145,7 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 退出群聊[群号]@Bot
- [x] *入群欢迎
- [x] *退群通知
- [x] 设置欢迎语[欢迎~]
- [x] 设置欢迎语[欢迎~] 可选添加 [{at}] [{nickname}] [{avatar}]
- [x] 在[MM]月[dd]日的[hh]点[mm]分时(用[url])提醒大家[xxx]
- [x] 在[MM]月[每周 | 周几]的[hh]点[mm]分时(用[url])提醒大家[xxx]
- [x] 取消在[MM]月[dd]日的[hh]点[mm]分的提醒
@@ -161,6 +161,7 @@ print("run[CQ:image,file="+j["img"]+"]")
- [ ] 警告[@xxx]
- [x] run[xxx]
-使用gist加群自动审批请在群介绍添加以下说明同时开启`需要回答问题并由管理员审核`加群请在github新建一个gist其文件名为本群群号的字符串的md5(小写)内容为一行是当前unix时间戳(10分钟内有效)。然后请将您的用户名和gist哈希(小写)按照username/gisthash的格式填写到回答即可。
- 设置欢迎语可选添加参数说明:{at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像
- **GitHub仓库搜索** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/github"`
- [x] >github [xxx]
- [x] >github -p [xxx]
@@ -193,9 +194,17 @@ print("run[CQ:image,file="+j["img"]+"]")
- **摸鱼** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu"`
- [x] /启用 moyu
- [x] /禁用 moyu
```
记录在"0 10 * * *"触发的指令
摸鱼提醒
```
- **摸鱼人日历** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar"`
- [x] /启用 moyucalendar
- [x] /禁用 moyucalendar
```
记录在"30 8 * * *"触发的指令
摸鱼人日历
```
- **涩图** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime"`
- [x] 来份[涩图/二次元/风景/车万]
- [x] 添加[涩图/二次元/风景/车万][P站图片ID]
@@ -345,6 +354,16 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] galCG[xxx]
- [x] gal表情包[xxx]
- [x] 更新gal
- **城市疫情查询** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/epidemic"`
- [x] xxx疫情
- **早报** `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/zaobao"`
- api早上8点更新推荐定时在8点30后。配合插件`job`中的记录在"cron"触发的指令使用
- [x] /启用 zaobao
- [x] /禁用 zaobao
```
记录在"00 9 * * *"触发的指令
今日早报
```
- **TODO...**
## 使用方法

4
go.mod
View File

@@ -3,9 +3,9 @@ module github.com/FloatTech/ZeroBot-Plugin
go 1.17
require (
github.com/FloatTech/AnimeAPI v1.3.1-0.20220311024222-ed58ddf6834e
github.com/FloatTech/AnimeAPI v1.3.1
github.com/FloatTech/sqlite v0.2.1
github.com/FloatTech/zbputils v1.3.1-0.20220311032316-df8ab8b3c180
github.com/FloatTech/zbputils v1.3.1
github.com/antchfx/htmlquery v1.2.4
github.com/corona10/goimagehash v1.0.3
github.com/fogleman/gg v1.3.0

9
go.sum
View File

@@ -1,11 +1,10 @@
github.com/FloatTech/AnimeAPI v1.3.1-0.20220311024222-ed58ddf6834e h1:PKm/g1M7rYu6YMOV5k8xCOkgXTKF2Tk9v2exeYyCe+0=
github.com/FloatTech/AnimeAPI v1.3.1-0.20220311024222-ed58ddf6834e/go.mod h1:JNF2O/RdbrsDIccSQ29a4g1pQRrZsN0Jh3ggFZYzZuY=
github.com/FloatTech/AnimeAPI v1.3.1 h1:EBNz9pzfH5vYqs8HyME9AL3gXjO+iJ5DgWmqOfdNb18=
github.com/FloatTech/AnimeAPI v1.3.1/go.mod h1:rEBMdnN1yPKt9DdL/BRSRibp7jR1SQiLIaEeQ53R+dk=
github.com/FloatTech/bot-manager v1.0.0/go.mod h1:8YYRJ16oroGHQGD2En0oVnmcKJkxR9O/jd5BPSfWfOQ=
github.com/FloatTech/sqlite v0.2.1 h1:9t6Me48XJJCIoPy4nLRvcdhcVKfT0c2lilp7SEKROG8=
github.com/FloatTech/sqlite v0.2.1/go.mod h1:6NfHRzqOo9RWeMJEoAQVuo51Omd5LFNxCNQhMF02/9U=
github.com/FloatTech/zbputils v1.3.1-0.20220307143543-1139754cacdf/go.mod h1:u+PiX1khNvtAgfRVTVP4hkA2oUnn5q5dTZSk1Cgp0Gw=
github.com/FloatTech/zbputils v1.3.1-0.20220311032316-df8ab8b3c180 h1:8tE9DYu0+YMk3oKeO0ffl80818ZrPM9afbRgb2ag0tQ=
github.com/FloatTech/zbputils v1.3.1-0.20220311032316-df8ab8b3c180/go.mod h1:u+PiX1khNvtAgfRVTVP4hkA2oUnn5q5dTZSk1Cgp0Gw=
github.com/FloatTech/zbputils v1.3.1 h1:ZdK5vkjdtDoeB83FoPAd+vIRmKdILeDSsLLiDIdanVI=
github.com/FloatTech/zbputils v1.3.1/go.mod h1:ByNutpHjwyrhaxo54dRjJDEppko0Fcot2iOv7WrIDUg=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc h1:AAx50/fb/xS4lvsdQg+bFbGvqSDhyV1MF+p2PLCamZ0=
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc/go.mod h1:OMmITAib6POA37xCichWM0aRnoVpSMZO1rB/G01wrr0=

View File

@@ -35,6 +35,8 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复
_ "github.com/FloatTech/zbputils/job" // 定时指令触发器
// ^^^^ //
// ^^^^^^^^^^^^^^ //
// ^^^^^^^高优先级区^^^^^^^ //
@@ -70,14 +72,15 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/epidemic" // 城市疫情查询
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/gif" // 制图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/github" // 搜索GitHub仓库
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/job" // 定时指令触发器
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
@@ -103,6 +106,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/zaobao" // 早报
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_push" // b站推送

123
plugin/epidemic/epidemic.go Normal file
View File

@@ -0,0 +1,123 @@
// Package epidemic 城市疫情查询
package epidemic
import (
"encoding/json"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/control/order"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/web"
)
const (
servicename = "epidemic"
txurl = "https://view.inews.qq.com/g2/getOnsInfo?name=disease_h5"
)
// result 疫情查询结果
type result struct {
Data string `json:"data"`
}
// epidemic 疫情数据
type epidemic struct {
LastUpdateTime string `json:"lastUpdateTime"`
AreaTree []*area `json:"areaTree"`
}
// area 城市疫情数据
type area struct {
Name string `json:"name"`
Today struct {
Confirm int `json:"confirm"`
} `json:"today"`
Total struct {
NowConfirm int `json:"nowConfirm"`
Confirm int `json:"confirm"`
Dead int `json:"dead"`
Heal int `json:"heal"`
Grade string `json:"grade"`
} `json:"total"`
Children []*area `json:"children"`
}
func init() {
engine := control.Register(servicename, order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "城市疫情查询\n" +
"- xxx疫情\n",
})
engine.OnSuffix("疫情").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
city := ctx.State["args"].(string)
if city == "" {
ctx.SendChain(message.Text("你还没有输入城市名字呢!"))
return
}
data, time, err := queryEpidemic(city)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
if data == nil {
ctx.SendChain(message.Text("没有找到【", city, "】城市的疫情数据."))
return
}
ctx.SendChain(
message.Text(
"【", data.Name, "】疫情数据\n",
"新增:", data.Today.Confirm, " ,",
"现有确诊:", data.Total.NowConfirm, " ,",
"治愈:", data.Total.Heal, " ,",
"死亡:", data.Total.Dead, " ", data.Total.Grade, "\n",
"更新时间:", time, "\n",
"温馨提示:请大家做好防疫工作,出门带好口罩!",
),
)
})
}
// rcity 查找城市
func rcity(a *area, cityName string) *area {
if a == nil {
return nil
}
if a.Name == cityName {
return a
}
for _, v := range a.Children {
if v.Name == cityName {
return v
}
c := rcity(v, cityName)
if c != nil {
return c
}
}
return nil
}
// queryEpidemic 查询城市疫情
func queryEpidemic(findCityName string) (citydata *area, times string, err error) {
data, err := web.GetData(txurl)
if err != nil {
return
}
var r result
err = json.Unmarshal(data, &r)
if err != nil {
return
}
var e epidemic
err = json.Unmarshal(binary.StringToBytes(r.Data), &e)
if err != nil {
return
}
citydata = rcity(e.AreaTree[0], findCityName)
return citydata, e.LastUpdateTime, nil
}

View File

@@ -1,541 +0,0 @@
// Package job 定时指令触发器
package job
import (
"encoding/json"
"hash/crc64"
"strconv"
"strings"
"sync"
"time"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/control/order"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/process"
"github.com/FloatTech/zbputils/vevent"
"github.com/FloatTech/zbputils/web"
"github.com/fumiama/cron"
"github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/extension/rate"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
lo map[int64]vevent.Loop
entries = map[int64]cron.EntryID{} // id entryid
matchers = map[int64]*zero.Matcher{}
mu sync.Mutex
limit = rate.NewLimiter(time.Second*2, 1)
en = control.Register("job", order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "定时指令触发器\n- 记录以\"完全匹配关键词\"触发的指令\n- 取消以\"完全匹配关键词\"触发的指令\n- 记录在\"cron\"触发的指令\n- 取消在\"cron\"触发的指令\n- 查看所有触发指令\n- 查看在\"cron\"触发的指令\n- 查看以\"完全匹配关键词\"触发的指令\n- 注入指令结果:任意指令\n- 执行指令:任意指令",
PrivateDataFolder: "job",
})
)
func init() {
db.DBPath = en.DataFolder() + "job.db"
err := db.Open()
if err != nil {
panic(err)
}
go func() {
process.GlobalInitMutex.Lock()
process.SleepAbout1sTo2s()
lo = make(map[int64]vevent.Loop, len(zero.BotConfig.Driver))
for _, drv := range zero.BotConfig.Driver {
id := drv.SelfID()
ids := strconv.FormatInt(id, 36)
c := &cmd{}
lo[id] = vevent.NewLoop(id)
err := db.Create(ids, c)
logrus.Infoln("[job]创建表", ids)
if err != nil {
panic(err)
}
_ = db.FindFor(ids, c, "", func() error {
mu.Lock()
defer mu.Unlock()
if strings.HasPrefix(c.Cron, "fm:") {
m := en.OnFullMatch(c.Cron[3:] /* skip fm: */).SetBlock(true)
m.Handle(generalhandler(c))
matchers[c.ID] = getmatcher(m)
return nil
}
if strings.HasPrefix(c.Cron, "sm:") {
m := en.OnFullMatch(c.Cron[3:] /* skip fm: */).SetBlock(true)
m.Handle(superuserhandler(c))
matchers[c.ID] = getmatcher(m)
return nil
}
eid, err := process.CronTab.AddFunc(c.Cron, inject(id, []byte(c.Cmd)))
if err != nil {
return err
}
entries[c.ID] = eid
return nil
})
}
logrus.Infoln("[job]本地环回初始化完成")
process.GlobalInitMutex.Unlock()
}()
en.OnRegex(`^记录在"(.*)"触发的指令$`, ctxext.UserOrGrpAdmin, islonotnil, isfirstregmatchnotnil, logevent).SetBlock(true).Handle(func(ctx *zero.Ctx) {
cron := ctx.State["regex_matched"].([]string)[1]
command := ctx.State["job_raw_event"].(string)
c := &cmd{
ID: idof(cron, command),
Cron: cron,
Cmd: command,
}
err := addcmd(ctx.Event.SelfID, c)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("成功!"))
})
en.OnRegex(`^记录以"(.*)"触发的指令$`, zero.SuperUserPermission, islonotnil, isfirstregmatchnotnil, logevent).SetBlock(true).Handle(func(ctx *zero.Ctx) {
cron := "fm:" + ctx.State["regex_matched"].([]string)[1]
command := ctx.State["job_new_event"].(gjson.Result).Get("message").Raw
logrus.Debugln("[job] get cmd:", command)
c := &cmd{
ID: idof(cron, command),
Cron: cron,
Cmd: command,
}
err := registercmd(ctx.Event.SelfID, c)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("成功!"))
})
en.OnRegex(`^记录以"(.*)"触发的代表我执行的指令$`, zero.SuperUserPermission, islonotnil, isfirstregmatchnotnil, logevent).SetBlock(true).Handle(func(ctx *zero.Ctx) {
cron := "sm:" + ctx.State["regex_matched"].([]string)[1]
command := ctx.State["job_raw_event"].(string)
logrus.Debugln("[job] get cmd:", command)
c := &cmd{
ID: idof(cron, command),
Cron: cron,
Cmd: command,
}
err := registercmd(ctx.Event.SelfID, c)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("成功!"))
})
en.OnRegex(`^取消在"(.*)"触发的指令$`, ctxext.UserOrGrpAdmin, islonotnil, isfirstregmatchnotnil).SetBlock(true).Handle(func(ctx *zero.Ctx) {
cron := ctx.State["regex_matched"].([]string)[1]
err := rmcmd(ctx.Event.SelfID, ctx.Event.UserID, cron)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("成功!"))
})
en.OnRegex(`^取消以"(.*)"触发的(代表我执行的)?指令$`, zero.SuperUserPermission, islonotnil, isfirstregmatchnotnil).SetBlock(true).Handle(func(ctx *zero.Ctx) {
issu := ctx.State["regex_matched"].([]string)[2] != ""
cron := ""
if issu {
cron = "sm:"
} else {
cron = "fm:"
}
cron += ctx.State["regex_matched"].([]string)[1]
err := delcmd(ctx.Event.SelfID, cron)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text("成功!"))
})
en.OnFullMatch("查看所有触发指令", zero.SuperUserPermission, islonotnil).SetBlock(true).Handle(func(ctx *zero.Ctx) {
c := &cmd{}
ids := strconv.FormatInt(ctx.Event.SelfID, 36)
mu.Lock()
defer mu.Unlock()
n, err := db.Count(ids)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
lst := make([]string, 0, n)
err = db.FindFor(ids, c, "GROUP BY cron", func() error {
lst = append(lst, c.Cron+"\n")
return nil
})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(lst))
})
en.OnRegex(`^查看在"(.*)"触发的指令$`, zero.SuperUserPermission, islonotnil, isfirstregmatchnotnil).SetBlock(true).Handle(func(ctx *zero.Ctx) {
c := &cmd{}
ids := strconv.FormatInt(ctx.Event.SelfID, 36)
cron := ctx.State["regex_matched"].([]string)[1]
mu.Lock()
defer mu.Unlock()
n, err := db.Count(ids)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
lst := make([]string, 0, n)
err = db.FindFor(ids, c, "WHERE cron='"+cron+"'", func() error {
lst = append(lst, c.Cmd+"\n")
return nil
})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(lst))
})
en.OnRegex(`^查看以"(.*)"触发的(代表我执行的)?指令$`, zero.SuperUserPermission, islonotnil, isfirstregmatchnotnil).SetBlock(true).Handle(func(ctx *zero.Ctx) {
c := &cmd{}
ids := strconv.FormatInt(ctx.Event.SelfID, 36)
issu := ctx.State["regex_matched"].([]string)[2] != ""
cron := ""
if issu {
cron = "sm:"
} else {
cron = "fm:"
}
cron += ctx.State["regex_matched"].([]string)[1]
mu.Lock()
defer mu.Unlock()
n, err := db.Count(ids)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
lst := make([]string, 0, n)
err = db.FindFor(ids, c, "WHERE cron='"+cron+"'", func() error {
lst = append(lst, c.Cmd+"\n")
return nil
})
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.Text(lst))
})
en.OnPrefix("执行指令:", ctxext.UserOrGrpAdmin, islonotnil, func(ctx *zero.Ctx) bool {
return ctx.State["args"].(string) != ""
}, parseArgs).SetBlock(true).Handle(func(ctx *zero.Ctx) {
ev := strings.ReplaceAll(ctx.Event.RawEvent.Raw, "执行指令:", "")
logrus.Debugln("[job] inject:", ev)
inject(ctx.Event.SelfID, binary.StringToBytes(ev))()
})
en.OnPrefix("注入指令结果:", ctxext.UserOrGrpAdmin, islonotnil, func(ctx *zero.Ctx) bool {
return ctx.State["args"].(string) != ""
}, parseArgs).SetBlock(true).Handle(func(ctx *zero.Ctx) {
vevent.NewLoopOf(vevent.NewAPICallerHook(ctx, func(rsp zero.APIResponse, err error) {
if err == nil {
logrus.Debugln("[job] CallerHook returned")
id := message.NewMessageID(rsp.Data.Get("message_id").String())
if id.ID() == 0 {
ctx.SendChain(message.Text("ERROR:未获取到返回结果"))
return
}
msg := ctx.GetMessage(id)
ctx.Event.NativeMessage = json.RawMessage("\"" + msg.Elements.String() + "\"")
ctx.Event.RawMessageID = json.RawMessage(msg.MessageId.String())
ctx.Event.RawMessage = msg.Elements.String()
time.Sleep(time.Second * 5) // 防止风控
ctx.Event.Time = time.Now().Unix()
ctx.DeleteMessage(id)
vev, cl := binary.OpenWriterF(func(w *binary.Writer) {
err = json.NewEncoder(w).Encode(ctx.Event)
})
if err != nil {
cl()
ctx.SendChain(message.Text("ERROR:", err))
return
}
logrus.Debugln("[job] inject:", binary.BytesToString(vev))
inject(ctx.Event.SelfID, vev)()
cl()
}
})).Echo(binary.StringToBytes(strings.ReplaceAll(ctx.Event.RawEvent.Raw, "注入指令结果:", "")))
})
}
func islonotnil(ctx *zero.Ctx) bool {
return len(lo) > 0
}
func isfirstregmatchnotnil(ctx *zero.Ctx) bool {
return ctx.State["regex_matched"].([]string)[1] != ""
}
func inject(bot int64, response []byte) func() {
return func() {
if limit.Acquire() {
lo[bot].Echo(response)
}
}
}
func idof(cron, cmd string) int64 {
return int64(crc64.Checksum(binary.StringToBytes(cron+cmd), crc64.MakeTable(crc64.ISO)))
}
func addcmd(bot int64, c *cmd) error {
mu.Lock()
defer mu.Unlock()
eid, err := process.CronTab.AddFunc(c.Cron, inject(bot, []byte(c.Cmd)))
if err != nil {
return err
}
entries[c.ID] = eid
return db.Insert(strconv.FormatInt(bot, 36), c)
}
func registercmd(bot int64, c *cmd) error {
mu.Lock()
defer mu.Unlock()
m := en.OnFullMatch(c.Cron[3:] /* skip fm: or sm: */).SetBlock(true)
if strings.HasPrefix(c.Cron, "sm:") {
m.Handle(superuserhandler(c))
} else {
m.Handle(generalhandler(c))
}
matchers[c.ID] = getmatcher(m)
return db.Insert(strconv.FormatInt(bot, 36), c)
}
func generalhandler(c *cmd) zero.Handler {
return func(ctx *zero.Ctx) {
ctx.Event.NativeMessage = json.RawMessage(c.Cmd) // c.Cmd only have message
ctx.Event.Time = time.Now().Unix()
var err error
vev, cl := binary.OpenWriterF(func(w *binary.Writer) {
err = json.NewEncoder(w).Encode(ctx.Event)
})
if err != nil {
cl()
ctx.SendChain(message.Text("ERROR:", err))
return
}
logrus.Debugln("[job] inject:", binary.BytesToString(vev))
inject(ctx.Event.SelfID, vev)()
cl()
}
}
func superuserhandler(c *cmd) zero.Handler {
e := &zero.Event{Sender: new(zero.User)}
err := json.Unmarshal(binary.StringToBytes(c.Cmd), e)
return func(ctx *zero.Ctx) {
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.Event.UserID = e.UserID
ctx.Event.RawMessage = e.RawMessage
ctx.Event.Sender = e.Sender
ctx.Event.NativeMessage = e.NativeMessage
vev, cl := binary.OpenWriterF(func(w *binary.Writer) {
err = json.NewEncoder(w).Encode(ctx.Event)
})
if err != nil {
cl()
ctx.SendChain(message.Text("ERROR:", err))
return
}
logrus.Debugln("[job] inject:", binary.BytesToString(vev))
inject(ctx.Event.SelfID, vev)()
cl()
}
}
func rmcmd(bot, caller int64, cron string) error {
c := &cmd{}
mu.Lock()
defer mu.Unlock()
bots := strconv.FormatInt(bot, 36)
e := new(zero.Event)
var delcmd []string
err := db.FindFor(bots, c, "WHERE cron='"+cron+"'", func() error {
err := json.Unmarshal(binary.StringToBytes(c.Cmd), e)
if err != nil {
return err
}
if e.UserID != caller {
return nil
}
eid, ok := entries[c.ID]
if ok {
process.CronTab.Remove(eid)
delete(entries, c.ID)
delcmd = append(delcmd, "id="+strconv.FormatInt(c.ID, 10))
}
return nil
})
if err != nil {
return err
}
if len(delcmd) > 0 {
return db.Del(bots, "WHERE "+strings.Join(delcmd, " or "))
}
return nil
}
func delcmd(bot int64, cron string) error {
c := &cmd{}
mu.Lock()
defer mu.Unlock()
bots := strconv.FormatInt(bot, 36)
var delcmd []string
err := db.FindFor(bots, c, "WHERE cron='"+cron+"'", func() error {
m, ok := matchers[c.ID]
if ok {
m.Delete()
delete(matchers, c.ID)
delcmd = append(delcmd, "id="+strconv.FormatInt(c.ID, 10))
}
return nil
})
if err != nil {
return err
}
if len(delcmd) > 0 {
return db.Del(bots, "WHERE "+strings.Join(delcmd, " or "))
}
return nil
}
func parseArgs(ctx *zero.Ctx) bool {
cmds := ctx.State["args"].(string)
if !strings.Contains(cmds, "?::") && !strings.Contains(cmds, "!::") {
return true
}
args := make(map[int]string)
for strings.Contains(ctx.Event.RawEvent.Raw, "?::") {
start := strings.Index(ctx.Event.RawEvent.Raw, "?::")
msgend := strings.Index(ctx.Event.RawEvent.Raw[start+3:], "::")
if msgend < 0 {
ctx.SendChain(message.Text("ERROR:找不到结束的::"))
return false
}
msgend += start + 3
numend := strings.Index(ctx.Event.RawEvent.Raw[msgend+2:], "!")
if numend <= 0 {
ctx.SendChain(message.Text("ERROR:找不到结束的!"))
return false
}
numend += msgend + 2
logrus.Debugln("[job]", start, msgend, numend)
msg := ctx.Event.RawEvent.Raw[start+3 : msgend]
arg, err := strconv.Atoi(ctx.Event.RawEvent.Raw[msgend+2 : numend])
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
arr, ok := args[arg]
if !ok {
var id message.MessageID
if msg == "" {
id = ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入参数", arg))
} else {
id = ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[", arg, "] ", msg))
}
select {
case <-time.After(time.Second * 120):
ctx.Send(message.ReplyWithMessage(id, message.Text("参数读取超时")))
if msg[0] != '?' {
return false
}
case e := <-zero.NewFutureEvent("message", 0, true, zero.CheckUser(ctx.Event.UserID)).Next():
args[arg] = e.Message.String()
arr = args[arg]
process.SleepAbout1sTo2s()
ctx.SendChain(message.Reply(e.MessageID), message.Text("已记录"))
process.SleepAbout1sTo2s()
}
}
ctx.Event.RawEvent.Raw = ctx.Event.RawEvent.Raw[:start] + arr + ctx.Event.RawEvent.Raw[numend+1:]
}
args = make(map[int]string)
for strings.Contains(ctx.Event.RawEvent.Raw, "!::") {
start := strings.Index(ctx.Event.RawEvent.Raw, "!::")
msgend := strings.Index(ctx.Event.RawEvent.Raw[start+3:], "::")
if msgend < 0 {
ctx.SendChain(message.Text("ERROR:找不到结束的::"))
return false
}
msgend += start + 3
numend := strings.Index(ctx.Event.RawEvent.Raw[msgend+2:], "!")
if numend <= 0 {
ctx.SendChain(message.Text("ERROR:找不到结束的!"))
return false
}
numend += msgend + 2
logrus.Debugln("[job]", start, msgend, numend)
u := ctx.Event.RawEvent.Raw[start+3 : msgend]
if u == "" {
return false
}
arg, err := strconv.Atoi(ctx.Event.RawEvent.Raw[msgend+2 : numend])
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return false
}
arr, ok := args[arg]
if !ok {
isnilable := u[0] == '?'
if isnilable {
u = u[1:]
if u == "" {
return false
}
}
b, err := web.GetData(u)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
if !isnilable {
return false
}
}
if len(b) > 0 {
type fakejson struct {
Arg string `json:"arg"`
}
f := fakejson{Arg: binary.BytesToString(b)}
w := binary.SelectWriter()
defer binary.PutWriter(w)
_ = json.NewEncoder(w).Encode(&f)
arr = w.String()[8 : w.Len()-3]
args[arg] = arr
}
}
w := binary.SelectWriter()
w.WriteString(ctx.Event.RawEvent.Raw[:start])
w.WriteString(arr)
w.WriteString(ctx.Event.RawEvent.Raw[numend+1:])
ctx.Event.RawEvent.Raw = string(w.Bytes())
binary.PutWriter(w)
}
return true
}
func logevent(ctx *zero.Ctx) bool {
ctx.SendChain(message.Text("您的下一条指令将被记录,在", ctx.State["regex_matched"].([]string)[1], "时触发"))
select {
case <-time.After(time.Second * 120):
ctx.SendChain(message.Text("指令记录超时"))
return false
case e := <-zero.NewFutureEvent("message", 0, true, zero.CheckUser(ctx.Event.UserID)).Next():
ctx.State["job_raw_event"] = e.RawEvent.Raw
ctx.State["job_new_event"] = e.RawEvent
return true
}
}

View File

@@ -1,16 +0,0 @@
package job
import (
"unsafe"
"github.com/FloatTech/zbputils/control"
zero "github.com/wdvxdr1123/ZeroBot"
)
type matcherinstance struct {
m *zero.Matcher
}
func getmatcher(m control.Matcher) *zero.Matcher {
return (*matcherinstance)(unsafe.Pointer(&m)).m
}

View File

@@ -1,11 +0,0 @@
package job
import sql "github.com/FloatTech/sqlite"
type cmd struct {
ID int64 `db:"id"`
Cron string `db:"cron"`
Cmd string `db:"cmd"`
}
var db = &sql.Sqlite{}

View File

@@ -48,7 +48,7 @@ const (
"- 取消在\"cron\"的提醒\n" +
"- 列出所有提醒\n" +
"- 翻牌\n" +
"- 设置欢迎语XXX(可加{at}在欢迎时@对方)\n" +
"- 设置欢迎语XXX 可选添加 [{at}] [{nickname}] [{avatar}] {at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像\n" +
"- 测试欢迎语\n" +
"- [开启 | 关闭]入群验证"
)
@@ -381,7 +381,7 @@ func init() { // 插件主体
var w welcome
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(strings.ReplaceAll(w.Msg, "{at}", "[CQ:at,qq="+strconv.FormatInt(ctx.Event.UserID, 10)+"]")))
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else {
ctx.SendChain(message.Text("欢迎~"))
}
@@ -454,7 +454,7 @@ func init() { // 插件主体
var w welcome
err := db.Find("welcome", &w, "where gid = "+strconv.FormatInt(ctx.Event.GroupID, 10))
if err == nil {
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(strings.ReplaceAll(w.Msg, "{at}", "[CQ:at,qq="+strconv.FormatInt(ctx.Event.UserID, 10)+"]")))
ctx.SendGroupMessage(ctx.Event.GroupID, message.ParseMessageFromString(welcometocq(ctx, w.Msg)))
} else {
ctx.SendChain(message.Text("欢迎~"))
}
@@ -540,3 +540,14 @@ func init() { // 插件主体
}
})
}
// 传入 ctx 和 welcome格式string 返回cq格式string 使用方法:welcometocq(ctx,w.Msg)
func welcometocq(ctx *zero.Ctx, welcome string) string {
nickname := ctx.GetGroupMemberInfo(ctx.Event.GroupID, ctx.Event.UserID, false).Get("nickname").Str
at := "[CQ:at,qq=" + strconv.FormatInt(ctx.Event.UserID, 10) + "]"
avatar := "[CQ:image,file=" + "http://q4.qlogo.cn/g?b=qq&nk=" + strconv.FormatInt(ctx.Event.UserID, 10) + "&s=640]"
cqstring := strings.ReplaceAll(welcome, "{at}", at)
cqstring = strings.ReplaceAll(cqstring, "{nickname}", nickname)
cqstring = strings.ReplaceAll(cqstring, "{avatar}", avatar)
return cqstring
}

View File

@@ -2,68 +2,62 @@
package moyu
import (
"sync"
"time"
control "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/process"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/zbputils/control/order"
)
var (
msg message.Message
mu sync.Mutex
lastupdate time.Time
)
func init() { // 插件主体
control.Register("moyu", order.AcquirePrio(), &control.Options{
DisableOnDefault: true,
Help: "moyu\n" +
"- /启用 moyu\n" +
"- /禁用 moyu",
})
// 定时任务每天10点执行一次
_, err := process.CronTab.AddFunc("0 10 * * *", func() { sendNotice() })
if err != nil {
panic(err)
}
}
// 获取数据拼接消息链并发送
func sendNotice() {
m, ok := control.Lookup("moyu")
if ok {
if registry.Connect() != nil {
return
}
msg := message.Message{
message.Text(time.Now().Format("2006-01-02")),
message.Text("上午好,摸鱼人!\n工作再累一定不要忘记摸鱼哦有事没事起身去茶水间去厕所去廊道走走别老在工位上坐着钱是老板的,但命是自己的。\n"),
message.Text(weekend()),
message.Text("\n"),
message.Text(GetHoliday("元旦")),
message.Text("\n"),
message.Text(GetHoliday("春节")),
message.Text("\n"),
message.Text(GetHoliday("清明节")),
message.Text("\n"),
message.Text(GetHoliday("劳动节")),
message.Text("\n"),
message.Text(GetHoliday("端午节")),
message.Text("\n"),
message.Text(GetHoliday("中秋节")),
message.Text("\n"),
message.Text(GetHoliday("国庆节")),
message.Text("\n"),
message.Text("上班是帮老板赚钱,摸鱼是赚老板的钱!最后,祝愿天下所有摸鱼人,都能愉快的渡过每一天…"),
}
_ = registry.Close()
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
for _, g := range ctx.GetGroupList().Array() {
grp := g.Get("group_id").Int()
if m.IsEnabledIn(grp) {
ctx.SendGroupMessage(grp, msg)
"- /禁用 moyu\n" +
"- 记录在\"0 10 * * *\"触发的指令\n" +
" - 摸鱼提醒",
}).OnFullMatch("摸鱼提醒").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
mu.Lock()
defer mu.Unlock()
if msg == nil || time.Since(lastupdate) > time.Hour*20 {
if registry.Connect() != nil {
return
}
msg = message.Message{
message.Text(time.Now().Format("2006-01-02")),
message.Text("上午好,摸鱼人!\n工作再累一定不要忘记摸鱼哦有事没事起身去茶水间去厕所去廊道走走别老在工位上坐着钱是老板的,但命是自己的。\n"),
message.Text(weekend()),
message.Text("\n"),
message.Text(GetHoliday("元旦")),
message.Text("\n"),
message.Text(GetHoliday("春节")),
message.Text("\n"),
message.Text(GetHoliday("清明节")),
message.Text("\n"),
message.Text(GetHoliday("劳动节")),
message.Text("\n"),
message.Text(GetHoliday("端午节")),
message.Text("\n"),
message.Text(GetHoliday("中秋节")),
message.Text("\n"),
message.Text(GetHoliday("国庆节")),
message.Text("\n"),
message.Text("上班是帮老板赚钱,摸鱼是赚老板的钱!最后,祝愿天下所有摸鱼人,都能愉快的渡过每一天…"),
}
_ = registry.Close()
lastupdate = time.Now()
}
return true
ctx.Send(msg)
})
}
}

View File

@@ -14,7 +14,6 @@ import (
control "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/control/order"
"github.com/FloatTech/zbputils/process"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
@@ -31,7 +30,9 @@ func init() {
DisableOnDefault: true,
Help: "摸鱼人日历\n" +
"- /启用 moyucalendar\n" +
"- /禁用 moyucalendar",
"- /禁用 moyucalendar\n" +
"- 记录在\"30 8 * * *\"触发的指令\n" +
" - 摸鱼人日历",
}).OnFullMatch("摸鱼人日历").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
title := fmt.Sprintf("摸鱼人日历 %d月%d日", time.Now().Month(), time.Now().Day())
@@ -52,40 +53,6 @@ func init() {
}
ctx.SendChain(message.Image(image))
})
// 定时任务每天8点30分执行一次
_, err := process.CronTab.AddFunc("30 8 * * *", func() {
m, ok := control.Lookup("moyucalendar")
if !ok {
return
}
title := fmt.Sprintf("摸鱼人日历 %d月%d日", time.Now().Month(), time.Now().Day())
sg, cookies, err := sougou(title, "摸鱼人日历", ua)
if err != nil {
return
}
wx, err := redirect(sg, cookies, ua)
if err != nil {
return
}
image, err := calendar(wx, ua)
if err != nil {
return
}
zero.RangeBot(func(id int64, ctx *zero.Ctx) bool {
for _, g := range ctx.GetGroupList().Array() {
grp := g.Get("group_id").Int()
if m.IsEnabledIn(grp) {
ctx.SendGroupMessage(grp, message.Message{message.Image(image)})
process.SleepAbout1sTo2s()
}
}
return true
})
})
if err != nil {
panic(err)
}
}
func sougou(title, publisher, ua string) (string, []*http.Cookie, error) {

67
plugin/zaobao/zaobao.go Normal file
View File

@@ -0,0 +1,67 @@
// Package zaobao 易即今日公众号api的今日早报
package zaobao
import (
"sync"
"time"
"github.com/tidwall/gjson"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/zbputils/binary"
control "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/web"
"github.com/FloatTech/zbputils/control/order"
)
const (
api = "http://api.soyiji.com/news_jpg"
referer = "safe.soyiji.com"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
)
var (
picdata []byte
mu sync.Mutex
pictime time.Time
)
func init() { // 插件主体
engine := control.Register("zaobao", order.AcquirePrio(), &control.Options{
DisableOnDefault: true,
Help: "易即今日公众号api的今日早报\n" +
"api早上8点更新推荐定时在8点30后\n" +
"配合插件job中的记录在\"cron\"触发的指令使用\n" +
"- 记录在\"0 9 * * *\"触发的指令\n" +
" - 今日早报",
})
engine.OnFullMatch("今日早报", zero.OnlyGroup).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
err := getdata()
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
ctx.SendChain(message.ImageBytes(picdata))
})
}
func getdata() error { // 获取图片链接并且下载
mu.Lock()
defer mu.Unlock()
if picdata != nil && time.Since(pictime) <= time.Hour*20 {
return nil
}
data, err := web.GetDataWith(web.NewDefaultClient(), api, "GET", "", ua)
if err != nil {
return err
}
picdata, err = web.GetDataWith(web.NewDefaultClient(), gjson.Get(binary.BytesToString(data), "url").String(), "GET", referer, ua)
if err != nil {
return err
}
pictime = time.Now()
return nil
}