home to your local SPACEGIRL 馃挮
arimelody.space
1package discord
2
3import (
4 "arimelody-web/model"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "net/http"
9 "net/url"
10 "strings"
11)
12
13const API_ENDPOINT = "https://discord.com/api/v10"
14
15type (
16 AccessTokenResponse struct {
17 AccessToken string `json:"access_token"`
18 TokenType string `json:"token_type"`
19 ExpiresIn int `json:"expires_in"`
20 RefreshToken string `json:"refresh_token"`
21 Scope string `json:"scope"`
22 }
23
24 AuthInfoResponse struct {
25 Application struct {
26 ID string `json:"id"`
27 Name string `json:"name"`
28 Icon string `json:"icon"`
29 Description string `json:"description"`
30 Hook bool `json:"hook"`
31 BotPublic bool `json:"bot_public"`
32 BotRequireCodeGrant bool `json:"bot_require_code_grant"`
33 VerifyKey string `json:"verify_key"`
34 } `json:"application"`
35 Scopes []string `json:"scopes"`
36 Expires string `json:"expires"`
37 User DiscordUser `json:"user"`
38 }
39
40 DiscordUser struct {
41 ID string `json:"id"`
42 Username string `json:"username"`
43 Avatar string `json:"avatar"`
44 Discriminator string `json:"discriminator"`
45 GlobalName string `json:"global_name"`
46 PublicFlags int `json:"public_flags"`
47 }
48)
49
50func GetOAuthTokenFromCode(app *model.AppState, code string) (string, error) {
51 // let's get an oauth token!
52 req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/oauth2/token", API_ENDPOINT),
53 strings.NewReader(url.Values{
54 "client_id": {app.Config.Discord.ClientID},
55 "client_secret": {app.Config.Discord.Secret},
56 "grant_type": {"authorization_code"},
57 "code": {code},
58 "redirect_uri": {GetOAuthCallbackURI(app.Config.BaseUrl)},
59 }.Encode()))
60 req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
61
62 res, err := http.DefaultClient.Do(req)
63 if err != nil {
64 return "", errors.New(fmt.Sprintf("Failed while contacting discord API: %s", err))
65 }
66
67 oauth := AccessTokenResponse{}
68
69 err = json.NewDecoder(res.Body).Decode(&oauth)
70 if err != nil {
71 return "", errors.New(fmt.Sprintf("Failed to parse OAuth response data from discord: %s\n", err))
72 }
73 res.Body.Close()
74
75 return oauth.AccessToken, nil
76}
77
78func GetDiscordUserFromAuth(token string) (DiscordUser, error) {
79 // let's get authorisation information!
80 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/oauth2/@me", API_ENDPOINT), nil)
81 req.Header.Add("Authorization", "Bearer " + token)
82
83 res, err := http.DefaultClient.Do(req)
84 if err != nil {
85 return DiscordUser{}, errors.New(fmt.Sprintf("Failed to retrieve discord auth information: %s\n", err))
86 }
87
88 auth_info := AuthInfoResponse{}
89 err = json.NewDecoder(res.Body).Decode(&auth_info)
90 if err != nil {
91 return DiscordUser{}, errors.New(fmt.Sprintf("Failed to parse auth information from discord: %s\n", err))
92 }
93 defer res.Body.Close()
94
95 return auth_info.User, nil
96}
97
98func GetOAuthCallbackURI(baseURL string) string {
99 return fmt.Sprintf("%s/admin/login", baseURL)
100}
101
102func GetRedirectURI(app *model.AppState) string {
103 return fmt.Sprintf(
104 "https://discord.com/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&scope=identify",
105 app.Config.Discord.ClientID,
106 GetOAuthCallbackURI(app.Config.BaseUrl),
107 )
108}