VainDiscord: A Practical Use of Astro Middleware

@kyanoxia.com

For a small unboosted server, Discord invite links are... not very pretty. With a string of seemingly random characters after a clearly labeled invite URL path, it's unappealing and hard to remember invite codes. To optimize server discovery and engagement, you as an administrator have two options:

  • Allow members to create invites
  • Boost the server to get a custom invite

Both of these solutions don't seem particularly ideal. You don't want to allow members to create custom invites, as this opens a risk for easy access to raids by bad actors, and makes it harder to tell where new members come from (if you're into analytics). Boosting the server may become possible in the future, but as a small server this may not always be an option.

So how do you get that pretty custom invite that you and your members can remember? Armed with my knowledge of development, I set off to answer this question.


So the first thing I did was go to the Discord Developer API Documentation, and check the references for anything that could be useful to me. If you're familiar with the API, you'll know that there's a GET: /invites/ endpoint which returns all information needed to build my own invite link. This endpoint returns an object containing information such as the server name, description, inviter, and much more. Going into a browser and navigating to the API endpoint for my server, https://discord.com/api/v10/invites/DVvGFXqpqH, I got the following response:

{
  "type": 0,
  "code": "DVvGFXqpqH",
  "inviter": {
    "id": "1167854202983297074",
    "username": "kyanoxia",
    "avatar": "d18a0f65374883fae756be7e76939863",
    "discriminator": "0",
    "public_flags": 4194368,
    "flags": 4194368,
    "banner": "2960f1dbacebbe2005c3b4482d3b65b1",
    "accent_color": 1842204,
    "global_name": "Kya",
    "avatar_decoration_data": null,
    "banner_color": "#1c1c1c",
    "clan": null,
    "primary_guild": null
  },
  "expires_at": null,
  "flags": 2,
  "guild": {
    "id": "1235261576395886612",
    "name": "KCON-DSC-01",
    "splash": null,
    "banner": null,
    "description": "k-on!",
    "icon": "9b4ded63fa29d81905c5dff026dfa363",
    "features": [
      "COMMUNITY",
      "GUILD_ONBOARDING_HAS_PROMPTS",
      "PREVIEW_ENABLED",
      "GUILD_ONBOARDING",
      "MEMBER_VERIFICATION_GATE_ENABLED",
      "AUTO_MODERATION",
      "GUILD_SERVER_GUIDE",
      "GUILD_ONBOARDING_EVER_ENABLED",
      "SOUNDBOARD",
      "NEWS"
    ],
    "verification_level": 2,
    "vanity_url_code": null,
    "nsfw_level": 0,
    "nsfw": false,
    "premium_subscription_count": 0
  },
  "guild_id": "1235261576395886612",
  "channel": {
    "id": "1235970945445789716",
    "type": 0,
    "name": "rules"
  }
}

While this response has a lot of useful information, we're only interested in a few for the moment:

  • guild.name
  • guild.id
  • guild.description
  • guild.icon

With these, I can construct a page for my website to compile everything nicely into meta tags to support embeds. First though, I need to get the API response. That's where middleware comes in:

if (context.url.pathname === "/join" || context.url.pathname === "/join/") {
    const inv: string = "DVvGFXqpqH";

    if (context.request.headers.get("user-agent")?.includes("bot", 0)) {
        // Await response and convert to json before continuing
        const res = await fetch(`https://discord.com/api/v10/invites/${inv}`);
        const data = await res.json();

        // Store in locals
        context.locals.dscName = data.guild.name;
        context.locals.dscImg = `https://cdn.discordapp.com/icons/${data.guild.id}/${data.guild.icon}`;
        context.locals.dscDesc = data.guild.description;
        context.locals.dscURL = `https://discord.com/invite/${inv}`;

        return next();
    }

    return Response.redirect(new URL(`https://discord.com/invite/${inv}`), 302)
}

This middleware checks if my pathname is /join (being kyanoxia.com/join). If it is, we make sure the user agent contains the bot keyword. This means that a crawler is trying to get the page for embed information. If that's the case, we want to fetch API data and store it in our Astro locals before instructing the server to respond with our join.astro file. Inside this file, I can just get the Astro locals like so, and construct an embed with the appropriate meta tags:

export const prerender = false
const req = Astro.locals;

Easy enough. If, however, our user agent doesn't contain the bot keyword, that means a user is trying to navigate to that link. So instead of directing them to the embed-building page, we want to tell their browser to get redirected to the appropriate Discord invite link, officially inviting them to the server.

VainDiscord being used

So what we have now is an expandable embedding solution for a vanity link on your domain. You can disable create invites for everyone in your server, and only use that single URL. Easy to remember, easy to manage, easy to create. To check out more, check the source code of my website on my GitHub.

kyanoxia.com
Kyanoxia | Artist & Developer

@kyanoxia.com

- Developer of @orchid.kyanoxia.com
- Lead developer @dtmunity.com
- Maker of various different things

I'll make you think...

https://kyanoxia.com/

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)