1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00
wtf/support/github.go
2020-10-11 16:55:16 -07:00

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
}