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": "" } }