diff --git a/.goreleaser.yml b/.goreleaser.yml
index 4206cc9658..a2bbb1eb0e 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -66,7 +66,7 @@ archives:
format: zip
nfpms:
- - license: GPL 3.0
+ - license: AGPL 3.0
homepage: https://github.com/FloatTech/ZeroBot-Plugin
file_name_template: "zbp_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
formats:
diff --git a/README.md b/README.md
index 19e1585937..29a6c059c2 100644
--- a/README.md
+++ b/README.md
@@ -970,6 +970,23 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 吟唱提示[xxxx]
+
+
+ 钓鱼模拟器
+
+ `import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/mcfish"`
+
+ - [x] 钓鱼商店
+ - [x] 购买xxx [数量]
+ - [x] 出售xxx [数量]
+ - [x] 钓鱼背包
+ - [x] 装备[xx竿|三叉戟|美西螈]
+ - [x] 附魔[诱钓|海之眷顾]
+ - [x] 修复鱼竿
+ - [x] 合成[xx竿|三叉戟]
+ - [x] 进行钓鱼
+ - [x] 进行n次钓鱼
+
简易midi音乐制作
diff --git a/data b/data
index 9cab8a7bae..32dffa9c50 160000
--- a/data
+++ b/data
@@ -1 +1 @@
-Subproject commit 9cab8a7bae5d076e7440475750cd0b54f19fe558
+Subproject commit 32dffa9c507055b583935a94712ea8b8c79100e6
diff --git a/go.mod b/go.mod
index ac51a1c74a..605efbd8cc 100644
--- a/go.mod
+++ b/go.mod
@@ -4,15 +4,15 @@ go 1.20
require (
github.com/Baidu-AIP/golang-sdk v1.1.1
- github.com/FloatTech/AnimeAPI v1.6.1-0.20230724165034-a3cf504fab92
- github.com/FloatTech/floatbox v0.0.0-20230331064925-9af336a84944
+ github.com/FloatTech/AnimeAPI v1.6.1-0.20230827161910-7e6a3d93a0ba
+ github.com/FloatTech/floatbox v0.0.0-20230827160415-f0865337a824
github.com/FloatTech/gg v1.1.3-0.20230226151425-6ea91286ba08
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef
github.com/FloatTech/rendercard v0.0.10-0.20230223064326-45d29fa4ede9
github.com/FloatTech/sqlite v1.6.2
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b
github.com/FloatTech/zbpctrl v1.5.3-0.20230514154630-b74e6fcca380
- github.com/FloatTech/zbputils v1.6.2-0.20230728081122-94d4d957f3bf
+ github.com/FloatTech/zbputils v1.6.2-0.20230903052647-820a67856603
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5
github.com/antchfx/htmlquery v1.2.5
@@ -21,7 +21,7 @@ require (
github.com/disintegration/imaging v1.6.2
github.com/fumiama/ahsai v0.1.0
github.com/fumiama/cron v1.3.0
- github.com/fumiama/go-base16384 v1.6.4
+ github.com/fumiama/go-base16384 v1.7.0
github.com/fumiama/go-registry v0.2.6
github.com/fumiama/gotracemoe v0.0.3
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565
@@ -31,8 +31,9 @@ require (
github.com/jozsefsallai/gophersauce v1.0.1
github.com/lithammer/fuzzysearch v1.1.5
github.com/mroth/weightedrand v1.0.0
+ github.com/notnil/chess v1.9.0
github.com/pkg/errors v0.9.1
- github.com/quic-go/quic-go v0.32.0
+ github.com/quic-go/quic-go v0.38.1
github.com/shirou/gopsutil/v3 v3.23.1
github.com/sirupsen/logrus v1.9.0
github.com/tidwall/gjson v1.14.4
@@ -40,12 +41,13 @@ require (
github.com/wdvxdr1123/ZeroBot v1.7.4
gitlab.com/gomidi/midi/v2 v2.0.25
golang.org/x/image v0.3.0
- golang.org/x/sys v0.4.0
- golang.org/x/text v0.6.0
+ golang.org/x/sys v0.8.0
+ golang.org/x/text v0.9.0
gopkg.in/yaml.v3 v3.0.1
)
require (
+ github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca // indirect
github.com/antchfx/xpath v1.2.1 // indirect
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect
github.com/faiface/beep v1.1.0 // indirect
@@ -54,7 +56,7 @@ require (
github.com/fumiama/imgsz v0.0.2 // indirect
github.com/gabriel-vasile/mimetype v1.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
- github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
+ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
@@ -67,14 +69,12 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
- github.com/onsi/ginkgo/v2 v2.2.0 // indirect
+ github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pkumza/numcn v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // indirect
- github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
- github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
- github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
+ github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
@@ -85,9 +85,9 @@ require (
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/exp/shiny v0.0.0-20221126150942-6ab00d035af9 // indirect
golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f // indirect
- golang.org/x/mod v0.6.0 // indirect
- golang.org/x/net v0.4.0 // indirect
- golang.org/x/tools v0.2.0 // indirect
+ golang.org/x/mod v0.10.0 // indirect
+ golang.org/x/net v0.10.0 // indirect
+ golang.org/x/tools v0.9.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
modernc.org/libc v1.21.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
diff --git a/go.sum b/go.sum
index 4fc6a4ad9c..ddf9df0629 100644
--- a/go.sum
+++ b/go.sum
@@ -2,10 +2,10 @@ github.com/Baidu-AIP/golang-sdk v1.1.1 h1:RQsAmgDSAkiq22I6n7XJ2t3afgzFeqjY46FGhv
github.com/Baidu-AIP/golang-sdk v1.1.1/go.mod h1:bXnGw7xPeKt8aF7UCELKrV6UZ/46spItONK1RQBQj1Y=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
-github.com/FloatTech/AnimeAPI v1.6.1-0.20230724165034-a3cf504fab92 h1:B4IaLEhnXdr7oqszneri6ykdHggL2GvPUueBB5WxYVQ=
-github.com/FloatTech/AnimeAPI v1.6.1-0.20230724165034-a3cf504fab92/go.mod h1:fWJ5hx1ggDmLyLldO53Go97zc1JpWnDpsC5qeTOcaVw=
-github.com/FloatTech/floatbox v0.0.0-20230331064925-9af336a84944 h1:/eQoMa6Aj3coF5F7yhzZe1+SzX6SItul7MW8//pl18o=
-github.com/FloatTech/floatbox v0.0.0-20230331064925-9af336a84944/go.mod h1:FwQm6wk+b4wuW54KCKn3zccMX47Q5apnHD/Yakzv0fI=
+github.com/FloatTech/AnimeAPI v1.6.1-0.20230827161910-7e6a3d93a0ba h1:0tqYkIc6RK8P3/cGEMSoBBwhiEHKPJC+1F7xlVkikgY=
+github.com/FloatTech/AnimeAPI v1.6.1-0.20230827161910-7e6a3d93a0ba/go.mod h1:6vYu7bW5gPQsBnXB+I6yk+eJQaaAwusoQ/I/wQMwOAI=
+github.com/FloatTech/floatbox v0.0.0-20230827160415-f0865337a824 h1:w72fzQg1Y9+VLSRl7iKzaZ6fG3myyMJfpOSajcjaMDM=
+github.com/FloatTech/floatbox v0.0.0-20230827160415-f0865337a824/go.mod h1:FwQm6wk+b4wuW54KCKn3zccMX47Q5apnHD/Yakzv0fI=
github.com/FloatTech/gg v1.1.3-0.20230226151425-6ea91286ba08 h1:dPLeoiTVSBlgls+66EB/UJ2e38BaASmBN5nANaycSBU=
github.com/FloatTech/gg v1.1.3-0.20230226151425-6ea91286ba08/go.mod h1:uzPzAeT35egARdRuu+1oyjU3CmTwCceoq3Vvje7LpcI=
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef h1:CJbK/2FRwPuZpeb6M4sWK2d7oXDnBEGhpkQuQrgc91A=
@@ -18,13 +18,15 @@ github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJG
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
github.com/FloatTech/zbpctrl v1.5.3-0.20230514154630-b74e6fcca380 h1:qmwoT8xVaND01aCdwy+5/j6z490nehQWZAVyTBN8ahU=
github.com/FloatTech/zbpctrl v1.5.3-0.20230514154630-b74e6fcca380/go.mod h1:gkGC1C1eEUd/Ld/ja68zas5j2ZktIZCdnj2FMaM+Au0=
-github.com/FloatTech/zbputils v1.6.2-0.20230728081122-94d4d957f3bf h1:PwH9aMnmN+m204cVIqUrI3e7nsdQi/IGW012Fjzb1bs=
-github.com/FloatTech/zbputils v1.6.2-0.20230728081122-94d4d957f3bf/go.mod h1:JRnGR7EGeEQgxOs+c0rZAhrS9Es2BTcGHdIDHXIPRzQ=
+github.com/FloatTech/zbputils v1.6.2-0.20230903052647-820a67856603 h1:06zrK+XJCOLhW+HADibOmeqcVEC6vwFGRQiDRyvioWQ=
+github.com/FloatTech/zbputils v1.6.2-0.20230903052647-820a67856603/go.mod h1:JRnGR7EGeEQgxOs+c0rZAhrS9Es2BTcGHdIDHXIPRzQ=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA=
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5 h1:bBmmB7he0iVN4m5mcehfheeRUEer/Avo4ujnxI3uCqs=
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5/go.mod h1:0UcFaCkhp6vZw6l5Dpq0Dp673CoF9GdvA8lTfst0GiU=
+github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca h1:kWzLcty5V2rzOqJM7Tp/MfSX0RMSI1x4IOLApEefYxA=
+github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antchfx/htmlquery v1.2.5 h1:1lXnx46/1wtv1E/kzmH8vrfMuUKYgkdDBA9pIdMJnk4=
github.com/antchfx/htmlquery v1.2.5/go.mod h1:2MCVBzYVafPBmKbrmwB9F5xdd+IEgRY61ci2oOsOQVw=
@@ -58,8 +60,8 @@ github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCO
github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo=
github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY=
-github.com/fumiama/go-base16384 v1.6.4 h1:rYDRwD/th2cG4U7QLokpzmST1cCxZGXtHmolOUePt5o=
-github.com/fumiama/go-base16384 v1.6.4/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
+github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
+github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
github.com/fumiama/go-registry v0.2.6 h1:+vEeBUwa1+GC87ujW3Km42fi8O/H7QcpVJWu1iuGNh0=
github.com/fumiama/go-registry v0.2.6/go.mod h1:HjYagPZXzR2xCCxaSQerqX7JRzC0yiv2kslDdBiTq/g=
github.com/fumiama/go-simple-protobuf v0.1.0 h1:rLzJgNqB6LHNDVMl81yyNt6ZKziWtVfu+ioF0edlEVw=
@@ -83,13 +85,14 @@ github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebK
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
github.com/go-audio/riff v1.0.0/go.mod h1:l3cQwc85y79NQFCRB7TiPoNiaijp6q8Z0Uv38rVG498=
github.com/go-audio/wav v1.0.0/go.mod h1:3yoReyQOsiARkvPl3ERCi8JFjihzG6WhjYpZCf5zAWE=
+github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
+github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
@@ -99,7 +102,7 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -151,9 +154,11 @@ github.com/mroth/weightedrand v1.0.0 h1:V8JeHChvl2MP1sAoXq4brElOcza+jxLkRuwvtQu8
github.com/mroth/weightedrand v1.0.0/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
-github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
-github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
-github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
+github.com/notnil/chess v1.9.0 h1:YMxR5kUVjtwcuFptGU0/3q7eG3MSHQNbg0VUekvRKV0=
+github.com/notnil/chess v1.9.0/go.mod h1:cRuJUIBFq9Xki05TWHJxHYkC+fFpq45IWwk94DdlCrA=
+github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
+github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
+github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -168,14 +173,10 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
-github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
-github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
-github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
-github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
-github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
-github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
-github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
-github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
+github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
+github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
+github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE=
+github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
@@ -185,7 +186,7 @@ github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -241,8 +242,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
-golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -254,8 +255,8 @@ golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
-golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -277,23 +278,25 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
-golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
+golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -302,7 +305,6 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/kanban/banner/banner.go b/kanban/banner/banner.go
index ba64dacdec..626de2a768 100644
--- a/kanban/banner/banner.go
+++ b/kanban/banner/banner.go
@@ -3,13 +3,13 @@
package banner
// Version ...
-var Version = "v1.7.2"
+var Version = "v1.7.3"
// Copyright ...
var Copyright = "© 2020 - 2023 FloatTech"
// Banner ...
var Banner = "* OneBot + ZeroBot + Golang\n" +
- "* Version " + Version + " - 2023-07-28 13:58:50 +0800 CST\n" +
+ "* Version " + Version + " - 2023-08-28 16:14:08 +0800 CST\n" +
"* Copyright " + Copyright + ". All Rights Reserved.\n" +
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
diff --git a/kanban/version_less_than_1.21.go b/kanban/version_less_than_1.21.go
new file mode 100644
index 0000000000..577d06be25
--- /dev/null
+++ b/kanban/version_less_than_1.21.go
@@ -0,0 +1,5 @@
+//go:build go1.21
+
+package kanban
+
+const Error int = "请使用小于1.21版本的Go"
diff --git a/main.go b/main.go
index b95aa9b494..21873f9b0d 100644
--- a/main.go
+++ b/main.go
@@ -34,7 +34,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chat" // 基础词库
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleep_manage" // 统计睡眠时间
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleepmanage" // 统计睡眠时间
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库
@@ -61,19 +61,20 @@ import (
// vvvv //
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ahsai" // ahsai tts
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_false" // 服务器监控
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aifalse" // 服务器监控
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aipaint" // ai绘图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/autowithdraw" // 触发者撤回时也自动撤回
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/base16384" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base64gua" // base64卦加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baseamasiro" // base天城文加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/book_review" // 哀伤雪刃吧推书记录
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bookreview" // 哀伤雪刃吧推书记录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/chess" // 国际象棋
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转
@@ -86,7 +87,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dish" // 程序员做饭指南
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drawlots" // 多功能抽签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dress" // 女装
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/driftbottle" // 漂流瓶
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/event" // 好友申请群聊邀请事件处理
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
@@ -100,7 +101,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hitokoto" // 一言
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hyaku" // 百人一首
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/imgfinder" // 关键字搜图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jiami" // 兽语加密
@@ -112,14 +113,14 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe" // 日韩 VITS 模型拟声
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar" // 摸鱼人日历
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyucalendar" // 摸鱼人日历
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife" // 本地老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nihongo" // 日语语法学习
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nwife" // 本地老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/quan" // QQ权重查询
@@ -139,16 +140,16 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vitsnyaru" // vits猫雷
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtbmusic" // vtb点歌
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtbquotation" // vtb语录
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wallet" // 钱包
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wantquotes" // 据意查句
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/warframeapi" // warframeAPI插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenben" // 文本指令大全
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinAI" // 百度文心AI画图
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinvilg" // 百度文心AI画图
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wife" // 抽老婆
- _ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count" // 聊天热词
+ _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordcount" // 聊天热词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygo" // 游戏王相关插件
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
diff --git a/plugin/ahsai/ahsai.go b/plugin/ahsai/ahsai.go
index 73d6df9163..697cf295a0 100644
--- a/plugin/ahsai/ahsai.go
+++ b/plugin/ahsai/ahsai.go
@@ -27,7 +27,7 @@ var (
)
func init() {
- engine := control.Register("ahsai", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "フリーテキスト音声合成",
Help: "- 使[伊織弓鶴|紲星あかり|結月ゆかり|京町セイカ|東北きりたん|東北イタコ|ついなちゃん標準語|ついなちゃん関西弁|音街ウナ|琴葉茜|吉田くん|民安ともえ|桜乃そら|月読アイ|琴葉葵|東北ずん子|月読ショウタ|水奈瀬コウ]说(日语)",
diff --git a/plugin/ai_reply/ai_tts.go b/plugin/ai_reply/ai_tts.go
index ae1abd0535..eaa7579e72 100644
--- a/plugin/ai_reply/ai_tts.go
+++ b/plugin/ai_reply/ai_tts.go
@@ -23,10 +23,10 @@ import (
// 数据结构: [4 bits] [4 bits] [8 bits]
// [ttscn模式] [百度模式] [tts模式]
-// [tts模式]: 0~63 genshin 64 baidu 65 ttscn
+// [tts模式]: 0~200 genshin 201 baidu 202 ttscn
const (
- lastgsttsindex = 63 + iota
+ lastgsttsindex = 200 + iota
baiduttsindex
ttscnttsindex
)
@@ -111,7 +111,7 @@ func (r replymode) getReplyMode(ctx *zero.Ctx) aireply.AIReply {
}
var ttsins = func() map[string]tts.TTS {
- m := make(map[string]tts.TTS, 128)
+ m := make(map[string]tts.TTS, 512)
for _, mode := range append(genshin.SoundList[:], extrattsname...) {
m[mode] = nil
}
@@ -119,8 +119,8 @@ var ttsins = func() map[string]tts.TTS {
}()
var ttsModes = func() []string {
- s := append(genshin.SoundList[:], make([]string, 64-len(genshin.SoundList))...) // 0-63
- s = append(s, extrattsname...) // 64 65 ...
+ s := append(genshin.SoundList[:], make([]string, lastgsttsindex-len(genshin.SoundList))...) // 0-200
+ s = append(s, extrattsname...) // 201 202 ...
return s
}()
diff --git a/plugin/ai_reply/main.go b/plugin/ai_reply/main.go
index 40d30d130f..447ee33469 100644
--- a/plugin/ai_reply/main.go
+++ b/plugin/ai_reply/main.go
@@ -2,6 +2,7 @@
package aireply
import (
+ "os"
"regexp"
"strconv"
"time"
@@ -75,6 +76,12 @@ func init() { // 插件主体
})
endpre := regexp.MustCompile(`\pP$`)
+ ttscachedir := ent.DataFolder() + "cache/"
+ _ = os.RemoveAll(ttscachedir)
+ err := os.MkdirAll(ttscachedir, 0755)
+ if err != nil {
+ panic(err)
+ }
ent.OnMessage(zero.OnlyToMe).SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
msg := ctx.ExtractPlainText()
@@ -123,27 +130,29 @@ func init() { // 插件主体
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
return
}
- if banner, ok := genshin.TestRecord[param]; ok {
- logrus.Debugln("[tts] banner:", banner, "get sound mode...")
- // 设置验证
- speaker, err := ttsmd.getSoundMode(ctx)
- if err != nil {
- ctx.SendChain(message.Text("ERROR: ", err))
- return
- }
- logrus.Debugln("[tts] got sound mode, speaking...")
- rec, err := speaker.Speak(ctx.Event.UserID, func() string { return banner })
- if err != nil {
- ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("无法发送测试语音,请重试。"))
- return
- }
- logrus.Debugln("[tts] sending...")
- if id := ctx.SendChain(message.Record(rec).Add("cache", 0)); id.ID() == 0 {
- ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("无法发送测试语音,请重试。"))
- return
- }
- time.Sleep(time.Second * 2)
+ banner := genshin.TestRecord[param]
+ if banner == "" {
+ banner = genshin.TestRecord["默认"]
+ }
+ logrus.Debugln("[tts] banner:", banner, "get sound mode...")
+ // 设置验证
+ speaker, err := ttsmd.getSoundMode(ctx)
+ if err != nil {
+ ctx.SendChain(message.Text("ERROR: ", err))
+ return
+ }
+ logrus.Debugln("[tts] got sound mode, speaking...")
+ rec, err := speaker.Speak(ctx.Event.UserID, func() string { return banner })
+ if err != nil {
+ ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("无法发送测试语音,请重试。"))
+ return
+ }
+ logrus.Debugln("[tts] sending...")
+ if id := ctx.SendChain(message.Record(rec).Add("cache", 0)); id.ID() == 0 {
+ ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("无法发送测试语音,请重试。"))
+ return
}
+ time.Sleep(time.Second * 2)
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("设置成功"))
})
diff --git a/plugin/ai_false/ai_false.go b/plugin/aifalse/main.go
similarity index 99%
rename from plugin/ai_false/ai_false.go
rename to plugin/aifalse/main.go
index 230ebfae35..0ed7ba5e60 100644
--- a/plugin/ai_false/ai_false.go
+++ b/plugin/aifalse/main.go
@@ -51,7 +51,7 @@ var (
)
func init() { // 插件主体
- engine := control.Register("aifalse", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "自检, 全局限速",
Help: "- 查询计算机当前活跃度: [检查身体 | 自检 | 启动自检 | 系统状态]\n" +
diff --git a/plugin/aipaint/aipaint.go b/plugin/aipaint/aipaint.go
index e6771bd1c5..6e370a644c 100644
--- a/plugin/aipaint/aipaint.go
+++ b/plugin/aipaint/aipaint.go
@@ -46,7 +46,7 @@ func (r *result) String() string {
}
func init() { // 插件主体
- engine := control.Register("aipaint", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "ai绘图",
Help: "- [ ai绘图 | 生成色图 | 生成涩图 | ai画图 ] xxx\n" +
diff --git a/plugin/aiwife/non-existent.go b/plugin/aiwife/non-existent.go
index 1de274673a..e4b89490cc 100644
--- a/plugin/aiwife/non-existent.go
+++ b/plugin/aiwife/non-existent.go
@@ -17,7 +17,7 @@ const (
)
func init() { // 插件主体
- control.Register("aiwife", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "ai随机生成老婆",
Help: "- waifu | 随机waifu",
diff --git a/plugin/alipayvoice/alipayvoice.go b/plugin/alipayvoice/alipayvoice.go
index 18a27c2cb2..dad75146d5 100644
--- a/plugin/alipayvoice/alipayvoice.go
+++ b/plugin/alipayvoice/alipayvoice.go
@@ -16,7 +16,7 @@ const (
)
func init() { // 插件主体
- engine := control.Register("alipayvoice", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "支付宝到账语音",
Help: "- 支付宝到账 1",
diff --git a/plugin/antiabuse/anti.go b/plugin/antiabuse/anti.go
index c9f19bd1a7..b4ba459f68 100644
--- a/plugin/antiabuse/anti.go
+++ b/plugin/antiabuse/anti.go
@@ -38,7 +38,7 @@ func onDel(uid int64, _ struct{}) {
}
func init() {
- engine := control.Register("antiabuse", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "违禁词检测",
Help: "- /[添加|删除|查看]违禁词",
diff --git a/plugin/atri/atri.go b/plugin/atri/atri.go
index 0b5331130f..cb0fcfffb7 100644
--- a/plugin/atri/atri.go
+++ b/plugin/atri/atri.go
@@ -48,7 +48,7 @@ func isAtriSleeping(*zero.Ctx) bool {
}
func init() { // 插件主体
- engine := control.Register("atri", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "atri人格文本回复",
Help: "本插件基于 ATRI ,为 Golang 移植版\n" +
diff --git a/plugin/autowithdraw/main.go b/plugin/autowithdraw/main.go
index 1960b33ef5..89e532b9e0 100644
--- a/plugin/autowithdraw/main.go
+++ b/plugin/autowithdraw/main.go
@@ -10,7 +10,7 @@ import (
)
func init() {
- control.Register("autowithdraw", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "触发者撤回时也自动撤回",
Help: "- 撤回一条消息\n",
diff --git a/plugin/baidu/search.go b/plugin/baidu/search.go
index 4942d24d08..3b44544a83 100644
--- a/plugin/baidu/search.go
+++ b/plugin/baidu/search.go
@@ -27,7 +27,7 @@ type result struct {
}
func init() { // 主函数
- en := control.Register("baidu", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "百科\n" +
"- 百度/百科/维基/wiki[关键字]",
diff --git a/plugin/baiduaudit/audit.go b/plugin/baiduaudit/audit.go
index 6827ad7de1..da62d48f89 100644
--- a/plugin/baiduaudit/audit.go
+++ b/plugin/baiduaudit/audit.go
@@ -34,7 +34,7 @@ var (
)
func init() {
- engine := control.Register("baiduaudit", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "百度内容审核",
Help: "##该功能来自百度内容审核, 需购买相关服务, 并创建app##\n" +
diff --git a/plugin/b14/main.go b/plugin/base16384/main.go
similarity index 97%
rename from plugin/b14/main.go
rename to plugin/base16384/main.go
index bce15ad06a..ac8a9c91b5 100644
--- a/plugin/b14/main.go
+++ b/plugin/base16384/main.go
@@ -12,7 +12,7 @@ import (
)
func init() {
- en := control.Register("base16384", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "base16384加解密",
Help: "- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
diff --git a/plugin/base64gua/main.go b/plugin/base64gua/main.go
index 833bdf02cb..2e99c0e869 100644
--- a/plugin/base64gua/main.go
+++ b/plugin/base64gua/main.go
@@ -12,7 +12,7 @@ import (
)
func init() {
- en := control.Register("base64gua", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "六十四卦加解密",
Help: "- 六十四卦加密xxx\n- 六十四卦解密xxx\n- 六十四卦用yyy加密xxx\n- 六十四卦用yyy解密xxx",
diff --git a/plugin/baseamasiro/main.go b/plugin/baseamasiro/main.go
index 951f12a0a9..e160247d68 100644
--- a/plugin/baseamasiro/main.go
+++ b/plugin/baseamasiro/main.go
@@ -12,7 +12,7 @@ import (
)
func init() {
- en := control.Register("baseamasiro", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "天城文加解密",
Help: "- 天城文加密xxx\n- 天城文解密xxx\n- 天城文用yyy加密xxx\n- 天城文用yyy解密xxx",
diff --git a/plugin/bilibili/bilibili.go b/plugin/bilibili/bilibili.go
index 60250b2d8d..9451dffe1e 100644
--- a/plugin/bilibili/bilibili.go
+++ b/plugin/bilibili/bilibili.go
@@ -48,7 +48,7 @@ var (
// 查成分的
func init() {
- engine := control.Register("bilibili", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "b站查成分查弹幕",
Help: "- >vup info [xxx]\n" +
diff --git a/plugin/bilibili/bilibilipush.go b/plugin/bilibili/bilibilipush.go
index 09bb21514e..2ed6d62308 100644
--- a/plugin/bilibili/bilibilipush.go
+++ b/plugin/bilibili/bilibilipush.go
@@ -3,38 +3,56 @@ package bilibili
import (
"bytes"
+ "crypto/md5"
+ "encoding/hex"
"encoding/json"
"fmt"
"net/http"
+ "net/url"
+ "path/filepath"
+ "sort"
"strconv"
+ "strings"
"time"
- "github.com/pkg/errors"
- "github.com/tidwall/gjson"
- zero "github.com/wdvxdr1123/ZeroBot"
- "github.com/wdvxdr1123/ZeroBot/message"
-
bz "github.com/FloatTech/AnimeAPI/bilibili"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
+ "github.com/RomiChan/syncx"
+ "github.com/pkg/errors"
+ "github.com/tidwall/gjson"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+ "github.com/wdvxdr1123/ZeroBot/utils/helper"
)
const (
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
- referer = "https://www.bilibili.com/"
- infoURL = "https://api.bilibili.com/x/space/acc/info?mid=%v"
+ referer = "https://space.bilibili.com/%v"
+ infoURL = "https://api.bilibili.com/x/space/wbi/acc/info?mid=%v&token=&platform=web&web_location=1550101"
+ navURL = "https://api.bilibili.com/x/web-interface/nav"
)
// bdb bilibili推送数据库
var bdb *bilibilipushdb
var (
- lastTime = map[int64]int64{}
- liveStatus = map[int64]int{}
- upMap = map[int64]string{}
+ lastTime = map[int64]int64{}
+ liveStatus = map[int64]int{}
+ upMap = map[int64]string{}
+ mixinKeyEncTab = [...]int{
+ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
+ 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
+ 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
+ 36, 20, 34, 44, 52,
+ }
+ cache syncx.Map[string, string]
+ lastUpdateTime time.Time
+
+ replacements = [...]string{"!", "'", "(", ")", "*"}
)
func init() {
@@ -60,7 +78,7 @@ func init() {
en.OnRegex(`^添加[B|b]站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
name, err := getName(buid)
- if err != nil {
+ if err != nil || name == "" {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
@@ -175,27 +193,17 @@ func init() {
func getName(buid int64) (name string, err error) {
var ok bool
if name, ok = upMap[buid]; !ok {
- var data []byte
- data, err = web.RequestDataWithHeaders(web.NewDefaultClient(), fmt.Sprintf(infoURL, buid), "GET", func(r *http.Request) error {
- r.Header.Set("refer", referer)
- r.Header.Set("user-agent", ua)
- cookie := ""
- if cfg != nil {
- cookie, err = cfg.Load()
- if err != nil {
- return err
- }
- }
- r.Header.Set("cookie", cookie)
+ data, err := web.RequestDataWithHeaders(web.NewDefaultClient(), signURL(fmt.Sprintf(infoURL, buid)), "GET", func(r *http.Request) error {
+ r.Header.Set("User-Agent", ua)
return nil
}, nil)
if err != nil {
- return
+ return "", err
}
status := int(gjson.Get(binary.BytesToString(data), "code").Int())
if status != 0 {
err = errors.New(gjson.Get(binary.BytesToString(data), "message").String())
- return
+ return "", err
}
name = gjson.Get(binary.BytesToString(data), "data.name").String()
bdb.insertBilibiliUp(buid, name)
@@ -204,6 +212,103 @@ func getName(buid int64) (name string, err error) {
return
}
+func getMixinKey(orig string) string {
+ var str strings.Builder
+ t := 0
+ for _, v := range mixinKeyEncTab {
+ if v < len(orig) {
+ str.WriteByte(orig[v])
+ t++
+ }
+ if t > 31 {
+ break
+ }
+ }
+ return str.String()
+}
+
+func wbiSign(params map[string]string, imgKey string, subKey string) map[string]string {
+ mixinKey := getMixinKey(imgKey + subKey)
+ currTime := strconv.FormatInt(time.Now().Unix(), 10)
+ params["wts"] = currTime
+ // Sort keys
+ keys := make([]string, 0, len(params))
+ for k, v := range params {
+ keys = append(keys, k)
+ for _, old := range replacements {
+ v = strings.ReplaceAll(v, old, "")
+ }
+ params[k] = v
+ }
+ sort.Strings(keys)
+ h := md5.New()
+ for k, v := range keys {
+ h.Write([]byte(v))
+ h.Write([]byte{'='})
+ h.Write([]byte(params[v]))
+ if k < len(keys)-1 {
+ h.Write([]byte{'&'})
+ }
+ }
+ h.Write([]byte(mixinKey))
+ params["w_rid"] = hex.EncodeToString(h.Sum(make([]byte, 0, md5.Size)))
+ return params
+}
+
+func getWbiKeysCached() (string, string) {
+ if time.Since(lastUpdateTime).Minutes() > 10 {
+ imgKey, subKey := getWbiKeys()
+ cache.Store("imgKey", imgKey)
+ cache.Store("subKey", subKey)
+ lastUpdateTime = time.Now()
+ return imgKey, subKey
+ }
+ imgKeyI, _ := cache.Load("imgKey")
+ subKeyI, _ := cache.Load("subKey")
+ return imgKeyI, subKeyI
+}
+
+func getWbiKeys() (string, string) {
+ data, _ := web.RequestDataWithHeaders(web.NewDefaultClient(), navURL, "GET", func(r *http.Request) error {
+ if cfg != nil {
+ cookie, err := cfg.Load()
+ if err == nil {
+ r.Header.Set("cookie", cookie)
+ return nil
+ }
+ return err
+ }
+ return errors.New("未配置-cookie")
+ }, nil)
+ json := helper.BytesToString(data)
+ imgURL := gjson.Get(json, "data.wbi_img.img_url").String()
+ subURL := gjson.Get(json, "data.wbi_img.sub_url").String()
+ imgKey := imgURL[strings.LastIndex(imgURL, "/")+1:]
+ imgKey = strings.TrimSuffix(imgKey, filepath.Ext(imgKey))
+ subKey := subURL[strings.LastIndex(subURL, "/")+1:]
+ subKey = strings.TrimSuffix(subKey, filepath.Ext(subKey))
+ return imgKey, subKey
+}
+
+func signURL(urlStr string) string {
+ urlObj, _ := url.Parse(urlStr)
+ imgKey, subKey := getWbiKeysCached()
+ query := urlObj.Query()
+ params := map[string]string{}
+ for k, v := range query {
+ if len(v) > 0 {
+ params[k] = v[0]
+ }
+ }
+ newParams := wbiSign(params, imgKey, subKey)
+ for k, v := range newParams {
+ query.Set(k, v)
+ }
+ urlObj.RawQuery = query.Encode()
+ newURL := urlObj.String()
+ return newURL
+}
+
// subscribe 订阅
func subscribe(buid, groupid int64) (err error) {
bpMap := map[string]any{
@@ -276,7 +381,7 @@ func sendDynamic(ctx *zero.Ctx) error {
return err
}
if len(cardList) == 0 {
- return errors.Errorf("%v的历史动态数为0", buid)
+ return nil
}
t, ok := lastTime[buid]
// 第一次先记录时间,啥也不做
diff --git a/plugin/book_review/book_review.go b/plugin/bookreview/book_review.go
similarity index 97%
rename from plugin/book_review/book_review.go
rename to plugin/bookreview/book_review.go
index 47ba6752e5..7336f68cbf 100644
--- a/plugin/book_review/book_review.go
+++ b/plugin/bookreview/book_review.go
@@ -16,7 +16,7 @@ import (
)
func init() {
- engine := control.Register("bookreview", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "哀伤雪刃推书书评",
Help: "- 书评[xxx]\n- 随机书评",
diff --git a/plugin/book_review/model.go b/plugin/bookreview/model.go
similarity index 100%
rename from plugin/book_review/model.go
rename to plugin/bookreview/model.go
diff --git a/plugin/breakrepeat/breakrepeat.go b/plugin/breakrepeat/breakrepeat.go
index f5e85dabca..1332694968 100644
--- a/plugin/breakrepeat/breakrepeat.go
+++ b/plugin/breakrepeat/breakrepeat.go
@@ -16,7 +16,7 @@ const throttle = 3 // 不可超过 9
var sm syncx.Map[int64, string]
func init() {
- engine := control.Register("breakrepeat", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "打断复读",
Help: "- 打断" + strconv.Itoa(throttle) + "次以上复读\n",
diff --git a/plugin/cangtoushi/cangtoushi.go b/plugin/cangtoushi/cangtoushi.go
index b0793f882a..74e5455a42 100644
--- a/plugin/cangtoushi/cangtoushi.go
+++ b/plugin/cangtoushi/cangtoushi.go
@@ -30,7 +30,7 @@ var (
)
func init() {
- engine := control.Register("cangtoushi", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "藏头诗, 藏尾诗",
Help: "- 藏头诗[xxx]\n- 藏尾诗[xxx]",
diff --git a/plugin/chat/chat.go b/plugin/chat/chat.go
index dc159d5e17..205a9af7f7 100644
--- a/plugin/chat/chat.go
+++ b/plugin/chat/chat.go
@@ -15,7 +15,7 @@ import (
var (
poke = rate.NewManager[int64](time.Minute*5, 8) // 戳一戳
- engine = control.Register("chat", &ctrl.Options[*zero.Ctx]{
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "基础反应, 群空调",
Help: "chat\n- [BOT名字]\n- [戳一戳BOT]\n- 空调开\n- 空调关\n- 群温度\n- 设置温度[正整数]",
diff --git a/plugin/chess/chess.go b/plugin/chess/chess.go
new file mode 100644
index 0000000000..9ae65c603e
--- /dev/null
+++ b/plugin/chess/chess.go
@@ -0,0 +1,165 @@
+// Package chess 国际象棋
+package chess
+
+import (
+ "fmt"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+ "time"
+
+ ctrl "github.com/FloatTech/zbpctrl"
+ "github.com/FloatTech/zbputils/control"
+ "github.com/FloatTech/zbputils/ctxext"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/extension/single"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+const helpString = `- 参与/创建一盘游戏:「下棋」(chess)
+- 参与/创建一盘盲棋:「盲棋」(blind)
+- 投降认输:「认输」 (resign)
+- 请求、接受和棋:「和棋」 (draw)
+- 走棋:!Nxf3 中英文感叹号均可,格式请参考“代数记谱法”(Algebraic notation)
+- 中断对局:「中断」 (abort)(仅群主/管理员有效)
+- 查看等级分排行榜:「排行榜」(ranking)
+- 查看自己的等级分:「等级分」(rate)
+- 清空等级分:「清空等级分 QQ号」(.clean.rate) (仅超管有效)`
+
+var (
+ limit = ctxext.NewLimiterManager(time.Microsecond*2500, 1)
+ tempFileDir string
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
+ DisableOnDefault: false,
+ Brief: "国际象棋",
+ Help: helpString,
+ PrivateDataFolder: "chess",
+ }).ApplySingle(single.New(
+ single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
+ single.WithPostFn[int64](func(ctx *zero.Ctx) {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("有操作正在执行, 请稍后再试..."),
+ ),
+ )
+ }),
+ ))
+)
+
+func init() {
+ // 初始化临时文件夹
+ tempFileDir = path.Join(engine.DataFolder(), "temp")
+ err := os.MkdirAll(tempFileDir, 0750)
+ if err != nil {
+ panic(err)
+ }
+ // 初始化数据库
+ dbFilePath := engine.DataFolder() + "chess.db"
+ initDatabase(dbFilePath)
+ // 注册指令
+ engine.OnFullMatchGroup([]string{"下棋", "chess"}, zero.OnlyGroup).
+ SetBlock(true).
+ Limit(limit.LimitByGroup).
+ Handle(func(ctx *zero.Ctx) {
+ if ctx.Event.Sender == nil {
+ return
+ }
+ userUin := ctx.Event.UserID
+ userName := ctx.Event.Sender.NickName
+ groupCode := ctx.Event.GroupID
+ if replyMessage := game(groupCode, userUin, userName); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnFullMatchGroup([]string{"认输", "resign"}, zero.OnlyGroup).
+ SetBlock(true).
+ Limit(limit.LimitByGroup).
+ Handle(func(ctx *zero.Ctx) {
+ userUin := ctx.Event.UserID
+ groupCode := ctx.Event.GroupID
+ if replyMessage := resign(groupCode, userUin); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnFullMatchGroup([]string{"和棋", "draw"}, zero.OnlyGroup).
+ SetBlock(true).
+ Limit(limit.LimitByGroup).
+ Handle(func(ctx *zero.Ctx) {
+ userUin := ctx.Event.UserID
+ groupCode := ctx.Event.GroupID
+ if replyMessage := draw(groupCode, userUin); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnFullMatchGroup([]string{"中断", "abort"}, zero.OnlyGroup, zero.AdminPermission).
+ SetBlock(true).
+ Limit(limit.LimitByGroup).
+ Handle(func(ctx *zero.Ctx) {
+ groupCode := ctx.Event.GroupID
+ if replyMessage := abort(groupCode); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnFullMatchGroup([]string{"盲棋", "blind"}, zero.OnlyGroup).
+ SetBlock(true).
+ Limit(limit.LimitByGroup).
+ Handle(func(ctx *zero.Ctx) {
+ if ctx.Event.Sender == nil {
+ return
+ }
+ userUin := ctx.Event.UserID
+ userName := ctx.Event.Sender.NickName
+ groupCode := ctx.Event.GroupID
+ if replyMessage := blindfold(groupCode, userUin, userName); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnRegex("^[!|!]([0-8]|[R|N|B|Q|K|O|a-h|x]|[-|=|+])+$", zero.OnlyGroup).
+ SetBlock(true).
+ Limit(limit.LimitByGroup).
+ Handle(func(ctx *zero.Ctx) {
+ userUin := ctx.Event.UserID
+ groupCode := ctx.Event.GroupID
+ userMsgStr := ctx.State["regex_matched"].([]string)[0]
+ moveStr := strings.TrimPrefix(strings.TrimPrefix(userMsgStr, "!"), "!")
+ if replyMessage := play(userUin, groupCode, moveStr); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnFullMatchGroup([]string{"排行榜", "ranking"}).
+ SetBlock(true).
+ Limit(limit.LimitByUser).
+ Handle(func(ctx *zero.Ctx) {
+ if replyMessage := ranking(); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnFullMatchGroup([]string{"等级分", "rate"}).
+ SetBlock(true).
+ Limit(limit.LimitByUser).
+ Handle(func(ctx *zero.Ctx) {
+ if ctx.Event.Sender == nil {
+ return
+ }
+ userUin := ctx.Event.UserID
+ userName := ctx.Event.Sender.NickName
+ if replyMessage := rate(userUin, userName); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+ engine.OnPrefixGroup([]string{"清空等级分", ".clean.rate"}, zero.SuperUserPermission).
+ SetBlock(true).
+ Limit(limit.LimitByUser).
+ Handle(func(ctx *zero.Ctx) {
+ args := ctx.State["args"].(string)
+ playerUin, err := strconv.ParseInt(strings.TrimSpace(args), 10, 64)
+ if err != nil || playerUin <= 0 {
+ ctx.Send(fmt.Sprintf("解析失败「%s」不是正确的 QQ 号。", args))
+ return
+ }
+ if replyMessage := cleanUserRate(playerUin); len(replyMessage) >= 1 {
+ ctx.Send(replyMessage)
+ }
+ })
+}
diff --git a/plugin/chess/core.go b/plugin/chess/core.go
new file mode 100644
index 0000000000..a2940d6a55
--- /dev/null
+++ b/plugin/chess/core.go
@@ -0,0 +1,717 @@
+package chess
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "image/color"
+ "io"
+ "os"
+ "os/exec"
+ "path"
+ "strings"
+ "time"
+
+ "github.com/FloatTech/floatbox/binary"
+ "github.com/FloatTech/floatbox/file"
+ "github.com/FloatTech/gg"
+ "github.com/FloatTech/zbputils/control"
+ "github.com/FloatTech/zbputils/img/text"
+ "github.com/RomiChan/syncx"
+ "github.com/jinzhu/gorm"
+ "github.com/notnil/chess"
+ "github.com/notnil/chess/image"
+ log "github.com/sirupsen/logrus"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+const eloDefault = 500
+
+var chessRoomMap syncx.Map[int64, *chessRoom]
+
+type chessRoom struct {
+ chessGame *chess.Game
+ whitePlayer int64
+ whiteName string
+ blackPlayer int64
+ blackName string
+ drawPlayer int64
+ lastMoveTime int64
+ isBlindfold bool
+ whiteErr bool // 违例记录(盲棋用)
+ blackErr bool
+}
+
+// game 下棋
+func game(groupCode, senderUin int64, senderName string) message.Message {
+ return createGame(false, groupCode, senderUin, senderName)
+}
+
+// blindfold 盲棋
+func blindfold(groupCode, senderUin int64, senderName string) message.Message {
+ return createGame(true, groupCode, senderUin, senderName)
+}
+
+// abort 中断对局
+func abort(groupCode int64) message.Message {
+ if room, ok := chessRoomMap.Load(groupCode); ok {
+ return abortGame(*room, groupCode, "对局已被管理员中断,游戏结束。")
+ }
+ return simpleText("对局不存在,发送「下棋」或「chess」可创建对局。")
+}
+
+// draw 和棋
+func draw(groupCode, senderUin int64) message.Message {
+ // 检查对局是否存在
+ room, ok := chessRoomMap.Load(groupCode)
+ if !ok {
+ return simpleText("对局不存在,发送「下棋」或「chess」可创建对局。")
+ }
+ // 检查消息发送者是否为对局中的玩家
+ if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
+ return textWithAt(senderUin, "您不是对局中的玩家,无法请求和棋。")
+ }
+ // 处理和棋逻辑
+ room.lastMoveTime = time.Now().Unix()
+ if room.drawPlayer == 0 {
+ room.drawPlayer = senderUin
+ chessRoomMap.Store(groupCode, room)
+ return textWithAt(senderUin, "请求和棋,发送「和棋」或「draw」接受和棋。走棋视为拒绝和棋。")
+ }
+ if room.drawPlayer == senderUin {
+ return textWithAt(senderUin, "已发起和棋请求,请勿重复发送。")
+ }
+ err := room.chessGame.Draw(chess.DrawOffer)
+ if err != nil {
+ log.Debugln("[chess]", "Fail to draw a game.", err)
+ return textWithAt(senderUin, fmt.Sprintln("程序发生了错误,和棋失败,请反馈开发者修复 bug。\nERROR:", err))
+ }
+ chessString := getChessString(*room)
+ eloString := ""
+ if len(room.chessGame.Moves()) > 4 {
+ // 若走子次数超过 4 认为是有效对局,存入数据库
+ dbService := newDBService()
+ if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
+ log.Debugln("[chess]", "Fail to create PGN.", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ whiteScore, blackScore := 0.5, 0.5
+ elo, err := getELOString(*room, whiteScore, blackScore)
+ if err != nil {
+ log.Debugln("[chess]", "Fail to get eloString.", eloString, err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ eloString = elo
+ }
+ replyMsg := textWithAt(senderUin, "接受和棋,游戏结束。\n"+eloString+chessString)
+ if inkscapeExists() {
+ if err := cleanTempFiles(groupCode); err != nil {
+ log.Debugln("[chess]", "Fail to clean temp files", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ }
+ chessRoomMap.Delete(groupCode)
+ return replyMsg
+}
+
+// resign 认输
+func resign(groupCode, senderUin int64) message.Message {
+ // 检查对局是否存在
+ room, ok := chessRoomMap.Load(groupCode)
+ if !ok {
+ return simpleText("对局不存在,发送「下棋」或「chess」可创建对局。")
+ }
+ // 检查是否是当前游戏玩家
+ if senderUin != room.whitePlayer && senderUin != room.blackPlayer {
+ return textWithAt(senderUin, "不是对局中的玩家,无法认输。")
+ }
+ // 如果对局未建立,中断对局
+ if room.whitePlayer == 0 || room.blackPlayer == 0 {
+ chessRoomMap.Delete(groupCode)
+ return simpleText("对局已释放。")
+ }
+ // 计算认输方
+ var resignColor chess.Color
+ if senderUin == room.whitePlayer {
+ resignColor = chess.White
+ } else {
+ resignColor = chess.Black
+ }
+ if isAprilFoolsDay() {
+ if resignColor == chess.White {
+ resignColor = chess.Black
+ } else {
+ resignColor = chess.White
+ }
+ }
+ room.chessGame.Resign(resignColor)
+ chessString := getChessString(*room)
+ eloString := ""
+ if len(room.chessGame.Moves()) > 4 {
+ // 若走子次数超过 4 认为是有效对局,存入数据库
+ dbService := newDBService()
+ if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
+ log.Debugln("[chess]", "Fail to create PGN.", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ whiteScore, blackScore := 1.0, 1.0
+ if resignColor == chess.White {
+ whiteScore = 0.0
+ } else {
+ blackScore = 0.0
+ }
+ elo, err := getELOString(*room, whiteScore, blackScore)
+ if err != nil {
+ log.Debugln("[chess]", "Fail to get eloString.", eloString, err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ eloString = elo
+ }
+ replyMsg := textWithAt(senderUin, "认输,游戏结束。\n"+eloString+chessString)
+ if isAprilFoolsDay() {
+ replyMsg = textWithAt(senderUin, "对手认输,游戏结束,你胜利了。\n"+eloString+chessString)
+ }
+ // 删除临时文件
+ if inkscapeExists() {
+ if err := cleanTempFiles(groupCode); err != nil {
+ log.Debugln("[chess]", "Fail to clean temp files", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ }
+ chessRoomMap.Delete(groupCode)
+ return replyMsg
+}
+
+// play 走棋
+func play(senderUin int64, groupCode int64, moveStr string) message.Message {
+ // 检查对局是否存在
+ room, ok := chessRoomMap.Load(groupCode)
+ if !ok {
+ return nil
+ }
+ // 不是对局中的玩家,忽略消息
+ if (senderUin != room.whitePlayer) && (senderUin != room.blackPlayer) && !isAprilFoolsDay() {
+ return nil
+ }
+ // 对局未建立
+ if (room.whitePlayer == 0) || (room.blackPlayer == 0) {
+ return textWithAt(senderUin, "请等候其他玩家加入游戏。")
+ }
+ // 需要对手走棋
+ if ((senderUin == room.whitePlayer) && (room.chessGame.Position().Turn() != chess.White)) || ((senderUin == room.blackPlayer) && (room.chessGame.Position().Turn() != chess.Black)) {
+ return textWithAt(senderUin, "请等待对手走棋。")
+ }
+ room.lastMoveTime = time.Now().Unix()
+ // 走棋
+ if err := room.chessGame.MoveStr(moveStr); err != nil {
+ // 指令错误时检查
+ if !room.isBlindfold {
+ // 未开启盲棋,提示指令错误
+ return simpleText(fmt.Sprintf("移动「%s」违规,请检查,格式请参考「代数记谱法」(Algebraic notation)。", moveStr))
+ }
+ // 开启盲棋,判断违例情况
+ var currentPlayerColor chess.Color
+ if senderUin == room.whitePlayer {
+ currentPlayerColor = chess.White
+ } else {
+ currentPlayerColor = chess.Black
+ }
+ // 第一次违例,提示
+ _flag := false
+ if (currentPlayerColor == chess.White) && !room.whiteErr {
+ room.whiteErr = true
+ chessRoomMap.Store(groupCode, room)
+ _flag = true
+ }
+ if (currentPlayerColor == chess.Black) && !room.blackErr {
+ room.blackErr = true
+ chessRoomMap.Store(groupCode, room)
+ _flag = true
+ }
+ if _flag {
+ return simpleText(fmt.Sprintf("移动「%s」违例,再次违例会立即判负。", moveStr))
+ }
+ // 出现多次违例,判负
+ room.chessGame.Resign(currentPlayerColor)
+ chessString := getChessString(*room)
+ replyMsg := textWithAt(senderUin, "违例两次,游戏结束。\n"+chessString)
+ // 删除临时文件
+ if inkscapeExists() {
+ if err := cleanTempFiles(groupCode); err != nil {
+ log.Debugln("[chess]", "Fail to clean temp files", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ }
+ chessRoomMap.Delete(groupCode)
+ return replyMsg
+ }
+ // 走子之后,视为拒绝和棋
+ if room.drawPlayer != 0 {
+ room.drawPlayer = 0
+ chessRoomMap.Store(groupCode, room)
+ }
+ // 生成棋盘图片
+ var boardImgEle message.MessageSegment
+ if !room.isBlindfold {
+ boardMsg, ok, errMsg := getBoardElement(groupCode)
+ boardImgEle = boardMsg
+ if !ok {
+ return errorText(errMsg)
+ }
+ }
+ // 检查游戏是否结束
+ if room.chessGame.Method() != chess.NoMethod {
+ whiteScore, blackScore := 0.5, 0.5
+ var msgBuilder strings.Builder
+ msgBuilder.WriteString("游戏结束,")
+ switch room.chessGame.Method() {
+ case chess.FivefoldRepetition:
+ msgBuilder.WriteString("和棋,因为五次重复走子。\n")
+ case chess.SeventyFiveMoveRule:
+ msgBuilder.WriteString("和棋,因为七十五步规则。\n")
+ case chess.InsufficientMaterial:
+ msgBuilder.WriteString("和棋,因为不可能将死。\n")
+ case chess.Stalemate:
+ msgBuilder.WriteString("和棋,因为逼和(无子可动和棋)。\n")
+ case chess.Checkmate:
+ var winner string
+ if room.chessGame.Position().Turn() == chess.White {
+ whiteScore = 0.0
+ blackScore = 1.0
+ winner = "黑方"
+ } else {
+ whiteScore = 1.0
+ blackScore = 0.0
+ winner = "白方"
+ }
+ msgBuilder.WriteString(winner)
+ msgBuilder.WriteString("胜利,因为将杀。\n")
+ case chess.NoMethod:
+ case chess.Resignation:
+ case chess.DrawOffer:
+ case chess.ThreefoldRepetition:
+ case chess.FiftyMoveRule:
+ default:
+ }
+ chessString := getChessString(*room)
+ eloString := ""
+ if len(room.chessGame.Moves()) > 4 {
+ // 若走子次数超过 4 认为是有效对局,存入数据库
+ dbService := newDBService()
+ if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
+ log.Debugln("[chess]", "Fail to create PGN.", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ // 仅有效对局才会计算等级分
+ elo, err := getELOString(*room, whiteScore, blackScore)
+ if err != nil {
+ log.Debugln("[chess]", "Fail to get eloString.", eloString, err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ eloString = elo
+ }
+ msgBuilder.WriteString(eloString)
+ msgBuilder.WriteString(chessString)
+ replyMsg := simpleText(msgBuilder.String())
+ if !room.isBlindfold {
+ replyMsg = append(replyMsg, boardImgEle)
+ }
+ if inkscapeExists() {
+ if err := cleanTempFiles(groupCode); err != nil {
+ log.Debugln("[chess]", "Fail to clean temp files", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ }
+ chessRoomMap.Delete(groupCode)
+ return replyMsg
+ }
+ // 提示玩家继续游戏
+ var currentPlayer int64
+ if room.chessGame.Position().Turn() == chess.White {
+ currentPlayer = room.whitePlayer
+ } else {
+ currentPlayer = room.blackPlayer
+ }
+ return append(textWithAt(currentPlayer, "对手已走子,游戏继续。"), boardImgEle)
+}
+
+// ranking 排行榜
+func ranking() message.Message {
+ ranking, err := getRankingString()
+ if err != nil {
+ log.Debugln("[chess]", "Fail to get player ranking.", err)
+ return simpleText(fmt.Sprintln("服务器错误,无法获取排行榜信息。请联系开发者修 bug。", err))
+ }
+ return simpleText(ranking)
+}
+
+// rate 获取等级分
+func rate(senderUin int64, senderName string) message.Message {
+ dbService := newDBService()
+ rate, err := dbService.getELORateByUin(senderUin)
+ if err == gorm.ErrRecordNotFound {
+ return simpleText("没有查找到等级分信息。请至少进行一局对局。")
+ }
+ if err != nil {
+ log.Debugln("[chess]", "Fail to get player rank.", err)
+ return simpleText(fmt.Sprintln("服务器错误,无法获取等级分信息。请联系开发者修 bug。", err))
+ }
+ return simpleText(fmt.Sprintf("玩家「%s」目前的等级分:%d", senderName, rate))
+}
+
+// cleanUserRate 清空用户等级分
+func cleanUserRate(senderUin int64) message.Message {
+ dbService := newDBService()
+ err := dbService.cleanELOByUin(senderUin)
+ if err == gorm.ErrRecordNotFound {
+ return simpleText("没有查找到等级分信息。请检查用户 uid 是否正确。")
+ }
+ if err != nil {
+ log.Debugln("[chess]", "Fail to clean player rank.", err)
+ return simpleText(fmt.Sprintln("服务器错误,无法清空等级分。请联系开发者修 bug。", err))
+ }
+ return simpleText(fmt.Sprintf("已清空用户「%d」的等级分。", senderUin))
+}
+
+// createGame 创建游戏
+func createGame(isBlindfold bool, groupCode int64, senderUin int64, senderName string) message.Message {
+ room, ok := chessRoomMap.Load(groupCode)
+ if !ok {
+ chessRoomMap.Store(groupCode, &chessRoom{
+ chessGame: chess.NewGame(),
+ whitePlayer: senderUin,
+ whiteName: senderName,
+ blackPlayer: 0,
+ blackName: "",
+ drawPlayer: 0,
+ lastMoveTime: time.Now().Unix(),
+ isBlindfold: isBlindfold,
+ whiteErr: false,
+ blackErr: false,
+ })
+ if isBlindfold {
+ return simpleText("已创建新的盲棋对局,发送「盲棋」或「blind」可加入对局。")
+ }
+ return simpleText("已创建新的对局,发送「下棋」或「chess」可加入对局。")
+ }
+ if room.blackPlayer != 0 {
+ // 检测对局是否已存在超过 6 小时
+ if (time.Now().Unix() - room.lastMoveTime) > 21600 {
+ autoAbortMsg := abortGame(*room, groupCode, "对局已存在超过 6 小时,游戏结束。")
+ autoAbortMsg = append(autoAbortMsg, message.Text("\n\n已有对局已被中断,如需创建新对局请重新发送指令。"))
+ autoAbortMsg = append(autoAbortMsg, message.At(senderUin))
+ return autoAbortMsg
+ }
+ // 对局在进行
+ msg := textWithAt(senderUin, "对局已在进行中,无法创建或加入对局,当前对局玩家为:")
+ if room.whitePlayer != 0 {
+ msg = append(msg, message.At(room.whitePlayer))
+ }
+ if room.blackPlayer != 0 {
+ msg = append(msg, message.At(room.blackPlayer))
+ }
+ msg = append(msg, message.Text(",群主或管理员发送「中断」或「abort」可中断对局(自动判和)。"))
+ return msg
+ }
+ if senderUin == room.whitePlayer {
+ return textWithAt(senderUin, "请等候其他玩家加入游戏。")
+ }
+ if room.isBlindfold && !isBlindfold {
+ return simpleText("已创建盲棋对局,请加入或等待盲棋对局结束之后创建普通对局。")
+ }
+ if !room.isBlindfold && isBlindfold {
+ return simpleText("已创建普通对局,请加入或等待普通对局结束之后创建盲棋对局。")
+ }
+ room.blackPlayer = senderUin
+ room.blackName = senderName
+ chessRoomMap.Store(groupCode, room)
+ var boardImgEle message.MessageSegment
+ if !room.isBlindfold {
+ boardMsg, ok, errMsg := getBoardElement(groupCode)
+ if !ok {
+ return errorText(errMsg)
+ }
+ boardImgEle = boardMsg
+ }
+ if isBlindfold {
+ return append(simpleText("黑棋已加入对局,请白方下棋。"), message.At(room.whitePlayer))
+ }
+ return append(simpleText("黑棋已加入对局,请白方下棋。"), message.At(room.whitePlayer), boardImgEle)
+}
+
+// abortGame 中断游戏
+func abortGame(room chessRoom, groupCode int64, hint string) message.Message {
+ err := room.chessGame.Draw(chess.DrawOffer)
+ if err != nil {
+ log.Debugln("[chess]", "Fail to draw a game.", err)
+ return simpleText(fmt.Sprintln("程序发生了错误,和棋失败,请反馈开发者修复 bug。", err))
+ }
+ chessString := getChessString(room)
+ if len(room.chessGame.Moves()) > 4 {
+ dbService := newDBService()
+ if err := dbService.createPGN(chessString, room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName); err != nil {
+ log.Debugln("[chess]", "Fail to create PGN.", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ }
+ if inkscapeExists() {
+ if err := cleanTempFiles(groupCode); err != nil {
+ log.Debugln("[chess]", "Fail to clean temp files", err)
+ return message.Message{message.Text("ERROR: ", err)}
+ }
+ }
+ chessRoomMap.Delete(groupCode)
+ msg := simpleText(hint)
+ if room.whitePlayer != 0 {
+ msg = append(msg, message.At(room.whitePlayer))
+ }
+ if room.blackPlayer != 0 {
+ msg = append(msg, message.At(room.blackPlayer))
+ }
+ msg = append(msg, message.Text("\n\n"+chessString))
+ return msg
+}
+
+// getBoardElement 获取棋盘图片的消息内容
+func getBoardElement(groupCode int64) (message.MessageSegment, bool, string) {
+ room, ok := chessRoomMap.Load(groupCode)
+ if !ok {
+ log.Debugln(fmt.Sprintf("No room for groupCode %d.", groupCode))
+ return message.MessageSegment{}, false, "对局不存在"
+ }
+ // 未安装 inkscape 直接返回对局字符串
+ // TODO: 使用原生 go 库渲染 svg
+ if !inkscapeExists() {
+ boardString := room.chessGame.Position().Board().Draw()
+ boardImageB64, err := generateCharBoardImage(boardString)
+ if err != nil {
+ return message.MessageSegment{}, false, "生成棋盘图片时发生错误"
+ }
+ replyMsg := message.Image("base64://" + boardImageB64)
+ return replyMsg, true, ""
+ }
+ // 获取高亮方块
+ highlightSquare := make([]chess.Square, 0, 2)
+ moves := room.chessGame.Moves()
+ if len(moves) != 0 {
+ lastMove := moves[len(moves)-1]
+ highlightSquare = append(highlightSquare, lastMove.S1())
+ highlightSquare = append(highlightSquare, lastMove.S2())
+ }
+ // 生成棋盘 svg 文件
+ svgFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.svg", groupCode))
+ fenStr := room.chessGame.FEN()
+ gameTurn := room.chessGame.Position().Turn()
+ if err := generateBoardSVG(svgFilePath, fenStr, gameTurn, highlightSquare...); err != nil {
+ log.Debugln("[chess]", "Unable to generate svg file.", err)
+ return message.MessageSegment{}, false, "无法生成 svg 图片,请检查后台日志。"
+ }
+ // 调用 inkscape 将 svg 图片转化为 png 图片
+ pngFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.png", groupCode))
+ if err := exec.Command("inkscape", "-w", "720", "-h", "720", svgFilePath, "-o", pngFilePath).Run(); err != nil {
+ log.Debugln("[chess]", "Unable to convert to png.", err)
+ return message.MessageSegment{}, false, "无法生成 png 图片,请检查 inkscape 安装情况及其依赖 libfuse。"
+ }
+ // 尝试读取 png 图片
+ imgData, err := os.ReadFile(pngFilePath)
+ if err != nil {
+ log.Debugln("[chess]", fmt.Sprintf("Unable to read image file in %s.", pngFilePath), err)
+ return message.MessageSegment{}, false, "无法读取 png 图片"
+ }
+ imgMsg := message.Image("base64://" + base64.StdEncoding.EncodeToString(imgData))
+ return imgMsg, true, ""
+}
+
+// getELOString 获得玩家等级分的文本内容
+func getELOString(room chessRoom, whiteScore, blackScore float64) (string, error) {
+ if room.whitePlayer == 0 || room.blackPlayer == 0 {
+ return "", nil
+ }
+ var msgBuilder strings.Builder
+ msgBuilder.WriteString("玩家等级分:\n")
+ dbService := newDBService()
+ if err := updateELORate(room.whitePlayer, room.blackPlayer, room.whiteName, room.blackName, whiteScore, blackScore, dbService); err != nil {
+ msgBuilder.WriteString("发生错误,无法更新等级分。")
+ msgBuilder.WriteString(err.Error())
+ return msgBuilder.String(), err
+ }
+ whiteRate, blackRate, err := getELORate(room.whitePlayer, room.blackPlayer, dbService)
+ if err != nil {
+ msgBuilder.WriteString("发生错误,无法获取等级分。")
+ msgBuilder.WriteString(err.Error())
+ return msgBuilder.String(), err
+ }
+ msgBuilder.WriteString(fmt.Sprintf("%s:%d\n%s:%d\n\n", room.whiteName, whiteRate, room.blackName, blackRate))
+ return msgBuilder.String(), nil
+}
+
+// getRankingString 获取等级分排行榜的文本内容
+func getRankingString() (string, error) {
+ dbService := newDBService()
+ eloList, err := dbService.getHighestRateList()
+ if err != nil {
+ return "", err
+ }
+ var msgBuilder strings.Builder
+ msgBuilder.WriteString("当前等级分排行榜:\n\n")
+ for _, elo := range eloList {
+ msgBuilder.WriteString(fmt.Sprintf("%s: %d\n", elo.Name, elo.Rate))
+ }
+ return msgBuilder.String(), nil
+}
+
+func simpleText(msg string) message.Message {
+ return []message.MessageSegment{message.Text(msg)}
+}
+
+func textWithAt(target int64, msg string) message.Message {
+ if target == 0 {
+ return simpleText("@全体成员 " + msg)
+ }
+ return []message.MessageSegment{message.At(target), message.Text(msg)}
+}
+
+func errorText(errMsg string) message.Message {
+ return simpleText("发生错误,请联系开发者修 bug。\n错误信息:" + errMsg)
+}
+
+// updateELORate 更新 elo 等级分
+// 当数据库中没有玩家的等级分信息时,自动新建一条记录
+func updateELORate(whiteUin, blackUin int64, whiteName, blackName string, whiteScore, blackScore float64, dbService *chessDBService) error {
+ whiteRate, err := dbService.getELORateByUin(whiteUin)
+ if err != nil {
+ if err != gorm.ErrRecordNotFound {
+ return err
+ }
+ // create white elo
+ if err := dbService.createELO(whiteUin, whiteName, eloDefault); err != nil {
+ return err
+ }
+ whiteRate = eloDefault
+ }
+ blackRate, err := dbService.getELORateByUin(blackUin)
+ if err != nil {
+ if err != gorm.ErrRecordNotFound {
+ return err
+ }
+ // create black elo
+ if err := dbService.createELO(blackUin, blackName, eloDefault); err != nil {
+ return err
+ }
+ blackRate = eloDefault
+ }
+ whiteRate, blackRate = calculateNewRate(whiteRate, blackRate, whiteScore, blackScore)
+ // 更新白棋玩家的 ELO 等级分
+ if err := dbService.updateELOByUin(whiteUin, whiteName, whiteRate); err != nil {
+ return err
+ }
+ // 更新黑棋玩家的 ELO 等级分
+ return dbService.updateELOByUin(blackUin, blackName, blackRate)
+}
+
+// cleanTempFiles 清理临时文件
+func cleanTempFiles(groupCode int64) error {
+ svgFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.svg", groupCode))
+ if err := os.Remove(svgFilePath); err != nil {
+ return err
+ }
+ pngFilePath := path.Join(tempFileDir, fmt.Sprintf("%d.png", groupCode))
+ return os.Remove(pngFilePath)
+}
+
+// generateCharBoardImage 生成文字版的棋盘
+func generateCharBoardImage(boardString string) (string, error) {
+ boardString = strings.Trim(boardString, "\n")
+ const FontSize = 72
+ h := FontSize*8 + 36
+ w := FontSize*9 + 24
+ dc := gg.NewContext(h, w)
+ dc.SetRGB(1, 1, 1)
+ dc.Clear()
+ dc.SetRGB(0, 0, 0)
+ fontdata, err := file.GetLazyData(text.GNUUnifontFontFile, control.Md5File, true)
+ if err != nil {
+ // TODO: err solve
+ panic(err)
+ }
+ if err := dc.ParseFontFace(fontdata, FontSize); err != nil {
+ return "", err
+ }
+ lines := strings.Split(boardString, "\n")
+ if len(lines) != 9 {
+ lines = make([]string, 9)
+ lines[0] = "ERROR [500]"
+ lines[1] = "程序内部错误"
+ lines[2] = "棋盘字符串不合法"
+ lines[3] = "请反馈开发者修复"
+ }
+ for i := 0; i < 9; i++ {
+ dc.DrawString(lines[i], 18, float64(FontSize*(i+1)))
+ }
+ imgBuffer := bytes.NewBuffer([]byte{})
+ if err := dc.EncodePNG(imgBuffer); err != nil {
+ return "", err
+ }
+ imgData, err := io.ReadAll(imgBuffer)
+ if err != nil {
+ return "", err
+ }
+ imgB64 := base64.StdEncoding.EncodeToString(imgData)
+ return imgB64, nil
+}
+
+// generateBoardSVG 生成棋盘 SVG 图片
+func generateBoardSVG(svgFilePath, fenStr string, gameTurn chess.Color, sqs ...chess.Square) error {
+ os.Remove(svgFilePath)
+ f, err := os.Create(svgFilePath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ pos := &chess.Position{}
+ if err := pos.UnmarshalText(binary.StringToBytes(fenStr)); err != nil {
+ return err
+ }
+ yellow := color.RGBA{255, 255, 0, 1}
+ mark := image.MarkSquares(yellow, sqs...)
+ board := pos.Board()
+ fromBlack := image.Perspective(gameTurn)
+ return image.SVG(f, board, fromBlack, mark)
+}
+
+// getChessString 获取 PGN 字符串
+func getChessString(room chessRoom) string {
+ game := room.chessGame
+ dataString := fmt.Sprintf("[Date \"%s\"]\n", time.Now().Format("2006-01-02"))
+ whiteString := fmt.Sprintf("[White \"%s\"]\n", room.whiteName)
+ blackString := fmt.Sprintf("[Black \"%s\"]\n", room.blackName)
+ chessString := game.String()
+
+ return dataString + whiteString + blackString + chessString
+}
+
+// getELORate 获取玩家的 ELO 等级分
+func getELORate(whiteUin, blackUin int64, dbService *chessDBService) (whiteRate int, blackRate int, err error) {
+ whiteRate, err = dbService.getELORateByUin(whiteUin)
+ if err != nil {
+ return
+ }
+ blackRate, err = dbService.getELORateByUin(blackUin)
+ if err != nil {
+ return
+ }
+ return
+}
+
+// inkscapeExists 判断 inkscape 是否存在
+func inkscapeExists() bool {
+ _, err := exec.LookPath("inkscape")
+ return err == nil
+}
+
+// isAprilFoolsDay 判断当前时间是否为愚人节期间
+func isAprilFoolsDay() bool {
+ now := time.Now()
+ return now.Month() == 4 && now.Day() == 1
+}
diff --git a/plugin/chess/db.go b/plugin/chess/db.go
new file mode 100644
index 0000000000..3a61ca22f7
--- /dev/null
+++ b/plugin/chess/db.go
@@ -0,0 +1,100 @@
+package chess
+
+import (
+ "os"
+
+ "github.com/jinzhu/gorm"
+)
+
+var chessDB *gorm.DB
+
+// elo user elo info
+type elo struct {
+ gorm.Model
+ Uin int64 `gorm:"unique_index"`
+ Name string
+ Rate int
+}
+
+// pgn chess pgn info
+type pgn struct {
+ gorm.Model
+ Data string
+ WhiteUin int64
+ BlackUin int64
+ WhiteName string
+ BlackName string
+}
+
+// chessDBService 数据库服务
+type chessDBService struct {
+ db *gorm.DB
+}
+
+// newDBService 创建数据库服务
+func newDBService() *chessDBService {
+ return &chessDBService{
+ db: chessDB,
+ }
+}
+
+// initDatabase init database
+func initDatabase(dbPath string) {
+ var err error
+ if _, err = os.Stat(dbPath); err != nil || os.IsNotExist(err) {
+ f, err := os.Create(dbPath)
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ }
+ chessDB, err = gorm.Open("sqlite3", dbPath)
+ if err != nil {
+ panic(err)
+ }
+ chessDB.AutoMigrate(&elo{}, &pgn{})
+}
+
+// createELO 创建 ELO
+func (s *chessDBService) createELO(uin int64, name string, rate int) error {
+ return s.db.Create(&elo{
+ Uin: uin,
+ Name: name,
+ Rate: rate,
+ }).Error
+}
+
+// getELORateByUin 获取 ELO 等级分
+func (s *chessDBService) getELORateByUin(uin int64) (int, error) {
+ var elo elo
+ err := s.db.Select("rate").Where("uin = ?", uin).First(&elo).Error
+ return elo.Rate, err
+}
+
+// getHighestRateList 获取最高的等级分列表
+func (s *chessDBService) getHighestRateList() ([]elo, error) {
+ var eloList []elo
+ err := s.db.Order("rate desc").Limit(10).Find(&eloList).Error
+ return eloList, err
+}
+
+// updateELOByUin 更新 ELO 等级分
+func (s *chessDBService) updateELOByUin(uin int64, name string, rate int) error {
+ return s.db.Model(&elo{}).Where("uin = ?", uin).Update("name", name).Update("rate", rate).Error
+}
+
+// cleanELOByUin 清空用户 ELO 等级分
+func (s *chessDBService) cleanELOByUin(uin int64) error {
+ return s.db.Model(&elo{}).Where("uin = ?", uin).Update("rate", 100).Error
+}
+
+// createPGN 创建 PGN
+func (s *chessDBService) createPGN(data string, whiteUin int64, blackUin int64, whiteName string, blackName string) error {
+ return s.db.Create(&pgn{
+ Data: data,
+ WhiteUin: whiteUin,
+ BlackUin: blackUin,
+ WhiteName: whiteName,
+ BlackName: blackName,
+ }).Error
+}
diff --git a/plugin/chess/elo.go b/plugin/chess/elo.go
new file mode 100644
index 0000000000..9f000164fe
--- /dev/null
+++ b/plugin/chess/elo.go
@@ -0,0 +1,37 @@
+package chess
+
+import (
+ "math"
+)
+
+// calculateNewRate calculate new rate of the player
+func calculateNewRate(whiteRate, blackRate int, whiteScore, blackScore float64) (int, int) {
+ k := getKFactor(whiteRate, blackRate)
+ exceptionWhite := calculateException(whiteRate, blackRate)
+ exceptionBlack := calculateException(blackRate, whiteRate)
+ whiteRate = calculateRate(whiteRate, whiteScore, exceptionWhite, k)
+ blackRate = calculateRate(blackRate, blackScore, exceptionBlack, k)
+ return whiteRate, blackRate
+}
+
+func calculateException(rate int, opponentRate int) float64 {
+ return 1.0 / (1.0 + math.Pow(10.0, float64(opponentRate-rate)/400.0))
+}
+
+func calculateRate(rate int, score float64, exception float64, k int) int {
+ newRate := int(math.Round(float64(rate) + float64(k)*(score-exception)))
+ if newRate < 1 {
+ newRate = 1
+ }
+ return newRate
+}
+
+func getKFactor(rateA, rateB int) int {
+ if rateA > 2400 && rateB > 2400 {
+ return 16
+ }
+ if rateA > 2100 && rateB > 2100 {
+ return 24
+ }
+ return 32
+}
diff --git a/plugin/chess/elo_test.go b/plugin/chess/elo_test.go
new file mode 100644
index 0000000000..7e483d62f7
--- /dev/null
+++ b/plugin/chess/elo_test.go
@@ -0,0 +1,80 @@
+package chess
+
+import (
+ "math"
+ "testing"
+)
+
+func TestCalculateNewRate(t *testing.T) {
+ type args struct {
+ whiteRate int
+ blackRate int
+ whiteScore float64
+ blackScore float64
+ }
+ tests := []struct {
+ name string
+ args args
+ want int
+ want1 int
+ }{
+ {
+ name: "test1",
+ args: args{
+ whiteRate: 1613,
+ blackRate: 1573,
+ whiteScore: 0.5,
+ blackScore: 0.5,
+ },
+ want: 1611,
+ want1: 1575,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, got1 := calculateNewRate(tt.args.whiteRate, tt.args.blackRate, tt.args.whiteScore, tt.args.blackScore)
+ if got != tt.want {
+ t.Errorf("CalculateNewRate() got = %v, want %v", got, tt.want)
+ }
+ if got1 != tt.want1 {
+ t.Errorf("CalculateNewRate() got1 = %v, want %v", got1, tt.want1)
+ }
+ })
+ }
+}
+
+func Test_calculateException(t *testing.T) {
+ type args struct {
+ rate int
+ opponentRate int
+ }
+ tests := []struct {
+ name string
+ args args
+ want float64
+ }{
+ {
+ name: "test1",
+ args: args{
+ rate: 1613,
+ opponentRate: 1573,
+ },
+ want: 0.5573116,
+ },
+ {
+ name: "test2",
+ args: args{
+ rate: 1613,
+ opponentRate: 1613,
+ },
+ want: 0.5,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := calculateException(tt.args.rate, tt.args.opponentRate); math.Abs(got-tt.want) > 0.0001 {
+ t.Errorf("calculateException() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/plugin/choose/choose.go b/plugin/choose/choose.go
index 36fe4a918e..7ec2a7141e 100644
--- a/plugin/choose/choose.go
+++ b/plugin/choose/choose.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- engine := control.Register("choose", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "选择困难症帮手",
Help: "例: 选择可口可乐还是百事可乐\n" +
diff --git a/plugin/chouxianghua/chouxianghua.go b/plugin/chouxianghua/chouxianghua.go
index e4d7ae806a..c7e0710542 100644
--- a/plugin/chouxianghua/chouxianghua.go
+++ b/plugin/chouxianghua/chouxianghua.go
@@ -14,7 +14,7 @@ import (
)
func init() {
- en := control.Register("chouxianghua", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "翻译为抽象话",
Help: "- 抽象翻译xxx",
diff --git a/plugin/chrev/init.go b/plugin/chrev/init.go
index 605788a812..8b0bb1f18f 100644
--- a/plugin/chrev/init.go
+++ b/plugin/chrev/init.go
@@ -12,7 +12,7 @@ import (
func init() {
// 初始化engine
- engine := control.Register("chrev", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "英文字符翻转",
Help: "例: 翻转 I love you",
diff --git a/plugin/coser/coser.go b/plugin/coser/coser.go
index 73b87ed2b9..201099d6f8 100644
--- a/plugin/coser/coser.go
+++ b/plugin/coser/coser.go
@@ -53,7 +53,7 @@ func init() {
if err != nil {
panic(err)
}
- control.Register("coser", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "三次元coser",
Help: "- coser",
diff --git a/plugin/cpstory/cpstory.go b/plugin/cpstory/cpstory.go
index e44327de81..1d38ad9706 100644
--- a/plugin/cpstory/cpstory.go
+++ b/plugin/cpstory/cpstory.go
@@ -16,7 +16,7 @@ import (
)
func init() {
- engine := control.Register("cpstory", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "cp短打", // 这里也许有更好的名字
Help: "- 组cp[@xxx][@xxx]\n- 磕cp大老师 雪乃",
diff --git a/plugin/curse/curse.go b/plugin/curse/curse.go
index 7116f1e494..e0761d033f 100644
--- a/plugin/curse/curse.go
+++ b/plugin/curse/curse.go
@@ -21,7 +21,7 @@ const (
)
func init() {
- engine := control.Register("curse", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "骂人反击",
Help: "- 骂我\n- 大力骂我",
diff --git a/plugin/dailynews/dailynews.go b/plugin/dailynews/dailynews.go
index 3ed761c298..a36b635d3b 100644
--- a/plugin/dailynews/dailynews.go
+++ b/plugin/dailynews/dailynews.go
@@ -14,7 +14,7 @@ import (
const api = "http://dwz.2xb.cn/zaob"
func init() {
- engine := control.Register("dailynews", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "今日早报",
Help: "- 今日早报",
diff --git a/plugin/danbooru/main.go b/plugin/danbooru/main.go
index bf9d947ef5..3fab22af28 100644
--- a/plugin/danbooru/main.go
+++ b/plugin/danbooru/main.go
@@ -17,7 +17,7 @@ import (
)
func init() { // 插件主体
- engine := control.Register("danbooru", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "二次元图片标签识别",
Help: "- 鉴赏图片[图片]",
diff --git a/plugin/diana/bing.go b/plugin/diana/bing.go
index fd7ff5a44b..9343a6df29 100644
--- a/plugin/diana/bing.go
+++ b/plugin/diana/bing.go
@@ -12,7 +12,7 @@ import (
"github.com/FloatTech/ZeroBot-Plugin/plugin/diana/data"
)
-var engine = control.Register("diana", &ctrl.Options[*zero.Ctx]{
+var engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "嘉然相关", // 也许使用常用功能当Brief更好
Help: "- 小作文\n" +
diff --git a/plugin/dish/dish.go b/plugin/dish/dish.go
index c3cccccf70..d6b5e2b002 100644
--- a/plugin/dish/dish.go
+++ b/plugin/dish/dish.go
@@ -30,7 +30,7 @@ var (
)
func init() {
- en := control.Register("dish", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "程序员做饭指南",
Help: "-怎么做[xxx]|烹饪[xxx]|随机菜谱|随便做点菜",
diff --git a/plugin/drawlots/main.go b/plugin/drawlots/main.go
index 9154629e04..8f3a6ad265 100644
--- a/plugin/drawlots/main.go
+++ b/plugin/drawlots/main.go
@@ -40,7 +40,7 @@ var (
}
return lotsList
}()
- en = control.Register("drawlots", &ctrl.Options[*zero.Ctx]{
+ en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "多功能抽签",
Help: "支持图包文件夹和gif抽签\n" +
diff --git a/plugin/dress/dress.go b/plugin/dress/dress.go
index e9b22f6244..d8f7407b41 100644
--- a/plugin/dress/dress.go
+++ b/plugin/dress/dress.go
@@ -18,7 +18,7 @@ import (
)
func init() { // 插件主体
- engine := control.Register("dress", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "女装",
Help: "女装\n" +
diff --git a/plugin/drift_bottle/main.go b/plugin/driftbottle/main.go
similarity index 98%
rename from plugin/drift_bottle/main.go
rename to plugin/driftbottle/main.go
index 1417f251bd..8aba986d55 100644
--- a/plugin/drift_bottle/main.go
+++ b/plugin/driftbottle/main.go
@@ -33,7 +33,7 @@ var seaLocker sync.RWMutex
// We need a container to inject what we need :(
func init() {
- en := control.Register("driftbottle", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "漂流瓶",
Help: "- @bot pick" + "- @bot throw xxx (xxx为投递内容)",
diff --git a/plugin/emojimix/mix.go b/plugin/emojimix/mix.go
index cb98bffa9d..eef35e72ee 100644
--- a/plugin/emojimix/mix.go
+++ b/plugin/emojimix/mix.go
@@ -17,7 +17,7 @@ import (
const bed = "https://www.gstatic.com/android/keyboard/emojikitchen/%d/u%x/u%x_u%x.png"
func init() {
- control.Register("emojimix", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "合成emoji",
Help: "- [emoji][emoji]",
diff --git a/plugin/event/event.go b/plugin/event/event.go
index bc1cb508ef..bcf73923b8 100644
--- a/plugin/event/event.go
+++ b/plugin/event/event.go
@@ -15,7 +15,7 @@ import (
)
func init() {
- engine := control.Register("event", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "好友申请和群聊邀请事件处理",
Help: "- [开启|关闭]自动同意[申请|邀请|主人]\n" +
diff --git a/plugin/font/main.go b/plugin/font/main.go
index b3b2f61bf5..6c85e99563 100644
--- a/plugin/font/main.go
+++ b/plugin/font/main.go
@@ -22,7 +22,7 @@ import (
)
func init() {
- control.Register("font", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "渲染任意文字到图片",
Help: "- (用[字体])渲染(抖动)文字xxx\n可选字体: [终末体|终末变体|紫罗兰体|樱酥体|Consolas体|粗苹方体|未来荧黑体|Gugi体|八丸体|Impact体|猫啃体|苹方体]",
diff --git a/plugin/fortune/fortune.go b/plugin/fortune/fortune.go
index 30f367e582..705f852d70 100644
--- a/plugin/fortune/fortune.go
+++ b/plugin/fortune/fortune.go
@@ -48,7 +48,7 @@ var (
func init() {
// 插件主体
- en := control.Register("fortune", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "每日运势",
Help: "- 运势 | 抽签\n" +
diff --git a/plugin/funny/laugh.go b/plugin/funny/laugh.go
index 86d28077b9..52de4b14b1 100644
--- a/plugin/funny/laugh.go
+++ b/plugin/funny/laugh.go
@@ -24,7 +24,7 @@ type joke struct {
var db = &sql.Sqlite{}
func init() {
- en := control.Register("funny", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "讲个笑话",
Help: "- 讲个笑话[@xxx|qq号|人名] | 夸夸[@xxx|qq号|人名] ",
diff --git a/plugin/genshin/ys.go b/plugin/genshin/ys.go
index 63bd1bed39..a2213a0115 100644
--- a/plugin/genshin/ys.go
+++ b/plugin/genshin/ys.go
@@ -35,7 +35,7 @@ var (
)
func init() {
- engine := control.Register("genshin", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "原神模拟抽卡",
Help: "- 原神十连\n- 切换原神卡池",
diff --git a/plugin/gif/run.go b/plugin/gif/run.go
index cd278aea26..bf9f036e3a 100644
--- a/plugin/gif/run.go
+++ b/plugin/gif/run.go
@@ -126,7 +126,7 @@ func init() { // 插件主体
for k := range cmdMap {
cmd = append(cmd, k)
}
- en := control.Register("gif", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "制图",
Help: "下为制图命令:\n" +
diff --git a/plugin/github/repo_searcher.go b/plugin/github/repo_searcher.go
index 7f9f2842e7..9821c30e15 100644
--- a/plugin/github/repo_searcher.go
+++ b/plugin/github/repo_searcher.go
@@ -18,7 +18,7 @@ import (
)
func init() { // 插件主体
- control.Register("github", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "GitHub仓库搜索",
Help: "- >github [xxx]\n" +
diff --git a/plugin/guessmusic/main.go b/plugin/guessmusic/main.go
index fa1621bffa..061a94b352 100644
--- a/plugin/guessmusic/main.go
+++ b/plugin/guessmusic/main.go
@@ -31,7 +31,7 @@ var (
// 用户数据
cfg config
// 插件主体
- engine = control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "猜歌插件",
Help: "------bot主人指令------\n" +
diff --git a/plugin/heisi/heisi.go b/plugin/heisi/heisi.go
index 7da4395645..3d9b5548a5 100644
--- a/plugin/heisi/heisi.go
+++ b/plugin/heisi/heisi.go
@@ -66,7 +66,7 @@ func init() { // 插件主体
panic(err)
}
- engine := control.Register("heisi", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "黑丝",
Help: "- 来点黑丝\n- 来点白丝\n- 来点jk\n- 来点巨乳\n- 来点足控\n- 来点网红",
diff --git a/plugin/hitokoto/hitokoto.go b/plugin/hitokoto/hitokoto.go
index c08c2f5f31..a8001e64b5 100644
--- a/plugin/hitokoto/hitokoto.go
+++ b/plugin/hitokoto/hitokoto.go
@@ -18,7 +18,7 @@ import (
)
func init() { // 插件主体
- engine := control.Register("hitokoto", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "一言",
Help: "- 一言[xxx]\n" +
diff --git a/plugin/hs/run.go b/plugin/hs/run.go
index efd481117a..f61af21dfd 100644
--- a/plugin/hs/run.go
+++ b/plugin/hs/run.go
@@ -41,7 +41,7 @@ const (
)
func init() {
- engine := control.Register("hs", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "炉石搜卡",
Help: "- 搜卡[xxxx]\n" +
diff --git a/plugin/hyaku/main.go b/plugin/hyaku/main.go
index 6ba4ae4132..7c7c3199f1 100644
--- a/plugin/hyaku/main.go
+++ b/plugin/hyaku/main.go
@@ -51,7 +51,7 @@ func (l *line) String() string {
var lines [100]*line
func init() {
- engine := control.Register("hyaku", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "百人一首",
Help: "- 百人一首(随机发一首)\n" +
diff --git a/plugin/image_finder/keyword.go b/plugin/imgfinder/keyword.go
similarity index 98%
rename from plugin/image_finder/keyword.go
rename to plugin/imgfinder/keyword.go
index d4bbd155ef..ce8b25a3f7 100644
--- a/plugin/image_finder/keyword.go
+++ b/plugin/imgfinder/keyword.go
@@ -62,7 +62,7 @@ type resultjson struct {
var hrefre = regexp.MustCompile(``)
func init() {
- control.Register("imgfinder", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "关键字搜图",
Help: "- 来张 [xxx]",
diff --git a/plugin/inject/main.go b/plugin/inject/main.go
index 43055befc1..7e422dad2b 100644
--- a/plugin/inject/main.go
+++ b/plugin/inject/main.go
@@ -9,7 +9,7 @@ import (
)
func init() {
- en := control.Register("inject", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "注入指令",
Help: "- run[CQ码]",
diff --git a/plugin/jandan/jandan.go b/plugin/jandan/jandan.go
index 3c54cfbe44..873f98b1c2 100644
--- a/plugin/jandan/jandan.go
+++ b/plugin/jandan/jandan.go
@@ -23,7 +23,7 @@ const (
)
func init() {
- engine := control.Register("jandan", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "煎蛋网无聊图",
Help: "- 来份[屌|弔|吊]图\n- 更新[屌|弔|吊]图\n",
diff --git a/plugin/jiami/jiami.go b/plugin/jiami/jiami.go
index 9849054c13..4cb05b96f7 100644
--- a/plugin/jiami/jiami.go
+++ b/plugin/jiami/jiami.go
@@ -25,7 +25,7 @@ type nmd struct { // struct解析格式大概是
}
func init() { // 主函数
- en := control.Register("jiami", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "兽语加解密",
Help: "兽语加解密\n" +
diff --git a/plugin/jptingroom/jptingroom.go b/plugin/jptingroom/jptingroom.go
index a901a2e32b..611b184c1f 100644
--- a/plugin/jptingroom/jptingroom.go
+++ b/plugin/jptingroom/jptingroom.go
@@ -15,7 +15,7 @@ import (
)
func init() { // 插件主体
- engine := control.Register("jptingroom", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "日语听力学习材料",
Help: "- 随机日语听力\n" +
diff --git a/plugin/juejuezi/juejuezi.go b/plugin/juejuezi/juejuezi.go
index e651e59e95..d701991dc8 100644
--- a/plugin/juejuezi/juejuezi.go
+++ b/plugin/juejuezi/juejuezi.go
@@ -23,7 +23,7 @@ const (
)
func init() {
- control.Register("juejuezi", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "绝绝子生成器",
Help: "例: 喝奶茶绝绝子\n绝绝子吃饭",
diff --git a/plugin/kfccrazythursday/kfccrazythursday.go b/plugin/kfccrazythursday/kfccrazythursday.go
index e7f071ff3d..22faee506c 100644
--- a/plugin/kfccrazythursday/kfccrazythursday.go
+++ b/plugin/kfccrazythursday/kfccrazythursday.go
@@ -15,7 +15,7 @@ const (
)
func init() {
- engine := control.Register("kfccrazythursday", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "疯狂星期四",
Help: "疯狂星期四\n",
diff --git a/plugin/lolicon/lolicon.go b/plugin/lolicon/lolicon.go
index cf956f7ebb..516a4ca3b9 100644
--- a/plugin/lolicon/lolicon.go
+++ b/plugin/lolicon/lolicon.go
@@ -32,7 +32,7 @@ var (
)
func init() {
- en := control.Register("lolicon", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "随机图片",
Help: "- 随机图片\n" +
diff --git a/plugin/magicprompt/magicprompt.go b/plugin/magicprompt/magicprompt.go
index 1f0d4d32b1..d090198416 100644
--- a/plugin/magicprompt/magicprompt.go
+++ b/plugin/magicprompt/magicprompt.go
@@ -23,7 +23,7 @@ const (
)
func init() { // 插件主体
- engine := control.Register("magicprompt", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "MagicPrompt-Stable-Diffusion吟唱提示",
Help: "- 吟唱提示 xxx",
diff --git a/plugin/manager/manager.go b/plugin/manager/manager.go
index a159781fb2..d4dcec52a5 100644
--- a/plugin/manager/manager.go
+++ b/plugin/manager/manager.go
@@ -64,7 +64,7 @@ var (
)
func init() { // 插件主体
- engine := control.Register("manager", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "群管插件",
Help: hint,
diff --git a/plugin/mcfish/fish.go b/plugin/mcfish/fish.go
new file mode 100644
index 0000000000..0c1924e795
--- /dev/null
+++ b/plugin/mcfish/fish.go
@@ -0,0 +1,331 @@
+// Package mcfish 钓鱼模拟器
+package mcfish
+
+import (
+ "math/rand"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/FloatTech/AnimeAPI/wallet"
+ "github.com/FloatTech/zbputils/ctxext"
+ "github.com/sirupsen/logrus"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+func init() {
+ engine.OnRegex(`^进行(([1-5]\d|[1-9])次)?钓鱼$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ fishNumber := 1
+ info := ctx.State["regex_matched"].([]string)[2]
+ if info != "" {
+ number, err := strconv.Atoi(info)
+ if err != nil || number > FishLimit {
+ ctx.SendChain(message.Text("请输入正确的次数"))
+ return
+ }
+ fishNumber = number
+ }
+ equipInfo, err := dbdata.getUserEquip(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.2]:", err))
+ return
+ }
+ if equipInfo == (equip{}) {
+ ok, err := dbdata.checkEquipFor(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.2.1]:", err))
+ return
+ }
+ if !ok {
+ ctx.SendChain(message.At(uid), message.Text("请装备鱼竿后钓鱼", err))
+ return
+ }
+ ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你尚未装备鱼竿,是否花费100购买鱼竿?\n回答\"是\"或\"否\""))
+ // 等待用户下一步选择
+ recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel()
+ buy := false
+ for {
+ select {
+ case <-time.After(time.Second * 120):
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("等待超时,取消钓鱼")))
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "否" {
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已取消购买")))
+ return
+ }
+ money := wallet.GetWalletOf(uid)
+ if money < 100 {
+ ctx.SendChain(message.Text("你钱包当前只有", money, "ATRI币,无法完成支付"))
+ return
+ }
+ err = wallet.InsertWalletOf(uid, -100)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.3]:", err))
+ return
+ }
+ equipInfo = equip{
+ ID: uid,
+ Equip: "木竿",
+ Durable: 30,
+ }
+ err = dbdata.updateUserEquip(equipInfo)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.4]:", err))
+ return
+ }
+ err = dbdata.setEquipFor(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.4]:", err))
+ return
+ }
+ buy = true
+ }
+ if buy {
+ break
+ }
+ }
+ }
+ if equipInfo.Durable < fishNumber {
+ fishNumber = equipInfo.Durable
+ }
+ residue, err := dbdata.updateFishInfo(uid, fishNumber)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.1]:", err))
+ return
+ }
+ if residue == 0 {
+ ctx.SendChain(message.Text("今天你已经进行", FishLimit, "次钓鱼了.\n游戏虽好,但请不要沉迷。"))
+ return
+ }
+ fishNumber = residue
+ msg := ""
+ if equipInfo.Equip != "美西螈" {
+ equipInfo.Durable -= fishNumber
+ err = dbdata.updateUserEquip(equipInfo)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.5]:", err))
+ return
+ }
+ if equipInfo.Durable < 10 && equipInfo.Durable > 0 {
+ msg = "(你的鱼竿耐久仅剩" + strconv.Itoa(equipInfo.Durable) + ")"
+ } else if equipInfo.Durable <= 0 {
+ msg = "(你的鱼竿耐已销毁)"
+ }
+ } else {
+ fishNmaes, err := dbdata.pickFishFor(uid, fishNumber)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err))
+ return
+ }
+ if len(fishNmaes) == 0 {
+ equipInfo.Durable = 0
+ err = dbdata.updateUserEquip(equipInfo)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.5]:", err))
+ }
+ ctx.SendChain(message.Text("美西螈因为没吃到鱼,钓鱼时一直没回来,你失去了美西螈"))
+ return
+ }
+ msg = "(美西螈吃掉了"
+ fishNumber = 0
+ for name, number := range fishNmaes {
+ fishNumber += number
+ msg += strconv.Itoa(number) + name + "、"
+ }
+ msg += ")"
+ }
+ waitTime := 120 / (equipInfo.Induce + 1)
+ ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你开始去钓鱼了,请耐心等待鱼上钩(预计要", time.Second*time.Duration(waitTime), ")"))
+ timer := time.NewTimer(time.Second * time.Duration(rand.Intn(waitTime)+1))
+ for {
+ <-timer.C
+ timer.Stop()
+ break
+ }
+ // 钓到鱼的范围
+ number, err := dbdata.getNumberFor(uid, "鱼")
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err))
+ return
+ }
+ if number > 100 || equipInfo.Equip == "美西螈" { // 放大概率
+ probabilities["treasure"] = probabilityLimit{
+ Min: 0,
+ Max: 2,
+ }
+ probabilities["pole"] = probabilityLimit{
+ Min: 2,
+ Max: 10,
+ }
+ probabilities["fish"] = probabilityLimit{
+ Min: 10,
+ Max: 45,
+ }
+ probabilities["waste"] = probabilityLimit{
+ Min: 45,
+ Max: 90,
+ }
+ }
+ for name, info := range probabilities {
+ switch name {
+ case "treasure":
+ info.Max += equipInfo.Favor
+ probabilities[name] = info
+ case "pole":
+ info.Min += equipInfo.Favor
+ info.Max += equipInfo.Favor * 2
+ probabilities[name] = info
+ case "fish":
+ info.Min += equipInfo.Favor * 2
+ info.Max += equipInfo.Favor * 3
+ probabilities[name] = info
+ case "waste":
+ info.Min += equipInfo.Favor * 3
+ probabilities[name] = info
+ }
+ }
+ // 钓鱼结算
+ picName := ""
+ thingNameList := make(map[string]int)
+ for i := fishNumber; i > 0; i-- {
+ thingName := ""
+ typeOfThing := ""
+ number := 1
+ dice := rand.Intn(100)
+ switch {
+ case dice >= probabilities["waste"].Min && dice < probabilities["waste"].Max: // 垃圾
+ typeOfThing = "waste"
+ thingName = wasteList[rand.Intn(len(wasteList))]
+ picName = thingName
+ case dice >= probabilities["treasure"].Min && dice < probabilities["treasure"].Max: // 宝藏
+ dice = rand.Intn(100)
+ switch {
+ case dice >= probabilities["美西螈"].Min && dice < probabilities["美西螈"].Max:
+ typeOfThing = "pole"
+ picName = "美西螈"
+ thingName = "美西螈"
+ case dice >= probabilities["唱片"].Min && dice < probabilities["唱片"].Max:
+ typeOfThing = "article"
+ picName = "唱片"
+ thingName = "唱片"
+ case dice >= probabilities["海之眷顾"].Min && dice < probabilities["海之眷顾"].Max:
+ typeOfThing = "article"
+ picName = "book"
+ thingName = "海之眷顾"
+ default:
+ typeOfThing = "article"
+ picName = "book"
+ thingName = "诱钓"
+ }
+ case dice >= probabilities["pole"].Min && dice < probabilities["pole"].Max: // 宝藏
+ typeOfThing = "pole"
+ dice := rand.Intn(100)
+ switch {
+ case dice >= probabilities["铁竿"].Min && dice < probabilities["铁竿"].Max:
+ thingName = "铁竿"
+ case dice >= probabilities["金竿"].Min && dice < probabilities["金竿"].Max:
+ thingName = "金竿"
+ case dice >= probabilities["钻石竿"].Min && dice < probabilities["钻石竿"].Max:
+ thingName = "钻石竿"
+ case dice >= probabilities["下界合金竿竿竿"].Min && dice < probabilities["下界合金竿竿竿"].Max:
+ thingName = "下界合金竿竿竿"
+ default:
+ thingName = "木竿"
+ }
+ picName = thingName
+ case dice >= probabilities["fish"].Min && dice < probabilities["fish"].Max:
+ typeOfThing = "fish"
+ dice = rand.Intn(100)
+ switch {
+ case dice >= probabilities["墨鱼"].Min && dice < probabilities["墨鱼"].Max:
+ thingName = "墨鱼"
+ case dice >= probabilities["鳕鱼"].Min && dice < probabilities["鳕鱼"].Max:
+ thingName = "鳕鱼"
+ case dice >= probabilities["鲑鱼"].Min && dice < probabilities["鲑鱼"].Max:
+ thingName = "鲑鱼"
+ case dice >= probabilities["热带鱼"].Min && dice < probabilities["热带鱼"].Max:
+ thingName = "热带鱼"
+ case dice >= probabilities["河豚"].Min && dice < probabilities["河豚"].Max:
+ thingName = "河豚"
+ default:
+ thingName = "鹦鹉螺"
+ }
+ picName = thingName
+ default:
+ thingNameList["赛博空气"]++
+ }
+ if thingName != "" {
+ newThing := article{}
+ if strings.Contains(thingName, "竿") {
+ info := strconv.Itoa(rand.Intn(durationList[thingName])+1) +
+ "/" + strconv.Itoa(rand.Intn(10)) + "/" +
+ strconv.Itoa(rand.Intn(3)) + "/" + strconv.Itoa(rand.Intn(2))
+ newThing = article{
+ Duration: time.Now().Unix()*100 + int64(i),
+ Type: typeOfThing,
+ Name: thingName,
+ Number: number,
+ Other: info,
+ }
+ } else {
+ thingInfo, err := dbdata.getUserThingInfo(uid, thingName)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.6]:", err))
+ return
+ }
+ if len(thingInfo) == 0 {
+ newThing = article{
+ Duration: time.Now().Unix()*100 + int64(i),
+ Type: typeOfThing,
+ Name: thingName,
+ }
+ } else {
+ newThing = thingInfo[0]
+ }
+ if equipInfo.Equip == "美西螈" && thingName != "美西螈" {
+ number += 2
+ }
+ newThing.Number += number
+ }
+ err = dbdata.updateUserThingInfo(uid, newThing)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.7]:", err))
+ return
+ }
+ thingNameList[thingName] += number
+ }
+ }
+ if len(thingNameList) == 1 {
+ thingName := ""
+ numberOfFish := 0
+ for name, number := range thingNameList {
+ thingName = name
+ numberOfFish = number
+ }
+ if picName != "" {
+ pic, err := engine.GetLazyData(picName+".png", false)
+ if err != nil {
+ logrus.Warnln("[mcfish]error:", err)
+ ctx.SendChain(message.At(uid), message.Text("恭喜你钓到了", numberOfFish, thingName, "\n", msg))
+ return
+ }
+ ctx.SendChain(message.At(uid), message.Text("恭喜你钓到了", numberOfFish, thingName, "\n", msg), message.ImageBytes(pic))
+ return
+ }
+ ctx.SendChain(message.At(uid), message.Text("恭喜你钓到了", numberOfFish, thingName, "\n", msg))
+ return
+ }
+ msgInfo := make(message.Message, 0, 3+len(thingNameList))
+ msgInfo = append(msgInfo, message.Reply(ctx.Event.MessageID), message.Text("你进行了", fishNumber, "次钓鱼,结果如下:\n"))
+ for name, number := range thingNameList {
+ msgInfo = append(msgInfo, message.Text(name, " : ", number, "\n"))
+ }
+ msgInfo = append(msgInfo, message.Text(msg))
+ ctx.Send(msgInfo)
+ })
+}
diff --git a/plugin/mcfish/main.go b/plugin/mcfish/main.go
new file mode 100644
index 0000000000..ef64bcfd7a
--- /dev/null
+++ b/plugin/mcfish/main.go
@@ -0,0 +1,622 @@
+// Package mcfish 钓鱼模拟器
+package mcfish
+
+import (
+ "encoding/json"
+ "math/rand"
+ "os"
+ "strconv"
+ "sync"
+ "time"
+
+ fcext "github.com/FloatTech/floatbox/ctxext"
+ sql "github.com/FloatTech/sqlite"
+ ctrl "github.com/FloatTech/zbpctrl"
+ "github.com/FloatTech/zbputils/control"
+ "github.com/FloatTech/zbputils/ctxext"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+type fishdb struct {
+ db *sql.Sqlite
+ sync.RWMutex
+}
+
+// FishLimit 钓鱼次数上限
+const FishLimit = 50
+
+// 各物品信息
+type jsonInfo struct {
+ ZoneInfo []zoneInfo `json:"分类"` // 区域概率
+ ArticleInfo []articleInfo `json:"物品"` // 物品信息
+}
+type zoneInfo struct {
+ Name string `json:"类型"` // 类型
+ Probability int `json:"概率[0-100)"` // 概率
+}
+type articleInfo struct {
+ Name string `json:"名称"` // 名称
+ Type string `json:"类型"` // 类型
+ Probability int `json:"概率[0-100),omitempty"` // 概率
+ Durable int `json:"耐久上限,omitempty"` // 耐久
+ Price int `json:"价格"` // 价格
+}
+
+type probabilityLimit struct {
+ Min int
+ Max int
+}
+
+type equip struct {
+ ID int64 // 用户
+ Equip string // 装备名称
+ Durable int // 耐久
+ Maintenance int // 维修次数
+ Induce int // 诱钓等级
+ Favor int // 眷顾等级
+}
+
+type article struct {
+ Duration int64
+ Name string
+ Number int
+ Other string // 耐久/维修次数/诱钓/眷顾
+ Type string
+}
+
+type store struct {
+ Duration int64
+ Name string
+ Number int
+ Price int
+ Other string // 耐久/维修次数/诱钓/眷顾
+ Type string
+}
+
+type fishState struct {
+ ID int64
+ Duration int64
+ Fish int
+ Equip int
+}
+
+type storeDiscount struct {
+ Name string
+ Discount int
+}
+
+var (
+ articlesInfo = jsonInfo{} // 物品信息
+ thingList = make([]string, 0, 100) // 竿列表
+ poleList = make([]string, 0, 10) // 竿列表
+ fishList = make([]string, 0, 10) // 鱼列表
+ treasureList = make([]string, 0, 10) // 鱼列表
+ wasteList = make([]string, 0, 10) // 垃圾列表
+ probabilities = make(map[string]probabilityLimit, 50) // 概率分布
+ priceList = make(map[string]int, 50) // 价格分布
+ durationList = make(map[string]int, 50) // 装备耐久分布
+ discountList = make(map[string]int, 50) // 价格波动信息
+ enchantLevel = []string{"0", "Ⅰ", "Ⅱ", "Ⅲ"}
+ dbdata = &fishdb{
+ db: &sql.Sqlite{},
+ }
+)
+
+var (
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
+ DisableOnDefault: false,
+ Brief: "钓鱼",
+ Help: "一款钓鱼模拟器\n----------指令----------\n" +
+ "- 钓鱼看板/钓鱼商店\n- 购买xxx\n- 购买xxx [数量]\n- 出售xxx\n- 出售xxx [数量]\n" +
+ "- 钓鱼背包\n- 装备[xx竿|三叉戟|美西螈]\n- 附魔[诱钓|海之眷顾]\n- 修复鱼竿\n- 合成[xx竿|三叉戟]\n" +
+ "- 进行钓鱼\n- 进行n次钓鱼\n- 当前装备概率明细\n" +
+ "规则:\n1.每日的商店价格是波动的!!如何最大化收益自己考虑一下喔\n" +
+ "2.装备信息:\n-> 木竿 : 耐久上限:30 均价:100 上钩概率:0.7%\n-> 铁竿 : 耐久上限:50 均价:300 上钩概率:0.2%\n-> 金竿 : 耐久上限:70 均价700 上钩概率:0.06%\n" +
+ "-> 钻石竿 : 耐久上限:100 均价1500 上钩概率:0.03%\n-> 下界合金竿 : 耐久上限:150 均价3100 上钩概率:0.01%\n-> 三叉戟 : 可使钓的鱼类物品数量变成3 耐久上限:300 均价4000 只能合成和交易\n" +
+ "3.附魔书信息:\n-> 诱钓 : 减少上钩时间. 均价:1000, 上钩概率:0.59%\n-> 海之眷顾 : 增加宝藏上钩概率. 均价:2500, 上钩概率:0.39%\n" +
+ "4.稀有物品:\n-> 唱片 : 出售物品时使用该物品使价格翻倍. 均价:3000, 上钩概率:0.01%\n-> 美西螈 : 可装备,获得隐形[钓鱼佬]buff,并让钓到除鱼竿和美西螈外的物品数量变成3,无耐久上限.不可修复/附魔,每次钓鱼消耗任意一鱼类物品. 均价:3000, 上钩概率:0.01%\n" +
+ "5.鱼类信息:\n-> 鳕鱼 : 均价:10 上钩概率:0.69%\n-> 鲑鱼 : 均价:50 上钩概率:0.2%\n-> 热带鱼 : 均价:100 上钩概率:0.06%\n-> 河豚 : 均价:300 上钩概率:0.03%\n-> 鹦鹉螺 : 均价:500 上钩概率:0.01%\n-> 墨鱼 : 均价:500 上钩概率:0.01%\n" +
+ "6.垃圾:\n-> 均价:10 上钩概率:30%\n" +
+ "7.物品BUFF:\n-> 钓鱼佬 : 当背包名字含有'鱼'的物品数量超过100时激活,钓到物品概率提高至90%\n-> 修复大师 : 当背包鱼竿数量超过10时激活,修复物品时耐久百分百继承\n" +
+ "8.合成:\n-> 铁竿 : 3x木竿\n-> 金竿 : 3x铁竿\n-> 钻石竿 : 3x金竿\n-> 下界合金竿 : 3x钻石竿\n-> 三叉戟 : 3x下界合金竿\n注:合成成功率90%,继承附魔等级合/3的等级\n" +
+ "9.杂项:\n-> 无装备的情况下,每人最多可以购买3次100块钱的鱼竿\n-> 默认状态钓鱼上钩概率为60%(理论值!!!)\n-> 附魔的鱼竿会因附魔变得昂贵,每个附魔最高3级\n-> 三叉戟不算鱼竿",
+ PublicDataFolder: "McFish",
+ }).ApplySingle(ctxext.DefaultSingle)
+ getdb = fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
+ dbdata.db.DBPath = engine.DataFolder() + "fishdata.db"
+ err := dbdata.db.Open(time.Hour * 24)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at main.go.1]:", err))
+ return false
+ }
+ return true
+ })
+)
+
+func init() {
+ // go func() {
+ _, err := engine.GetLazyData("articlesInfo.json", false)
+ if err != nil {
+ panic(err)
+ }
+ reader, err := os.Open(engine.DataFolder() + "articlesInfo.json")
+ if err == nil {
+ err = json.NewDecoder(reader).Decode(&articlesInfo)
+ }
+ if err == nil {
+ err = reader.Close()
+ }
+ if err != nil {
+ panic(err)
+ }
+ probableList := make([]int, 4)
+ for _, info := range articlesInfo.ZoneInfo {
+ switch info.Name {
+ case "treasure":
+ probableList[0] = info.Probability
+ case "pole":
+ probableList[1] = info.Probability
+ case "fish":
+ probableList[2] = info.Probability
+ case "waste":
+ probableList[3] = info.Probability
+ }
+ }
+ probabilities["treasure"] = probabilityLimit{
+ Min: 0,
+ Max: probableList[0],
+ }
+ probabilities["pole"] = probabilityLimit{
+ Min: probableList[0],
+ Max: probableList[1],
+ }
+ probabilities["fish"] = probabilityLimit{
+ Min: probableList[1],
+ Max: probableList[2],
+ }
+ probabilities["waste"] = probabilityLimit{
+ Min: probableList[2],
+ Max: probableList[3],
+ }
+ min := make(map[string]int, 4)
+ for _, info := range articlesInfo.ArticleInfo {
+ switch {
+ case info.Type == "pole" || info.Name == "美西螈":
+ poleList = append(poleList, info.Name)
+ case info.Type == "fish":
+ fishList = append(fishList, info.Name)
+ case info.Type == "waste":
+ wasteList = append(wasteList, info.Name)
+ case info.Type == "treasure":
+ treasureList = append(treasureList, info.Name)
+ }
+ thingList = append(thingList, info.Name)
+ priceList[info.Name] = info.Price
+ if info.Durable != 0 {
+ durationList[info.Name] = info.Durable
+ }
+ probabilities[info.Name] = probabilityLimit{
+ Min: min[info.Type],
+ Max: min[info.Type] + info.Probability,
+ }
+ min[info.Type] += info.Probability
+ }
+ // }()
+}
+
+// 更新上限信息
+func (sql *fishdb) updateFishInfo(uid int64, number int) (residue int, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := fishState{ID: uid}
+ err = sql.db.Create("fishState", &userInfo)
+ if err != nil {
+ return 0, err
+ }
+ _ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10))
+ if time.Unix(userInfo.Duration, 0).Day() != time.Now().Day() {
+ userInfo.Fish = 0
+ userInfo.Duration = time.Now().Unix()
+ }
+ if userInfo.Fish >= FishLimit {
+ return 0, nil
+ }
+ residue = number
+ if userInfo.Fish+number > FishLimit {
+ residue = FishLimit - userInfo.Fish
+ number = residue
+ }
+ userInfo.Fish += number
+ err = sql.db.Insert("fishState", &userInfo)
+ return
+}
+
+/*********************************************************/
+/************************装备相关函数***********************/
+/*********************************************************/
+
+func (sql *fishdb) checkEquipFor(uid int64) (ok bool, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := fishState{ID: uid}
+ err = sql.db.Create("fishState", &userInfo)
+ if err != nil {
+ return false, err
+ }
+ if !sql.db.CanFind("fishState", "where ID = "+strconv.FormatInt(uid, 10)) {
+ return true, nil
+ }
+ err = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10))
+ if err != nil {
+ return false, err
+ }
+ if userInfo.Equip > 3 {
+ return false, nil
+ }
+ return true, nil
+}
+
+func (sql *fishdb) setEquipFor(uid int64) (err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := fishState{ID: uid}
+ err = sql.db.Create("fishState", &userInfo)
+ if err != nil {
+ return err
+ }
+ _ = sql.db.Find("fishState", &userInfo, "where ID = "+strconv.FormatInt(uid, 10))
+ if err != nil {
+ return err
+ }
+ userInfo.Equip++
+ return sql.db.Insert("fishState", &userInfo)
+}
+
+// 获取装备信息
+func (sql *fishdb) getUserEquip(uid int64) (userInfo equip, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ err = sql.db.Create("equips", &userInfo)
+ if err != nil {
+ return
+ }
+ if !sql.db.CanFind("equips", "where ID = "+strconv.FormatInt(uid, 10)) {
+ return
+ }
+ err = sql.db.Find("equips", &userInfo, "where ID = "+strconv.FormatInt(uid, 10))
+ return
+}
+
+// 更新装备信息
+func (sql *fishdb) updateUserEquip(userInfo equip) (err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ err = sql.db.Create("equips", &userInfo)
+ if err != nil {
+ return
+ }
+ if userInfo.Durable == 0 {
+ return sql.db.Del("equips", "where ID = "+strconv.FormatInt(userInfo.ID, 10))
+ }
+ return sql.db.Insert("equips", &userInfo)
+}
+
+func (sql *fishdb) pickFishFor(uid int64, number int) (fishNames map[string]int, err error) {
+ fishNames = make(map[string]int, 6)
+ name := strconv.FormatInt(uid, 10) + "Pack"
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := article{}
+ err = sql.db.Create(name, &userInfo)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count(name)
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return
+ }
+ if !sql.db.CanFind(name, "where Type is 'fish'") {
+ return
+ }
+ fishTypes := make([]article, 0, count)
+ fishInfo := article{}
+ err = sql.db.FindFor(name, &fishInfo, "where Type is 'fish'", func() error {
+ fishTypes = append(fishTypes, fishInfo)
+ return nil
+ })
+ if err != nil {
+ return
+ }
+ if len(fishTypes) == 0 {
+ return
+ }
+ max := 0
+ for _, info := range fishTypes {
+ max += info.Number
+ }
+ if max < number {
+ number = max
+ }
+ for i := number; i > 0; {
+ randNumber := rand.Intn(len(fishTypes))
+ if fishTypes[randNumber].Number <= 0 {
+ continue
+ }
+ fishTypes[randNumber].Number--
+ if fishTypes[randNumber].Number <= 0 {
+ err = sql.db.Del(name, "where Duration = "+strconv.FormatInt(fishTypes[randNumber].Duration, 10))
+ } else {
+ err = sql.db.Insert(name, &fishTypes[randNumber])
+ }
+ if err != nil {
+ return
+ }
+ fishNames[fishTypes[randNumber].Name]++
+ i--
+ }
+ return
+}
+
+/*********************************************************/
+/************************背包相关函数***********************/
+/*********************************************************/
+
+// 获取用户背包信息
+func (sql *fishdb) getUserPack(uid int64) (thingInfos []article, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := article{}
+ err = sql.db.Create(strconv.FormatInt(uid, 10)+"Pack", &userInfo)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count(strconv.FormatInt(uid, 10) + "Pack")
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return
+ }
+ err = sql.db.FindFor(strconv.FormatInt(uid, 10)+"Pack", &userInfo, "ORDER by Type, Name, Other ASC", func() error {
+ thingInfos = append(thingInfos, userInfo)
+ return nil
+ })
+ return
+}
+
+// 获取用户物品信息
+func (sql *fishdb) getUserThingInfo(uid int64, thing string) (thingInfos []article, err error) {
+ name := strconv.FormatInt(uid, 10) + "Pack"
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := article{}
+ err = sql.db.Create(name, &userInfo)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count(name)
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return
+ }
+ if !sql.db.CanFind(name, "where Name = '"+thing+"'") {
+ return
+ }
+ err = sql.db.FindFor(name, &userInfo, "where Name = '"+thing+"'", func() error {
+ thingInfos = append(thingInfos, userInfo)
+ return nil
+ })
+ return
+}
+
+// 更新用户物品信息
+func (sql *fishdb) updateUserThingInfo(uid int64, userInfo article) (err error) {
+ name := strconv.FormatInt(uid, 10) + "Pack"
+ sql.Lock()
+ defer sql.Unlock()
+ err = sql.db.Create(name, &userInfo)
+ if err != nil {
+ return
+ }
+ if userInfo.Number == 0 {
+ return sql.db.Del(name, "where Duration = "+strconv.FormatInt(userInfo.Duration, 10))
+ }
+ return sql.db.Insert(name, &userInfo)
+}
+
+// 获取某关键字的数量
+func (sql *fishdb) getNumberFor(uid int64, thing string) (number int, err error) {
+ name := strconv.FormatInt(uid, 10) + "Pack"
+ sql.Lock()
+ defer sql.Unlock()
+ userInfo := article{}
+ err = sql.db.Create(name, &userInfo)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count(name)
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return
+ }
+ if !sql.db.CanFind(name, "where Name glob '*"+thing+"*'") {
+ return
+ }
+ info := article{}
+ err = sql.db.FindFor(name, &info, "where Name glob '*"+thing+"*'", func() error {
+ number += info.Number
+ return nil
+ })
+ return
+}
+
+/*********************************************************/
+/************************商店相关函数***********************/
+/*********************************************************/
+
+// 刷新商店信息
+func (sql *fishdb) refreshStroeInfo() (ok bool, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ err = sql.db.Create("stroeDiscount", &storeDiscount{})
+ if err != nil {
+ return false, err
+ }
+ lastTime := storeDiscount{}
+ _ = sql.db.Find("stroeDiscount", &lastTime, "where Name = 'lastTime'")
+ refresh := false
+ timeNow := time.Now().Day()
+ if timeNow != lastTime.Discount {
+ lastTime = storeDiscount{
+ Name: "lastTime",
+ Discount: timeNow,
+ }
+ err = sql.db.Insert("stroeDiscount", &lastTime)
+ if err != nil {
+ return false, err
+ }
+ refresh = true
+ }
+ for _, name := range thingList {
+ thing := storeDiscount{}
+ switch refresh {
+ case true:
+ thingDiscount := 50 + rand.Intn(150)
+ thing = storeDiscount{
+ Name: name,
+ Discount: thingDiscount,
+ }
+ err = sql.db.Insert("stroeDiscount", &thing)
+ if err != nil {
+ return
+ }
+ default:
+ _ = sql.db.Find("stroeDiscount", &thing, "where Name = '"+name+"'")
+ }
+ if thing.Discount != 0 {
+ discountList[name] = thing.Discount
+ } else {
+ discountList[name] = 100
+ }
+ }
+ if refresh { // 每天调控1种鱼
+ thingInfo := store{}
+ err = sql.db.Create("store", &thingInfo)
+ if err != nil {
+ return
+ }
+ fish := fishList[rand.Intn(len(fishList))]
+ _ = sql.db.Find("store", &thingInfo, "where Name = '"+fish+"'")
+ if thingInfo == (store{}) {
+ thingInfo.Duration = time.Now().Unix()
+ thingInfo.Type = "fish"
+ thingInfo.Name = fish
+ thingInfo.Price = priceList[fish] * discountList[fish] / 100
+ }
+ thingInfo.Number += (100 - discountList[fish])
+ if thingInfo.Number < 1 {
+ thingInfo.Number = 1
+ }
+ _ = sql.db.Insert("store", &thingInfo)
+ }
+ return true, nil
+}
+
+// 获取商店信息
+func (sql *fishdb) getStoreInfo() (thingInfos []store, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ thingInfo := store{}
+ err = sql.db.Create("store", &thingInfo)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count("store")
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return
+ }
+ err = sql.db.FindFor("store", &thingInfo, "ORDER by Type, Name, Price ASC", func() error {
+ thingInfos = append(thingInfos, thingInfo)
+ return nil
+ })
+ return
+}
+
+// 获取商店物品信息
+func (sql *fishdb) getStoreThingInfo(thing string) (thingInfos []store, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ thingInfo := store{}
+ err = sql.db.Create("store", &thingInfo)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count("store")
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return
+ }
+ if !sql.db.CanFind("store", "where Name = '"+thing+"'") {
+ return
+ }
+ err = sql.db.FindFor("store", &thingInfo, "where Name = '"+thing+"'", func() error {
+ thingInfos = append(thingInfos, thingInfo)
+ return nil
+ })
+ return
+}
+
+// 获取商店物品信息
+func (sql *fishdb) checkStoreFor(thing store, number int) (ok bool, err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ err = sql.db.Create("store", &thing)
+ if err != nil {
+ return
+ }
+ count, err := sql.db.Count("store")
+ if err != nil {
+ return
+ }
+ if count == 0 {
+ return false, nil
+ }
+ if !sql.db.CanFind("store", "where Duration = "+strconv.FormatInt(thing.Duration, 10)) {
+ return false, nil
+ }
+ err = sql.db.Find("store", &thing, "where Duration = "+strconv.FormatInt(thing.Duration, 10))
+ if err != nil {
+ return
+ }
+ if thing.Number < number {
+ return false, nil
+ }
+ return true, nil
+}
+
+// 更新商店信息
+func (sql *fishdb) updateStoreInfo(thingInfo store) (err error) {
+ sql.Lock()
+ defer sql.Unlock()
+ err = sql.db.Create("store", &thingInfo)
+ if err != nil {
+ return
+ }
+ if thingInfo.Number == 0 {
+ return sql.db.Del("store", "where Duration = "+strconv.FormatInt(thingInfo.Duration, 10))
+ }
+ return sql.db.Insert("store", &thingInfo)
+}
diff --git a/plugin/mcfish/pack.go b/plugin/mcfish/pack.go
new file mode 100644
index 0000000000..7c1043e2cb
--- /dev/null
+++ b/plugin/mcfish/pack.go
@@ -0,0 +1,454 @@
+// Package mcfish 钓鱼模拟器
+package mcfish
+
+import (
+ "bytes"
+ "errors"
+ "image"
+ "image/color"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/FloatTech/AnimeAPI/wallet"
+ "github.com/FloatTech/floatbox/file"
+ "github.com/FloatTech/floatbox/math"
+ "github.com/FloatTech/gg"
+ "github.com/FloatTech/imgfactory"
+ "github.com/FloatTech/zbputils/control"
+ "github.com/FloatTech/zbputils/ctxext"
+ "github.com/FloatTech/zbputils/img/text"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+func init() {
+ engine.OnFullMatch("钓鱼背包", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ equipInfo, err := dbdata.getUserEquip(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pack.go.1]:", err))
+ return
+ }
+ articles, err := dbdata.getUserPack(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pack.go.2]:", err))
+ return
+ }
+ pic, err := drawPackImage(uid, equipInfo, articles)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pack.go.3]:", err))
+ return
+ }
+ ctx.SendChain(message.ImageBytes(pic))
+ })
+ engine.OnFullMatch("当前装备概率明细", getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ equipInfo, err := dbdata.getUserEquip(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pack.go.1]:", err))
+ return
+ }
+ number, err := dbdata.getNumberFor(uid, "鱼")
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err))
+ return
+ }
+ msg := make(message.Message, 0, 20+len(thingList))
+ msg = append(msg, message.At(uid), message.Text("\n大类概率:\n"))
+ probableList := make([]int, 4)
+ for _, info := range articlesInfo.ZoneInfo {
+ switch info.Name {
+ case "treasure":
+ probableList[0] = info.Probability
+ case "pole":
+ probableList[1] = info.Probability
+ case "fish":
+ probableList[2] = info.Probability
+ case "waste":
+ probableList[3] = info.Probability
+ }
+ }
+ if number > 100 || equipInfo.Equip == "美西螈" { // 放大概率
+ probableList = []int{2, 8, 35, 45}
+ }
+ if equipInfo.Favor > 0 {
+ probableList[0] += equipInfo.Favor
+ probableList[1] += equipInfo.Favor
+ probableList[2] += equipInfo.Favor
+ probableList[3] -= equipInfo.Favor * 3
+ }
+ probable := probableList[0]
+ msg = append(msg, message.Text("宝藏 : ", probableList[0], "%\n"))
+ probable += probableList[1]
+ msg = append(msg, message.Text("鱼竿 : ", probableList[1], "%\n"))
+ probable += probableList[2]
+ msg = append(msg, message.Text("鱼类 : ", probableList[2], "%\n"))
+ probable += probableList[3]
+ msg = append(msg, message.Text("垃圾 : ", probableList[3], "%\n"))
+ msg = append(msg, message.Text("合计 : ", probable, "%\n"))
+ msg = append(msg, message.Text("-----------\n宝藏概率:\n"))
+ for _, name := range treasureList {
+ msg = append(msg, message.Text(name, " : ",
+ strconv.FormatFloat(float64(probabilities[name].Max-probabilities[name].Min)*float64(probableList[0])/100, 'f', 2, 64),
+ "%\n"))
+ }
+ msg = append(msg, message.Text("-----------\n鱼竿概率:\n"))
+ for _, name := range poleList {
+ msg = append(msg, message.Text(name, " : ",
+ strconv.FormatFloat(float64(probabilities[name].Max-probabilities[name].Min)*float64(probableList[1])/100, 'f', 2, 64),
+ "%\n"))
+ }
+ msg = append(msg, message.Text("-----------\n鱼类概率:\n"))
+ for _, name := range fishList {
+ msg = append(msg, message.Text(name, " : ",
+ strconv.FormatFloat(float64(probabilities[name].Max-probabilities[name].Min)*float64(probableList[2])/100, 'f', 2, 64),
+ "%\n"))
+ }
+ msg = append(msg, message.Text("-----------"))
+ ctx.Send(msg)
+ })
+}
+
+func drawPackImage(uid int64, equipInfo equip, articles []article) (imagePicByte []byte, err error) {
+ fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ wg sync.WaitGroup
+ equipBlock image.Image // 装备信息
+ packBlock image.Image // 背包信息
+ )
+ wg.Add(1)
+ // 绘制ID
+ go func() {
+ defer wg.Done()
+ if equipInfo == (equip{}) {
+ equipBlock, err = drawEquipEmptyBlock(fontdata)
+ } else {
+ equipBlock, err = drawEquipInfoBlock(equipInfo, fontdata)
+ }
+ if err != nil {
+ return
+ }
+ }()
+ wg.Add(1)
+ // 绘制基本信息
+ go func() {
+ defer wg.Done()
+ if len(articles) == 0 {
+ packBlock, err = drawArticleEmptyBlock(fontdata)
+ } else {
+ packBlock, err = drawArticleInfoBlock(uid, articles, fontdata)
+ }
+ if err != nil {
+ return
+ }
+ }()
+ wg.Wait()
+ if equipBlock == nil || packBlock == nil {
+ err = errors.New("生成图片失败,数据缺失")
+ return
+ }
+ // 计算图片高度
+ backDX := 1020
+ backDY := 10 + equipBlock.Bounds().Dy() + 10 + packBlock.Bounds().Dy() + 10
+ canvas := gg.NewContext(backDX, backDY)
+
+ // 画底色
+ canvas.DrawRectangle(0, 0, float64(backDX), float64(backDY))
+ canvas.SetRGBA255(150, 150, 150, 255)
+ canvas.Fill()
+ canvas.DrawRectangle(10, 10, float64(backDX-20), float64(backDY-20))
+ canvas.SetRGBA255(255, 255, 255, 255)
+ canvas.Fill()
+
+ canvas.DrawImage(equipBlock, 10, 10)
+ canvas.DrawImage(packBlock, 10, 10+equipBlock.Bounds().Dy()+10)
+
+ return imgfactory.ToBytes(canvas.Image())
+}
+
+// 绘制装备栏区域
+func drawEquipEmptyBlock(fontdata []byte) (image.Image, error) {
+ canvas := gg.NewContext(1000, 300)
+ // 画底色
+ canvas.DrawRectangle(0, 0, 1000, 300)
+ canvas.SetRGBA255(255, 255, 255, 150)
+ canvas.Fill()
+ // 边框框
+ canvas.DrawRectangle(0, 0, 1000, 300)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ canvas.SetColor(color.Black)
+ err := canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ textW, textH := canvas.MeasureString("装备信息")
+ canvas.DrawString("装备信息", 10, 10+textH)
+ canvas.DrawLine(10, textH*1.2, textW, textH*1.2)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+ if err = canvas.ParseFontFace(fontdata, 50); err != nil {
+ return nil, err
+ }
+ canvas.DrawString("没有装备任何鱼竿", 50, 10+textH*2+50)
+ return canvas.Image(), nil
+}
+func drawEquipInfoBlock(equipInfo equip, fontdata []byte) (image.Image, error) {
+ canvas := gg.NewContext(1, 1)
+ err := canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ _, titleH := canvas.MeasureString("装备信息")
+ err = canvas.ParseFontFace(fontdata, 50)
+ if err != nil {
+ return nil, err
+ }
+ _, textH := canvas.MeasureString("装备信息")
+
+ backDY := math.Max(int(10+titleH*2+(textH*2)*4+10), 300)
+
+ canvas = gg.NewContext(1000, backDY)
+ // 画底色
+ canvas.DrawRectangle(0, 0, 1000, float64(backDY))
+ canvas.SetRGBA255(255, 255, 255, 150)
+ canvas.Fill()
+ // 边框框
+ canvas.DrawRectangle(0, 0, 1000, float64(backDY))
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+ getAvatar, err := engine.GetLazyData(equipInfo.Equip+".png", false)
+ if err != nil {
+ return nil, err
+ }
+ equipPic, _, err := image.Decode(bytes.NewReader(getAvatar))
+ if err != nil {
+ return nil, err
+ }
+ picDy := float64(backDY) - 10 - titleH*2
+ equipPic = imgfactory.Size(equipPic, int(picDy)-10, int(picDy)-10).Image()
+ canvas.DrawImage(equipPic, 10, 10+int(titleH)*2)
+
+ // 放字
+ canvas.SetColor(color.Black)
+ if err = canvas.ParseFontFace(fontdata, 100); err != nil {
+ return nil, err
+ }
+ titleW, titleH := canvas.MeasureString("装备信息")
+ canvas.DrawString("装备信息", 10, 10+titleH*1.2)
+ canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ textDx := picDy + 10
+ textDy := 10 + titleH*2
+ if err = canvas.ParseFontFace(fontdata, 75); err != nil {
+ return nil, err
+ }
+ textW, textH := canvas.MeasureString(equipInfo.Equip)
+ canvas.DrawStringAnchored(equipInfo.Equip, textDx+textW/2, textDy+textH/2, 0.5, 0.5)
+
+ textDy += textH * 1.5
+ if err = canvas.ParseFontFace(fontdata, 50); err != nil {
+ return nil, err
+ }
+ textW, textH = canvas.MeasureString("维修次数")
+ durable := strconv.Itoa(equipInfo.Durable)
+ valueW, _ := canvas.MeasureString("100")
+ barW := 1000 - textDx - textW - 10 - valueW - 10
+
+ canvas.DrawStringAnchored("装备耐久", textDx+textW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawRectangle(textDx+textW+5, textDy, barW, textH*1.2)
+ canvas.SetRGB255(150, 150, 150)
+ canvas.Fill()
+ canvas.SetRGB255(0, 0, 0)
+ durableW := barW * float64(equipInfo.Durable) / float64(durationList[equipInfo.Equip])
+ canvas.DrawRectangle(textDx+textW+5, textDy, durableW, textH*1.2)
+ canvas.SetRGB255(102, 102, 102)
+ canvas.Fill()
+ canvas.SetColor(color.Black)
+ canvas.DrawStringAnchored(durable, textDx+textW+5+barW+5+valueW/2, textDy+textH/2, 0.5, 0.5)
+
+ textDy += textH * 2
+ maintenance := strconv.Itoa(equipInfo.Maintenance)
+ canvas.DrawStringAnchored("维修次数", textDx+textW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawRectangle(textDx+textW+5, textDy, barW, textH*1.2)
+ canvas.SetRGB255(150, 150, 150)
+ canvas.Fill()
+ canvas.SetRGB255(0, 0, 0)
+ canvas.DrawRectangle(textDx+textW+5, textDy, barW*float64(equipInfo.Maintenance)/10, textH*1.2)
+ canvas.SetRGB255(102, 102, 102)
+ canvas.Fill()
+ canvas.SetColor(color.Black)
+ canvas.DrawStringAnchored(maintenance, textDx+textW+5+barW+5+valueW/2, textDy+textH/2, 0.5, 0.5)
+
+ textDy += textH * 3
+ canvas.DrawString(" 附魔: 诱钓"+enchantLevel[equipInfo.Induce]+" 海之眷顾"+enchantLevel[equipInfo.Favor], textDx, textDy)
+ return canvas.Image(), nil
+}
+
+// 绘制背包信息区域
+func drawArticleEmptyBlock(fontdata []byte) (image.Image, error) {
+ canvas := gg.NewContext(1000, 300)
+ // 画底色
+ canvas.DrawRectangle(0, 0, 1000, 300)
+ canvas.SetRGBA255(255, 255, 255, 150)
+ canvas.Fill()
+ // 边框框
+ canvas.DrawRectangle(0, 0, 1000, 300)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ canvas.SetColor(color.Black)
+ err := canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ textW, textH := canvas.MeasureString("背包信息")
+ canvas.DrawString("背包信息", 10, 10+textH*1.2)
+ canvas.DrawLine(10, textH*1.6, textW, textH*1.6)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+ if err = canvas.ParseFontFace(fontdata, 50); err != nil {
+ return nil, err
+ }
+ canvas.DrawStringAnchored("背包没有存放任何东西", 500, 10+textH*2+50, 0.5, 0)
+ return canvas.Image(), nil
+}
+func drawArticleInfoBlock(uid int64, articles []article, fontdata []byte) (image.Image, error) {
+ canvas := gg.NewContext(1, 1)
+ err := canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ titleW, titleH := canvas.MeasureString("背包信息")
+ front := 45.0
+ err = canvas.ParseFontFace(fontdata, front)
+ if err != nil {
+ return nil, err
+ }
+ _, textH := canvas.MeasureString("高度")
+
+ nameWOfFiest := 0.0
+ nameWOfSecond := 0.0
+ for i, info := range articles {
+ textW, _ := canvas.MeasureString(info.Name + "(" + info.Other + ")")
+ if i%2 == 0 && textW > nameWOfFiest {
+ nameWOfFiest = textW
+ } else if textW > nameWOfSecond {
+ nameWOfSecond = textW
+ }
+ }
+ valueW, _ := canvas.MeasureString("10000")
+
+ if (10+nameWOfFiest+10+valueW+10)+(10+nameWOfSecond+10+valueW+10) > 980 {
+ front = 32.0
+ err = canvas.ParseFontFace(fontdata, front)
+ if err != nil {
+ return nil, err
+ }
+ _, textH = canvas.MeasureString("高度")
+
+ nameWOfFiest = 0
+ nameWOfSecond = 0
+ for i, info := range articles {
+ textW, _ := canvas.MeasureString(info.Name + "(" + info.Other + ")")
+ if i%2 == 0 && textW > nameWOfFiest {
+ nameWOfFiest = textW
+ } else if textW > nameWOfSecond {
+ nameWOfSecond = textW
+ }
+ }
+ valueW, _ = canvas.MeasureString("10000")
+ }
+ wallW := (980 - (10 + nameWOfFiest + 10 + valueW + 10) - (10 + nameWOfSecond + 10 + valueW + 10)) / 2
+ backY := math.Max(10+int(titleH*1.6)+10+int(textH*2)*(math.Ceil(len(articles), 2)+1), 500)
+ canvas = gg.NewContext(1000, backY)
+ // 画底色
+ canvas.DrawRectangle(0, 0, 1000, float64(backY))
+ canvas.SetRGBA255(255, 255, 255, 150)
+ canvas.Fill()
+ // 边框框
+ canvas.DrawRectangle(0, 0, 1000, float64(backY))
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ // 放字
+ canvas.SetColor(color.Black)
+ err = canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ canvas.DrawString("背包信息", 10, 10+titleH*1.2)
+ canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ textDy := 10 + titleH*1.7
+ if err = canvas.ParseFontFace(fontdata, front); err != nil {
+ return nil, err
+ }
+ canvas.SetColor(color.Black)
+ numberOfFish := 0
+ numberOfEquip := 0
+ canvas.DrawStringAnchored("名称", wallW+20+nameWOfFiest/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored("数量", wallW+20+nameWOfFiest+10+valueW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored("名称", wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored("数量", wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond+10+valueW/2, textDy+textH/2, 0.5, 0.5)
+ textDy += textH * 2
+ for i, info := range articles {
+ name := info.Name
+ if info.Other != "" {
+ if strings.Contains(info.Name, "竿") {
+ numberOfEquip++
+ }
+ name += "(" + info.Other + ")"
+ } else if strings.Contains(name, "鱼") {
+ numberOfFish += info.Number
+ }
+ valueStr := strconv.Itoa(info.Number)
+ if i%2 == 0 {
+ if i != 0 {
+ textDy += textH * 2
+ }
+ canvas.DrawStringAnchored(name, wallW+20+nameWOfFiest/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored(valueStr, wallW+20+nameWOfFiest+10+valueW/2, textDy+textH/2, 0.5, 0.5)
+ } else {
+ canvas.DrawStringAnchored(name, wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored(valueStr, wallW+20+nameWOfFiest+10+valueW+10+10+nameWOfSecond+10+valueW/2, textDy+textH/2, 0.5, 0.5)
+ }
+ }
+ if err = canvas.ParseFontFace(fontdata, 30); err != nil {
+ return nil, err
+ }
+ textDy = 10
+ text := "钱包余额: " + strconv.Itoa(wallet.GetWalletOf(uid))
+ textW, textH := canvas.MeasureString(text)
+ w, _ := canvas.MeasureString("维修大师[已激活]")
+ if w > textW {
+ textW = w
+ }
+ canvas.DrawStringAnchored(text, 980-textW/2, textDy+textH/2, 0.5, 0.5)
+ textDy += textH * 1.5
+ if numberOfFish > 100 {
+ canvas.DrawStringAnchored("钓鱼佬[已激活]", 980-textW/2, textDy+textH/2, 0.5, 0.5)
+ textDy += textH * 1.5
+ }
+ if numberOfEquip > 10 {
+ canvas.DrawStringAnchored("维修大师[已激活]", 980-textW/2, textDy+textH/2, 0.5, 0.5)
+ }
+ return canvas.Image(), nil
+}
diff --git a/plugin/mcfish/pole.go b/plugin/mcfish/pole.go
new file mode 100644
index 0000000000..ec4e08fdbe
--- /dev/null
+++ b/plugin/mcfish/pole.go
@@ -0,0 +1,485 @@
+// Package mcfish 钓鱼模拟器
+package mcfish
+
+import (
+ "math/rand"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/FloatTech/zbputils/ctxext"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+func init() {
+ engine.OnRegex(`^装备(`+strings.Join(poleList, "|")+`)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ equipInfo, err := dbdata.getUserEquip(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.1]:", err))
+ return
+ }
+ thingName := ctx.State["regex_matched"].([]string)[1]
+ articles, err := dbdata.getUserThingInfo(uid, thingName)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.2]:", err))
+ return
+ }
+ if len(articles) == 0 {
+ ctx.SendChain(message.Text("你的背包不存在该物品"))
+ return
+ }
+ poles := make([]equip, 0, len(articles))
+ if thingName != "美西螈" {
+ for _, info := range articles {
+ poleInfo := strings.Split(info.Other, "/")
+ durable, _ := strconv.Atoi(poleInfo[0])
+ maintenance, _ := strconv.Atoi(poleInfo[1])
+ induceLevel, _ := strconv.Atoi(poleInfo[2])
+ favorLevel, _ := strconv.Atoi(poleInfo[3])
+ poles = append(poles, equip{
+ ID: uid,
+ Equip: info.Name,
+ Durable: durable,
+ Maintenance: maintenance,
+ Induce: induceLevel,
+ Favor: favorLevel,
+ })
+ }
+ } else {
+ poles = append(poles, equip{
+ ID: uid,
+ Equip: thingName,
+ Durable: 999,
+ })
+ }
+ check := false
+ index := 0
+ if len(poles) > 1 {
+ msg := make(message.Message, 0, 3+len(articles))
+ msg = append(msg, message.Reply(ctx.Event.MessageID), message.Text("找到以下鱼竿:\n"))
+ for i, info := range poles {
+ msg = append(msg, message.Text("[", i, "] ", info.Equip, " : 耐", info.Durable, "/修", info.Maintenance,
+ "/诱", enchantLevel[info.Induce], "/眷顾", enchantLevel[info.Favor], "\n"))
+ }
+ msg = append(msg, message.Text("————————\n输入对应序号进行装备,或回复“取消”取消"))
+ ctx.Send(msg)
+ // 等待用户下一步选择
+ recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel()
+ for {
+ select {
+ case <-time.After(time.Second * 120):
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("等待超时,取消装备"),
+ ),
+ )
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "取消" {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("已取消装备"),
+ ),
+ )
+ return
+ }
+ index, err = strconv.Atoi(nextcmd)
+ if err != nil || index > len(articles)-1 {
+ ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号"))
+ continue
+ }
+ check = true
+ }
+ if check {
+ break
+ }
+ }
+ }
+ newEquipInfo := poles[index]
+ packEquip := articles[index]
+ packEquip.Number--
+ err = dbdata.updateUserThingInfo(uid, packEquip)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.3]:", err))
+ return
+ }
+ err = dbdata.updateUserEquip(newEquipInfo)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.3.1]:", err))
+ return
+ }
+ oldthing := article{}
+ if equipInfo != (equip{}) && equipInfo.Equip != "美西螈" {
+ oldthing = article{
+ Duration: time.Now().Unix(),
+ Type: "pole",
+ Name: equipInfo.Equip,
+ Number: 1,
+ Other: strconv.Itoa(equipInfo.Durable) + "/" + strconv.Itoa(equipInfo.Maintenance) + "/" + strconv.Itoa(equipInfo.Induce) + "/" + strconv.Itoa(equipInfo.Favor),
+ }
+ } else if equipInfo.Equip == "美西螈" {
+ articles, err = dbdata.getUserThingInfo(uid, "美西螈")
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.3.2]:", err))
+ return
+ }
+ if len(articles) == 0 {
+ oldthing = article{
+ Duration: time.Now().Unix(),
+ Type: "pole",
+ Name: equipInfo.Equip,
+ Number: 1,
+ }
+ } else {
+ oldthing = articles[0]
+ oldthing.Number++
+ }
+ }
+ err = dbdata.updateUserThingInfo(uid, oldthing)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.4]:", err))
+ return
+ }
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("装备成功"),
+ ),
+ )
+ })
+ engine.OnFullMatchGroup([]string{"修复鱼竿", "维修鱼竿"}, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ equipInfo, err := dbdata.getUserEquip(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.5]:", err))
+ return
+ }
+ if equipInfo.Equip == "" || equipInfo.Equip == "美西螈" {
+ ctx.SendChain(message.Text("仅能修复装备中的鱼竿"))
+ return
+ }
+ if equipInfo.Maintenance >= 10 {
+ ctx.SendChain(message.Text("装备的鱼竿已经达到修复上限"))
+ return
+ }
+ articles, err := dbdata.getUserThingInfo(uid, equipInfo.Equip)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.6]:", err))
+ return
+ }
+ if len(articles) == 0 {
+ ctx.SendChain(message.Text("你的背包不存在相同鱼竿进行修复"))
+ return
+ }
+ poles := make([]equip, 0, len(articles))
+ for _, info := range articles {
+ poleInfo := strings.Split(info.Other, "/")
+ durable, _ := strconv.Atoi(poleInfo[0])
+ maintenance, _ := strconv.Atoi(poleInfo[1])
+ induceLevel, _ := strconv.Atoi(poleInfo[2])
+ favorLevel, _ := strconv.Atoi(poleInfo[3])
+ poles = append(poles, equip{
+ ID: uid,
+ Equip: info.Name,
+ Durable: durable,
+ Maintenance: maintenance,
+ Induce: induceLevel,
+ Favor: favorLevel,
+ })
+ }
+ index := 0
+ check := false
+ if len(articles) > 1 {
+ msg := make(message.Message, 0, 3+len(articles))
+ msg = append(msg, message.Text("找到以下鱼竿:\n"))
+ for i, info := range poles {
+ msg = append(msg, message.Text("[", i, "] ", info.Equip, " : 耐", info.Durable, "/修", info.Maintenance,
+ "/诱", enchantLevel[info.Induce], "/眷顾", enchantLevel[info.Favor], "\n"))
+ }
+ msg = append(msg, message.Text("————————\n输入对应序号进行修复,或回复“取消”取消"))
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, msg...))
+ // 等待用户下一步选择
+ recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel()
+ for {
+ select {
+ case <-time.After(time.Second * 120):
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("等待超时,取消修复"),
+ ),
+ )
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "取消" {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("已取消修复"),
+ ),
+ )
+ return
+ }
+ index, err = strconv.Atoi(nextcmd)
+ if err != nil || index > len(articles)-1 {
+ ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号"))
+ continue
+ }
+ check = true
+ }
+ if check {
+ break
+ }
+ }
+ }
+ newEquipInfo := poles[index]
+ number, err := dbdata.getNumberFor(uid, "竿")
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at fish.go.5.1]:", err))
+ return
+ }
+ if number <= 10 {
+ number = 8
+ } else {
+ number = 10
+ }
+ equipInfo.Durable += newEquipInfo.Durable * number / 10
+ if equipInfo.Durable > durationList[equipInfo.Equip] || equipInfo.Equip == "三叉戟" {
+ equipInfo.Durable = durationList[equipInfo.Equip]
+ }
+ msg := ""
+ if newEquipInfo.Induce != 0 && rand.Intn(100) < 50 {
+ equipInfo.Induce += newEquipInfo.Induce
+ if equipInfo.Induce > 3 {
+ equipInfo.Induce = 3
+ }
+ msg += ",诱钓等级提升至" + enchantLevel[equipInfo.Induce]
+ }
+ if newEquipInfo.Favor != 0 && rand.Intn(100) < 50 {
+ equipInfo.Favor += newEquipInfo.Favor
+ if equipInfo.Favor > 3 {
+ equipInfo.Favor = 3
+ }
+ msg += ",海之眷顾等级提升至" + enchantLevel[equipInfo.Favor]
+ }
+ thingInfo := articles[index]
+ thingInfo.Number = 0
+ err = dbdata.updateUserThingInfo(uid, thingInfo)
+ if err == nil {
+ equipInfo.Maintenance++
+ err = dbdata.updateUserEquip(equipInfo)
+ }
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.7]:", err))
+ return
+ }
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("鱼竿修复成功,耐久提高至", equipInfo.Durable, msg),
+ ),
+ )
+ })
+ engine.OnRegex(`^附魔(诱钓|海之眷顾)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ equipInfo, err := dbdata.getUserEquip(uid)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.7]:", err))
+ return
+ }
+ if equipInfo.Equip == "" || equipInfo.Equip == "美西螈" {
+ ctx.SendChain(message.Text("仅可对装备中的进行附魔"))
+ return
+ }
+ book := ctx.State["regex_matched"].([]string)[1]
+ books, err := dbdata.getUserThingInfo(uid, book)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.8]:", err))
+ return
+ }
+ if len(books) == 0 {
+ ctx.SendChain(message.Text("你的背包不存在", book, "进行附魔"))
+ return
+ }
+ bookInfo := books[0]
+ bookInfo.Number--
+ err = dbdata.updateUserThingInfo(uid, bookInfo)
+ number := 0
+ if err == nil {
+ if rand.Intn(100) > 50 {
+ ctx.SendChain(message.Text("附魔失败了"))
+ return
+ }
+ switch book {
+ case "诱钓":
+ equipInfo.Induce++
+ if equipInfo.Induce > 3 {
+ equipInfo.Induce = 3
+ }
+ number = equipInfo.Induce
+ case "海之眷顾":
+ equipInfo.Favor++
+ if equipInfo.Favor > 3 {
+ equipInfo.Favor = 3
+ }
+ number = equipInfo.Favor
+ default:
+ ctx.SendChain(message.Text("附魔失败了"))
+ return
+ }
+ err = dbdata.updateUserEquip(equipInfo)
+ }
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.9]:", err))
+ return
+ }
+ ctx.SendChain(message.Text("附魔成功,", book, "等级提高至", enchantLevel[number]))
+ })
+ engine.OnRegex(`^合成(.+竿|三叉戟)$`, getdb).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ thingList := []string{"木竿", "铁竿", "金竿", "钻石竿", "下界合金竿", "三叉戟"}
+ thingName := ctx.State["regex_matched"].([]string)[1]
+ indexOfMaterial := -1
+ for i, name := range thingList {
+ if thingName == name {
+ indexOfMaterial = (i - 1)
+ break
+ }
+ }
+ if indexOfMaterial < 0 {
+ return
+ }
+ articles, err := dbdata.getUserThingInfo(uid, thingList[indexOfMaterial])
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.10]:", err))
+ return
+ }
+ max := len(articles)
+ if max < 3 {
+ ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("你的合成材料不足"))
+ return
+ }
+ poles := make([]equip, 0, max)
+ for _, info := range articles {
+ poleInfo := strings.Split(info.Other, "/")
+ durable, _ := strconv.Atoi(poleInfo[0])
+ maintenance, _ := strconv.Atoi(poleInfo[1])
+ induceLevel, _ := strconv.Atoi(poleInfo[2])
+ favorLevel, _ := strconv.Atoi(poleInfo[3])
+ poles = append(poles, equip{
+ ID: uid,
+ Equip: info.Name,
+ Durable: durable,
+ Maintenance: maintenance,
+ Induce: induceLevel,
+ Favor: favorLevel,
+ })
+ }
+ list := []int{0, 1, 2}
+ check := false
+ if len(articles) > 3 {
+ msg := make(message.Message, 0, 3+len(articles))
+ msg = append(msg, message.Text("找到以下鱼竿:\n"))
+ for i, info := range poles {
+ msg = append(msg, message.Text("[", i, "] ", info.Equip, " : 耐", info.Durable, "/修", info.Maintenance,
+ "/诱", enchantLevel[info.Induce], "/眷顾", enchantLevel[info.Favor], "\n"))
+ }
+ msg = append(msg, message.Text("————————\n输入3个序号进行合成(用空格分割),或回复“取消”取消"))
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, msg...))
+ // 等待用户下一步选择
+ recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+ \d+ \d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel()
+ for {
+ select {
+ case <-time.After(time.Second * 120):
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("等待超时,取消合成"),
+ ),
+ )
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "取消" {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("已取消合成"),
+ ),
+ )
+ return
+ }
+ chooseList := strings.Split(nextcmd, " ")
+ if list[0] == list[1] || list[0] == list[2] || list[1] == list[2] {
+ ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[0]请输入正确的序号\n", list))
+ continue
+ }
+ first, err := strconv.Atoi(chooseList[0])
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.11.1]:", err))
+ return
+ }
+ second, err := strconv.Atoi(chooseList[1])
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.11.2]:", err))
+ return
+ }
+ third, err := strconv.Atoi(chooseList[2])
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.11.3]:", err))
+ return
+ }
+ if first > max || second > max || third > max {
+ ctx.SendChain(message.At(ctx.Event.UserID), message.Text("[", max, "]请输入正确的序号\n", list))
+ continue
+ }
+ list = []int{first, second, third}
+ check = true
+ }
+ if check {
+ break
+ }
+ }
+ }
+ favorLevel := 0
+ induceLevel := 0
+ for _, index := range list {
+ thingInfo := articles[index]
+ thingInfo.Number = 0
+ err = dbdata.updateUserThingInfo(uid, thingInfo)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.12]:", err))
+ return
+ }
+ favorLevel += poles[index].Favor
+ induceLevel += poles[index].Induce
+ }
+ if rand.Intn(100) >= 90 {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("合成失败,材料已销毁"),
+ ),
+ )
+ return
+ }
+ attribute := strconv.Itoa(durationList[thingName]) + "/0/" + strconv.Itoa(induceLevel/3) + "/" + strconv.Itoa(favorLevel/3)
+ newthing := article{
+ Duration: time.Now().Unix(),
+ Type: "pole",
+ Name: thingName,
+ Number: 1,
+ Other: attribute,
+ }
+ err = dbdata.updateUserThingInfo(uid, newthing)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at pole.go.12]:", err))
+ return
+ }
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text(thingName, "合成成功\n属性: ", attribute),
+ ),
+ )
+ })
+}
diff --git a/plugin/mcfish/store.go b/plugin/mcfish/store.go
new file mode 100644
index 0000000000..28cc622c15
--- /dev/null
+++ b/plugin/mcfish/store.go
@@ -0,0 +1,599 @@
+// Package mcfish 钓鱼模拟器
+package mcfish
+
+import (
+ "image"
+ "image/color"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/FloatTech/AnimeAPI/wallet"
+ "github.com/FloatTech/floatbox/file"
+ "github.com/FloatTech/floatbox/math"
+ "github.com/FloatTech/gg"
+ "github.com/FloatTech/imgfactory"
+ "github.com/FloatTech/zbputils/control"
+ "github.com/FloatTech/zbputils/ctxext"
+ "github.com/FloatTech/zbputils/img/text"
+ zero "github.com/wdvxdr1123/ZeroBot"
+ "github.com/wdvxdr1123/ZeroBot/message"
+)
+
+var (
+ refresh = false
+ timeNow = 0
+ refreshFish = func(ctx *zero.Ctx) bool {
+ if refresh && timeNow == time.Now().Day() {
+ return true
+ }
+ refresh, err := dbdata.refreshStroeInfo()
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.1]:", err))
+ return refresh
+ }
+ timeNow = time.Now().Day()
+ return refresh
+ }
+)
+
+func init() {
+ engine.OnFullMatchGroup([]string{"钓鱼看板", "钓鱼商店"}, getdb, refreshFish).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ infos, err := dbdata.getStoreInfo()
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.2]:", err))
+ return
+ }
+ var picImage image.Image
+ if len(infos) == 0 {
+ picImage, err = drawStroeEmptyImage()
+ } else {
+ picImage, err = drawStroeInfoImage(infos)
+ }
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.3]:", err))
+ return
+ }
+ pic, err := imgfactory.ToBytes(picImage)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.4]:", err))
+ return
+ }
+ ctx.SendChain(message.ImageBytes(pic))
+ })
+ engine.OnRegex(`^出售(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ thingName := ctx.State["regex_matched"].([]string)[1]
+ number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
+ if number == 0 {
+ number = 1
+ }
+ articles, err := dbdata.getUserThingInfo(uid, thingName)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.5]:", err))
+ return
+ }
+ if len(articles) == 0 {
+ ctx.SendChain(message.Text("你的背包不存在该物品"))
+ return
+ }
+ index := 0
+ thing := article{}
+ if len(articles) > 1 {
+ msg := make(message.Message, 0, 3+len(articles))
+ msg = append(msg, message.Reply(ctx.Event.MessageID), message.Text("找到以下物品:\n"))
+ for i, info := range articles {
+ if info.Other != "" && info.Name != "美西螈" {
+ msg = append(msg, message.Text("[", i, "] ", info.Name, "(", info.Other, ")\n"))
+ } else {
+ msg = append(msg, message.Text(
+ "[", i, "]", info.Name, " 数量: ", info.Number, "\n"))
+ }
+ }
+ msg = append(msg, message.Reply(ctx.Event.MessageID), message.Text("————————\n输入对应序号进行装备,或回复“取消”取消"))
+ ctx.Send(msg)
+ // 等待用户下一步选择
+ sell := false
+ recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel()
+ for {
+ select {
+ case <-time.After(time.Second * 120):
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("等待超时,取消出售"),
+ ),
+ )
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "取消" {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("已取消出售"),
+ ),
+ )
+ return
+ }
+ index, err = strconv.Atoi(nextcmd)
+ if err != nil || index > len(articles)-1 {
+ ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号"))
+ continue
+ }
+ sell = true
+ }
+ if sell {
+ break
+ }
+ }
+ }
+
+ thing = articles[index]
+ if thing.Number < number {
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("背包数量不足")))
+ return
+ }
+
+ var pice int
+ if strings.Contains(thingName, "竿") || thingName == "三叉戟" {
+ poleInfo := strings.Split(articles[index].Other, "/")
+ durable, _ := strconv.Atoi(poleInfo[0])
+ maintenance, _ := strconv.Atoi(poleInfo[1])
+ induceLevel, _ := strconv.Atoi(poleInfo[2])
+ favorLevel, _ := strconv.Atoi(poleInfo[3])
+ pice = (priceList[thingName] - (durationList[thingName] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[thingName] / 100
+ } else {
+ pice = priceList[thingName] * discountList[thingName] / 100
+ }
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("是否接受商店将以", pice*number*8/10, "收购", number, "个", thingName, "?\n回答\"是\"或\"否\"")))
+ // 等待用户下一步选择
+ recv, cancel1 := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel1()
+ buy := false
+ for {
+ select {
+ case <-time.After(time.Second * 60):
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("等待超时,取消钓鱼")))
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "否" {
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已取消购买")))
+ return
+ }
+ buy = true
+ }
+ if buy {
+ break
+ }
+ }
+
+ records, err := dbdata.getUserThingInfo(uid, "唱片")
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.9.1]:", err))
+ return
+ }
+ if len(records) != 0 {
+ recordInfo := records[0]
+ numberOfRecord := recordInfo.Number
+ if thingName == "唱片" {
+ numberOfRecord--
+ }
+ if numberOfRecord > 0 {
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("是否使用唱片让价格翻倍?\n回答\"是\"或\"否\"")))
+ // 等待用户下一步选择
+ recv, cancel2 := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel2()
+ use := false
+ checkTime := false
+ for {
+ select {
+ case <-time.After(time.Second * 60):
+ checkTime = true
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "是" {
+ use = true
+ }
+ checkTime = true
+ }
+ if checkTime {
+ break
+ }
+ }
+ if use {
+ pice *= 2
+ recordInfo.Number--
+ err = dbdata.updateUserThingInfo(uid, recordInfo)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.9.2]:", err))
+ return
+ }
+ }
+ }
+ }
+ thing.Number -= number
+ err = dbdata.updateUserThingInfo(uid, thing)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.6]:", err))
+ return
+ }
+ newCommodity := store{}
+ if strings.Contains(thingName, "竿") || thingName == "三叉戟" {
+ if pice >= priceList[thingName]*4/5 { // 不值钱的删了
+ newCommodity = store{
+ Duration: time.Now().Unix(),
+ Type: "pole",
+ Name: thingName,
+ Number: 1,
+ Price: pice,
+ Other: thing.Other,
+ }
+ }
+ } else {
+ things, err1 := dbdata.getStoreThingInfo(thingName)
+ if err1 != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.8]:", err1))
+ return
+ }
+ if len(things) == 0 {
+ things = append(things, store{
+ Duration: time.Now().Unix(),
+ Name: thingName,
+ Number: 0,
+ Price: pice,
+ })
+ switch {
+ case thingName == "海之眷顾" || thingName == "诱钓" || thingName == "唱片":
+ things[0].Type = "article"
+ case thingName == "美西螈":
+ things[0].Type = "pole"
+ default:
+ things[0].Type = "fish"
+ }
+ }
+ newCommodity = things[0]
+ newCommodity.Number += number
+ }
+ err = dbdata.updateStoreInfo(newCommodity)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.9]:", err))
+ return
+ }
+ pice = pice * 8 / 10
+ err = wallet.InsertWalletOf(uid, pice*number)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.10]:", err))
+ return
+ }
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("出售成功,你赚到了", pice*number)))
+ })
+ engine.OnRegex(`^购买(`+strings.Join(thingList, "|")+`)\s*(\d*)$`, getdb, refreshFish).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
+ uid := ctx.Event.UserID
+ thingName := ctx.State["regex_matched"].([]string)[1]
+ number, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
+ if number == 0 {
+ number = 1
+ }
+ thingInfos, err := dbdata.getStoreThingInfo(thingName)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.11]:", err))
+ return
+ }
+ if len(thingInfos) == 0 {
+ ctx.SendChain(message.Text("当前商店并没有上架该物品"))
+ return
+ }
+ index := 0
+ pice := make([]int, 0, len(thingInfos))
+ for _, info := range thingInfos {
+ if strings.Contains(thingName, "竿") || thingName == "三叉戟" {
+ poleInfo := strings.Split(info.Other, "/")
+ durable, _ := strconv.Atoi(poleInfo[0])
+ maintenance, _ := strconv.Atoi(poleInfo[1])
+ induceLevel, _ := strconv.Atoi(poleInfo[2])
+ favorLevel, _ := strconv.Atoi(poleInfo[3])
+ thingPice := (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100
+ pice = append(pice, thingPice)
+ } else {
+ thingPice := priceList[info.Name] * discountList[info.Name] / 100
+ pice = append(pice, thingPice)
+ }
+ }
+ if len(thingInfos) > 1 {
+ msg := make(message.Message, 0, 3+len(thingInfos))
+ msg = append(msg, message.Text("找到以下物品:\n"))
+ for i, info := range thingInfos {
+ if strings.Contains(thingName, "竿") || thingName == "三叉戟" {
+ msg = append(msg, message.Text(
+ "[", i, "]", info.Name, "(", info.Other, ") 价格:", pice[i], "\n"))
+ } else {
+ msg = append(msg, message.Text(
+ "[", i, "]", info.Name, " 数量:", info.Number, " 价格:", pice[i], "\n"))
+ }
+ }
+ msg = append(msg, message.Text("————————\n输入对应序号进行装备,或回复“取消”取消"))
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, msg...))
+ // 等待用户下一步选择
+ sell := false
+ recv, cancel := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(取消|\d+)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel()
+ for {
+ select {
+ case <-time.After(time.Second * 120):
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("等待超时,取消购买"),
+ ),
+ )
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "取消" {
+ ctx.Send(
+ message.ReplyWithMessage(ctx.Event.MessageID,
+ message.Text("已取消购买"),
+ ),
+ )
+ return
+ }
+ index, err = strconv.Atoi(nextcmd)
+ if err != nil || index > len(thingInfos)-1 {
+ ctx.SendChain(message.At(ctx.Event.UserID), message.Text("请输入正确的序号"))
+ continue
+ }
+ sell = true
+ }
+ if sell {
+ break
+ }
+ }
+ }
+
+ thing := thingInfos[index]
+ if thing.Number < number {
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("商店数量不足")))
+ return
+ }
+
+ money := wallet.GetWalletOf(uid)
+ if money < pice[index]*number {
+ ctx.SendChain(message.Text("你身上的钱(", money, ")不够支付"))
+ return
+ }
+
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你确定花费", pice[index]*number, "购买", number, "个", thingName, "?\n回答\"是\"或\"否\"")))
+ // 等待用户下一步选择
+ recv, cancel1 := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^(是|否)$`), zero.CheckUser(ctx.Event.UserID)).Repeat()
+ defer cancel1()
+ buy := false
+ for {
+ select {
+ case <-time.After(time.Second * 60):
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("等待超时,取消购买")))
+ return
+ case e := <-recv:
+ nextcmd := e.Event.Message.String()
+ if nextcmd == "否" {
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已取消购买")))
+ return
+ }
+ buy = true
+ }
+ if buy {
+ break
+ }
+ }
+
+ ok, err := dbdata.checkStoreFor(thing, number)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.11]:", err))
+ return
+ }
+ if !ok {
+ ctx.SendChain(message.Text("你慢了一步,物品被别人买走了"))
+ return
+ }
+ thing.Number -= number
+ err = dbdata.updateStoreInfo(thing)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.12]:", err))
+ return
+ }
+ err = wallet.InsertWalletOf(uid, -pice[index]*number)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.13]:", err))
+ return
+ }
+ newCommodity := article{}
+ if strings.Contains(thingName, "竿") || thingName == "三叉戟" {
+ newCommodity = article{
+ Duration: time.Now().Unix(),
+ Type: "pole",
+ Name: thingName,
+ Number: 1,
+ Other: thing.Other,
+ }
+ } else {
+ things, err1 := dbdata.getUserThingInfo(uid, thingName)
+ if err1 != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.15]:", err1))
+ return
+ }
+ if len(things) == 0 {
+ things = append(things, article{
+ Duration: time.Now().Unix(),
+ Name: thingName,
+ Number: 0,
+ })
+ switch {
+ case thingName == "海之眷顾" || thingName == "诱钓" || thingName == "唱片":
+ things[0].Type = "article"
+ case thingName == "美西螈":
+ things[0].Type = "pole"
+ default:
+ things[0].Type = "fish"
+ }
+ }
+ newCommodity = things[0]
+ newCommodity.Number += number
+ }
+ err = dbdata.updateUserThingInfo(uid, newCommodity)
+ if err != nil {
+ ctx.SendChain(message.Text("[ERROR at store.go.14]:", err))
+ return
+ }
+ ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("购买成功")))
+ })
+}
+
+func drawStroeEmptyImage() (picImage image.Image, err error) {
+ fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
+ if err != nil {
+ return nil, err
+ }
+ canvas := gg.NewContext(1000, 300)
+ // 画底色
+ canvas.DrawRectangle(0, 0, 1000, 300)
+ canvas.SetRGBA255(255, 255, 255, 150)
+ canvas.Fill()
+ // 边框框
+ canvas.DrawRectangle(0, 0, 1000, 300)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ canvas.SetColor(color.Black)
+ err = canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ textW, textH := canvas.MeasureString("价格信息")
+ canvas.DrawString("价格信息", 10, 10+textH*1.2)
+ canvas.DrawLine(10, textH*1.6, textW, textH*1.6)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+ if err = canvas.ParseFontFace(fontdata, 50); err != nil {
+ return nil, err
+ }
+ canvas.DrawStringAnchored("当前商店并没有上架任何物品", 500, 10+textH*2+50, 0.5, 0)
+ return canvas.Image(), nil
+}
+
+func drawStroeInfoImage(stroeInfo []store) (picImage image.Image, err error) {
+ fontdata, err := file.GetLazyData(text.BoldFontFile, control.Md5File, true)
+ if err != nil {
+ return nil, err
+ }
+ canvas := gg.NewContext(1, 1)
+ err = canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ titleW, titleH := canvas.MeasureString("价格信息")
+
+ err = canvas.ParseFontFace(fontdata, 50)
+ if err != nil {
+ return nil, err
+ }
+ _, textH := canvas.MeasureString("高度")
+ nameW, _ := canvas.MeasureString("下界合金竿(100/100/0/0)")
+ numberW, _ := canvas.MeasureString("10000")
+ priceW, _ := canvas.MeasureString("10000")
+
+ bolckW := int(10 + nameW + 50 + numberW + 50 + priceW + 10)
+ backY := 10 + int(titleH*2+10)*2 + 10 + (len(stroeInfo)+len(discountList)/2+2)*int(textH*2) + 10
+ canvas = gg.NewContext(bolckW, math.Max(backY, 500))
+ // 画底色
+ canvas.DrawRectangle(0, 0, float64(bolckW), float64(backY))
+ canvas.SetRGBA255(150, 150, 150, 255)
+ canvas.Fill()
+
+ // 放字
+ canvas.SetColor(color.Black)
+ err = canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ canvas.DrawString("今日波动", 10, 10+titleH*1.2)
+ canvas.DrawLine(10, titleH*1.6, titleW, titleH*1.6)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ textDy := 10 + titleH*1.7
+ if err = canvas.ParseFontFace(fontdata, 35); err != nil {
+ return nil, err
+ }
+ textDx, textDh := canvas.MeasureString("下界合金竿(均价1000)")
+ valueDx, _ := canvas.MeasureString("+100%")
+ i := 0
+ for _, name := range thingList {
+ text := name + "(均价" + strconv.Itoa(priceList[name]) + ") "
+
+ if i == 2 {
+ i = 0
+ textDy += textDh * 2
+ }
+ canvas.SetColor(color.Black)
+ canvas.DrawStringAnchored(text, 20+(textDx+valueDx+10)*float64(i)+10, textDy+textDh/2, 0, 0.5)
+ if discountList[name]-100 > 0 {
+ canvas.SetRGBA255(200, 50, 50, 255)
+ text = "+" + strconv.Itoa(discountList[name]-100) + "%"
+ } else {
+ canvas.SetRGBA255(63, 133, 55, 255)
+ text = strconv.Itoa(discountList[name]-100) + "%"
+ }
+ canvas.DrawStringAnchored(text, 20+(textDx+valueDx+10)*float64(i)+10+textDx+10, textDy+textDh/2, 0, 0.5)
+ i++
+ }
+ canvas.SetColor(color.Black)
+ textDy += textDh * 2
+ canvas.DrawStringAnchored("注:出售商品将会额外扣除20%的税收,附魔鱼竿请按实际价格", 10, textDy+10+textDh/2, 0, 0.5)
+
+ textDy += textH * 2
+ err = canvas.ParseFontFace(fontdata, 100)
+ if err != nil {
+ return nil, err
+ }
+ canvas.DrawString("上架内容", 10, textDy+titleH*1.2)
+ canvas.DrawLine(10, textDy+titleH*1.6, titleW, textDy+titleH*1.6)
+ canvas.SetLineWidth(3)
+ canvas.SetRGBA255(0, 0, 0, 255)
+ canvas.Stroke()
+
+ textDy += 10 + titleH*1.7
+ if err = canvas.ParseFontFace(fontdata, 50); err != nil {
+ return nil, err
+ }
+
+ canvas.DrawStringAnchored("名称", 10+nameW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored("数量", 10+nameW+10+numberW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored("价格", 10+nameW+10+numberW+50+priceW/2, textDy+textH/2, 0.5, 0.5)
+
+ for _, info := range stroeInfo {
+ textDy += textH * 2
+ name := info.Name
+ if info.Other != "" && info.Name != "美西螈" {
+ name += "(" + info.Other + ")"
+ }
+ numberStr := strconv.Itoa(info.Number)
+ pice := 0
+ if strings.Contains(name, "竿") {
+ poleInfo := strings.Split(info.Other, "/")
+ durable, _ := strconv.Atoi(poleInfo[0])
+ maintenance, _ := strconv.Atoi(poleInfo[1])
+ induceLevel, _ := strconv.Atoi(poleInfo[2])
+ favorLevel, _ := strconv.Atoi(poleInfo[3])
+ pice = (priceList[info.Name] - (durationList[info.Name] - durable) - maintenance*2 + induceLevel*600 + favorLevel*1800) * discountList[info.Name] / 100
+ } else {
+ pice = priceList[info.Name] * discountList[info.Name] / 100
+ }
+
+ canvas.DrawStringAnchored(name, 10+nameW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored(numberStr, 10+nameW+10+numberW/2, textDy+textH/2, 0.5, 0.5)
+ canvas.DrawStringAnchored(strconv.Itoa(pice), 10+nameW+10+numberW+50+priceW/2, textDy+textH/2, 0.5, 0.5)
+ }
+ return canvas.Image(), nil
+}
diff --git a/plugin/midicreate/midicreate.go b/plugin/midicreate/midicreate.go
index 0e6f5eb69a..73cf1acf2c 100644
--- a/plugin/midicreate/midicreate.go
+++ b/plugin/midicreate/midicreate.go
@@ -28,7 +28,7 @@ import (
)
func init() {
- engine := control.Register("midicreate", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "midi音乐制作",
Help: "- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n" +
diff --git a/plugin/moegoe/main.go b/plugin/moegoe/main.go
index 6a4f3e8b35..5b43f17771 100644
--- a/plugin/moegoe/main.go
+++ b/plugin/moegoe/main.go
@@ -2,6 +2,8 @@
package moegoe
import (
+ "crypto/md5"
+ "encoding/hex"
"fmt"
"net/url"
@@ -9,6 +11,8 @@ import (
"github.com/wdvxdr1123/ZeroBot/message"
"github.com/FloatTech/AnimeAPI/tts/genshin"
+ "github.com/FloatTech/floatbox/binary"
+ "github.com/FloatTech/floatbox/file"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
@@ -22,18 +26,17 @@ const (
var speakers = map[string]uint{
"宁宁": 0, "爱瑠": 1, "芳乃": 2, "茉子": 3, "丛雨": 4, "小春": 5, "七海": 6,
"Sua": 0, "Mimiru": 1, "Arin": 2, "Yeonhwa": 3, "Yuhwa": 4, "Seonbae": 5,
- "派蒙": 0, "凯亚": 1, "安柏": 2, "丽莎": 3, "琴": 4, "香菱": 5, "枫原万叶": 6, "迪卢克": 7, "温迪": 8, "可莉": 9, "早柚": 10, "托马": 11, "芭芭拉": 12, "优菈": 13, "云堇": 14, "钟离": 15, "魈": 16, "凝光": 17, "雷电将军": 18, "北斗": 19, "甘雨": 20, "七七": 21, "刻晴": 22, "神里绫华": 23, "戴因斯雷布": 24, "雷泽": 25, "神里绫人": 26, "罗莎莉亚": 27, "阿贝多": 28, "八重神子": 29, "宵宫": 30, "荒泷一斗": 31, "九条裟罗": 32, "夜兰": 33, "珊瑚宫心海": 34, "五郎": 35, "散兵": 36, "女士": 37, "达达利亚": 38, "莫娜": 39, "班尼特": 40, "申鹤": 41, "行秋": 42, "烟绯": 43, "久岐忍": 44, "辛焱": 45, "砂糖": 46, "胡桃": 47, "重云": 48, "菲谢尔": 49, "诺艾尔": 50, "迪奥娜": 51, "鹿野院平藏": 52,
}
var 原 = newapikeystore("./data/tts/o.txt")
func init() {
- en := control.Register("moegoe", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "日韩中 VITS 模型拟声",
Help: "- 让[宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海]说(日语)\n" +
"- 让[Sua|Mimiru|Arin|Yeonhwa|Yuhwa|Seonbae]说(韩语)\n" +
- "- 让[派蒙|凯亚|安柏|丽莎|琴|香菱|枫原万叶|迪卢克|温迪|可莉|早柚|托马|芭芭拉|优菈|云堇|钟离|魈|凝光|雷电将军|北斗|甘雨|七七|刻晴|神里绫华|雷泽|神里绫人|罗莎莉亚|阿贝多|八重神子|宵宫|荒泷一斗|九条裟罗|夜兰|珊瑚宫心海|五郎|达达利亚|莫娜|班尼特|申鹤|行秋|烟绯|久岐忍|辛焱|砂糖|胡桃|重云|菲谢尔|诺艾尔|迪奥娜|鹿野院平藏]说(中文)",
+ "- 让[空|荧|派蒙|纳西妲|阿贝多|温迪|枫原万叶|钟离|荒泷一斗|八重神子|艾尔海森|提纳里|迪希雅|卡维|宵宫|莱依拉|赛诺|诺艾尔|托马|凝光|莫娜|北斗|神里绫华|雷电将军|芭芭拉|鹿野院平藏|五郎|迪奥娜|凯亚|安柏|班尼特|琴|柯莱|夜兰|妮露|辛焱|珐露珊|魈|香菱|达达利亚|砂糖|早柚|云堇|刻晴|丽莎|迪卢克|烟绯|重云|珊瑚宫心海|胡桃|可莉|流浪者|久岐忍|神里绫人|甘雨|戴因斯雷布|优菈|菲谢尔|行秋|白术|九条裟罗|雷泽|申鹤|迪娜泽黛|凯瑟琳|多莉|坎蒂丝|萍姥姥|罗莎莉亚|留云借风真君|绮良良|瑶瑶|七七|奥兹|米卡|夏洛蒂|埃洛伊|博士|女士|大慈树王|三月七|娜塔莎|希露瓦|虎克|克拉拉|丹恒|希儿|布洛妮娅|瓦尔特|杰帕德|佩拉|姬子|艾丝妲|白露|星|穹|桑博|伦纳德|停云|罗刹|卡芙卡|彦卿|史瓦罗|螺丝咕姆|阿兰|银狼|素裳|丹枢|黑塔|景元|帕姆|可可利亚|半夏|符玄|公输师傅|奥列格|青雀|大毫|青镞|费斯曼|绿芙蓉|镜流|信使|丽塔|失落迷迭|缭乱星棘|伊甸|伏特加女孩|狂热蓝调|莉莉娅|萝莎莉娅|八重樱|八重霞|卡莲|第六夜想曲|卡萝尔|姬子|极地战刃|布洛妮娅|次生银翼|理之律者|真理之律者|迷城骇兔|希儿|魇夜星渊|黑希儿|帕朵菲莉丝|天元骑英|幽兰黛尔|德丽莎|月下初拥|朔夜观星|暮光骑士|明日香|李素裳|格蕾修|梅比乌斯|渡鸦|人之律者|爱莉希雅|爱衣|天穹游侠|琪亚娜|空之律者|终焉之律者|薪炎之律者|云墨丹心|符华|识之律者|维尔薇|始源之律者|芽衣|雷之律者|苏莎娜|阿波尼亚|陆景和|莫弈|夏彦|左然|标贝]说(中文)",
}).ApplySingle(ctxext.DefaultSingle)
en.OnRegex("^让(宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海)说([A-Za-z\\s\\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d\\pP]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
@@ -47,13 +50,23 @@ func init() {
id := speakers[ctx.State["regex_matched"].([]string)[1]]
ctx.SendChain(message.Record(fmt.Sprintf(krapi, url.QueryEscape(text), id)))
})
- en.OnRegex("^让(派蒙|凯亚|安柏|丽莎|琴|香菱|枫原万叶|迪卢克|温迪|可莉|早柚|托马|芭芭拉|优菈|云堇|钟离|魈|凝光|雷电将军|北斗|甘雨|七七|刻晴|神里绫华|雷泽|神里绫人|罗莎莉亚|阿贝多|八重神子|宵宫|荒泷一斗|九条裟罗|夜兰|珊瑚宫心海|五郎|达达利亚|莫娜|班尼特|申鹤|行秋|烟绯|久岐忍|辛焱|砂糖|胡桃|重云|菲谢尔|诺艾尔|迪奥娜|鹿野院平藏)说([\\s\u4e00-\u9fa5\\pP]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
+ en.OnRegex("^让(空|荧|派蒙|纳西妲|阿贝多|温迪|枫原万叶|钟离|荒泷一斗|八重神子|艾尔海森|提纳里|迪希雅|卡维|宵宫|莱依拉|赛诺|诺艾尔|托马|凝光|莫娜|北斗|神里绫华|雷电将军|芭芭拉|鹿野院平藏|五郎|迪奥娜|凯亚|安柏|班尼特|琴|柯莱|夜兰|妮露|辛焱|珐露珊|魈|香菱|达达利亚|砂糖|早柚|云堇|刻晴|丽莎|迪卢克|烟绯|重云|珊瑚宫心海|胡桃|可莉|流浪者|久岐忍|神里绫人|甘雨|戴因斯雷布|优菈|菲谢尔|行秋|白术|九条裟罗|雷泽|申鹤|迪娜泽黛|凯瑟琳|多莉|坎蒂丝|萍姥姥|罗莎莉亚|留云借风真君|绮良良|瑶瑶|七七|奥兹|米卡|夏洛蒂|埃洛伊|博士|女士|大慈树王|三月七|娜塔莎|希露瓦|虎克|克拉拉|丹恒|希儿|布洛妮娅|瓦尔特|杰帕德|佩拉|姬子|艾丝妲|白露|星|穹|桑博|伦纳德|停云|罗刹|卡芙卡|彦卿|史瓦罗|螺丝咕姆|阿兰|银狼|素裳|丹枢|黑塔|景元|帕姆|可可利亚|半夏|符玄|公输师傅|奥列格|青雀|大毫|青镞|费斯曼|绿芙蓉|镜流|信使|丽塔|失落迷迭|缭乱星棘|伊甸|伏特加女孩|狂热蓝调|莉莉娅|萝莎莉娅|八重樱|八重霞|卡莲|第六夜想曲|卡萝尔|姬子|极地战刃|布洛妮娅|次生银翼|理之律者|真理之律者|迷城骇兔|希儿|魇夜星渊|黑希儿|帕朵菲莉丝|天元骑英|幽兰黛尔|德丽莎|月下初拥|朔夜观星|暮光骑士|明日香|李素裳|格蕾修|梅比乌斯|渡鸦|人之律者|爱莉希雅|爱衣|天穹游侠|琪亚娜|空之律者|终焉之律者|薪炎之律者|云墨丹心|符华|识之律者|维尔薇|始源之律者|芽衣|雷之律者|苏莎娜|阿波尼亚|陆景和|莫弈|夏彦|左然|标贝)说([\\s\u4e00-\u9fa5\\pP]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
if 原.k == "" {
return
}
text := ctx.State["regex_matched"].([]string)[2]
- id := speakers[ctx.State["regex_matched"].([]string)[1]]
- ctx.SendChain(message.Record(fmt.Sprintf(genshin.CNAPI, id, url.QueryEscape(text), url.QueryEscape(原.k))))
+ name := ctx.State["regex_matched"].([]string)[1]
+ rec := fmt.Sprintf(genshin.CNAPI, url.QueryEscape(name), url.QueryEscape(text), url.QueryEscape(原.k))
+ b := md5.Sum(binary.StringToBytes(rec))
+ fn := hex.EncodeToString(b[:])
+ fp := "data/tts/" + fn
+ if file.IsNotExist(fp) {
+ if file.DownloadTo(rec, fp) != nil {
+ return
+ }
+ }
+ rec = "file:///" + file.BOTPATH + "/" + fp
+ ctx.SendChain(message.Record(rec))
})
}
diff --git a/plugin/moyu/holiday_test.go b/plugin/moyu/holiday_test.go
index 529b311ccd..013dd52277 100644
--- a/plugin/moyu/holiday_test.go
+++ b/plugin/moyu/holiday_test.go
@@ -23,31 +23,31 @@ func TestSetHoliday(t *testing.T) {
t.Fatal(err)
}
- err = SetHoliday("元旦", 1, 2023, 1, 1)
+ err = SetHoliday("元旦", 1, 2024, 1, 1)
if err != nil {
t.Fatal(err)
}
- err = SetHoliday("春节", 7, 2023, 1, 21)
+ err = SetHoliday("春节", 7, 2024, 2, 10)
if err != nil {
t.Fatal(err)
}
- err = SetHoliday("清明节", 1, 2023, 4, 5)
+ err = SetHoliday("清明节", 1, 2024, 4, 5)
if err != nil {
t.Fatal(err)
}
- err = SetHoliday("劳动节", 1, 2023, 5, 1)
+ err = SetHoliday("劳动节", 1, 2024, 5, 1)
if err != nil {
t.Fatal(err)
}
- err = SetHoliday("端午节", 1, 2023, 6, 22)
+ err = SetHoliday("端午节", 1, 2023, 6, 10)
if err != nil {
t.Fatal(err)
}
- err = SetHoliday("中秋节", 1, 2023, 9, 29)
+ err = SetHoliday("中秋节", 2, 2023, 9, 29)
if err != nil {
t.Fatal(err)
}
- err = SetHoliday("国庆节", 7, 2023, 10, 1)
+ err = SetHoliday("国庆节", 6, 2023, 10, 1)
if err != nil {
t.Fatal(err)
}
diff --git a/plugin/moyu/run.go b/plugin/moyu/run.go
index 1783c98216..bed95cebf2 100644
--- a/plugin/moyu/run.go
+++ b/plugin/moyu/run.go
@@ -18,7 +18,7 @@ var (
)
func init() { // 插件主体
- control.Register("moyu", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "摸鱼提醒",
Help: "- /启用 moyu\n" +
diff --git a/plugin/moyu_calendar/calendar.go b/plugin/moyucalendar/calendar.go
similarity index 92%
rename from plugin/moyu_calendar/calendar.go
rename to plugin/moyucalendar/calendar.go
index 9c37be285e..b9dc68a561 100644
--- a/plugin/moyu_calendar/calendar.go
+++ b/plugin/moyucalendar/calendar.go
@@ -10,7 +10,7 @@ import (
)
func init() {
- control.Register("moyucalendar", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "摸鱼人日历",
Help: "- /启用 moyucalendar\n" +
diff --git a/plugin/music/selecter.go b/plugin/music/selecter.go
index 713334577f..d4b40a933a 100644
--- a/plugin/music/selecter.go
+++ b/plugin/music/selecter.go
@@ -22,7 +22,7 @@ import (
)
func init() {
- control.Register("music", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "点歌",
Help: "- 点歌[xxx]\n" +
diff --git a/plugin/nativesetu/main.go b/plugin/nativesetu/main.go
index 6e7ef67971..3f98341b48 100644
--- a/plugin/nativesetu/main.go
+++ b/plugin/nativesetu/main.go
@@ -23,7 +23,7 @@ var (
)
func init() {
- engine := control.Register("nativesetu", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "本地涩图",
Help: "- 本地[xxx]\n" +
diff --git a/plugin/nbnhhsh/nbnhhsh.go b/plugin/nbnhhsh/nbnhhsh.go
index 12ef8dda9d..f12f931e44 100644
--- a/plugin/nbnhhsh/nbnhhsh.go
+++ b/plugin/nbnhhsh/nbnhhsh.go
@@ -15,7 +15,7 @@ import (
)
func init() {
- control.Register("nbnhhsh", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "拼音首字母释义工具",
Help: "- ?? [缩写]",
diff --git a/plugin/nihongo/nihongo.go b/plugin/nihongo/nihongo.go
index 842c85689a..453262515e 100644
--- a/plugin/nihongo/nihongo.go
+++ b/plugin/nihongo/nihongo.go
@@ -15,7 +15,7 @@ import (
)
func init() {
- engine := control.Register("nihongo", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "日语学习",
Help: "- 日语语法[xxx](使用tag随机)\n" +
diff --git a/plugin/novel/qianbi.go b/plugin/novel/qianbi.go
index 964b9c8af6..f5223e0cdc 100644
--- a/plugin/novel/qianbi.go
+++ b/plugin/novel/qianbi.go
@@ -48,7 +48,7 @@ var (
)
func init() {
- engine := control.Register("novel", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("novel"),
Brief: "铅笔小说网搜索",
diff --git a/plugin/nsfw/main.go b/plugin/nsfw/main.go
index 1c76929f82..703619e379 100644
--- a/plugin/nsfw/main.go
+++ b/plugin/nsfw/main.go
@@ -14,7 +14,7 @@ import (
const hso = "https://gchat.qpic.cn/gchatpic_new//--4234EDEC5F147A4C319A41149D7E0EA9/0"
func init() {
- engine := control.Register("nsfw", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "nsfw图片识别",
Help: "- nsfw打分[图片]",
@@ -33,7 +33,7 @@ func init() {
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(judge(p))))
}
})
- control.Register("nsfwauto", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: true,
Brief: "nsfw图片自动识别",
Help: "- 当图片属于非 neutral 类别时自动发送评价",
diff --git a/plugin/nativewife/main.go b/plugin/nwife/main.go
similarity index 98%
rename from plugin/nativewife/main.go
rename to plugin/nwife/main.go
index f54fd5cbfa..79bade4105 100644
--- a/plugin/nativewife/main.go
+++ b/plugin/nwife/main.go
@@ -23,7 +23,7 @@ import (
)
func init() {
- engine := control.Register("nwife", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "本地老婆",
Help: "- 抽wife[@xxx]\n- 添加wife[名字][图片]\n- 删除wife[名字]\n- [让 | 不让]所有人均可添加wife",
diff --git a/plugin/omikuji/sensou.go b/plugin/omikuji/sensou.go
index 0fd84a6a02..daa4dbeca2 100644
--- a/plugin/omikuji/sensou.go
+++ b/plugin/omikuji/sensou.go
@@ -20,7 +20,7 @@ import (
const bed = "https://gitcode.net/u011570312/senso-ji-omikuji/-/raw/main/"
func init() { // 插件主体
- engine := control.Register("omikuji", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "浅草寺求签",
Help: "- 求签 | 占卜\n- 解签",
diff --git a/plugin/qqwife/command.go b/plugin/qqwife/command.go
index bfc1696ff5..6915272b22 100644
--- a/plugin/qqwife/command.go
+++ b/plugin/qqwife/command.go
@@ -56,7 +56,7 @@ var (
民政局 = &婚姻登记{
db: &sql.Sqlite{},
}
- engine = control.Register("qqwife", &ctrl.Options[*zero.Ctx]{
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "一群一天一夫一妻制群老婆",
Help: "- 娶群友\n- 群老婆列表\n- [允许|禁止]自由恋爱\n- [允许|禁止]牛头人\n- 设置CD为xx小时 →(默认12小时)\n- 重置花名册\n- 重置所有花名册(用于清除所有群数据及其设置)\n- 查好感度[对方Q号|@对方QQ]\n- 好感度列表\n- 好感度数据整理 (当好感度列表出现重复名字时使用)\n" +
diff --git a/plugin/quan/quan.go b/plugin/quan/quan.go
index 07271ddb15..9cd35ede5c 100644
--- a/plugin/quan/quan.go
+++ b/plugin/quan/quan.go
@@ -24,7 +24,7 @@ type result struct {
}
func init() { // 主函数
- en := control.Register("quan", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "QQ权重查询",
Help: "权重查询方式\n" +
diff --git a/plugin/qzone/qzone.go b/plugin/qzone/qzone.go
index e6942b38bc..166ebd5636 100644
--- a/plugin/qzone/qzone.go
+++ b/plugin/qzone/qzone.go
@@ -36,7 +36,7 @@ const (
)
func init() {
- engine := control.Register("qzone", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "QQ空间表白墙",
Help: "- 登录QQ空间 (Cookie过期很快, 要经常登录)\n" +
diff --git a/plugin/realcugan/realcugan.go b/plugin/realcugan/realcugan.go
index 0c3f65d557..65b94557d8 100644
--- a/plugin/realcugan/realcugan.go
+++ b/plugin/realcugan/realcugan.go
@@ -24,7 +24,7 @@ const (
)
func init() { // 插件主体
- engine := control.Register("realcugan", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "Real-CUGAN清晰术",
Help: "- 清晰术(双重吟唱|三重吟唱|四重吟唱)(强力术式|中等术式|弱术式|不变式|原式)[图片]",
diff --git a/plugin/reborn/main.go b/plugin/reborn/main.go
index cdfba310bc..222da37052 100644
--- a/plugin/reborn/main.go
+++ b/plugin/reborn/main.go
@@ -15,7 +15,7 @@ import (
)
func init() {
- en := control.Register("reborn", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "投胎模拟器",
Help: "- reborn",
diff --git a/plugin/runcode/code_runner.go b/plugin/runcode/code_runner.go
index 359e70c666..944889f5d9 100644
--- a/plugin/runcode/code_runner.go
+++ b/plugin/runcode/code_runner.go
@@ -15,7 +15,7 @@ import (
var ro = runoob.NewRunOOB("066417defb80d038228de76ec581a50a")
func init() {
- control.Register("runcode", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "在线代码运行",
Help: ">runcode [language] [code block]\n" +
diff --git a/plugin/saucenao/searcher.go b/plugin/saucenao/searcher.go
index e51b692d2b..843a16eba3 100644
--- a/plugin/saucenao/searcher.go
+++ b/plugin/saucenao/searcher.go
@@ -29,7 +29,7 @@ var (
)
func init() { // 插件主体
- engine := control.Register("saucenao", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "以图搜图",
Help: "- 以图搜图 | 搜索图片 | 以图识图[图片]\n" +
diff --git a/plugin/scale/main.go b/plugin/scale/main.go
index e22eab9078..12fa6fe854 100644
--- a/plugin/scale/main.go
+++ b/plugin/scale/main.go
@@ -31,7 +31,7 @@ import (
)
func init() {
- engine := control.Register("scale", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "二次元图片放大",
Help: "- 放大图片[图片]",
diff --git a/plugin/score/sign_in.go b/plugin/score/sign_in.go
index 17185d44eb..61303a0090 100644
--- a/plugin/score/sign_in.go
+++ b/plugin/score/sign_in.go
@@ -35,7 +35,7 @@ const (
var (
rankArray = [...]int{0, 10, 20, 50, 100, 200, 350, 550, 750, 1000, 1200}
- engine = control.Register("score", &ctrl.Options[*zero.Ctx]{
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "签到",
Help: "- 签到\n- 获得签到背景[@xxx] | 获得签到背景\n- 设置签到预设(0~3)\n- 查看等级排名\n注:为跨群排名\n- 查看我的钱包\n- 查看钱包排名\n注:为本群排行,若群人数太多不建议使用该功能!!!",
diff --git a/plugin/setutime/setu_geter.go b/plugin/setutime/setu_geter.go
index b78ab10359..1e8eeeeb65 100644
--- a/plugin/setutime/setu_geter.go
+++ b/plugin/setutime/setu_geter.go
@@ -52,7 +52,7 @@ var pool = &imgpool{
}
func init() { // 插件主体
- engine := control.Register("setutime", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "涩图",
Help: "- 来份[涩图/二次元/风景/车万]\n" +
diff --git a/plugin/shadiao/shadiao.go b/plugin/shadiao/shadiao.go
index a9b7ee465f..66a83f0ff8 100644
--- a/plugin/shadiao/shadiao.go
+++ b/plugin/shadiao/shadiao.go
@@ -27,7 +27,7 @@ const (
)
var (
- engine = control.Register("shadiao", &ctrl.Options[*zero.Ctx]{
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "沙雕app", // 也许可以更好
Help: "- 哄我\n- 渣我\n- 来碗绿茶\n- 发个朋友圈\n- 来碗毒鸡汤\n- 讲个段子\n- 马丁路德骂我\n",
diff --git a/plugin/shindan/shindan.go b/plugin/shindan/shindan.go
index 57d8d8aee6..18c1844eac 100644
--- a/plugin/shindan/shindan.go
+++ b/plugin/shindan/shindan.go
@@ -14,7 +14,7 @@ import (
)
func init() {
- engine := control.Register("shindan", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "shindan测定",
Help: "- 今天是什么少女[@xxx]\n" +
diff --git a/plugin/sleep_manage/model.go b/plugin/sleepmanage/model.go
similarity index 100%
rename from plugin/sleep_manage/model.go
rename to plugin/sleepmanage/model.go
diff --git a/plugin/sleep_manage/sleep_manage.go b/plugin/sleepmanage/sleep_manage.go
similarity index 97%
rename from plugin/sleep_manage/sleep_manage.go
rename to plugin/sleepmanage/sleep_manage.go
index d7d5b2becc..2f05f4ea7c 100644
--- a/plugin/sleep_manage/sleep_manage.go
+++ b/plugin/sleepmanage/sleep_manage.go
@@ -14,7 +14,7 @@ import (
)
func init() {
- engine := control.Register("sleepmanage", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "睡眠小助手",
Help: "- 早安\n- 晚安",
diff --git a/plugin/steam/steam.go b/plugin/steam/steam.go
index fe913fa797..457c01f438 100644
--- a/plugin/steam/steam.go
+++ b/plugin/steam/steam.go
@@ -17,7 +17,7 @@ import (
)
var (
- engine = control.Register("steam", &ctrl.Options[*zero.Ctx]{
+ engine = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("steam"),
Brief: "steam相关插件",
diff --git a/plugin/tarot/tarot.go b/plugin/tarot/tarot.go
index bb9a6c7ea4..442313b817 100644
--- a/plugin/tarot/tarot.go
+++ b/plugin/tarot/tarot.go
@@ -51,7 +51,7 @@ var (
)
func init() {
- engine := control.Register("tarot", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "塔罗牌",
Help: "- 抽[塔罗牌|大阿卡纳|小阿卡纳]\n" +
diff --git a/plugin/thesaurus/chat.go b/plugin/thesaurus/chat.go
index 1f75bcd46c..99d0f84916 100644
--- a/plugin/thesaurus/chat.go
+++ b/plugin/thesaurus/chat.go
@@ -26,7 +26,7 @@ import (
)
func init() {
- engine := control.Register("thesaurus", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "词典匹配回复",
Help: "- 切换[kimo|傲娇|可爱|🦙]词库\n- 设置词库触发概率0.x (0TL [好|good]",
diff --git a/plugin/vitsnyaru/vitsnyaru.go b/plugin/vitsnyaru/vitsnyaru.go
index a729e82d65..709bc5b471 100644
--- a/plugin/vitsnyaru/vitsnyaru.go
+++ b/plugin/vitsnyaru/vitsnyaru.go
@@ -21,7 +21,7 @@ const (
)
func init() { // 插件主体
- engine := control.Register("vitsnyaru", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "vits猫雷",
Help: "- 让猫雷说 xxx",
diff --git a/plugin/vtbmusic/vtbmusic.go b/plugin/vtbmusic/vtbmusic.go
index bbee2ab96e..835ff56738 100644
--- a/plugin/vtbmusic/vtbmusic.go
+++ b/plugin/vtbmusic/vtbmusic.go
@@ -95,7 +95,7 @@ type musicList struct {
}
func init() { // 插件主体
- engine := control.Register("vtbmusic", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "vtbmusic.com点歌",
Help: "- vtb点歌\n" +
diff --git a/plugin/vtb_quotation/model/model.go b/plugin/vtbquotation/model/model.go
similarity index 100%
rename from plugin/vtb_quotation/model/model.go
rename to plugin/vtbquotation/model/model.go
diff --git a/plugin/vtb_quotation/vtb_quotation.go b/plugin/vtbquotation/vtb_quotation.go
similarity index 98%
rename from plugin/vtb_quotation/vtb_quotation.go
rename to plugin/vtbquotation/vtb_quotation.go
index 8735c3818a..4aa3e6b720 100644
--- a/plugin/vtb_quotation/vtb_quotation.go
+++ b/plugin/vtbquotation/vtb_quotation.go
@@ -24,13 +24,13 @@ import (
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
- "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation/model"
+ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtbquotation/model"
)
var reg = regexp.MustCompile(".*/(.*)")
func init() {
- engine := control.Register("vtbquotation", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "vtb语录",
Help: "- vtb语录\n- 随机vtb\n- 更新vtb\n来源: vtbkeyboard.moe",
diff --git a/plugin/wallet/wallet.go b/plugin/wallet/wallet.go
index e8c4898cd0..9791cf824b 100644
--- a/plugin/wallet/wallet.go
+++ b/plugin/wallet/wallet.go
@@ -20,7 +20,7 @@ import (
)
func init() {
- en := control.Register("wallet", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "钱包",
Help: "- 查看我的钱包\n- 查看钱包排名",
diff --git a/plugin/wangyiyun/main.go b/plugin/wangyiyun/main.go
index 9ea3f6f406..5c56d1d6d3 100644
--- a/plugin/wangyiyun/main.go
+++ b/plugin/wangyiyun/main.go
@@ -18,7 +18,7 @@ const (
)
func init() {
- control.Register("wangyiyun", &ctrl.Options[*zero.Ctx]{
+ control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "网易云热评",
Help: "- 来份网易云热评",
diff --git a/plugin/wantquotes/wantquotes.go b/plugin/wantquotes/wantquotes.go
index 25cb036128..7c3662b954 100644
--- a/plugin/wantquotes/wantquotes.go
+++ b/plugin/wantquotes/wantquotes.go
@@ -60,7 +60,7 @@ type Quotes struct {
}
func init() {
- engine := control.Register("wantquotes", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Extra: control.ExtraFromString("wantquotes"),
Brief: "据意查句",
diff --git a/plugin/warframeapi/main.go b/plugin/warframeapi/main.go
index a28a590758..1fb04d4eda 100644
--- a/plugin/warframeapi/main.go
+++ b/plugin/warframeapi/main.go
@@ -17,7 +17,7 @@ import (
)
func init() {
- eng := control.Register("warframeapi", &ctrl.Options[*zero.Ctx]{
+ eng := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "星际战甲",
Help: "- wf时间同步\n" +
diff --git a/plugin/wenben/wenben.go b/plugin/wenben/wenben.go
index 273286f2f8..51f664058e 100644
--- a/plugin/wenben/wenben.go
+++ b/plugin/wenben/wenben.go
@@ -30,7 +30,7 @@ type rspData struct {
}
func init() { // 主函数
- en := control.Register("wenben", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "天气/拼音查询",
Help: "文本命令大全\n" +
diff --git a/plugin/wenxinAI/wenxinAI.go b/plugin/wenxinvilg/main.go
similarity index 99%
rename from plugin/wenxinAI/wenxinAI.go
rename to plugin/wenxinvilg/main.go
index 9f1c379091..701a7eb468 100644
--- a/plugin/wenxinAI/wenxinAI.go
+++ b/plugin/wenxinvilg/main.go
@@ -67,7 +67,7 @@ func init() { // 插件主体
defer process.GlobalInitMutex.Unlock()
name = zero.BotConfig.NickName[0]
}()
- engine := control.Register("wenxinvilg", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "文心AI画图",
Help: "基于百度文心的免费AI画图插件,\n因为是免费的,图片质量你懂的。\n" +
diff --git a/plugin/wife/main.go b/plugin/wife/main.go
index 1658f876ff..a353ee3a01 100644
--- a/plugin/wife/main.go
+++ b/plugin/wife/main.go
@@ -16,7 +16,7 @@ import (
)
func init() {
- engine := control.Register("wife", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "- 抽老婆",
Brief: "从老婆库抽每日老婆",
diff --git a/plugin/word_count/word_count.go b/plugin/wordcount/main.go
similarity index 98%
rename from plugin/word_count/word_count.go
rename to plugin/wordcount/main.go
index 57b09dd9e0..bb91222d71 100644
--- a/plugin/word_count/word_count.go
+++ b/plugin/wordcount/main.go
@@ -32,7 +32,7 @@ var (
)
func init() {
- engine := control.Register("wordcount", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "聊天热词",
Help: "- 热词 [群号] [消息数目]|热词 123456 1000",
diff --git a/plugin/wordle/wordle.go b/plugin/wordle/wordle.go
index f163e0cad2..3a3615846f 100644
--- a/plugin/wordle/wordle.go
+++ b/plugin/wordle/wordle.go
@@ -61,7 +61,7 @@ type dictionary map[int]struct {
var words = make(dictionary)
func init() {
- en := control.Register("wordle", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "猜单词",
Help: "- 个人猜单词\n" +
diff --git a/plugin/wtf/main.go b/plugin/wtf/main.go
index a3290d327d..b24f1ab15a 100644
--- a/plugin/wtf/main.go
+++ b/plugin/wtf/main.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- en := control.Register("wtf", &ctrl.Options[*zero.Ctx]{
+ en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "鬼东西",
Help: "- 鬼东西列表\n- 查询鬼东西[序号][@xxx]",
diff --git a/plugin/ymgal/ymgal.go b/plugin/ymgal/ymgal.go
index 024e0a5076..4b492a491b 100644
--- a/plugin/ymgal/ymgal.go
+++ b/plugin/ymgal/ymgal.go
@@ -13,7 +13,7 @@ import (
)
func init() {
- engine := control.Register("ymgal", &ctrl.Options[*zero.Ctx]{
+ engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Brief: "月慕galgame相关",
Help: "- 随机galCG\n- 随机gal表情包\n- galCG[xxx]\n- gal表情包[xxx]\n- 更新gal",
diff --git a/winres/winres.json b/winres/winres.json
index aab129f673..0208c2e335 100644
--- a/winres/winres.json
+++ b/winres/winres.json
@@ -12,7 +12,7 @@
"0409": {
"identity": {
"name": "ZeroBot-Plugin",
- "version": "1.7.2.1831"
+ "version": "1.7.3.1842"
},
"description": "",
"minimum-os": "vista",
@@ -36,23 +36,23 @@
"#1": {
"0000": {
"fixed": {
- "file_version": "1.7.2.1831",
- "product_version": "v1.7.2",
- "timestamp": "2023-07-28T13:59:16+08:00"
+ "file_version": "1.7.3.1842",
+ "product_version": "v1.7.3",
+ "timestamp": "2023-08-28T16:14:29+08:00"
},
"info": {
"0409": {
"Comments": "OneBot plugins based on ZeroBot",
"CompanyName": "FloatTech",
"FileDescription": "https://github.com/FloatTech/ZeroBot-Plugin",
- "FileVersion": "1.7.2.1831",
+ "FileVersion": "1.7.3.1842",
"InternalName": "",
"LegalCopyright": "© 2020 - 2023 FloatTech. All Rights Reserved.",
"LegalTrademarks": "",
"OriginalFilename": "ZBP.EXE",
"PrivateBuild": "",
"ProductName": "ZeroBot-Plugin",
- "ProductVersion": "v1.7.2",
+ "ProductVersion": "v1.7.3",
"SpecialBuild": ""
}
}