npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

discord-linked-roles

v0.2.1

Published

A powerful package to create & update linked roles for applications

Downloads

149

Readme

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

Featured by Discord Creators:

Exclusive Private Community for Verified Bot Developers.

Meet big bot (1 mio+) and small bot developers and have a nice exchange...

Feel free to test the Feature in this Server:

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