From 324d45803bec2d55580deb1b0b2d3362577bd9fe Mon Sep 17 00:00:00 2001 From: danluki Date: Tue, 26 Mar 2024 17:57:55 +0500 Subject: [PATCH 1/7] feat: nightbot integration migration --- libs/gomodels/integrations.go | 1 + .../20240325093234_nightbot_integraion.sql | 10 ++++++ libs/migrations/seeds/integrations.go | 31 ++++++++++++++++--- 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 libs/migrations/migrations/20240325093234_nightbot_integraion.sql diff --git a/libs/gomodels/integrations.go b/libs/gomodels/integrations.go index d024f60d4..b172ebb80 100755 --- a/libs/gomodels/integrations.go +++ b/libs/gomodels/integrations.go @@ -44,6 +44,7 @@ const ( IntegrationServiceDonatello IntegrationService = "DONATELLO" IntegrationServiceValorant IntegrationService = "VALORANT" IntegrationServiceDonateStream IntegrationService = "DONATE_STREAM" + IntegrationServiceNightbot IntegrationService = "NIGHTBOT" ) func (c IntegrationService) String() string { diff --git a/libs/migrations/migrations/20240325093234_nightbot_integraion.sql b/libs/migrations/migrations/20240325093234_nightbot_integraion.sql new file mode 100644 index 000000000..3202f407a --- /dev/null +++ b/libs/migrations/migrations/20240325093234_nightbot_integraion.sql @@ -0,0 +1,10 @@ +-- +goose Up +-- +goose StatementBegin +SELECT 'up SQL query'; +ALTER TYPE integrations_service_enum ADD VALUE 'NIGHTBOT'; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +SELECT 'down SQL query'; +-- +goose StatementEnd diff --git a/libs/migrations/seeds/integrations.go b/libs/migrations/seeds/integrations.go index cdb56cb8a..d5448d894 100644 --- a/libs/migrations/seeds/integrations.go +++ b/libs/migrations/seeds/integrations.go @@ -2,27 +2,48 @@ package seeds import ( "database/sql" - cfg "github.com/satont/twir/libs/config" "log/slog" + + cfg "github.com/satont/twir/libs/config" ) func CreateIntegrations(db *sql.DB, config *cfg.Config) error { - _, err := db.Query(`INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, "DONATEPAY") + _, err := db.Query( + `INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, + "DONATEPAY", + ) + if err != nil { + return err + } + + _, err = db.Query( + `INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, + "VALORANT", + ) if err != nil { return err } - _, err = db.Query(`INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, "VALORANT") + _, err = db.Query( + `INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, + "DONATE_STREAM", + ) if err != nil { return err } - _, err = db.Query(`INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, "DONATE_STREAM") + _, err = db.Query( + `INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, + "DONATELLO", + ) if err != nil { return err } - _, err = db.Query(`INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, "DONATELLO") + _, err = db.Query( + `INSERT INTO integrations (service) VALUES ($1) ON CONFLICT DO NOTHING`, + "NIGHTBOT", + ) if err != nil { return err } From b00fa57dd5fdcbee41dcb566503edbde3bc32cf7 Mon Sep 17 00:00:00 2001 From: danluki Date: Tue, 26 Mar 2024 18:01:57 +0500 Subject: [PATCH 2/7] feat: nightbot integration backend --- apps/api/go.mod | 4 + apps/api/go.sum | 29 ++ .../impl_protected/integrations/nightbot.go | 391 ++++++++++++++++++ apps/integrations/src/index.js | 12 + apps/integrations/src/libs/db.js | 1 + apps/integrations/src/services/nightbot.js | 33 ++ apps/integrations/src/store/nightbot.js | 68 +++ go.work.sum | 7 +- libs/api/api.proto | 7 + .../integrations_nightbot.proto | 22 + 10 files changed, 569 insertions(+), 5 deletions(-) create mode 100644 apps/api/internal/impl_protected/integrations/nightbot.go create mode 100644 apps/integrations/src/services/nightbot.js create mode 100644 apps/integrations/src/store/nightbot.js create mode 100644 libs/api/messages/integrations_nightbot/integrations_nightbot.proto diff --git a/apps/api/go.mod b/apps/api/go.mod index 44fa5fc84..2011085a7 100644 --- a/apps/api/go.mod +++ b/apps/api/go.mod @@ -76,6 +76,9 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.7 // indirect @@ -136,6 +139,7 @@ require ( require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/jackc/pgconn v1.14.3 github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/pgx/v5 v5.5.2 // indirect diff --git a/apps/api/go.sum b/apps/api/go.sum index 9c24eee80..fb5bc554c 100644 --- a/apps/api/go.sum +++ b/apps/api/go.sum @@ -66,10 +66,12 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= +github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= @@ -85,8 +87,17 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/imroc/req/v3 v3.42.3 h1:ryPG2AiwouutAopwPxKpWKyxgvO8fB3hts4JXlh3PaE= github.com/imroc/req/v3 v3.42.3/go.mod h1:Axz9Y/a2b++w5/Jht3IhQsdBzrG1ftJd1OJhu21bB2Q= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA= @@ -104,6 +115,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= @@ -198,6 +210,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -215,6 +228,7 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4 go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1 h1:m9ReioVPIffxjJlGNRd0d5poy+9oTro3D+YbiEzUDOc= go.opentelemetry.io/contrib/instrumentation/runtime v0.46.1/go.mod h1:CANkrsXNzqOKXfOomu2zhOmc1/J5UZK9SGjrat6ZCG0= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= @@ -224,10 +238,13 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -241,13 +258,18 @@ go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -257,18 +279,25 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/apps/api/internal/impl_protected/integrations/nightbot.go b/apps/api/internal/impl_protected/integrations/nightbot.go new file mode 100644 index 000000000..bf24b55e9 --- /dev/null +++ b/apps/api/internal/impl_protected/integrations/nightbot.go @@ -0,0 +1,391 @@ +package integrations + +import ( + "context" + "errors" + "fmt" + "net/url" + "strings" + + "github.com/google/uuid" + "github.com/guregu/null" + "github.com/imroc/req/v3" + "github.com/jackc/pgx/v5/pgconn" + "github.com/lib/pq" + "github.com/samber/lo" + model "github.com/satont/twir/libs/gomodels" + "github.com/twirapp/twir/libs/api/messages/integrations_nightbot" + "google.golang.org/protobuf/types/known/emptypb" +) + +type nightbotTokensResponse struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + Scope string `json:"scope"` +} + +type nightbotChannelResponse struct { + User struct { + DisplayName string `json:"displayName"` + Avatar string `json:"avatar"` + } `json:"user"` +} + +type nightbotCustomCommandsResponse struct { + Commands []struct { + Name string `json:"name"` + Message string `json:"message"` + CoolDown int `json:"coolDown"` + Count int `json:"count"` + UserLevel string `json:"userLevel"` + } `json:"commands"` + TotalCount int `json:"_total"` +} + +func (c *Integrations) IntegrationsNightbotImportCommands( + ctx context.Context, + _ *emptypb.Empty, +) (*integrations_nightbot.ImportCommandsResponse, error) { + dashboardId := ctx.Value("dashboardId").(string) + + integration, err := c.getChannelIntegrationByService( + ctx, + model.IntegrationServiceNightbot, + dashboardId, + ) + if err != nil { + return nil, err + } + + if !integration.AccessToken.Valid { + return nil, errors.New("enable nightbot integration first") + } + + commandsData := nightbotCustomCommandsResponse{} + resp, err := req.R(). + SetContext(ctx). + SetBearerAuthToken(integration.AccessToken.String). + SetSuccessResult(&commandsData). + Get("https://api.nightbot.tv/1/commands") + if err != nil { + return nil, err + } + if !resp.IsSuccessState() { + return nil, fmt.Errorf("nightbot integration error: %s", resp.String()) + } + + if len(commandsData.Commands) == 0 { + return &integrations_nightbot.ImportCommandsResponse{ + ImportedCount: 0, + FailedCount: 0, + FailedCommandsNames: []string{}, + }, nil + } + + twirRoles := []model.ChannelRole{} + err = c.Db.Where(`"channelId" = ?`, dashboardId).Find(&twirRoles).Error + if err != nil { + return nil, errors.New("twir internal error") + } + broadcasterRole, ok := lo.Find(twirRoles, func(r model.ChannelRole) bool { + return r.Name == "BROADCASTER" + }) + if !ok { + return nil, errors.New("twir internal error") + } + moderatorRole, ok := lo.Find(twirRoles, func(r model.ChannelRole) bool { + return r.Name == "MODERATOR" + }) + if !ok { + return nil, errors.New("twir internal error") + } + subscriberRole, ok := lo.Find(twirRoles, func(r model.ChannelRole) bool { + return r.Name == "SUBSCRIBER" + }) + if !ok { + return nil, errors.New("twir internal error") + } + vipRole, ok := lo.Find(twirRoles, func(r model.ChannelRole) bool { + return r.Name == "VIP" + }) + if !ok { + return nil, errors.New("twir internal error") + } + + importedCount := 0 + failedCount := 0 + failedCommandsNames := []string{} + for _, command := range commandsData.Commands { + commandName := strings.ToLower(command.Name) + if command.Name[0] == '!' { + commandName = commandName[1:] + } + commandRoles := []string{} + commandResponse := command.Message + + twirCommand := model.ChannelsCommands{} + err = c.Db.Where(`"channelId" = ? AND "name" = ?`, dashboardId, commandName). + Find(&twirCommand). + Error + if err != nil { + return nil, err + } + + if twirCommand.ID != "" { + failedCount++ + failedCommandsNames = append( + failedCommandsNames, + command.Name+" (command with this name already exists)", + ) + continue + } + + switch command.UserLevel { + case "admin": + failedCount++ + failedCommandsNames = append( + failedCommandsNames, + command.Name+" (command userlevel is not supported)", + ) + continue + case "owner": + commandRoles = append(commandRoles, broadcasterRole.ID) + case "moderator": + commandRoles = append(commandRoles, broadcasterRole.ID, moderatorRole.ID) + case "twitch_vip": + commandRoles = append(commandRoles, broadcasterRole.ID, moderatorRole.ID, vipRole.ID) + case "regular": + failedCount++ + failedCommandsNames = append( + failedCommandsNames, + command.Name+" (command userlevel is not supported)", + ) + continue + case "subscriber": + commandRoles = append( + commandRoles, + broadcasterRole.ID, + moderatorRole.ID, + subscriberRole.ID, + ) + case "everyone": + commandRoles = []string{} + case "default": + failedCount++ + failedCommandsNames = append( + failedCommandsNames, + command.Name+" (command userlevel is not supported)", + ) + } + + newCommand := model.ChannelsCommands{ + ID: uuid.NewString(), + Name: commandName, + Cooldown: null.IntFrom(int64(command.CoolDown)), + CooldownType: "GLOBAL", + Default: false, + DefaultName: null.String{}, + Module: "CUSTOM", + IsReply: true, + KeepResponsesOrder: true, + DeniedUsersIDS: []string{}, + AllowedUsersIDS: []string{}, + RolesIDS: commandRoles, + OnlineOnly: false, + RequiredWatchTime: 0, + RequiredMessages: 0, + RequiredUsedChannelPoints: 0, + Responses: make( + []*model.ChannelsCommandsResponses, + 0, + 1, + ), + GroupID: null.String{}, + EnabledCategories: pq.StringArray{}, + CooldownRolesIDs: pq.StringArray{}, + Enabled: true, + Aliases: pq.StringArray{}, + Visible: true, + ChannelID: dashboardId, + Description: null.String{}, + } + + newCommand.Responses = append(newCommand.Responses, &model.ChannelsCommandsResponses{ + ID: uuid.NewString(), + Text: null.StringFrom(commandResponse), + Order: 0, + }) + + err = c.Db.WithContext(ctx).Create(&newCommand).Error + if err != nil { + if pgerr, ok := err.(*pgconn.PgError); ok { + if pgerr.Code == "23505" { + failedCount++ + failedCommandsNames = append( + failedCommandsNames, + command.Name+" (command with this name already exists)", + ) + + continue + } + } + + return nil, err + } + importedCount++ + } + + return &integrations_nightbot.ImportCommandsResponse{ + ImportedCount: int32(importedCount), + FailedCount: int32(failedCount), + FailedCommandsNames: failedCommandsNames, + }, nil +} + +func (c *Integrations) IntegrationsNightbotGetAuthLink( + ctx context.Context, + _ *emptypb.Empty, +) (*integrations_nightbot.GetAuthLink, error) { + integration, err := c.getIntegrationByService(ctx, model.IntegrationServiceNightbot) + if err != nil { + return nil, err + } + + if !integration.ClientID.Valid || !integration.ClientSecret.Valid || + !integration.RedirectURL.Valid { + return nil, errors.New("nightbot not enabled on our side, please be patient") + } + + link, _ := url.Parse("https://api.nightbot.tv/oauth2/authorize") + + q := link.Query() + q.Add("response_type", "code") + q.Add("client_id", integration.ClientID.String) + q.Add("scope", "commands commands_default timers regulars spam_protection") + q.Add("redirect_uri", integration.RedirectURL.String) + link.RawQuery = q.Encode() + + return &integrations_nightbot.GetAuthLink{ + Link: link.String(), + }, nil +} + +func (c *Integrations) IntegrationsNightbotGetData( + ctx context.Context, + _ *emptypb.Empty, +) (*integrations_nightbot.GetDataResponse, error) { + dashboardId := ctx.Value("dashboardId").(string) + integration, err := c.getChannelIntegrationByService( + ctx, + model.IntegrationServiceNightbot, + dashboardId, + ) + if err != nil { + return nil, err + } + + return &integrations_nightbot.GetDataResponse{ + UserName: integration.Data.UserName, + Avatar: integration.Data.Avatar, + }, nil +} + +func (c *Integrations) IntegrationsNightbotPostCode( + ctx context.Context, + request *integrations_nightbot.PostCodeRequest, +) (*emptypb.Empty, error) { + dashboardId := ctx.Value("dashboardId").(string) + channelIntegration, err := c.getChannelIntegrationByService( + ctx, + model.IntegrationServiceNightbot, + dashboardId, + ) + if err != nil { + return nil, err + } + + tokensData := nightbotTokensResponse{} + resp, err := req.R(). + SetContext(ctx). + SetFormData( + map[string]string{ + "grant_type": "authorization_code", + "client_id": channelIntegration.Integration.ClientID.String, + "client_secret": channelIntegration.Integration.ClientSecret.String, + "redirect_uri": channelIntegration.Integration.RedirectURL.String, + "code": request.Code, + }, + ). + SetSuccessResult(&tokensData). + SetContentType("application/x-www-form-urlencoded"). + Post("https://api.nightbot.tv/oauth2/token") + if err != nil { + return nil, err + } + if !resp.IsSuccessState() { + return nil, fmt.Errorf("nightbot token request failed: %s", resp.String()) + } + + channelData := &nightbotChannelResponse{} + resp, err = req.R(). + SetContext(ctx). + SetSuccessResult(channelData). + SetBearerAuthToken(tokensData.AccessToken). + Get("https://api.nightbot.tv/1/me") + if err != nil { + return nil, err + } + if !resp.IsSuccessState() { + return nil, fmt.Errorf("nightbot token request failed: %s", resp.String()) + } + + channelIntegration.Data = &model.ChannelsIntegrationsData{ + UserName: &channelData.User.DisplayName, + Avatar: &channelData.User.Avatar, + } + channelIntegration.AccessToken = null.StringFrom(tokensData.AccessToken) + channelIntegration.RefreshToken = null.StringFrom(tokensData.RefreshToken) + channelIntegration.Enabled = true + + if err = c.Db.WithContext(ctx).Save(channelIntegration).Error; err != nil { + return nil, err + } + + if err = c.sendGrpcEvent(ctx, channelIntegration.ID, channelIntegration.Enabled); err != nil { + return nil, err + } + + return &emptypb.Empty{}, nil +} + +func (c *Integrations) IntegrationsNightbotLogout( + ctx context.Context, + empty *emptypb.Empty, +) (*emptypb.Empty, error) { + dashboardId := ctx.Value("dashboardId").(string) + integration, err := c.getChannelIntegrationByService( + ctx, + model.IntegrationServiceNightbot, + dashboardId, + ) + if err != nil { + return nil, err + } + + integration.Data = &model.ChannelsIntegrationsData{} + integration.AccessToken = null.String{} + integration.RefreshToken = null.String{} + integration.Enabled = false + + if err = c.Db.WithContext(ctx).Save(&integration).Error; err != nil { + return nil, err + } + + if err = c.sendGrpcEvent(ctx, integration.ID, integration.Enabled); err != nil { + return nil, err + } + + return &emptypb.Empty{}, nil +} diff --git a/apps/integrations/src/index.js b/apps/integrations/src/index.js index 072e560e1..9e9736e6d 100644 --- a/apps/integrations/src/index.js +++ b/apps/integrations/src/index.js @@ -5,6 +5,7 @@ import { createServer } from 'nice-grpc'; import { getIntegrations, Services } from './libs/db.js'; import { addIntegration as addDonatePayIntegration, removeIntegration as removeDonatePayIntegration } from './store/donatePay.js'; import { addIntegration as addDonationAlertsIntegration, removeIntegration as removeDonationAlertsIntegration } from './store/donationAlerts.js'; +import { addIntegration as addNightbotIntegration, removeIntegration as removeNightbotIntegration } from './store/nightbot.js'; import { addIntegration as addStreamlabsIntegration, removeIntegration as removeStreamlabsIntegration } from './store/streamlabs.js'; import './pubsub.js'; @@ -23,6 +24,10 @@ for (const integration of integrations) { if (integration.integration.service === Services.DONATEPAY) { addDonatePayIntegration(integration); } + + if (integration.integration.service === Services.NIGHTBOT) { + addNightbotIntegration(integration); + } } /** @@ -47,6 +52,9 @@ const integrationsServer = { if (integration.integration.service === Services.DONATEPAY) { await addDonatePayIntegration(integration); } + if (integration.integration.service === Services.NIGHTBOT) { + await addNightbotIntegration(integration); + } return {}; }, @@ -88,6 +96,10 @@ export async function removeIntegration(integration) { if (integration.integration.service === Services.DONATEPAY) { removeDonatePayIntegration(integration.channelId); } + + if (integration.integration.service === Services.NIGHTBOT) { + removeNightbotIntegration(integration.channelId); + } } process.on('uncaughtException', console.error); diff --git a/apps/integrations/src/libs/db.js b/apps/integrations/src/libs/db.js index 477f20469..1d3a5980d 100644 --- a/apps/integrations/src/libs/db.js +++ b/apps/integrations/src/libs/db.js @@ -10,6 +10,7 @@ export const Services = Object.freeze({ DONATIONALERTS: 'DONATIONALERTS', STREAMLABS: 'STREAMLABS', DONATEPAY: 'DONATEPAY', + NIGHTBOT: 'NIGHTBOT', }); /** diff --git a/apps/integrations/src/services/nightbot.js b/apps/integrations/src/services/nightbot.js new file mode 100644 index 000000000..8ffbf6810 --- /dev/null +++ b/apps/integrations/src/services/nightbot.js @@ -0,0 +1,33 @@ +export class Nightbot { + + accessToken; + + /** + * + * @param {string} accessToken + */ + constructor(accessToken) { + this.accessToken = accessToken; + } + + async getCustomCommands() { + const req = await fetch('https://api.nightbot.tv/1/commands', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${this.accessToken}`, + }, + }); + + const data = await req.json(); + + if (!data.commands) { + throw new Error('incorrect response'); + } + + return data.commands; + } + + async destroy() { + + } +} diff --git a/apps/integrations/src/store/nightbot.js b/apps/integrations/src/store/nightbot.js new file mode 100644 index 000000000..6124fa71a --- /dev/null +++ b/apps/integrations/src/store/nightbot.js @@ -0,0 +1,68 @@ +import { db } from '../libs/db.js'; +import { Nightbot } from '../services/nightbot.js'; + +/** + * + * @type {Map} + */ +export const nightbotStore = new Map(); + +/** + * + * @param {Integration} integration + */ +export const addIntegration = async (integration) => { + if ( + !integration.accessToken || + !integration.refreshToken || + !integration.integration || + !integration.integration.clientId || + !integration.integration.clientSecret || + !integration.integration.redirectUrl + ) { + return; + } + + removeIntegration(integration.channelId); + + const refresh = await fetch('https://api.nightbot.tv/oauth2/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'refresh_token', + refresh_token: integration.refreshToken, + redirect_url: integration.integration.redirectUrl, + client_id: integration.integration.clientId, + client_secret: integration.integration.clientSecret, + }).toString(), + }); + + if (!refresh.ok) { + console.error(await refresh.text()); + return; + } + + const refreshResponse = await refresh.json(); + + await db('channels_integrations').where('id', integration.id).update({ + accessToken: refreshResponse.access_token, + refreshToken: refreshResponse.refresh_token, + }); + + const instance = new Nightbot(refreshResponse.access_token); + + return instance; +}; + +/** + * + * @param {string} channelId + */ +export const removeIntegration = async (channelId) => { + const existed = nightbotStore.get(channelId); + if (!existed) return; + await existed.destroy(); + nightbotStore.delete(channelId); +}; diff --git a/go.work.sum b/go.work.sum index e3eec13ec..8b5103e0c 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1097,7 +1097,6 @@ github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQy github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/trillian v1.5.2 h1:roGP6G8aaAch7vP08+oitPkvmZzxjTfIkguozqJ04Ok= @@ -1221,6 +1220,8 @@ github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5Uybo github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= github.com/jackc/puddle/v2 v2.1.2 h1:0f7vaaXINONKTsxYDn4otOAiJanX/BMeAtY//BXqzlg= @@ -1743,10 +1744,8 @@ go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0/go.mod h1:UFG7EBM go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= @@ -1757,7 +1756,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5 h1:+hE86LblG4AyDgwMCLTE6FOlM9+qjHSYS+rKqxUVdsM= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d h1:E2M5QgjZ/Jg+ObCQAudsXxuTsLj7Nl5RV/lZcQZmKSo= @@ -1847,7 +1845,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go. google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= -google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc h1:g3hIDl0jRNd9PPTs2uBzYuaD5mQuwOkZY0vSc0LR32o= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231212172506-995d672761c0 h1:Y6QQt9D/syZt/Qgnz5a1y2O3WunQeeVDfS9+Xr82iFA= diff --git a/libs/api/api.proto b/libs/api/api.proto index 51c1a6e4f..e5fbe1272 100644 --- a/libs/api/api.proto +++ b/libs/api/api.proto @@ -31,6 +31,7 @@ import "messages/integrations_streamlabs/integrations_streamlabs.proto"; import "messages/integrations_vk/integrations_vk.proto"; import "messages/integrations_valorant/integrations_valorant.proto"; import "messages/integrations_discord/integrations_discord.proto"; +import "messages/integrations_nightbot/integrations_nightbot.proto"; import "messages/modules_obs_websocket/modules_obs_websocket.proto"; import "messages/modules_tts/modules_tts.proto"; @@ -126,6 +127,12 @@ service Protected { rpc IntegrationsStreamlabsPostCode(messages.integrations_streamlabs.PostCodeRequest) returns (google.protobuf.Empty) {} rpc IntegrationsStreamlabsLogout(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc IntegrationsNightbotGetAuthLink(google.protobuf.Empty) returns (messages.integrations_nightbot.GetAuthLink) {} + rpc IntegrationsNightbotGetData(google.protobuf.Empty) returns (messages.integrations_nightbot.GetDataResponse) {} + rpc IntegrationsNightbotPostCode(messages.integrations_nightbot.PostCodeRequest) returns (google.protobuf.Empty) {} + rpc IntegrationsNightbotLogout(google.protobuf.Empty) returns (google.protobuf.Empty) {} + rpc IntegrationsNightbotImportCommands(google.protobuf.Empty) returns (messages.integrations_nightbot.ImportCommandsResponse) {} + rpc IntegrationsVKGetAuthLink(google.protobuf.Empty) returns (messages.integrations_vk.GetAuthLink) {} rpc IntegrationsVKGetData(google.protobuf.Empty) returns (messages.integrations_vk.GetDataResponse) {} rpc IntegrationsVKPostCode(messages.integrations_vk.PostCodeRequest) returns (google.protobuf.Empty) {} diff --git a/libs/api/messages/integrations_nightbot/integrations_nightbot.proto b/libs/api/messages/integrations_nightbot/integrations_nightbot.proto new file mode 100644 index 000000000..be7efae07 --- /dev/null +++ b/libs/api/messages/integrations_nightbot/integrations_nightbot.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +option go_package = "github.com/twirapp/twir/libs/api/messages/integrations_nightbot"; +package messages.integrations_nightbot; + +message GetAuthLink { + string link = 1; +} + +message GetDataResponse { + optional string user_name = 1; + optional string avatar = 2; +} + +message PostCodeRequest { + string code = 1; +} + +message ImportCommandsResponse { + int32 imported_count = 1; + int32 failed_count = 2; + repeated string failed_commands_names = 3; +} From 1337fe4814d5f85473fa949d5ba67e165df78bbf Mon Sep 17 00:00:00 2001 From: danluki Date: Tue, 26 Mar 2024 18:02:06 +0500 Subject: [PATCH 3/7] feat: nightbot integration frontend --- .../dashboard/src/api/integrations/index.ts | 1 + .../src/api/integrations/nightbot.ts | 15 + .../dashboard/src/api/integrations/oauth.ts | 10 + .../src/assets/integrations/nightbot.png | Bin 0 -> 17747 bytes .../src/assets/integrations/nightbot.svg | 698 ++++++++++++++++++ .../src/components/commands/importModal.vue | 73 ++ .../src/components/commands/list.vue | 19 + .../src/components/integrations/nightbot.vue | 27 + frontend/dashboard/src/locales/en.json | 4 + frontend/dashboard/src/pages/Integrations.vue | 4 + .../src/pages/IntegrationsCallback.vue | 6 +- 11 files changed, 856 insertions(+), 1 deletion(-) create mode 100644 frontend/dashboard/src/api/integrations/nightbot.ts create mode 100644 frontend/dashboard/src/assets/integrations/nightbot.png create mode 100644 frontend/dashboard/src/assets/integrations/nightbot.svg create mode 100644 frontend/dashboard/src/components/commands/importModal.vue create mode 100644 frontend/dashboard/src/components/integrations/nightbot.vue diff --git a/frontend/dashboard/src/api/integrations/index.ts b/frontend/dashboard/src/api/integrations/index.ts index 1164472b0..6140cd6fc 100644 --- a/frontend/dashboard/src/api/integrations/index.ts +++ b/frontend/dashboard/src/api/integrations/index.ts @@ -3,3 +3,4 @@ export * from './donatello.js'; export * from './donatepay.js'; export * from './donatestream.js'; export * from './discord.js'; +export * from './nightbot.js'; diff --git a/frontend/dashboard/src/api/integrations/nightbot.ts b/frontend/dashboard/src/api/integrations/nightbot.ts new file mode 100644 index 000000000..1dbce501d --- /dev/null +++ b/frontend/dashboard/src/api/integrations/nightbot.ts @@ -0,0 +1,15 @@ +import { useMutation } from '@tanstack/vue-query'; + +import { protectedApiClient } from '@/api/twirp'; + +export const useNightbotIntegrationImporter = () => { + return { + useCommandsImporter: () => useMutation({ + mutationKey: ['integrationsNightbotImportCommands'], + mutationFn: async () => { + const call = await protectedApiClient.integrationsNightbotImportCommands({}); + return call.response; + }, + }), + }; +}; diff --git a/frontend/dashboard/src/api/integrations/oauth.ts b/frontend/dashboard/src/api/integrations/oauth.ts index 31feed5bf..0eb704df8 100644 --- a/frontend/dashboard/src/api/integrations/oauth.ts +++ b/frontend/dashboard/src/api/integrations/oauth.ts @@ -1,6 +1,8 @@ import type { RpcOptions, UnaryCall } from '@protobuf-ts/runtime-rpc'; import { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query'; +import { createIntegrationOauth } from './oauth'; + import { protectedApiClient } from '@/api/twirp.js'; type CallFunc< @@ -138,3 +140,11 @@ export const useValorantIntegration = () => createIntegrationOauth({ useLogout: protectedApiClient.integrationsValorantLogout, }); +export const useNightbotIntegration = () => createIntegrationOauth({ + integrationName: 'nightbot', + getData: protectedApiClient.integrationsNightbotGetData, + getAuthLink: protectedApiClient.integrationsNightbotGetAuthLink, + usePostCode: protectedApiClient.integrationsNightbotPostCode, + useLogout: protectedApiClient.integrationsNightbotLogout, +}); + diff --git a/frontend/dashboard/src/assets/integrations/nightbot.png b/frontend/dashboard/src/assets/integrations/nightbot.png new file mode 100644 index 0000000000000000000000000000000000000000..995f8800823f0f66c38e640c579bd863b20910bc GIT binary patch literal 17747 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 literal 0 HcmV?d00001 diff --git a/frontend/dashboard/src/assets/integrations/nightbot.svg b/frontend/dashboard/src/assets/integrations/nightbot.svg new file mode 100644 index 000000000..b1b4c5902 --- /dev/null +++ b/frontend/dashboard/src/assets/integrations/nightbot.svg @@ -0,0 +1,698 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/dashboard/src/components/commands/importModal.vue b/frontend/dashboard/src/components/commands/importModal.vue new file mode 100644 index 000000000..6f5ddeb76 --- /dev/null +++ b/frontend/dashboard/src/components/commands/importModal.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/frontend/dashboard/src/components/commands/list.vue b/frontend/dashboard/src/components/commands/list.vue index 441be3154..9e385bef3 100644 --- a/frontend/dashboard/src/components/commands/list.vue +++ b/frontend/dashboard/src/components/commands/list.vue @@ -16,6 +16,7 @@ import ColumnActions from './list/column-actions.vue'; import { type Group, isCommand, createGroups } from './list/create-groups'; import { useUserAccessFlagChecker } from '@/api/index.js'; +import ImportModal from '@/components/commands/importModal.vue'; import ManageGroups from '@/components/commands/manageGroups.vue'; import Modal from '@/components/commands/modal.vue'; import type { EditableCommand } from '@/components/commands/types.js'; @@ -149,6 +150,9 @@ const table = useVueTable({ } }, }); + +const showImportModal = ref(false); + diff --git a/frontend/dashboard/src/pages/IntegrationsCallback.vue b/frontend/dashboard/src/pages/IntegrationsCallback.vue index 6b56bd198..d6cbf89af 100644 --- a/frontend/dashboard/src/pages/IntegrationsCallback.vue +++ b/frontend/dashboard/src/pages/IntegrationsCallback.vue @@ -10,7 +10,7 @@ import { useStreamlabsIntegration, useDonationAlertsIntegration, useFaceitIntegration, - useDiscordIntegration, useValorantIntegration, + useDiscordIntegration, useValorantIntegration, useNightbotIntegration, } from '@/api/index.js'; const router = useRouter(); @@ -59,6 +59,10 @@ const integrationsHooks: { manager: useValorantIntegration(), closeWindow: true, }, + 'nightbot': { + manager: useNightbotIntegration(), + closeWindow: true, + }, }; onMounted(async () => { From 05d7f1caf0feac43b31fcf94af7b877bf0c84364 Mon Sep 17 00:00:00 2001 From: danluki Date: Tue, 26 Mar 2024 19:04:08 +0500 Subject: [PATCH 4/7] chore: miss import --- frontend/dashboard/src/api/integrations/oauth.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/dashboard/src/api/integrations/oauth.ts b/frontend/dashboard/src/api/integrations/oauth.ts index 0eb704df8..047ac7094 100644 --- a/frontend/dashboard/src/api/integrations/oauth.ts +++ b/frontend/dashboard/src/api/integrations/oauth.ts @@ -1,8 +1,6 @@ import type { RpcOptions, UnaryCall } from '@protobuf-ts/runtime-rpc'; import { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query'; -import { createIntegrationOauth } from './oauth'; - import { protectedApiClient } from '@/api/twirp.js'; type CallFunc< From fd79bd5f08007b73553509bcdc445f045f7714f8 Mon Sep 17 00:00:00 2001 From: danluki Date: Wed, 27 Mar 2024 13:59:36 +0500 Subject: [PATCH 5/7] chore: some fixes --- .../impl_protected/integrations/nightbot.go | 41 +++++++---- apps/integrations/src/index.js | 11 --- apps/integrations/src/libs/db.js | 1 - apps/integrations/src/services/nightbot.js | 33 --------- apps/integrations/src/store/nightbot.js | 68 ------------------- .../src/components/integrations/nightbot.vue | 1 + 6 files changed, 28 insertions(+), 127 deletions(-) delete mode 100644 apps/integrations/src/services/nightbot.js delete mode 100644 apps/integrations/src/store/nightbot.js diff --git a/apps/api/internal/impl_protected/integrations/nightbot.go b/apps/api/internal/impl_protected/integrations/nightbot.go index bf24b55e9..958d90cb9 100644 --- a/apps/api/internal/impl_protected/integrations/nightbot.go +++ b/apps/api/internal/impl_protected/integrations/nightbot.go @@ -13,6 +13,7 @@ import ( "github.com/jackc/pgx/v5/pgconn" "github.com/lib/pq" "github.com/samber/lo" + "github.com/satont/twir/apps/api/internal/helpers" model "github.com/satont/twir/libs/gomodels" "github.com/twirapp/twir/libs/api/messages/integrations_nightbot" "google.golang.org/protobuf/types/known/emptypb" @@ -48,7 +49,10 @@ func (c *Integrations) IntegrationsNightbotImportCommands( ctx context.Context, _ *emptypb.Empty, ) (*integrations_nightbot.ImportCommandsResponse, error) { - dashboardId := ctx.Value("dashboardId").(string) + dashboardId, err := helpers.GetSelectedDashboardIDFromContext(ctx) + if err != nil { + return nil, err + } integration, err := c.getChannelIntegrationByService( ctx, @@ -232,7 +236,13 @@ func (c *Integrations) IntegrationsNightbotImportCommands( } } - return nil, err + failedCount++ + failedCommandsNames = append( + failedCommandsNames, + command.Name+" (twir internal error)", + ) + + continue } importedCount++ } @@ -276,7 +286,11 @@ func (c *Integrations) IntegrationsNightbotGetData( ctx context.Context, _ *emptypb.Empty, ) (*integrations_nightbot.GetDataResponse, error) { - dashboardId := ctx.Value("dashboardId").(string) + dashboardId, err := helpers.GetSelectedDashboardIDFromContext(ctx) + if err != nil { + return nil, err + } + integration, err := c.getChannelIntegrationByService( ctx, model.IntegrationServiceNightbot, @@ -296,7 +310,11 @@ func (c *Integrations) IntegrationsNightbotPostCode( ctx context.Context, request *integrations_nightbot.PostCodeRequest, ) (*emptypb.Empty, error) { - dashboardId := ctx.Value("dashboardId").(string) + dashboardId, err := helpers.GetSelectedDashboardIDFromContext(ctx) + if err != nil { + return nil, err + } + channelIntegration, err := c.getChannelIntegrationByService( ctx, model.IntegrationServiceNightbot, @@ -319,7 +337,6 @@ func (c *Integrations) IntegrationsNightbotPostCode( }, ). SetSuccessResult(&tokensData). - SetContentType("application/x-www-form-urlencoded"). Post("https://api.nightbot.tv/oauth2/token") if err != nil { return nil, err @@ -353,10 +370,6 @@ func (c *Integrations) IntegrationsNightbotPostCode( return nil, err } - if err = c.sendGrpcEvent(ctx, channelIntegration.ID, channelIntegration.Enabled); err != nil { - return nil, err - } - return &emptypb.Empty{}, nil } @@ -364,7 +377,11 @@ func (c *Integrations) IntegrationsNightbotLogout( ctx context.Context, empty *emptypb.Empty, ) (*emptypb.Empty, error) { - dashboardId := ctx.Value("dashboardId").(string) + dashboardId, err := helpers.GetSelectedDashboardIDFromContext(ctx) + if err != nil { + return nil, err + } + integration, err := c.getChannelIntegrationByService( ctx, model.IntegrationServiceNightbot, @@ -383,9 +400,5 @@ func (c *Integrations) IntegrationsNightbotLogout( return nil, err } - if err = c.sendGrpcEvent(ctx, integration.ID, integration.Enabled); err != nil { - return nil, err - } - return &emptypb.Empty{}, nil } diff --git a/apps/integrations/src/index.js b/apps/integrations/src/index.js index 9e9736e6d..c454788f6 100644 --- a/apps/integrations/src/index.js +++ b/apps/integrations/src/index.js @@ -5,7 +5,6 @@ import { createServer } from 'nice-grpc'; import { getIntegrations, Services } from './libs/db.js'; import { addIntegration as addDonatePayIntegration, removeIntegration as removeDonatePayIntegration } from './store/donatePay.js'; import { addIntegration as addDonationAlertsIntegration, removeIntegration as removeDonationAlertsIntegration } from './store/donationAlerts.js'; -import { addIntegration as addNightbotIntegration, removeIntegration as removeNightbotIntegration } from './store/nightbot.js'; import { addIntegration as addStreamlabsIntegration, removeIntegration as removeStreamlabsIntegration } from './store/streamlabs.js'; import './pubsub.js'; @@ -25,9 +24,6 @@ for (const integration of integrations) { addDonatePayIntegration(integration); } - if (integration.integration.service === Services.NIGHTBOT) { - addNightbotIntegration(integration); - } } /** @@ -52,9 +48,6 @@ const integrationsServer = { if (integration.integration.service === Services.DONATEPAY) { await addDonatePayIntegration(integration); } - if (integration.integration.service === Services.NIGHTBOT) { - await addNightbotIntegration(integration); - } return {}; }, @@ -96,10 +89,6 @@ export async function removeIntegration(integration) { if (integration.integration.service === Services.DONATEPAY) { removeDonatePayIntegration(integration.channelId); } - - if (integration.integration.service === Services.NIGHTBOT) { - removeNightbotIntegration(integration.channelId); - } } process.on('uncaughtException', console.error); diff --git a/apps/integrations/src/libs/db.js b/apps/integrations/src/libs/db.js index 1d3a5980d..477f20469 100644 --- a/apps/integrations/src/libs/db.js +++ b/apps/integrations/src/libs/db.js @@ -10,7 +10,6 @@ export const Services = Object.freeze({ DONATIONALERTS: 'DONATIONALERTS', STREAMLABS: 'STREAMLABS', DONATEPAY: 'DONATEPAY', - NIGHTBOT: 'NIGHTBOT', }); /** diff --git a/apps/integrations/src/services/nightbot.js b/apps/integrations/src/services/nightbot.js deleted file mode 100644 index 8ffbf6810..000000000 --- a/apps/integrations/src/services/nightbot.js +++ /dev/null @@ -1,33 +0,0 @@ -export class Nightbot { - - accessToken; - - /** - * - * @param {string} accessToken - */ - constructor(accessToken) { - this.accessToken = accessToken; - } - - async getCustomCommands() { - const req = await fetch('https://api.nightbot.tv/1/commands', { - method: 'GET', - headers: { - 'Authorization': `Bearer ${this.accessToken}`, - }, - }); - - const data = await req.json(); - - if (!data.commands) { - throw new Error('incorrect response'); - } - - return data.commands; - } - - async destroy() { - - } -} diff --git a/apps/integrations/src/store/nightbot.js b/apps/integrations/src/store/nightbot.js deleted file mode 100644 index 6124fa71a..000000000 --- a/apps/integrations/src/store/nightbot.js +++ /dev/null @@ -1,68 +0,0 @@ -import { db } from '../libs/db.js'; -import { Nightbot } from '../services/nightbot.js'; - -/** - * - * @type {Map} - */ -export const nightbotStore = new Map(); - -/** - * - * @param {Integration} integration - */ -export const addIntegration = async (integration) => { - if ( - !integration.accessToken || - !integration.refreshToken || - !integration.integration || - !integration.integration.clientId || - !integration.integration.clientSecret || - !integration.integration.redirectUrl - ) { - return; - } - - removeIntegration(integration.channelId); - - const refresh = await fetch('https://api.nightbot.tv/oauth2/token', { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: integration.refreshToken, - redirect_url: integration.integration.redirectUrl, - client_id: integration.integration.clientId, - client_secret: integration.integration.clientSecret, - }).toString(), - }); - - if (!refresh.ok) { - console.error(await refresh.text()); - return; - } - - const refreshResponse = await refresh.json(); - - await db('channels_integrations').where('id', integration.id).update({ - accessToken: refreshResponse.access_token, - refreshToken: refreshResponse.refresh_token, - }); - - const instance = new Nightbot(refreshResponse.access_token); - - return instance; -}; - -/** - * - * @param {string} channelId - */ -export const removeIntegration = async (channelId) => { - const existed = nightbotStore.get(channelId); - if (!existed) return; - await existed.destroy(); - nightbotStore.delete(channelId); -}; diff --git a/frontend/dashboard/src/components/integrations/nightbot.vue b/frontend/dashboard/src/components/integrations/nightbot.vue index 60fa69926..ac580bbb0 100644 --- a/frontend/dashboard/src/components/integrations/nightbot.vue +++ b/frontend/dashboard/src/components/integrations/nightbot.vue @@ -10,6 +10,7 @@ const logout = manager.useLogout(); const { data: authLink } = manager.useAuthLink(); +