Feature: Support multi user per irc network (#78)

* feat: delete irc network in transaction

* feat: use compound key for irc handlers

* chore: update deps

* refactor: start network

* refactor: init irc handler

* indexers: update network name
This commit is contained in:
Ludvig Lundgren 2022-01-13 21:26:23 +01:00 committed by GitHub
parent f466657ed4
commit 2daeedbdc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 491 additions and 428 deletions

6
go.mod
View file

@ -15,7 +15,6 @@ require (
github.com/gorilla/sessions v1.2.1 github.com/gorilla/sessions v1.2.1
github.com/kr/pretty v0.3.0 // indirect github.com/kr/pretty v0.3.0 // indirect
github.com/lib/pq v1.10.4 github.com/lib/pq v1.10.4
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.10 github.com/mattn/go-sqlite3 v1.14.10
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/r3labs/sse/v2 v2.7.2 github.com/r3labs/sse/v2 v2.7.2
@ -29,13 +28,10 @@ require (
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
golang.org/x/tools v0.1.8 // indirect
gopkg.in/ini.v1 v1.64.0 // indirect gopkg.in/ini.v1 v1.64.0 // indirect
gopkg.in/irc.v3 v3.1.4 gopkg.in/irc.v3 v3.1.4
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
modernc.org/ccgo/v3 v3.14.0 // indirect
modernc.org/libc v1.13.2 // indirect
modernc.org/sqlite v1.14.3
) )

151
go.sum
View file

@ -257,7 +257,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@ -280,8 +279,6 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@ -342,8 +339,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -377,12 +372,9 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.7.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -441,8 +433,6 @@ github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4
github.com/r3labs/sse/v2 v2.7.2 h1:GUyMTu1EPAVUDPZUSkFWx1fXYXXxH4xAcTAIWSs89ZU= github.com/r3labs/sse/v2 v2.7.2 h1:GUyMTu1EPAVUDPZUSkFWx1fXYXXxH4xAcTAIWSs89ZU=
github.com/r3labs/sse/v2 v2.7.2/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8= github.com/r3labs/sse/v2 v2.7.2/go.mod h1:hUrYMKfu9WquG9MyI0r6TKiNH+6Sw/QPKm2YbNbU5g8=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@ -504,7 +494,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
@ -569,8 +558,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -617,7 +604,6 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -696,7 +682,6 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -716,9 +701,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -785,7 +767,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -797,12 +778,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@ -970,135 +948,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.22 h1:BzShpwCAP7TWzFppM4k2t03RhXhgYqaibROWkrWq7lE=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.88/go.mod h1:0MFzUHIuSIthpVZyMWiFYMwjiFnhrN5MkvBrUwON+ZM=
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.12.95/go.mod h1:ZcLyvtocXYi8uF+9Ebm3G8EF8HNY5hGomBqthDp4eC8=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.14.0 h1:Zr1Ny9+7r5yAiXpBdgp8XiXqkNA4ARrRphHGHVXeAp0=
modernc.org/ccgo/v3 v3.14.0/go.mod h1:hBrkiBlUwvr5vV/ZH9YzXIp982jKE8Ek8tR1ytoAL6Q=
modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.90/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.99/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.11.104/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.13.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.13.2 h1:GCFjY9bmwDZ/TJC4OZOUWaNgxIxwb104C/QZrqpcVEA=
modernc.org/libc v1.13.2/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.14.3 h1:psrTwgpEujgWEP3FNdsC9yNh5tSeA77U0GeWhHH4XmQ=
modernc.org/sqlite v1.14.3/go.mod h1:xMpicS1i2MJ4C8+Ap0vYBqTwYfpFvdnPE6brbFOtV2Y=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.9.2 h1:YA87dFLOsR2KqMka371a2Xgr+YsyUwo7OmHVSv/kztw=
modernc.org/tcl v1.9.2/go.mod h1:aw7OnlIoiuJgu1gwbTZtrKnGpDqH9wyH++jZcxdqNsg=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.2.20 h1:DyboxM1sJR2NB803j2StnbnL6jcQXz273OhHDGu8dGk=
modernc.org/z v1.2.20/go.mod h1:zU9FiF4PbHdOTUxw+IF8j7ArBMRPsHgq10uVPt6xTzo=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View file

@ -49,25 +49,25 @@ func (r *IrcRepo) GetNetworkByID(id int64) (*domain.IrcNetwork, error) {
} }
func (r *IrcRepo) DeleteNetwork(ctx context.Context, id int64) error { func (r *IrcRepo) DeleteNetwork(ctx context.Context, id int64) error {
tx, err := r.db.handler.BeginTx(ctx, nil) tx, err := r.db.BeginTx(ctx, nil)
if err != nil { if err != nil {
return err return err
} }
defer tx.Rollback() defer tx.Rollback()
_, err = tx.ExecContext(ctx, `DELETE FROM irc_network WHERE id = ?`, id)
if err != nil {
log.Error().Stack().Err(err).Msgf("error deleting network: %v", id)
return err
}
_, err = tx.ExecContext(ctx, `DELETE FROM irc_channel WHERE network_id = ?`, id) _, err = tx.ExecContext(ctx, `DELETE FROM irc_channel WHERE network_id = ?`, id)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error deleting channels for network: %v", id) log.Error().Stack().Err(err).Msgf("error deleting channels for network: %v", id)
return err return err
} }
_, err = tx.ExecContext(ctx, `DELETE FROM irc_network WHERE id = ?`, id)
if err != nil {
log.Error().Stack().Err(err).Msgf("error deleting network: %v", id)
return err
}
err = tx.Commit() err = tx.Commit()
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error deleting network: %v", id) log.Error().Stack().Err(err).Msgf("error deleting network: %v", id)

View file

@ -79,3 +79,20 @@ func (db *SqliteDB) Close() error {
} }
return nil return nil
} }
func (db *SqliteDB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
tx, err := db.handler.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
return &Tx{
Tx: tx,
handler: db,
}, nil
}
type Tx struct {
*sql.Tx
handler *SqliteDB
}

View file

@ -19,7 +19,6 @@ type ircService interface {
StoreNetwork(ctx context.Context, network *domain.IrcNetwork) error StoreNetwork(ctx context.Context, network *domain.IrcNetwork) error
UpdateNetwork(ctx context.Context, network *domain.IrcNetwork) error UpdateNetwork(ctx context.Context, network *domain.IrcNetwork) error
StoreChannel(networkID int64, channel *domain.IrcChannel) error StoreChannel(networkID int64, channel *domain.IrcChannel) error
StopNetwork(name string) error
} }
type ircHandler struct { type ircHandler struct {
@ -39,7 +38,6 @@ func (h ircHandler) Routes(r chi.Router) {
r.Post("/", h.storeNetwork) r.Post("/", h.storeNetwork)
r.Put("/network/{networkID}", h.updateNetwork) r.Put("/network/{networkID}", h.updateNetwork)
r.Post("/network/{networkID}/channel", h.storeChannel) r.Post("/network/{networkID}/channel", h.storeChannel)
r.Get("/network/{networkID}/stop", h.stopNetwork)
r.Get("/network/{networkID}", h.getNetworkByID) r.Get("/network/{networkID}", h.getNetworkByID)
r.Delete("/network/{networkID}", h.deleteNetwork) r.Delete("/network/{networkID}", h.deleteNetwork)
} }
@ -132,20 +130,6 @@ func (h ircHandler) storeChannel(w http.ResponseWriter, r *http.Request) {
h.encoder.StatusResponse(ctx, w, nil, http.StatusCreated) h.encoder.StatusResponse(ctx, w, nil, http.StatusCreated)
} }
func (h ircHandler) stopNetwork(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
networkID = chi.URLParam(r, "networkID")
)
err := h.service.StopNetwork(networkID)
if err != nil {
//
}
h.encoder.StatusResponse(ctx, w, nil, http.StatusCreated)
}
func (h ircHandler) deleteNetwork(w http.ResponseWriter, r *http.Request) { func (h ircHandler) deleteNetwork(w http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()

View file

@ -20,7 +20,7 @@ settings:
help: "Check how to get cookies in your browser and find the uid and pass cookies. Example: uid=1234; pass=asdf12347asdf13" help: "Check how to get cookies in your browser and find the uid and pass cookies. Example: uid=1234; pass=asdf12347asdf13"
irc: irc:
network: P2P-NET network: P2P-Network
server: irc.p2p-network.net server: irc.p2p-network.net
port: 6697 port: 6697
tls: true tls: true

View file

@ -20,7 +20,7 @@ settings:
regex: /([a-zA-Z0-9\+]) regex: /([a-zA-Z0-9\+])
irc: irc:
network: p2p-network.net network: P2P-Network
server: irc.p2p-network.net server: irc.p2p-network.net
port: 6697 port: 6697
tls: true tls: true

View file

@ -0,0 +1,63 @@
---
#id: tracker01
name: Mock1
identifier: mock1
description: Mock1 is just a mock indexer
language: en-us
urls:
- http://localhost.test
privacy: private
protocol: torrent
supports:
- irc
- rss
source: custom
settings:
- name: rsskey
type: secret
label: RSS key
help: "Go to your profile and copy your RSS key"
regex: /([\da-fA-F]{20})
irc:
network: LocalHost
server: localhost
port: 6697
tls: true
channels:
- "#announces"
announcers:
- _AnnounceBot_
settings:
- name: nickserv.account
type: text
required: true
label: NickServ Account
help: NickServ account. Make sure to group your user and bot. Eg. user_bot
- name: nickserv.password
type: secret
required: false
label: NickServ Password
help: NickServ password
parse:
type: single
lines:
- test:
- "New Torrent Announcement: <PC :: Iso> Name:'debian live 10 6 0 amd64 standard iso' uploaded by 'Anonymous' - http://www.tracker01.test/torrent/000000"
- "New Torrent Announcement: <PC :: Iso> Name:'debian live 10 6 0 amd64 standard iso' uploaded by 'Anonymous' freeleech - http://www.tracker01.test/torrent/000000"
pattern: New Torrent Announcement:\s*<([^>]*)>\s*Name:'(.*)' uploaded by '([^']*)'\s*(freeleech)*\s*-\s*(https?\:\/\/[^\/]+\/)torrent\/(\d+)
vars:
- category
- torrentName
- uploader
- freeleech
- baseUrl
- torrentId
match:
torrenturl: "{{ .baseUrl }}rss/download/{{ .torrentId }}/{{ .rsskey }}/{{ .torrentName }}.torrent"
encode:
- torrentName

View file

@ -0,0 +1,63 @@
---
#id: tracker01
name: Mock2
identifier: mock2
description: Mock2 is just a mock indexer
language: en-us
urls:
- http://localhost.test
privacy: private
protocol: torrent
supports:
- irc
- rss
source: custom
settings:
- name: rsskey
type: secret
label: RSS key
help: "Go to your profile and copy your RSS key"
regex: /([\da-fA-F]{20})
irc:
network: LocalHost
server: localhost
port: 6697
tls: true
channels:
- "#mannounces"
announcers:
- Jerry
settings:
- name: nickserv.account
type: text
required: true
label: NickServ Account
help: NickServ account. Make sure to group your user and bot. Eg. user_bot
- name: nickserv.password
type: secret
required: false
label: NickServ Password
help: NickServ password
parse:
type: single
lines:
- test:
- "New Torrent Announcement: <PC :: Iso> Name:'debian live 10 6 0 amd64 standard iso' uploaded by 'Anonymous' - http://www.tracker01.test/torrent/000000"
- "New Torrent Announcement: <PC :: Iso> Name:'debian live 10 6 0 amd64 standard iso' uploaded by 'Anonymous' freeleech - http://www.tracker01.test/torrent/000000"
pattern: New Torrent Announcement:\s*<([^>]*)>\s*Name:'(.*)' uploaded by '([^']*)'\s*(freeleech)*\s*-\s*(https?\:\/\/[^\/]+\/)torrent\/(\d+)
vars:
- category
- torrentName
- uploader
- freeleech
- baseUrl
- torrentId
match:
torrenturl: "{{ .baseUrl }}rss/download/{{ .torrentId }}/{{ .rsskey }}/{{ .torrentName }}.torrent"
encode:
- torrentName

View file

@ -19,7 +19,7 @@ settings:
help: "Go to https://www.trancetraffic.com/links.php, click on RSS Feed and copy your passkey from the url." help: "Go to https://www.trancetraffic.com/links.php, click on RSS Feed and copy your passkey from the url."
irc: irc:
network: P2P-NET network: P2P-Network
server: irc.p2p-network.net server: irc.p2p-network.net
port: 6697 port: 6697
tls: true tls: true
@ -35,7 +35,7 @@ irc:
help: NickServ account. Make sure to group your user and bot. Eg. user|autodl help: NickServ account. Make sure to group your user and bot. Eg. user|autodl
- name: nickserv.password - name: nickserv.password
type: secret type: secret
required: false required: true
label: NickServ Password label: NickServ Password
help: NickServ password help: NickServ password

View file

@ -243,6 +243,16 @@ func (s *service) addIndexer(indexer domain.Indexer) error {
s.mapIRCIndexerLookup(indexer.Identifier, *indexerDefinition) s.mapIRCIndexerLookup(indexer.Identifier, *indexerDefinition)
// add to irc server lookup table
s.mapIRCServerDefinitionLookup(indexerDefinition.IRC.Server, *indexerDefinition)
// check if it has api and add to api service
if indexerDefinition.Enabled && indexerDefinition.HasApi() {
if err := s.apiService.AddClient(indexerDefinition.Identifier, indexerDefinition.SettingsMap); err != nil {
log.Error().Stack().Err(err).Msgf("indexer.start: could not init api client for: '%v'", indexer.Identifier)
}
}
return nil return nil
} }

View file

@ -52,7 +52,7 @@ type Handler struct {
filterService filter.Service filterService filter.Service
releaseService release.Service releaseService release.Service
announceProcessors map[string]announce.Processor announceProcessors map[string]announce.Processor
definitions []domain.IndexerDefinition definitions map[string]*domain.IndexerDefinition
client *irc.Client client *irc.Client
conn net.Conn conn net.Conn
@ -79,22 +79,35 @@ func NewHandler(network domain.IrcNetwork, filterService filter.Service, release
network: &network, network: &network,
filterService: filterService, filterService: filterService,
releaseService: releaseService, releaseService: releaseService,
definitions: definitions, definitions: map[string]*domain.IndexerDefinition{},
announceProcessors: map[string]announce.Processor{}, announceProcessors: map[string]announce.Processor{},
validAnnouncers: map[string]struct{}{}, validAnnouncers: map[string]struct{}{},
validChannels: map[string]struct{}{}, validChannels: map[string]struct{}{},
channelHealth: map[string]*channelHealth{}, channelHealth: map[string]*channelHealth{},
} }
// init indexer, announceProcessor
h.InitIndexers(definitions)
return h
}
func (h *Handler) InitIndexers(definitions []domain.IndexerDefinition) {
// Networks can be shared by multiple indexers but channels are unique // Networks can be shared by multiple indexers but channels are unique
// so let's add a new AnnounceProcessor per channel // so let's add a new AnnounceProcessor per channel
for _, definition := range definitions { for _, definition := range definitions {
// indexers can use multiple channels, but it's not common, but let's handle that anyway. if _, ok := h.definitions[definition.Identifier]; ok {
continue
}
h.definitions[definition.Identifier] = &definition
// indexers can use multiple channels, but it'h not common, but let'h handle that anyway.
for _, channel := range definition.IRC.Channels { for _, channel := range definition.IRC.Channels {
// some channels are defined in mixed case // some channels are defined in mixed case
channel = strings.ToLower(channel) channel = strings.ToLower(channel)
h.announceProcessors[channel] = announce.NewAnnounceProcessor(definition, filterService, releaseService) h.announceProcessors[channel] = announce.NewAnnounceProcessor(definition, h.filterService, h.releaseService)
h.channelHealth[channel] = &channelHealth{ h.channelHealth[channel] = &channelHealth{
name: channel, name: channel,
@ -110,20 +123,25 @@ func NewHandler(network domain.IrcNetwork, filterService filter.Service, release
h.validAnnouncers[announcer] = struct{}{} h.validAnnouncers[announcer] = struct{}{}
} }
} }
return h
} }
func (s *Handler) Run() error { func (h *Handler) removeIndexer() {
//log.Debug().Msgf("server %+v", s.network) // TODO remove validAnnouncers
// TODO remove validChannels
// TODO remove definition
// TODO remove announceProcessor
}
if s.network.Server == "" { func (h *Handler) Run() error {
//log.Debug().Msgf("server %+v", h.network)
if h.network.Server == "" {
return errors.New("addr not set") return errors.New("addr not set")
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
s.ctx = ctx h.ctx = ctx
s.cancel = cancel h.cancel = cancel
dialer := net.Dialer{ dialer := net.Dialer{
Timeout: connectTimeout, Timeout: connectTimeout,
@ -132,44 +150,69 @@ func (s *Handler) Run() error {
var netConn net.Conn var netConn net.Conn
var err error var err error
addr := fmt.Sprintf("%v:%v", s.network.Server, s.network.Port) addr := fmt.Sprintf("%v:%v", h.network.Server, h.network.Port)
// decide to use SSL or not // decide to use SSL or not
if s.network.TLS { if h.network.TLS {
tlsConf := &tls.Config{ tlsConf := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
netConn, err = dialer.DialContext(s.ctx, "tcp", addr) netConn, err = dialer.DialContext(h.ctx, "tcp", addr)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("failed to dial %v", addr) log.Error().Err(err).Msgf("failed to dial %v", addr)
return fmt.Errorf("failed to dial %q: %v", addr, err) return fmt.Errorf("failed to dial %q: %v", addr, err)
} }
netConn = tls.Client(netConn, tlsConf) netConn = tls.Client(netConn, tlsConf)
s.conn = netConn h.conn = netConn
} else { } else {
netConn, err = dialer.DialContext(s.ctx, "tcp", addr) netConn, err = dialer.DialContext(h.ctx, "tcp", addr)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("failed to dial %v", addr) log.Error().Err(err).Msgf("failed to dial %v", addr)
return fmt.Errorf("failed to dial %q: %v", addr, err) return fmt.Errorf("failed to dial %q: %v", addr, err)
} }
s.conn = netConn h.conn = netConn
} }
log.Info().Msgf("Connected to: %v", addr) log.Info().Msgf("Connected to: %v", addr)
config := irc.ClientConfig{ config := irc.ClientConfig{
Nick: s.network.NickServ.Account, Nick: h.network.NickServ.Account,
User: s.network.NickServ.Account, User: h.network.NickServ.Account,
Name: s.network.NickServ.Account, Name: h.network.NickServ.Account,
Pass: s.network.Pass, Pass: h.network.Pass,
Handler: irc.HandlerFunc(func(c *irc.Client, m *irc.Message) { Handler: irc.HandlerFunc(h.handleMessage),
}
// Create the client
client := irc.NewClient(h.conn, config)
h.client = client
// set connected since now
h.setConnectionStatus()
// Connect
err = client.RunContext(ctx)
if err != nil {
log.Error().Err(err).Msgf("could not connect to %v", addr)
// reset connection status on handler and channels
h.resetConnectionStatus()
return err
}
return nil
}
func (h *Handler) handleMessage(c *irc.Client, m *irc.Message) {
switch m.Command { switch m.Command {
case "001": case "001":
// 001 is a welcome event, so we join channels there // 001 is a welcome event, so we join channels there
err := s.onConnect(s.network.Channels) err := h.onConnect(h.network.Channels)
if err != nil { if err != nil {
log.Error().Msgf("error joining channels %v", err) log.Error().Msgf("error joining channels %v", err)
} }
@ -182,160 +225,155 @@ func (s *Handler) Run() error {
// 353 @ // 353 @
// 396 Displayed host // 396 Displayed host
case "366": // JOINED case "366": // JOINED
s.handleJoined(m) h.handleJoined(m)
case "JOIN": case "JOIN":
log.Trace().Msgf("%v: JOIN %v", s.network.Server, m) if h.isOurNick(m.Prefix.Name) {
log.Trace().Msgf("%v: JOIN %v", h.network.Server, m)
}
case "QUIT":
if h.isOurNick(m.Prefix.Name) {
log.Trace().Msgf("%v: QUIT %v", h.network.Server, m)
}
case "433": case "433":
// TODO: handle nick in use // TODO: handle nick in use
log.Debug().Msgf("%v: NICK IN USE: %v", s.network.Server, m) log.Debug().Msgf("%v: NICK IN USE: %v", h.network.Server, m)
case "448", "473", "475", "477": case "448", "473", "475", "477":
// TODO: handle join failed // TODO: handle join failed
log.Debug().Msgf("%v: JOIN FAILED %v: %v", s.network.Server, m.Params[1], m) log.Debug().Msgf("%v: JOIN FAILED %v: %v", h.network.Server, m.Params[1], m)
case "900": // Invite bot logged in case "900": // Invite bot logged in
log.Debug().Msgf("%v: %v", s.network.Server, m.Trailing()) log.Debug().Msgf("%v: %v", h.network.Server, m.Trailing())
case "KICK": case "KICK":
log.Debug().Msgf("%v: KICK: %v", s.network.Server, m) log.Debug().Msgf("%v: KICK: %v", h.network.Server, m)
case "MODE": case "MODE":
err := s.handleMode(m) err := h.handleMode(m)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error MODE change: %v", m) log.Error().Err(err).Msgf("error MODE change: %v", m)
} }
case "INVITE": case "INVITE":
// TODO: handle invite // TODO: handle invite
log.Debug().Msgf("%v: INVITE: %v", s.network.Server, m) log.Debug().Msgf("%v: INVITE: %v", h.network.Server, m)
case "PART": case "PART":
// TODO: handle parted // TODO: handle parted
log.Debug().Msgf("%v: PART: %v", s.network.Server, m) if h.isOurNick(m.Prefix.Name) {
log.Debug().Msgf("%v: PART: %v", h.network.Server, m)
}
case "PRIVMSG": case "PRIVMSG":
err := s.onMessage(m) err := h.onMessage(m)
if err != nil { if err != nil {
log.Error().Msgf("error on message %v", err) log.Error().Msgf("error on message %v", err)
} }
case "CAP": case "CAP":
log.Debug().Msgf("%v: CAP: %v", s.network.Server, m) log.Debug().Msgf("%v: CAP: %v", h.network.Server, m)
case "NOTICE": case "NOTICE":
log.Trace().Msgf("%v: %v", s.network.Server, m) log.Trace().Msgf("%v: %v", h.network.Server, m)
case "PING": case "PING":
err := s.handlePing(m) err := h.handlePing(m)
if err != nil { if err != nil {
log.Error().Stack().Err(err) log.Error().Stack().Err(err)
} }
//case "372": //case "372":
// log.Debug().Msgf("372: %v", m) // log.Debug().Msgf("372: %v", m)
default: //default:
log.Trace().Msgf("%v: %v", s.network.Server, m) // log.Trace().Msgf("%v: %v", h.network.Server, m)
} }
}), return
} }
// Create the client func (h *Handler) isOurNick(nick string) bool {
client := irc.NewClient(s.conn, config) return h.network.NickServ.Account == nick
}
s.client = client
func (h *Handler) setConnectionStatus() {
// set connected since now // set connected since now
s.setConnectionStatus() h.connectedSince = time.Now()
h.connected = true
// Connect
err = client.RunContext(ctx)
if err != nil {
log.Error().Err(err).Msgf("could not connect to %v", addr)
// reset connection status on handler and channels
s.resetConnectionStatus()
return err
} }
return nil func (h *Handler) resetConnectionStatus() {
}
func (s *Handler) setConnectionStatus() {
// set connected since now
s.connectedSince = time.Now()
s.connected = true
}
func (s *Handler) resetConnectionStatus() {
// set connected false if we loose connection or stop // set connected false if we loose connection or stop
s.connectedSince = time.Time{} h.connectedSince = time.Time{}
s.connected = false h.connected = false
// loop over channelHealth and reset each one // loop over channelHealth and reset each one
for _, h := range s.channelHealth { for _, h := range h.channelHealth {
if h != nil { if h != nil {
h.resetMonitoring() h.resetMonitoring()
} }
} }
} }
func (s *Handler) GetNetwork() *domain.IrcNetwork { func (h *Handler) GetNetwork() *domain.IrcNetwork {
return s.network return h.network
} }
func (s *Handler) UpdateNetwork(network *domain.IrcNetwork) { func (h *Handler) UpdateNetwork(network *domain.IrcNetwork) {
s.network = network h.network = network
} }
func (s *Handler) Stop() { func (h *Handler) SetNetwork(network *domain.IrcNetwork) {
s.cancel() h.network = network
if !s.isStopped() {
close(s.stopped)
} }
if s.conn != nil { func (h *Handler) Stop() {
s.conn.Close() h.cancel()
if !h.isStopped() {
close(h.stopped)
}
if h.conn != nil {
h.conn.Close()
} }
} }
func (s *Handler) isStopped() bool { func (h *Handler) isStopped() bool {
select { select {
case <-s.stopped: case <-h.stopped:
return true return true
default: default:
return false return false
} }
} }
func (s *Handler) Restart() error { func (h *Handler) Restart() error {
s.cancel() h.cancel()
if !s.isStopped() { if !h.isStopped() {
close(s.stopped) close(h.stopped)
} }
if s.conn != nil { if h.conn != nil {
s.conn.Close() h.conn.Close()
} }
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
return s.Run() return h.Run()
} }
func (s *Handler) onConnect(channels []domain.IrcChannel) error { func (h *Handler) onConnect(channels []domain.IrcChannel) error {
identified := false identified := false
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
if s.network.NickServ.Password != "" { if h.network.NickServ.Password != "" {
err := s.handleNickServPRIVMSG(s.network.NickServ.Account, s.network.NickServ.Password) err := h.handleNickServPRIVMSG(h.network.NickServ.Account, h.network.NickServ.Password)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error nickserv: %v", s.network.Name) log.Error().Stack().Err(err).Msgf("error nickserv: %v", h.network.Name)
return err return err
} }
identified = true identified = true
@ -343,11 +381,10 @@ func (s *Handler) onConnect(channels []domain.IrcChannel) error {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
if s.network.InviteCommand != "" { if h.network.InviteCommand != "" {
err := h.handleConnectCommands(h.network.InviteCommand)
err := s.handleInvitePRIVMSG(s.network.InviteCommand)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error sending connect command %v to network: %v", s.network.InviteCommand, s.network.Name) log.Error().Stack().Err(err).Msgf("error sending connect command %v to network: %v", h.network.InviteCommand, h.network.Name)
return err return err
} }
@ -356,7 +393,7 @@ func (s *Handler) onConnect(channels []domain.IrcChannel) error {
if !identified { if !identified {
for _, channel := range channels { for _, channel := range channels {
err := s.HandleJoinChannel(channel.Name, channel.Password) err := h.HandleJoinChannel(channel.Name, channel.Password)
if err != nil { if err != nil {
log.Error().Stack().Err(err) log.Error().Stack().Err(err)
return err return err
@ -367,34 +404,33 @@ func (s *Handler) onConnect(channels []domain.IrcChannel) error {
return nil return nil
} }
func (s *Handler) OnJoin(msg string) (interface{}, error) { func (h *Handler) OnJoin(msg string) (interface{}, error) {
return nil, nil return nil, nil
} }
func (s *Handler) onMessage(msg *irc.Message) error { func (h *Handler) onMessage(msg *irc.Message) error {
// parse announce // parse announce
channel := &msg.Params[0] channel := &msg.Params[0]
announcer := &msg.Name announcer := &msg.Name
message := msg.Trailing() message := msg.Trailing()
// TODO add network
// check if message is from a valid channel, if not return // check if message is from a valid channel, if not return
validChannel := s.isValidChannel(*channel) validChannel := h.isValidChannel(*channel)
if !validChannel { if !validChannel {
return nil return nil
} }
// check if message is from announce bot, if not return // check if message is from announce bot, if not return
validAnnouncer := s.isValidAnnouncer(*announcer) validAnnouncer := h.isValidAnnouncer(*announcer)
if !validAnnouncer { if !validAnnouncer {
return nil return nil
} }
// clean message // clean message
cleanedMsg := cleanMessage(message) cleanedMsg := cleanMessage(message)
log.Debug().Msgf("%v: %v %v: %v", s.network.Server, *channel, *announcer, cleanedMsg) log.Debug().Msgf("%v: %v %v: %v", h.network.Server, *channel, *announcer, cleanedMsg)
if err := s.sendToAnnounceProcessor(*channel, cleanedMsg); err != nil { if err := h.sendToAnnounceProcessor(*channel, cleanedMsg); err != nil {
log.Error().Stack().Err(err).Msgf("could not queue line: %v", cleanedMsg) log.Error().Stack().Err(err).Msgf("could not queue line: %v", cleanedMsg)
return err return err
} }
@ -402,11 +438,11 @@ func (s *Handler) onMessage(msg *irc.Message) error {
return nil return nil
} }
func (s *Handler) sendToAnnounceProcessor(channel string, msg string) error { func (h *Handler) sendToAnnounceProcessor(channel string, msg string) error {
channel = strings.ToLower(channel) channel = strings.ToLower(channel)
// check if queue exists // check if queue exists
queue, ok := s.announceProcessors[channel] queue, ok := h.announceProcessors[channel]
if !ok { if !ok {
return fmt.Errorf("queue '%v' not found", channel) return fmt.Errorf("queue '%v' not found", channel)
} }
@ -418,7 +454,7 @@ func (s *Handler) sendToAnnounceProcessor(channel string, msg string) error {
return err return err
} }
v, ok := s.channelHealth[channel] v, ok := h.channelHealth[channel]
if !ok { if !ok {
return nil return nil
} }
@ -428,11 +464,11 @@ func (s *Handler) sendToAnnounceProcessor(channel string, msg string) error {
return nil return nil
} }
func (s *Handler) sendPrivMessage(msg string) error { func (h *Handler) sendPrivMessage(msg string) error {
msg = strings.TrimLeft(msg, "/") msg = strings.TrimLeft(msg, "/")
privMsg := fmt.Sprintf("PRIVMSG %s", msg) privMsg := fmt.Sprintf("PRIVMSG %h", msg)
err := s.client.Write(privMsg) err := h.client.Write(privMsg)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not send priv msg: %v", msg) log.Error().Err(err).Msgf("could not send priv msg: %v", msg)
return err return err
@ -441,7 +477,7 @@ func (s *Handler) sendPrivMessage(msg string) error {
return nil return nil
} }
func (s *Handler) HandleJoinChannel(channel string, password string) error { func (h *Handler) HandleJoinChannel(channel string, password string) error {
// support channel password // support channel password
params := []string{channel} params := []string{channel}
if password != "" { if password != "" {
@ -453,82 +489,105 @@ func (s *Handler) HandleJoinChannel(channel string, password string) error {
Params: params, Params: params,
} }
log.Trace().Msgf("%v: sending %v", s.network.Server, m.String()) log.Trace().Msgf("%v: sending %v", h.network.Server, m.String())
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error handling join: %v", m.String()) log.Error().Stack().Err(err).Msgf("error handling join: %v", m.String())
return err return err
} }
// only set values if channel is found in map
v, ok := s.channelHealth[channel]
if ok {
v.SetMonitoring()
}
return nil return nil
} }
func (s *Handler) HandlePartChannel(channel string) error { func (h *Handler) HandlePartChannel(channel string) error {
m := irc.Message{ m := irc.Message{
Command: "PART", Command: "PART",
Params: []string{channel}, Params: []string{channel},
} }
log.Debug().Msgf("%v: %v", s.network.Server, m.String()) log.Debug().Msgf("%v: %v", h.network.Server, m.String())
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error handling part: %v", m.String()) log.Error().Err(err).Msgf("error handling part: %v", m.String())
return err return err
} }
log.Info().Msgf("Left channel '%v' on network '%s'", channel, s.network.Server) // reset monitoring status
v, ok := h.channelHealth[channel]
if !ok {
return nil
}
v.resetMonitoring()
// TODO remove announceProcessor
log.Info().Msgf("Left channel '%v' on network '%h'", channel, h.network.Server)
return nil return nil
} }
func (s *Handler) handleJoined(msg *irc.Message) { func (h *Handler) handleJoined(msg *irc.Message) {
log.Debug().Msgf("%v: JOINED: %v", s.network.Server, msg.Trailing()) log.Debug().Msgf("%v: JOINED: %v", h.network.Server, msg.Params[1])
log.Info().Msgf("%v: Monitoring channel %s", s.network.Server, msg.Params[1]) // get channel
channel := &msg.Params[1]
// set monitoring on current channelHealth, or add new
v, ok := h.channelHealth[strings.ToLower(*channel)]
if ok {
v.SetMonitoring()
} else if v == nil {
h.channelHealth[*channel] = &channelHealth{
name: *channel,
monitoring: true,
monitoringSince: time.Now(),
}
} }
func (s *Handler) handleInvitePRIVMSG(msg string) error { log.Info().Msgf("%v: Monitoring channel %v", h.network.Server, msg.Params[1])
msg = strings.TrimPrefix(msg, "/msg") }
split := strings.Split(msg, " ")
func (h *Handler) handleConnectCommands(msg string) error {
connectCommand := strings.ReplaceAll(msg, "/msg", "")
connectCommands := strings.Split(connectCommand, ",")
for _, command := range connectCommands {
cmd := strings.TrimSpace(command)
m := irc.Message{ m := irc.Message{
Command: "PRIVMSG", Command: "PRIVMSG",
Params: split, Params: strings.Split(cmd, " "),
} }
log.Info().Msgf("%v: Invite command: %v", s.network.Server, m.String()) log.Debug().Msgf("%v: sending connect command", h.network.Server)
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error handling invite: %v", m.String()) log.Error().Err(err).Msgf("error handling invite: %v", m.String())
return err return err
} }
}
return nil return nil
} }
func (s *Handler) handlePRIVMSG(msg string) error { func (h *Handler) handlePRIVMSG(msg string) error {
msg = strings.TrimLeft(msg, "/") msg = strings.TrimLeft(msg, "/")
m := irc.Message{ m := irc.Message{
Command: "PRIVMSG", Command: "PRIVMSG",
Params: []string{msg}, Params: []string{msg},
} }
log.Debug().Msgf("%v: Handle privmsg: %v", s.network.Server, m.String()) log.Debug().Msgf("%v: Handle privmsg: %v", h.network.Server, m.String())
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error handling PRIVMSG: %v", m.String()) log.Error().Err(err).Msgf("error handling PRIVMSG: %v", m.String())
return err return err
@ -537,15 +596,15 @@ func (s *Handler) handlePRIVMSG(msg string) error {
return nil return nil
} }
func (s *Handler) handleNickServPRIVMSG(nick, password string) error { func (h *Handler) handleNickServPRIVMSG(nick, password string) error {
m := irc.Message{ m := irc.Message{
Command: "PRIVMSG", Command: "PRIVMSG",
Params: []string{"NickServ", "IDENTIFY", nick, password}, Params: []string{"NickServ", "IDENTIFY", nick, password},
} }
log.Debug().Msgf("%v: NickServ: %v", s.network.Server, m.String()) log.Debug().Msgf("%v: NickServ: %v", h.network.Server, m.String())
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error identifying with nickserv: %v", m.String()) log.Error().Err(err).Msgf("error identifying with nickserv: %v", m.String())
return err return err
@ -554,15 +613,15 @@ func (s *Handler) handleNickServPRIVMSG(nick, password string) error {
return nil return nil
} }
func (s *Handler) HandleNickServIdentify(nick, password string) error { func (h *Handler) HandleNickServIdentify(nick, password string) error {
m := irc.Message{ m := irc.Message{
Command: "PRIVMSG", Command: "PRIVMSG",
Params: []string{"NickServ", "IDENTIFY", nick, password}, Params: []string{"NickServ", "IDENTIFY", nick, password},
} }
log.Debug().Msgf("%v: NickServ: %v", s.network.Server, m.String()) log.Debug().Msgf("%v: NickServ: %v", h.network.Server, m.String())
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error identifying with nickserv: %v", m.String()) log.Error().Stack().Err(err).Msgf("error identifying with nickserv: %v", m.String())
return err return err
@ -571,15 +630,15 @@ func (s *Handler) HandleNickServIdentify(nick, password string) error {
return nil return nil
} }
func (s *Handler) HandleNickChange(nick string) error { func (h *Handler) HandleNickChange(nick string) error {
m := irc.Message{ m := irc.Message{
Command: "NICK", Command: "NICK",
Params: []string{nick}, Params: []string{nick},
} }
log.Debug().Msgf("%v: Nick change: %v", s.network.Server, m.String()) log.Debug().Msgf("%v: Nick change: %v", h.network.Server, m.String())
err := s.client.Write(m.String()) err := h.client.Write(m.String())
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("error changing nick: %v", m.String()) log.Error().Stack().Err(err).Msgf("error changing nick: %v", m.String())
return err return err
@ -588,18 +647,18 @@ func (s *Handler) HandleNickChange(nick string) error {
return nil return nil
} }
func (s *Handler) handleMode(msg *irc.Message) error { func (h *Handler) handleMode(msg *irc.Message) error {
log.Debug().Msgf("%v: MODE: %v %v", s.network.Server, msg.User, msg.Trailing()) log.Debug().Msgf("%v: MODE: %v %v", h.network.Server, msg.User, msg.Trailing())
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
if s.network.NickServ.Password != "" && !strings.Contains(msg.String(), s.client.CurrentNick()) || !strings.Contains(msg.String(), "+r") { if h.network.NickServ.Password != "" && !strings.Contains(msg.String(), h.client.CurrentNick()) || !strings.Contains(msg.String(), "+r") {
log.Trace().Msgf("%v: MODE: Not correct permission yet: %v", s.network.Server, msg.String()) log.Trace().Msgf("%v: MODE: Not correct permission yet: %v", h.network.Server, msg.String())
return nil return nil
} }
for _, ch := range s.network.Channels { for _, ch := range h.network.Channels {
err := s.HandleJoinChannel(ch.Name, ch.Password) err := h.HandleJoinChannel(ch.Name, ch.Password)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error joining channel: %v", ch.Name) log.Error().Err(err).Msgf("error joining channel: %v", ch.Name)
continue continue
@ -611,30 +670,30 @@ func (s *Handler) handleMode(msg *irc.Message) error {
return nil return nil
} }
func (s *Handler) handlePing(msg *irc.Message) error { func (h *Handler) handlePing(msg *irc.Message) error {
log.Trace().Msgf("%v: %v", s.network.Server, msg) log.Trace().Msgf("%v: %v", h.network.Server, msg)
pong := irc.Message{ pong := irc.Message{
Command: "PONG", Command: "PONG",
Params: msg.Params, Params: msg.Params,
} }
log.Trace().Msgf("%v: %v", s.network.Server, pong.String()) log.Trace().Msgf("%v: %v", h.network.Server, pong.String())
err := s.client.Write(pong.String()) err := h.client.Write(pong.String())
if err != nil { if err != nil {
log.Error().Err(err).Msgf("error PING PONG response: %v", pong.String()) log.Error().Err(err).Msgf("error PING PONG response: %v", pong.String())
return err return err
} }
s.setLastPing() h.setLastPing()
return nil return nil
} }
// check if announcer is one from the list in the definition // check if announcer is one from the list in the definition
func (s *Handler) isValidAnnouncer(nick string) bool { func (h *Handler) isValidAnnouncer(nick string) bool {
_, ok := s.validAnnouncers[nick] _, ok := h.validAnnouncers[nick]
if !ok { if !ok {
return false return false
} }
@ -643,8 +702,8 @@ func (s *Handler) isValidAnnouncer(nick string) bool {
} }
// check if channel is one from the list in the definition // check if channel is one from the list in the definition
func (s *Handler) isValidChannel(channel string) bool { func (h *Handler) isValidChannel(channel string) bool {
_, ok := s.validChannels[strings.ToLower(channel)] _, ok := h.validChannels[strings.ToLower(channel)]
if !ok { if !ok {
return false return false
} }
@ -652,12 +711,12 @@ func (s *Handler) isValidChannel(channel string) bool {
return true return true
} }
func (s *Handler) setLastPing() { func (h *Handler) setLastPing() {
s.lastPing = time.Now() h.lastPing = time.Now()
} }
func (s *Handler) GetLastPing() time.Time { func (h *Handler) GetLastPing() time.Time {
return s.lastPing return h.lastPing
} }
// irc line can contain lots of extra stuff like color so lets clean that // irc line can contain lots of extra stuff like color so lets clean that

View file

@ -17,7 +17,7 @@ import (
type Service interface { type Service interface {
StartHandlers() StartHandlers()
StopHandlers() StopHandlers()
StopNetwork(name string) error StopNetwork(key handlerKey) error
ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error) ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error)
GetNetworksWithHealth(ctx context.Context) ([]domain.IrcNetworkWithHealth, error) GetNetworksWithHealth(ctx context.Context) ([]domain.IrcNetworkWithHealth, error)
GetNetworkByID(id int64) (*domain.IrcNetwork, error) GetNetworkByID(id int64) (*domain.IrcNetwork, error)
@ -33,7 +33,7 @@ type service struct {
indexerService indexer.Service indexerService indexer.Service
releaseService release.Service releaseService release.Service
indexerMap map[string]string indexerMap map[string]string
handlers map[string]*Handler handlers map[handlerKey]*Handler
stopWG sync.WaitGroup stopWG sync.WaitGroup
lock sync.Mutex lock sync.Mutex
@ -45,10 +45,15 @@ func NewService(repo domain.IrcRepo, filterService filter.Service, indexerSvc in
filterService: filterService, filterService: filterService,
indexerService: indexerSvc, indexerService: indexerSvc,
releaseService: releaseSvc, releaseService: releaseSvc,
handlers: make(map[string]*Handler), handlers: make(map[handlerKey]*Handler),
} }
} }
type handlerKey struct {
server string
nick string
}
func (s *service) StartHandlers() { func (s *service) StartHandlers() {
networks, err := s.repo.FindActiveNetworks(context.Background()) networks, err := s.repo.FindActiveNetworks(context.Background())
if err != nil { if err != nil {
@ -76,8 +81,9 @@ func (s *service) StartHandlers() {
// init new irc handler // init new irc handler
handler := NewHandler(network, s.filterService, s.releaseService, definitions) handler := NewHandler(network, s.filterService, s.releaseService, definitions)
// TODO use network.Server? + nick? Need a way to use multiple indexers for one network if same nick // use network.Server + nick to use multiple indexers with different nick per network
s.handlers[network.Server] = handler // this allows for multiple handlers to one network
s.handlers[handlerKey{network.Server, network.NickServ.Account}] = handler
s.lock.Unlock() s.lock.Unlock()
log.Debug().Msgf("starting network: %+v", network.Name) log.Debug().Msgf("starting network: %+v", network.Name)
@ -105,7 +111,7 @@ func (s *service) StopHandlers() {
func (s *service) startNetwork(network domain.IrcNetwork) error { func (s *service) startNetwork(network domain.IrcNetwork) error {
// look if we have the network in handlers already, if so start it // look if we have the network in handlers already, if so start it
if existingHandler, found := s.handlers[network.Server]; found { if existingHandler, found := s.handlers[handlerKey{network.Server, network.NickServ.Account}]; found {
log.Debug().Msgf("starting network: %+v", network.Name) log.Debug().Msgf("starting network: %+v", network.Name)
if existingHandler.conn != nil { if existingHandler.conn != nil {
@ -131,7 +137,7 @@ func (s *service) startNetwork(network domain.IrcNetwork) error {
// init new irc handler // init new irc handler
handler := NewHandler(network, s.filterService, s.releaseService, definitions) handler := NewHandler(network, s.filterService, s.releaseService, definitions)
s.handlers[network.Server] = handler s.handlers[handlerKey{network.Server, network.NickServ.Account}] = handler
s.lock.Unlock() s.lock.Unlock()
log.Debug().Msgf("starting network: %+v", network.Name) log.Debug().Msgf("starting network: %+v", network.Name)
@ -152,9 +158,8 @@ func (s *service) startNetwork(network domain.IrcNetwork) error {
func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error { func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error {
// look if we have the network in handlers, if so restart it // look if we have the network in handlers, if so restart it
// TODO check if we need to add indexerDefinitions etc if existingHandler, found := s.handlers[handlerKey{network.Server, network.NickServ.Account}]; found {
if existingHandler, found := s.handlers[network.Server]; found { log.Debug().Msgf("irc: decide if irc network handler needs restart or updating: %+v", network.Server)
log.Debug().Msgf("decide if irc network handler needs restart or updating: %+v", network.Name)
// if server, tls, invite command, port : changed - restart // if server, tls, invite command, port : changed - restart
// if nickserv account, nickserv password : changed - stay connected, and change those // if nickserv account, nickserv password : changed - stay connected, and change those
@ -173,18 +178,20 @@ func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error
restartNeeded = true restartNeeded = true
} }
if restartNeeded { if restartNeeded {
log.Info().Msgf("irc: restarting network: %+v", network.Name) log.Info().Msgf("irc: restarting network: %+v", network.Server)
// we need to reinitialize with new network config // we need to reinitialize with new network config
existingHandler.UpdateNetwork(network) existingHandler.UpdateNetwork(network)
// todo reset channelHealth?
go func() { go func() {
if err := existingHandler.Restart(); err != nil { if err := existingHandler.Restart(); err != nil {
log.Error().Stack().Err(err).Msgf("failed to restart network %q", existingHandler.network.Name) log.Error().Stack().Err(err).Msgf("failed to restart network %q", existingHandler.network.Name)
} }
}() }()
// return now since the restart will read the network again OR FIXME // return now since the restart will read the network again
return nil return nil
} }
@ -230,6 +237,7 @@ func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error
channelsToLeave = append(channelsToLeave, handlerChan.Name) channelsToLeave = append(channelsToLeave, handlerChan.Name)
} }
// check new channels against handler to see which to join
for _, channel := range network.Channels { for _, channel := range network.Channels {
_, ok := handlerChannels[channel.Name] _, ok := handlerChannels[channel.Name]
if ok { if ok {
@ -243,6 +251,7 @@ func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error
// leave channels // leave channels
for _, leaveChannel := range channelsToLeave { for _, leaveChannel := range channelsToLeave {
log.Debug().Msgf("%v: part channel %v", network.Server, leaveChannel)
err := existingHandler.HandlePartChannel(leaveChannel) err := existingHandler.HandlePartChannel(leaveChannel)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("failed to leave channel: %q", leaveChannel) log.Error().Stack().Err(err).Msgf("failed to leave channel: %q", leaveChannel)
@ -251,12 +260,21 @@ func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error
// join channels // join channels
for _, joinChannel := range channelsToJoin { for _, joinChannel := range channelsToJoin {
// TODO handle invite commands before? log.Debug().Msgf("%v: join new channel %v", network.Server, joinChannel)
err := existingHandler.HandleJoinChannel(joinChannel.Name, joinChannel.Password) err := existingHandler.HandleJoinChannel(joinChannel.Name, joinChannel.Password)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("failed to join channel: %q", joinChannel.Name) log.Error().Stack().Err(err).Msgf("failed to join channel: %q", joinChannel.Name)
} }
} }
// update network for handler
// TODO move all this restart logic inside handler to let it decide what to do
existingHandler.SetNetwork(network)
// find indexer definitions for network and add
definitions := s.indexerService.GetIndexersByIRCNetwork(network.Server)
existingHandler.InitIndexers(definitions)
} }
} else { } else {
err := s.startNetwork(*network) err := s.startNetwork(*network)
@ -270,7 +288,7 @@ func (s *service) checkIfNetworkRestartNeeded(network *domain.IrcNetwork) error
func (s *service) restartNetwork(network domain.IrcNetwork) error { func (s *service) restartNetwork(network domain.IrcNetwork) error {
// look if we have the network in handlers, if so restart it // look if we have the network in handlers, if so restart it
if existingHandler, found := s.handlers[network.Server]; found { if existingHandler, found := s.handlers[handlerKey{network.Server, network.NickServ.Account}]; found {
log.Info().Msgf("restarting network: %+v", network.Name) log.Info().Msgf("restarting network: %+v", network.Name)
if existingHandler.conn != nil { if existingHandler.conn != nil {
@ -287,31 +305,31 @@ func (s *service) restartNetwork(network domain.IrcNetwork) error {
return nil return nil
} }
func (s *service) StopNetwork(name string) error { func (s *service) StopNetwork(key handlerKey) error {
if handler, found := s.handlers[name]; found { if handler, found := s.handlers[key]; found {
handler.Stop() handler.Stop()
log.Debug().Msgf("stopped network: %+v", name) log.Debug().Msgf("stopped network: %+v", key.server)
} }
return nil return nil
} }
func (s *service) StopAndRemoveNetwork(name string) error { func (s *service) StopAndRemoveNetwork(key handlerKey) error {
if handler, found := s.handlers[name]; found { if handler, found := s.handlers[key]; found {
handler.Stop() handler.Stop()
// remove from handlers // remove from handlers
delete(s.handlers, name) delete(s.handlers, key)
log.Debug().Msgf("stopped network: %+v", name) log.Debug().Msgf("stopped network: %+v", key)
} }
return nil return nil
} }
func (s *service) StopNetworkIfRunning(name string) error { func (s *service) StopNetworkIfRunning(key handlerKey) error {
if handler, found := s.handlers[name]; found { if handler, found := s.handlers[key]; found {
handler.Stop() handler.Stop()
log.Debug().Msgf("stopped network: %+v", name) log.Debug().Msgf("stopped network: %+v", key.server)
} }
return nil return nil
@ -351,8 +369,6 @@ func (s *service) ListNetworks(ctx context.Context) ([]domain.IrcNetwork, error)
} }
n.Channels = append(n.Channels, channels...) n.Channels = append(n.Channels, channels...)
// TODO get handler channelHealth
ret = append(ret, n) ret = append(ret, n)
} }
@ -382,7 +398,7 @@ func (s *service) GetNetworksWithHealth(ctx context.Context) ([]domain.IrcNetwor
Connected: false, Connected: false,
} }
handler, ok := s.handlers[n.Server] handler, ok := s.handlers[handlerKey{n.Server, n.NickServ.Account}]
if ok { if ok {
// only set connected and connected since if we have an active handler and connection // only set connected and connected since if we have an active handler and connection
if handler.conn != nil { if handler.conn != nil {
@ -441,7 +457,7 @@ func (s *service) DeleteNetwork(ctx context.Context, id int64) error {
// Remove network and handler // Remove network and handler
//if err = s.StopNetwork(network.Server); err != nil { //if err = s.StopNetwork(network.Server); err != nil {
if err = s.StopAndRemoveNetwork(network.Server); err != nil { if err = s.StopAndRemoveNetwork(handlerKey{network.Server, network.NickServ.Account}); err != nil {
return err return err
} }
@ -478,9 +494,8 @@ func (s *service) UpdateNetwork(ctx context.Context, network *domain.IrcNetwork)
} }
} else { } else {
// TODO take into account multiple channels per network // take into account multiple channels per network
//err := s.StopNetwork(network.Server) err := s.StopAndRemoveNetwork(handlerKey{network.Server, network.NickServ.Account})
err := s.StopAndRemoveNetwork(network.Server)
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msgf("could not stop network: %+v", network.Name) log.Error().Stack().Err(err).Msgf("could not stop network: %+v", network.Name)
return fmt.Errorf("could not stop network: %v", network.Name) return fmt.Errorf("could not stop network: %v", network.Name)
@ -514,16 +529,23 @@ func (s *service) StoreNetwork(ctx context.Context, network *domain.IrcNetwork)
return nil return nil
} }
// get channels for existing network
existingChannels, err := s.repo.ListChannels(existingNetwork.ID)
if err != nil {
log.Error().Err(err).Msgf("failed to list channels for network %q", existingNetwork.Server)
}
existingNetwork.Channels = existingChannels
if network.Channels != nil { if network.Channels != nil {
for _, channel := range network.Channels { for _, channel := range network.Channels {
// TODO store or add. Make sure it doesn't delete before // add channels. Make sure it doesn't delete before
if err := s.repo.StoreChannel(existingNetwork.ID, &channel); err != nil { if err := s.repo.StoreChannel(existingNetwork.ID, &channel); err != nil {
return err return err
} }
} }
// append channels to existing network // append channels to existing network
network.Channels = append(network.Channels, existingNetwork.Channels...) existingNetwork.Channels = append(existingNetwork.Channels, network.Channels...)
} }
if existingNetwork.Enabled { if existingNetwork.Enabled {
@ -531,10 +553,10 @@ func (s *service) StoreNetwork(ctx context.Context, network *domain.IrcNetwork)
// if nickserv account, nickserv password : changed - stay connected, and change those // if nickserv account, nickserv password : changed - stay connected, and change those
// if channels len : changes - join or leave // if channels len : changes - join or leave
err := s.checkIfNetworkRestartNeeded(network) err := s.checkIfNetworkRestartNeeded(existingNetwork)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not restart network: %+v", network.Name) log.Error().Err(err).Msgf("could not restart network: %+v", existingNetwork.Name)
return fmt.Errorf("could not restart network: %v", network.Name) return fmt.Errorf("could not restart network: %v", existingNetwork.Name)
} }
} }

View file

@ -68,7 +68,7 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
} }
const network: Network = { const network: Network = {
name: ind.name, name: ind.irc.network,
enabled: false, enabled: false,
server: ind.irc.server, server: ind.irc.server,
port: ind.irc.port, port: ind.irc.port,