mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-08 08:00:25 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da81875f8b | ||
|
|
dd1a0ae478 | ||
|
|
aafac8d6fa | ||
|
|
a74bcb869a | ||
|
|
584882282f | ||
|
|
05345e4c13 | ||
|
|
b8a57b80f2 | ||
|
|
177fdbae68 |
12
README.md
12
README.md
@@ -1284,6 +1284,17 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 打劫[对方Q号|@对方QQ]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>RSSHub</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub"`
|
||||
|
||||
- [x] 添加rsshub订阅-/bookfere/weekly
|
||||
- [x] 删除rsshub订阅-/bookfere/weekly
|
||||
- [x] 查看rsshub订阅列表
|
||||
- [x] rsshub同步 (使用job执行定时任务------记录在"@every 10m"触发的指令)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>在线代码运行</summary>
|
||||
@@ -1674,6 +1685,7 @@ go version
|
||||
go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go env -w GO111MODULE=auto
|
||||
go mod tidy
|
||||
go generate main.go
|
||||
```
|
||||
|
||||
3. 编辑 main.go 文件,内容按需修改
|
||||
|
||||
26
go.mod
26
go.mod
@@ -5,16 +5,16 @@ go 1.24.2
|
||||
require (
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250926171956-ba37dfebfc4a
|
||||
github.com/FloatTech/floatbox v0.0.0-20250513111443-adba80e84e80
|
||||
github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31
|
||||
github.com/FloatTech/gg v1.1.3
|
||||
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef
|
||||
github.com/FloatTech/rendercard v0.2.0
|
||||
github.com/FloatTech/sqlite v1.7.2
|
||||
github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d
|
||||
github.com/FloatTech/zbpctrl v1.7.0
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250930154951-b9dba62964b2
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20251002080916-b554b7039913
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5
|
||||
github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438
|
||||
github.com/Tnze/go-mc v1.20.2
|
||||
github.com/antchfx/htmlquery v1.3.4
|
||||
github.com/corona10/goimagehash v1.1.0
|
||||
@@ -29,9 +29,8 @@ require (
|
||||
github.com/fumiama/gotracemoe v0.0.3
|
||||
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565
|
||||
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4
|
||||
github.com/fumiama/terasu v0.0.0-20250930151601-1d9b679c36c2
|
||||
github.com/fumiama/terasu v0.0.0-20251006080703-541b84ca4a5f
|
||||
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6
|
||||
github.com/go-ego/gse v0.80.3
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jinzhu/gorm v1.9.16
|
||||
@@ -39,6 +38,7 @@ require (
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5
|
||||
github.com/lithammer/fuzzysearch v1.1.8
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5
|
||||
github.com/mmcdole/gofeed v1.3.0
|
||||
github.com/mroth/weightedrand v1.0.0
|
||||
github.com/notnil/chess v1.10.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -46,25 +46,27 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250921063512-13752a73d444
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20251002074418-56567b7fc282
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16
|
||||
golang.org/x/image v0.31.0
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/text v0.29.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
|
||||
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/antchfx/xpath v1.3.3 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/oto/v3 v3.3.2 // indirect
|
||||
github.com/ebitengine/purego v0.8.0 // indirect
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect
|
||||
github.com/fumiama/go-simple-protobuf v0.2.0 // indirect
|
||||
github.com/fumiama/gofastTEA v0.0.10 // indirect
|
||||
github.com/fumiama/gofastTEA v0.1.3 // indirect
|
||||
github.com/fumiama/imgsz v0.0.4 // indirect
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -72,10 +74,14 @@ require (
|
||||
github.com/jfreymuth/oggvorbis v1.0.5 // indirect
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
@@ -88,9 +94,9 @@ require (
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/vcaesar/cedar v0.20.2 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.61.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
|
||||
64
go.sum
64
go.sum
@@ -2,8 +2,8 @@ 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/FloatTech/AnimeAPI v1.7.1-0.20250926171956-ba37dfebfc4a h1:D/+ni0hzmfC+5TVQyGuq/AReGrSNKWTSMqU+lNG60rc=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250926171956-ba37dfebfc4a/go.mod h1:cuDd67B23xmICSmFBhWzXN51blod2BlM1liN9Ux0pSc=
|
||||
github.com/FloatTech/floatbox v0.0.0-20250513111443-adba80e84e80 h1:lFD1pd8NkYCrw0QpTX/T5pJ67I7AL5eGxQ4v0r9f81Q=
|
||||
github.com/FloatTech/floatbox v0.0.0-20250513111443-adba80e84e80/go.mod h1:IWoFFqu+0FeaHHQdddyiTRL5z7gJME6qHC96qh0R2sc=
|
||||
github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31 h1:2K+/M64ixD1Pg5hr00Nbxr7GoWQOgahvpmp1pAMnrYc=
|
||||
github.com/FloatTech/floatbox v0.0.0-20251002074805-f95cbc7edb31/go.mod h1:kf+Ywc2lk8PLdg3RX0vrUmFLPO6k+23MFmt4GviV8C0=
|
||||
github.com/FloatTech/gg v1.1.3 h1:+GlL02lTKsxJQr4WCuNwVxC1/eBZrCvypCIBtxuOFb4=
|
||||
github.com/FloatTech/gg v1.1.3/go.mod h1:/9oLP54CMfq4r+71XL26uaFTJ1uL1boAyX67680/1HE=
|
||||
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef h1:CJbK/2FRwPuZpeb6M4sWK2d7oXDnBEGhpkQuQrgc91A=
|
||||
@@ -16,13 +16,15 @@ github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d h1:mUQ/c3wXKsUGa4Sg9
|
||||
github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/FloatTech/zbpctrl v1.7.0 h1:Hxo6EIhJo+pHjcQP9QgIJgluaT1pHH99zkk3njqTNMo=
|
||||
github.com/FloatTech/zbpctrl v1.7.0/go.mod h1:xmM4dSwHA02Gei3ogCRiG+RTrw/7Z69PfrN5NYf8BPE=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250930154951-b9dba62964b2 h1:3XgC953tTNOt4sbvST4LqddsH2yV355qaxFUskvzW1A=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250930154951-b9dba62964b2/go.mod h1:klguZgIkWv7+1U/eh/5MFR9enyPqUuthSdXp/KrBE2g=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20251002080916-b554b7039913 h1:uGexKAPL26sAWGemyHbfkjYyzFItMsbI8EREBLSZ/sU=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20251002080916-b554b7039913/go.mod h1:mNvv0+wCou042n/3QkK23WmbayNctT5wgkKC3A6nbmM=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/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/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438 h1:I0bdwHZ+2DY45b39xPoTD2u+Z8zhvBuu9aZfjMZeiZM=
|
||||
github.com/RomiChan/websocket v1.4.3-0.20251002072000-d3eb41798438/go.mod h1:GO+9i5UYB4BuZEel6BfGx7O1u3ggwgZWUnGxPATUoTE=
|
||||
github.com/Tnze/go-mc v1.20.2 h1:arHCE/WxLCxY73C/4ZNLdOymRYtdwoXE05ohB7HVN6Q=
|
||||
github.com/Tnze/go-mc v1.20.2/go.mod h1:geoRj2HsXSkB3FJBuhr7wCzXegRlzWsVXd7h7jiJ6aQ=
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew=
|
||||
@@ -30,6 +32,8 @@ github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0
|
||||
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/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||
github.com/antchfx/htmlquery v1.3.4 h1:Isd0srPkni2iNTWCwVj/72t7uCphFeor5Q8nCzj1jdQ=
|
||||
github.com/antchfx/htmlquery v1.3.4/go.mod h1:K9os0BwIEmLAvTqaNSua8tXLWRWZpocZIH73OzWQbwM=
|
||||
github.com/antchfx/xpath v1.3.3 h1:tmuPQa1Uye0Ym1Zn65vxPgfltWb/Lxu2jeqIGteJSRs=
|
||||
@@ -69,8 +73,8 @@ github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnO
|
||||
github.com/fumiama/go-registry v0.2.7/go.mod h1:m+wp5fF8dYgVoFkBPZl+vlK90loymaJE0JCtocVQLEs=
|
||||
github.com/fumiama/go-simple-protobuf v0.2.0 h1:ACyN1MAlu7pDR3EszWgzUeNP+IRsSHwH6V9JCJA5R5o=
|
||||
github.com/fumiama/go-simple-protobuf v0.2.0/go.mod h1:5yYNapXq1tQMOZg9bOIVhQlZk9pQqpuFIO4DZLbsdy4=
|
||||
github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ=
|
||||
github.com/fumiama/gofastTEA v0.0.10/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
|
||||
github.com/fumiama/gofastTEA v0.1.3 h1:fxOi2D66knV6QN170hb59YiqxPhjlgizvBw+o0OjxUA=
|
||||
github.com/fumiama/gofastTEA v0.1.3/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
|
||||
github.com/fumiama/gotracemoe v0.0.3 h1:iI5EbE9A3UUbfukG6+/soYPjp1S31eCNYf4tw7s6/Jc=
|
||||
github.com/fumiama/gotracemoe v0.0.3/go.mod h1:tyqahdUzHf0bQIAVY/GYmDWvYYe5ik1ZbhnGYh+zl40=
|
||||
github.com/fumiama/imgsz v0.0.4 h1:Lsasu2hdSSFS+vnD+nvR1UkiRMK7hcpyYCC0FzgSMFI=
|
||||
@@ -79,19 +83,19 @@ github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565 h1:sQuR2+N5HurnvsZhi
|
||||
github.com/fumiama/jieba v0.0.0-20221203025406-36c17a10b565/go.mod h1:UUEvyLTJ7yoOA/viKG4wEis4ERydM7+Ny6gZUWgkS80=
|
||||
github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5 h1:jDxsIupsT84A6WHcs6kWbst+KqrRQ8/o0VyoFMnbBOA=
|
||||
github.com/fumiama/libc v0.0.0-20240530081950-6f6d8586b5c5/go.mod h1:15P6ublJ9FJR8YQCGy8DeQ2Uwur7iW9Hserr/T3OFZE=
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 h1:j9o0XVvdAeLwrBYMnh0SerrMc9CgNU6AGszbsvFzoc0=
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5/go.mod h1:FOjdw7KdCbK2eH3gRPhwFNCoXKpu9sN5vPH4El/8e0c=
|
||||
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4 h1:zN9e09TYKXI1mNkuS6YbH+Sn+4k5tBir+ovhZZcRYAs=
|
||||
github.com/fumiama/slowdo v0.0.0-20241001074058-27c4fe5259a4/go.mod h1:iZf1H/Jcw5gjOOFb4C5nlweJtViWc7uwUxRCe14pbYk=
|
||||
github.com/fumiama/sqlite3 v1.29.10-simp h1:c5y3uKyU0q9t0/SyfynzYyuslQ5zP+5CD8e0yYY554A=
|
||||
github.com/fumiama/sqlite3 v1.29.10-simp/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
|
||||
github.com/fumiama/terasu v0.0.0-20250930151601-1d9b679c36c2 h1:NAmsblfyAfQIZLPxkJil/kh6EV7Vrs/c7LGOavnUONM=
|
||||
github.com/fumiama/terasu v0.0.0-20250930151601-1d9b679c36c2/go.mod h1:UVx8YP1jKKL1Cj+uy+OnQRM2Ih6U36Mqy9GSf7jabsI=
|
||||
github.com/fumiama/terasu v0.0.0-20251006080703-541b84ca4a5f h1:skKZClM6lBzK8VyiFX/a2+nMs4W+pfGOXIgt2LZBVMM=
|
||||
github.com/fumiama/terasu v0.0.0-20251006080703-541b84ca4a5f/go.mod h1:5wnbYtJ8Rv0GG7EIiYSqniKnGDXDvkKqCcZQehh3UCQ=
|
||||
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6 h1:LtDgr628eji8jRpjPCxsk7ibjcfi97QieZVCTjxLCBw=
|
||||
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6/go.mod h1:lEaZsT4FRSqcjnQ5q8y+mkenkzR/r1D3BJmfdp0vqDg=
|
||||
github.com/gabriel-vasile/mimetype v1.0.4/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/go-ego/gse v0.80.3 h1:YNFkjMhlhQnUeuoFcUEd1ivh6SOB764rT8GDsEbDiEg=
|
||||
github.com/go-ego/gse v0.80.3/go.mod h1:Gt3A9Ry1Eso2Kza4MRaiZ7f2DTAvActmETY46Lxg0gU=
|
||||
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=
|
||||
@@ -107,6 +111,7 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
|
||||
@@ -123,6 +128,8 @@ github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
|
||||
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
@@ -141,6 +148,15 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
|
||||
github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
|
||||
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 h1:Zr92CAlFhy2gL+V1F+EyIuzbQNbSgP4xhTODZtrXUtk=
|
||||
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mroth/weightedrand v1.0.0 h1:V8JeHChvl2MP1sAoXq4brElOcza+jxLkRuwvtQu8L3E=
|
||||
github.com/mroth/weightedrand v1.0.0/go.mod h1:3p2SIcC8al1YMzGhAIoXD+r9olo/g/cdJgAD905gyNE=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
@@ -172,10 +188,11 @@ github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnj
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0=
|
||||
github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
@@ -188,14 +205,10 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/vcaesar/cedar v0.20.2 h1:TDx7AdZhilKcfE1WvdToTJf5VrC/FXcUOW+KY1upLZ4=
|
||||
github.com/vcaesar/cedar v0.20.2/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
|
||||
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2 h1:Y17/oYNuXwZg6TFag06qe8sBajwwsuvPiJJXcUcLL6E=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2/go.mod h1:Zi4hbaqlWpYajnXB2K22IUYVXRXaLfSGNNR7P4ukyyQ=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250921063512-13752a73d444 h1:7aYFXzvVr2zuxBvqrGaJb24Z4W12aXBdW8DuE1mteE4=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250921063512-13752a73d444/go.mod h1:C86nQ0gIdAri4K2vg8IIQIslt08zzrKMcqYt8zhkx1M=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20251002074418-56567b7fc282 h1:YctW/t88sQ0H8cJ69PWULU6xWfh8kNsX/XgCpW2OPHw=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20251002074418-56567b7fc282/go.mod h1:trueIIVRywKJa3ov4QphzVvzYzgCNrlXdf9JvPJOFW8=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
@@ -209,8 +222,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA=
|
||||
@@ -228,6 +241,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
@@ -235,8 +249,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -252,6 +266,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -277,6 +292,7 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
|
||||
@@ -8,8 +8,8 @@ schema = 3
|
||||
version = "v1.7.1-0.20250926171956-ba37dfebfc4a"
|
||||
hash = "sha256-wtr9hgHSPsD5NqSmI3Lo8RVWE+cNMIUGo2bcsKTN8Gg="
|
||||
[mod."github.com/FloatTech/floatbox"]
|
||||
version = "v0.0.0-20250513111443-adba80e84e80"
|
||||
hash = "sha256-Zt9zkUa3qqldrSttAq66YLPZPxrnkOR2MaU7oapIWEE="
|
||||
version = "v0.0.0-20251002074805-f95cbc7edb31"
|
||||
hash = "sha256-c50unGhF0JVPHN8geZM/YYQKgGqJgCtVksh4Ij1Pg+4="
|
||||
[mod."github.com/FloatTech/gg"]
|
||||
version = "v1.1.3"
|
||||
hash = "sha256-7K/R2mKjUHVnoJ3b1wDObJ5Un2Htj59Y97G1Ja1tuPo="
|
||||
@@ -29,14 +29,17 @@ schema = 3
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-HDDnE0oktWJH1tkxuQwUUbeJhmVwY5fyc/vR72D2mkU="
|
||||
[mod."github.com/FloatTech/zbputils"]
|
||||
version = "v1.7.2-0.20250930154951-b9dba62964b2"
|
||||
hash = "sha256-slSHly7Po3LXvmU99RKvdYHkecDzvNRExWLysUfQmFk="
|
||||
version = "v1.7.2-0.20251002080916-b554b7039913"
|
||||
hash = "sha256-9z7c79uuFl2LKaCgW1gQN5lmMjgKIcKrakcBlb3zJ90="
|
||||
[mod."github.com/PuerkitoBio/goquery"]
|
||||
version = "v1.8.0"
|
||||
hash = "sha256-I3QaPWATvBOL/F26fIiYWKS13yBUYo+9o3tcsGIu8tY="
|
||||
[mod."github.com/RomiChan/syncx"]
|
||||
version = "v0.0.0-20240418144900-b7402ffdebc7"
|
||||
hash = "sha256-L1j1vgiwqXpF9pjMoRRlrQUHzoULisw/01plaEAwxs4="
|
||||
[mod."github.com/RomiChan/websocket"]
|
||||
version = "v1.4.3-0.20220227141055-9b2c6168c9c5"
|
||||
hash = "sha256-Adx+gvqB+CCoUXx7ebIaBDjVkav+wS5qZPmaqcApBWA="
|
||||
version = "v1.4.3-0.20251002072000-d3eb41798438"
|
||||
hash = "sha256-vLu9Va+9AbOIdh1LEetz5JlJK0P2IXKsYRvCdAO8tYw="
|
||||
[mod."github.com/Tnze/go-mc"]
|
||||
version = "v1.20.2"
|
||||
hash = "sha256-Nu4PXNxeARH0itm6yIIplFaywL2yQnPJFksmmuyIptI="
|
||||
@@ -46,6 +49,9 @@ schema = 3
|
||||
[mod."github.com/ajstarks/svgo"]
|
||||
version = "v0.0.0-20200320125537-f189e35d30ca"
|
||||
hash = "sha256-ALeRuEJN9jHjGb4wNKJcxC59vVx8Tj7hHikEGkaZZ0s="
|
||||
[mod."github.com/andybalholm/cascadia"]
|
||||
version = "v1.3.1"
|
||||
hash = "sha256-M0u22DXSeXUaYtl1KoW1qWL46niFpycFkraCEQ/luYA="
|
||||
[mod."github.com/antchfx/htmlquery"]
|
||||
version = "v1.3.4"
|
||||
hash = "sha256-nrtIgRgdOvo0iIQyrhHOFKOmoT8e2gduUsct3f5zDNA="
|
||||
@@ -95,8 +101,8 @@ schema = 3
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-2kULBi1sXsFDX2g/KRFmCGkwF60o/UXacNUbIYa/cvw="
|
||||
[mod."github.com/fumiama/gofastTEA"]
|
||||
version = "v0.0.10"
|
||||
hash = "sha256-FOCbkXoS8s/K54yZbhX5pmaN/ouELnCHZoNS8a90VAg="
|
||||
version = "v0.1.3"
|
||||
hash = "sha256-/Qu57mkkFt7aFufhlkMYPgrWj5XCGbuM28EHDD8w4XY="
|
||||
[mod."github.com/fumiama/gotracemoe"]
|
||||
version = "v0.0.3"
|
||||
hash = "sha256-O3cDkVXu5NG1ZtzubxhH+S91zfgu4uH1L+OiSGYSNXQ="
|
||||
@@ -106,21 +112,21 @@ schema = 3
|
||||
[mod."github.com/fumiama/jieba"]
|
||||
version = "v0.0.0-20221203025406-36c17a10b565"
|
||||
hash = "sha256-DvDx1pdldkdaSszrbadM/VwqT9TTSmWl6G6a+ysXYEM="
|
||||
[mod."github.com/fumiama/orbyte"]
|
||||
version = "v0.0.0-20251002065953-3bb358367eb5"
|
||||
hash = "sha256-mRQwhR0v922UXlJ7lXo/osv21K8kZDaHx3DsBCjmzoo="
|
||||
[mod."github.com/fumiama/slowdo"]
|
||||
version = "v0.0.0-20241001074058-27c4fe5259a4"
|
||||
hash = "sha256-rsV3MKRCSOBMIgJXFCGbCHRY2aBAb32ftU49hT3GjqY="
|
||||
[mod."github.com/fumiama/terasu"]
|
||||
version = "v0.0.0-20250930151601-1d9b679c36c2"
|
||||
hash = "sha256-82fl+7St6LaUR6J4G/1RTToBlutA2EF40vAMYptAS/g="
|
||||
version = "v0.0.0-20251006080703-541b84ca4a5f"
|
||||
hash = "sha256-FQnOlahP7384Z2TFOCVWOjsWB/aqpbumPOwCo4TlI/k="
|
||||
[mod."github.com/fumiama/unibase2n"]
|
||||
version = "v0.0.0-20240530074540-ec743fd5a6d6"
|
||||
hash = "sha256-I3xNzjrj5y0fy0dfa75V57GanfmHIHmubEn9/y0BBHw="
|
||||
[mod."github.com/gabriel-vasile/mimetype"]
|
||||
version = "v1.4.8"
|
||||
hash = "sha256-ElqfQtnoGHyVqtN0mJjeWakQ6N5x+nVaX3+uOV7Q5Xk="
|
||||
[mod."github.com/go-ego/gse"]
|
||||
version = "v0.80.3"
|
||||
hash = "sha256-uxTQN4cxE/ZReZqjlIEQ3WYD9w2Ec37LRHQftJXsSZQ="
|
||||
[mod."github.com/go-ole/go-ole"]
|
||||
version = "v1.2.6"
|
||||
hash = "sha256-+oxitLeJxYF19Z6g+6CgmCHJ1Y5D8raMi2Cb3M6nXCs="
|
||||
@@ -151,6 +157,9 @@ schema = 3
|
||||
[mod."github.com/jozsefsallai/gophersauce"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-29DsfnGmK51DPunR/leRBKCcokN/yLoB7S2HxCsqtgY="
|
||||
[mod."github.com/json-iterator/go"]
|
||||
version = "v1.1.12"
|
||||
hash = "sha256-To8A0h+lbfZ/6zM+2PpRpY3+L6725OPC66lffq6fUoM="
|
||||
[mod."github.com/kanrichan/resvg-go"]
|
||||
version = "v0.0.2-0.20231001163256-63db194ca9f5"
|
||||
hash = "sha256-plRZ3yhyCafCXmAD4vnFUoCTRsHmLp7Jn9gFKcEKbds="
|
||||
@@ -172,6 +181,18 @@ schema = 3
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.20"
|
||||
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
|
||||
[mod."github.com/mmcdole/gofeed"]
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-GHpqGZvNg+3RSIkVKXrWg6/e8dJD8Y5v2Sx6MzmRlQ0="
|
||||
[mod."github.com/mmcdole/goxpp"]
|
||||
version = "v1.1.1-0.20240225020742-a0c311522b23"
|
||||
hash = "sha256-2pGg+LxHHQn2lwQBvc7EtrpMwZbZF7qepglzhS3TfW4="
|
||||
[mod."github.com/modern-go/concurrent"]
|
||||
version = "v0.0.0-20180306012644-bacd9c7ef1dd"
|
||||
hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo="
|
||||
[mod."github.com/modern-go/reflect2"]
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU="
|
||||
[mod."github.com/mroth/weightedrand"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-bP+yIaBUY5+oI455mNM8zh14z/SNPaQg44L3RJ0/v/c="
|
||||
@@ -226,15 +247,12 @@ schema = 3
|
||||
[mod."github.com/tklauser/numcpus"]
|
||||
version = "v0.6.1"
|
||||
hash = "sha256-8eFcw4YI0w6+GPhU5xMMQjiio94q/O5PpNO3QsvXve0="
|
||||
[mod."github.com/vcaesar/cedar"]
|
||||
version = "v0.20.2"
|
||||
hash = "sha256-3WblBdkR9AZcvZCKSteBV5kdhahiFHG2dbLWfwrVkwM="
|
||||
[mod."github.com/wcharczuk/go-chart/v2"]
|
||||
version = "v2.1.2"
|
||||
hash = "sha256-GXWWea/u6BezTsPPrWhTYiTetPP/YW6P+Sj4YdocPaM="
|
||||
[mod."github.com/wdvxdr1123/ZeroBot"]
|
||||
version = "v1.8.2-0.20250921063512-13752a73d444"
|
||||
hash = "sha256-eTqHrn/uzpa+IUoIg+LxHKQmR3oF1CgByz7speXxazE="
|
||||
version = "v1.8.2-0.20251002074418-56567b7fc282"
|
||||
hash = "sha256-KaoqopWcXqiRhGYNaA3UqYtXf27yMuBEj/bvqOWxaC4="
|
||||
[mod."github.com/yusufpapurcu/wmi"]
|
||||
version = "v1.2.4"
|
||||
hash = "sha256-N+YDBjOW59YOsZ2lRBVtFsEEi48KhNQRb63/0ZSU3bA="
|
||||
@@ -245,8 +263,8 @@ schema = 3
|
||||
version = "v0.31.0"
|
||||
hash = "sha256-ZFTlu9+4QToPPLA8C5UcG2eq/lQylq81RoG/WtYo9rg="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.42.0"
|
||||
hash = "sha256-YxileisIIez+kcAI+21kY5yk0iRuEqti2YdmS8jvP2s="
|
||||
version = "v0.43.0"
|
||||
hash = "sha256-bf3iQFrsC8BoarVaS0uSspEFAcr1zHp1uziTtBpwV34="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.36.0"
|
||||
hash = "sha256-9h4SHGnlJzmTENUp6226hC8fQ73QrQC3D85NNMxLuXg="
|
||||
|
||||
1
main.go
1
main.go
@@ -136,6 +136,7 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/realcugan" // realcugan清晰术
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/robbery" // 打劫群友的ATRI币
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub" // RSSHub订阅姬
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
|
||||
|
||||
134
plugin/rsshub/domain/job.go
Normal file
134
plugin/rsshub/domain/job.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package domain rsshub领域逻辑
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mmcdole/gofeed"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// syncRss 同步所有频道
|
||||
// 返回:更新的频道&订阅信息 map[int64]*RssClientView
|
||||
// 1. 获取所有频道
|
||||
// 2. 遍历所有频道,检查频道是否更新
|
||||
// 3. 如果更新,获取更新的内容,但是返回的数据
|
||||
func (repo *RssDomain) syncRss(ctx context.Context) (updated map[int64]*RssClientView, err error) {
|
||||
updated = make(map[int64]*RssClientView)
|
||||
// 获取所有频道
|
||||
sources, err := repo.storage.GetSources(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 遍历所有源,获取每个channel对应的rss内容
|
||||
rssView := make([]*RssClientView, len(sources))
|
||||
for i, channel := range sources {
|
||||
var feed *gofeed.Feed
|
||||
// 从site获取rss内容
|
||||
feed, err = repo.rssHubClient.FetchFeed(channel.RssHubFeedPath)
|
||||
// 如果获取失败,则跳过
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] fetch path(%+v) error: %v", channel.RssHubFeedPath, err)
|
||||
continue
|
||||
}
|
||||
rv := convertFeedToRssView(0, channel.RssHubFeedPath, feed)
|
||||
rssView[i] = rv
|
||||
}
|
||||
// 检查频道是否更新
|
||||
for _, cv := range rssView {
|
||||
if cv == nil {
|
||||
continue
|
||||
}
|
||||
var needUpdate bool
|
||||
needUpdate, err = repo.checkSourceNeedUpdate(ctx, cv.Source)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] checkSourceNeedUpdate error: %v", err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
// 保存
|
||||
logrus.WithContext(ctx).Infof("[rsshub syncRss] cv %+v, need update(real): %v", cv.Source, needUpdate)
|
||||
// 如果需要更新,更新channel 和 content
|
||||
if needUpdate {
|
||||
err = repo.storage.UpsertSource(ctx, cv.Source)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] upsert source error: %v", err)
|
||||
}
|
||||
}
|
||||
var updateChannelView = &RssClientView{Source: cv.Source, Contents: []*RssContent{}}
|
||||
err = repo.processContentsUpdate(ctx, cv, updateChannelView)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] processContentsUpdate error: %v", err)
|
||||
continue
|
||||
}
|
||||
if len(updateChannelView.Contents) == 0 {
|
||||
logrus.WithContext(ctx).Infof("[rsshub syncRss] cv %s, no new content", cv.Source.RssHubFeedPath)
|
||||
continue
|
||||
}
|
||||
updateChannelView.Sort()
|
||||
updated[updateChannelView.Source.ID] = updateChannelView
|
||||
logrus.WithContext(ctx).Debugf("[rsshub syncRss] cv %s, new contents: %v", cv.Source.RssHubFeedPath, len(updateChannelView.Contents))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// checkSourceNeedUpdate 检查频道是否需要更新
|
||||
func (repo *RssDomain) checkSourceNeedUpdate(ctx context.Context, source *RssSource) (needUpdate bool, err error) {
|
||||
var sourceInDB *RssSource
|
||||
sourceInDB, err = repo.storage.GetSourceByRssHubFeedLink(ctx, source.RssHubFeedPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if sourceInDB == nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] source not found: %v", source.RssHubFeedPath)
|
||||
return
|
||||
}
|
||||
source.ID = sourceInDB.ID
|
||||
// 检查是否需要更新到db
|
||||
if sourceInDB.IfNeedUpdate(source) {
|
||||
needUpdate = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// processContentsUpdate 处理内容(s)更新
|
||||
func (repo *RssDomain) processContentsUpdate(ctx context.Context, cv *RssClientView, updateChannelView *RssClientView) error {
|
||||
var err error
|
||||
for _, content := range cv.Contents {
|
||||
if content == nil {
|
||||
continue
|
||||
}
|
||||
content.RssSourceID = cv.Source.ID
|
||||
var existed bool
|
||||
existed, err = repo.processContentItemUpdate(ctx, content)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] upsert content error: %v", err)
|
||||
err = nil
|
||||
continue
|
||||
}
|
||||
if !existed {
|
||||
updateChannelView.Contents = append(updateChannelView.Contents, content)
|
||||
logrus.WithContext(ctx).Infof("[rsshub syncRss] cv %s, add new content: %v", cv.Source.RssHubFeedPath, content.Title)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// processContentItemUpdate 处理单个内容更新
|
||||
func (repo *RssDomain) processContentItemUpdate(ctx context.Context, content *RssContent) (existed bool, err error) {
|
||||
existed, err = repo.storage.IsContentHashIDExist(ctx, content.HashID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 不需要更新&不需要发送
|
||||
if existed {
|
||||
return
|
||||
}
|
||||
// 保存
|
||||
err = repo.storage.UpsertContent(ctx, content)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub syncRss] upsert content error: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
118
plugin/rsshub/domain/model.go
Normal file
118
plugin/rsshub/domain/model.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ======== RSS ========[START]
|
||||
|
||||
func genHashForFeedItem(link, guid string) string {
|
||||
h := fnv.New32()
|
||||
// 分三次写入数据:link、分隔符、guid
|
||||
_, _ = h.Write([]byte(link))
|
||||
_, _ = h.Write([]byte("||"))
|
||||
_, _ = h.Write([]byte(guid))
|
||||
|
||||
encoded := hex.EncodeToString(h.Sum(nil))
|
||||
return encoded
|
||||
}
|
||||
|
||||
// RssClientView 频道视图
|
||||
type RssClientView struct {
|
||||
Source *RssSource
|
||||
Contents []*RssContent
|
||||
}
|
||||
|
||||
// ======== RSS ========[END]
|
||||
|
||||
// ======== DB ========[START]
|
||||
|
||||
const (
|
||||
tableNameRssSource = "rss_source"
|
||||
tableNameRssContent = "rss_content"
|
||||
tableNameRssSubscribe = "rss_subscribe"
|
||||
)
|
||||
|
||||
// RssSource RSS频道
|
||||
type RssSource struct {
|
||||
// Id 自增id
|
||||
ID int64 `gorm:"column:id;primary_key;AUTO_INCREMENT"`
|
||||
// RssHubFeedPath 频道路由 用于区分rss_hub 不同的频道 例如: `/bangumi/tv/calendar/today`
|
||||
RssHubFeedPath string `gorm:"column:rss_hub_feed_path;not null;unique;" json:"rss_hub_feed_path"`
|
||||
// Title 频道标题
|
||||
Title string `gorm:"column:title" json:"title"`
|
||||
// ChannelDesc 频道描述
|
||||
ChannelDesc string `gorm:"column:channel_desc" json:"channel_desc"`
|
||||
// ImageURL 频道图片
|
||||
ImageURL string `gorm:"column:image_url" json:"image_url"`
|
||||
// Link 频道链接
|
||||
Link string `gorm:"column:link" json:"link"`
|
||||
// UpdatedParsed RSS页面更新时间
|
||||
UpdatedParsed time.Time `gorm:"column:updated_parsed" json:"updated_parsed"`
|
||||
// Mtime update time
|
||||
Mtime time.Time `gorm:"column:mtime;default:current_timestamp;" json:"mtime"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (RssSource) TableName() string {
|
||||
return tableNameRssSource
|
||||
}
|
||||
|
||||
// IfNeedUpdate ...
|
||||
func (r RssSource) IfNeedUpdate(cmp *RssSource) bool {
|
||||
if r.Link != cmp.Link {
|
||||
return false
|
||||
}
|
||||
return r.UpdatedParsed.Unix() < cmp.UpdatedParsed.Unix()
|
||||
}
|
||||
|
||||
// RssContent 订阅的RSS频道的推送信息
|
||||
type RssContent struct {
|
||||
// Id 自增id
|
||||
ID int64 `gorm:"column:id;primary_key;AUTO_INCREMENT"`
|
||||
HashID string `gorm:"column:hash_id;unique" json:"hash_id"`
|
||||
RssSourceID int64 `gorm:"column:rss_source_id;not null" json:"rss_source_id"`
|
||||
Title string `gorm:"column:title" json:"title"`
|
||||
Description string `gorm:"column:description" json:"description"`
|
||||
Link string `gorm:"column:link" json:"link"`
|
||||
Date time.Time `gorm:"column:date" json:"date"`
|
||||
Author string `gorm:"column:author" json:"author"`
|
||||
Thumbnail string `gorm:"column:thumbnail" json:"thumbnail"`
|
||||
Content string `gorm:"column:content" json:"content"`
|
||||
// Mtime update time
|
||||
Mtime time.Time `gorm:"column:mtime;default:current_timestamp;" json:"mtime"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (RssContent) TableName() string {
|
||||
return tableNameRssContent
|
||||
}
|
||||
|
||||
// Sort ... order by Date desc
|
||||
func (r *RssClientView) Sort() {
|
||||
sort.Slice(r.Contents, func(i, j int) bool {
|
||||
return r.Contents[i].Date.Unix() > r.Contents[j].Date.Unix()
|
||||
})
|
||||
}
|
||||
|
||||
// RssSubscribe 订阅关系表:群组-RSS频道
|
||||
type RssSubscribe struct {
|
||||
// Id 自增id
|
||||
ID int64 `gorm:"column:id;primary_key;AUTO_INCREMENT"`
|
||||
// 订阅群组
|
||||
GroupID int64 `gorm:"column:group_id;not null;uniqueIndex:uk_sid_gid"`
|
||||
// 订阅频道
|
||||
RssSourceID int64 `gorm:"column:rss_source_id;not null;uniqueIndex:uk_sid_gid"`
|
||||
// Mtime update time
|
||||
Mtime time.Time `gorm:"column:mtime;default:current_timestamp;" json:"mtime"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (RssSubscribe) TableName() string {
|
||||
return tableNameRssSubscribe
|
||||
}
|
||||
|
||||
// ======== DB ========[END]
|
||||
101
plugin/rsshub/domain/rawFeed.go
Normal file
101
plugin/rsshub/domain/rawFeed.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
"github.com/mmcdole/gofeed"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
// RSSHubMirrors RSSHub镜像站地址列表,第一个为默认地址
|
||||
rssHubMirrors = []string{
|
||||
"https://rsshub.rssforever.com",
|
||||
"https://rss.injahow.cn",
|
||||
}
|
||||
)
|
||||
|
||||
// RssHubClient rss hub client (http)
|
||||
type RssHubClient struct {
|
||||
*http.Client
|
||||
}
|
||||
|
||||
// FetchFeed 获取rss feed信息
|
||||
func (c *RssHubClient) FetchFeed(path string) (feed *gofeed.Feed, err error) {
|
||||
var data []byte
|
||||
// 遍历 rssHubMirrors,直到获取成功
|
||||
for _, mirror := range rssHubMirrors {
|
||||
data, err = web.RequestDataWith(c.Client, mirror+path, "GET", "", web.RandUA(), nil)
|
||||
if err == nil && len(data) > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Errorf("[rsshub FetchFeed] fetch feed error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
logrus.Errorf("[rsshub FetchFeed] fetch feed error: data is empty")
|
||||
return nil, errors.New("feed data is empty")
|
||||
}
|
||||
feed, err = gofeed.NewParser().Parse(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func convertFeedToRssView(channelID int64, cPath string, feed *gofeed.Feed) (view *RssClientView) {
|
||||
var imgURL string
|
||||
if feed.Image != nil {
|
||||
imgURL = feed.Image.URL
|
||||
}
|
||||
view = &RssClientView{
|
||||
Source: &RssSource{
|
||||
ID: channelID,
|
||||
RssHubFeedPath: cPath,
|
||||
Title: feed.Title,
|
||||
ChannelDesc: feed.Description,
|
||||
ImageURL: imgURL,
|
||||
Link: feed.Link,
|
||||
UpdatedParsed: *(feed.UpdatedParsed),
|
||||
Mtime: time.Now(),
|
||||
},
|
||||
// 不用定长,后面可能会过滤一些元素再append
|
||||
Contents: []*RssContent{},
|
||||
}
|
||||
// convert feed items to rss content
|
||||
for _, item := range feed.Items {
|
||||
if item.Link == "" || item.Title == "" {
|
||||
continue
|
||||
}
|
||||
var thumbnail string
|
||||
if item.Image != nil {
|
||||
thumbnail = item.Image.URL
|
||||
}
|
||||
var publishedParsed = item.PublishedParsed
|
||||
if publishedParsed == nil {
|
||||
publishedParsed = &time.Time{}
|
||||
}
|
||||
aus, _ := json.Marshal(item.Authors)
|
||||
view.Contents = append(view.Contents, &RssContent{
|
||||
ID: 0,
|
||||
HashID: genHashForFeedItem(item.Link, item.GUID),
|
||||
RssSourceID: channelID,
|
||||
Title: item.Title,
|
||||
Description: item.Description,
|
||||
Link: item.Link,
|
||||
Date: *publishedParsed,
|
||||
Author: string(aus),
|
||||
Thumbnail: thumbnail,
|
||||
Content: item.Content,
|
||||
Mtime: time.Now(),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
178
plugin/rsshub/domain/rssHub.go
Normal file
178
plugin/rsshub/domain/rssHub.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// RssDomain RssRepo定义
|
||||
type RssDomain struct {
|
||||
storage *repoStorage
|
||||
rssHubClient *RssHubClient
|
||||
}
|
||||
|
||||
// NewRssDomain 新建RssDomain,调用方保证单例模式
|
||||
func NewRssDomain(dbPath string) (*RssDomain, error) {
|
||||
return newRssDomain(dbPath)
|
||||
}
|
||||
|
||||
func newRssDomain(dbPath string) (*RssDomain, error) {
|
||||
if _, err := os.Stat(dbPath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
orm, err := gorm.Open("sqlite3", dbPath)
|
||||
if err != nil {
|
||||
logrus.Errorf("[rsshub NewRssDomain] open db error: %v", err)
|
||||
panic(err)
|
||||
}
|
||||
repo := &RssDomain{
|
||||
storage: &repoStorage{orm: orm},
|
||||
rssHubClient: &RssHubClient{Client: http.DefaultClient},
|
||||
}
|
||||
err = repo.storage.initDB()
|
||||
if err != nil {
|
||||
logrus.Errorf("[rsshub NewRssDomain] open db error: %v", err)
|
||||
panic(err)
|
||||
}
|
||||
return repo, nil
|
||||
}
|
||||
|
||||
// Subscribe QQ群订阅Rss频道
|
||||
func (repo *RssDomain) Subscribe(ctx context.Context, gid int64, feedPath string) (
|
||||
rv *RssClientView, isChannelExisted, isSubExisted bool, err error) {
|
||||
// 验证
|
||||
feed, err := repo.rssHubClient.FetchFeed(feedPath)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] add source error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Infof("[rsshub Subscribe] try get source success: %v", len(feed.Title))
|
||||
// 新建source结构体
|
||||
rv = convertFeedToRssView(0, feedPath, feed)
|
||||
feedChannel, err := repo.storage.GetSourceByRssHubFeedLink(ctx, feedPath)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] query source by feedPath error: %v", err)
|
||||
return
|
||||
}
|
||||
// 如果已经存在
|
||||
if feedChannel != nil {
|
||||
logrus.WithContext(ctx).Warningf("[rsshub Subscribe] source existed: %v", feedChannel)
|
||||
isChannelExisted = true
|
||||
} else {
|
||||
// 不存在的情况,要把更新时间置空,保证下一次同步时能够更新
|
||||
rv.Source.UpdatedParsed = time.Time{}
|
||||
}
|
||||
// 保存
|
||||
err = repo.storage.UpsertSource(ctx, rv.Source)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] save source error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.Infof("[rsshub Subscribe] save/update source success %v", rv.Source.ID)
|
||||
// 添加群号到订阅
|
||||
subscribe, err := repo.storage.GetSubscribeByID(ctx, gid, rv.Source.ID)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] query subscribe error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Infof("[rsshub Subscribe] query subscribe success: %v", subscribe)
|
||||
// 如果已经存在,直接返回
|
||||
if subscribe != nil {
|
||||
isSubExisted = true
|
||||
logrus.WithContext(ctx).Infof("[rsshub Subscribe] subscribe existed: %v", subscribe)
|
||||
return
|
||||
}
|
||||
// 如果不存在,保存
|
||||
err = repo.storage.CreateSubscribe(ctx, gid, rv.Source.ID)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] save subscribe error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Infof("[rsshub Subscribe] success: %v", len(rv.Contents))
|
||||
return
|
||||
}
|
||||
|
||||
// Unsubscribe 群组取消订阅
|
||||
func (repo *RssDomain) Unsubscribe(ctx context.Context, gid int64, feedPath string) (err error) {
|
||||
existedSubscribes, ifExisted, err := repo.storage.GetIfExistedSubscribe(ctx, gid, feedPath)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] query sub by route error: %v", err)
|
||||
return errors.New("数据库错误")
|
||||
}
|
||||
logrus.WithContext(ctx).Infof("[rsshub Subscribe] query source by route success: %v", existedSubscribes)
|
||||
// 如果不存在订阅关系,直接返回
|
||||
if !ifExisted || existedSubscribes == nil {
|
||||
logrus.WithContext(ctx).Infof("[rsshub Subscribe] source existed: %v", ifExisted)
|
||||
return errors.New("频道不存在")
|
||||
}
|
||||
err = repo.storage.DeleteSubscribe(ctx, existedSubscribes.ID)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] delete source error: %v", err)
|
||||
return errors.New("删除失败")
|
||||
}
|
||||
// 查询是否还有群订阅这个频道
|
||||
subscribesNeedsToDel, err := repo.storage.GetSubscribesBySource(ctx, feedPath)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] query source by route error: %v", err)
|
||||
return
|
||||
}
|
||||
// 没有群订阅的时候,把频道删除
|
||||
if len(subscribesNeedsToDel) == 0 {
|
||||
err = repo.storage.DeleteSource(ctx, existedSubscribes.RssSourceID)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Subscribe] delete source error: %v", err)
|
||||
return errors.New("清除频道信息失败")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetSubscribedChannelsByGroupID 获取群对应的订阅的频道信息
|
||||
func (repo *RssDomain) GetSubscribedChannelsByGroupID(ctx context.Context, gid int64) ([]*RssClientView, error) {
|
||||
channels, err := repo.storage.GetSubscribedChannelsByGroupID(ctx, gid)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub GetSubscribedChannelsByGroupID] GetSubscribedChannelsByGroupID error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
rv := make([]*RssClientView, len(channels))
|
||||
logrus.WithContext(ctx).Infof("[rsshub GetSubscribedChannelsByGroupID] query subscribe success: %v", len(channels))
|
||||
for i, cn := range channels {
|
||||
rv[i] = &RssClientView{
|
||||
Source: cn,
|
||||
}
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// Sync 同步任务,按照群组订阅情况做好map切片
|
||||
func (repo *RssDomain) Sync(ctx context.Context) (groupView map[int64][]*RssClientView, err error) {
|
||||
groupView = make(map[int64][]*RssClientView)
|
||||
// 获取所有Rss频道
|
||||
// 获取所有频道
|
||||
updatedViews, err := repo.syncRss(ctx)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Sync] sync rss feed error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Infof("[rsshub Sync] updated channels: %v", len(updatedViews))
|
||||
subscribes, err := repo.storage.GetSubscribes(ctx)
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub Sync] get subscribes error: %v", err)
|
||||
return
|
||||
}
|
||||
for _, subscribe := range subscribes {
|
||||
groupView[subscribe.GroupID] = append(groupView[subscribe.GroupID], updatedViews[subscribe.RssSourceID])
|
||||
}
|
||||
return
|
||||
}
|
||||
105
plugin/rsshub/domain/rssHub_test.go
Normal file
105
plugin/rsshub/domain/rssHub_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewRssDomain(t *testing.T) {
|
||||
dm, err := newRssDomain("rsshub.db")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
if dm == nil {
|
||||
t.Fatal("domain is nil")
|
||||
}
|
||||
}
|
||||
|
||||
//var testRssHubChannelUrl = "https://rsshub.rssforever.com/bangumi/tv/calendar/today"
|
||||
|
||||
var dm, _ = newRssDomain("rsshub.db")
|
||||
|
||||
func TestSub(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
feedLink string
|
||||
gid int64
|
||||
}{
|
||||
{
|
||||
name: "test1",
|
||||
feedLink: "/bangumi/tv/calendar/today",
|
||||
gid: 99,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
feedLink: "/go-weekly",
|
||||
gid: 99,
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
feedLink: "/go-weekly",
|
||||
gid: 123,
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
feedLink: "/go-weekly",
|
||||
gid: 321,
|
||||
},
|
||||
{
|
||||
name: "test3",
|
||||
feedLink: "/go-weekly",
|
||||
gid: 4123,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
channel, ifExisted, ifSub, err := dm.Subscribe(ctx, tc.gid, tc.feedLink)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
t.Logf("[TEST] add sub res: %+v,%+v,%+v\n", channel, ifExisted, ifSub)
|
||||
res, ext, err := dm.storage.GetIfExistedSubscribe(ctx, tc.gid, tc.feedLink)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
t.Logf("[TEST] if exist: %+v,%+v", res, ext)
|
||||
channels, err := dm.GetSubscribedChannelsByGroupID(ctx, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
t.Logf("[TEST] 2 channels: %+v", channels)
|
||||
// del
|
||||
//err = dm.Unsubscribe(ctx, tc.gid, tc.feedLink)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
// return
|
||||
//}
|
||||
//res, ext, err = dm.storage.GetIfExistedSubscribe(ctx, tc.gid, tc.feedLink)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
// return
|
||||
//}
|
||||
//t.Logf("[TEST] after del: %+v,%+v", res, ext)
|
||||
//if res != nil || ext {
|
||||
// t.Fatal("delete failed")
|
||||
//}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_SyncFeed(t *testing.T) {
|
||||
feed, err := dm.Sync(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
rs, _ := json.Marshal(feed)
|
||||
t.Logf("[Test] feed: %+v", string(rs))
|
||||
}
|
||||
271
plugin/rsshub/domain/storageRepo.go
Normal file
271
plugin/rsshub/domain/storageRepo.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// repoStorage db struct for rss
|
||||
type repoStorage struct {
|
||||
orm *gorm.DB
|
||||
}
|
||||
|
||||
// initDB ...
|
||||
func (s *repoStorage) initDB() (err error) {
|
||||
err = s.orm.AutoMigrate(&RssSource{}, &RssContent{}, &RssSubscribe{}).Error
|
||||
if err != nil {
|
||||
logrus.Errorf("[rsshub initDB] error: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// s.orm.LogMode(true)
|
||||
}
|
||||
|
||||
// GetSubscribesBySource Impl
|
||||
func (s *repoStorage) GetSubscribesBySource(ctx context.Context, feedPath string) ([]*RssSubscribe, error) {
|
||||
logrus.WithContext(ctx).Infof("[rsshub GetSubscribesBySource] feedPath: %s", feedPath)
|
||||
rs := make([]*RssSubscribe, 0)
|
||||
err := s.orm.Model(&RssSubscribe{}).Joins(fmt.Sprintf("%s left join %s on %s.rss_source_id=%s.id", tableNameRssSubscribe, tableNameRssSource, tableNameRssSubscribe, tableNameRssSource)).
|
||||
Where("rss_source.rss_hub_feed_path = ?", feedPath).Select("rss_subscribe.*").Find(&rs).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub GetSubscribesBySource] error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
// GetIfExistedSubscribe Impl
|
||||
func (s *repoStorage) GetIfExistedSubscribe(ctx context.Context, gid int64, feedPath string) (*RssSubscribe, bool, error) {
|
||||
rs := RssSubscribe{}
|
||||
|
||||
err := s.orm.Table(tableNameRssSubscribe).
|
||||
Select("rss_subscribe.id, rss_subscribe.group_id, rss_subscribe.rss_source_id, rss_subscribe.mtime").
|
||||
Joins(fmt.Sprintf("INNER JOIN %s ON %s.rss_source_id=%s.id",
|
||||
tableNameRssSource, tableNameRssSubscribe, tableNameRssSource)).
|
||||
Where("rss_source.rss_hub_feed_path = ? AND rss_subscribe.group_id = ?", feedPath, gid).Scan(&rs).Error
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, false, nil
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub GetIfExistedSubscribe] error: %v", err)
|
||||
return nil, false, err
|
||||
}
|
||||
if rs.ID == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &rs, true, nil
|
||||
}
|
||||
|
||||
// ==================== RepoSource ==================== [Start]
|
||||
|
||||
// UpsertSource Impl
|
||||
func (s *repoStorage) UpsertSource(ctx context.Context, source *RssSource) (err error) {
|
||||
// Update columns to default value on `id` conflict
|
||||
querySource := &RssSource{RssHubFeedPath: source.RssHubFeedPath}
|
||||
err = s.orm.First(querySource, "rss_hub_feed_path = ?", querySource.RssHubFeedPath).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = s.orm.Create(source).Omit("id").Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] add source error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
source.ID = querySource.ID
|
||||
logrus.WithContext(ctx).Infof("[rsshub] update source: %+v", source.UpdatedParsed)
|
||||
err = s.orm.Model(&source).Where(&RssSource{ID: source.ID}).
|
||||
Updates(&RssSource{
|
||||
Title: source.Title,
|
||||
ChannelDesc: source.ChannelDesc,
|
||||
ImageURL: source.ImageURL,
|
||||
Link: source.Link,
|
||||
UpdatedParsed: source.UpdatedParsed,
|
||||
Mtime: time.Now(),
|
||||
}).Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] update source error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.Println("[rsshub] add source success: ", source.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSources Impl
|
||||
func (s *repoStorage) GetSources(ctx context.Context) (sources []RssSource, err error) {
|
||||
sources = []RssSource{}
|
||||
err = s.orm.Find(&sources, "id > 0").Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errors.New("source not found")
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] get sources error: %v", err)
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Infof("[rsshub] get sources success: %d", len(sources))
|
||||
return
|
||||
}
|
||||
|
||||
// GetSourceByRssHubFeedLink Impl
|
||||
func (s *repoStorage) GetSourceByRssHubFeedLink(ctx context.Context, rssHubFeedLink string) (source *RssSource, err error) {
|
||||
source = &RssSource{RssHubFeedPath: rssHubFeedLink}
|
||||
err = s.orm.Take(source, source).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] get source error: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteSource Impl
|
||||
func (s *repoStorage) DeleteSource(ctx context.Context, fID int64) (err error) {
|
||||
err = s.orm.Delete(&RssSource{}, "id = ?", fID).Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.DeleteSource: %v", err)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return errors.New("source not found")
|
||||
}
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ==================== RepoSource ==================== [End]
|
||||
|
||||
// ==================== RepoContent ==================== [Start]
|
||||
|
||||
// UpsertContent Impl
|
||||
func (s *repoStorage) UpsertContent(ctx context.Context, content *RssContent) (err error) {
|
||||
// check params
|
||||
if content == nil {
|
||||
err = errors.New("content is nil")
|
||||
return
|
||||
}
|
||||
// check params.RssHubFeedPath and params.HashID
|
||||
if content.RssSourceID < 0 || content.HashID == "" || content.Title == "" {
|
||||
err = errors.New("content.RssSourceID or content.HashID or content.Title is empty")
|
||||
return
|
||||
}
|
||||
err = s.orm.Create(content).Omit("id").Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.UpsertContent: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteSourceContents Impl
|
||||
func (s *repoStorage) DeleteSourceContents(ctx context.Context, channelID int64) (rows int64, err error) {
|
||||
err = s.orm.Delete(&RssSubscribe{}).Where(&RssSubscribe{RssSourceID: channelID}).Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.DeleteSourceContents: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsContentHashIDExist Impl
|
||||
func (s *repoStorage) IsContentHashIDExist(ctx context.Context, hashID string) (bool, error) {
|
||||
wanted := &RssContent{HashID: hashID}
|
||||
err := s.orm.Take(wanted, wanted).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.IsContentHashIDExist: %v", err)
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ==================== RepoContent ==================== [End]
|
||||
|
||||
// ==================== RepoSubscribe ==================== [Start]
|
||||
|
||||
// CreateSubscribe Impl
|
||||
func (s *repoStorage) CreateSubscribe(ctx context.Context, gid, rssSourceID int64) (err error) {
|
||||
// check subscribe
|
||||
if rssSourceID < 0 || gid == 0 {
|
||||
err = errors.New("gid or rssSourceID is empty")
|
||||
return
|
||||
}
|
||||
err = s.orm.Create(&RssSubscribe{GroupID: gid, RssSourceID: rssSourceID}).Omit("id").Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.CreateSubscribe: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteSubscribe Impl
|
||||
func (s *repoStorage) DeleteSubscribe(ctx context.Context, subscribeID int64) (err error) {
|
||||
err = s.orm.Delete(&RssSubscribe{}, "id = ?", subscribeID).Error
|
||||
if err != nil {
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.DeleteSubscribe error: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetSubscribeByID Impl
|
||||
func (s *repoStorage) GetSubscribeByID(ctx context.Context, gid int64, subscribeID int64) (res *RssSubscribe, err error) {
|
||||
res = &RssSubscribe{}
|
||||
err = s.orm.First(res, &RssSubscribe{GroupID: gid, RssSourceID: subscribeID}).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.GetSubscribeByID: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetSubscribedChannelsByGroupID Impl
|
||||
func (s *repoStorage) GetSubscribedChannelsByGroupID(ctx context.Context, gid int64) (res []*RssSource, err error) {
|
||||
res = make([]*RssSource, 0)
|
||||
err = s.orm.Model(&RssSource{}).
|
||||
Joins(fmt.Sprintf("join %s on rss_source_id=%s.id", tableNameRssSubscribe, tableNameRssSource)).Where("rss_subscribe.group_id = ?", gid).
|
||||
Select("rss_source.*").
|
||||
Find(&res).
|
||||
Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.GetSubscribedChannelsByGroupID: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetSubscribes Impl
|
||||
func (s *repoStorage) GetSubscribes(ctx context.Context) (res []*RssSubscribe, err error) {
|
||||
res = make([]*RssSubscribe, 0)
|
||||
err = s.orm.Find(&res).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
logrus.WithContext(ctx).Errorf("[rsshub] storage.GetSubscribes: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ==================== RepoSubscribe ==================== [End]
|
||||
152
plugin/rsshub/main.go
Normal file
152
plugin/rsshub/main.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// Package rsshub rss_hub订阅插件
|
||||
package rsshub
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zbpCtxExt "github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub/domain"
|
||||
)
|
||||
|
||||
// 初始化 repo
|
||||
var (
|
||||
rssRepo *domain.RssDomain
|
||||
initErr error
|
||||
regexpForSQL = regexp.MustCompile(`[\^<>\[\]%&\*\(\)\{\}\|\=]|(union\s+select|update\s+|delete\s+|drop\s+|truncate\s+|insert\s+|exec\s+|declare\s+)`)
|
||||
)
|
||||
|
||||
var (
|
||||
// 注册插件
|
||||
engine = control.Register("rsshub", &ctrl.Options[*zero.Ctx]{
|
||||
// 默认不启动
|
||||
DisableOnDefault: false,
|
||||
Brief: "rsshub订阅姬",
|
||||
// 详细帮助
|
||||
Help: "rsshub订阅姬desu~ \n" +
|
||||
"支持的详细订阅列表文档可见:\n" +
|
||||
"https://rsshub.netlify.app/zh/ \n" +
|
||||
"- 添加rsshub订阅-/bookfere/weekly \n" +
|
||||
"- 删除rsshub订阅-/bookfere/weekly \n" +
|
||||
"- 查看rsshub订阅列表 \n" +
|
||||
"- rsshub同步 \n" +
|
||||
"Tips: 定时刷新rsshub订阅信息需要配合job一起使用, 全局只需要设置一个, 无视响应状态推送, 下为例子\n" +
|
||||
"记录在\"@every 10m\"触发的指令)\n" +
|
||||
"rsshub同步",
|
||||
// 插件数据存储路径
|
||||
PrivateDataFolder: "rsshub",
|
||||
OnEnable: func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬现在启动了哦"))
|
||||
},
|
||||
OnDisable: func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬现在关闭了哦"))
|
||||
},
|
||||
}).ApplySingle(zbpCtxExt.DefaultSingle)
|
||||
)
|
||||
|
||||
// init 命令路由
|
||||
func init() {
|
||||
rssRepo, initErr = domain.NewRssDomain(engine.DataFolder() + "rsshub.db")
|
||||
if initErr != nil {
|
||||
logrus.Errorln("rsshub订阅姬:初始化失败", initErr)
|
||||
panic(initErr)
|
||||
}
|
||||
engine.OnFullMatch("rsshub同步", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
// 群组-频道推送视图 map[群组]推送内容数组
|
||||
groupToFeedsMap, err := rssRepo.Sync(context.Background())
|
||||
if err != nil {
|
||||
logrus.Errorln("rsshub同步失败", err)
|
||||
ctx.SendPrivateMessage(zero.BotConfig.SuperUsers[0], message.Text("rsshub同步失败", err))
|
||||
return
|
||||
}
|
||||
// 没有更新的[群组-频道推送视图]则不推送
|
||||
if len(groupToFeedsMap) == 0 {
|
||||
logrus.Info("rsshub未发现更新")
|
||||
return
|
||||
}
|
||||
sendRssUpdateMsg(ctx, groupToFeedsMap)
|
||||
})
|
||||
// 添加订阅
|
||||
engine.OnPrefix("添加rsshub订阅-", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
routeStr := ctx.State["args"].(string)
|
||||
input := regexpForSQL.ReplaceAllString(routeStr, "")
|
||||
logrus.Debugf("添加rsshub订阅:raw(%s), replaced(%s)", routeStr, input)
|
||||
rv, _, isSubExisted, err := rssRepo.Subscribe(context.Background(), ctx.Event.GroupID, input)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬:添加失败", err.Error()))
|
||||
return
|
||||
}
|
||||
if isSubExisted {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬:已存在,更新成功"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬:添加成功\n", rv.Source.Title))
|
||||
}
|
||||
// 添加成功,发送订阅源快照
|
||||
msg, err := newRssDetailsMsg(ctx, rv)
|
||||
if len(msg) == 0 || err != nil {
|
||||
ctx.SendPrivateMessage(zero.BotConfig.SuperUsers[0], message.Text("rsshub推送错误", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.Send(msg).ID(); id == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 发送订阅源快照失败,可能被风控了"))
|
||||
}
|
||||
})
|
||||
engine.OnPrefix("删除rsshub订阅-", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
routeStr := ctx.State["args"].(string)
|
||||
input := regexpForSQL.ReplaceAllString(routeStr, "")
|
||||
logrus.Debugf("删除rsshub订阅:raw(%s), replaced(%s)", routeStr, input)
|
||||
err := rssRepo.Unsubscribe(context.Background(), ctx.Event.GroupID, input)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬:删除失败 ", err.Error()))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(fmt.Sprintf("rsshub订阅姬:删除%s成功", input)))
|
||||
})
|
||||
engine.OnFullMatch("查看rsshub订阅列表", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
rv, err := rssRepo.GetSubscribedChannelsByGroupID(context.Background(), ctx.Event.GroupID)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬:查询失败 ", err.Error()))
|
||||
return
|
||||
}
|
||||
// 添加成功,发送订阅源信息
|
||||
msg, err := newRssSourcesMsg(ctx, rv)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("rsshub订阅姬:查询失败 ", err.Error()))
|
||||
return
|
||||
}
|
||||
if len(msg) == 0 {
|
||||
ctx.SendChain(message.Text("ん? 没有订阅的频道哦~"))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(msg...)
|
||||
})
|
||||
}
|
||||
|
||||
// sendRssUpdateMsg 发送Rss更新消息
|
||||
func sendRssUpdateMsg(ctx *zero.Ctx, groupToFeedsMap map[int64][]*domain.RssClientView) {
|
||||
for groupID, views := range groupToFeedsMap {
|
||||
logrus.Infof("rsshub插件在群 %d 触发推送检查", groupID)
|
||||
for _, view := range views {
|
||||
if view == nil || len(view.Contents) == 0 {
|
||||
continue
|
||||
}
|
||||
msg, err := newRssDetailsMsg(ctx, view)
|
||||
if len(msg) == 0 || err != nil {
|
||||
ctx.SendPrivateMessage(zero.BotConfig.SuperUsers[0], message.Text(rssHubPushErrMsg, err))
|
||||
continue
|
||||
}
|
||||
logrus.Infof("rsshub插件在群 %d 开始推送 %s", groupID, view.Source.Title)
|
||||
ctx.SendGroupMessage(groupID, message.Text(fmt.Sprintf("%s\n该rsshub频道下有更新了哦~", view.Source.Title)))
|
||||
if res := ctx.SendGroupForwardMessage(groupID, msg); !res.Exists() {
|
||||
ctx.SendPrivateMessage(zero.BotConfig.SuperUsers[0], message.Text(rssHubPushErrMsg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
plugin/rsshub/view.go
Normal file
100
plugin/rsshub/view.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package rsshub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/floatbox/binary"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/ZeroBot-Plugin/plugin/rsshub/domain"
|
||||
)
|
||||
|
||||
const (
|
||||
rssHubPushErrMsg = "RssHub推送错误"
|
||||
)
|
||||
|
||||
// formatRssViewToMessagesSlice 格式化RssClientView为消息切片
|
||||
func formatRssViewToMessagesSlice(view *domain.RssClientView) ([]message.Message, error) {
|
||||
// 取前20条
|
||||
cts := view.Contents
|
||||
if len(cts) > 20 {
|
||||
cts = cts[:20]
|
||||
}
|
||||
// 2n+1条消息
|
||||
fv := make([]message.Message, len(cts)*2+1)
|
||||
// 订阅源头图
|
||||
toastPic, err := text.RenderToBase64(fmt.Sprintf("%s\n\n\n%s\n\n\n更新时间:%v\n\n\n",
|
||||
view.Source.Title, view.Source.Link, view.Source.UpdatedParsed.Local().Format(time.DateTime)),
|
||||
text.SakuraFontFile, 1200, 40)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fv[0] = message.Message{message.Image("base64://" + binary.BytesToString(toastPic))}
|
||||
// 元素信息
|
||||
for idx, item := range cts {
|
||||
contentStr := fmt.Sprintf("%s\n\n\n", item.Title)
|
||||
// Date为空时不显示
|
||||
if !item.Date.IsZero() {
|
||||
contentStr += fmt.Sprintf("更新时间:\n%v\n", item.Date.Local().Format(time.DateTime))
|
||||
}
|
||||
var content []byte
|
||||
content, err = text.RenderToBase64(contentStr, text.SakuraFontFile, 1200, 40)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("RssHub订阅姬渲染图片失败")
|
||||
continue
|
||||
}
|
||||
itemMessagePic := message.Message{message.Image("base64://" + binary.BytesToString(content))}
|
||||
fv[2*idx+1] = itemMessagePic
|
||||
fv[2*idx+2] = message.Message{message.Text(item.Link)}
|
||||
}
|
||||
return fv, nil
|
||||
}
|
||||
|
||||
// newRssSourcesMsg Rss订阅源列表
|
||||
func newRssSourcesMsg(ctx *zero.Ctx, view []*domain.RssClientView) (message.Message, error) {
|
||||
var msgSlice []message.Message
|
||||
// 生成消息
|
||||
for _, v := range view {
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
item, err := formatRssViewToMessagesSlice(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgSlice = append(msgSlice, item...)
|
||||
}
|
||||
// 伪造一个发送者为RssHub订阅姬的消息节点
|
||||
msg := make(message.Message, len(msgSlice))
|
||||
for i, item := range msgSlice {
|
||||
msg[i] = fakeSenderForwardNode(ctx.Event.SelfID, item...)
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// newRssDetailsMsg Rss订阅源详情(包含文章信息列表)
|
||||
func newRssDetailsMsg(ctx *zero.Ctx, view *domain.RssClientView) (message.Message, error) {
|
||||
// 生成消息
|
||||
msgSlice, err := formatRssViewToMessagesSlice(view)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 伪造一个发送者为RssHub订阅姬的消息节点
|
||||
msg := make(message.Message, len(msgSlice))
|
||||
for i, item := range msgSlice {
|
||||
msg[i] = fakeSenderForwardNode(ctx.Event.SelfID, item...)
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// fakeSenderForwardNode 伪造一个发送者为RssHub订阅姬的消息节点
|
||||
func fakeSenderForwardNode(userID int64, msgs ...message.Segment) message.Segment {
|
||||
return message.CustomNode(
|
||||
"RssHub订阅姬",
|
||||
userID,
|
||||
msgs)
|
||||
}
|
||||
@@ -2,19 +2,10 @@
|
||||
package thesaurus
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ego/gse"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/kimoi"
|
||||
"github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/floatbox/process"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
)
|
||||
@@ -23,143 +14,35 @@ func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "词典匹配回复, 仅@触发",
|
||||
Help: "- 切换[kimo|傲娇|可爱]词库",
|
||||
PublicDataFolder: "Chat",
|
||||
})
|
||||
engine.OnRegex(`^切换(kimo|傲娇|可爱)词库$`, zero.AdminPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: 找不到 manager"))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
d := c.GetData(gid)
|
||||
t := int64(0)
|
||||
switch ctx.State["regex_matched"].([]string)[1] {
|
||||
case "kimo":
|
||||
t = tKIMO
|
||||
case "傲娇":
|
||||
t = tDERE
|
||||
case "可爱":
|
||||
t = tKAWA
|
||||
}
|
||||
err := c.SetData(gid, (d&^3)|t)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
})
|
||||
go func() {
|
||||
var seg gse.Segmenter
|
||||
err := seg.LoadDictEmbed()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
smd, err := engine.GetLazyData("simai.yml", false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sm := simai{D: make(map[string][]string, 8192), K: make(map[string][]string, 16384)}
|
||||
err = yaml.Unmarshal(smd, &sm)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
chatListD := make([]string, 0, len(sm.D))
|
||||
for k := range sm.D {
|
||||
chatListD = append(chatListD, k)
|
||||
}
|
||||
chatListK := make([]string, 0, len(sm.K))
|
||||
for k := range sm.K {
|
||||
chatListK = append(chatListK, k)
|
||||
}
|
||||
logrus.Infoln("[thesaurus]加载", len(chatListD), "条傲娇词库", len(chatListK), "条可爱词库")
|
||||
|
||||
engine.OnMessage(zero.OnlyToMe, canmatch(tKIMO)).
|
||||
SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
msg := ctx.ExtractPlainText()
|
||||
r, err := kimoi.Chat(msg)
|
||||
if err == nil {
|
||||
c := 0
|
||||
for r.Confidence < 0.2 && c < 3 {
|
||||
r, err = kimoi.Chat(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c++
|
||||
}
|
||||
if r.Confidence < 0.2 {
|
||||
engine.OnMessage(zero.OnlyToMe, canmatch()).
|
||||
SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
msg := ctx.ExtractPlainText()
|
||||
r, err := kimoi.Chat(msg)
|
||||
if err == nil {
|
||||
c := 0
|
||||
for r.Confidence < 0.2 && c < 3 {
|
||||
r, err = kimoi.Chat(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ctx.Block()
|
||||
ctx.SendChain(message.Text(r.Reply))
|
||||
c++
|
||||
}
|
||||
})
|
||||
engine.OnMessage(zero.OnlyToMe, canmatch(tDERE), match(chatListD, &seg)).
|
||||
SetBlock(false).
|
||||
Handle(randreply(sm.D))
|
||||
engine.OnMessage(zero.OnlyToMe, canmatch(tKAWA), match(chatListK, &seg)).
|
||||
SetBlock(false).
|
||||
Handle(randreply(sm.K))
|
||||
}()
|
||||
if r.Confidence < 0.2 {
|
||||
return
|
||||
}
|
||||
ctx.Block()
|
||||
ctx.SendChain(message.Text(r.Reply))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type simai struct {
|
||||
D map[string][]string `yaml:"傲娇"`
|
||||
K map[string][]string `yaml:"可爱"`
|
||||
}
|
||||
|
||||
const (
|
||||
tKIMO = iota
|
||||
tDERE
|
||||
tKAWA
|
||||
)
|
||||
|
||||
func match(l []string, seg *gse.Segmenter) zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
return ctxext.JiebaSimilarity(0.66, seg, func(ctx *zero.Ctx) string {
|
||||
return ctx.ExtractPlainText()
|
||||
}, l...)(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func canmatch(typ int64) zero.Rule {
|
||||
func canmatch() zero.Rule {
|
||||
return func(ctx *zero.Ctx) bool {
|
||||
if zero.HasPicture(ctx) {
|
||||
return false
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
d := c.GetData(gid)
|
||||
return ctx.ExtractPlainText() != "" && d&3 == typ
|
||||
}
|
||||
}
|
||||
|
||||
func randreply(m map[string][]string) zero.Handler {
|
||||
return func(ctx *zero.Ctx) {
|
||||
ctx.Block()
|
||||
key := ctx.State["matched"].(string)
|
||||
val := m[key]
|
||||
nick := zero.BotConfig.NickName[rand.Intn(len(zero.BotConfig.NickName))]
|
||||
text := val[rand.Intn(len(val))]
|
||||
text = strings.ReplaceAll(text, "{name}", ctx.CardOrNickName(ctx.Event.UserID))
|
||||
text = strings.ReplaceAll(text, "{me}", nick)
|
||||
id := ctx.Event.MessageID
|
||||
for _, t := range strings.Split(text, "{segment}") {
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
process.SleepAbout1sTo2s()
|
||||
id = ctx.SendChain(message.Reply(id), message.Text(t))
|
||||
}
|
||||
return ctx.ExtractPlainText() != ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package wordcount
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-ego/gse"
|
||||
"github.com/fumiama/jieba"
|
||||
"github.com/golang/freetype"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
@@ -26,13 +27,11 @@ import (
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
)
|
||||
|
||||
var (
|
||||
re = regexp.MustCompile(`^[一-龥]+$`)
|
||||
stopwords []string
|
||||
seg gse.Segmenter
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -43,8 +42,11 @@ func init() {
|
||||
PublicDataFolder: "WordCount",
|
||||
})
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
// 读取gse内置中文词典
|
||||
err := seg.LoadDictEmbed()
|
||||
dat, err := file.GetLazyData("data/Chat/dict.txt", control.Md5File, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
seg, err := jieba.LoadDictionary(bytes.NewReader(dat))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -108,8 +110,7 @@ func init() {
|
||||
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
|
||||
tex := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
|
||||
if tex != "" {
|
||||
segments := seg.Segment(helper.StringToBytes(tex))
|
||||
words := gse.ToSlice(segments, true)
|
||||
words := seg.Cut(tex, true)
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
i := sort.SearchStrings(stopwords, word)
|
||||
|
||||
Reference in New Issue
Block a user