diff --git a/.gitignore b/.gitignore index 02ed68c..00e4b74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ tokens/*token -tokens/*logins bin/* !./bin/.gitkeep !./bin/* diff --git a/changelog.md b/changelog.md index 728e083..368fc5d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,57 +1,5 @@ # Changelog -## 5.1.0 - 2020-03-18 -### Added -- PetCat twitch chat handler -- DailyEmote twitch chat handler - -## 5.0.1 - 2020-03-08 -### Changed -- Updated `go-twitch-irc` lib to v2.2.2 - - -## 5.0.0 - 2020-01-11 -### Added -- Twitch point song request feature with dedicated web server and twitch -chat handler -- Mentions of `!galched` command in bot messages -- Chiki-briki song for discord bot - -### Fixed -- Typos in the bot messages - -### Changed -- Updated `go-twitch-irc` lib to v2.2.1 - -## 4.2.0 - 2019-10-20 -### Added -- Universal song handler with polka and sax commands - -### Changed -- Updated discordgo library up to v0.20.1 - -## 4.1.0 - 2019-07-28 -### Added -- Song player handler - -## 4.0.2 - 2019-07-28 -### Changed -- Info about 7th subday - -## 4.0.1 - 2019-07-22 -### Changed -- Twitch chat library version from v1 to v2 - -## 4.0.0 - 2019-06-23 -### Added -- Twitch chat module -- Twitch chat handler that calculates duplicates in chat - -## 3.0.2 - 2019-06-03 -### Added -- Command !subhistory -- Info about 5th and 6th subday - ## 3.0.1 - 2019-05-10 ### Added - Readme file diff --git a/go.mod b/go.mod index 154df17..2008330 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,12 @@ module galched-bot require ( - github.com/bwmarrin/discordgo v0.20.1 - github.com/gempir/go-twitch-irc/v2 v2.2.2 - github.com/gorilla/websocket v1.4.1 // indirect + github.com/bwmarrin/discordgo v0.19.0 github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.3.0 // indirect - go.uber.org/atomic v1.3.2 + go.uber.org/atomic v1.3.2 // indirect go.uber.org/dig v1.7.0 // indirect go.uber.org/fx v1.9.0 go.uber.org/goleak v0.10.0 // indirect go.uber.org/multierr v1.1.0 // indirect - golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect - golang.org/x/sys v0.0.0-20191018095205-727590c5006e // indirect - google.golang.org/api v0.15.0 ) - -go 1.13 diff --git a/go.sum b/go.sum index bf28298..31b3e85 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,9 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/bwmarrin/discordgo v0.20.1 h1:Ihh3/mVoRwy3otmaoPDUioILBJq4fdWkpsi83oj2Lmk= -github.com/bwmarrin/discordgo v0.20.1/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY= +github.com/bwmarrin/discordgo v0.19.0/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gempir/go-twitch-irc/v2 v2.2.1 h1:jMiEgw6zzrgiz4viG7lgj148J6enLls5aicF+zsi1bk= -github.com/gempir/go-twitch-irc/v2 v2.2.1/go.mod h1:0HXoEr9l7gNjwajosptV0w0xGpHeU6gsD7JDlfvjTYI= -github.com/gempir/go-twitch-irc/v2 v2.2.2 h1:uzinel2qApXL1UVfr3QcZ3dJsf+YU+PaUp0qJk03qNo= -github.com/gempir/go-twitch-irc/v2 v2.2.2/go.mod h1:0HXoEr9l7gNjwajosptV0w0xGpHeU6gsD7JDlfvjTYI= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -41,8 +11,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/dig v1.7.0 h1:E5/L92iQTNJTjfgJF2KgU+/JpMaiuvK2DHLBj0+kSZk= @@ -55,64 +23,3 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -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/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ= -golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.15.0 h1:yzlyyDW/J0w8yNFJIhiAJy4kq74S+1DOLdawELNxFMA= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 81705ed..5b7b8e0 100644 --- a/main.go +++ b/main.go @@ -4,16 +4,12 @@ import ( "context" "log" - "go.uber.org/fx" - "galched-bot/modules/discord" "galched-bot/modules/grace" - "galched-bot/modules/patpet" "galched-bot/modules/settings" "galched-bot/modules/subday" - "galched-bot/modules/twitchat" - "galched-bot/modules/web" - "galched-bot/modules/youtube" + + "go.uber.org/fx" ) type ( @@ -25,8 +21,6 @@ type ( Context context.Context Discord *discord.Discord Settings *settings.Settings - Chat *twitchat.TwitchIRC - Server *web.WebServer } ) @@ -41,39 +35,14 @@ func start(p appParam) error { if err != nil { log.Print("discord: cannot start instance", err) return err + } log.Printf("main: discord instance running") - - err = p.Chat.Start() - if err != nil { - log.Print("chat: cannot start instance", err) - return err - } - log.Printf("main: twitch chat instance running") - - err = p.Server.Start() - if err != nil { - log.Print("web: cannot start instance", err) - return err - } - log.Printf("main: web server instance running") - log.Printf("main: — — —") + <-p.Context.Done() log.Print("main: stopping galched-bot") - err = p.Server.Stop(p.Context) - if err != nil { - log.Print("web: cannot stop instance", err) - return err - } - - err = p.Chat.Stop() - if err != nil { - log.Print("chat: cannot stop instance", err) - return err - } - err = p.Discord.Stop() if err != nil { log.Print("discord: cannot stop instance", err) @@ -88,8 +57,7 @@ func main() { var err error app := fx.New( fx.Logger(new(silentPrinter)), - fx.Provide(settings.New, grace.New, discord.New, subday.New, - twitchat.New, web.New, youtube.New, patpet.New), + fx.Provide(settings.New, grace.New, discord.New, subday.New), fx.Invoke(start)) err = app.Start(context.Background()) diff --git a/modules/discord/discord.go b/modules/discord/discord.go index 62c266e..37a40e0 100644 --- a/modules/discord/discord.go +++ b/modules/discord/discord.go @@ -31,10 +31,6 @@ func New(s *settings.Settings, subday *subday.Subday) (*Discord, error) { processor.AddHandler(subdayHandler) } - for _, songHandler := range SongHandlers(s) { - processor.AddHandler(songHandler) - } - log.Printf("discord: added %d message handlers", len(processor.handlers)) if len(processor.handlers) > 0 { for i := range processor.handlers { diff --git a/modules/discord/handlers.go b/modules/discord/handlers.go index 05a241c..37b2dde 100644 --- a/modules/discord/handlers.go +++ b/modules/discord/handlers.go @@ -53,7 +53,6 @@ func (h *HandlerProcessor) Process(s *discordgo.Session, m *discordgo.MessageCre if strings.HasPrefix(m.Content, "!galched") { LogMessage(m) SendMessage(s, m, h.HelpMessage()) - return } for i := range h.handlers { diff --git a/modules/discord/songhandlers.go b/modules/discord/songhandlers.go deleted file mode 100644 index cc76886..0000000 --- a/modules/discord/songhandlers.go +++ /dev/null @@ -1,191 +0,0 @@ -package discord - -import ( - "encoding/binary" - "io" - "log" - "os" - "time" - - "github.com/bwmarrin/discordgo" - "github.com/pkg/errors" - "go.uber.org/atomic" - - "galched-bot/modules/settings" -) - -type ( - songData [][]byte - - SongHandler struct { - globalLock *atomic.Bool - songLock *atomic.Bool - - song songData - signature string - description string - voiceChannel string - permissions []string - timeout time.Duration - } -) - -func (h *SongHandler) Signature() string { - return h.signature -} - -func (h *SongHandler) Description() string { - return h.description -} - -func (h *SongHandler) IsValid(msg string) bool { - return msg == h.signature -} - -func (h *SongHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { - var permitted bool - for i := range h.permissions { - if m.Author.Username == h.permissions[i] { - permitted = true - break - } - } - if len(h.permissions) > 0 && !permitted { - log.Printf("discord: unathorized %s message from %s", - h.signature, m.Author.Username) - return - } - - // Find the channel that the message came from. - c, err := s.State.Channel(m.ChannelID) - if err != nil { - // Could not find channel. - return - } - - // Find the guild for that channel. - g, err := s.State.Guild(c.GuildID) - if err != nil { - // Could not find guild. - return - } - - // Look for the message sender in that guild's current voice states. - LogMessage(m) - if h.globalLock.CAS(false, true) { - if h.songLock.CAS(false, true) { - - err = playSound(s, g.ID, h.voiceChannel, h.song) - if err != nil { - log.Println("discord: error playing sound:", err) - } - - h.globalLock.Store(false) - time.Sleep(h.timeout) - defer h.songLock.Store(false) - - return - } - h.globalLock.Store(false) - } -} - -func SongHandlers(s *settings.Settings) []MessageHandler { - result := make([]MessageHandler, 0, len(s.Songs)) - g := new(atomic.Bool) - - for i := range s.Songs { - song, err := loadSong(s.Songs[i].Path) - if err != nil { - log.Println("discord: error loading song file", err) - continue - } - handler := &SongHandler{ - globalLock: g, - songLock: new(atomic.Bool), - song: song, - signature: s.Songs[i].Signature, - description: s.Songs[i].Description, - voiceChannel: s.DiscordVoiceChannel, - permissions: s.Songs[i].Permissions, - timeout: s.Songs[i].Timeout, - } - result = append(result, handler) - } - - return result -} - -// playSound plays the current buffer to the provided channel. -func playSound(s *discordgo.Session, guildID, channelID string, song songData) error { - - // Join the provided voice channel. - vc, err := s.ChannelVoiceJoin(guildID, channelID, false, true) - if err != nil { - return err - } - - // Sleep for a specified amount of time before playing the sound - time.Sleep(250 * time.Millisecond) - - // Start speaking. - vc.Speaking(true) - - // Send the buffer data. - for _, buff := range song { - vc.OpusSend <- buff - } - - // Stop speaking - vc.Speaking(false) - - // Sleep for a specified amount of time before ending. - time.Sleep(250 * time.Millisecond) - - // Disconnect from the provided voice channel. - vc.Disconnect() - - return nil -} - -func loadSong(path string) (songData, error) { - file, err := os.Open(path) - if err != nil { - return nil, errors.Wrap(err, "error opening dca file") - } - - var ( - opuslen int16 - buffer = make(songData, 0) - ) - - for { - // Read opus frame length from dca file. - err = binary.Read(file, binary.LittleEndian, &opuslen) - - // If this is the end of the file, just return. - if err == io.EOF || err == io.ErrUnexpectedEOF { - err := file.Close() - if err != nil { - return nil, err - } - return buffer, nil - } - - if err != nil { - return nil, errors.Wrap(err, "error reading from dca file") - } - - // Read encoded pcm from dca file. - InBuf := make([]byte, opuslen) - err = binary.Read(file, binary.LittleEndian, &InBuf) - - // Should not be any end of file errors - if err != nil { - return nil, errors.Wrap(err, "error reading from dca file") - } - - // Append encoded pcm data to the buffer. - buffer = append(buffer, InBuf) - } -} diff --git a/modules/discord/subdayhandlers.go b/modules/discord/subdayhandlers.go index 70c5821..91329da 100644 --- a/modules/discord/subdayhandlers.go +++ b/modules/discord/subdayhandlers.go @@ -38,7 +38,10 @@ func (h *SubdayListHandler) Handle(s *discordgo.Session, m *discordgo.MessageCre log.Print("discord: cannot obtain guild", err) return } - message := "Игры предыдущих сабдеев доступны по команде **!subhistory**\n" + + message := "Игры предыдущих сабдеев:\n**20.10.18**: _DmC_ -> _Fable 1_ -> _Overcooked 2_\n" + + "**17.11.18**: _The Witcher_ -> _Xenus: Белое Золото_ -> _NFS: Underground 2_\n" + + "**22.12.18**: _True Crime: Streets of LA_ -> _Serious Sam 3_ -> _Kholat_\n" + + "**26.01.19**: _Disney’s Aladdin_ -> _~~Gothic~~_ -> _Scrapland_ -> _Donut County_\n\n" + "Список игр для следующего сабдея:\n" for k, v := range h.subday.Database() { nickname := " " @@ -55,7 +58,6 @@ func (h *SubdayListHandler) Handle(s *discordgo.Session, m *discordgo.MessageCre message += fmt.Sprintf(" **- %s** от _%s_\n", game, nickname) } } - message += "\nВсе команды бота: !galched\n" SendMessage(s, m, strings.Trim(message, "\n")) } @@ -136,35 +138,10 @@ loop: } } -type SubdayHistoryHandler struct{} - -func (h *SubdayHistoryHandler) Signature() string { - return "!subhistory" -} -func (h *SubdayHistoryHandler) Description() string { - return "история прошлых сабдеев" -} -func (h *SubdayHistoryHandler) IsValid(msg string) bool { - return strings.HasPrefix(msg, "!subhistory") -} -func (h *SubdayHistoryHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { - LogMessage(m) - message := "Игры предыдущих сабдеев:\n**20.10.18**: _DmC_ -> _Fable 1_ -> _Overcooked 2_\n" + - "**17.11.18**: _The Witcher_ -> _Xenus: Белое Золото_ -> _NFS: Underground 2_\n" + - "**22.12.18**: _True Crime: Streets of LA_ -> _Serious Sam 3_ -> _Kholat_\n" + - "**26.01.19**: _Disney’s Aladdin_ -> _~~Gothic~~_ -> _Scrapland_ -> _Donut County_\n" + - "**24.02.19**: _Tetris 99_ -> _~~Bully~~_ -> _~~GTA: Vice City~~_\n" + - "**02.06.19**: _Spec Ops: The Line_ -> _Escape from Tarkov_\n" + - "**28.07.19**: _Crypt of the Necrodancer_ -> _My Friend Pedro_ -> _Ape Out_\n" + - "\nВсе команды бота: !galched\n" - SendMessage(s, m, message) -} - func SubdayHandlers(s *subday.Subday, r []string) []MessageHandler { var result []MessageHandler addHandler := &SubdayAddHandler{s, r} listHandler := &SubdayListHandler{s} - histHandler := new(SubdayHistoryHandler) - return append(result, addHandler, listHandler, histHandler) + return append(result, addHandler, listHandler) } diff --git a/modules/patpet/patpet.go b/modules/patpet/patpet.go deleted file mode 100644 index 35a9592..0000000 --- a/modules/patpet/patpet.go +++ /dev/null @@ -1,88 +0,0 @@ -package patpet - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" - "sync" - - "galched-bot/modules/settings" -) - -type ( - Pet struct { - sync.RWMutex - path string - counter int - } -) - -func New(s *settings.Settings) (*Pet, error) { - var ( - err error - counter int - ) - - petData, err := ioutil.ReadFile(s.PetDataPath) - if err != nil { - log.Print("pet: cannot read data file", err) - log.Print("pet: creating new counter") - } else { - err = json.Unmarshal(petData, &counter) - if err != nil { - counter = 0 - log.Print("pet: cannot unmarshal data file", err) - log.Print("pet: creating new counter") - } else { - log.Print("pet: using previously saved counter") - } - } - - return &Pet{ - RWMutex: sync.RWMutex{}, - path: s.PetDataPath, - counter: counter, - }, nil -} - -func (p *Pet) Pet() int { - p.Lock() - defer p.Unlock() - - p.counter++ - return p.counter -} - -func (p *Pet) Counter() int { - p.RUnlock() - defer p.RUnlock() - - return p.counter -} - -func (p *Pet) Dump() { - p.RLock() - defer p.RUnlock() - - data, err := json.Marshal(p.counter) - if err != nil { - log.Print("pet: cannot marshal counter", err) - return - } - file, err := os.Create(p.path) - if err != nil { - log.Print("pet: cannot open counter file", err) - return - } - _, err = fmt.Fprintf(file, string(data)) - if err != nil { - log.Print("pet: cannot write to counter file") - } - err = file.Close() - if err != nil { - log.Print("pet: cannot close counter file") - } - log.Print("pet: counter dumped to file:", p.counter) -} diff --git a/modules/settings/settings.go b/modules/settings/settings.go index 39d5152..6a3c9af 100644 --- a/modules/settings/settings.go +++ b/modules/settings/settings.go @@ -1,56 +1,28 @@ package settings import ( - "encoding/json" "io/ioutil" "log" - "time" ) const ( - version = "5.1.0" - twitchUser = "galchedbot" - twitchIRCRoom = "galched" + version = "3.0.1" discordTokenPath = "./tokens/.discordtoken" - twitchTokenPath = "./tokens/.twitchtoken" subdayDataPath = "./backups/subday" - petDataPath = "./backups/pets" - youtubeTokenPath = "./tokens/.youtubetoken" - webLoginsPath = "./tokens/.weblogins" // Permitted roles in discord for subday subRole1 = "433672344737677322" subRole2 = "433680494635515904" galchedRole = "301467455497175041" smorcRole = "301470784491356172" - - defaultQueueAddr = ":8888" ) type ( - SongInfo struct { - Path string - Signature string - Description string - Permissions []string - Timeout time.Duration - } - Settings struct { - Version string - DiscordToken string - TwitchUser string - TwitchIRCRoom string - TwitchToken string - YoutubeToken string - SubdayDataPath string - PetDataPath string - PermittedRoles []string - DiscordVoiceChannel string - Songs []SongInfo - - QueueAddress string - LoginUsers map[string]string + Version string + DiscordToken string + SubdayDataPath string + PermittedRoles []string } ) @@ -59,59 +31,11 @@ func New() (*Settings, error) { if err != nil { log.Print("settings: cannot read discord token file", err) } - twitchToken, err := ioutil.ReadFile(twitchTokenPath) - if err != nil { - log.Print("settings: cannot read twitch token file", err) - } - youtubetoken, err := ioutil.ReadFile(youtubeTokenPath) - if err != nil { - log.Print("settings: cannot read twitch token file", err) - } - - webLogins := make(map[string]string) - webLoginsRaw, err := ioutil.ReadFile(webLoginsPath) - if err != nil { - log.Print("settings: cannot read web login file", err) - } else { - err = json.Unmarshal(webLoginsRaw, &webLogins) - if err != nil { - log.Print("settings: cannot parse web login file", err) - } - } return &Settings{ - Version: version, - DiscordToken: string(discordToken), - TwitchToken: string(twitchToken), - YoutubeToken: string(youtubetoken), - TwitchUser: twitchUser, - TwitchIRCRoom: twitchIRCRoom, - SubdayDataPath: subdayDataPath, - PetDataPath: petDataPath, - DiscordVoiceChannel: "301793085522706432", - PermittedRoles: []string{subRole1, subRole2, galchedRole, smorcRole}, - Songs: []SongInfo{ - { - Path: "songs/polka.dca", - Signature: "!song", - Description: "сыграть гимн галчед (только для избранных)", - Permissions: []string{"AlexV", "Rummy_Quamox", "Lidiya_owl"}, - Timeout: 10 * time.Second, - }, - { - Path: "songs/whisper.dca", - Signature: "!sax", - Description: "kreygasm", - Timeout: 20 * time.Second, - }, - { - Path: "songs/st.dca", - Signature: "!chiki", - Description: "briki v damki", - Timeout: 20 * time.Second, - }, - }, - QueueAddress: defaultQueueAddr, - LoginUsers: webLogins, + Version: version, + DiscordToken: string(discordToken), + SubdayDataPath: subdayDataPath, + PermittedRoles: []string{subRole1, subRole2, galchedRole, smorcRole}, }, nil } diff --git a/modules/twitchat/dailyemote.go b/modules/twitchat/dailyemote.go deleted file mode 100644 index 810ecec..0000000 --- a/modules/twitchat/dailyemote.go +++ /dev/null @@ -1,257 +0,0 @@ -package twitchat - -import ( - "fmt" - "hash/fnv" - "math/rand" - "time" - - "github.com/gempir/go-twitch-irc/v2" -) - -const emoteMsg = "!emote" - -var ( - emotes = []string{ - "4Head", - "ANELE", - "ArgieB8", - "ArsonNoSexy", - "AsexualPride", - "AsianGlow", - "BCWarrior", - "BOP", - "BabyRage", - "BatChest", - "BegWan", - "BibleThump", - "BigBrother", - "BigPhish", - "BisexualPride", - "BlargNaut", - "BlessRNG", - "BloodTrail", - "BrainSlug", - "BrokeBack", - "BuddhaBar", - "CarlSmile", - "ChefFrank", - "CoolCat", - "CoolStoryBob", - "CorgiDerp", - "CrreamAwk", - "CurseLit", - "DAESuppy", - "DBstyle", - "DansGame", - "DarkMode", - "DatSheffy", - "DendiFace", - "DogFace", - "DoritosChip", - "DrinkPurple", - "DxCat", - "EarthDay", - "EleGiggle", - "EntropyWins", - "FBBlock", - "FBCatch", - "FBChallenge", - "FBPass", - "FBPenalty", - "FBRun", - "FBSpiral", - "FBtouchdown", - "FUNgineer", - "FailFish", - "FrankerZ", - "FreakinStinkin", - "FutureMan", - "GayPride", - "GenderFluidPride", - "GingerPower", - "GivePLZ", - "GrammarKing", - "GreenTeam", - "GunRun", - "HSCheers", - "HSWP", - "HassaanChop", - "HassanChop", - "HeyGuys", - "HolidayCookie", - "HolidayLog", - "HolidayOrnament", - "HolidayPresent", - "HolidaySanta", - "HolidayTree", - "HotPokket", - "HumbleLife", - "IntersexPride", - "InuyoFace", - "ItsBoshyTime", - "JKanStyle", - "Jebaited", - "JonCarnage", - "KAPOW", - "Kappa", - "KappaClaus", - "KappaPride", - "KappaRoss", - "KappaWealth", - "Kappu", - "Keepo", - "KevinTurtle", - "Kippa", - "KomodoHype", - "KonCha", - "Kreygasm", - "LUL", - "LesbianPride", - "MVGame", - "Mau5", - "MaxLOL", - "MercyWing1", - "MercyWing2", - "MikeHogu", - "MingLee", - "MorphinTime", - "MrDestructoid", - "NinjaGrumpy", - "NomNom", - "NonBinaryPride", - "NotATK", - "NotLikeThis", - "OSFrog", - "OhMyDog", - "OneHand", - "OpieOP", - "OptimizePrime", - "PJSalt", - "PJSugar", - "PMSTwin", - "PRChase", - "PanicVis", - "PansexualPride", - "PartyHat", - "PartyTime", - "PeoplesChamp", - "PermaSmug", - "PicoMause", - "PinkMercy", - "PipeHype", - "PixelBob", - "PogChamp", - "Poooound", - "PopCorn", - "PorscheWIN", - "PowerUpL", - "PowerUpR", - "PraiseIt", - "PrimeMe", - "PunOko", - "PunchTrees", - "PurpleStar", - "RaccAttack", - "RalpherZ", - "RedCoat", - "RedTeam", - "ResidentSleeper", - "RitzMitz", - "RlyTho", - "RuleFive", - "SMOrc", - "SSSsss", - "SabaPing", - "SeemsGood", - "SeriousSloth", - "ShadyLulu", - "ShazBotstix", - "SingsMic", - "SingsNote", - "SmoocherZ", - "SoBayed", - "SoonerLater", - "Squid1", - "Squid2", - "Squid3", - "Squid4", - "StinkyCheese", - "StoneLightning", - "StrawBeary", - "SuperVinlin", - "SwiftRage", - "TBAngel", - "TF2John", - "TPFufun", - "TPcrunchyroll", - "TTours", - "TakeNRG", - "TearGlove", - "TehePelo", - "ThankEgg", - "TheIlluminati", - "TheRinger", - "TheTarFu", - "TheThing", - "ThunBeast", - "TinyFace", - "TombRaid", - "TooSpicy", - "TransgenderPride", - "TriHard", - "TwitchLit", - "TwitchRPG", - "TwitchSings", - "TwitchUnity", - "TwitchVotes", - "UWot", - "UnSane", - "UncleNox", - "VoHiYo", - "VoteNay", - "VoteYea", - "WTRuck", - "WholeWheat", - "WutFace", - "YouDontSay", - "YouWHY", - "bleedPurple", - "cmonBruh", - "copyThis", - "duDudu", - "imGlitch", - "mcaT", - "panicBasket", - "pastaThat", - "riPepperonis", - "twitchRaid", - } -) - -type ( - dailyEmote struct{} -) - -func DailyEmote() *dailyEmote { - return new(dailyEmote) -} - -func (h *dailyEmote) IsValid(m *twitch.PrivateMessage) bool { - return (m.Tags["msg-id"] == "highlighted-message") && m.Message == emoteMsg -} - -func (h *dailyEmote) Handle(m *twitch.PrivateMessage, r Responser) { - data := time.Now().Format("2006-01-02") + m.User.DisplayName - rng := rand.New(rand.NewSource(hashSeed(data))) - emote := emotes[rng.Intn(len(emotes))] - - msg := fmt.Sprintf("@%s твой эмоут дня: %s", m.User.DisplayName, emote) - r.Say(m.Channel, msg) -} - -func hashSeed(s string) int64 { - h := fnv.New64() - h.Write([]byte(s)) - return int64(h.Sum64()) -} diff --git a/modules/twitchat/duphandler.go b/modules/twitchat/duphandler.go deleted file mode 100644 index 699964a..0000000 --- a/modules/twitchat/duphandler.go +++ /dev/null @@ -1,48 +0,0 @@ -package twitchat - -import ( - "fmt" - "log" - "strings" - - "github.com/gempir/go-twitch-irc/v2" -) - -type ( - dupHandler struct { - lastMessage string - counter int - dupMinimal int - } -) - -const DupMinimal = 3 - -func DupHandler() PrivateMessageHandler { - return &dupHandler{ - lastMessage: "", - counter: 0, - dupMinimal: DupMinimal, - } -} - -func (h *dupHandler) IsValid(m *twitch.PrivateMessage) bool { - return true -} - -func (h *dupHandler) Handle(m *twitch.PrivateMessage, r Responser) { - data := strings.Fields(m.Message) - for i := range data { - if data[i] == h.lastMessage { - h.counter++ - } else { - if h.counter >= h.dupMinimal { - msg := fmt.Sprintf("%d %s подряд", h.counter, h.lastMessage) - r.Say(m.Channel, msg) - log.Print("chat: ", msg) - } - h.counter = 1 - h.lastMessage = data[i] - } - } -} diff --git a/modules/twitchat/handlers.go b/modules/twitchat/handlers.go deleted file mode 100644 index 4e3d327..0000000 --- a/modules/twitchat/handlers.go +++ /dev/null @@ -1,16 +0,0 @@ -package twitchat - -import ( - "github.com/gempir/go-twitch-irc/v2" -) - -type ( - Responser interface { - Say(channel, message string) - } - - PrivateMessageHandler interface { - IsValid(m *twitch.PrivateMessage) bool - Handle(m *twitch.PrivateMessage, r Responser) - } -) diff --git a/modules/twitchat/logcheck.go b/modules/twitchat/logcheck.go deleted file mode 100644 index 98ef08a..0000000 --- a/modules/twitchat/logcheck.go +++ /dev/null @@ -1,23 +0,0 @@ -package twitchat - -import ( - "log" - - "github.com/gempir/go-twitch-irc/v2" -) - -type ( - logCheck struct{} -) - -func LogCheck() PrivateMessageHandler { - return new(logCheck) -} - -func (h *logCheck) IsValid(m *twitch.PrivateMessage) bool { - return true -} - -func (h *logCheck) Handle(m *twitch.PrivateMessage, r Responser) { - log.Print("chat <", m.User.DisplayName, "> : ", m.Message) -} diff --git a/modules/twitchat/petcat.go b/modules/twitchat/petcat.go deleted file mode 100644 index e9aeb13..0000000 --- a/modules/twitchat/petcat.go +++ /dev/null @@ -1,39 +0,0 @@ -package twitchat - -import ( - "fmt" - "strings" - - "galched-bot/modules/patpet" - "github.com/gempir/go-twitch-irc/v2" -) - -const ( - petMsg1 = "!погладь" - petMsg2 = "!гладь" - petMsg3 = "!погладить" -) - -type ( - petCat struct { - cat *patpet.Pet - } -) - -func PetCat(pet *patpet.Pet) *petCat { - return &petCat{ - cat: pet, - } -} - -func (h *petCat) IsValid(m *twitch.PrivateMessage) bool { - return (m.Tags["msg-id"] == "highlighted-message") && (strings.HasPrefix(m.Message, petMsg1) || - strings.HasPrefix(m.Message, petMsg2) || - strings.HasPrefix(m.Message, petMsg3)) -} - -func (h *petCat) Handle(m *twitch.PrivateMessage, r Responser) { - msg := fmt.Sprintf("Котэ поглажен уже %d раз(а) InuyoFace", h.cat.Pet()) - r.Say(m.Channel, msg) - h.cat.Dump() -} diff --git a/modules/twitchat/songrequest.go b/modules/twitchat/songrequest.go deleted file mode 100644 index f9c5612..0000000 --- a/modules/twitchat/songrequest.go +++ /dev/null @@ -1,61 +0,0 @@ -package twitchat - -import ( - "fmt" - "log" - "strings" - - "github.com/gempir/go-twitch-irc/v2" - - "galched-bot/modules/youtube" -) - -const ( - songMsg = "!song" - reqPrefix = "!req " // space in the end is important -) - -type ( - songRequest struct { - r *youtube.Requester - } -) - -func SongRequest(r *youtube.Requester) PrivateMessageHandler { - return &songRequest{r: r} -} - -func (h *songRequest) IsValid(m *twitch.PrivateMessage) bool { - return (strings.HasPrefix(m.Message, reqPrefix) && m.Tags["msg-id"] == "highlighted-message") || - strings.TrimSpace(m.Message) == songMsg - // return strings.HasPrefix(m.Message, reqPrefix) || strings.TrimSpace(m.Message) == songMsg -} - -func (h *songRequest) Handle(m *twitch.PrivateMessage, r Responser) { - if strings.TrimSpace(m.Message) == "!song" { - list := h.r.List() - if len(list) > 0 { - line := fmt.Sprintf("Сейчас играет: <%s>", list[0].Title) - r.Say(m.Channel, line) - } else { - r.Say(m.Channel, "Очередь видео пуста") - } - return - } - - query := strings.TrimPrefix(m.Message, "!req ") - if len(query) == 0 { - return - } - - chatMsg, err := h.r.AddVideo(query, m.User.DisplayName) - if err != nil { - log.Printf("yt: cannot add song from msg <%s>, err: %v", m.Message, err) - if len(chatMsg) > 0 { - r.Say(m.Channel, m.User.DisplayName+" "+chatMsg) - } - return - } - r.Say(m.Channel, m.User.DisplayName+" добавил "+chatMsg) - return -} diff --git a/modules/twitchat/twitchat.go b/modules/twitchat/twitchat.go deleted file mode 100644 index e64063a..0000000 --- a/modules/twitchat/twitchat.go +++ /dev/null @@ -1,58 +0,0 @@ -package twitchat - -import ( - "galched-bot/modules/patpet" - "galched-bot/modules/settings" - "galched-bot/modules/youtube" - - "github.com/gempir/go-twitch-irc/v2" -) - -type ( - TwitchIRC struct { - username string - chat *twitch.Client - handlers []PrivateMessageHandler - } -) - -func New(s *settings.Settings, r *youtube.Requester, pet *patpet.Pet) (*TwitchIRC, error) { - var irc = new(TwitchIRC) - - irc.username = s.TwitchUser - - irc.handlers = append(irc.handlers, DupHandler()) - irc.handlers = append(irc.handlers, DailyEmote()) - irc.handlers = append(irc.handlers, SongRequest(r)) - irc.handlers = append(irc.handlers, PetCat(pet)) - // irc.handlers = append(irc.handlers, LogCheck()) - - irc.chat = twitch.NewClient(s.TwitchUser, s.TwitchToken) - irc.chat.OnPrivateMessage(irc.PrivateMessageHandler) - irc.chat.Join(s.TwitchIRCRoom) - - return irc, nil -} - -func (c *TwitchIRC) Start() error { - go func() { - err := c.chat.Connect() - _ = err // no point in error because disconnect will be called anyway - }() - return nil -} - -func (c *TwitchIRC) Stop() error { - return c.chat.Disconnect() -} - -func (c *TwitchIRC) PrivateMessageHandler(msg twitch.PrivateMessage) { - if msg.User.Name == c.username { - return - } - for i := range c.handlers { - if c.handlers[i].IsValid(&msg) { - c.handlers[i].Handle(&msg, c.chat) - } - } -} diff --git a/modules/web/server.go b/modules/web/server.go deleted file mode 100644 index b7b3551..0000000 --- a/modules/web/server.go +++ /dev/null @@ -1,159 +0,0 @@ -package web - -import ( - "context" - "crypto/rand" - "encoding/hex" - "encoding/json" - "io/ioutil" - "log" - "net/http" - "time" - - "galched-bot/modules/settings" - "galched-bot/modules/youtube" -) - -type ( - WebServer struct { - server http.Server - r *youtube.Requester - - users map[string]string - authed map[string]struct{} - } -) - -func New(s *settings.Settings, r *youtube.Requester) *WebServer { - srv := http.Server{ - Addr: s.QueueAddress, - } - - webServer := &WebServer{ - server: srv, - r: r, - - users: s.LoginUsers, - authed: make(map[string]struct{}, 10), - } - - http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { - if request.Method != http.MethodGet { - return - } - - if webServer.IsAuthorized(request) { - http.ServeFile(writer, request, "web/index.html") - } else { - http.Redirect(writer, request, "/login", 301) - } - }) - - http.HandleFunc("/login", func(writer http.ResponseWriter, request *http.Request) { - if request.Method == http.MethodGet { - http.ServeFile(writer, request, "web/login.html") - return - } else if request.Method != http.MethodPost { - return - } - - login := request.FormValue("login") - pwd := request.FormValue("password") - - log.Print("web: trying to log in with user: ", login) - - if webServer.IsRegistered(login, pwd) { - webServer.Authorize(writer) - } else { - log.Print("web: incorrect password attempt") - } - http.Redirect(writer, request, "/", http.StatusSeeOther) - }) - - http.HandleFunc("/scripts.js", func(writer http.ResponseWriter, request *http.Request) { - if request.Method != http.MethodGet { - return - } - - http.ServeFile(writer, request, "web/scripts.js") - }) - - http.HandleFunc("/style.css", func(writer http.ResponseWriter, request *http.Request) { - if request.Method != http.MethodGet { - return - } - - http.ServeFile(writer, request, "web/style.css") - }) - - http.HandleFunc("/queue", func(writer http.ResponseWriter, request *http.Request) { - if !webServer.IsAuthorized(request) { - http.Error(writer, "not authorized", http.StatusUnauthorized) - return - } - - switch request.Method { - case http.MethodGet: - case http.MethodPost: - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Print("web: cannot read body msg, %v", err) - return - } - id := string(body) - if len(id) != youtube.YoutubeIDLength && len(id) > 0 { - log.Printf("web: incorrect data in body, <%s>", id) - return - } - r.Remove(id) - default: - return - } - writer.Header().Set("Content-Type", "application/json") - writer.Header().Set("Access-Control-Allow-Origin", "*") - json.NewEncoder(writer).Encode(webServer.r.List()) - }) - - return webServer -} - -func (s WebServer) Start() error { - go func() { - s.server.ListenAndServe() - }() - return nil -} - -func (s WebServer) Stop(ctx context.Context) error { - return s.server.Shutdown(ctx) -} - -func (s WebServer) IsAuthorized(request *http.Request) bool { - if cookie, err := request.Cookie("session"); err == nil { - if _, ok := s.authed[cookie.Value]; ok { - return true - } - } - return false -} - -func (s WebServer) IsRegistered(login, pwd string) bool { - return s.users[login] == pwd -} - -func (s WebServer) Authorize(response http.ResponseWriter) { - var byteKey = make([]byte, 16) - rand.Read(byteKey[:]) - stringKey := hex.EncodeToString(byteKey) - - s.authed[stringKey] = struct{}{} - log.Print("web: authenticated new user") - - expires := time.Now().AddDate(0, 1, 0) - http.SetCookie(response, &http.Cookie{ - Name: "session", - Value: stringKey, - Path: "/", - Expires: expires, - }) -} diff --git a/modules/youtube/requester.go b/modules/youtube/requester.go deleted file mode 100644 index e50ed93..0000000 --- a/modules/youtube/requester.go +++ /dev/null @@ -1,195 +0,0 @@ -package youtube - -import ( - "context" - "errors" - "fmt" - "log" - "regexp" - "strconv" - "strings" - "sync" - "time" - - "google.golang.org/api/option" - "google.golang.org/api/youtube/v3" - - "galched-bot/modules/settings" -) - -const ( - YoutubeIDLength = 11 - youtubeRegexpID = `^.*((youtu.be\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?\s]*).*` -) - -var ( - urlRegex = regexp.MustCompile(youtubeRegexpID) - durationRegex = regexp.MustCompile(`P(?P\d+Y)?(?P\d+M)?(?P\d+D)?T?(?P\d+H)?(?P\d+M)?(?P\d+S)?`) -) - -type ( - Video struct { - ID string - Title string - From string - Duration string - - Upvotes uint64 - Downvotes uint64 - Views uint64 - } - - Requester struct { - mu *sync.RWMutex - - srv *youtube.Service - requests []Video - } -) - -func New(ctx context.Context, s *settings.Settings) (*Requester, error) { - srv, err := youtube.NewService(ctx, option.WithAPIKey(s.YoutubeToken)) - if err != nil { - return nil, err - } - - return &Requester{ - mu: new(sync.RWMutex), - srv: srv, - }, nil -} - -func (r *Requester) AddVideo(query, from string) (string, error) { - var ( - id string - err error - ) - - // try parse video id from the query - id, err = videoID(query) - if err != nil { - // if we can't fo that, then search for the query - resp, err := r.srv.Search.List("snippet").Type("video").MaxResults(1).Q(query).Do() - if err != nil || len(resp.Items) == 0 || resp.Items[0].Id == nil { - return "", fmt.Errorf("cannot parse youtube id: %w", err) - } - - id = resp.Items[0].Id.VideoId - } - - // get video info from api - resp, err := r.srv.Videos.List("snippet,statistics,contentDetails").Id(id).Do() - if err != nil { - return "", fmt.Errorf("cannot send request to youtube api: %w", err) - } - - // check if response have all required fields - if len(resp.Items) == 0 { - return "", errors.New("youtube api response does not contain items") - } - if resp.Items[0].Snippet == nil { - return "", errors.New("youtube api response does not contain snippet") - } - if resp.Items[0].Statistics == nil { - return "", errors.New("youtube api response does not contain statistics") - } - if resp.Items[0].ContentDetails == nil { - return "", errors.New("youtube api response does not contain content details") - } - - // check length of the video not more than 5 minutes - if parseDuration(resp.Items[0].ContentDetails.Duration) == 0 { - err = errors.New("видео не должно быть трансляцией") - return err.Error(), err - } - - // check video is not live - if parseDuration(resp.Items[0].ContentDetails.Duration) > time.Minute*5 { - err = errors.New("видео должно быть короче 5 минут") - return err.Error(), err - } - - r.mu.Lock() - defer r.mu.Unlock() - - // check if video already in the queue - for i := range r.requests { - if r.requests[i].ID == id { - err = errors.New("видео уже есть в очереди") - return err.Error(), err - } - } - - r.requests = append(r.requests, Video{ - ID: id, - From: from, - Duration: strings.ToLower(resp.Items[0].ContentDetails.Duration[2:]), - Title: resp.Items[0].Snippet.Title, - Upvotes: resp.Items[0].Statistics.LikeCount, - Views: resp.Items[0].Statistics.ViewCount, - Downvotes: resp.Items[0].Statistics.DislikeCount, - }) - log.Printf("yt: added video < %s > from < %s >\n", resp.Items[0].Snippet.Title, from) - - return resp.Items[0].Snippet.Title, nil -} - -func (r *Requester) List() []Video { - r.mu.RLock() - defer r.mu.RUnlock() - - result := make([]Video, len(r.requests)) - copy(result, r.requests) - - return result -} - -func (r *Requester) Remove(id string) { - r.mu.Lock() - defer r.mu.Unlock() - - for i := range r.requests { - if r.requests[i].ID == id { - r.requests = append(r.requests[:i], r.requests[i+1:]...) - return - } - } -} - -func videoID(url string) (string, error) { - result := urlRegex.FindStringSubmatch(url) - - ln := len(result) - if ln == 0 || len(result[ln-1]) != YoutubeIDLength { - return "", fmt.Errorf("id haven't matched in \"%s\"", url) - } - - return result[ln-1], nil -} - -func parseDuration(str string) time.Duration { - matches := durationRegex.FindStringSubmatch(str) - - years := parseInt64(matches[1]) - months := parseInt64(matches[2]) - days := parseInt64(matches[3]) - hours := parseInt64(matches[4]) - minutes := parseInt64(matches[5]) - seconds := parseInt64(matches[6]) - - hour := int64(time.Hour) - minute := int64(time.Minute) - second := int64(time.Second) - return time.Duration(years*24*365*hour + months*30*24*hour + days*24*hour + hours*hour + minutes*minute + seconds*second) -} - -func parseInt64(value string) int64 { - if len(value) == 0 { - return 0 - } - parsed, err := strconv.Atoi(value[:len(value)-1]) - if err != nil { - return 0 - } - return int64(parsed) -} diff --git a/modules/youtube/requester_test.go b/modules/youtube/requester_test.go deleted file mode 100644 index 6e786d5..0000000 --- a/modules/youtube/requester_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package youtube - -import ( - "io/ioutil" - "testing" -) - -func TestPlayground(t *testing.T) { - youtubeTokenPath := "../../tokens/.youtubetoken" - youtubetoken, err := ioutil.ReadFile(youtubeTokenPath) - if err != nil { - t.Errorf("cannot read youtube token: %v", err) - } - _ = youtubetoken -} - -func TestFetchID(t *testing.T) { - positiveCases := [][]string{ - { - "https://www.youtube.com/watch?v=_v5IzvVTw7A&feature=feedrec_grec_index", - "_v5IzvVTw7A", - }, - { - "https://www.youtube.com/watch?v=_v5IzvVTw7A#t=0m10s", - "_v5IzvVTw7A", - }, - { - "https://www.youtube.com/embed/_v5IzvVTw7A?rel=0", - "_v5IzvVTw7A", - }, - { - "https://www.youtube.com/watch?v=_v5IzvVTw7A ", // multiple spaces in the end - "_v5IzvVTw7A", - }, - { - "https://youtu.be/_v5IzvVTw7A", - "_v5IzvVTw7A", - }, - { - "https://www.youtube.com/watch?v=PCp2iXA1uLE&list=PLvx4lPhqncyf10ymYz8Ph8EId0cafzhdZ&index=2&t=0s", - "PCp2iXA1uLE", - }, - } - - negativeCases := []string{ - "https://youtu.be/_vIzvVTw7A", // short video - "https://vimeo.com/_v5IzvVTw7A", // incorrect domain - "youtube prime video", - } - - for _, testCase := range positiveCases { - res, err := videoID(testCase[0]) - if err != nil { - t.Errorf("expecting error: , got: <%v>, url: %s\n", err, testCase[0]) - } - if res != testCase[1] { - t.Errorf("expecting result: %s, got: %s\n", testCase[0], res) - } - } - - for _, testCase := range negativeCases { - _, err := videoID(testCase) - if err == nil { - t.Error("expecting error: , got: ") - } - } -} diff --git a/readme.md b/readme.md deleted file mode 100644 index 42a196d..0000000 --- a/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Galched Bot - -This bot is made for galched twitch and discord channel \ No newline at end of file diff --git a/songs/.gitkeep b/songs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/web/index.html b/web/index.html deleted file mode 100644 index f73dd9a..0000000 --- a/web/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - -
-
-
- - - \ No newline at end of file diff --git a/web/login.html b/web/login.html deleted file mode 100644 index c406fd2..0000000 --- a/web/login.html +++ /dev/null @@ -1,14 +0,0 @@ - - - -
-
-

- -

- - -
-
- - diff --git a/web/scripts.js b/web/scripts.js deleted file mode 100644 index 17481a0..0000000 --- a/web/scripts.js +++ /dev/null @@ -1,120 +0,0 @@ -// queue is used to play videos -var queue -// player is a iframe youtube player -var player -// vid is a current playing video id -var vid = "" - -function updateTable() { - var myTableDiv = document.getElementById("video_queue") - myTableDiv.innerHTML = "" - - var table = document.createElement('TABLE') - var tableBody = document.createElement('TBODY') - table.border = '1' - table.appendChild(tableBody); - - for (i = 0; i < queue.length; i++) { - var tr = document.createElement('TR'); - - var td = document.createElement('TD'); - td.appendChild(document.createTextNode(queue[i].Duration)); - tr.appendChild(td); - - var td = document.createElement('TD'); - td.appendChild(document.createTextNode(queue[i].Title)); - tr.appendChild(td) - - var td = document.createElement('TD'); - td.appendChild(document.createTextNode( queue[i].From)); - tr.appendChild(td) - - var td = document.createElement('TD'); - td.appendChild(document.createTextNode( queue[i].Views)); - tr.appendChild(td) - - var td = document.createElement('TD'); - td.appendChild(document.createTextNode(((queue[i].Upvotes/(queue[i].Upvotes + queue[i].Downvotes)) * 100).toFixed(2) + '%')); - tr.appendChild(td) - - tableBody.appendChild(tr); - } - - myTableDiv.appendChild(table) -} - -function bgUpdateQueue() { - initQueue() - updateTable() - - if (vid === "" && queue.length > 0) { - vid = queue[0].ID - player.loadVideoById(vid) - } -} - -function initQueue() { - var request = new XMLHttpRequest() - request.open('GET', './queue', false) - request.onload = function () { - queue = JSON.parse(this.response) - } - request.send() - updateTable() -} - -function updateQueue() { - var request = new XMLHttpRequest() - request.open('POST', './queue', false) - request.onload = function () { - queue = JSON.parse(this.response) - } - request.send(vid) - updateTable() -} - -function nextVideo() { - updateQueue() - if (queue.length > 0) { - vid = queue[0].ID - } else { - vid = "" - } - - player.loadVideoById(vid) - updateTable() -} - -function onYouTubeIframeAPIReady() { - if (queue.length != 0) { - vid = queue[0].ID - } - console.log(vid) - player = new YT.Player('player', { - height: '390', - width: '640', - videoId: vid, - events: { - 'onReady': onPlayerReady, - 'onStateChange': onPlayerStateChange - } - }); - -} - -function onPlayerReady(event) { - event.target.playVideo(); -} - -function onPlayerStateChange(event) { - if (event.data === 0) { - nextVideo() - } -} - -initQueue() -setInterval(bgUpdateQueue, 4000); -var tag = document.createElement('script'); -tag.src = "https://www.youtube.com/iframe_api"; -var firstScriptTag = document.getElementsByTagName('script')[0]; -firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); diff --git a/web/style.css b/web/style.css deleted file mode 100644 index c771414..0000000 --- a/web/style.css +++ /dev/null @@ -1,4 +0,0 @@ -body { - background-color: #000000; - color: #ffffff; -} \ No newline at end of file