Skip to content

Instantly share code, notes, and snippets.

@Still34
Last active May 18, 2021 16:18
Show Gist options
  • Save Still34/edf0db32dede055329cebaffdc7a77a3 to your computer and use it in GitHub Desktop.
Save Still34/edf0db32dede055329cebaffdc7a77a3 to your computer and use it in GitHub Desktop.
Discord.Net Frequently Asked Questions

Basic Concepts / Getting Started

How do I get started? Is there a really simple example I can follow?

First of all, welcome! Before you delve into using the library, however, you should have some decent understanding of the language you are about to use. This library touches on TAP (Task-based Asynchronous Pattern), polymorphism, interface and many more advanced topics extensively. Please make sure that you understand these topics to some extent before proceeding.

Here are some examples:

  1. Official quick start guide
  2. Official template

Please note that you should not try to blindly copy paste the code. It is meant to be a template or a guide. It is not meant to be something that will work out of the box.

How do I add my bot to my server/guild?

The OAuth2 URL can be generated via the Discord developer page. This allows you to set the permissions that the bot will be added with. With this method, bots will also be assigned their own special roles that normal users cannot use, which is what we call a Managed role.

What is a Client/User/Object ID? Is it the token?

Each user and object on Discord has its own snowflake ID generated based on various conditions, see here. The ID can be seen by anyone; it is public. It is merely used to identify an object in the Discord ecosystem. Many things in the library require an ID to retrieve the said object.

There are 2 ways to obtain the said ID.

  1. Using the built-in dev mode. With dev mode enabled, you can - as an example - right click on a guild and copy the guild id (please note that this does not apply to Role IDs, see below).
  2. Escape the object using \ in front the object. For example, when you do \@Example#1234, it will return the user ID of the aforementioned user.

A token is a credential used to log into an account. This information should be kept private and for your eyes only. Anyone with your token can log into your account. This applies to both user and bot accounts. That also means that you should never ever hardcode your token or add it into source control, as your identity may be stolen by scrape bots on the internet that scours through constantly to obtain a token.

How do I get the role ID?

Few common ways to do this:

  1. Make the role mentionable and mention the role, and escape it using the \ character in front.
  2. Inspect the roles collection within the guild via your debugger.

Please note that right-clicking on the role and copying the ID will not work. It will only copy the message ID.

I have more questions!

Please visit us at #dotnet_discord-net at the Discord API server. Describe the problem in details to us, and preferably with the problematic code uploaded onto Hastebin.

Client Basics Questions

My client keeps returning 401 upon logging in!

There are few possible reasons why this may occur.

  1. You are not using the appropriate TokenType. If you are using a bot account created from the Discord Developer portal, you should be using TokenType.Bot.
  2. You are not using the correct login credentials. Please keep in mind that tokens start with Mj*. If it starts with any other characters, chances are, you are using the client secret, which has nothing to do with the login token.

How do I do X, Y, Z when my bot connects/logs on? Why do I get a NullReferenceException upon calling any client methods after connect?

Your bot should not attempt to interact in any way with guilds/servers until the Ready event fires. When the bot connects, it first has to download guild information from Discord in order for you to get access to any server information; the client is not ready at this point. Technically, the GuildAvailable event fires once the data for a particular guild has downloaded; however, it's best to wait for all guilds to be downloaded. Once all downloads are complete, the Ready event is triggered, then you can proceed to do whatever you like.

How do I get a message's previous content when that message is edited?

If you need to do anything with messages (e.g. checking Reactions, checking the content of edited/deleted messages), you must set the MessageCacheSize in your DiscordSocketConfig settings in order to use the cached message entity. Read more about it here.

  1. Message Cache must be enabled.
  2. Hook the MessageUpdated event. This event provides a before and after object.
  3. Only messages received AFTER the bot comes online will be available in the cache.

Basic Operations Questions

How should I safely check a type?

In Discord.NET, the idea of polymorphism is used throughout. You may need to cast the object as a certain type before you can perform any action. There are several ways to cast, with direct casting (Type)type being the the least recommended, as it can throw an InvalidCastException when the object isn't the desired type. Please refer to this post for more details.

A good and safe casting example:

public async Task MessageReceivedHandler(SocketMessage msg)
{
   // Option 1:
   // Using the `as` keyword, which will return `null` if the object isn't the desired type.
   var usermsg = msg as SocketUserMessage;
   // We bail when the message isn't the desired type.
   if (msg == null) return;
   
   // Option 2:
   // Using the `is` keyword to cast (C#7 or above only)
   if (msg is SocketUserMessage usermsg) 
   {
      // Do things
   }
}

How do I send a message?

Any implementation of IMessageChannel has a SendMessageAsync method. Using the client, you can get an appropriate channel (GetChannel(id)) to send a message to. Remember, when using Discord.NET, polymorphism is a common recurring theme. This means an object may take in many shapes or form, which means casting is your friend. You should attempt to cast the channel as an IMessageChannel or any other entity that implements it to be able to message.

How can I tell if a message is from X, Y, Z?

You may check message channel type.

  • A Text channel (ITextChannel) is a message channel from a Guild.
  • A DM channel (IDMChannel) is a message channel from a DM.
  • A Group channel (IGroupChannel) is a message channel from a Group (this is rarely used, due to the bot's inability to join a group).
  • A Private channel (IPrivateChannel) is a DM or a Group.
  • A Message channel (IMessageChannel) is all of the above.

How do I add hyperlink text to an embed?

Embeds can use standard markdown in the Description as well as in field values. With that in mind, links can be added using the following format [text](link).

How do I add reactions to a message?

Any entities that implement IUserMessage has an AddReactionAsync method. This method expects an IEmote as a parameter. In Discord.Net, an Emote represents a server custom emote, while an Emoji is a Unicode emoji (standard emoji). Both Emoji and Emote implement IEmote and are valid options.

// bail if the message is not a user one (system messages cannot have reactions)
var usermsg = msg as IUserMessage;
if (usermsg == null) return;

// standard Unicode emojis
Emoji emoji = new Emoji("👍");
// or
// Emoji emoji = new Emoji("\u23F8");

// custom guild emotes
Emote emote = Emote.Parse("<:dotnet:232902710280716288>");  
// using Emote.TryParse may be safer in regards to errors being thrown;
// please note that the method does not verify if the emote exists,
// it simply creates the Emote object for you.

// add the reaction to the message
await usermsg.AddReactionAsync(emoji); 
await usermsg.AddReactionAsync(emote); 

Why am I getting so many preemptive rate limits when I try to add more than one reactions?

This is due to how .NET parses the HTML header, mistreating 0.25sec/action to 1sec. This casues the lib to throw preemptive rate limit more frequently than it should for methods such as adding reactions.

Can I opt-out of preemptive rate limits?

Unfortunately, not at the moment. See #401.

Advanced Operations

I want to create a service that sends a message or does things at a regular interval!

You will need to create a separate service alongside the program. Here's an example to get you started.

Command-related Questions

How can I restrict some of my commands so only certain users can execute them?

Based on how you want to implement the restrictions, you can use the built-in RequireUserPermission precondition, which allows you to restrict the command based on the user's current permissions in the guild or channel (e.g. GuildPermission.Administrator, ChannelPermission.ManageMessages etc.). If, however, you wish to restrict the commands based on the user's role, you can eithe create your own custom precondition or use Joe4evr's Preconditions Addons that provides a few custom preconditions that aren't provided in the stock library. Its source can also be used as an example for creating your own custom preconditions.

I'm getting an error about Assembly#GetEntryAssembly. What now?

You may be confusing CommandService#AddModulesAsync with CommandService#AddModuleAsync. The former is used to add modules via the assembly, while the latter is used to add a single module.

What does [Remainder] do in the command signature?

The RemainderAttribute leaves the string unparsed, meaning you don't have to add quotes around the text for the text to be recognized as a single object. Please note that if your method has multiple parameters, the remainder attribute can only be applied to the last parameter.

// !echo repeat this message in chat
[Command("echo")]
[Summary("Replies whatever the user adds")]
[Remarks("The entire message is considered one String")]
public Task EchoAsync([Remainder]String text) => ReplyAsync(text);  

// !echo repeat this message in chat
[Command("echo")]
[Summary("Replies whatever the user adds")]
[Remarks("This command will error for having too many arguments.  
The message would be seen as having 5 parameters while the method only accepts one.  
Wrapping the message in quotes solves this - '!echo repeat this message in chat' -  
this way, the system knows the entire message is to be parsed as a single String")]
public Task EchoAsync(String text) => ReplyAsync(text);

What is a service? Why does my module not hold any data after execution?

In Discord.NET, modules are created similarly to ASP.NET, meaning that they have a transient nature. This means that they are spawned every time when a request is received, and are killed from memory when the execution finishes. This is why you cannot store persistent data inside a module. To workaround this, consider using a service. Service is often used to hold data externally, so that they will persist throughout execution. Think of it like a chest that holds whatever you throw at it that won't be affected by anything unless you want it to. Note that you should also learn Microsoft's implementation of Dependency Injection before proceeding. You can learn more about it here, and how it works in Discord.NET here. A brief example of service and dependency injection can be seen below,

public class MyService
{
    public string MyCoolString {get; set;}
}
public class SetupOrWhatever
{
    public IServiceProvider BuildProvider() => new ServiceCollection().AddSingleton<MyService>().BuildServiceProvider();
}
public class MyModule : ModuleBase<SocketCommandContext>
{
    // Inject via public settable prop
    public MyService MyService {get; set;}
    // or via ctor
    private readonly MyService _myService;
    public MyModule (MyService myService) => _myService = myService;
    [Command("setorprintstring")]
    public Task GetOrSetStringAsync()
    {
        if (_myService.MyCoolString == null) _myService.MyCoolString = "ya boi";
        return ReplyAsync(_myService.MyCoolString);
    }
}

I have a long-running Task in my command, and Discord.NET keeps saying that a MessageReceived handler is blocking the gateway. What gives?

By default, all commands are executed on the same thread as the gateway task, which is responsible for keeping the connection from your client to Discord alive. When you execute a long-running task, this blocks the gateway from communicating for as long as the command task is being executed. The library will warn you about any long running event handler (in this case, the command handler) that persists for more than 3 seconds.

To resolve this, the library has designed a flag called RunMode. There are 2 main RunModes. One being RunMode.Sync, which is the default; another being RunMode.Async. RunMode.Async essentially calls an unawaited Task and continues with the execution without waiting for the command task to finish. You should use RunMode.Async in either the CommandAttribute or the DefaultRunMode flag in CommandServiceConfig. Further details regarding RunMode.Async can be found below.

Okay, that's great and all, but how does RunMode.Async work, and if it's so great, why is the lib not using it by default?

As with any async operation, RunMode.Async also comes at a cost. The following are the caveats with RunMode.Async,

  1. You introduce race condition.
  2. Unnecessary overhead caused by async state machine (learn more about it here).
  3. CommandService#ExecuteAsync will immediately return ExecuteResult instead of other result types (this is particularly important for those who wish to utilize RuntimeResult in 2.0).
  4. Exceptions are swallowed.

However, there are ways to remedy #3 and #4.

For #3, in Discord.NET 2.0, the library introduces a new event called CommandExecuted, which is raised whenever the command is finished. This event will be called regardless of the RunMode type and will return the appropriate execution result

For #4, exceptions are caught in CommandService#Log under (CommandException)LogMessage.Exception.

Legacy Questions

X, Y, Z does not work! It doesn't return a valid value anymore.

If you're currently using 1.0.0, please upgrade to the latest 2.0 beta to ensure maximum compatibility. Several methods or props may be broken in 1.0.x and will not be fixed in the 1.0 branch due to their breaking nature. Notable breaking changes are as follows,

  • IChannel#IsNsfw has been replaced with ITextChannel#IsNsfw and now returns valid value in 2.0.
  • Bulk message removal (DeletedMessagesAsync) has been moved from IMessageChannel to ITextChannel.
  • IAsyncEnumerable#Flatten has been renamed to FlattenAsync.

I came from an earlier version of Discord.NET 1.0, and DependencyMap doesn't seem to exist anymore in the later revision? What happened to it?

The DependencyMap has been replaced with Microsoft's DependencyInjection Abstractions. An example usage can be seen here.

@Still34
Copy link
Author

Still34 commented Mar 10, 2020

So much useful stuff here, perfectly explained for beginners.

Then you'll love our nightly documentation, which has all of these FAQs included!

@AlexprogramSwe
Copy link

how do I get responses to my bot messages

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment