🎨 重构代码以使用新的微信客户端库,更新相关函数以支持新的API;优化内存和网络统计显示逻辑
All checks were successful
BuildImage / build-image (push) Successful in 2m37s
All checks were successful
BuildImage / build-image (push) Successful in 2m37s
This commit is contained in:
parent
dc28090064
commit
e6457b7b22
61
go.mod
61
go.mod
@ -3,13 +3,14 @@ module gitee.ltd/lxh/wechat-robot
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/docker/docker v25.0.3+incompatible
|
||||
gitee.ltd/lxh/xybot v0.0.3
|
||||
github.com/docker/docker v28.1.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/go-co-op/gocron/v2 v2.16.1
|
||||
github.com/go-resty/resty/v2 v2.10.0
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/gofiber/fiber/v2 v2.52.6
|
||||
github.com/gofiber/template/html/v2 v2.1.3
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/spf13/viper v1.20.1
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
@ -19,64 +20,70 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/distribution/reference v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.9.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gofiber/template v1.8.3 // indirect
|
||||
github.com/gofiber/utils v1.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.5.5 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.4 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/logto-io/go v1.0.6 // indirect
|
||||
github.com/logto-io/go/v2 v2.0.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.28 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
)
|
||||
|
178
go.sum
178
go.sum
@ -1,9 +1,13 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
gitee.ltd/lxh/xybot v0.0.3 h1:/xRCnW2nMtx/hdV7TdpEcL3Sh8f9oBGHToONk0yGstA=
|
||||
gitee.ltd/lxh/xybot v0.0.3/go.mod h1:jYfEAQ3WPsST/PY4fEEVFjU6KtMocxn3sQi78I+vdxc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
@ -13,8 +17,8 @@ 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=
|
||||
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
|
||||
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
|
||||
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@ -23,19 +27,24 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-co-op/gocron/v2 v2.16.1 h1:ux/5zxVRveCaCuTtNI3DiOk581KC1KpJbpJFYUEVYwo=
|
||||
github.com/go-co-op/gocron/v2 v2.16.1/go.mod h1:opexeOFy5BplhsKdA7bzY9zeYih8I8/WNJ4arTIFPVc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo=
|
||||
github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
|
||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
@ -50,6 +59,8 @@ github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@ -58,12 +69,12 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rH
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
@ -74,22 +85,31 @@ github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbd
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/logto-io/go v1.0.6 h1:p+KHiXk6v0vIuq+1sYabjbgsnHQP5lV4t2ZnkLNtRkM=
|
||||
github.com/logto-io/go v1.0.6/go.mod h1:owKJJjlaiQxbBGeIDFQTTtufp7ANb9odBXyqUheqWFI=
|
||||
github.com/logto-io/go/v2 v2.0.0 h1:2Eo/S8nfIVNEa2VnzFODc3796vD2BwEOS4f+QboPG3U=
|
||||
github.com/logto-io/go/v2 v2.0.0/go.mod h1:SqrSoACLSnGgO7yam0ecpEjc+ze1tbBaj+Q6OJVL7G8=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
@ -98,26 +118,27 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
|
||||
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
@ -133,13 +154,12 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw=
|
||||
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
@ -150,100 +170,74 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMey
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
|
||||
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
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=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.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.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
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.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
|
||||
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
@ -109,7 +108,7 @@ func CreateContainer(ctx context.Context, cfg *config.DockerConfig, name string,
|
||||
isUserNetwork := false
|
||||
if cfg.Network != "bridge" && cfg.Network != "host" && cfg.Network != "none" {
|
||||
// 检查网络是否存在
|
||||
_, err := cli.NetworkInspect(ctx, cfg.Network, types.NetworkInspectOptions{})
|
||||
_, err := cli.NetworkInspect(ctx, cfg.Network, network.InspectOptions{})
|
||||
if err == nil {
|
||||
isUserNetwork = true
|
||||
}
|
||||
@ -153,7 +152,7 @@ func CreateContainer(ctx context.Context, cfg *config.DockerConfig, name string,
|
||||
// getNextAvailableIPInNetwork 获取网络中下一个可用的IP地址
|
||||
func getNextAvailableIPInNetwork(ctx context.Context, cli *client.Client, networkName string) (string, error) {
|
||||
// 获取网络信息
|
||||
networkResource, err := cli.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
|
||||
networkResource, err := cli.NetworkInspect(ctx, networkName, network.InspectOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("无法检查网络: %w", err)
|
||||
}
|
||||
@ -176,7 +175,7 @@ func getNextAvailableIPInNetwork(ctx context.Context, cli *client.Client, networ
|
||||
}
|
||||
|
||||
// 获取网络中的所有容器
|
||||
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{All: true})
|
||||
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("无法列出容器: %w", err)
|
||||
}
|
||||
@ -300,7 +299,7 @@ func incrementIP(ip net.IP) net.IP {
|
||||
// getNextAvailableIP 获取网络中下一个可用的IP地址
|
||||
func getNextAvailableIP(ctx context.Context, cli *client.Client, networkName string) (string, error) {
|
||||
// 获取网络信息
|
||||
networkResource, err := cli.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
|
||||
networkResource, err := cli.NetworkInspect(ctx, networkName, network.InspectOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("无法检查网络: %w", err)
|
||||
}
|
||||
@ -324,7 +323,7 @@ func getNextAvailableIP(ctx context.Context, cli *client.Client, networkName str
|
||||
|
||||
// 获取现有容器的IP地址
|
||||
existingIPs := make(map[string]bool)
|
||||
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{All: true})
|
||||
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("无法列出容器: %w", err)
|
||||
}
|
||||
@ -493,9 +492,7 @@ func findMaxPortForImage(ctx context.Context, cli *client.Client, imageName stri
|
||||
maxPort := 9000
|
||||
|
||||
// 获取所有容器
|
||||
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{
|
||||
All: true,
|
||||
})
|
||||
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
|
||||
if err != nil {
|
||||
return maxPort, err
|
||||
}
|
||||
@ -518,7 +515,7 @@ func findMaxPortForImage(ctx context.Context, cli *client.Client, imageName stri
|
||||
// StartContainer 启动容器
|
||||
func StartContainer(ctx context.Context, containerID string) error {
|
||||
cli := GetClient()
|
||||
return cli.ContainerStart(ctx, containerID, types.ContainerStartOptions{})
|
||||
return cli.ContainerStart(ctx, containerID, container.StartOptions{})
|
||||
}
|
||||
|
||||
// StopContainer 停止容器
|
||||
@ -531,7 +528,7 @@ func StopContainer(ctx context.Context, containerID string, timeout *time.Durati
|
||||
// RemoveContainer 删除容器
|
||||
func RemoveContainer(ctx context.Context, containerID string, force bool) error {
|
||||
cli := GetClient()
|
||||
return cli.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{
|
||||
return cli.ContainerRemove(ctx, containerID, container.RemoveOptions{
|
||||
Force: force,
|
||||
})
|
||||
}
|
||||
@ -548,7 +545,7 @@ func ListContainers(ctx context.Context, filterArgs map[string][]string) ([]Cont
|
||||
}
|
||||
}
|
||||
|
||||
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{
|
||||
containers, err := cli.ContainerList(ctx, container.ListOptions{
|
||||
All: true, // 包括未运行的容器
|
||||
Filters: filterSet,
|
||||
})
|
||||
@ -575,7 +572,7 @@ func ListContainers(ctx context.Context, filterArgs map[string][]string) ([]Cont
|
||||
func GetContainerLogs(ctx context.Context, containerID string, tail string) (string, error) {
|
||||
cli := GetClient()
|
||||
|
||||
options := types.ContainerLogsOptions{
|
||||
options := container.LogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Tail: tail,
|
||||
|
@ -1,97 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// ExecConfig 命令执行配置
|
||||
type ExecConfig struct {
|
||||
Cmd []string
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
Detach bool
|
||||
Tty bool
|
||||
}
|
||||
|
||||
// DefaultExecConfig 返回默认的命令执行配置
|
||||
func DefaultExecConfig(cmd []string) ExecConfig {
|
||||
return ExecConfig{
|
||||
Cmd: cmd,
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
Detach: false,
|
||||
Tty: false,
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteCommand 在容器中执行命令并返回执行ID
|
||||
func ExecuteCommand(ctx context.Context, containerID string, config ExecConfig) (string, error) {
|
||||
cli := GetClient()
|
||||
|
||||
execConfig := types.ExecConfig{
|
||||
Cmd: config.Cmd,
|
||||
AttachStdout: config.AttachStdout,
|
||||
AttachStderr: config.AttachStderr,
|
||||
Detach: config.Detach,
|
||||
Tty: config.Tty,
|
||||
}
|
||||
|
||||
execResp, err := cli.ContainerExecCreate(ctx, containerID, execConfig)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return execResp.ID, nil
|
||||
}
|
||||
|
||||
// StartExecCommand 启动已创建的命令执行
|
||||
func StartExecCommand(ctx context.Context, execID string) (*bufio.Reader, error) {
|
||||
cli := GetClient()
|
||||
|
||||
resp, err := cli.ContainerExecAttach(ctx, execID, types.ExecStartCheck{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp.Reader, nil
|
||||
}
|
||||
|
||||
// GetExecOutput 获取执行命令的输出
|
||||
func GetExecOutput(ctx context.Context, execID string) (string, error) {
|
||||
cli := GetClient()
|
||||
|
||||
// 附加到命令执行
|
||||
resp, err := cli.ContainerExecAttach(ctx, execID, types.ExecStartCheck{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Close()
|
||||
|
||||
// 读取输出
|
||||
output, err := io.ReadAll(resp.Reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// GetExecStatus 获取命令执行状态
|
||||
func GetExecStatus(ctx context.Context, execID string) (types.ContainerExecInspect, error) {
|
||||
cli := GetClient()
|
||||
return cli.ContainerExecInspect(ctx, execID)
|
||||
}
|
||||
|
||||
// ExecuteCommandWithOutput 执行命令并直接返回输出
|
||||
func ExecuteCommandWithOutput(ctx context.Context, containerID string, cmd []string) (string, error) {
|
||||
execID, err := ExecuteCommand(ctx, containerID, DefaultExecConfig(cmd))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return GetExecOutput(ctx, execID)
|
||||
}
|
@ -2,13 +2,10 @@ package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gitee.ltd/lxh/wechat-robot/internal/model"
|
||||
)
|
||||
|
||||
// ContainerMonitor 容器监控器
|
||||
@ -37,34 +34,34 @@ func NewContainerMonitor(db *gorm.DB, interval time.Duration) *ContainerMonitor
|
||||
|
||||
// Start 启动监控
|
||||
func (m *ContainerMonitor) Start(ctx context.Context) {
|
||||
m.mutex.Lock()
|
||||
if m.monitorActive {
|
||||
m.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
m.monitorActive = true
|
||||
m.mutex.Unlock()
|
||||
|
||||
log.Println("Starting container monitor...")
|
||||
|
||||
// 启动监控协程
|
||||
go func() {
|
||||
ticker := time.NewTicker(m.interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
m.checkRobots(context.Background())
|
||||
case <-m.stopChan:
|
||||
log.Println("Container monitor stopped")
|
||||
return
|
||||
case <-ctx.Done():
|
||||
log.Println("Container monitor stopped due to context cancellation")
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
//m.mutex.Lock()
|
||||
//if m.monitorActive {
|
||||
// m.mutex.Unlock()
|
||||
// return
|
||||
//}
|
||||
//m.monitorActive = true
|
||||
//m.mutex.Unlock()
|
||||
//
|
||||
//log.Println("Starting container monitor...")
|
||||
//
|
||||
//// 启动监控协程
|
||||
//go func() {
|
||||
// ticker := time.NewTicker(m.interval)
|
||||
// defer ticker.Stop()
|
||||
//
|
||||
// for {
|
||||
// select {
|
||||
// case <-ticker.C:
|
||||
// m.checkRobots(context.Background())
|
||||
// case <-m.stopChan:
|
||||
// log.Println("Container monitor stopped")
|
||||
// return
|
||||
// case <-ctx.Done():
|
||||
// log.Println("Container monitor stopped due to context cancellation")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//}()
|
||||
}
|
||||
|
||||
// Stop 停止监控
|
||||
@ -93,50 +90,50 @@ func (m *ContainerMonitor) RemoveRobot(containerID string) {
|
||||
}
|
||||
|
||||
// checkRobots 检查所有机器人状态
|
||||
func (m *ContainerMonitor) checkRobots(ctx context.Context) {
|
||||
m.mutex.RLock()
|
||||
robotIDs := make([]string, 0, len(m.robots))
|
||||
for id := range m.robots {
|
||||
robotIDs = append(robotIDs, id)
|
||||
}
|
||||
m.mutex.RUnlock()
|
||||
|
||||
for _, containerID := range robotIDs {
|
||||
// 使用新的上下文,避免一个检查失败影响其他检查
|
||||
checkCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 获取机器人的当前状态
|
||||
status, errMsg, err := GetWechatBotStatus(checkCtx, containerID)
|
||||
if err != nil {
|
||||
log.Printf("Error checking robot %s: %v", containerID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 更新数据库中的状态
|
||||
robot := &model.Robot{}
|
||||
result := m.db.Where("container_id = ?", containerID).First(robot)
|
||||
if result.Error != nil {
|
||||
log.Printf("Error finding robot %s in database: %v", containerID, result.Error)
|
||||
continue
|
||||
}
|
||||
|
||||
// 只有状态变化时才更新
|
||||
if robot.Status != status {
|
||||
robot.Status = status
|
||||
robot.ErrorMessage = errMsg
|
||||
|
||||
// 如果状态变为在线,更新登录时间
|
||||
if status == model.RobotStatusOnline {
|
||||
now := time.Now()
|
||||
robot.LastLoginAt = &now
|
||||
}
|
||||
|
||||
if err := m.db.Save(robot).Error; err != nil {
|
||||
log.Printf("Error updating robot %s status: %v", containerID, err)
|
||||
} else {
|
||||
log.Printf("Robot %s status updated to %s", containerID, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//func (m *ContainerMonitor) checkRobots(ctx context.Context) {
|
||||
// m.mutex.RLock()
|
||||
// robotIDs := make([]string, 0, len(m.robots))
|
||||
// for id := range m.robots {
|
||||
// robotIDs = append(robotIDs, id)
|
||||
// }
|
||||
// m.mutex.RUnlock()
|
||||
//
|
||||
// for _, containerID := range robotIDs {
|
||||
// // 使用新的上下文,避免一个检查失败影响其他检查
|
||||
// checkCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
// defer cancel()
|
||||
//
|
||||
// // 获取机器人的当前状态
|
||||
// status, errMsg, err := GetWechatBotStatus(checkCtx, containerID)
|
||||
// if err != nil {
|
||||
// log.Printf("Error checking robot %s: %v", containerID, err)
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // 更新数据库中的状态
|
||||
// robot := &model.Robot{}
|
||||
// result := m.db.Where("container_id = ?", containerID).First(robot)
|
||||
// if result.Error != nil {
|
||||
// log.Printf("Error finding robot %s in database: %v", containerID, result.Error)
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // 只有状态变化时才更新
|
||||
// if robot.Status != status {
|
||||
// robot.Status = status
|
||||
// robot.ErrorMessage = errMsg
|
||||
//
|
||||
// // 如果状态变为在线,更新登录时间
|
||||
// if status == model.RobotStatusOnline {
|
||||
// now := time.Now()
|
||||
// robot.LastLoginAt = &now
|
||||
// }
|
||||
//
|
||||
// if err := m.db.Save(robot).Error; err != nil {
|
||||
// log.Printf("Error updating robot %s status: %v", containerID, err)
|
||||
// } else {
|
||||
// log.Printf("Robot %s status updated to %s", containerID, status)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
@ -2,15 +2,8 @@ package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"gitee.ltd/lxh/wechat-robot/internal/config"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/model"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -20,13 +13,6 @@ const (
|
||||
WechatBotLabelValue = "wechat-bot"
|
||||
)
|
||||
|
||||
// 定义一个HTTP客户端生成函数,用于向容器内的API发出请求
|
||||
func newHTTPClient() *resty.Client {
|
||||
return resty.New().
|
||||
SetTimeout(30*time.Second).
|
||||
SetHeader("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
// CreateRobotContainer 创建微信机器人容器
|
||||
func CreateRobotContainer(ctx context.Context, cfg *config.DockerConfig, robotName string, port int) (string, string, error) {
|
||||
// 创建容器标签
|
||||
@ -63,377 +49,3 @@ func CreateRobotContainer(ctx context.Context, cfg *config.DockerConfig, robotNa
|
||||
|
||||
return containerID, containerHost, nil
|
||||
}
|
||||
|
||||
// GetLoginQRCode 获取登录二维码(旧方法)
|
||||
func GetLoginQRCode(ctx context.Context, containerID string) (string, error) {
|
||||
return ExecuteCommandWithOutput(ctx, containerID, []string{"cat", "/data/qrcode.png"})
|
||||
}
|
||||
|
||||
// GetQRCode 获取登录二维码
|
||||
func GetQRCode(ctx context.Context, containerHost string) (*BaseResponse[QRCodeResponse], error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/GetQRCode", containerHost)
|
||||
|
||||
var response BaseResponse[QRCodeResponse]
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody("{}").
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取二维码请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("获取二维码API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
if !response.Success {
|
||||
return nil, fmt.Errorf("获取二维码API错误: %s", response.Message)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// CheckUuid 检查二维码状态
|
||||
func CheckUuid(ctx context.Context, uuid string, containerHost string) (*BaseResponse[CheckUuidResponse], error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/CheckUuid", containerHost)
|
||||
reqBody := map[string]string{"Uuid": uuid}
|
||||
|
||||
var response BaseResponse[CheckUuidResponse]
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(reqBody).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查二维码状态请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("检查二维码状态API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// AwakenLogin 唤醒登录
|
||||
func AwakenLogin(ctx context.Context, wxid string, containerHost string) (*BaseResponse[AwakenLoginResponse], error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/AwakenLogin", containerHost)
|
||||
reqBody := map[string]string{"Wxid": wxid}
|
||||
|
||||
var response BaseResponse[AwakenLoginResponse]
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(reqBody).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("唤醒登录请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("唤醒登录API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// AutoHeartbeatStart 开启自动心跳
|
||||
func AutoHeartbeatStart(ctx context.Context, wxid string, containerHost string) (*BaseResponse[AutoHeartbeatResponse], error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/AutoHeartbeatStart", containerHost)
|
||||
reqBody := map[string]string{"Wxid": wxid}
|
||||
|
||||
var response BaseResponse[AutoHeartbeatResponse]
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(reqBody).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("启动自动心跳请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("启动自动心跳API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
if !response.Success {
|
||||
return nil, fmt.Errorf("启动自动心跳API错误: %s", response.Message)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// AutoHeartbeatStatus 获取自动心跳状态
|
||||
func AutoHeartbeatStatus(ctx context.Context, wxid string, containerHost string) (*AutoHeartbeatStatusResponse, error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/AutoHeartbeatStatus", containerHost)
|
||||
reqBody := map[string]string{"Wxid": wxid}
|
||||
|
||||
var response AutoHeartbeatStatusResponse
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(reqBody).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取自动心跳状态请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("获取自动心跳状态API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// AutoHeartbeatStop 停止自动心跳
|
||||
func AutoHeartbeatStop(ctx context.Context, wxid string, containerHost string) (*AutoHeartbeatResponse, error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/AutoHeartbeatStop", containerHost)
|
||||
reqBody := map[string]string{"Wxid": wxid}
|
||||
|
||||
var response AutoHeartbeatResponse
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(reqBody).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("停止自动心跳请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("停止自动心跳API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
if !response.Success {
|
||||
return nil, fmt.Errorf("停止自动心跳API错误: %s", response.Message)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// LogOut 登出微信
|
||||
func LogOut(ctx context.Context, wxid string, containerHost string) (*AutoHeartbeatResponse, error) {
|
||||
client := newHTTPClient()
|
||||
|
||||
url := fmt.Sprintf("http://%s/LogOut", containerHost)
|
||||
reqBody := map[string]string{"Wxid": wxid}
|
||||
|
||||
var response AutoHeartbeatResponse
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(reqBody).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("退出登录请求失败: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return nil, fmt.Errorf("退出登录API返回错误状态码: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
if !response.Success {
|
||||
return nil, fmt.Errorf("退出登录API错误: %s", response.Message)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// GetWechatBotStatus 获取微信机器人状态(使用HTTP请求替代)
|
||||
func GetWechatBotStatus(ctx context.Context, containerID string) (model.RobotStatus, string, error) {
|
||||
// 检查容器状态
|
||||
status, err := GetContainerStatus(ctx, containerID)
|
||||
if err != nil {
|
||||
return model.RobotStatusError, "", fmt.Errorf("failed to get container status: %w", err)
|
||||
}
|
||||
|
||||
if status != "running" {
|
||||
return model.RobotStatusOffline, "", nil
|
||||
}
|
||||
|
||||
// 获取容器IP(简化处理,默认使用localhost)
|
||||
hostIP := "localhost"
|
||||
|
||||
client := newHTTPClient()
|
||||
url := fmt.Sprintf("http://%s:3000/api/status", hostIP)
|
||||
|
||||
var response BaseResponse[any]
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetResult(&response).
|
||||
Get(url)
|
||||
|
||||
if err != nil {
|
||||
return model.RobotStatusError, "", fmt.Errorf("failed to get status result: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode() != 200 {
|
||||
return model.RobotStatusError, "", fmt.Errorf("status API returned non-200 status code: %d", resp.StatusCode())
|
||||
}
|
||||
|
||||
if !response.Success {
|
||||
return model.RobotStatusError, response.Message, nil
|
||||
}
|
||||
|
||||
// 解析状态数据
|
||||
data, ok := response.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
return model.RobotStatusError, "invalid status data format", nil
|
||||
}
|
||||
|
||||
isLoggedIn, ok := data["isLoggedIn"].(bool)
|
||||
if !ok {
|
||||
return model.RobotStatusError, "invalid login status format", nil
|
||||
}
|
||||
|
||||
if isLoggedIn {
|
||||
return model.RobotStatusOnline, "", nil
|
||||
}
|
||||
|
||||
return model.RobotStatusOffline, "", nil
|
||||
}
|
||||
|
||||
// ListWechatBots 列出所有微信机器人容器
|
||||
func ListWechatBots(ctx context.Context) ([]ContainerInfo, error) {
|
||||
// 过滤条件:所有微信机器人容器
|
||||
filter := map[string][]string{
|
||||
"label": {fmt.Sprintf("%s=%s", WechatBotLabelKey, WechatBotLabelValue)},
|
||||
}
|
||||
|
||||
return ListContainers(ctx, filter)
|
||||
}
|
||||
|
||||
// GetWechatContacts 获取微信联系人列表
|
||||
func GetWechatContacts(ctx context.Context, containerHost string) (string, error) {
|
||||
client := newHTTPClient()
|
||||
url := fmt.Sprintf("http://%s/api/contacts", containerHost)
|
||||
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
Get(url)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取联系人列表请求失败: %w", err)
|
||||
}
|
||||
|
||||
return resp.String(), nil
|
||||
}
|
||||
|
||||
// GetWechatGroupMembers 获取微信群成员
|
||||
func GetWechatGroupMembers(ctx context.Context, groupID string, containerHost string) (string, error) {
|
||||
client := newHTTPClient()
|
||||
url := fmt.Sprintf("http://%s/api/group/%s/members", containerHost, groupID)
|
||||
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
Get(url)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("获取群成员请求失败: %w", err)
|
||||
}
|
||||
|
||||
return resp.String(), nil
|
||||
}
|
||||
|
||||
// GetWechatMessages 获取微信消息历史
|
||||
func GetWechatMessages(ctx context.Context, containerHost, robotWxId string) (msg []Message, isOffline bool, err error) {
|
||||
client := newHTTPClient()
|
||||
url := fmt.Sprintf("http://%s/Sync", containerHost)
|
||||
|
||||
var response BaseResponse[Sync]
|
||||
_, err = client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(map[string]any{
|
||||
"Scene": 0,
|
||||
"Synckey": "",
|
||||
"Wxid": robotWxId,
|
||||
}).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("获取消息历史请求失败: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 判断是否离线
|
||||
if strings.Contains(response.Message, "用户可能退出") || strings.Contains(response.Message, "数据[DecryptData]失败") {
|
||||
isOffline = true
|
||||
}
|
||||
|
||||
msg = response.Data.AddMsgs
|
||||
return
|
||||
}
|
||||
|
||||
// GetContactList
|
||||
// @description: 获取联系人微信Id
|
||||
// @param ctx
|
||||
// @param containerHost
|
||||
// @param robotWxId
|
||||
// @return list
|
||||
// @return err
|
||||
func GetContactList(ctx context.Context, containerHost, robotWxId string) (list []string, err error) {
|
||||
client := newHTTPClient()
|
||||
url := fmt.Sprintf("http://%s/GetContractList", containerHost)
|
||||
|
||||
var response BaseResponse[ContactListResponse]
|
||||
_, err = client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(map[string]any{
|
||||
"CurrentChatroomContactSeq": 0,
|
||||
"CurrentWxcontactSeq": 0,
|
||||
"Wxid": robotWxId,
|
||||
}).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
list = response.Data.ContactUsernameList
|
||||
return
|
||||
}
|
||||
|
||||
// GetContactDetail
|
||||
// @description: 获取联系人详细信息
|
||||
// @param ctx
|
||||
// @param containerHost
|
||||
// @param robotWxId
|
||||
// @param wxId
|
||||
// @return info
|
||||
// @return err
|
||||
func GetContactDetail(ctx context.Context, containerHost, robotWxId string, wxId []string) (info []ContactDetailInfoItem, err error) {
|
||||
client := newHTTPClient()
|
||||
url := fmt.Sprintf("http://%s/GetContact", containerHost)
|
||||
|
||||
var response BaseResponse[ContactDetailInfoResponse]
|
||||
_, err = client.R().
|
||||
SetContext(ctx).
|
||||
SetBody(map[string]any{
|
||||
"Chatroom": "",
|
||||
"RequestWxids": strings.Join(wxId, ","),
|
||||
"Wxid": robotWxId,
|
||||
}).
|
||||
SetResult(&response).
|
||||
Post(url)
|
||||
|
||||
info = response.Data.ContactList
|
||||
return
|
||||
}
|
||||
|
@ -1,218 +0,0 @@
|
||||
package docker
|
||||
|
||||
import "gitee.ltd/lxh/wechat-robot/internal/types"
|
||||
|
||||
// BaseResponse API响应结构
|
||||
type BaseResponse[T any] struct {
|
||||
Success bool `json:"Success"`
|
||||
Code int `json:"Code"`
|
||||
Message string `json:"Message"`
|
||||
Data T `json:"Data"`
|
||||
}
|
||||
|
||||
// QRCodeResponse 获取二维码响应
|
||||
type QRCodeResponse struct {
|
||||
UUID string `json:"Uuid"`
|
||||
ExpiredTime string `json:"ExpiredTime"`
|
||||
QRCodeBase64 string `json:"QRCodeBase64"`
|
||||
QRCodeURL string `json:"QRCodeURL"`
|
||||
}
|
||||
|
||||
// CheckUuidResponse 检查二维码状态响应
|
||||
type CheckUuidResponse struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Status int `json:"status"` // 状态
|
||||
PushLoginUrlExpiredTime int `json:"pushLoginUrlexpiredTime"` // 推送登录url过期时间
|
||||
ExpiredTime int `json:"expiredTime"` // 过期时间(秒)
|
||||
HeadImgUrl string `json:"headImgUrl"` // 头像
|
||||
NickName string `json:"nickName"` // 昵称
|
||||
AcctSectResp map[string]any `json:"acctSectResp"` // 账号信息-登录成功之后才有
|
||||
}
|
||||
|
||||
// AwakenLoginResponse 唤醒登录响应
|
||||
type AwakenLoginResponse struct {
|
||||
QrCodeResponse struct {
|
||||
BaseResponse any `json:"BaseResponse"`
|
||||
BlueToothBroadCastContent any `json:"BlueToothBroadCastContent"`
|
||||
BlueToothBroadCastUuid string `json:"BlueToothBroadCastUuid"`
|
||||
CheckTime int `json:"CheckTime"`
|
||||
ExpiredTime int `json:"ExpiredTime"`
|
||||
NotifyKey any `json:"NotifyKey"`
|
||||
Uuid string `json:"Uuid"`
|
||||
} `json:"QrCodeResponse"`
|
||||
}
|
||||
|
||||
// AutoHeartbeatResponse 心跳响应
|
||||
type AutoHeartbeatResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data struct{}
|
||||
}
|
||||
|
||||
// AutoHeartbeatStatusResponse 心跳状态响应
|
||||
type AutoHeartbeatStatusResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
|
||||
// Sync
|
||||
// @description: 同步微信消息返回值
|
||||
type Sync struct {
|
||||
ModUserInfos any `json:"ModUserInfos"`
|
||||
ModContacts any `json:"ModContacts"`
|
||||
DelContacts any `json:"DelContacts"`
|
||||
ModUserImgs any `json:"ModUserImgs"`
|
||||
FunctionSwitchs any `json:"FunctionSwitchs"`
|
||||
UserInfoExts any `json:"UserInfoExts"`
|
||||
AddMsgs []Message `json:"AddMsgs"`
|
||||
ContinueFlag int `json:"ContinueFlag"`
|
||||
KeyBuf struct {
|
||||
ILen int `json:"iLen"`
|
||||
Buffer string `json:"buffer"`
|
||||
} `json:"KeyBuf"`
|
||||
Status int `json:"Status"`
|
||||
Continue int `json:"Continue"`
|
||||
Time int `json:"Time"`
|
||||
UnknownCmdId string `json:"UnknownCmdId"`
|
||||
Remarks string `json:"Remarks"`
|
||||
}
|
||||
|
||||
// Message
|
||||
// @description: 微信消息
|
||||
type Message struct {
|
||||
MsgId int `json:"MsgId"`
|
||||
FromUserName struct {
|
||||
String string `json:"string"`
|
||||
} `json:"FromUserName"`
|
||||
ToWxid struct {
|
||||
String string `json:"string"`
|
||||
} `json:"ToWxid"`
|
||||
MsgType types.MessageType `json:"MsgType"`
|
||||
Content struct {
|
||||
String string `json:"string"`
|
||||
} `json:"Content"`
|
||||
Status int `json:"Status"`
|
||||
ImgStatus int `json:"ImgStatus"`
|
||||
ImgBuf struct {
|
||||
ILen int `json:"iLen"`
|
||||
} `json:"ImgBuf"`
|
||||
CreateTime int `json:"CreateTime"`
|
||||
MsgSource string `json:"MsgSource"`
|
||||
NewMsgId int64 `json:"NewMsgId"`
|
||||
MsgSeq int `json:"MsgSeq"`
|
||||
PushContent string `json:"PushContent,omitempty"`
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
type ContactListResponse struct {
|
||||
BaseResponse struct {
|
||||
Ret int `json:"ret"`
|
||||
ErrMsg struct {
|
||||
String string `json:"string"`
|
||||
} `json:"errMsg"`
|
||||
} `json:"BaseResponse"`
|
||||
CurrentWxcontactSeq int `json:"CurrentWxcontactSeq"`
|
||||
CurrentChatRoomContactSeq int `json:"CurrentChatRoomContactSeq"`
|
||||
CountinueFlag int `json:"CountinueFlag"`
|
||||
ContactUsernameList []string `json:"ContactUsernameList"` // 联系人微信Id列表
|
||||
}
|
||||
|
||||
// ContactDetailInfo
|
||||
// @description: 联系人详情
|
||||
type ContactDetailInfoResponse struct {
|
||||
BaseResponse struct {
|
||||
Ret int `json:"ret"`
|
||||
ErrMsg struct {
|
||||
} `json:"errMsg"`
|
||||
} `json:"BaseResponse"`
|
||||
ContactCount int `json:"ContactCount"`
|
||||
ContactList []ContactDetailInfoItem `json:"ContactList"`
|
||||
Ret []int `json:"Ret"`
|
||||
Ticket []struct {
|
||||
} `json:"Ticket"`
|
||||
}
|
||||
|
||||
// ContactDetailInfoItem
|
||||
// @description: 详情
|
||||
type ContactDetailInfoItem struct {
|
||||
UserName struct {
|
||||
String string `json:"string"`
|
||||
} `json:"UserName"` // 微信Id
|
||||
NickName struct {
|
||||
String string `json:"string"`
|
||||
} `json:"NickName"` // 昵称
|
||||
Pyinitial struct {
|
||||
String string `json:"string"`
|
||||
} `json:"Pyinitial"` // 昵称拼音首字母大写
|
||||
QuanPin struct {
|
||||
String string `json:"string"`
|
||||
} `json:"QuanPin"` // 昵称拼音全拼小写
|
||||
Sex int `json:"Sex"` // 性别 0:未知 1:男 2:女
|
||||
ImgBuf struct {
|
||||
ILen int `json:"iLen"`
|
||||
} `json:"ImgBuf"`
|
||||
BitMask int64 `json:"BitMask"`
|
||||
BitVal int `json:"BitVal"`
|
||||
ImgFlag int `json:"ImgFlag"`
|
||||
Remark struct {
|
||||
} `json:"Remark"`
|
||||
RemarkPyinitial struct {
|
||||
} `json:"RemarkPyinitial"`
|
||||
RemarkQuanPin struct {
|
||||
} `json:"RemarkQuanPin"`
|
||||
ContactType int `json:"ContactType"`
|
||||
RoomInfoCount int `json:"RoomInfoCount"`
|
||||
DomainList struct {
|
||||
} `json:"DomainList"`
|
||||
ChatRoomNotify int `json:"ChatRoomNotify"`
|
||||
AddContactScene int `json:"AddContactScene"`
|
||||
Province string `json:"Province"` // 省份
|
||||
City string `json:"City"` // 城市
|
||||
Signature string `json:"Signature"` // 个性签名
|
||||
PersonalCard int `json:"PersonalCard"`
|
||||
HasWeiXinHdHeadImg int `json:"HasWeiXinHdHeadImg"`
|
||||
VerifyFlag int `json:"VerifyFlag"`
|
||||
Level int `json:"Level"`
|
||||
Source int `json:"Source"`
|
||||
Alias string `json:"Alias"` // 微信号
|
||||
WeiboFlag int `json:"WeiboFlag"`
|
||||
AlbumStyle int `json:"AlbumStyle"`
|
||||
AlbumFlag int `json:"AlbumFlag"`
|
||||
SnsUserInfo struct {
|
||||
SnsFlag int `json:"SnsFlag"`
|
||||
SnsBgimgId string `json:"SnsBgimgId"` // 朋友圈背景图
|
||||
SnsBgobjectId float64 `json:"SnsBgobjectId"`
|
||||
SnsFlagEx int `json:"SnsFlagEx"`
|
||||
} `json:"SnsUserInfo"`
|
||||
Country string `json:"Country"` // 国家
|
||||
BigHeadImgUrl string `json:"BigHeadImgUrl"` // 大头像地址
|
||||
SmallHeadImgUrl string `json:"SmallHeadImgUrl"` // 小头像地址
|
||||
MyBrandList string `json:"MyBrandList"`
|
||||
CustomizedInfo struct {
|
||||
BrandFlag int `json:"BrandFlag"`
|
||||
} `json:"CustomizedInfo"`
|
||||
HeadImgMd5 string `json:"HeadImgMd5"`
|
||||
EncryptUserName string `json:"EncryptUserName"`
|
||||
AdditionalContactList struct {
|
||||
LinkedinContactItem struct {
|
||||
} `json:"LinkedinContactItem"`
|
||||
} `json:"AdditionalContactList"`
|
||||
ChatroomVersion int `json:"ChatroomVersion"`
|
||||
ChatroomMaxCount int `json:"ChatroomMaxCount"`
|
||||
ChatroomAccessType int `json:"ChatroomAccessType"`
|
||||
NewChatroomData struct {
|
||||
MemberCount int `json:"MemberCount"`
|
||||
InfoMask int `json:"InfoMask"`
|
||||
} `json:"NewChatroomData"`
|
||||
DeleteFlag int `json:"DeleteFlag"`
|
||||
LabelIdlist string `json:"LabelIdlist"`
|
||||
PhoneNumListInfo struct {
|
||||
Count int `json:"Count"`
|
||||
} `json:"PhoneNumListInfo"`
|
||||
ChatroomInfoVersion int `json:"ChatroomInfoVersion"`
|
||||
DeleteContactScene int `json:"DeleteContactScene"`
|
||||
ChatroomStatus int `json:"ChatroomStatus"`
|
||||
ExtFlag int `json:"ExtFlag"`
|
||||
}
|
@ -4,9 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// ContainerStats 容器统计数据结构
|
||||
@ -78,7 +77,7 @@ func GetStats(ctx context.Context, containerID string) (*ContainerStats, error)
|
||||
defer stats.Body.Close()
|
||||
|
||||
// 解析统计数据JSON
|
||||
var statsJSON types.StatsJSON
|
||||
var statsJSON container.StatsResponse
|
||||
if err := json.NewDecoder(stats.Body).Decode(&statsJSON); err != nil {
|
||||
return nil, fmt.Errorf("解析容器统计数据失败: %w", err)
|
||||
}
|
||||
@ -111,7 +110,7 @@ func GetStats(ctx context.Context, containerID string) (*ContainerStats, error)
|
||||
}
|
||||
|
||||
// 计算CPU使用百分比
|
||||
func calculateCPUPercentage(stats *types.StatsJSON) float64 {
|
||||
func calculateCPUPercentage(stats *container.StatsResponse) float64 {
|
||||
if stats == nil {
|
||||
return 0.0
|
||||
}
|
||||
@ -133,7 +132,7 @@ func calculateCPUPercentage(stats *types.StatsJSON) float64 {
|
||||
}
|
||||
|
||||
// 获取内存使用量
|
||||
func getMemoryUsage(stats *types.StatsJSON) int64 {
|
||||
func getMemoryUsage(stats *container.StatsResponse) int64 {
|
||||
if stats == nil || stats.MemoryStats.Usage == 0 {
|
||||
return 0
|
||||
}
|
||||
@ -147,7 +146,7 @@ func getMemoryUsage(stats *types.StatsJSON) int64 {
|
||||
}
|
||||
|
||||
// 获取内存限制
|
||||
func getMemoryLimit(stats *types.StatsJSON) int64 {
|
||||
func getMemoryLimit(stats *container.StatsResponse) int64 {
|
||||
if stats == nil || stats.MemoryStats.Limit == 0 {
|
||||
return 0
|
||||
}
|
||||
@ -155,7 +154,7 @@ func getMemoryLimit(stats *types.StatsJSON) int64 {
|
||||
}
|
||||
|
||||
// 计算内存使用百分比
|
||||
func calculateMemoryPercentage(stats *types.StatsJSON) float64 {
|
||||
func calculateMemoryPercentage(stats *container.StatsResponse) float64 {
|
||||
if stats == nil {
|
||||
return 0.0
|
||||
}
|
||||
@ -177,7 +176,7 @@ type networkStats struct {
|
||||
}
|
||||
|
||||
// 获取网络统计数据
|
||||
func getNetworkStats(stats *types.StatsJSON) networkStats {
|
||||
func getNetworkStats(stats *container.StatsResponse) networkStats {
|
||||
if stats == nil || len(stats.Networks) == 0 {
|
||||
return networkStats{}
|
||||
}
|
||||
@ -198,7 +197,7 @@ type ioStats struct {
|
||||
}
|
||||
|
||||
// 获取IO统计数据
|
||||
func getIOStats(stats *types.StatsJSON) ioStats {
|
||||
func getIOStats(stats *container.StatsResponse) ioStats {
|
||||
if stats == nil {
|
||||
return ioStats{}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/config"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/tasks"
|
||||
"gitee.ltd/lxh/xybot"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gitee.ltd/lxh/wechat-robot/internal/docker"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/model"
|
||||
)
|
||||
|
||||
@ -51,12 +50,15 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// 调用CheckUuid API检查二维码状态
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 调用CheckUuid API检查二维码状态,传递容器访问地址
|
||||
response, err := docker.CheckUuid(ctx, uuid, robot.ContainerHost)
|
||||
robotCli, err := xybot.NewClient(robot.WechatID, robot.ContainerHost, false)
|
||||
if err != nil {
|
||||
return c.JSON(fiber.Map{
|
||||
"success": false,
|
||||
"message": "创建微信客户端失败: " + err.Error(),
|
||||
})
|
||||
}
|
||||
response, err := robotCli.Login.CheckUuid(uuid)
|
||||
if err != nil {
|
||||
return c.JSON(fiber.Map{
|
||||
"success": false,
|
||||
@ -70,20 +72,20 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// 如果返回status=1,表示已扫码,暂存一下昵称和头像
|
||||
if response.Data.Status == 1 {
|
||||
robot.Nickname = response.Data.NickName
|
||||
robot.Avatar = response.Data.HeadImgUrl
|
||||
if response.Status == 1 {
|
||||
robot.Nickname = response.NickName
|
||||
robot.Avatar = response.HeadImgUrl
|
||||
db.Save(&robot)
|
||||
}
|
||||
|
||||
// 如果检测到已登录,更新机器人状态
|
||||
if response.Success && response.Data.AcctSectResp != nil {
|
||||
response.Data.Status = 99
|
||||
robot.WechatID = response.Data.AcctSectResp["userName"].(string)
|
||||
if response.AcctSectResp.Username != "" {
|
||||
response.Status = 99
|
||||
robot.WechatID = response.AcctSectResp.Username
|
||||
|
||||
// 开启自动心跳,传递容器访问地址
|
||||
if robot.WechatID != "" {
|
||||
_, _ = docker.AutoHeartbeatStart(ctx, robot.WechatID, robot.ContainerHost)
|
||||
_ = robotCli.Login.AutoHeartbeatStart()
|
||||
}
|
||||
|
||||
// 更新机器人状态
|
||||
@ -97,9 +99,9 @@ func CheckQRCodeStatus(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"success": response.Success,
|
||||
"status": response.Data.Status,
|
||||
"message": response.Message,
|
||||
"userInfo": response.Data.AcctSectResp,
|
||||
"success": true,
|
||||
"status": response.Status,
|
||||
"message": "success",
|
||||
"userInfo": response.AcctSectResp,
|
||||
})
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ func HealthCheck(c *fiber.Ctx) error {
|
||||
"status": "ok",
|
||||
"message": "service is healthy",
|
||||
"timestamp": time.Now().Format(time.RFC3339),
|
||||
"database": map[string]interface{}{
|
||||
"database": map[string]any{
|
||||
"connected": dbErr == nil,
|
||||
},
|
||||
"system": map[string]interface{}{
|
||||
"system": map[string]any{
|
||||
"goroutines": runtime.NumGoroutine(),
|
||||
"memory": map[string]interface{}{
|
||||
"memory": map[string]any{
|
||||
"alloc": m.Alloc / 1024 / 1024,
|
||||
"total_alloc": m.TotalAlloc / 1024 / 1024,
|
||||
"sys": m.Sys / 1024 / 1024,
|
||||
|
@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gitee.ltd/lxh/xybot"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -168,8 +169,13 @@ func DeleteRobot(c *fiber.Ctx) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
robotCli, err := xybot.NewClient(robot.WechatID, robot.ContainerHost, false)
|
||||
if err != nil {
|
||||
log.Printf("创建微信客户端失败: %v", err)
|
||||
}
|
||||
|
||||
if robot.Status == model.RobotStatusOnline {
|
||||
if _, err = docker.LogOut(ctx, robot.WechatID, robot.ContainerHost); err != nil {
|
||||
if err = robotCli.Login.Logout(); err != nil {
|
||||
log.Printf("登出机器人失败: %v", err)
|
||||
// 继续删除流程,不因登出失败而中断
|
||||
}
|
||||
@ -242,13 +248,20 @@ func RobotLogin(c *fiber.Ctx) error {
|
||||
// 检查是否指定使用二维码登录
|
||||
forceQrcode := c.Query("qrcode") == "1"
|
||||
|
||||
// 创建微信客户端
|
||||
robotCli, err := xybot.NewClient(robot.WechatID, robot.ContainerHost, false)
|
||||
if err != nil {
|
||||
return c.Render("robot/login", fiber.Map{
|
||||
"Title": "微信登录",
|
||||
"Robot": robot,
|
||||
"Message": "创建微信客户端失败: " + err.Error(),
|
||||
"IsError": true,
|
||||
})
|
||||
}
|
||||
|
||||
// 如果存在WechatID且没有强制要求使用二维码,则使用唤醒登录
|
||||
if robot.WechatID != "" && !forceQrcode {
|
||||
// 尝试唤醒登录
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
awakenResp, err := docker.AwakenLogin(ctx, robot.WechatID, robot.ContainerHost)
|
||||
awakenResp, err := robotCli.Login.AwakenLogin()
|
||||
if err != nil {
|
||||
return c.Render("robot/login", fiber.Map{
|
||||
"Title": "微信登录",
|
||||
@ -262,18 +275,14 @@ func RobotLogin(c *fiber.Ctx) error {
|
||||
return c.Render("robot/login", fiber.Map{
|
||||
"Title": "微信登录",
|
||||
"Robot": robot,
|
||||
"UUID": awakenResp.Data.QrCodeResponse.Uuid,
|
||||
"Expired": awakenResp.Data.QrCodeResponse.ExpiredTime,
|
||||
"UUID": awakenResp.QrCodeResponse.Uuid,
|
||||
"Expired": awakenResp.QrCodeResponse.ExpiredTime,
|
||||
"IsAwaken": true,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取登录二维码
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 使用新的GetQRCode接口获取二维码,并传递容器访问地址
|
||||
qrcodeResp, err := docker.GetQRCode(ctx, robot.ContainerHost)
|
||||
qrcodeResp, err := robotCli.Login.GetQRCode(robot.DeviceId, robot.DeviceName)
|
||||
if err != nil {
|
||||
return c.Render("robot/login", fiber.Map{
|
||||
"Title": "微信登录",
|
||||
@ -287,9 +296,9 @@ func RobotLogin(c *fiber.Ctx) error {
|
||||
return c.Render("robot/login", fiber.Map{
|
||||
"Title": "微信登录",
|
||||
"Robot": robot,
|
||||
"QRCode": qrcodeResp.Data.QRCodeURL,
|
||||
"UUID": qrcodeResp.Data.UUID,
|
||||
"Expired": qrcodeResp.Data.ExpiredTime,
|
||||
"QRCode": qrcodeResp.QRCodeURL,
|
||||
"UUID": qrcodeResp.Uuid,
|
||||
"Expired": qrcodeResp.ExpiredTime,
|
||||
"IsAwaken": false,
|
||||
})
|
||||
}
|
||||
@ -311,13 +320,14 @@ func RobotLogout(c *fiber.Ctx) error {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "查询数据库失败")
|
||||
}
|
||||
|
||||
// 登出微信
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 使用新的LogOut API接口,传递容器访问地址
|
||||
// 创建微信客户端
|
||||
robotCli, err := xybot.NewClient(robot.WechatID, robot.ContainerHost, false)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "创建微信客户端失败: "+err.Error())
|
||||
}
|
||||
// 使用新的Logout API接口,传递容器访问地址
|
||||
if robot.WechatID != "" {
|
||||
if _, err = docker.LogOut(ctx, robot.WechatID, robot.ContainerHost); err != nil {
|
||||
if err = robotCli.Login.Logout(); err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "登出微信失败: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -45,12 +45,12 @@ func New(cfg *config.Config) *Server {
|
||||
})
|
||||
|
||||
// 添加map函数,用于在模板中创建映射
|
||||
engine.AddFunc("map", func(values ...interface{}) map[string]interface{} {
|
||||
engine.AddFunc("map", func(values ...any) map[string]any {
|
||||
if len(values)%2 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := make(map[string]interface{}, len(values)/2)
|
||||
m := make(map[string]any, len(values)/2)
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
if key, ok := values[i].(string); ok {
|
||||
m[key] = values[i+1]
|
||||
|
@ -1,13 +1,10 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/docker"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/model"
|
||||
"gitee.ltd/lxh/xybot"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// syncContact
|
||||
@ -16,27 +13,21 @@ import (
|
||||
// @param robotWxId 机器人微信号
|
||||
// @param robotId 机器人ID
|
||||
func syncContact(containerHost, robotWxId string, robotId uint) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
robotCli, err := xybot.NewClient(robotWxId, containerHost, false)
|
||||
if err != nil {
|
||||
log.Errorf("创建微信客户端失败: %v", err)
|
||||
}
|
||||
|
||||
// 先获取全部id
|
||||
ids, err := docker.GetContactList(ctx, containerHost, robotWxId)
|
||||
ids, err := robotCli.Friend.GetContractList(true)
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
log.Errorf("[%s]获取联系人列表失败: %v", robotWxId, err)
|
||||
return
|
||||
}
|
||||
// 过滤掉特殊微信Id
|
||||
var specialId = []string{"filehelper", "newsapp", "fmessage", "weibo", "qqmail", "tmessage", "qmessage", "qqsync",
|
||||
"floatbottle", "lbsapp", "shakeapp", "medianote", "qqfriend", "readerapp", "blogapp", "facebookapp", "masssendapp",
|
||||
"meishiapp", "feedsapp", "voip", "blogappweixin", "weixin", "brandsessionholder", "weixinreminder", "officialaccounts",
|
||||
"notification_messages", "wxitil", "userexperience_alarm", "notification_messages", "exmail_tool", "mphelper"}
|
||||
ids = slices.DeleteFunc(ids, func(id string) bool {
|
||||
return slices.Contains(specialId, id) || strings.HasPrefix(id, "gh_") || strings.TrimSpace(id) == ""
|
||||
})
|
||||
|
||||
// 获取昵称等详细信息
|
||||
contacts, err := docker.GetContactDetail(ctx, containerHost, robotWxId, ids)
|
||||
contacts, err := robotCli.Friend.GetContractDetail(ids)
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
log.Errorf("[%s]获取联系人详情失败: %v", robotWxId, err)
|
||||
@ -71,7 +62,7 @@ func syncContact(containerHost, robotWxId string, robotId uint) {
|
||||
"province": contact.Province,
|
||||
"city": contact.City,
|
||||
"signature": contact.Signature,
|
||||
"sns_background": contact.SnsUserInfo.SnsBgimgId,
|
||||
"sns_background": contact.SnsUserInfo.SnsBgImgId,
|
||||
}
|
||||
if contact.BigHeadImgUrl == "" {
|
||||
pm["avatar"] = contact.SmallHeadImgUrl
|
||||
@ -100,7 +91,7 @@ func syncContact(containerHost, robotWxId string, robotId uint) {
|
||||
c.Province = contact.Province
|
||||
c.City = contact.City
|
||||
c.Signature = contact.Signature
|
||||
c.SnsBackground = contact.SnsUserInfo.SnsBgimgId
|
||||
c.SnsBackground = contact.SnsUserInfo.SnsBgImgId
|
||||
|
||||
err = db.Create(&c).Error
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/docker"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/model"
|
||||
"gitee.ltd/lxh/wechat-robot/internal/types"
|
||||
"gitee.ltd/lxh/xybot"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -14,21 +13,22 @@ import (
|
||||
// @param containerHost 机器人接口地址
|
||||
// @param robotWxId 机器人微信Id
|
||||
// @param robotId 机器人数据Id
|
||||
func syncMessage(containerHost, robotWxId string, robotId uint) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
messages, isOffline, err := docker.GetWechatMessages(ctx, containerHost, robotWxId)
|
||||
if err != nil {
|
||||
// 处理错误
|
||||
return
|
||||
}
|
||||
func syncMessage(client *xybot.Client, robotId uint) {
|
||||
// 获取数据库连接
|
||||
db := model.GetDB()
|
||||
if isOffline {
|
||||
// 删除定时任务
|
||||
DeleteJob(robotId)
|
||||
// 修改机器人状态
|
||||
db.Model(&model.Robot{}).Where("id = ?", robotId).Update("status", "offline")
|
||||
|
||||
// 获取新消息
|
||||
messages, err := client.Message.Sync()
|
||||
if err != nil {
|
||||
// 手动处理一下是否离线了
|
||||
isOffline := strings.Contains(err.Error(), "用户可能退出") || strings.Contains(err.Error(), "数据[DecryptData]失败")
|
||||
if isOffline {
|
||||
// 删除定时任务
|
||||
DeleteJob(robotId)
|
||||
// 修改机器人状态
|
||||
db.Model(&model.Robot{}).Where("id = ?", robotId).Update("status", "offline")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ func syncMessage(containerHost, robotWxId string, robotId uint) {
|
||||
m.MsgId = message.NewMsgId
|
||||
m.CreateTime = message.CreateTime
|
||||
m.CreateAt = time.Unix(int64(message.CreateTime), 0)
|
||||
m.Type = message.MsgType
|
||||
m.Type = types.MessageType(message.MsgType)
|
||||
m.Content = message.Content.String
|
||||
m.DisplayFullContent = message.PushContent
|
||||
m.FromUser = message.FromUserName.String
|
||||
@ -53,7 +53,7 @@ func syncMessage(containerHost, robotWxId string, robotId uint) {
|
||||
|
||||
// 如果是群聊消息,单独处理一下
|
||||
// Sys类型的消息正文不包含微信 Id,所以不需要处理
|
||||
if strings.HasSuffix(message.FromUserName.String, "@chatroom") && message.MsgType != types.MsgTypeSys {
|
||||
if strings.HasSuffix(message.FromUserName.String, "@chatroom") && m.Type != types.MsgTypeSys {
|
||||
// 群消息,处理一下消息和发信人
|
||||
groupUser := strings.Split(m.Content, "\n")[0]
|
||||
groupUser = strings.ReplaceAll(groupUser, ":", "")
|
||||
|
@ -2,6 +2,7 @@ package tasks
|
||||
|
||||
import (
|
||||
"gitee.ltd/lxh/wechat-robot/internal/model"
|
||||
"gitee.ltd/lxh/xybot"
|
||||
"github.com/go-co-op/gocron/v2"
|
||||
"github.com/google/uuid"
|
||||
"log"
|
||||
@ -33,10 +34,15 @@ func Start() {
|
||||
}
|
||||
// 遍历机器人,添加任务
|
||||
for _, robot := range robots {
|
||||
// 初始化微信客户端
|
||||
robotCli, err := xybot.NewClient(robot.WechatID, robot.ContainerHost, false)
|
||||
if err != nil {
|
||||
log.Panicf("初始化微信客户端失败: %v", err)
|
||||
}
|
||||
var job gocron.Job
|
||||
job, err = scheduler.NewJob(
|
||||
gocron.CronJob("*/5 * * * * *", true), // 五秒钟同步一次
|
||||
gocron.NewTask(syncMessage, robot.ContainerHost, robot.WechatID, robot.ID),
|
||||
gocron.NewTask(syncMessage, robotCli, robot.ID),
|
||||
)
|
||||
if err != nil {
|
||||
log.Panicf("添加定时任务失败: %v", err)
|
||||
@ -64,9 +70,15 @@ func Start() {
|
||||
// AddJob
|
||||
// @description: 添加任务
|
||||
func AddJob(robot model.Robot) {
|
||||
// 初始化微信客户端
|
||||
robotCli, err := xybot.NewClient(robot.WechatID, robot.ContainerHost, false)
|
||||
if err != nil {
|
||||
log.Panicf("初始化微信客户端失败: %v", err)
|
||||
}
|
||||
|
||||
job, err := scheduler.NewJob(
|
||||
gocron.CronJob("*/5 * * * * *", true), // 五秒钟同步一次
|
||||
gocron.NewTask(syncMessage, robot.ContainerHost, robot.WechatID, robot.ID),
|
||||
gocron.NewTask(syncMessage, robotCli, robot.ID),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("添加定时任务失败: %v", err)
|
||||
|
@ -108,28 +108,33 @@
|
||||
<div class="w-full bg-gray-100 rounded-full h-2 mr-3">
|
||||
<div id="cpu-bar" class="bg-blue-500 h-2 rounded-full" style="width: 0%"></div>
|
||||
</div>
|
||||
<span id="cpu-usage" class="text-sm font-medium text-gray-700 min-w-[40px]">0%</span>
|
||||
<span id="cpu-usage" class="text-sm font-medium text-gray-700 min-w-[120px]">0%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<p class="text-sm text-gray-500">内存使用率</p>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full bg-gray-100 rounded-full h-2 mr-3">
|
||||
<div id="memory-bar" class="bg-green-500 h-2 rounded-full" style="width: 0%"></div>
|
||||
<div class="w-full bg-gray-100 rounded-full h-6 mr-3 overflow-hidden relative">
|
||||
<div id="memory-bar" class="bg-green-500 h-6 rounded-full" style="width: 0%"></div>
|
||||
<div class="absolute inset-0 flex items-center px-3">
|
||||
<span id="memory-usage-display" class="text-xs font-medium text-gray-800">0MB/0MB</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm font-medium text-gray-700 min-w-[50px]">
|
||||
<span id="memory-percent">0%</span>
|
||||
</div>
|
||||
<span id="memory-usage" class="text-sm font-medium text-gray-700 min-w-[40px]">0%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">网络接收</p>
|
||||
<p id="network-rx" class="text-lg font-medium text-gray-800">0 KB/s</p>
|
||||
<p id="network-rx" class="text-lg font-medium text-gray-800">0 KB</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 mb-1">网络发送</p>
|
||||
<p id="network-tx" class="text-lg font-medium text-gray-800">0 KB/s</p>
|
||||
<p id="network-tx" class="text-lg font-medium text-gray-800">0 KB</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -210,12 +215,16 @@
|
||||
const memoryUsage = data.stats.memory_usage || 0;
|
||||
const memoryLimit = data.stats.memory_limit || 1;
|
||||
const memoryPercent = ((memoryUsage / memoryLimit) * 100).toFixed(2);
|
||||
document.getElementById('memory-usage').textContent = `${memoryPercent}%`;
|
||||
const formattedMemoryUsage = formatBytes(memoryUsage);
|
||||
const formattedMemoryTotal = formatBytes(memoryLimit);
|
||||
|
||||
document.getElementById('memory-usage-display').textContent = `${formattedMemoryUsage}/${formattedMemoryTotal}`;
|
||||
document.getElementById('memory-percent').textContent = `${memoryPercent}%`;
|
||||
document.getElementById('memory-bar').style.width = `${Math.min(memoryPercent, 100)}%`;
|
||||
|
||||
// 更新网络信息
|
||||
const networkRx = formatBytes(data.stats.network_rx || 0) + '/s';
|
||||
const networkTx = formatBytes(data.stats.network_tx || 0) + '/s';
|
||||
const networkRx = formatBytes(data.stats.network_rx || 0);
|
||||
const networkTx = formatBytes(data.stats.network_tx || 0);
|
||||
document.getElementById('network-rx').textContent = networkRx;
|
||||
document.getElementById('network-tx').textContent = networkTx;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user