0.2.8 • Published 3 years ago
@airdot/linked-roles v0.2.8
🪄 This is a fork meister03/discord-linked-roles
This is a fork of meister03/discord-linked-roles, this fork fixes some invalid typings.
Discord-linked-roles
A powerful and easy to use package to create linked roles and update user metadata.
Features:
- Persistent storage of access tokens to avoid re-authorization (Mongoose or CustomProvider)
 - OAuth2 Authorization flow
 - Less backend code
 
Examples

Getting Started
- Create a new application on the Discord Developer Portal
 - Create a new bot and copy the token
 - Go to the Information tab and set the following: 
http://localhost:3000/linked-roleaslinked-roleurl - Go to the OAuth2 Section and add the following 
redirectUri:http://localhost:3000/auth-callback - Get the 
clientSecretfrom the same page - Create a 
config.jsonfile with following entries below: 
{
    "token": "...",
    "id": "...",
    "clientSecret": "...",
    "redirectUri": "http://localhost:3000/auth-callback"
}Registering the Application Metadata
- The application metadata is a schema that contains maximally 5 entries.
 - The entries consist of the following types: 
{key: string, name: string, description: string, type: number} - Once the OAuth2 takes place, you can set the metadata of the user by using the 
setUserMetaDatamethod. 
const { Application, MapProvider, MetaDataTypes } = require('discord-linked-roles');
const config = require('./config.json');
const application = new Application({
    id: config.id,
    token: config.token,
    clientSecret: config.clientSecret,
    redirectUri: config.redirectUri,
    scopes: config.scopes,
    databaseProvider: new MapProvider() // new MongooseProvider(databaseUrl),
});
// Following value types exists: Boolean, Date, Integer
application.registerMetaData([
        {
            key: 'level',
            name: 'Level',
            description: 'The level of the user',
            type: MetaDataTypes.INTEGER_GREATER_THAN_OR_EQUAL
        },
        {
            key: 'xp',
            name: 'Total XP',
            description: 'The total xp of the user',
            type: MetaDataTypes.INTEGER_GREATER_THAN_OR_EQUAL
        }
]);Set the User Metadata
- You need the 
access_tokento set the user metadata. - The 
access_tokenis provided after the OAuth2 flow. - The 
tokensare saved underapplication.tokenStorage - Install the required packages: 
npm i express cookie-parser crypto express 
const { Application, MapProvider, MetaDataTypes } = require('discord-linked-roles');
const config = require('./config.json');
const crypto = require('crypto');
const express = require('express');
const cookieParser = require('cookie-parser');
const application = new Application({
    id: config.id,
    token: config.token,
    clientSecret: config.clientSecret,
    redirectUri: config.redirectUri,
    scopes: config.scopes,
    databaseProvider: new MapProvider() // new MongooseProvider(databaseUrl),
});
const app = express();
app.use(cookieParser(crypto.randomUUID()));
app.get('/linked-role', application.authorization.setCookieAndRedirect.bind(application.authorization));
app.get('/auth-callback', async (req, res) => {
    try {
        // Verifies if the cookie equals the one given on the /linked-role route
        const code = application.authorization.checkCookieAndReturnCode(req, res);
        // Invalid Cookie
        if (!code) return res.sendStatus(403);
        // Gets the user and stores the tokens
        const data = await application.authorization.getUserAndStoreToken(code);
        if(!application.authorization.checkRequiredScopesPresent(data.scopes)) return res.redirect('/linked-role');
        const user = data.user;
        // const advancedUser = await application.fetchUser(user.id); , User with email, verified ...
        
        // Set Application MetaData
        application.setUserMetaData(user.id, user.username ,{ level: 24, xp: 523 })
        res.send("Successfully linked your account!")
    } catch (e) {
        console.log(e);
        res.sendStatus(500);
    }
})
app.listen(3000, () => {
  console.log(`Example app listening on port ${port}`);
});
Update Users Metadata
- You likely want to update the users metadata on some certain point
 - You would need an access token to update the metadata, which is the reason you need a persistent storage
 
async function startDailyMetaDataUpdates() {
    let users = await application.tokenStorage.getAllUsers();
    console.log("[Starting Daily Metadata Updates] Users to update:", users.length)
    for (let i = 0; i < users.length; i++) {
      setTimeout(async () => {
        await updateMetadata(users[i].id);
      }, i * 1000 * 180);
    }
}
async function updateMetadata(userId) {
    const user = await application.fetchUser(userId);
    application.setUserMetaData(user.id, user.username ,{ level: Number((Math.random()*24).toFixed(0)), xp: Number((Math.random()*523).toFixed(0)) })
}Get the User Metadata
- You can get the user metadata by using the 
getUserMetaDatamethod. - Access token is required to get the metadata.
 
const metadata = await application.getUserMetaData(userId);Fetch the User
- You can fetch the user by using the 
fetchUsermethod. - Access token is required to fetch the user.
 - Based on the scopes in the authorization, you can get more information about the user such as email, verified, etc.
 
const user = await application.fetchUser(userId);Fetch Guilds
- You can fetch the guilds by using the 
fetchUserGuildsmethod. - Access token is required to fetch the guilds.
 - You need the 
guildsscope to fetch the guilds. 
const guilds = await application.fetchUserGuilds(userId);Fetch User Connections
- You can fetch the user connections by using the 
fetchUserConnectionsmethod. - Access token is required to fetch the user connections.
 - You need the 
connectionsscope to fetch the user connections. 
const connections = await application.fetchUserConnections(userId);Fetch GuildMember of User in a Guild
- You can fetch the guild member of a user in a guild by using the 
fetchUserGuildMembermethod. - Access token is required to fetch the guild member.
 - You need the 
guilds.members.readscope to fetch the guild member. 
const guildMember = await application.fetchUserGuildMember(userId, guildId);Persistent Storage of Access Tokens
- You can use the 
MapProviderto store the access tokens in memory. - You can use the 
MongooseProviderto store the access tokens in a MongoDB database. - When you want to store the access tokens on your way, then you can create a database provider with following types:
 
export type DataBaseProvider = {
    findAll(): Promise<{tokens: OAuthTokens, id: string}>;
    // Gets the token for the user
    fetchUser: (userId: string) => Promise<OAuthTokens | undefined>;
    createOrUpdate: (userId: string, token: OAuthTokens) => Promise<void>;
    deleteUser: (userId: string) => Promise<void>;
}
export interface OAuthTokens {
    access_token: string;
    refresh_token: string;
    expires_at: number;
}Bugs, glitches and issues
If you encounter any problems feel free to open an issue in our GitHub repository or join the Discord server.
Credits
All Credit of the Authorization flow goes to this repo: https://github.com/discord/linked-roles-sample