commit
d3af0b0736
17 changed files with 718 additions and 0 deletions
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
body { |
||||
background: #fff; |
||||
color: #000; |
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
body { |
||||
background: #fff; |
||||
color: #000; |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
.header { |
||||
display: flex; |
||||
width: 100%; |
||||
margin: 0 auto; |
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1), /* Subtle shadow */ |
||||
0 6px 20px rgba(0, 0, 0, 0.1); /* Deeper shadow */ |
||||
border: 1px solid rgba(0, 0, 0, 0.05); /* Light border */ |
||||
} |
||||
|
||||
.logo { |
||||
width: 30px; |
||||
margin: 8px 20px; |
||||
align-self: center; |
||||
} |
||||
|
||||
.title { |
||||
flex: 1; |
||||
font-family: "Helvetica Neue", "Helvetica", serif; |
||||
margin: 0; |
||||
align-self: center; |
||||
font-size: 20px; |
||||
} |
||||
|
||||
.login-container { |
||||
align-self: center; |
||||
margin-right: 20px |
||||
} |
||||
|
||||
.twitch-login { |
||||
font-size: 14px; |
||||
padding: 5px 10px; |
||||
background-color: #9147ff; |
||||
color: white; |
||||
border: none; |
||||
border-radius: 5px; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.banner { |
||||
width: 100%; |
||||
height:400px; |
||||
background-image: url("../images/banner.jpg"); |
||||
background-repeat: no-repeat; |
||||
margin: 0 auto; |
||||
background-position: center; |
||||
background-size: 100%; |
||||
} |
||||
|
After Width: | Height: | Size: 540 KiB |
|
After Width: | Height: | Size: 47 KiB |
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
package db |
||||
|
||||
import ( |
||||
"database/sql" |
||||
"fmt" |
||||
"github.com/jmoiron/sqlx" |
||||
_ "github.com/tursodatabase/go-libsql" |
||||
) |
||||
|
||||
type Database interface { |
||||
Query(query string, args ...interface{}) (*sqlx.Rows, error) |
||||
Exec(query string, args ...interface{}) (sql.Result, error) |
||||
Close() error |
||||
} |
||||
|
||||
type DBClient struct { |
||||
db *sqlx.DB |
||||
} |
||||
|
||||
func NewDbClient(dsn string) (Database, error) { |
||||
db, err := sqlx.Open("libsql", dsn) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("failed to open db: %w", err) |
||||
} |
||||
|
||||
fmt.Println("connected to db") |
||||
return &DBClient{db: db}, nil |
||||
} |
||||
|
||||
func (db *DBClient) Query(query string, args ...interface{}) (*sqlx.Rows, error) { |
||||
return db.db.Queryx(query, args...) |
||||
} |
||||
|
||||
func (db *DBClient) Exec(query string, args ...interface{}) (sql.Result, error) { |
||||
return db.db.Exec(query, args...) |
||||
} |
||||
|
||||
func (db *DBClient) Close() error { |
||||
return db.db.Close() |
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
module sponsorahacker |
||||
|
||||
go 1.22 |
||||
|
||||
require ( |
||||
github.com/gin-gonic/gin v1.10.0 |
||||
github.com/jmoiron/sqlx v1.4.0 |
||||
github.com/markbates/goth v1.80.0 |
||||
github.com/nicklaw5/helix v1.25.0 |
||||
github.com/tursodatabase/go-libsql v0.0.0-20241011135853-3effbb6dea5c |
||||
) |
||||
|
||||
require ( |
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect |
||||
github.com/bytedance/sonic v1.11.6 // indirect |
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect |
||||
github.com/cloudwego/base64x v0.1.4 // indirect |
||||
github.com/cloudwego/iasm v0.2.0 // indirect |
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect |
||||
github.com/gin-contrib/sse v0.1.0 // indirect |
||||
github.com/go-playground/locales v0.14.1 // indirect |
||||
github.com/go-playground/universal-translator v0.18.1 // indirect |
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect |
||||
github.com/goccy/go-json v0.10.2 // indirect |
||||
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect |
||||
github.com/golang/protobuf v1.5.3 // indirect |
||||
github.com/gorilla/context v1.1.1 // indirect |
||||
github.com/gorilla/mux v1.6.2 // indirect |
||||
github.com/gorilla/securecookie v1.1.1 // indirect |
||||
github.com/gorilla/sessions v1.1.1 // indirect |
||||
github.com/json-iterator/go v1.1.12 // indirect |
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect |
||||
github.com/kr/text v0.2.0 // indirect |
||||
github.com/leodido/go-urn v1.4.0 // indirect |
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 // indirect |
||||
github.com/mattn/go-isatty v0.0.20 // indirect |
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect |
||||
github.com/modern-go/reflect2 v1.0.2 // indirect |
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect |
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect |
||||
github.com/ugorji/go/codec v1.2.12 // indirect |
||||
golang.org/x/arch v0.8.0 // indirect |
||||
golang.org/x/crypto v0.23.0 // indirect |
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect |
||||
golang.org/x/net v0.25.0 // indirect |
||||
golang.org/x/oauth2 v0.17.0 // indirect |
||||
golang.org/x/sys v0.20.0 // indirect |
||||
golang.org/x/text v0.15.0 // indirect |
||||
google.golang.org/appengine v1.6.8 // indirect |
||||
google.golang.org/protobuf v1.34.1 // indirect |
||||
gopkg.in/yaml.v3 v3.0.1 // indirect |
||||
) |
||||
@ -0,0 +1,169 @@
@@ -0,0 +1,169 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= |
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= |
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= |
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= |
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= |
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= |
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= |
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= |
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= |
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= |
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= |
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= |
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= |
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= |
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= |
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= |
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= |
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= |
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= |
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= |
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= |
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= |
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= |
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= |
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= |
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= |
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= |
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= |
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= |
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= |
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= |
||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= |
||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= |
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= |
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= |
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= |
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= |
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= |
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= |
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= |
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= |
||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= |
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= |
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= |
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= |
||||
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE= |
||||
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= |
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= |
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= |
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= |
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= |
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= |
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= |
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= |
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= |
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= |
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= |
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= |
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= |
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= |
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= |
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= |
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= |
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM= |
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM= |
||||
github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8= |
||||
github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY= |
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= |
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= |
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= |
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= |
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= |
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= |
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= |
||||
github.com/nicklaw5/helix v1.25.0 h1:Mrz537izZVsGdM3I46uGAAlslj61frgkhS/9xQqyT/M= |
||||
github.com/nicklaw5/helix v1.25.0/go.mod h1:yvXZFapT6afIoxnAvlWiJiUMsYnoHl7tNs+t0bloAMw= |
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= |
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= |
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= |
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= |
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= |
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= |
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= |
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= |
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= |
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |
||||
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= |
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= |
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= |
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= |
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= |
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= |
||||
github.com/tursodatabase/go-libsql v0.0.0-20241011135853-3effbb6dea5c h1:a8TrFzP+zK+uYcMWuLQoNOR78SG/yISSnHwMIcyWa2Q= |
||||
github.com/tursodatabase/go-libsql v0.0.0-20241011135853-3effbb6dea5c/go.mod h1:TjsB2miB8RW2Sse8sdxzVTdeGlx74GloD5zJYUC38d8= |
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= |
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= |
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= |
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= |
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= |
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= |
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= |
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= |
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= |
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= |
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= |
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= |
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= |
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= |
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= |
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= |
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= |
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= |
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= |
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= |
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= |
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= |
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= |
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= |
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= |
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= |
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= |
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= |
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= |
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= |
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= |
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= |
||||
google.golang.org/protobuf v1.34.1/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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= |
||||
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= |
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= |
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= |
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= |
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"github.com/gin-gonic/gin" |
||||
"github.com/markbates/goth" |
||||
"github.com/markbates/goth/gothic" |
||||
"github.com/markbates/goth/providers/google" |
||||
"github.com/markbates/goth/providers/twitch" |
||||
"github.com/markbates/goth/providers/twitter" |
||||
"log" |
||||
"net/http" |
||||
"sponsorahacker/db" |
||||
) |
||||
|
||||
// route to authenticate
|
||||
// route for log
|
||||
func main() { |
||||
r := gin.Default() |
||||
r.LoadHTMLGlob("templates/*") |
||||
r.Static("/assets", "./assets") |
||||
|
||||
r.GET("/", func(c *gin.Context) { |
||||
isLoggedIn := checkIfLoggedIn(c) |
||||
fmt.Println("isLoggedIn:", isLoggedIn) |
||||
|
||||
_, err := db.NewDbClient("libsql://sponsorahacker-stephanie-gredell.turso.io") |
||||
if err != nil { |
||||
log.Fatal(err) |
||||
} |
||||
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{ |
||||
"title": "Sponsor a Hacker", |
||||
"isLoggedIn": isLoggedIn, |
||||
}) |
||||
}) |
||||
|
||||
r.GET("/apply", func(c *gin.Context) { |
||||
c.HTML(http.StatusOK, "apply.html", gin.H{ |
||||
"title": "Apply", |
||||
}) |
||||
}) |
||||
|
||||
goth.UseProviders( |
||||
twitch.New("3bozqr9birw9qcpdmoeo4my9vjuvoa", "bidyehx9w9csdqqtx95ndpa7zwrjir", "http://localhost:8080/auth/twitch/callback"), |
||||
google.New("695096681313-07o8ocke1ifpuim1c9n4k1uacbdvrb4j.apps.googleusercontent.com", "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", "http://localhost:8080/auth/google/callback"), |
||||
twitter.New("WSuqS3zaiCFEQOVwXXUq68Duj", "xuIh2dLwXO7P2UeGiBTDUmAfTPqkRdPx4PxwCP88Mj9oSXBg56", "http://localhost:8080/auth/twitter/callback"), |
||||
) |
||||
|
||||
r.Use(func(c *gin.Context) { |
||||
gothic.GetProviderName = func(req *http.Request) (string, error) { |
||||
return "twitch", nil |
||||
} |
||||
c.Next() |
||||
}) |
||||
|
||||
// do the authentication thing
|
||||
r.GET("/auth/login/:provider", func(c *gin.Context) { |
||||
gothic.BeginAuthHandler(c.Writer, c.Request) |
||||
}) |
||||
|
||||
r.GET("/auth/:provider/callback", func(c *gin.Context) { |
||||
user, err := gothic.CompleteUserAuth(c.Writer, c.Request) |
||||
if err != nil { |
||||
log.Println("Error during user authentication:", err) |
||||
c.Redirect(http.StatusTemporaryRedirect, "/homepage") |
||||
return |
||||
} |
||||
c.SetCookie("user_id", user.UserID, 3600, "/", "localhost", false, true) |
||||
|
||||
// For now, redirect to profile page after successful login
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/") |
||||
}) |
||||
|
||||
// do the logout thang
|
||||
r.GET("/auth/logout/:provider", func(c *gin.Context) { |
||||
c.SetCookie("user_id", "", -1, "/", "localhost", false, true) |
||||
c.Redirect(http.StatusTemporaryRedirect, "/") |
||||
}) |
||||
|
||||
err := r.Run(":8080") |
||||
|
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
func checkIfLoggedIn(c *gin.Context) bool { |
||||
userID, err := c.Cookie("user_id") |
||||
if err != nil || userID == "" { |
||||
return false |
||||
} |
||||
|
||||
return true |
||||
} |
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
<html> |
||||
<head> |
||||
<link rel="stylesheet" type="text/css" href="assets/css/login.css" /> |
||||
</head> |
||||
<body> |
||||
<h1> |
||||
{{ .title }} |
||||
</h1> |
||||
|
||||
|
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
<html> |
||||
<head> |
||||
<link rel="stylesheet" type="text/css" href="assets/css/style.css" /> |
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> |
||||
</head> |
||||
<body> |
||||
<div class="header"> |
||||
<img src="assets/images/logo.jpg" class="logo"/> |
||||
<h1 class="title"> |
||||
{{ .title }} |
||||
</h1> |
||||
|
||||
<div class="login-container"> |
||||
{{if eq .isLoggedIn true}} |
||||
{{template "logoutButton"}} |
||||
{{ else }} |
||||
{{template "loginButton" . }} |
||||
{{ end }} |
||||
</div> |
||||
</div> |
||||
|
||||
<h2 class="goals">Reach Your Goals</h2> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
{{ define "loginButton" }} |
||||
<a href="/auth/login/twitch"> |
||||
<button class="twitch-login"> |
||||
Login |
||||
</button> |
||||
</a> |
||||
{{ end }} |
||||
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
<html> |
||||
<head> |
||||
<link rel="stylesheet" type="text/css" href="assets/css/style.css" /> |
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> |
||||
</head> |
||||
<body> |
||||
<div class="login-container"> |
||||
<h2>Login with</h2> |
||||
<a href="/auth/login/twitch" class="login-button twitch"> |
||||
<i class="fab fa-twitch"></i> Twitch |
||||
</a> |
||||
<a href="/auth/login/google" class="login-button google"> |
||||
<i class="fab fa-google"></i> Google |
||||
</a> |
||||
<a href="/auth/login/x" class="login-button x"> |
||||
<i class="fab fa-twitter"></i> X |
||||
</a> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
|
||||
{{ define "logoutButton" }} |
||||
<a href="/auth/logout/twitch"> |
||||
<button style="font-size: 18px; padding: 10px 20px; background-color: #9147ff; color: white; border: none; border-radius: 5px; cursor: pointer;"> |
||||
Logout |
||||
</button> |
||||
</a> |
||||
{{ end }} |
||||
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
<html> |
||||
<head> |
||||
<link rel="stylesheet" type="text/css" href="assets/css/login.css" /> |
||||
</head> |
||||
<body> |
||||
<h1> |
||||
{{ .title }} |
||||
</h1> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,133 @@
@@ -0,0 +1,133 @@
|
||||
package twitch |
||||
|
||||
import ( |
||||
"fmt" |
||||
"github.com/nicklaw5/helix" |
||||
) |
||||
|
||||
type TwitchClient struct { |
||||
client *helix.Client |
||||
} |
||||
|
||||
type User struct { |
||||
ID string |
||||
Username string |
||||
DisplayName string |
||||
Description string |
||||
ProfileImageURL string |
||||
Following []User |
||||
Subscriptions []Subscription |
||||
} |
||||
|
||||
type Subscription struct { |
||||
UserID string |
||||
UserName string |
||||
ChannelID string |
||||
ChannelName string |
||||
Tier string // Subscription tier (e.g., "1000", "2000", etc.)
|
||||
} |
||||
|
||||
type EventSubscription struct { |
||||
UserID string |
||||
UserLogin string |
||||
isGift bool |
||||
tier int |
||||
} |
||||
|
||||
func NewTwitchClient(clientID string, clientSecret string) (*TwitchClient, error) { |
||||
client, err := helix.NewClient(&helix.Options{ |
||||
ClientID: clientID, |
||||
ClientSecret: clientSecret, |
||||
}) |
||||
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return &TwitchClient{client: client}, nil |
||||
} |
||||
|
||||
func (t *TwitchClient) GetUser(username string) (*User, error) { |
||||
// get the user
|
||||
user, err := t.client.GetUsers(&helix.UsersParams{ |
||||
Logins: []string{username}, |
||||
}) |
||||
|
||||
if err != nil { |
||||
fmt.Println("error getting user") |
||||
return nil, err |
||||
} |
||||
|
||||
if len(user.Data.Users) == 0 { |
||||
return nil, fmt.Errorf("user not found") |
||||
} |
||||
|
||||
helixUser := user.Data.Users[0] |
||||
|
||||
// get the people this user follows
|
||||
followResp, err := t.client.GetUsersFollows(&helix.UsersFollowsParams{ |
||||
FromID: helixUser.ID, |
||||
}) |
||||
|
||||
followedUsers := make([]User, 0) |
||||
|
||||
for i, follow := range followResp.Data.Follows { |
||||
followedUsers[i] = User{ |
||||
ID: follow.ToID, |
||||
Username: follow.ToName, |
||||
} |
||||
} |
||||
|
||||
// get the user subscriptions
|
||||
subscriptionResp, err := t.client.GetSubscriptions(&helix.SubscriptionsParams{ |
||||
UserID: []string{helixUser.ID}, |
||||
}) |
||||
|
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
subscriptions := make([]Subscription, len(subscriptionResp.Data.Subscriptions)) |
||||
|
||||
for i, sub := range subscriptionResp.Data.Subscriptions { |
||||
subscriptions[i] = Subscription{ |
||||
UserID: sub.UserID, |
||||
UserName: sub.UserName, |
||||
ChannelID: sub.BroadcasterID, |
||||
ChannelName: sub.BroadcasterName, |
||||
Tier: sub.Tier, |
||||
} |
||||
} |
||||
|
||||
// build the custom user object that has all the things
|
||||
customUser := &User{ |
||||
ID: helixUser.ID, |
||||
Username: helixUser.Login, |
||||
DisplayName: helixUser.DisplayName, |
||||
Description: helixUser.Description, |
||||
ProfileImageURL: helixUser.ProfileImageURL, |
||||
Following: followedUsers, |
||||
Subscriptions: subscriptions, |
||||
} |
||||
|
||||
return customUser, nil |
||||
} |
||||
|
||||
func (t *TwitchClient) CreateEventSubscription(broadcasterID string) error { |
||||
_, err := t.client.CreateEventSubSubscription(&helix.EventSubSubscription{ |
||||
Type: "channel.subscribe", |
||||
Version: "1", |
||||
Condition: helix.EventSubCondition{BroadcasterUserID: broadcasterID}, |
||||
Transport: helix.EventSubTransport{ |
||||
Method: "webhook", |
||||
Callback: "http://localhost:8080/webhook", |
||||
Secret: "secret", |
||||
}, |
||||
}) |
||||
|
||||
if err != nil { |
||||
fmt.Println("error creating event subscription") |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
package twitter |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"net/http" |
||||
) |
||||
|
||||
type TwitterClient interface { |
||||
GetUsername(userId string) (string, error) |
||||
GetFollowing(userId string) ([]User, error) |
||||
} |
||||
|
||||
type Client struct { |
||||
httpClient *http.Client |
||||
bearerToken string |
||||
baseURL string |
||||
} |
||||
|
||||
type User struct { |
||||
ID string |
||||
Username string |
||||
Name string |
||||
} |
||||
|
||||
func NewClient(bearerToken string) *Client { |
||||
return &Client{ |
||||
httpClient: &http.Client{}, |
||||
bearerToken: bearerToken, |
||||
baseURL: "https://api.twitter.com", |
||||
} |
||||
} |
||||
|
||||
func (c *Client) GetUsername(userId string) (string, error) { |
||||
url := c.baseURL + "/users/" + userId |
||||
req, err := http.NewRequest("GET", url, nil) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
req.Header.Set("Authorization", "Bearer "+c.bearerToken) |
||||
|
||||
resp, err := c.httpClient.Do(req) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
defer resp.Body.Close() |
||||
|
||||
var userResponse struct { |
||||
Data User |
||||
} |
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&userResponse); err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
return userResponse.Data.Username, nil |
||||
} |
||||
|
||||
func (c *Client) GetFollowing(userId string) ([]User, error) { |
||||
url := c.baseURL + "/users/" + userId + "/following" |
||||
req, err := http.NewRequest("GET", url, nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
req.Header.Set("Authorization", "Bearer "+c.bearerToken) |
||||
|
||||
resp, err := c.httpClient.Do(req) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
defer func() { |
||||
if cerr := resp.Body.Close(); cerr != nil { |
||||
err = cerr |
||||
} |
||||
}() |
||||
|
||||
var followingResponse struct { |
||||
Data []User |
||||
} |
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&followingResponse); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return followingResponse.Data, nil |
||||
} |
||||
Loading…
Reference in new issue