Skip to content

Commit

Permalink
Merge pull request #219 from disgoorg/patch/role-connections
Browse files Browse the repository at this point in the history
  • Loading branch information
mlnrDev committed Dec 15, 2022
2 parents d1911d5 + f3a4a02 commit bb4c3d6
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 57 deletions.
113 changes: 113 additions & 0 deletions _examples/verified_roles/main.go
@@ -0,0 +1,113 @@
package main

import (
"math/rand"
"net/http"
"os"
"strconv"

"github.com/disgoorg/disgo"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/oauth2"
"github.com/disgoorg/json"
"github.com/disgoorg/log"
)

var (
letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
token = os.Getenv("disgo_token")
clientSecret = os.Getenv("disgo_client_secret")
baseURL = os.Getenv("disgo_base_url")
client bot.Client
oAuth2Client oauth2.Client
)

func main() {
log.SetLevel(log.LevelDebug)
log.Info("starting example...")
log.Infof("disgo %s", disgo.Version)

var err error
client, err = disgo.New(token)
if err != nil {
log.Panic(err)
}

_, _ = client.Rest().UpdateApplicationRoleConnectionMetadata(client.ApplicationID(), []discord.ApplicationRoleConnectionMetadata{
{
Type: discord.ApplicationRoleConnectionMetadataTypeIntegerGreaterThanOrEqual,
Key: "cookies_eaten",
Name: "Cookies Eaten",
Description: "How many cookies have you eaten?",
},
})

oAuth2Client = oauth2.New(client.ApplicationID(), clientSecret)

mux := http.NewServeMux()
mux.HandleFunc("/verify", handleVerify)
mux.HandleFunc("/callback", handleCallback)
_ = http.ListenAndServe(":6969", mux)
}

func handleVerify(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, oAuth2Client.GenerateAuthorizationURL(baseURL+"/callback", discord.PermissionsNone, 0, false, discord.OAuth2ScopeIdentify, discord.OAuth2ScopeRoleConnectionsWrite), http.StatusTemporaryRedirect)
}

func handleCallback(w http.ResponseWriter, r *http.Request) {
var (
query = r.URL.Query()
code = query.Get("code")
state = query.Get("state")
)
if code != "" && state != "" {
identifier := randStr(32)
session, err := oAuth2Client.StartSession(code, state, identifier)
if err != nil {
writeError(w, "error while starting session", err)
return
}

user, err := oAuth2Client.GetUser(session)
if err != nil {
writeError(w, "error while getting user", err)
return
}

_, err = oAuth2Client.UpdateApplicationRoleConnection(session, client.ApplicationID(), discord.ApplicationRoleConnectionUpdate{
PlatformName: json.Ptr("Cookie Monster " + user.Username),
PlatformUsername: json.Ptr("Cookie Monster " + user.Tag()),
Metadata: &map[string]string{
"cookies_eaten": strconv.Itoa(rand.Intn(100)),
},
})
if err != nil {
writeError(w, "error while updating role connection", err)
return
}

metadata, err := oAuth2Client.GetApplicationRoleConnection(session, client.ApplicationID())
if err != nil {
writeError(w, "error while getting role connection", err)
return
}

data, _ := json.MarshalIndent(metadata, "", "\t")
_, _ = w.Write([]byte("updated role connection:\n" + string(data)))

}
}

func writeError(w http.ResponseWriter, text string, err error) {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(text + ": " + err.Error()))
}

func randStr(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
58 changes: 30 additions & 28 deletions discord/application.go
Expand Up @@ -9,27 +9,28 @@ import (
)

type Application struct {
ID snowflake.ID `json:"id"`
Name string `json:"name"`
Icon *string `json:"icon,omitempty"`
Description string `json:"description"`
RPCOrigins []string `json:"rpc_origins"`
BotPublic bool `json:"bot_public"`
BotRequireCodeGrant bool `json:"bot_require_code_grant"`
TermsOfServiceURL *string `json:"terms_of_service_url,omitempty"`
PrivacyPolicyURL *string `json:"privacy_policy_url,omitempty"`
CustomInstallURL *string `json:"custom_install_url,omitempty"`
InstallParams *InstallParams `json:"install_params"`
Tags []string `json:"tags"`
Owner *User `json:"owner,omitempty"`
Summary string `json:"summary"`
VerifyKey string `json:"verify_key"`
Team *Team `json:"team,omitempty"`
GuildID *snowflake.ID `json:"guild_id,omitempty"`
PrimarySkuID *snowflake.ID `json:"primary_sku_id,omitempty"`
Slug *string `json:"slug,omitempty"`
CoverImage *string `json:"cover_image,omitempty"`
Flags ApplicationFlags `json:"flags,omitempty"`
ID snowflake.ID `json:"id"`
Name string `json:"name"`
Icon *string `json:"icon,omitempty"`
Description string `json:"description"`
RPCOrigins []string `json:"rpc_origins"`
BotPublic bool `json:"bot_public"`
BotRequireCodeGrant bool `json:"bot_require_code_grant"`
TermsOfServiceURL *string `json:"terms_of_service_url,omitempty"`
PrivacyPolicyURL *string `json:"privacy_policy_url,omitempty"`
CustomInstallURL *string `json:"custom_install_url,omitempty"`
RoleConnectionsVerificationURL *string `json:"role_connections_verification_url"`
InstallParams *InstallParams `json:"install_params"`
Tags []string `json:"tags"`
Owner *User `json:"owner,omitempty"`
Summary string `json:"summary"`
VerifyKey string `json:"verify_key"`
Team *Team `json:"team,omitempty"`
GuildID *snowflake.ID `json:"guild_id,omitempty"`
PrimarySkuID *snowflake.ID `json:"primary_sku_id,omitempty"`
Slug *string `json:"slug,omitempty"`
CoverImage *string `json:"cover_image,omitempty"`
Flags ApplicationFlags `json:"flags,omitempty"`
}

func (a Application) IconURL(opts ...CDNOpt) *string {
Expand Down Expand Up @@ -100,13 +101,14 @@ const (
OAuth2ScopeGuildsMembersRead OAuth2Scope = "guilds.members.read"
OAuth2ScopeGDMJoin OAuth2Scope = "gdm.join"

OAuth2ScopeRelationshipsRead OAuth2Scope = "relationships.read"
OAuth2ScopeIdentify OAuth2Scope = "identify"
OAuth2ScopeEmail OAuth2Scope = "email"
OAuth2ScopeConnections OAuth2Scope = "connections"
OAuth2ScopeBot OAuth2Scope = "bot"
OAuth2ScopeMessagesRead OAuth2Scope = "messages.read"
OAuth2ScopeWebhookIncoming OAuth2Scope = "webhook.incoming"
OAuth2ScopeRelationshipsRead OAuth2Scope = "relationships.read"
OAuth2ScopeRoleConnectionsWrite OAuth2Scope = "role_connections.write"
OAuth2ScopeIdentify OAuth2Scope = "identify"
OAuth2ScopeEmail OAuth2Scope = "email"
OAuth2ScopeConnections OAuth2Scope = "connections"
OAuth2ScopeBot OAuth2Scope = "bot"
OAuth2ScopeMessagesRead OAuth2Scope = "messages.read"
OAuth2ScopeWebhookIncoming OAuth2Scope = "webhook.incoming"
)

func (s OAuth2Scope) String() string {
Expand Down
23 changes: 23 additions & 0 deletions discord/application_role_connection_metadata.go
@@ -0,0 +1,23 @@
package discord

type ApplicationRoleConnectionMetadata struct {
Type ApplicationRoleConnectionMetadataType `json:"type"`
Key string `json:"key"`
Name string `json:"name"`
NameLocalizations map[Locale]string `json:"name_localizations,omitempty"`
Description string `json:"description"`
DescriptionLocalizations map[Locale]string `json:"description_localizations,omitempty"`
}

type ApplicationRoleConnectionMetadataType int

const (
ApplicationRoleConnectionMetadataTypeIntegerLessThanOrEqual ApplicationRoleConnectionMetadataType = iota + 1
ApplicationRoleConnectionMetadataTypeIntegerGreaterThanOrEqual
ApplicationRoleConnectionMetadataTypeIntegerEqual
ApplicationRoleConnectionMetadataTypeIntegerNotEqual
ApplicationRoleConnectionMetadataTypeDateTimeLessThanOrEqual
ApplicationRoleConnectionMetadataTypeDateTimeGreaterThanOrEqual
ApplicationRoleConnectionMetadataTypeBooleanEqual
ApplicationRoleConnectionMetadataTypeBooleanNotEqual
)
1 change: 1 addition & 0 deletions discord/role.go
Expand Up @@ -13,6 +13,7 @@ var _ Mentionable = (*Role)(nil)
type Role struct {
ID snowflake.ID `json:"id"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Color int `json:"color"`
Hoist bool `json:"hoist"`
Position int `json:"position"`
Expand Down
12 changes: 12 additions & 0 deletions discord/user.go
Expand Up @@ -170,3 +170,15 @@ type SelfUserUpdate struct {
Username string `json:"username,omitempty"`
Avatar *json.Nullable[Icon] `json:"avatar,omitempty"`
}

type ApplicationRoleConnection struct {
PlatformName *string `json:"platform_name"`
PlatformUsername *string `json:"platform_username"`
Metadata map[string]string `json:"metadata"`
}

type ApplicationRoleConnectionUpdate struct {
PlatformName *string `json:"platform_name,omitempty"`
PlatformUsername *string `json:"platform_username,omitempty"`
Metadata *map[string]string `json:"metadata,omitempty"`
}
4 changes: 4 additions & 0 deletions oauth2/client.go
Expand Up @@ -55,4 +55,8 @@ type Client interface {
GetGuilds(session Session, opts ...rest.RequestOpt) ([]discord.OAuth2Guild, error)
// GetConnections returns the discord.Connection(s) the user has connected. This requires the discord.OAuth2ScopeConnections scope in the Session
GetConnections(session Session, opts ...rest.RequestOpt) ([]discord.Connection, error)
// GetApplicationRoleConnection returns the discord.ApplicationRoleConnection for the given application. This requires the discord.OAuth2ScopeRoleConnectionsWrite scope in the Session
GetApplicationRoleConnection(session Session, applicationID snowflake.ID, opts ...rest.RequestOpt) (*discord.ApplicationRoleConnection, error)
// UpdateApplicationRoleConnection updates the discord.ApplicationRoleConnection for the given application. This requires the discord.OAuth2ScopeRoleConnectionsWrite scope in the Session
UpdateApplicationRoleConnection(session Session, applicationID snowflake.ID, update discord.ApplicationRoleConnectionUpdate, opts ...rest.RequestOpt) (*discord.ApplicationRoleConnection, error)
}
50 changes: 31 additions & 19 deletions oauth2/client_impl.go
Expand Up @@ -90,41 +90,53 @@ func (c *clientImpl) RefreshSession(identifier string, session Session, opts ...
}

func (c *clientImpl) GetUser(session Session, opts ...rest.RequestOpt) (*discord.OAuth2User, error) {
if session.Expiration().Before(time.Now()) {
return nil, ErrAccessTokenExpired
}
if !discord.HasScope(discord.OAuth2ScopeIdentify, session.Scopes()...) {
return nil, ErrMissingOAuth2Scope(discord.OAuth2ScopeIdentify)
if err := checkSession(session, discord.OAuth2ScopeIdentify); err != nil {
return nil, err
}
return c.Rest().GetCurrentUser(session.AccessToken(), opts...)
}

func (c *clientImpl) GetMember(session Session, guildID snowflake.ID, opts ...rest.RequestOpt) (*discord.Member, error) {
if session.Expiration().Before(time.Now()) {
return nil, ErrAccessTokenExpired
}
if !discord.HasScope(discord.OAuth2ScopeGuildsMembersRead, session.Scopes()...) {
return nil, ErrMissingOAuth2Scope(discord.OAuth2ScopeGuildsMembersRead)
if err := checkSession(session, discord.OAuth2ScopeGuildsMembersRead); err != nil {
return nil, err
}
return c.Rest().GetCurrentMember(session.AccessToken(), guildID, opts...)
}

func (c *clientImpl) GetGuilds(session Session, opts ...rest.RequestOpt) ([]discord.OAuth2Guild, error) {
if session.Expiration().Before(time.Now()) {
return nil, ErrAccessTokenExpired
}
if !discord.HasScope(discord.OAuth2ScopeGuilds, session.Scopes()...) {
return nil, ErrMissingOAuth2Scope(discord.OAuth2ScopeGuilds)
if err := checkSession(session, discord.OAuth2ScopeGuilds); err != nil {
return nil, err
}
return c.Rest().GetCurrentUserGuilds(session.AccessToken(), 0, 0, 0, opts...)
}

func (c *clientImpl) GetConnections(session Session, opts ...rest.RequestOpt) ([]discord.Connection, error) {
if err := checkSession(session, discord.OAuth2ScopeConnections); err != nil {
return nil, err
}
return c.Rest().GetCurrentUserConnections(session.AccessToken(), opts...)
}

func (c *clientImpl) GetApplicationRoleConnection(session Session, applicationID snowflake.ID, opts ...rest.RequestOpt) (*discord.ApplicationRoleConnection, error) {
if err := checkSession(session, discord.OAuth2ScopeRoleConnectionsWrite); err != nil {
return nil, err
}
return c.Rest().GetCurrentUserApplicationRoleConnection(session.AccessToken(), applicationID, opts...)
}

func (c *clientImpl) UpdateApplicationRoleConnection(session Session, applicationID snowflake.ID, update discord.ApplicationRoleConnectionUpdate, opts ...rest.RequestOpt) (*discord.ApplicationRoleConnection, error) {
if err := checkSession(session, discord.OAuth2ScopeRoleConnectionsWrite); err != nil {
return nil, err
}
return c.Rest().UpdateCurrentUserApplicationRoleConnection(session.AccessToken(), applicationID, update, opts...)
}

func checkSession(session Session, scope discord.OAuth2Scope) error {
if session.Expiration().Before(time.Now()) {
return nil, ErrAccessTokenExpired
return ErrAccessTokenExpired
}
if !discord.HasScope(discord.OAuth2ScopeConnections, session.Scopes()...) {
return nil, ErrMissingOAuth2Scope(discord.OAuth2ScopeConnections)
if !discord.HasScope(scope, session.Scopes()...) {
return ErrMissingOAuth2Scope(scope)
}
return c.Rest().GetCurrentUserConnections(session.AccessToken(), opts...)
return nil
}
13 changes: 13 additions & 0 deletions rest/applications.go
Expand Up @@ -28,6 +28,9 @@ type Applications interface {

GetGuildCommandsPermissions(applicationID snowflake.ID, guildID snowflake.ID, opts ...RequestOpt) ([]discord.ApplicationCommandPermissions, error)
GetGuildCommandPermissions(applicationID snowflake.ID, guildID snowflake.ID, commandID snowflake.ID, opts ...RequestOpt) (*discord.ApplicationCommandPermissions, error)

GetApplicationRoleConnectionMetadata(applicationID snowflake.ID, opts ...RequestOpt) ([]discord.ApplicationRoleConnectionMetadata, error)
UpdateApplicationRoleConnectionMetadata(applicationID snowflake.ID, newRecords []discord.ApplicationRoleConnectionMetadata, opts ...RequestOpt) ([]discord.ApplicationRoleConnectionMetadata, error)
}

type applicationsImpl struct {
Expand Down Expand Up @@ -142,6 +145,16 @@ func (s *applicationsImpl) GetGuildCommandPermissions(applicationID snowflake.ID
return
}

func (s *applicationsImpl) GetApplicationRoleConnectionMetadata(applicationID snowflake.ID, opts ...RequestOpt) (metadata []discord.ApplicationRoleConnectionMetadata, err error) {
err = s.client.Do(GetApplicationRoleConnectionMetadata.Compile(nil, applicationID), nil, &metadata, opts...)
return
}

func (s *applicationsImpl) UpdateApplicationRoleConnectionMetadata(applicationID snowflake.ID, newRecords []discord.ApplicationRoleConnectionMetadata, opts ...RequestOpt) (metadata []discord.ApplicationRoleConnectionMetadata, err error) {
err = s.client.Do(UpdateApplicationRoleConnectionMetadata.Compile(nil, applicationID), newRecords, &metadata, opts...)
return
}

func unmarshalApplicationCommandsToApplicationCommands(unmarshalCommands []discord.UnmarshalApplicationCommand) []discord.ApplicationCommand {
commands := make([]discord.ApplicationCommand, len(unmarshalCommands))
for i := range unmarshalCommands {
Expand Down
13 changes: 13 additions & 0 deletions rest/oauth2.go
Expand Up @@ -25,6 +25,9 @@ type OAuth2 interface {

SetGuildCommandPermissions(bearerToken string, applicationID snowflake.ID, guildID snowflake.ID, commandID snowflake.ID, commandPermissions []discord.ApplicationCommandPermission, opts ...RequestOpt) (*discord.ApplicationCommandPermissions, error)

GetCurrentUserApplicationRoleConnection(bearerToken string, applicationID snowflake.ID, opts ...RequestOpt) (*discord.ApplicationRoleConnection, error)
UpdateCurrentUserApplicationRoleConnection(bearerToken string, applicationID snowflake.ID, connectionUpdate discord.ApplicationRoleConnectionUpdate, opts ...RequestOpt) (*discord.ApplicationRoleConnection, error)

GetAccessToken(clientID snowflake.ID, clientSecret string, code string, redirectURI string, opts ...RequestOpt) (*discord.AccessTokenResponse, error)
RefreshAccessToken(clientID snowflake.ID, clientSecret string, refreshToken string, opts ...RequestOpt) (*discord.AccessTokenResponse, error)
}
Expand Down Expand Up @@ -97,6 +100,16 @@ func (s *oAuth2Impl) SetGuildCommandPermissions(bearerToken string, applicationI
return
}

func (s *oAuth2Impl) GetCurrentUserApplicationRoleConnection(bearerToken string, applicationID snowflake.ID, opts ...RequestOpt) (connection *discord.ApplicationRoleConnection, err error) {
err = s.client.Do(GetCurrentUserApplicationRoleConnection.Compile(nil, applicationID), nil, &connection, withBearerToken(bearerToken, opts)...)
return
}

func (s *oAuth2Impl) UpdateCurrentUserApplicationRoleConnection(bearerToken string, applicationID snowflake.ID, connectionUpdate discord.ApplicationRoleConnectionUpdate, opts ...RequestOpt) (connection *discord.ApplicationRoleConnection, err error) {
err = s.client.Do(UpdateCurrentUserApplicationRoleConnection.Compile(nil, applicationID), connectionUpdate, &connection, withBearerToken(bearerToken, opts)...)
return
}

func (s *oAuth2Impl) exchangeAccessToken(clientID snowflake.ID, clientSecret string, grantType discord.GrantType, codeOrRefreshToken string, redirectURI string, opts ...RequestOpt) (exchange *discord.AccessTokenResponse, err error) {
values := url.Values{
"client_id": []string{clientID.String()},
Expand Down

0 comments on commit bb4c3d6

Please sign in to comment.