var apilog = c.C.Log.Base(`api`)
var api_limit = limit.New(2, "1s", "30s") //频率限制2次/s,最大等待时间30s
-var biliApi = cmp.Get(id, func(ba BiliApiInter) BiliApiInter {
- ba.SetLocation(c.C.SerLocation)
- ba.SetProxy(c.C.Proxy)
- ba.SetReqPool(c.C.ReqPool)
- return ba
+var biliApi = cmp.Get(id, cmp.PreFuncCu[BiliApiInter]{
+ Initf: func(ba BiliApiInter) BiliApiInter {
+ ba.SetLocation(c.C.SerLocation)
+ ba.SetProxy(c.C.Proxy)
+ ba.SetReqPool(c.C.ReqPool)
+ return ba
+ },
})
var (
"errors"
"fmt"
"io"
- "math"
"net"
"net/http"
"net/http/pprof"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
psql "github.com/qydysky/part/sql"
- "golang.org/x/text/encoding/simplifiedchinese"
_ "modernc.org/sqlite"
"github.com/dustin/go-humanize"
psync "github.com/qydysky/part/sync"
pweb "github.com/qydysky/part/web"
websocket "github.com/qydysky/part/websocket"
-
- encoder "golang.org/x/text/encoding"
)
/*
}()
}
-// Ass 弹幕转字幕
-type Ass struct {
- file string //弹幕ass文件名
- startT time.Time //开始记录的基准时间
- header string //ass开头
- wrap encoder.Encoding //编码
-}
-
-var (
- Ass_height = 720 //字幕高度
- Ass_width = 1280 //字幕宽度
- Ass_font = 50 //字幕字体大小
- Ass_T = 7 //单条字幕显示时间
- Ass_loc = 7 //字幕位置 小键盘对应的位置
-)
-
-var ass = Ass{
- header: `[Script Info]
-Title: Default Ass file
-ScriptType: v4.00+
-WrapStyle: 0
-ScaledBorderAndShadow: yes
-PlayResX: ` + strconv.Itoa(Ass_height) + `
-PlayResY: ` + strconv.Itoa(Ass_width) + `
-
-[V4+ Styles]
-Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
-Style: Default,,` + strconv.Itoa(Ass_font) + `,&H40FFFFFF,&H000017FF,&H80000000,&H40000000,0,0,0,0,100,100,0,0,1,4,4,` + strconv.Itoa(Ass_loc) + `,20,20,50,1
-
-[Events]
-Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
-`,
- wrap: simplifiedchinese.GB18030,
-}
-
-func init() {
- accept := map[string]bool{
- `GB18030`: true,
- `utf-8`: true,
- }
- if v, ok := c.C.K_v.LoadV("Ass编码").(string); ok {
- if v1, ok := accept[v]; ok && v1 {
- c.C.Log.Base(`Ass`).L(`T: `, "编码:", v)
- if v == `utf-8` {
- ass.wrap = nil
- }
- }
- }
-}
-
-// 设定字幕文件名,为""时停止输出
-func Ass_f(ctx context.Context, save_path string, filePath string, st time.Time) {
- if !IsOn(`仅保存当前直播间流`) {
- return
- }
- ass.file = filePath
- if filePath == "" {
- return
- }
- fl := flog.Base_add(`Ass`)
- fl.L(`I: `, `开始`)
- f := &file.File{
- Config: file.Config{
- FilePath: ass.file + ".ass",
- AutoClose: true,
- Coder: ass.wrap,
- },
- }
- _, _ = f.Write([]byte(ass.header), true)
- ass.startT = st
-
- ctx, done := pctx.WaitCtx(ctx)
- defer done()
- <-ctx.Done()
-
- ass.file = ""
- fl.L(`I: `, "结束")
-}
-
-// 传入要显示的单条字幕
-func Assf(s string) {
- if !IsOn("生成Ass弹幕") {
- return
- }
- if ass.file == "" {
- return
- }
-
- if s == "" {
- return
- }
-
- st := time.Since(ass.startT) + time.Duration(p.Rand().MixRandom(0, 2000))*time.Millisecond
- et := st + time.Duration(Ass_T)*time.Second
-
- var b string
- // b += "Comment: " + strconv.Itoa(loc) + " "+ Dtos(showedt) + "\n"
- b += `Dialogue: 0,`
- b += dtos(st) + `,` + dtos(et)
- b += `,Default,,0,0,0,,{\fad(200,500)\blur3}` + s + "\n"
-
- f := file.New(ass.file+".ass", -1, true)
- f.Config.Coder = ass.wrap
- if _, e := f.Write([]byte(b), true); e != nil {
- flog.Base(`Assf`).L(`E: `, e)
- }
-}
-
-// 时间转化为0:00:00.00规格字符串
-func dtos(t time.Duration) string {
- M := int(math.Floor(t.Minutes())) % 60
- S := int(math.Floor(t.Seconds())) % 60
- Ns := t.Nanoseconds() / int64(time.Millisecond) % 1000 / 10
-
- return fmt.Sprintf("%d:%02d:%02d.%02d", int(math.Floor(t.Hours())), M, S, Ns)
-}
-
// 获取实例的Common
func StreamOCommon(roomid int) (array []*c.Common) {
if roomid != -1 { //返回特定房间
return func(s ...string) {}
}
-// type Obs struct {
-// c obsws.Client
-// Prog string //程序路径
-// }
-
-// var obs = Obs{
-// c: obsws.Client{Host: "127.0.0.1", Port: 4444},
-// Prog: "obs",
-// }
-
-// func Obsf(on bool) {
-// if !IsOn("调用obs") {
-// return
-// }
-// l := c.C.Log.Base(`obs`)
-
-// if on {
-// if sys.Sys().CheckProgram("obs")[0] != 0 {
-// l.L(`W: `, "obs已经启动")
-// return
-// }
-// if sys.Sys().CheckProgram("obs")[0] == 0 {
-// if obs.Prog == "" {
-// l.L(`E: `, "未知的obs程序位置")
-// return
-// }
-// l.L(`I: `, "启动obs")
-// p.Exec().Start(exec.Command(obs.Prog))
-// sys.Sys().Timeoutf(3)
-// }
-
-// // Connect a client.
-// if err := obs.c.Connect(); err != nil {
-// l.L(`E: `, err)
-// return
-// }
-// } else {
-// if sys.Sys().CheckProgram("obs")[0] == 0 {
-// l.L(`W: `, "obs未启动")
-// return
-// }
-// obs.c.Disconnect()
-// }
-// }
-
-// func Obs_R(on bool) {
-// if !IsOn("调用obs") {
-// return
-// }
-
-// l := c.C.Log.Base("obs_R")
-
-// if sys.Sys().CheckProgram("obs")[0] == 0 {
-// l.L(`W: `, "obs未启动")
-// return
-// } else {
-// if err := obs.c.Connect(); err != nil {
-// l.L(`E: `, err)
-// return
-// }
-// }
-// //录
-// if on {
-// req := obsws.NewStartRecordingRequest()
-// if err := req.Send(obs.c); err != nil {
-// l.L(`E: `, err)
-// return
-// }
-// resp, err := req.Receive()
-// if err != nil {
-// l.L(`E: `, err)
-// return
-// }
-// if resp.Status() == "ok" {
-// l.L(`I: `, "开始录制")
-// }
-// } else {
-// req := obsws.NewStopRecordingRequest()
-// if err := req.Send(obs.c); err != nil {
-// l.L(`E: `, err)
-// return
-// }
-// resp, err := req.Receive()
-// if err != nil {
-// l.L(`E: `, err)
-// return
-// }
-// if resp.Status() == "ok" {
-// l.L(`I: `, "停止录制")
-// }
-// sys.Sys().Timeoutf(3)
-// }
-// }
-
-// type Autoban struct {
-// Banbuf []string
-// buf []string
-// }
-
-// var autoban = Autoban{}
-
-// func Autobanf(s string) bool {
-// if !IsOn("Autoban") {
-// return false
-// }
-
-// l := c.C.Log.Base("autoban")
-
-// if len(autoban.Banbuf) == 0 {
-// f := file.New("Autoban.txt", -1, false)
-// for {
-// if data, e := f.ReadUntil('\n', 50, 5000); e != nil {
-// if !errors.Is(e, io.EOF) {
-// l.L(`E: `, e)
-// }
-// break
-// } else {
-// autoban.Banbuf = append(autoban.Banbuf, string(data))
-// }
-// }
-// }
-
-// if len(autoban.buf) < 10 {
-// autoban.buf = append(autoban.buf, s)
-// return false
-// }
-// defer func() {
-// autoban.buf = append(autoban.buf[1:], s)
-// }()
-
-// var res []float32
-// {
-// pt := float32(len([]rune(s)))
-// if pt <= 5 {
-// return false
-// } //字数过少去除
-// res = append(res, pt)
-// }
-// {
-// pt := selfcross(s)
-// // if pt > 0.5 {return false}//自身重复高去除
-// // res = append(res, pt)
-
-// pt1 := cross(s, autoban.buf)
-// if pt+pt1 > 0.3 {
-// return false
-// } //历史重复高去除
-// res = append(res, pt, pt1)
-// }
-// {
-// pt := cross(s, autoban.Banbuf)
-// if pt < 0.8 {
-// return false
-// } //ban字符重复低去除
-// res = append(res, pt)
-// }
-// l.L(`W: `, res)
-// return true
-// }
-
-type Danmuji struct {
- Buf map[string]string
- Inuse_auto bool
- reflect_limit *limit.Limit
-
- mute bool
-}
-
-var danmuji = Danmuji{
- Inuse_auto: IsOn("自动弹幕机"),
- Buf: map[string]string{
- "弹幕机在么": "在",
- },
- reflect_limit: limit.New(1, "4s", "8s"),
-}
-
-func init() { //初始化反射型弹幕机
- f := file.New("config/config_auto_reply.json", 0, true)
- if !f.IsExist() {
- return
- }
- bb, err := f.ReadAll(100, 1<<16)
- if !errors.Is(err, io.EOF) {
- return
- }
- var buf map[string]interface{}
- _ = json.Unmarshal(bb, &buf)
- for k, v := range buf {
- if k == v {
- continue
- }
- danmuji.Buf[k] = v.(string)
- }
-}
-
-func Danmujif(s string) {
- if !IsOn("反射弹幕机") {
- return
- }
-
- if danmuji.reflect_limit.TO() {
- return
- }
-
- for k, v := range danmuji.Buf {
- if strings.Contains(s, k) {
- Msg_senddanmu(v)
- break
- }
- }
-}
-
-func Danmuji_auto() {
- if !IsOn("反射弹幕机") || !IsOn("自动弹幕机") || danmuji.mute {
- return
- }
-
- danmuji.mute = true
-
- var (
- list []string
- timeout int
- )
- for _, v := range c.C.K_v.LoadV(`自动弹幕机_内容`).([]interface{}) {
- list = append(list, v.(string))
- }
- timeout = int(c.C.K_v.LoadV(`自动弹幕机_发送间隔s`).(float64))
- if timeout < 5 {
- timeout = 5
- }
-
- go func() {
- for i := 0; true; i++ {
- if i >= len(list) {
- i = 0
- }
- if msg := list[i]; msg != `` {
- Msg_senddanmu(msg)
- }
- time.Sleep(time.Duration(timeout) * time.Second)
- }
- }()
-}
-
type Autoskip struct {
roomid int
buf map[string]Autoskip_item
--- /dev/null
+package ass
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "strconv"
+ "time"
+
+ p "github.com/qydysky/part"
+ comp "github.com/qydysky/part/component2"
+ pctx "github.com/qydysky/part/ctx"
+ file "github.com/qydysky/part/file"
+ encoder "golang.org/x/text/encoding"
+ "golang.org/x/text/encoding/simplifiedchinese"
+)
+
+type i interface {
+ Assf(s string) error
+ Ass_f(ctx context.Context, enc, savePath string, st time.Time)
+}
+
+func init() {
+ if e := comp.Register[i]("ass", &Ass{header: Ass_header}); e != nil {
+ panic(e)
+ }
+}
+
+var (
+ Ass_height = 720 //字幕高度
+ Ass_width = 1280 //字幕宽度
+ Ass_font = 50 //字幕字体大小
+ Ass_T = 7 //单条字幕显示时间
+ Ass_loc = 7 //字幕位置 小键盘对应的位置
+ accept = map[string]encoder.Encoding{
+ ``: simplifiedchinese.GB18030,
+ `GB18030`: simplifiedchinese.GB18030,
+ `utf-8`: nil,
+ }
+ Ass_header = `[Script Info]
+ Title: Default Ass file
+ ScriptType: v4.00+
+ WrapStyle: 0
+ ScaledBorderAndShadow: yes
+ PlayResX: ` + strconv.Itoa(Ass_height) + `
+ PlayResY: ` + strconv.Itoa(Ass_width) + `
+
+ [V4+ Styles]
+ Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
+ Style: Default,,` + strconv.Itoa(Ass_font) + `,&H40FFFFFF,&H000017FF,&H80000000,&H40000000,0,0,0,0,100,100,0,0,1,4,4,` + strconv.Itoa(Ass_loc) + `,20,20,50,1
+
+ [Events]
+ Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+ `
+)
+
+// Ass 弹幕转字幕
+type Ass struct {
+ savePath string //弹幕ass文件名
+ startT time.Time //开始记录的基准时间
+ header string //ass开头
+ wrap encoder.Encoding //编码
+}
+
+func (t *Ass) Assf(s string) error {
+ if t.savePath == "" || s == "" {
+ return nil
+ }
+
+ st := time.Since(t.startT) + time.Duration(p.Rand().MixRandom(0, 2000))*time.Millisecond
+ et := st + time.Duration(Ass_T)*time.Second
+
+ var b string
+ // b += "Comment: " + strconv.Itoa(loc) + " "+ Dtos(showedt) + "\n"
+ b += `Dialogue: 0,`
+ b += dtos(st) + `,` + dtos(et)
+ b += `,Default,,0,0,0,,{\fad(200,500)\blur3}` + s + "\n"
+
+ f := file.New(t.savePath+"0.ass", -1, true)
+ f.Config.Coder = t.wrap
+ if _, e := f.Write([]byte(b), true); e != nil {
+ return e
+ }
+ return nil
+}
+
+func (t *Ass) Ass_f(ctx context.Context, enc, savePath string, st time.Time) {
+ if v1, ok := accept[enc]; ok {
+ t.wrap = v1
+ }
+
+ t.savePath = savePath
+ f := &file.File{
+ Config: file.Config{
+ FilePath: t.savePath + "0.ass",
+ AutoClose: true,
+ Coder: t.wrap,
+ },
+ }
+ _, _ = f.Write([]byte(t.header), true)
+ t.startT = st
+
+ ctx, done := pctx.WaitCtx(ctx)
+ defer done()
+ <-ctx.Done()
+
+ t.savePath = ""
+}
+
+// 时间转化为0:00:00.00规格字符串
+func dtos(t time.Duration) string {
+ M := int(math.Floor(t.Minutes())) % 60
+ S := int(math.Floor(t.Seconds())) % 60
+ Ns := t.Nanoseconds() / int64(time.Millisecond) % 1000 / 10
+
+ return fmt.Sprintf("%d:%02d:%02d.%02d", int(math.Floor(t.Hours())), M, S, Ns)
+}
import (
"context"
"net/http"
+ "time"
+ _ "github.com/qydysky/bili_danmu/Reply/F/ass"
_ "github.com/qydysky/bili_danmu/Reply/F/danmuCountPerMin"
+ _ "github.com/qydysky/bili_danmu/Reply/F/danmuji"
comp "github.com/qydysky/part/component2"
)
Rec(ctx context.Context, roomid int, savePath string)
Do(roomid int)
}](`danmuCountPerMin`)
+
+var Ass = comp.Get[interface {
+ Assf(s string) error
+ Ass_f(ctx context.Context, enc, savePath string, st time.Time)
+}](`ass`)
+
+var Danmuji = comp.Get[interface {
+ Danmujif(s string, then func(string))
+ Danmuji_auto(ctx context.Context, danmus []any, waitSec float64, then func(string))
+}](`danmuji`)
--- /dev/null
+package danmuji
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "strings"
+ "time"
+
+ comp "github.com/qydysky/part/component2"
+ file "github.com/qydysky/part/file"
+ limit "github.com/qydysky/part/limit"
+)
+
+var bbuf = make(map[string]string)
+
+type i interface {
+ Danmujif(s string, then func(string))
+ Danmuji_auto(ctx context.Context, danmus []any, waitSec float64, then func(string))
+}
+
+func init() {
+ f := file.New("config/config_auto_reply.json", 0, true)
+ if !f.IsExist() {
+ return
+ }
+ bb, err := f.ReadAll(100, 1<<16)
+ if !errors.Is(err, io.EOF) {
+ return
+ }
+ var buf map[string]interface{}
+ _ = json.Unmarshal(bb, &buf)
+
+ for k, v := range buf {
+ if k == v {
+ continue
+ }
+ bbuf[k] = v.(string)
+ }
+
+ if e := comp.Register[i]("danmuji", &Danmuji{
+ reflect_limit: limit.New(1, "4s", "8s"),
+ }); e != nil {
+ panic(e)
+ }
+}
+
+type Danmuji struct {
+ reflect_limit *limit.Limit
+}
+
+func (t *Danmuji) Danmujif(s string, then func(string)) {
+ if !t.reflect_limit.TO() {
+ for k, v := range bbuf {
+ if strings.Contains(s, k) {
+ then(v)
+ break
+ }
+ }
+ }
+}
+
+func (t *Danmuji) Danmuji_auto(ctx context.Context, danmus []any, waitSec float64, then func(string)) {
+ if waitSec < 5 {
+ waitSec = 5
+ }
+
+ go func() {
+ for i := 0; true; i++ {
+ if i >= len(danmus) {
+ i = 0
+ }
+ if msg, ok := danmus[i].(string); ok && msg != `` {
+ then(msg)
+ }
+ select {
+ case <-ctx.Done():
+ return
+ case <-time.After(time.Duration(waitSec) * time.Second):
+ }
+ }
+ }()
+}
}
{ //额外 ass
- Assf(fmt.Sprintln("天选之人", J.Data.AwardName, "开始"))
+ replyFunc.Ass.Assf(fmt.Sprintln("天选之人", J.Data.AwardName, "开始"))
}
fmt.Println(sh...)
Gui_show(Itos(sh), `0tianxuan`)
}
sh = append(sh, "]")
{ //额外 ass
- Assf(fmt.Sprintln("天选之人", J.Data.AwardName, "结束"))
+ replyFunc.Ass.Assf(fmt.Sprintln("天选之人", J.Data.AwardName, "结束"))
}
fmt.Println(sh...)
Gui_show(Itos(sh), `0tianxuan`)
})
}
{ //额外 ass 私信
- Assf(fmt.Sprintln(sh...))
+ replyFunc.Ass.Assf(fmt.Sprintln(sh...))
t.Common.Danmu_Main_mq.Push_tag(`guard_update`, nil) //使用连续付费的新舰长无法区分,刷新舰长数
if msg := t.Common.K_v.LoadV(`上舰私信`).(string); uid != 0 && msg != "" {
t.Common.Danmu_Main_mq.Push_tag(`pm`, send.Pm_item{
})
}
{ //额外
- Assf(fmt.Sprintln(sh...))
+ replyFunc.Ass.Assf(fmt.Sprintln(sh...))
}
fmt.Println("\n====")
fmt.Println(sh...)
fmt.Print("====\n")
{ //额外
- Assf(fmt.Sprintln(sh...))
+ replyFunc.Ass.Assf(fmt.Sprintln(sh...))
Gui_show(Itos(sh), "0superchat")
//直播流服务弹幕
SendStreamWs(Danmu_item{
}
// 反射弹幕机
if IsOn("反射弹幕机") {
- go Danmujif(item.msg)
+ go replyFunc.Danmuji.Danmujif(item.msg, Msg_senddanmu)
}
if i := Autoskipf(item.msg); i > 0 {
danmulog.L(`I: `, item.auth, ":", item.msg)
//展示
{
//ass
- Assf(item.msg)
+ replyFunc.Ass.Assf(item.msg)
//直播流服务弹幕
SendStreamWs(item)
l.L(`E: `, e)
}
- go StartRecDanmu(contextC, ms.GetSavePath()) //保存弹幕
- go Ass_f(contextC, ms.GetSavePath(), ms.GetSavePath()+"0", time.Now()) //开始ass
+ //保存弹幕
+ go StartRecDanmu(contextC, ms.GetSavePath())
+
+ //ass
+ if enc, ok := c.C.K_v.LoadV("Ass编码").(string); c.C.IsOn(`生成Ass弹幕`) && c.C.IsOn(`仅保存当前直播间流`) && ok {
+ go replyFunc.Ass.Ass_f(contextC, enc, ms.GetSavePath(), time.Now())
+ }
startT := time.Now()
if e := ms.PusherToFile(contextC, ms.GetSavePath()+`0.`+ms.GetStreamType(), startf, stopf); e != nil {
c "github.com/qydysky/bili_danmu/CV"
F "github.com/qydysky/bili_danmu/F"
reply "github.com/qydysky/bili_danmu/Reply"
+ replyFunc "github.com/qydysky/bili_danmu/Reply/F"
"github.com/qydysky/bili_danmu/Reply/F/danmuReLiveTriger"
"github.com/qydysky/bili_danmu/Reply/F/genCpuPprof"
"github.com/qydysky/bili_danmu/Reply/F/recStartEnd"
`bili_jct`,
`DedeUserID`,
`LIVE_BUVID`,
- }); len(missKey) == 0 {
+ }); len(missKey) == 0 && reply.IsOn("自动弹幕机") {
//附加功能 弹幕机 无cookie无法发送弹幕
- reply.Danmuji_auto()
+ replyFunc.Danmuji.Danmuji_auto(mainCtx, c.C.K_v.LoadV(`自动弹幕机_内容`).([]any), c.C.K_v.LoadV(`自动弹幕机_发送间隔s`).(float64), reply.Msg_senddanmu)
}
{ //附加功能 进房间发送弹幕 直播流保存 每日签到
go F.Dosign()
require (
github.com/gotk3/gotk3 v0.6.4
github.com/mdp/qrterminal/v3 v3.2.0
- github.com/qydysky/part v0.28.20241116155801
+ github.com/qydysky/part v0.28.20241119180138
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
golang.org/x/text v0.17.0
github.com/qydysky/biliApi v0.0.0-20240725184407-15076dddb6fb/go.mod h1:om024vfxALQ5vxsbaGoMm8IS0esLYBnEOpJI8FsGoDg=
github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9 h1:k451T+bpsLr+Dq9Ujo+Qtx0iomRA1XXS5ttlEojvfuQ=
github.com/qydysky/brotli v0.0.0-20240828134800-e9913a6e7ed9/go.mod h1:cI8/gy/wjy2Eb+p2IUj2ZuDnC8R5Vrx3O0VMPvMvphA=
-github.com/qydysky/part v0.28.20241116155801 h1:krLZOaSui6lZlS39ldQoH635PPgbMZ26rzyQdPtrgAI=
-github.com/qydysky/part v0.28.20241116155801/go.mod h1:s3h7P6YdST5b9WoqjlS9w+JzDFdMeSENQOi8noYuopg=
+github.com/qydysky/part v0.28.20241119180138 h1:9ghtIT8aMqCD7v7ZBRE+RiQt9Sn+L1ERG13nMRdzSy0=
+github.com/qydysky/part v0.28.20241119180138/go.mod h1:s3h7P6YdST5b9WoqjlS9w+JzDFdMeSENQOi8noYuopg=
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.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=