diff --git a/go.mod b/go.mod index c684112..dbfe76d 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/robfig/cron v1.2.0 go.uber.org/zap v1.27.0 - golang.org/x/net v0.39.0 + golang.org/x/net v0.40.0 ) require ( @@ -21,6 +21,8 @@ require ( github.com/gocolly/colly v1.2.0 github.com/ncruces/go-sqlite3 v0.18.4 github.com/nikoksr/notify v1.0.0 + github.com/openai/openai-go v0.1.0-beta.10 + github.com/pion/stun/v3 v3.0.0 github.com/stretchr/testify v1.9.0 golift.io/starr v1.0.0 ) @@ -55,7 +57,6 @@ require ( github.com/bits-and-blooms/bitset v1.2.2 // indirect github.com/blinkbean/dingtalk v1.1.3 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect - github.com/buger/jsonparser v1.1.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect @@ -73,19 +74,17 @@ require ( github.com/gregdel/pushover v1.3.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/mschoch/smat v0.2.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.0.6 // indirect github.com/ncruces/julianday v1.0.0 // indirect - github.com/openai/openai-go v0.1.0-beta.10 // indirect github.com/pion/datachannel v1.5.9 // indirect github.com/pion/dtls/v3 v3.0.3 // indirect github.com/pion/ice/v4 v4.0.2 // indirect github.com/pion/interceptor v0.1.37 // indirect - github.com/pion/logging v0.2.2 // indirect + github.com/pion/logging v0.2.3 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.14 // indirect @@ -93,7 +92,6 @@ require ( github.com/pion/sctp v1.8.33 // indirect github.com/pion/sdp/v3 v3.0.9 // indirect github.com/pion/srtp/v3 v3.0.4 // indirect - github.com/pion/stun/v3 v3.0.0 // indirect github.com/pion/transport/v3 v3.0.7 // indirect github.com/pion/turn/v4 v4.0.0 // indirect github.com/pion/webrtc/v4 v4.0.0 // indirect @@ -112,15 +110,13 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect - github.com/wlynxg/anet v0.0.3 // indirect + github.com/wlynxg/anet v0.0.5 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect - golang.org/x/sync v0.13.0 // indirect + golang.org/x/sync v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.32.0 // indirect google.golang.org/appengine v1.6.8 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -148,7 +144,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-json v0.10.2 github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -174,11 +170,11 @@ require ( github.com/ugorji/go/codec v1.2.12 // indirect github.com/zclconf/go-cty v1.8.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.37.0 + golang.org/x/crypto v0.38.0 golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 golang.org/x/mod v0.24.0 // indirect - golang.org/x/sys v0.32.0 - golang.org/x/text v0.24.0 // indirect + golang.org/x/sys v0.33.0 + golang.org/x/text v0.25.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -188,7 +184,6 @@ require ( github.com/cyruzin/golang-tmdb v1.6.3 github.com/gin-gonic/gin v1.10.0 github.com/hekmon/transmissionrpc/v3 v3.0.0 - github.com/invopop/jsonschema v0.13.0 github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index ab82235..b73bfd6 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,6 @@ github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2w github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= @@ -280,13 +278,8 @@ github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= -github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -320,12 +313,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 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.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -360,8 +349,6 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/nikoksr/notify v1.0.0 h1:qe9/6FRsWdxBgQgWcpvQ0sv8LRGJZDpRB4TkL2uNdO8= github.com/nikoksr/notify v1.0.0/go.mod h1:hPaaDt30d6LAA7/5nb0e48Bp/MctDfycCSs8VEgN29I= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -380,8 +367,8 @@ github.com/pion/ice/v4 v4.0.2 h1:1JhBRX8iQLi0+TfcavTjPjI6GO41MFn4CeTBX+Y9h5s= github.com/pion/ice/v4 v4.0.2/go.mod h1:DCdqyzgtsDNYN6/3U8044j3U7qsJ9KFJC92VnOWHvXg= github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= -github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= @@ -468,8 +455,6 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= @@ -522,10 +507,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg= -github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= @@ -559,8 +542,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA= @@ -601,8 +584,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -618,8 +601,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -647,8 +630,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -672,8 +655,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -688,10 +671,6 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= 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= diff --git a/pkg/go-qbittorrent/qbt/api.go b/pkg/go-qbittorrent/qbt/api.go index a44ecc3..9713fdc 100644 --- a/pkg/go-qbittorrent/qbt/api.go +++ b/pkg/go-qbittorrent/qbt/api.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "io" + "log" "mime/multipart" "net/http" "net/http/cookiejar" @@ -93,6 +94,28 @@ func (client *Client) get(endpoint string, opts map[string]string) (*http.Respon return resp, nil } +func (cleint *Client) postJson(endpoint string, body any) (*http.Response, error) { + var buff bytes.Buffer + buff.WriteString("json=") + d, err := json.Marshal(body) + if err!= nil { + return nil, err + } + buff.Write(d) + log.Println(buff.String()) + req, err := http.NewRequest("POST", cleint.URL+endpoint, &buff) + if err!= nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("User-Agent", "go-qbittorrent v0.1") + resp, err := cleint.http.Do(req) + if err!= nil { + return nil, err + } + return resp, nil +} + // post will perform a POST request with no content-type specified func (client *Client) post(endpoint string, opts map[string]string) (*http.Response, error) { // add optional parameters that the user wants @@ -315,8 +338,9 @@ func (client *Client) Preferences() (prefs Preferences, err error) { } // SetPreferences of the qbittorrent client -func (client *Client) SetPreferences() (prefsSet bool, err error) { - resp, err := client.post("api/v2/app/setPreferences", nil) +func (client *Client) SetPreferences(m map[string]any) (prefsSet bool, err error) { + + resp, err := client.postJson("api/v2/app/setPreferences", m) return (resp.Status == "200 OK"), err } diff --git a/pkg/nat/cmd/main.go b/pkg/nat/cmd/main.go new file mode 100644 index 0000000..c098962 --- /dev/null +++ b/pkg/nat/cmd/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "net" + "polaris/log" + "polaris/pkg/nat" +) + +func main() { + // This is a placeholder for the main function. + // The actual implementation will depend on the specific requirements of the application. + src, err := net.Listen("tcp", ":8080") + if err != nil { + panic(err) + } + + for { + conn, err := src.Accept() + if err != nil { + panic(err) + } + log.Infof("new connection: %+v", conn) + dest, err := net.Dial("tcp", "10.0.0.8:8080") + if err != nil { + panic(err) + } + + go nat.ReverseProxy(conn, dest) + } + select {} +} diff --git a/pkg/nat/reverse_proxy.go b/pkg/nat/reverse_proxy.go new file mode 100644 index 0000000..a7c4026 --- /dev/null +++ b/pkg/nat/reverse_proxy.go @@ -0,0 +1,67 @@ +package nat + +import ( + "io" + "log" + "net" +) + +func ReverseProxy(sourceConn net.Conn, targetConn net.Conn) { + serverClosed := make(chan struct{}, 1) + clientClosed := make(chan struct{}, 1) + + go broker(sourceConn, targetConn, clientClosed) + go broker(targetConn, sourceConn, serverClosed) + + // wait for one half of the proxy to exit, then trigger a shutdown of the + // other half by calling CloseRead(). This will break the read loop in the + // broker and allow us to fully close the connection cleanly without a + // "use of closed network connection" error. + var waitFor chan struct{} + select { + case <-clientClosed: + // the client closed first and any more packets from the server aren't + // useful, so we can optionally SetLinger(0) here to recycle the port + // faster. + waitFor = serverClosed + case <-serverClosed: + waitFor = clientClosed + } + + // Wait for the other connection to close. + // This "waitFor" pattern isn't required, but gives us a way to track the + // connection and ensure all copies terminate correctly; we can trigger + // stats on entry and deferred exit of this function. + <-waitFor +} + +func pipe(src net.Conn, dest net.Conn) { + errChan := make(chan error, 1) + go func() { + _, err := io.Copy(src, dest) + errChan <- err + }() + go func() { + _, err := io.Copy(dest, src) + errChan <- err + }() + <-errChan +} + +// This does the actual data transfer. +// The broker only closes the Read side. +func broker(dst, src net.Conn, srcClosed chan struct{}) { + // We can handle errors in a finer-grained manner by inlining io.Copy (it's + // simple, and we drop the ReaderFrom or WriterTo checks for + // net.Conn->net.Conn transfers, which aren't needed). This would also let + // us adjust buffersize. + _, err := io.Copy(dst, src) + + if err != nil { + log.Printf("Copy error: %s", err) + } + if err := src.Close(); err != nil { + log.Printf("Close error: %s", err) + } + srcClosed <- struct{}{} +} diff --git a/pkg/nat/stun.go b/pkg/nat/stun.go new file mode 100644 index 0000000..330fa1e --- /dev/null +++ b/pkg/nat/stun.go @@ -0,0 +1,169 @@ +package nat + +import ( + "fmt" + "strings" + "polaris/log" + + "github.com/pion/stun/v3" +) + +func getNatIpAndPort() (*stun.XORMappedAddress, error) { + + var xorAddr stun.XORMappedAddress + + for _, server := range getStunServers() { + log.Infof("try to connect to stun server: %s", server) + u, err := stun.ParseURI("stun:" + server) + if err != nil { + continue + } + // Creating a "connection" to STUN server. + c, err := stun.DialURI(u, &stun.DialConfig{}) + if err != nil { + continue + } + // Building binding request with random transaction id. + message := stun.MustBuild(stun.TransactionID, stun.BindingRequest) + // Sending request to STUN server, waiting for response message. + var err1 error + if err := c.Do(message, func(res stun.Event) { + if res.Error != nil { + err1 = res.Error + return + } + log.Infof("stun server %s response: %v", server, res.Message.String()) + // Decoding XOR-MAPPED-ADDRESS attribute from message. + + if err := xorAddr.GetFrom(res.Message); err != nil { + err1 = err + return + } + fmt.Println("your IP is", xorAddr.IP) + fmt.Println("your port is", xorAddr.Port) + }); err != nil { + log.Warnf("stun server %s error: %v", server, err) + continue + } + if err1 != nil { + log.Warnf("stun server %s error: %v", server, err1) + continue + } + break + } + return &xorAddr, nil +} + +func getStunServers() []string { + var servers []string + for _, line := range strings.Split(strings.TrimSpace(stunServers1), "\n") { + line = strings.TrimSpace(line) + if line == "" { + continue + } + servers = append(servers, line) + } + return servers +} + +// https://github.com/heiher/natmap/issues/18 +const stunServers1 = ` +stun.miwifi.com:3478 +stun.chat.bilibili.com:3478 +stun.cloudflare.com:3478 +turn.cloudflare.com:3478 +fwa.lifesizecloud.com:3478 +` + +// https://github.com/pradt2/always-online-stun +const stunServers = ` +stun.miwifi.com:3478 +stun.ukh.de:3478 +stun.kanojo.de:3478 +stun.m-online.net:3478 +stun.nextcloud.com:3478 +stun.voztovoice.org:3478 +stun.oncloud7.ch:3478 +stun.antisip.com:3478 +stun.bitburger.de:3478 +stun.acronis.com:3478 +stun.signalwire.com:3478 +stun.sonetel.net:3478 +stun.poetamatusel.org:3478 +stun.avigora.fr:3478 +stun.diallog.com:3478 +stun.nanocosmos.de:3478 +stun.romaaeterna.nl:3478 +stun.heeds.eu:3478 +stun.freeswitch.org:3478 +stun.engineeredarts.co.uk:3478 +stun.root-1.de:3478 +stun.healthtap.com:3478 +stun.allflac.com:3478 +stun.vavadating.com:3478 +stun.godatenow.com:3478 +stun.mixvoip.com:3478 +stun.sip.us:3478 +stun.sipthor.net:3478 +stun.stochastix.de:3478 +stun.kaseya.com:3478 +stun.files.fm:3478 +stun.meetwife.com:3478 +stun.myspeciality.com:3478 +stun.3wayint.com:3478 +stun.voip.blackberry.com:3478 +stun.axialys.net:3478 +stun.bridesbay.com:3478 +stun.threema.ch:3478 +stun.siptrunk.com:3478 +stun.ncic.com:3478 +stun.1cbit.ru:3478 +stun.ttmath.org:3478 +stun.yesdates.com:3478 +stun.sonetel.com:3478 +stun.peethultra.be:3478 +stun.pure-ip.com:3478 +stun.business-isp.nl:3478 +stun.ringostat.com:3478 +stun.imp.ch:3478 +stun.cope.es:3478 +stun.baltmannsweiler.de:3478 +stun.lovense.com:3478 +stun.frozenmountain.com:3478 +stun.linuxtrent.it:3478 +stun.thinkrosystem.com:3478 +stun.3deluxe.de:3478 +stun.skydrone.aero:3478 +stun.ru-brides.com:3478 +stun.streamnow.ch:3478 +stun.atagverwarming.nl:3478 +stun.ipfire.org:3478 +stun.fmo.de:3478 +stun.moonlight-stream.org:3478 +stun.f.haeder.net:3478 +stun.nextcloud.com:443 +stun.finsterwalder.com:3478 +stun.voipia.net:3478 +stun.zepter.ru:3478 +stun.sipnet.net:3478 +stun.hot-chilli.net:3478 +stun.zentauron.de:3478 +stun.geesthacht.de:3478 +stun.annatel.net:3478 +stun.flashdance.cx:3478 +stun.voipgate.com:3478 +stun.genymotion.com:3478 +stun.graftlab.com:3478 +stun.fitauto.ru:3478 +stun.telnyx.com:3478 +stun.verbo.be:3478 +stun.dcalling.de:3478 +stun.lleida.net:3478 +stun.romancecompass.com:3478 +stun.siplogin.de:3478 +stun.bethesda.net:3478 +stun.alpirsbacher.de:3478 +stun.uabrides.com:3478 +stun.technosens.fr:3478 +stun.radiojar.com:3478 +` diff --git a/pkg/nat/stun_test.go b/pkg/nat/stun_test.go new file mode 100644 index 0000000..9d30027 --- /dev/null +++ b/pkg/nat/stun_test.go @@ -0,0 +1,14 @@ +package nat + +import "testing" + +func TestStun1(t *testing.T) { + // s,err := getNatIpAndPort() + // if err != nil { + // t.Logf("get nat ip and port error: %v", err) + // t.Fail() + // } + + //NatTraversal() + t.Logf("nat ip: ") +} diff --git a/pkg/nat/traversal.go b/pkg/nat/traversal.go new file mode 100644 index 0000000..b69a4f0 --- /dev/null +++ b/pkg/nat/traversal.go @@ -0,0 +1,197 @@ +package nat + +import ( + "fmt" + "log" + "net" + "time" + + "github.com/pion/stun/v3" +) + +const ( + udp = "udp4" + pingMsg = "ping" + pongMsg = "pong" + timeoutMillis = 500 +) + +type natTraversal struct { + peerAddr *net.UDPAddr + cancel chan struct{} + port <-chan int +} + +func (s *natTraversal) Port() int { + return <-s.port +} + +func (s *natTraversal) Cancel() { + s.cancel <- struct{}{} +} + +func NatTraversal(targetAddr string) (*natTraversal, error) { //nolint:gocognit,cyclop + + srvAddr, err := net.ResolveUDPAddr(udp, getStunServers()[0]) + if err != nil { + log.Fatalf("Failed to resolve server addr: %s", err) + } + + conn, err := net.ListenUDP(udp, nil) + if err != nil { + return nil, fmt.Errorf("listen: %w", err) + } + + log.Printf("Listening on %s", conn.LocalAddr()) + + peerAddr, err := net.ResolveUDPAddr(udp, targetAddr) + if err != nil { + return nil, fmt.Errorf("resolve peeraddr: %w", err) + } + err = sendBindingRequest(conn, srvAddr) + if err != nil { + return nil, fmt.Errorf("send binding request: %w", err) + } + nt := &natTraversal{ + peerAddr: peerAddr, + cancel: make(chan struct{}), + port: make(chan int), + } + go func() { + err := doTraversal(conn, peerAddr, nt.cancel) + if err != nil { + log.Println("nat traversal error:", err) + } + }() + return nt, nil + +} + +func doTraversal(conn *net.UDPConn, peerAddr *net.UDPAddr, quit <-chan struct{}) error { + defer func() { + _ = conn.Close() + }() + + var publicAddr stun.XORMappedAddress + + messageChan := listen(conn) + //var peerAddrChan <-chan string + + keepalive := time.Tick(timeoutMillis * time.Millisecond) + keepaliveMsg := pingMsg + + gotPong := false + sentPong := false + + for { + select { + case message, ok := <-messageChan: + if !ok { + return nil + } + + switch { + case string(message) == pingMsg: + keepaliveMsg = pongMsg + + case string(message) == pongMsg: + if !gotPong { + log.Println("Received pong message.") + } + + // One client may skip sending ping if it receives + // a ping message before knowning the peer address. + keepaliveMsg = pongMsg + + gotPong = true + + case stun.IsMessage(message): + m := new(stun.Message) + m.Raw = message + decErr := m.Decode() + if decErr != nil { + log.Println("decode:", decErr) + + break + } + var xorAddr stun.XORMappedAddress + if getErr := xorAddr.GetFrom(m); getErr != nil { + log.Println("getFrom:", getErr) + + break + } + + if publicAddr.String() != xorAddr.String() { + log.Printf("My public address: %s\n", xorAddr) + publicAddr = xorAddr + + //peerAddrChan = getPeerAddr() + } + + default: + send(message, conn, peerAddr) + } + + case <-keepalive: + // Keep NAT binding alive using STUN server or the peer once it's known + err := sendStr(keepaliveMsg, conn, peerAddr) + if keepaliveMsg == pongMsg { + sentPong = true + } + _ = sentPong + + if err != nil { + log.Panicln("keepalive:", err) + } + + case <-quit: + _ = conn.Close() + } + } + +} + +func listen(conn *net.UDPConn) <-chan []byte { + messages := make(chan []byte) + go func() { + for { + buf := make([]byte, 10240) + + n, _, err := conn.ReadFromUDP(buf) + if err != nil { + close(messages) + + return + } + buf = buf[:n] + + messages <- buf + } + }() + + return messages +} + +func sendBindingRequest(conn *net.UDPConn, addr *net.UDPAddr) error { + m := stun.MustBuild(stun.TransactionID, stun.BindingRequest) + + err := send(m.Raw, conn, addr) + if err != nil { + return fmt.Errorf("binding: %w", err) + } + + return nil +} + +func send(msg []byte, conn *net.UDPConn, addr *net.UDPAddr) error { + _, err := conn.WriteToUDP(msg, addr) + if err != nil { + return fmt.Errorf("send: %w", err) + } + + return nil +} + +func sendStr(msg string, conn *net.UDPConn, addr *net.UDPAddr) error { + return send([]byte(msg), conn, addr) +} diff --git a/pkg/qbittorrent/qbittorrent.go b/pkg/qbittorrent/qbittorrent.go index 1ace58f..1023153 100644 --- a/pkg/qbittorrent/qbittorrent.go +++ b/pkg/qbittorrent/qbittorrent.go @@ -61,6 +61,23 @@ func (c *Client) GetAll() ([]pkg.Torrent, error) { return res, nil } +func (c *Client) GetListenPort() (int, error) { + pref, err := c.c.Preferences() + if err != nil { + return 0, errors.Wrap(err, "get preferences") + } + + return pref.ListenPort, nil +} + +func (c *Client) SetListenPort(port int) error { + ok, err := c.c.SetPreferences(map[string]any{"listen_port": port}) + if !ok || err != nil { + return errors.Wrap(err, "set preferences") + } + return nil +} + func (c *Client) Download(link, hash, dir string) (pkg.Torrent, error) { err := c.c.DownloadLinks([]string{link}, qbt.DownloadOptions{Savepath: &dir, Category: &c.category}) if err != nil { diff --git a/pkg/qbittorrent/qbittorrent_test.go b/pkg/qbittorrent/qbittorrent_test.go index 2cce6d5..17df32d 100644 --- a/pkg/qbittorrent/qbittorrent_test.go +++ b/pkg/qbittorrent/qbittorrent_test.go @@ -11,10 +11,17 @@ func Test1(t *testing.T) { log.Errorf("new client error: %v", err) t.Fail() } - all, err := c.GetAll() - for _, t := range all { - name, _ := t.Name() - log.Infof("torrent: %+v", name) + log.Infof("new client success: %v", c) + port, err := c.GetListenPort() + if err != nil { + log.Errorf("get listen port error: %v", err) + t.Fail() + } else { + log.Infof("listen port: %d", port) + err := c.SetListenPort(port + 1) + if err!= nil { + log.Errorf("set listen port error: %v", err) + t.Fail() + } } - }