@airdot/linked-roles
TypeScript icon, indicating that this package has built-in type declarations

0.2.8 • Public • Published

Discord server

🪄 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

Example Image Example Image

Getting Started

  1. Create a new application on the Discord Developer Portal
  2. Create a new bot and copy the token
  3. Go to the Information tab and set the following: http://localhost:3000/linked-role as linked-role url
  4. Go to the OAuth2 Section and add the following redirectUri: http://localhost:3000/auth-callback
  5. Get the clientSecret from the same page
  6. Create a config.json file 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 setUserMetaData method.
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_token to set the user metadata.
  • The access_token is provided after the OAuth2 flow.
  • The tokens are saved under application.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 getUserMetaData method.
  • 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 fetchUser method.
  • 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 fetchUserGuilds method.
  • Access token is required to fetch the guilds.
  • You need the guilds scope to fetch the guilds.
const guilds = await application.fetchUserGuilds(userId);

Fetch User Connections

  • You can fetch the user connections by using the fetchUserConnections method.
  • Access token is required to fetch the user connections.
  • You need the connections scope 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 fetchUserGuildMember method.
  • Access token is required to fetch the guild member.
  • You need the guilds.members.read scope to fetch the guild member.
const guildMember = await application.fetchUserGuildMember(userId, guildId);

Persistent Storage of Access Tokens

  • You can use the MapProvider to store the access tokens in memory.
  • You can use the MongooseProvider to 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

Package Sidebar

Install

npm i @airdot/linked-roles

Weekly Downloads

3

Version

0.2.8

License

MIT

Unpacked Size

54.7 kB

Total Files

33

Last publish

Collaborators

  • turtlepaw