mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-06 15:20:22 +00:00
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d30750f42 | ||
|
|
78e1aa6783 | ||
|
|
9bfaa0c195 | ||
|
|
36397fb583 | ||
|
|
ae165349b9 | ||
|
|
1584ab8b11 | ||
|
|
5283e6de61 | ||
|
|
caa66d6715 | ||
|
|
f77d9b3509 | ||
|
|
afc1e034e0 | ||
|
|
67c36aab29 | ||
|
|
3e837bf64c | ||
|
|
c2af29f23c | ||
|
|
71d43eb101 | ||
|
|
57c41a7db2 | ||
|
|
91d512498d | ||
|
|
ec59133f96 | ||
|
|
8fc733ffd1 | ||
|
|
6deaa8b36e | ||
|
|
e187a3159c | ||
|
|
0d80a43055 | ||
|
|
9bad003301 | ||
|
|
a5a1edda2d | ||
|
|
6a5ffa0b16 | ||
|
|
531ea7fa0c | ||
|
|
f23e44503c | ||
|
|
3f0e284cee | ||
|
|
6634d94ff4 | ||
|
|
bf3ecfcb55 | ||
|
|
6a83d74eb7 | ||
|
|
a13486faef | ||
|
|
a828f49012 | ||
|
|
2ac3730f87 | ||
|
|
b897a11158 | ||
|
|
62a57fa61d | ||
|
|
0142050d6a | ||
|
|
9cfad308b0 | ||
|
|
28a8ee945c | ||
|
|
2154faec2c | ||
|
|
577b3f3f30 | ||
|
|
99c9cc3de6 | ||
|
|
216d99c05d | ||
|
|
8101c02ca8 | ||
|
|
da81875f8b | ||
|
|
dd1a0ae478 | ||
|
|
aafac8d6fa | ||
|
|
a74bcb869a | ||
|
|
584882282f | ||
|
|
05345e4c13 | ||
|
|
b8a57b80f2 | ||
|
|
177fdbae68 |
8
.github/workflows/gomod2nix.yml
vendored
8
.github/workflows/gomod2nix.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: 自动更新 nix 依赖
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'go.mod'
|
||||
- 'go.sum'
|
||||
@@ -23,6 +25,7 @@ jobs:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: gomod2nix update
|
||||
@@ -35,11 +38,12 @@ jobs:
|
||||
git config --local user.name 'github-actions[bot]'
|
||||
git config --local user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git add --all
|
||||
git commit -m "chore: bump deps"
|
||||
git commit -m "chore(nix): bump deps"
|
||||
- name: Create Pull Request
|
||||
if: ${{ !github.head_ref }}
|
||||
continue-on-error: true
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
delete-branch: true
|
||||
branch-suffix: short-commit-hash
|
||||
title: "chore(nix): bump deps"
|
||||
|
||||
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@@ -6,7 +6,7 @@ env:
|
||||
BINARY_PREFIX: "zbp_"
|
||||
BINARY_SUFFIX: ""
|
||||
PR_PROMPT: "::warning:: Build artifact will not be uploaded due to the workflow is trigged by pull request."
|
||||
LD_FLAGS: "-w -s -checklinkname=0"
|
||||
LD_FLAGS: "-w -s"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
13
.github/workflows/push.yml
vendored
13
.github/workflows/push.yml
vendored
@@ -1,5 +1,8 @@
|
||||
name: PushLint
|
||||
on: [ push ]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
@@ -8,12 +11,14 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@master
|
||||
with:
|
||||
go-version: '1.24'
|
||||
go-version: '1.25'
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Tidy Modules
|
||||
run: |
|
||||
@@ -37,4 +42,6 @@ jobs:
|
||||
- name: Create Pull Request
|
||||
if: ${{ !github.head_ref }}
|
||||
continue-on-error: true
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
title: "chore(lint): 改进代码样式"
|
||||
|
||||
@@ -57,6 +57,8 @@ run:
|
||||
issues-exit-code: 1
|
||||
tests: false
|
||||
go: '1.24'
|
||||
skip-dirs:
|
||||
- data
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
|
||||
@@ -26,7 +26,7 @@ builds:
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w -checklinkname=0
|
||||
- -s -w
|
||||
- id: win
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
@@ -39,7 +39,7 @@ builds:
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w -checklinkname=0
|
||||
- -s -w
|
||||
|
||||
checksum:
|
||||
name_template: "zbp_checksums.txt"
|
||||
|
||||
59
README.md
59
README.md
@@ -917,6 +917,15 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 疯狂星期四
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>大模型聊天和群聊总结</summary>
|
||||
|
||||
`_ "github.com/FloatTech/ZeroBot-Plugin/plugin/llm"`
|
||||
|
||||
- [x] 群聊总结 [消息数目]|群聊总结 1000
|
||||
- [x] /gpt [内容](使用大模型聊天)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>kokomi原神面板</summary>
|
||||
@@ -1284,6 +1293,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>
|
||||
@@ -1503,6 +1523,16 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- 注:由于需要科学,默认注释。
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>小红书文案</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/xhstext"`
|
||||
|
||||
- [x] 捧场
|
||||
|
||||
- [x] 有梗
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>游戏王白鸽API卡查</summary>
|
||||
@@ -1583,9 +1613,9 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
### *低优先级*
|
||||
|
||||
<details>
|
||||
<summary>OpenAI聊天</summary>
|
||||
<summary>大模型聊天和Agent配置</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat"`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichatcfg"`
|
||||
|
||||
- [x] 设置AI聊天触发概率10
|
||||
- [x] 设置AI聊天温度80
|
||||
@@ -1603,10 +1633,20 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
- [x] 设置AI聊天最大长度4096
|
||||
- [x] 设置AI聊天TopP 0.9
|
||||
- [x] 设置AI聊天(不)以AI语音输出
|
||||
- [x] 设置AI聊天Agent性格
|
||||
- [x] 查看AI聊天Agent性格
|
||||
- [x] 重置AI聊天Agent性格
|
||||
- [x] 重置AI聊天Agent
|
||||
- [x] 查看AI聊天配置
|
||||
- [x] 重置AI聊天
|
||||
- [x] 群聊总结 [消息数目]|群聊总结 1000
|
||||
- [x] /gpt [内容](使用大模型聊天)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>大模型聊天和Agent</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat"`
|
||||
|
||||
- [x] (随意聊天, 概率匹配)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -1674,6 +1714,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 文件,内容按需修改
|
||||
@@ -1681,15 +1722,15 @@ go mod tidy
|
||||
|
||||
```bash
|
||||
# 本机平台
|
||||
go build -ldflags "-s -w -checklinkname=0" -o zerobot -trimpath
|
||||
go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
# x64 Linux 平台 如各种云服务器
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -checklinkname=0" -o zerobot -trimpath
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
# x64 Windows 平台 如大多数家用电脑
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -checklinkname=0" -o zerobot.exe -trimpath
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o zerobot.exe -trimpath
|
||||
# armv6 Linux 平台 如树莓派 zero W
|
||||
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags "-s -w -checklinkname=0" -o zerobot -trimpath
|
||||
GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
# (由于引入了github.com/fumiama/sqlite3,本项不再可用)mips Linux 平台 如 路由器 wndr4300
|
||||
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w -checklinkname=0" -o zerobot -trimpath
|
||||
GOOS=linux GOARCH=mips GOMIPS=softfloat CGO_ENABLED=0 go build -ldflags "-s -w" -o zerobot -trimpath
|
||||
```
|
||||
|
||||
5. 运行 OneBot 框架,并同时运行本插件
|
||||
|
||||
2
data
2
data
Submodule data updated: 1b0abcd3fe...74e3bf5dc8
79
go.mod
79
go.mod
@@ -4,34 +4,34 @@ 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/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c
|
||||
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/zbpctrl v1.7.1
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20260106163901-1f0a51ecf35d
|
||||
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
|
||||
github.com/antchfx/htmlquery v1.3.5
|
||||
github.com/corona10/goimagehash v1.1.1-0.20240121134706-d8115886f360
|
||||
github.com/davidscholberg/go-durationfmt v0.0.0-20170122144659-64843a2083d3
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/fumiama/ahsai v0.1.1
|
||||
github.com/fumiama/cron v1.3.0
|
||||
github.com/fumiama/deepinfra v0.0.0-20250924162107-cf156d49a0fa
|
||||
github.com/fumiama/go-base16384 v1.7.0
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20250926145606-37ebfa6131c8
|
||||
github.com/fumiama/deepinfra v0.0.0-20251221163610-e98ee3ba437a
|
||||
github.com/fumiama/go-base16384 v1.7.1
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20260106161505-f7d25272034c
|
||||
github.com/fumiama/go-registry v0.2.7
|
||||
github.com/fumiama/gotracemoe v0.0.3
|
||||
github.com/fumiama/imgsz v0.0.4
|
||||
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 v1.0.2
|
||||
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,62 +39,67 @@ 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
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/shirou/gopsutil/v4 v4.25.12
|
||||
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
|
||||
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
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.3-0.20260103120253-8a8f1347f983
|
||||
gitlab.com/gomidi/midi/v2 v2.3.18
|
||||
golang.org/x/image v0.34.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/text v0.32.0
|
||||
)
|
||||
|
||||
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/antchfx/xpath v1.3.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/antchfx/xpath v1.3.5 // 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/ebitengine/purego v0.9.1 // 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/imgsz v0.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/fumiama/gofastTEA v0.1.3 // indirect
|
||||
github.com/fumiama/orbyte v0.0.0-20251002065953-3bb358367eb5 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/gopxl/beep/v2 v2.1.1 // indirect
|
||||
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/ncruces/go-strftime v0.1.9 // 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 v1.0.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // 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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/tetratelabs/wazero v1.5.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
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/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
modernc.org/libc v1.61.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/sqlite v1.33.1 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.67.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.41.0 // indirect
|
||||
)
|
||||
|
||||
replace modernc.org/sqlite => github.com/fumiama/sqlite3 v1.29.10-simp
|
||||
|
||||
174
go.sum
174
go.sum
@@ -1,9 +1,9 @@
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1 h1:RQsAmgDSAkiq22I6n7XJ2t3afgzFeqjY46FGhvrx4cw=
|
||||
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/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c h1:fmvlRUzwoK6KdoRSW+XeTQ9myKHimd0pV6GbmRJLNRo=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20251028071248-0c948e3db65c/go.mod h1:cuDd67B23xmICSmFBhWzXN51blod2BlM1liN9Ux0pSc=
|
||||
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=
|
||||
@@ -14,15 +14,17 @@ github.com/FloatTech/sqlite v1.7.2 h1:b8COegNLSzofzOyARsVwSbz9OOzWEa8IElsTlx1TBL
|
||||
github.com/FloatTech/sqlite v1.7.2/go.mod h1:/4tzfCGhrZnnjC1U8vcfwGQeF6eR649fhOsS3+Le0+s=
|
||||
github.com/FloatTech/ttl v0.0.0-20250224045156-012b1463287d h1:mUQ/c3wXKsUGa4Sg9DBy01APXKB68PmobhxOyaJI7lY=
|
||||
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/zbpctrl v1.7.1 h1:0yPEmCForhyMbnhTckmjDUFFDZgQp1RjO2bVF4ZVqOs=
|
||||
github.com/FloatTech/zbpctrl v1.7.1/go.mod h1:xmM4dSwHA02Gei3ogCRiG+RTrw/7Z69PfrN5NYf8BPE=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20260106163901-1f0a51ecf35d h1:Ur2R0bWD88qbY6lgEQ21QojZ7ErQRWDHz4RA4NNX8gw=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20260106163901-1f0a51ecf35d/go.mod h1:O3Js6obaao6BhxHx7hl1xwup4jXm/Fk2cmZEg5qNur8=
|
||||
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,12 +32,14 @@ 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/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=
|
||||
github.com/antchfx/xpath v1.3.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI=
|
||||
github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
|
||||
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.5 h1:aYthDDClnG2a2xePf6tys/UyyM/kRcsFRm+ifhFKoU0=
|
||||
github.com/antchfx/htmlquery v1.3.5/go.mod h1:5oyIPIa3ovYGtLqMPNjBF2Uf25NPCKsMjCnQ8lvjaoA=
|
||||
github.com/antchfx/xpath v1.3.5 h1:PqbXLC3TkfeZyakF5eeh3NTWEbYl4VHNVeufANzDbKQ=
|
||||
github.com/antchfx/xpath v1.3.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/corona10/goimagehash v1.1.1-0.20240121134706-d8115886f360 h1:SvD9vQN+3r0wskoSrQ7IOyDmOtRIXhT3rlnf819r/bY=
|
||||
github.com/corona10/goimagehash v1.1.1-0.20240121134706-d8115886f360/go.mod h1:PFDP0Q0oYvEuipJHMSbeK8tjbuRzkQqjPDYm3n71ITA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -49,8 +53,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/oto/v3 v3.3.2 h1:VTWBsKX9eb+dXzaF4jEwQbs4yWIdXukJ0K40KgkpYlg=
|
||||
github.com/ebitengine/oto/v3 v3.3.2/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
|
||||
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
|
||||
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 h1:BBade+JlV/f7JstZ4pitd4tHhpN+w+6I+LyOS7B4fyU=
|
||||
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4/go.mod h1:H7chHJglrhPPzetLdzBleF8d22WYOv7UM/lEKYiwlKM=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
@@ -59,18 +63,18 @@ github.com/fumiama/ahsai v0.1.1 h1:/t5tdKRim0TK6YwgNFQfqtDOW7Y2tFBsmdUWt3JK+C0=
|
||||
github.com/fumiama/ahsai v0.1.1/go.mod h1:rBhHLgN2bygcqLpBi+XQa8B8Afn4UkPHQ5vvQibdbDQ=
|
||||
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/deepinfra v0.0.0-20250924162107-cf156d49a0fa h1:UMMNejpPp8dn92GPaVSZ2XKNSgp7+CVneOkZfExUilk=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250924162107-cf156d49a0fa/go.mod h1:uqsWK/GM9OvKV0pXZOQB63rWugBbiXInY8E1JoRKhkg=
|
||||
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-onebot-agent v0.0.0-20250926145606-37ebfa6131c8 h1:aXk5IVXvPy2IfajL6gH+V/6ZOVV1BBVKjnFISLvyw60=
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20250926145606-37ebfa6131c8/go.mod h1:oH8DGDpRPjUAu8Fd/K+RxsB+z0Yis+BHeJAh+ZkO5EM=
|
||||
github.com/fumiama/deepinfra v0.0.0-20251221163610-e98ee3ba437a h1:a0+2vaXajfxsNcIaYG7A/cAb2e4+M3s5J6oyIiPsL3c=
|
||||
github.com/fumiama/deepinfra v0.0.0-20251221163610-e98ee3ba437a/go.mod h1:uqsWK/GM9OvKV0pXZOQB63rWugBbiXInY8E1JoRKhkg=
|
||||
github.com/fumiama/go-base16384 v1.7.1 h1:1P1x6FWRvd7PtbH4idDAGWAjKKcVxggxlROYKRXbw58=
|
||||
github.com/fumiama/go-base16384 v1.7.1/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20260106161505-f7d25272034c h1:kCAFF41GkJklKvftchjuEpo4Mp/qUSiEGBeChs4QJdk=
|
||||
github.com/fumiama/go-onebot-agent v0.0.0-20260106161505-f7d25272034c/go.mod h1:rTrS23rvTYuZcSngENJTvcBFTz1nGsImSv+bW7yfhqs=
|
||||
github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnOHeNs=
|
||||
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 v1.0.2 h1:Dxj2vPIgLHyeMlBd61xYHx5Jg8v7k24yg7taNLlK8/8=
|
||||
github.com/fumiama/terasu v1.0.2/go.mod h1:1eHzpL/GJVcOnqEkh1vUbKu2zo6gojSuueUNJ9yHJE0=
|
||||
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/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
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=
|
||||
@@ -105,8 +109,10 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
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/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
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 +129,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,10 +149,19 @@ 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=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
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/notnil/chess v1.10.0 h1:RR3MgS9G6zZmJ+VPTJolyxdaIgxoUPyUUY+2iaw35G0=
|
||||
@@ -159,48 +176,43 @@ github.com/pkumza/numcn v1.0.0 h1:ZT5cf9IJkUZgRgEtCiNNykk0RwsrKXSTsvDHOwUTzgE=
|
||||
github.com/pkumza/numcn v1.0.0/go.mod h1:QSeH+al9dWCd8di5HZM/ZqHqhZmUKfph572e9Ev/ETc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
||||
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=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
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/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
|
||||
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
|
||||
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
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.3-0.20260103120253-8a8f1347f983 h1:hb2FUDooAf3u32wCTgJcBBuGPZF1sjgj8NfJaSy529s=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.3-0.20260103120253-8a8f1347f983/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=
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16 h1:yufWSENyjnJ4LFQa9BerzUm4E4aLfTyzw5nmnCteO0c=
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16/go.mod h1:jDpP4O4skYi+7iVwt6Zyp18bd2M4hkjtMuw2cmgKgfw=
|
||||
gitlab.com/gomidi/midi/v2 v2.3.18 h1:sj2fOhtvOe+zI8YJe8qTxLw5zv0ntULLUDwcFOaZQbI=
|
||||
gitlab.com/gomidi/midi/v2 v2.3.18/go.mod h1:jDpP4O4skYi+7iVwt6Zyp18bd2M4hkjtMuw2cmgKgfw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -209,25 +221,26 @@ 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.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
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=
|
||||
golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA=
|
||||
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
|
||||
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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 +248,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.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
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=
|
||||
@@ -244,14 +257,15 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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=
|
||||
@@ -259,13 +273,12 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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=
|
||||
@@ -277,6 +290,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=
|
||||
@@ -285,16 +299,16 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
@@ -310,10 +324,10 @@ modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
|
||||
155
gomod2nix.toml
155
gomod2nix.toml
@@ -5,11 +5,11 @@ schema = 3
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-hKshA0K92bKuK92mmtM0osVmqLJcSbeobeWSDpQoRCo="
|
||||
[mod."github.com/FloatTech/AnimeAPI"]
|
||||
version = "v1.7.1-0.20250926171956-ba37dfebfc4a"
|
||||
hash = "sha256-wtr9hgHSPsD5NqSmI3Lo8RVWE+cNMIUGo2bcsKTN8Gg="
|
||||
version = "v1.7.1-0.20251028071248-0c948e3db65c"
|
||||
hash = "sha256-EkC8QYgmXElKGM8GzcQ4r/c+lM1ysJOccfoeqhMtAvs="
|
||||
[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="
|
||||
@@ -26,17 +26,20 @@ schema = 3
|
||||
version = "v0.0.0-20250224045156-012b1463287d"
|
||||
hash = "sha256-C5xBt0roPgahradCOTgkhL+j5bvoSXmGwdqcu0aSczc="
|
||||
[mod."github.com/FloatTech/zbpctrl"]
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-HDDnE0oktWJH1tkxuQwUUbeJhmVwY5fyc/vR72D2mkU="
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-wkeiaUTpPVbpH7fcXeoLtG+aGIMJbvoc/9sbi2IXK0I="
|
||||
[mod."github.com/FloatTech/zbputils"]
|
||||
version = "v1.7.2-0.20250930154951-b9dba62964b2"
|
||||
hash = "sha256-slSHly7Po3LXvmU99RKvdYHkecDzvNRExWLysUfQmFk="
|
||||
version = "v1.7.2-0.20260106163901-1f0a51ecf35d"
|
||||
hash = "sha256-Tb4X/usnhiNKaArlp96+KTKOdM4DK0SUZLHo8TlXi/c="
|
||||
[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,15 +49,18 @@ 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="
|
||||
version = "v1.3.5"
|
||||
hash = "sha256-AyfSTQY2eiNPhTS/FVgaBlSzPOObSaluhSee8Gvc8ho="
|
||||
[mod."github.com/antchfx/xpath"]
|
||||
version = "v1.3.3"
|
||||
hash = "sha256-Ent9bgBTjKS8/61LKrIu/JcBI/Qsv6EEIojwsMjCgdY="
|
||||
version = "v1.3.5"
|
||||
hash = "sha256-AVM0rR81hgVAI0QVzlz4WijFUjByf6Zew3ZwuikKw2Q="
|
||||
[mod."github.com/corona10/goimagehash"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-HyS8nc7kUNnDaVBDzJ9Ym4pRs83YB4M2vHSRwfm6mr4="
|
||||
version = "v1.1.1-0.20240121134706-d8115886f360"
|
||||
hash = "sha256-/corDVYILmy/DMWtgM1D1MK6I7pejA5JNfaxeojwkkA="
|
||||
[mod."github.com/davidscholberg/go-durationfmt"]
|
||||
version = "v0.0.0-20170122144659-64843a2083d3"
|
||||
hash = "sha256-0rdbpBf3AAjMpxvVEGFb2ImgB2i7vdEhIwCyqJs1iHE="
|
||||
@@ -68,8 +74,8 @@ schema = 3
|
||||
version = "v3.3.2"
|
||||
hash = "sha256-TPu3qvJscLZbjwIqC3jj0T1md0mX3lQxcC8GAk7kB1w="
|
||||
[mod."github.com/ebitengine/purego"]
|
||||
version = "v0.8.0"
|
||||
hash = "sha256-fM+5HYIKDPLDrvv2f9/WQG0F20+o0+At0AbPQtF2JP0="
|
||||
version = "v0.9.1"
|
||||
hash = "sha256-iVfU8vaJ7IPa92dUeHeuW+yKvUbe59F/eV7GlDRIAcE="
|
||||
[mod."github.com/ericpauley/go-quantize"]
|
||||
version = "v0.0.0-20200331213906-ae555eb2afa4"
|
||||
hash = "sha256-sMN6D7IlDpDqUWM8ppoE5Sdb7DvLAJaN6qAucBWJ3rs="
|
||||
@@ -80,14 +86,14 @@ schema = 3
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-/sN7X8dKXQgv8J+EDzVUB+o+AY9gBC8e1C6sYhaTy1k="
|
||||
[mod."github.com/fumiama/deepinfra"]
|
||||
version = "v0.0.0-20250924162107-cf156d49a0fa"
|
||||
hash = "sha256-D0lgA7jBDLE8v9ePDiWwH439eB5+cDlj2fKRAc0wUms="
|
||||
version = "v0.0.0-20251221163610-e98ee3ba437a"
|
||||
hash = "sha256-XubewUDOLaN7fgv9E8PAfP7DtADdLm+M2sgSn5OE6eQ="
|
||||
[mod."github.com/fumiama/go-base16384"]
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-vTAsBBYe2ISzb2Nba5E96unodZSkhMcqo6hbwR01nz8="
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-Fd1QaeYx+3q4C3XQXlPFnDmKPsoZH6837fN/7rn8i9s="
|
||||
[mod."github.com/fumiama/go-onebot-agent"]
|
||||
version = "v0.0.0-20250926145606-37ebfa6131c8"
|
||||
hash = "sha256-oq1SSyddsXhsFvSHFhqSEAF9SH03b2jI0KvGbsFPXYQ="
|
||||
version = "v0.0.0-20260106161505-f7d25272034c"
|
||||
hash = "sha256-+bu2VbZVwS7SDgfx53aZjQxSRtrXFi+g0Jn8W9w6QQ8="
|
||||
[mod."github.com/fumiama/go-registry"]
|
||||
version = "v0.2.7"
|
||||
hash = "sha256-Rjl+z0Hlp2LMi8+pnFe5HrxctyHMi7UPiK33g/OgLdA="
|
||||
@@ -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 = "v1.0.2"
|
||||
hash = "sha256-tYHvvfJT1/R4koqGFPjdU211Dcez2XWuvpRke/tFfQ8="
|
||||
[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="
|
||||
version = "v1.4.12"
|
||||
hash = "sha256-vY2g58yUrkT//8fttRKhS9rbg89YSae/BzOARS5uH30="
|
||||
[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,12 +181,24 @@ 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="
|
||||
[mod."github.com/ncruces/go-strftime"]
|
||||
version = "v0.1.9"
|
||||
hash = "sha256-T0iw+UEckzueWHT88PkTnZZixyKCEa+DTLzIiiohuWY="
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-GYIwYDONuv/yTE0AEugCHQbtV3oiBaco93xUNYFcVBQ="
|
||||
[mod."github.com/nfnt/resize"]
|
||||
version = "v0.0.0-20180221191011-83c6a9932646"
|
||||
hash = "sha256-yvPV+HlDOyJsiwAcVHQkmtw8DHSXyw+cXHkigXm8rAA="
|
||||
@@ -194,17 +215,14 @@ schema = 3
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-cPxqj5tb10+MurN1Lehkk/v8KjaxXpL08+pVgL4x4Hg="
|
||||
[mod."github.com/power-devops/perfstat"]
|
||||
version = "v0.0.0-20210106213030-5aafc221ea8c"
|
||||
hash = "sha256-ywykDYuqcMt0TvZOz1l9Z6Z2JMTYQw8cP2fT8AtpmX4="
|
||||
version = "v0.0.0-20240221224432-82ca36839d55"
|
||||
hash = "sha256-ujzuJ1ttQgjHQJEij4O/2+I8DZaUVZQCQgA4ysfqulI="
|
||||
[mod."github.com/remyoudompheng/bigfft"]
|
||||
version = "v0.0.0-20230129092748-24d4a6f8daec"
|
||||
hash = "sha256-vYmpyCE37eBYP/navhaLV4oX4/nu0Z/StAocLIFqrmM="
|
||||
[mod."github.com/shirou/gopsutil/v3"]
|
||||
version = "v3.24.5"
|
||||
hash = "sha256-tc+t1u7gf5A+Bd956dYeM8pGbxs9ezQHqKAKfLQLpuQ="
|
||||
[mod."github.com/shoenig/go-m1cpu"]
|
||||
version = "v0.1.6"
|
||||
hash = "sha256-hT+JP30BBllsXosK/lo89HV/uxxPLsUyO3dRaDiLnCg="
|
||||
[mod."github.com/shirou/gopsutil/v4"]
|
||||
version = "v4.25.12"
|
||||
hash = "sha256-gzk9GW4+tXUWmxAVD3by/k4D/+l++TvajRVTkQJvwmM="
|
||||
[mod."github.com/sirupsen/logrus"]
|
||||
version = "v1.9.3"
|
||||
hash = "sha256-EnxsWdEUPYid+aZ9H4/iMTs1XMvCLbXZRDyvj89Ebms="
|
||||
@@ -215,44 +233,41 @@ schema = 3
|
||||
version = "v1.18.0"
|
||||
hash = "sha256-CO6hqDu8Y58Po6A01e5iTpwiUBQ5khUZsw7czaJHw0I="
|
||||
[mod."github.com/tidwall/match"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-M2klhPId3Q3T3VGkSbOkYl/2nLHnsG+yMbXkPkyrRdg="
|
||||
[mod."github.com/tidwall/pretty"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-esRQGsn2Ee/CiySlwyuOICSLdqUkH4P7u8qXszos8Yc="
|
||||
hash = "sha256-O2wTU0SmNIEEOxfncl2BW2czgWeIW5vqR6+A7dtNtXI="
|
||||
[mod."github.com/tidwall/pretty"]
|
||||
version = "v1.2.1"
|
||||
hash = "sha256-S0uTDDGD8qr415Ut7QinyXljCp0TkL4zOIrlJ+9OMl8="
|
||||
[mod."github.com/tklauser/go-sysconf"]
|
||||
version = "v0.3.12"
|
||||
hash = "sha256-91VBZNb3L2TZkEETF1AE4wnraLoGxKeofUbC5ZiWVHk="
|
||||
version = "v0.3.16"
|
||||
hash = "sha256-hNVbsk0G+M9bLtHNywPD0nCW0z/9pM4jQV53nnDo/MY="
|
||||
[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="
|
||||
version = "v0.11.0"
|
||||
hash = "sha256-ObydGqAvRiHwVfO2ytmL28pRHyuCWYWwjH4hWdgI/Vs="
|
||||
[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.3-0.20260103120253-8a8f1347f983"
|
||||
hash = "sha256-/6biuhXUIYxyJ0uT1R5Yk8RD2cm9/SQWtFvcyQgnWJE="
|
||||
[mod."github.com/yusufpapurcu/wmi"]
|
||||
version = "v1.2.4"
|
||||
hash = "sha256-N+YDBjOW59YOsZ2lRBVtFsEEi48KhNQRb63/0ZSU3bA="
|
||||
[mod."gitlab.com/gomidi/midi/v2"]
|
||||
version = "v2.3.16"
|
||||
hash = "sha256-o+6UtQH+TRSQlcX8J53esAA/b2c9e7BY7gcO5iSeOy0="
|
||||
version = "v2.3.18"
|
||||
hash = "sha256-lkU9M+h56+Ai/bpQDST3Su71dhjp1Vk2S7/okrELo7s="
|
||||
[mod."golang.org/x/image"]
|
||||
version = "v0.31.0"
|
||||
hash = "sha256-ZFTlu9+4QToPPLA8C5UcG2eq/lQylq81RoG/WtYo9rg="
|
||||
version = "v0.34.0"
|
||||
hash = "sha256-7V1bDhd++dCw11DCXOAUb5f1Hj51EfS0DZ03pq0V/p0="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.42.0"
|
||||
hash = "sha256-YxileisIIez+kcAI+21kY5yk0iRuEqti2YdmS8jvP2s="
|
||||
version = "v0.48.0"
|
||||
hash = "sha256-oZpddsiJwWCH3Aipa+XXpy7G/xHY5fEagUSok7T0bXE="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.36.0"
|
||||
hash = "sha256-9h4SHGnlJzmTENUp6226hC8fQ73QrQC3D85NNMxLuXg="
|
||||
version = "v0.39.0"
|
||||
hash = "sha256-dxTBu/JAWUkPbjFIXXRFdhQWyn+YyEpIC+tWqGo0Y6U="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.29.0"
|
||||
hash = "sha256-2cWBtJje+Yc+AnSgCANqBlIwnOMZEGkpQ2cFI45VfLI="
|
||||
version = "v0.32.0"
|
||||
hash = "sha256-9PXtWBKKY9rG4AgjSP4N+I1DhepXhy8SF/vWSIDIoWs="
|
||||
[mod."gopkg.in/yaml.v3"]
|
||||
version = "v3.0.1"
|
||||
hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU="
|
||||
@@ -261,11 +276,11 @@ schema = 3
|
||||
hash = "sha256-SJYYRaiDUmIbqy9l/IgiT/4VkFsPYsaslqGEowut34w="
|
||||
replaced = "github.com/fumiama/libc"
|
||||
[mod."modernc.org/mathutil"]
|
||||
version = "v1.6.0"
|
||||
hash = "sha256-lfuEiS1odd2TWrTylnaGihSJ9myqKs3FLdpvd7PqTnE="
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-COZ5rF2GhQVR1r6a0DanJ8qwQ94JSKdQxTMWrDzE0Cc="
|
||||
[mod."modernc.org/memory"]
|
||||
version = "v1.8.0"
|
||||
hash = "sha256-ucvPr73zg8LjvU+bcoIPKTgwgcon3U9VhKrLEMH81xg="
|
||||
version = "v1.11.0"
|
||||
hash = "sha256-MkybF8vvrxXS5j7O8w3skwTo0aMo1yjWS0K440rYcHM="
|
||||
[mod."modernc.org/sqlite"]
|
||||
version = "v1.29.10-simp"
|
||||
hash = "sha256-HCUVN6gZDG0g2WIsQ4ksqE1+XR1IjxvnqEBEU2MO1eE="
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
package banner
|
||||
|
||||
// Version ...
|
||||
var Version = "v1.10.0"
|
||||
var Version = "v1.10.9"
|
||||
|
||||
// Copyright ...
|
||||
var Copyright = "© 2020 - 2025 FloatTech"
|
||||
var Copyright = "© 2020 - 2026 FloatTech"
|
||||
|
||||
// Banner ...
|
||||
var Banner = "* OneBot + ZeroBot + Golang\n" +
|
||||
"* Version " + Version + " - 2025-09-30 23:45:28 +0800 CST\n" +
|
||||
"* Version " + Version + " - 2026-01-06 21:24:56 +0800 CST\n" +
|
||||
"* Copyright " + Copyright + ". All Rights Reserved.\n" +
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
|
||||
|
||||
5
main.go
5
main.go
@@ -112,6 +112,7 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jptingroom" // 日语听力学习材料
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/kfccrazythursday" // 疯狂星期四
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/llm" // 大模型聊天和群聊总结
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolimi" // 桑帛云 API
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/magicprompt" // magicprompt吟唱提示
|
||||
@@ -136,6 +137,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" // 分数
|
||||
@@ -153,6 +155,7 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wife" // 抽老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordcount" // 聊天热词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/xhstext" // 小红书文案
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygocdb" // 游戏王白鸽API卡查
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ygotrade" // 游戏王集换社卡价查询
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
|
||||
@@ -178,6 +181,8 @@ import (
|
||||
// vvvvvvvvvvvvvv //
|
||||
// vvvv //
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichatcfg" // AI聊天配置
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat" // AI聊天
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/curse" // 骂人
|
||||
|
||||
@@ -1,302 +0,0 @@
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/fumiama/deepinfra"
|
||||
"github.com/fumiama/deepinfra/model"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg = newconfig()
|
||||
)
|
||||
|
||||
var (
|
||||
apitypes = map[string]uint8{
|
||||
"OpenAI": 0,
|
||||
"OLLaMA": 1,
|
||||
"GenAI": 2,
|
||||
}
|
||||
apilist = [3]string{"OpenAI", "OLLaMA", "GenAI"}
|
||||
)
|
||||
|
||||
// ModelType 支持打印 string 并生产 protocal
|
||||
type ModelType int
|
||||
|
||||
func newModelType(typ string) (ModelType, error) {
|
||||
t, ok := apitypes[typ]
|
||||
if !ok {
|
||||
return 0, errors.New("未知类型 " + typ)
|
||||
}
|
||||
return ModelType(t), nil
|
||||
}
|
||||
|
||||
func (mt ModelType) String() string {
|
||||
return apilist[mt]
|
||||
}
|
||||
|
||||
func (mt ModelType) protocol(modn string, temp float32, topp float32, maxn uint) (mod model.Protocol, err error) {
|
||||
switch cfg.Type {
|
||||
case 0:
|
||||
mod = model.NewOpenAI(
|
||||
modn, cfg.Separator,
|
||||
temp, topp, maxn,
|
||||
)
|
||||
case 1:
|
||||
mod = model.NewOLLaMA(
|
||||
modn, cfg.Separator,
|
||||
temp, topp, maxn,
|
||||
)
|
||||
case 2:
|
||||
mod = model.NewGenAI(
|
||||
modn,
|
||||
temp, topp, maxn,
|
||||
)
|
||||
default:
|
||||
err = errors.New("unsupported model type " + strconv.Itoa(int(cfg.Type)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ModelBool 支持打印成 "是/否"
|
||||
type ModelBool bool
|
||||
|
||||
func (mb ModelBool) String() string {
|
||||
if mb {
|
||||
return "是"
|
||||
}
|
||||
return "否"
|
||||
}
|
||||
|
||||
// ModelKey 支持隐藏密钥
|
||||
type ModelKey string
|
||||
|
||||
func (mk ModelKey) String() string {
|
||||
if len(mk) == 0 {
|
||||
return "未设置"
|
||||
}
|
||||
if len(mk) <= 4 {
|
||||
return "****"
|
||||
}
|
||||
key := string(mk)
|
||||
return key[:2] + strings.Repeat("*", len(key)-4) + key[len(key)-2:]
|
||||
}
|
||||
|
||||
type config struct {
|
||||
ModelName string
|
||||
ImageModelName string
|
||||
AgentModelName string
|
||||
Type ModelType
|
||||
ImageType ModelType
|
||||
AgentType ModelType
|
||||
MaxN uint
|
||||
TopP float32
|
||||
SystemP string
|
||||
API string
|
||||
ImageAPI string
|
||||
AgentAPI string
|
||||
Key ModelKey
|
||||
ImageKey ModelKey
|
||||
AgentKey ModelKey
|
||||
Separator string
|
||||
NoSystemP ModelBool
|
||||
}
|
||||
|
||||
func newconfig() config {
|
||||
return config{
|
||||
ModelName: model.ModelDeepDeek,
|
||||
SystemP: chat.SystemPrompt,
|
||||
API: deepinfra.OpenAIDeepInfra,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *config) String() string {
|
||||
topp, maxn := c.mparams()
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(fmt.Sprintf("• 模型名:%s\n", c.ModelName))
|
||||
sb.WriteString(fmt.Sprintf("• 图像模型名:%s\n", c.ImageModelName))
|
||||
sb.WriteString(fmt.Sprintf("• Agent模型名:%s\n", c.AgentModelName))
|
||||
sb.WriteString(fmt.Sprintf("• 接口类型:%v\n", c.Type))
|
||||
sb.WriteString(fmt.Sprintf("• 图像接口类型:%v\n", c.ImageType))
|
||||
sb.WriteString(fmt.Sprintf("• Agent接口类型:%v\n", c.AgentType))
|
||||
sb.WriteString(fmt.Sprintf("• 最大长度:%d\n", maxn))
|
||||
sb.WriteString(fmt.Sprintf("• TopP:%.1f\n", topp))
|
||||
sb.WriteString(fmt.Sprintf("• 系统提示词:%s\n", c.SystemP))
|
||||
sb.WriteString(fmt.Sprintf("• 接口地址:%s\n", c.API))
|
||||
sb.WriteString(fmt.Sprintf("• 图像接口地址:%s\n", c.ImageAPI))
|
||||
sb.WriteString(fmt.Sprintf("• Agent接口地址:%s\n", c.AgentAPI))
|
||||
sb.WriteString(fmt.Sprintf("• 密钥:%v\n", c.Key))
|
||||
sb.WriteString(fmt.Sprintf("• 图像密钥:%v\n", c.ImageKey))
|
||||
sb.WriteString(fmt.Sprintf("• Agent密钥:%v\n", c.AgentKey))
|
||||
sb.WriteString(fmt.Sprintf("• 分隔符:%s\n", c.Separator))
|
||||
sb.WriteString(fmt.Sprintf("• 支持系统提示词:%v\n", !c.NoSystemP))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (c *config) isvalid() bool {
|
||||
return c.ModelName != "" && c.API != "" && c.Key != ""
|
||||
}
|
||||
|
||||
// 获取全局模型参数:TopP和最大长度
|
||||
func (c *config) mparams() (topp float32, maxn uint) {
|
||||
// 处理TopP参数
|
||||
topp = c.TopP
|
||||
if topp == 0 {
|
||||
topp = 0.9
|
||||
}
|
||||
|
||||
// 处理最大长度参数
|
||||
maxn = c.MaxN
|
||||
if maxn == 0 {
|
||||
maxn = 4096
|
||||
}
|
||||
|
||||
return topp, maxn
|
||||
}
|
||||
|
||||
func ensureconfig(ctx *zero.Ctx) bool {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !cfg.isvalid() {
|
||||
err := c.GetExtra(&cfg)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: get extra err:", err)
|
||||
}
|
||||
if !cfg.isvalid() {
|
||||
cfg = newconfig()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func newextrasetstr[T ~string](ptr *T) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
*ptr = T(args)
|
||||
err := c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetbool[T ~bool](ptr *T) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := ctx.State["regex_matched"].([]string)
|
||||
isno := args[1] == "不"
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
*ptr = T(isno)
|
||||
err := c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetuint(ptr *uint) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseUint(args, 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = uint(n)
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetfloat32(ptr *float32) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseFloat(args, 32)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = float32(n)
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetmodeltype(ptr *ModelType) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
typ, err := newModelType(args)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = typ
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,13 @@
|
||||
// Package aichat OpenAI聊天和群聊总结
|
||||
// Package aichat 大模型聊天和Agent
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/deepinfra"
|
||||
"github.com/fumiama/deepinfra/model"
|
||||
goba "github.com/fumiama/go-onebot-agent"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||
@@ -23,7 +18,6 @@ import (
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,27 +25,8 @@ var (
|
||||
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("aichat"),
|
||||
Brief: "OpenAI聊天",
|
||||
Help: "- 设置AI聊天触发概率10\n" +
|
||||
"- 设置AI聊天温度80\n" +
|
||||
"- 设置AI聊天(识图|Agent)接口类型[OpenAI|OLLaMA|GenAI]\n" +
|
||||
"- 设置AI聊天(不)使用Agent模式\n" +
|
||||
"- 设置AI聊天(不)支持系统提示词\n" +
|
||||
"- 设置AI聊天(识图|Agent)接口地址https://api.siliconflow.cn/v1/chat/completions\n" +
|
||||
"- 设置AI聊天(识图|Agent)密钥xxx\n" +
|
||||
"- 设置AI聊天(识图|Agent)模型名Qwen/Qwen3-8B\n" +
|
||||
"- 查看AI聊天系统提示词\n" +
|
||||
"- 重置AI聊天系统提示词\n" +
|
||||
"- 设置AI聊天系统提示词xxx\n" +
|
||||
"- 设置AI聊天分隔符</think>(留空则清除)\n" +
|
||||
"- 设置AI聊天(不)响应AT\n" +
|
||||
"- 设置AI聊天最大长度4096\n" +
|
||||
"- 设置AI聊天TopP 0.9\n" +
|
||||
"- 设置AI聊天(不)以AI语音输出\n" +
|
||||
"- 查看AI聊天配置\n" +
|
||||
"- 重置AI聊天\n" +
|
||||
"- 群聊总结 [消息数目]|群聊总结 1000\n" +
|
||||
"- /gpt [内容] (使用大模型聊天)\n",
|
||||
Brief: "大模型聊天和Agent",
|
||||
Help: "- (随意聊天, 概率匹配)",
|
||||
|
||||
PrivateDataFolder: "aichat",
|
||||
}).ApplySingle(single.New(
|
||||
@@ -66,46 +41,46 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
limit = ctxext.NewLimiterManager(time.Second*30, 1)
|
||||
fastfailnorecord = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
en.OnMessage(ensureconfig, func(ctx *zero.Ctx) bool {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
en.OnMessage(chat.EnsureConfig, func(ctx *zero.Ctx) bool {
|
||||
stor, ok := ctx.State[zero.StateKeyPrefixKeep+"aichatcfg_stor__"].(chat.Storage)
|
||||
if !ok {
|
||||
logrus.Warnln("ERROR: cannot get stor")
|
||||
return false
|
||||
}
|
||||
ctx.State["__aichat_stor__"] = stor
|
||||
return ctx.ExtractPlainText() != "" &&
|
||||
(!stor.noreplyat() || (stor.noreplyat() && !ctx.Event.IsToMe))
|
||||
if !(ctx.ExtractPlainText() != "" &&
|
||||
(!stor.NoReplyAt() || (stor.NoReplyAt() && !ctx.Event.IsToMe))) {
|
||||
return false
|
||||
}
|
||||
rate := stor.Rate()
|
||||
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
|
||||
return false
|
||||
}
|
||||
if chat.AC.Key == "" {
|
||||
logrus.Warnln("ERROR: get extra err: empty key")
|
||||
return false
|
||||
}
|
||||
if ctx.Event.IsToMe {
|
||||
ctx.Block()
|
||||
}
|
||||
return true
|
||||
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
stor := ctx.State["__aichat_stor__"].(storage)
|
||||
rate := stor.rate()
|
||||
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
|
||||
return
|
||||
}
|
||||
if ctx.Event.IsToMe {
|
||||
ctx.Block()
|
||||
}
|
||||
if cfg.Key == "" {
|
||||
logrus.Warnln("ERROR: get extra err: empty key")
|
||||
return
|
||||
}
|
||||
temperature := stor.temp()
|
||||
topp, maxn := cfg.mparams()
|
||||
stor := ctx.State[zero.StateKeyPrefixKeep+"aichatcfg_stor__"].(chat.Storage)
|
||||
temperature := stor.Temp()
|
||||
topp, maxn := chat.AC.MParams()
|
||||
|
||||
if !stor.noagent() && cfg.AgentAPI != "" && cfg.AgentModelName != "" {
|
||||
x := deepinfra.NewAPI(cfg.AgentAPI, string(cfg.AgentKey))
|
||||
mod, err := cfg.Type.protocol(cfg.AgentModelName, temperature, topp, maxn)
|
||||
logrus.Debugln("[aichat] agent mode test: noagent", stor.NoAgent(), "hasapi", chat.AC.AgentAPI != "", "hasmodel", chat.AC.AgentModelName != "")
|
||||
if !stor.NoAgent() && chat.AC.AgentAPI != "" && chat.AC.AgentModelName != "" {
|
||||
logrus.Debugln("[aichat] enter agent mode")
|
||||
x := deepinfra.NewAPI(chat.AC.AgentAPI, string(chat.AC.AgentKey))
|
||||
mod, err := chat.AC.Type.Protocol(chat.AC.AgentModelName, temperature, topp, maxn)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return
|
||||
@@ -117,49 +92,81 @@ func init() {
|
||||
role = goba.PermRoleOwner
|
||||
}
|
||||
}
|
||||
ag := chat.AgentOf(ctx.Event.SelfID)
|
||||
if cfg.ImageAPI != "" && !ag.CanViewImage() {
|
||||
mod, err := cfg.ImageType.protocol(cfg.ImageModelName, temperature, topp, maxn)
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
logrus.Warnln("ERROR: cannot get ctrl mamager")
|
||||
}
|
||||
ag := chat.AgentOf(ctx.Event.SelfID, c.Service)
|
||||
logrus.Debugln("[aichat] got agent")
|
||||
if chat.AC.ImageAPI != "" && !ag.CanViewImage() {
|
||||
mod, err := chat.AC.ImageType.Protocol(chat.AC.ImageModelName, temperature, topp, maxn)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return
|
||||
}
|
||||
ag.SetViewImageAPI(deepinfra.NewAPI(cfg.ImageAPI, string(cfg.ImageKey)), mod)
|
||||
ag.SetViewImageAPI(deepinfra.NewAPI(chat.AC.ImageAPI, string(chat.AC.ImageKey)), mod)
|
||||
logrus.Debugln("[aichat] agent set img")
|
||||
}
|
||||
ctx.NoTimeout()
|
||||
logrus.Debugln("[aichat] agent set no timeout")
|
||||
hasresp := false
|
||||
ispuremsg := false
|
||||
hassavemem := false
|
||||
var (
|
||||
reqs []zero.APIRequest
|
||||
cl func()
|
||||
)
|
||||
defer func() {
|
||||
if cl != nil {
|
||||
cl()
|
||||
}
|
||||
}()
|
||||
for i := 0; i < 8; i++ { // 最大运行 8 轮因为问答上下文只有 16
|
||||
reqs := chat.CallAgent(ag, zero.SuperUserPermission(ctx), x, mod, gid, role)
|
||||
reqs, cl = chat.CallAgent(ag, zero.SuperUserPermission(ctx), i+1, x, mod, gid, role)
|
||||
if cl != nil {
|
||||
cl()
|
||||
cl = nil
|
||||
}
|
||||
if len(reqs) == 0 {
|
||||
logrus.Debugln("[aichat] agent call got empty response")
|
||||
break
|
||||
}
|
||||
hasresp = true
|
||||
for _, req := range reqs {
|
||||
resp := ctx.CallAction(req.Action, req.Params)
|
||||
logrus.Infoln("[aichat] agent get resp:", reqs)
|
||||
ag.AddResponse(gid, &goba.APIResponse{
|
||||
Status: resp.Status,
|
||||
Data: json.RawMessage(resp.Data.Raw),
|
||||
Message: resp.Message,
|
||||
Wording: resp.Wording,
|
||||
RetCode: resp.RetCode,
|
||||
})
|
||||
if req.Action == goba.SVM { // is a fake action
|
||||
if hassavemem {
|
||||
ag.AddTerminus(gid)
|
||||
logrus.Warnln("[aichat] agent call save mem multi times, force inserting EOA")
|
||||
return
|
||||
}
|
||||
hassavemem = true
|
||||
continue
|
||||
}
|
||||
if req.Action == "send_private_msg" || req.Action == "send_group_msg" {
|
||||
if ispuremsg {
|
||||
ag.AddTerminus(gid)
|
||||
logrus.Warnln("[aichat] agent call send msg multi times, force inserting EOA")
|
||||
return
|
||||
}
|
||||
ispuremsg = true
|
||||
}
|
||||
_ = ctx.CallAction(req.Action, req.Params)
|
||||
}
|
||||
}
|
||||
if hasresp {
|
||||
ag.AddTerminus(gid)
|
||||
return
|
||||
}
|
||||
// no response, fall back to normal chat
|
||||
logrus.Debugln("[aichat] agent fell back to normal chat")
|
||||
}
|
||||
|
||||
x := deepinfra.NewAPI(cfg.API, string(cfg.Key))
|
||||
mod, err := cfg.Type.protocol(cfg.ModelName, temperature, topp, maxn)
|
||||
x := deepinfra.NewAPI(chat.AC.API, string(chat.AC.Key))
|
||||
mod, err := chat.AC.Type.Protocol(chat.AC.ModelName, temperature, topp, maxn)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return
|
||||
}
|
||||
data, err := x.Request(chat.GetChatContext(mod, gid, cfg.SystemP, bool(cfg.NoSystemP)))
|
||||
data, err := x.Request(chat.GetChatContext(mod, gid, chat.AC.SystemP, bool(chat.AC.NoSystemP)))
|
||||
if err != nil {
|
||||
logrus.Warnln("[aichat] post err:", err)
|
||||
return
|
||||
@@ -182,295 +189,21 @@ func init() {
|
||||
logrus.Infoln("[aichat] 回复内容:", t)
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ""
|
||||
if !stor.norecord() {
|
||||
if !fastfailnorecord && !stor.NoRecord() {
|
||||
record = ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, t)
|
||||
}
|
||||
if record != "" {
|
||||
ctx.SendChain(message.Record(record))
|
||||
} else {
|
||||
if id != nil {
|
||||
id = ctx.SendChain(message.Reply(id), message.Text(t))
|
||||
} else {
|
||||
id = ctx.SendChain(message.Text(t))
|
||||
if record != "" {
|
||||
ctx.SendChain(message.Record(record))
|
||||
continue
|
||||
}
|
||||
fastfailnorecord = true
|
||||
}
|
||||
if id != nil {
|
||||
id = ctx.SendChain(message.Reply(id), message.Text(t))
|
||||
} else {
|
||||
id = ctx.SendChain(message.Text(t))
|
||||
}
|
||||
process.SleepAbout1sTo2s()
|
||||
}
|
||||
}
|
||||
})
|
||||
en.OnPrefix("设置AI聊天触发概率", zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBitmapHandler(bitmaprate, 0, 100))
|
||||
en.OnPrefix("设置AI聊天温度", zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBitmapHandler(bitmaptemp, 0, 100))
|
||||
en.OnPrefix("设置AI聊天接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetmodeltype(&cfg.Type))
|
||||
en.OnPrefix("设置AI聊天识图接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetmodeltype(&cfg.ImageType))
|
||||
en.OnPrefix("设置AI聊天Agent接口类型", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetmodeltype(&cfg.AgentType))
|
||||
en.OnPrefix("设置AI聊天接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.API))
|
||||
en.OnPrefix("设置AI聊天识图接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ImageAPI))
|
||||
en.OnPrefix("设置AI聊天Agent接口地址", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.AgentAPI))
|
||||
en.OnPrefix("设置AI聊天密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.Key))
|
||||
en.OnPrefix("设置AI聊天识图密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ImageKey))
|
||||
en.OnPrefix("设置AI聊天Agent密钥", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.AgentKey))
|
||||
en.OnPrefix("设置AI聊天模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ModelName))
|
||||
en.OnPrefix("设置AI聊天识图模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.ImageModelName))
|
||||
en.OnPrefix("设置AI聊天Agent模型名", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.AgentModelName))
|
||||
en.OnPrefix("设置AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.SystemP))
|
||||
en.OnFullMatch("查看AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(cfg.SystemP))
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天系统提示词", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
cfg.SystemP = chat.SystemPrompt
|
||||
err := c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
en.OnPrefix("设置AI聊天分隔符", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetstr(&cfg.Separator))
|
||||
en.OnRegex("^设置AI聊天(不)?响应AT$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnrat))
|
||||
en.OnRegex("^设置AI聊天(不)?支持系统提示词$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetbool(&cfg.NoSystemP))
|
||||
en.OnRegex("^设置AI聊天(不)?使用Agent模式$", ensureconfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnagt))
|
||||
en.OnPrefix("设置AI聊天最大长度", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetuint(&cfg.MaxN))
|
||||
en.OnPrefix("设置AI聊天TopP", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetfloat32(&cfg.TopP))
|
||||
en.OnRegex("^设置AI聊天(不)?以AI语音输出$", ensureconfig, zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(bitmapnrec))
|
||||
en.OnFullMatch("查看AI聊天配置", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(
|
||||
message.Text(
|
||||
"【当前AI聊天本群配置】\n",
|
||||
"• 触发概率:", int(stor.rate()), "\n",
|
||||
"• 温度:", stor.temp(), "\n",
|
||||
"• 以AI语音输出:", ModelBool(!stor.norecord()), "\n",
|
||||
"• 使用Agent:", ModelBool(!stor.noagent()), "\n",
|
||||
"• 响应@:", ModelBool(!stor.noreplyat()), "\n",
|
||||
),
|
||||
message.Text("【当前AI聊天全局配置】\n", &cfg),
|
||||
)
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
chat.ResetChat()
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
|
||||
// 添加群聊总结功能
|
||||
en.OnRegex(`^群聊总结\s?(\d*)$`, ensureconfig, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女思考中..."))
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if p > 1000 {
|
||||
p = 1000
|
||||
}
|
||||
if p == 0 {
|
||||
p = 200
|
||||
}
|
||||
group := ctx.GetGroupInfo(gid, false)
|
||||
if group.MemberCount == 0 {
|
||||
ctx.SendChain(message.Text(zero.BotConfig.NickName[0], "未加入", group.Name, "(", gid, "),无法获取总结"))
|
||||
return
|
||||
}
|
||||
|
||||
var messages []string
|
||||
|
||||
h := ctx.GetGroupMessageHistory(gid, 0, p, false)
|
||||
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
|
||||
nickname := msgObj.Get("sender.nickname").Str
|
||||
text := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
|
||||
if text != "" {
|
||||
messages = append(messages, nickname+": "+text)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(messages) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 历史消息为空或者无法获得历史消息"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构造总结请求提示
|
||||
summaryPrompt := "请总结这个群聊内容,要求按发言顺序梳理,明确标注每个发言者的昵称,并完整呈现其核心观点、提出的问题、发表的看法或做出的回应,确保不遗漏关键信息,且能体现成员间的对话逻辑和互动关系:\n" +
|
||||
strings.Join(messages, "\n")
|
||||
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 调用大模型API进行总结
|
||||
summary, err := llmchat(summaryPrompt, stor.temp())
|
||||
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("群 ")
|
||||
b.WriteString(group.Name)
|
||||
b.WriteByte('(')
|
||||
b.WriteString(strconv.FormatInt(gid, 10))
|
||||
b.WriteString(") 的 ")
|
||||
b.WriteString(strconv.FormatInt(p, 10))
|
||||
b.WriteString(" 条消息总结:\n\n")
|
||||
b.WriteString(summary)
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
summaryText := b.String()
|
||||
msg := make(message.Message, 0)
|
||||
for len(summaryText) > 0 {
|
||||
if len(summaryText) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(summaryText)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := summaryText[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = summaryText[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
summaryText = summaryText[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
|
||||
// 添加 /gpt 命令处理(同时支持回复消息和直接使用)
|
||||
en.OnKeyword("/gpt", ensureconfig).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
text := ctx.MessageString()
|
||||
|
||||
var query string
|
||||
var replyContent string
|
||||
|
||||
// 检查是否是回复消息 (使用MessageElement检查而不是CQ码)
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "reply" {
|
||||
// 提取被回复的消息ID
|
||||
replyIDStr := elem.Data["id"]
|
||||
replyID, err := strconv.ParseInt(replyIDStr, 10, 64)
|
||||
if err == nil {
|
||||
// 获取被回复的消息内容
|
||||
replyMsg := ctx.GetMessage(replyID)
|
||||
if replyMsg.Elements != nil {
|
||||
replyContent = replyMsg.Elements.ExtractPlainText()
|
||||
}
|
||||
}
|
||||
break // 找到回复元素后退出循环
|
||||
}
|
||||
}
|
||||
|
||||
// 提取 /gpt 后面的内容
|
||||
parts := strings.SplitN(text, "/gpt", 2)
|
||||
|
||||
var gContent string
|
||||
if len(parts) > 1 {
|
||||
gContent = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
// 组合内容:优先使用回复内容,如果同时有/gpt内容则拼接
|
||||
switch {
|
||||
case replyContent != "" && gContent != "":
|
||||
query = replyContent + "\n" + gContent
|
||||
case replyContent != "":
|
||||
query = replyContent
|
||||
case gContent != "":
|
||||
query = gContent
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
stor, err := newstorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 调用大模型API进行聊天
|
||||
reply, err := llmchat(query, stor.temp())
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
msg := make(message.Message, 0)
|
||||
for len(reply) > 0 {
|
||||
if len(reply) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(reply)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := reply[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = reply[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
reply = reply[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// llmchat 调用大模型API包装
|
||||
func llmchat(prompt string, temp float32) (string, error) {
|
||||
topp, maxn := cfg.mparams()
|
||||
|
||||
x := deepinfra.NewAPI(cfg.API, string(cfg.Key))
|
||||
|
||||
mod, err := cfg.Type.protocol(cfg.ModelName, temp, topp, maxn)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
data, err := x.Request(mod.User(model.NewContentText(prompt)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(data), nil
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
const (
|
||||
bitmaprate = 0x0000ff
|
||||
bitmaptemp = 0x00ff00
|
||||
bitmapnagt = 0x010000
|
||||
bitmapnrec = 0x020000
|
||||
bitmapnrat = 0x040000
|
||||
)
|
||||
|
||||
type storage ctxext.Storage
|
||||
|
||||
func newstorage(ctx *zero.Ctx, gid int64) (storage, error) {
|
||||
s, err := ctxext.NewStorage(ctx, gid)
|
||||
return storage(s), err
|
||||
}
|
||||
|
||||
func (s storage) rate() uint8 {
|
||||
return uint8((ctxext.Storage)(s).Get(bitmaprate))
|
||||
}
|
||||
|
||||
func (s storage) temp() float32 {
|
||||
temp := (ctxext.Storage)(s).Get(bitmaptemp)
|
||||
// 处理温度参数
|
||||
if temp <= 0 {
|
||||
temp = 70 // default setting
|
||||
}
|
||||
if temp > 100 {
|
||||
temp = 100
|
||||
}
|
||||
return float32(temp) / 100
|
||||
}
|
||||
|
||||
func (s storage) noagent() bool {
|
||||
return (ctxext.Storage)(s).GetBool(bitmapnagt)
|
||||
}
|
||||
|
||||
func (s storage) norecord() bool {
|
||||
return (ctxext.Storage)(s).GetBool(bitmapnrec)
|
||||
}
|
||||
|
||||
func (s storage) noreplyat() bool {
|
||||
return (ctxext.Storage)(s).GetBool(bitmapnrat)
|
||||
}
|
||||
181
plugin/aichatcfg/main.go
Normal file
181
plugin/aichatcfg/main.go
Normal file
@@ -0,0 +1,181 @@
|
||||
// Package aichatcfg aichat 的配置, 优先级要比 aichat 高
|
||||
package aichatcfg
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
var (
|
||||
// en data [8 temp] [8 rate] LSB
|
||||
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("aichat"),
|
||||
Brief: "aichat 的配置",
|
||||
Help: "- 设置AI聊天触发概率10\n" +
|
||||
"- 设置AI聊天温度80\n" +
|
||||
"- 设置AI聊天(|识图|Agent)接口类型[OpenAI|OLLaMA|GenAI]\n" +
|
||||
"- 设置AI聊天(不)使用Agent模式\n" +
|
||||
"- 设置AI聊天(不)支持系统提示词\n" +
|
||||
"- 设置AI聊天(|识图|Agent)接口地址https://api.siliconflow.cn/v1/chat/completions\n" +
|
||||
"- 设置AI聊天(|识图|Agent)密钥xxx\n" +
|
||||
"- 设置AI聊天(|识图|Agent)模型名Qwen/Qwen3-8B\n" +
|
||||
"- 查看AI聊天系统提示词\n" +
|
||||
"- 重置AI聊天系统提示词\n" +
|
||||
"- 设置AI聊天系统提示词xxx\n" +
|
||||
"- 设置AI聊天Agent性格" +
|
||||
"- 查看AI聊天Agent性格xxx" +
|
||||
"- 重置AI聊天Agent性格\n" +
|
||||
"- 设置AI聊天分隔符</think>(留空则清除)\n" +
|
||||
"- 设置AI聊天(不)响应AT\n" +
|
||||
"- 设置AI聊天最大长度4096\n" +
|
||||
"- 设置AI聊天TopP 0.9\n" +
|
||||
"- 设置AI聊天(不)以AI语音输出\n" +
|
||||
"- 查看AI聊天配置\n" +
|
||||
"- 重置AI聊天Agent\n" +
|
||||
"- 重置AI聊天\n",
|
||||
})
|
||||
)
|
||||
|
||||
func init() {
|
||||
en.UsePreHandler(chat.EnsureConfig, func(ctx *zero.Ctx) bool {
|
||||
if !chat.IsAgentCharReady {
|
||||
if chat.AC.AgentChar != "" {
|
||||
chat.AgentChar = []byte(chat.AC.AgentChar)
|
||||
}
|
||||
chat.IsAgentCharReady = true
|
||||
}
|
||||
k := zero.StateKeyPrefixKeep + "aichatcfg_stor__"
|
||||
if _, ok := ctx.State[k]; ok {
|
||||
return true
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
stor, err := chat.NewStorage(ctx, gid)
|
||||
if err != nil {
|
||||
logrus.Warnln("ERROR: ", err)
|
||||
return false
|
||||
}
|
||||
ctx.State[k] = stor
|
||||
return true
|
||||
})
|
||||
en.OnPrefix("设置AI聊天触发概率", zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBitmapHandler(chat.BitmapRate, 0, 100))
|
||||
en.OnPrefix("设置AI聊天温度", zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBitmapHandler(chat.BitmapTemp, 0, 100))
|
||||
en.OnPrefix("设置AI聊天接口类型", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetModelType(&chat.AC.Type))
|
||||
en.OnPrefix("设置AI聊天识图接口类型", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetModelType(&chat.AC.ImageType))
|
||||
en.OnPrefix("设置AI聊天Agent接口类型", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetModelType(&chat.AC.AgentType))
|
||||
en.OnPrefix("设置AI聊天接口地址", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.API))
|
||||
en.OnPrefix("设置AI聊天识图接口地址", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.ImageAPI))
|
||||
en.OnPrefix("设置AI聊天Agent接口地址", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.AgentAPI))
|
||||
en.OnPrefix("设置AI聊天密钥", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.Key))
|
||||
en.OnPrefix("设置AI聊天识图密钥", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.ImageKey))
|
||||
en.OnPrefix("设置AI聊天Agent密钥", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.AgentKey))
|
||||
en.OnPrefix("设置AI聊天模型名", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.ModelName))
|
||||
en.OnPrefix("设置AI聊天识图模型名", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.ImageModelName))
|
||||
en.OnPrefix("设置AI聊天Agent模型名", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.AgentModelName))
|
||||
en.OnPrefix("设置AI聊天系统提示词", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.SystemP))
|
||||
en.OnPrefix("设置AI聊天Agent性格", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.AgentChar))
|
||||
en.OnFullMatch("查看AI聊天系统提示词", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(chat.AC.SystemP))
|
||||
})
|
||||
en.OnFullMatch("查看AI聊天Agent性格", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(chat.AC.AgentChar))
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天系统提示词", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
chat.AC.SystemP = chat.SystemPrompt
|
||||
err := c.SetExtra(&chat.AC)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天Agent性格", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
chat.AC.AgentChar = ""
|
||||
err := c.SetExtra(&chat.AC)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功, 请重置AI聊天Agent"))
|
||||
})
|
||||
en.OnPrefix("设置AI聊天分隔符", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetStr(&chat.AC.Separator))
|
||||
en.OnRegex("^设置AI聊天(不)?响应AT$", chat.EnsureConfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(chat.BitmapNrat))
|
||||
en.OnRegex("^设置AI聊天(不)?支持系统提示词$", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetBool(&chat.AC.NoSystemP))
|
||||
en.OnRegex("^设置AI聊天(不)?使用Agent模式$", chat.EnsureConfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(chat.BitmapNagt))
|
||||
en.OnPrefix("设置AI聊天最大长度", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetUint(&chat.AC.MaxN))
|
||||
en.OnPrefix("设置AI聊天TopP", chat.EnsureConfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(chat.NewExtraSetFloat32(&chat.AC.TopP))
|
||||
en.OnRegex("^设置AI聊天(不)?以AI语音输出$", chat.EnsureConfig, zero.AdminPermission).SetBlock(true).
|
||||
Handle(ctxext.NewStorageSaveBoolHandler(chat.BitmapNrec))
|
||||
en.OnFullMatch("查看AI聊天配置", chat.EnsureConfig, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
stor, err := chat.NewStorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(
|
||||
message.Text(
|
||||
"【当前AI聊天本群配置】\n",
|
||||
"• 触发概率:", int(stor.Rate()), "\n",
|
||||
"• 温度:", stor.Temp(), "\n",
|
||||
"• 以AI语音输出:", chat.ModelBool(!stor.NoRecord()), "\n",
|
||||
"• 使用Agent:", chat.ModelBool(!stor.NoAgent()), "\n",
|
||||
"• 响应@:", chat.ModelBool(!stor.NoReplyAt()), "\n",
|
||||
),
|
||||
message.Text("【当前AI聊天全局配置】\n", &chat.AC),
|
||||
)
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天Agent", chat.EnsureConfig, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
chat.ResetAgents()
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天", chat.EnsureConfig, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
chat.ResetChat()
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
}
|
||||
@@ -25,10 +25,10 @@ import (
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/disk"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v4/cpu"
|
||||
"github.com/shirou/gopsutil/v4/disk"
|
||||
"github.com/shirou/gopsutil/v4/host"
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
246
plugin/llm/main.go
Normal file
246
plugin/llm/main.go
Normal file
@@ -0,0 +1,246 @@
|
||||
// Package llm 大模型聊天和群聊总结
|
||||
package llm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/deepinfra"
|
||||
"github.com/fumiama/deepinfra/model"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
var (
|
||||
// en data [8 temp] [8 rate] LSB
|
||||
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "大模型聊天和群聊总结",
|
||||
Help: "- 群聊总结 [消息数目]|群聊总结 1000\n" +
|
||||
"- /gpt [内容] (使用大模型聊天)\n",
|
||||
}).ApplySingle(single.New(
|
||||
single.WithKeyFn(func(ctx *zero.Ctx) int64 {
|
||||
if ctx.Event.GroupID == 0 {
|
||||
return -ctx.Event.UserID
|
||||
}
|
||||
return ctx.Event.GroupID
|
||||
}),
|
||||
// no post option, silently quit
|
||||
))
|
||||
)
|
||||
|
||||
var (
|
||||
limit = ctxext.NewLimiterManager(time.Second*30, 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 添加群聊总结功能
|
||||
en.OnRegex(`^群聊总结\s?(\d*)$`, chat.EnsureConfig, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女思考中..."))
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if p > 1000 {
|
||||
p = 1000
|
||||
}
|
||||
if p == 0 {
|
||||
p = 200
|
||||
}
|
||||
group := ctx.GetGroupInfo(gid, false)
|
||||
if group.MemberCount == 0 {
|
||||
ctx.SendChain(message.Text(zero.BotConfig.NickName[0], "未加入", group.Name, "(", gid, "),无法获取总结"))
|
||||
return
|
||||
}
|
||||
|
||||
var messages []string
|
||||
|
||||
h := ctx.GetGroupMessageHistory(gid, 0, p, false)
|
||||
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
|
||||
nickname := msgObj.Get("sender.nickname").Str
|
||||
text := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
|
||||
if text != "" {
|
||||
messages = append(messages, nickname+": "+text)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(messages) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 历史消息为空或者无法获得历史消息"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构造总结请求提示 (使用通用版省流提示词)
|
||||
// 使用反引号定义多行字符串,更清晰
|
||||
promptTemplate := `请对以下群聊对话进行【极简总结】。
|
||||
要求:
|
||||
1. 剔除客套与废话,直击主题。
|
||||
2. 使用 Markdown 列表格式。
|
||||
3. 按以下结构输出:
|
||||
- 🎯 核心议题:(一句话概括)
|
||||
- 💡 关键观点/结论:(提取3-5个重点)
|
||||
- ✅ 下一步/待办:(如果有,明确谁做什么)
|
||||
|
||||
群聊对话内容如下:
|
||||
`
|
||||
summaryPrompt := promptTemplate + strings.Join(messages, "\n")
|
||||
|
||||
stor, err := chat.NewStorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 调用大模型API进行总结
|
||||
summary, err := llmchat(summaryPrompt, stor.Temp())
|
||||
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("群 ")
|
||||
b.WriteString(group.Name)
|
||||
b.WriteByte('(')
|
||||
b.WriteString(strconv.FormatInt(gid, 10))
|
||||
b.WriteString(") 的 ")
|
||||
b.WriteString(strconv.FormatInt(p, 10))
|
||||
b.WriteString(" 条消息总结:\n\n")
|
||||
b.WriteString(summary)
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
summaryText := b.String()
|
||||
msg := make(message.Message, 0)
|
||||
for len(summaryText) > 0 {
|
||||
if len(summaryText) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(summaryText)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := summaryText[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = summaryText[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
summaryText = summaryText[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
|
||||
// 添加 /gpt 命令处理(同时支持回复消息和直接使用)
|
||||
en.OnKeyword("/gpt", chat.EnsureConfig).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
text := ctx.MessageString()
|
||||
|
||||
var query string
|
||||
var replyContent string
|
||||
|
||||
// 检查是否是回复消息 (使用MessageElement检查而不是CQ码)
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "reply" {
|
||||
// 提取被回复的消息ID
|
||||
replyIDStr := elem.Data["id"]
|
||||
replyID, err := strconv.ParseInt(replyIDStr, 10, 64)
|
||||
if err == nil {
|
||||
// 获取被回复的消息内容
|
||||
replyMsg := ctx.GetMessage(replyID)
|
||||
if replyMsg.Elements != nil {
|
||||
replyContent = replyMsg.Elements.ExtractPlainText()
|
||||
}
|
||||
}
|
||||
break // 找到回复元素后退出循环
|
||||
}
|
||||
}
|
||||
|
||||
// 提取 /gpt 后面的内容
|
||||
parts := strings.SplitN(text, "/gpt", 2)
|
||||
|
||||
var gContent string
|
||||
if len(parts) > 1 {
|
||||
gContent = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
// 组合内容:优先使用回复内容,如果同时有/gpt内容则拼接
|
||||
switch {
|
||||
case replyContent != "" && gContent != "":
|
||||
query = replyContent + "\n" + gContent
|
||||
case replyContent != "":
|
||||
query = replyContent
|
||||
case gContent != "":
|
||||
query = gContent
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
stor, err := chat.NewStorage(ctx, gid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
// 调用大模型API进行聊天
|
||||
reply, err := llmchat(query, stor.Temp())
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
msg := make(message.Message, 0)
|
||||
for len(reply) > 0 {
|
||||
if len(reply) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(reply)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := reply[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = reply[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
reply = reply[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// llmchat 调用大模型API包装
|
||||
func llmchat(prompt string, temp float32) (string, error) {
|
||||
topp, maxn := chat.AC.MParams()
|
||||
|
||||
x := deepinfra.NewAPI(chat.AC.API, string(chat.AC.Key))
|
||||
|
||||
mod, err := chat.AC.Type.Protocol(chat.AC.ModelName, temp, topp, maxn)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
data, err := x.Request(mod.User(model.NewContentText(prompt)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(data), nil
|
||||
}
|
||||
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.AutoRegister(&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() != ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ package wife
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
zbmath "github.com/FloatTech/floatbox/math"
|
||||
"github.com/FloatTech/imgfactory"
|
||||
)
|
||||
|
||||
@@ -134,24 +132,8 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
// 马赛克生成
|
||||
// 高斯模糊生成
|
||||
func mosaic(dst *imgfactory.Factory, level int) ([]byte, error) {
|
||||
b := dst.Image().Bounds()
|
||||
p := imgfactory.NewFactoryBG(dst.W(), dst.H(), color.NRGBA{255, 255, 255, 255})
|
||||
markSize := zbmath.Max(b.Max.X, b.Max.Y) * sizeList[level] / 200
|
||||
|
||||
for yOfMarknum := 0; yOfMarknum <= zbmath.Ceil(b.Max.Y, markSize); yOfMarknum++ {
|
||||
for xOfMarknum := 0; xOfMarknum <= zbmath.Ceil(b.Max.X, markSize); xOfMarknum++ {
|
||||
a := dst.Image().At(xOfMarknum*markSize+markSize/2, yOfMarknum*markSize+markSize/2)
|
||||
cc := color.NRGBAModel.Convert(a).(color.NRGBA)
|
||||
for y := 0; y < markSize; y++ {
|
||||
for x := 0; x < markSize; x++ {
|
||||
xOfPic := xOfMarknum*markSize + x
|
||||
yOfPic := yOfMarknum*markSize + y
|
||||
p.Image().Set(xOfPic, yOfPic, cc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return imgfactory.ToBytes(p.Blur(3).Image())
|
||||
blurRadius := float64(sizeList[level] * 3)
|
||||
return imgfactory.ToBytes(dst.Blur(blurRadius).Image())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
83
plugin/xhstext/xhstext.go
Normal file
83
plugin/xhstext/xhstext.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// Package xhstext 小红书文案
|
||||
package xhstext
|
||||
|
||||
import (
|
||||
"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"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
type xhstext struct {
|
||||
ID uint32 `db:"id"`
|
||||
Text string `db:"text"`
|
||||
Label string `db:"label"`
|
||||
}
|
||||
|
||||
var db sql.Sqlite
|
||||
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "小红书文案",
|
||||
Help: "- 捧场\n- 有梗",
|
||||
PublicDataFolder: "Xhstext",
|
||||
})
|
||||
|
||||
// 初始化数据库
|
||||
initDB := fcext.DoOnceOnSuccess(
|
||||
func(ctx *zero.Ctx) bool {
|
||||
db = sql.New(en.DataFolder() + "xhstext.db")
|
||||
_, err := en.GetLazyData("xhstext.db", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Open(time.Hour)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
err = db.Create("all_texts", &xhstext{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
c, err := db.Count("all_texts")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return false
|
||||
}
|
||||
logrus.Infoln("[xhstext]加载", c, "条小红书文案")
|
||||
return true
|
||||
},
|
||||
)
|
||||
|
||||
// 捧场命令
|
||||
en.OnFullMatch("捧场", initDB).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
|
||||
var x xhstext
|
||||
err := db.Find("all_texts", &x, "WHERE label = '捧场' ORDER BY RANDOM() LIMIT 1")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(x.Text))
|
||||
})
|
||||
|
||||
// 有梗命令
|
||||
en.OnFullMatch("有梗", initDB).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
|
||||
var x xhstext
|
||||
err := db.Find("all_texts", &x, "WHERE label = '有梗' ORDER BY RANDOM() LIMIT 1")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(x.Text))
|
||||
})
|
||||
}
|
||||
3
run.bat
3
run.bat
@@ -3,5 +3,6 @@ go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go env -w GO111MODULE=auto
|
||||
go mod tidy
|
||||
::go build -ldflags="-s -w" -o ZeroBot-Plugin.exe
|
||||
go run main.go
|
||||
go generate main.go
|
||||
go run -ldflags "-s -w" main.go
|
||||
pause
|
||||
|
||||
3
run.sh
3
run.sh
@@ -3,4 +3,5 @@ go env -w GOPROXY=https://goproxy.cn,direct
|
||||
go env -w GO111MODULE=auto
|
||||
go mod tidy
|
||||
#go build -ldflags="-s -w" -o ZeroBot-Plugin
|
||||
go run main.go
|
||||
go generate main.go
|
||||
go run -ldflags "-s -w" main.go
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"0409": {
|
||||
"identity": {
|
||||
"name": "ZeroBot-Plugin",
|
||||
"version": "1.10.0.2279"
|
||||
"version": "1.10.9.2338"
|
||||
},
|
||||
"description": "",
|
||||
"minimum-os": "vista",
|
||||
@@ -36,23 +36,23 @@
|
||||
"#1": {
|
||||
"0000": {
|
||||
"fixed": {
|
||||
"file_version": "1.10.0.2279",
|
||||
"product_version": "v1.10.0",
|
||||
"timestamp": "2025-09-30T23:54:09+08:00"
|
||||
"file_version": "1.10.9.2338",
|
||||
"product_version": "v1.10.9",
|
||||
"timestamp": "2026-01-06T21:25:04+08:00"
|
||||
},
|
||||
"info": {
|
||||
"0409": {
|
||||
"Comments": "OneBot plugins based on ZeroBot",
|
||||
"CompanyName": "FloatTech",
|
||||
"FileDescription": "https://github.com/FloatTech/ZeroBot-Plugin",
|
||||
"FileVersion": "1.10.0.2279",
|
||||
"FileVersion": "1.10.9.2338",
|
||||
"InternalName": "",
|
||||
"LegalCopyright": "© 2020 - 2025 FloatTech. All Rights Reserved.",
|
||||
"LegalCopyright": "© 2020 - 2026 FloatTech. All Rights Reserved.",
|
||||
"LegalTrademarks": "",
|
||||
"OriginalFilename": "ZBP.EXE",
|
||||
"PrivateBuild": "",
|
||||
"ProductName": "ZeroBot-Plugin",
|
||||
"ProductVersion": "v1.10.0",
|
||||
"ProductVersion": "v1.10.9",
|
||||
"SpecialBuild": ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user