mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
234 lines
4.9 KiB
Go
234 lines
4.9 KiB
Go
package support
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
|
|
ghb "github.com/google/go-github/v32/github"
|
|
"github.com/shurcooL/githubv4"
|
|
"golang.org/x/oauth2"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
var sponsorQuery struct {
|
|
User struct {
|
|
SponsorshipsAsSponsor struct {
|
|
Nodes []struct {
|
|
Sponsorable struct {
|
|
SponsorsListing struct {
|
|
Slug string
|
|
}
|
|
}
|
|
}
|
|
} `graphql:"sponsorshipsAsSponsor(first: 10)"`
|
|
} `graphql:"user(login: $loginName)"`
|
|
}
|
|
|
|
// GitHubUser represents a GitHub user account as defined by a GitHub API access key
|
|
// This is used to determine whether or not the WTF user is a sponsor (via GitHub sponsors)
|
|
// and/or a contributor to WTF
|
|
type GitHubUser struct {
|
|
apiKey string
|
|
|
|
loginName string
|
|
|
|
clientV3 *ghb.Client
|
|
clientV4 *githubv4.Client
|
|
|
|
IsContributor bool
|
|
IsSponsor bool
|
|
}
|
|
|
|
// NewGitHubUser creates and returns an instance of GitHub user with the boolean fields
|
|
// populated
|
|
func NewGitHubUser(githubAPIKey string) *GitHubUser {
|
|
ghUser := GitHubUser{
|
|
apiKey: githubAPIKey,
|
|
|
|
clientV3: nil,
|
|
clientV4: nil,
|
|
|
|
loginName: "",
|
|
|
|
IsContributor: false,
|
|
IsSponsor: false,
|
|
}
|
|
|
|
if ghUser.hasAPIKey() {
|
|
// Use the v3 API to get the contributors because this doesn't seem to be supported by the v4 API yet
|
|
clientV3, _ := ghUser.authenticateV3()
|
|
ghUser.clientV3 = clientV3
|
|
|
|
// Use the v4 API to get sponsors because this doesn't seem to be supported in v3
|
|
clientV4, _ := ghUser.authenticateV4()
|
|
ghUser.clientV4 = clientV4
|
|
}
|
|
|
|
return &ghUser
|
|
}
|
|
|
|
/* -------------------- Exported Functions -------------------- */
|
|
|
|
// Load loads the user's data from GitHub
|
|
func (ghUser *GitHubUser) Load() error {
|
|
err := ghUser.verifyGitHubClients()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = ghUser.loadGitHubData()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/* -------------------- Unexported Functions -------------------- */
|
|
|
|
func (ghUser *GitHubUser) authenticateV3() (*ghb.Client, error) {
|
|
src := oauth2.StaticTokenSource(
|
|
&oauth2.Token{AccessToken: ghUser.apiKey},
|
|
)
|
|
|
|
oauthClient := oauth2.NewClient(context.Background(), src)
|
|
client := ghb.NewClient(oauthClient)
|
|
|
|
return client, nil
|
|
}
|
|
|
|
func (ghUser *GitHubUser) authenticateV4() (*githubv4.Client, error) {
|
|
src := oauth2.StaticTokenSource(
|
|
&oauth2.Token{AccessToken: ghUser.apiKey},
|
|
)
|
|
|
|
oauthClient := oauth2.NewClient(context.Background(), src)
|
|
client := githubv4.NewClient(oauthClient)
|
|
|
|
return client, nil
|
|
}
|
|
|
|
// hasAPIKey returns TRUE if the user has put a GitHub API key into their
|
|
// configuration and we've managed to find and read it
|
|
func (ghUser *GitHubUser) hasAPIKey() bool {
|
|
return ghUser.apiKey != ""
|
|
}
|
|
|
|
func (ghUser *GitHubUser) loadGitHubData() error {
|
|
var err error
|
|
|
|
login, err := ghUser.loadLoginName()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ghUser.loginName = login
|
|
|
|
var isContrib, isSponsor bool
|
|
|
|
ctx := context.Background()
|
|
g, ctx := errgroup.WithContext(ctx)
|
|
|
|
g.Go(func() error {
|
|
isContrib, err = ghUser.loadContributorStatus(ctx)
|
|
return err
|
|
})
|
|
|
|
g.Go(func() error {
|
|
isSponsor, err = ghUser.loadSponsorStatus(ctx)
|
|
return err
|
|
})
|
|
|
|
err = g.Wait()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ghUser.IsContributor = isContrib
|
|
ghUser.IsSponsor = isSponsor
|
|
|
|
return nil
|
|
}
|
|
|
|
// loadLoginName figures out the GitHub user's login name from their API key
|
|
func (ghUser *GitHubUser) loadLoginName() (string, error) {
|
|
user, _, err := ghUser.clientV3.Users.Get(context.Background(), "")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
login := user.GetLogin()
|
|
|
|
return login, nil
|
|
}
|
|
|
|
// loadContributorStatus figures out if this GitHub account has contributed to WTF
|
|
func (ghUser *GitHubUser) loadContributorStatus(ctx context.Context) (bool, error) {
|
|
page := 1
|
|
isContributor := false
|
|
|
|
for {
|
|
opts := &ghb.ListContributorsOptions{
|
|
ListOptions: ghb.ListOptions{
|
|
Page: page,
|
|
PerPage: 100,
|
|
},
|
|
}
|
|
|
|
contributors, resp, err := ghUser.clientV3.Repositories.ListContributors(ctx, "wtfutil", "wtf", opts)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK || len(contributors) < 1 {
|
|
break
|
|
}
|
|
|
|
for _, contrib := range contributors {
|
|
if contrib.GetLogin() == ghUser.loginName {
|
|
isContributor = true
|
|
break
|
|
}
|
|
}
|
|
|
|
page++
|
|
}
|
|
|
|
return isContributor, nil
|
|
}
|
|
|
|
// loadSponsorStatus figures out if this GitHub account has sponsored WTF
|
|
func (ghUser *GitHubUser) loadSponsorStatus(ctx context.Context) (bool, error) {
|
|
vars := map[string]interface{}{
|
|
"loginName": githubv4.String(ghUser.loginName),
|
|
}
|
|
|
|
err := ghUser.clientV4.Query(ctx, &sponsorQuery, vars)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
isSponsor := false
|
|
|
|
for _, spon := range sponsorQuery.User.SponsorshipsAsSponsor.Nodes {
|
|
if spon.Sponsorable.SponsorsListing.Slug == "sponsors-senorprogrammer" {
|
|
isSponsor = true
|
|
break
|
|
}
|
|
}
|
|
|
|
return isSponsor, nil
|
|
}
|
|
|
|
func (ghUser *GitHubUser) verifyGitHubClients() error {
|
|
if ghUser.clientV3 == nil {
|
|
return errors.New("github client v3 failed to load")
|
|
}
|
|
|
|
if ghUser.clientV4 == nil {
|
|
return errors.New("github client v4 failed to load")
|
|
}
|
|
|
|
return nil
|
|
}
|