So you are a developer that wants to build a Discord bot and you don't know
where to start, right? Well, you are in the right place, follow this guide and
learn the basics of the Discord API.
Discord does not provide wrappers for it's API so instead it advertises
some 3rd Party ones. Developers have created lots of wrappers but the one we
are using is
Discord.NET.
Discord.NET is a 3rd party Discord API wrapper for C#. It is not the only
one as there is also
DSharp+. The
most common library is
discord.js. It is
widely used for developing bots because it is the easiest to use.
Discord.NET doesn't have much documentation but it has a very efficient
logger service that will say exactly what happens when a command is
executed.
Onto the code now!
The first thing that you need to do is create a C# console project. Name
the project with the name of your bot. Once you got that ready, head over to
the
Discord Developer Portal and create an application. Name the application with the same name as
your console project. Once the application is created head over to the
"Bots" tab and click create bot.
Note: The action is irreversible, once you create a bot you cannot delete
it.
Once the bot is created don't close the page but head
over to your code. You should have 2 classes already created: Startup.cs and
Program.cs. Head over to the NuGet package manager and install these
packages:
Once you got them installed proceed to to Program.cs and type the
following:
using System;
using System.Threading.Tasks;
using Discord.Net;
namespace YourProjectName
{
class
Program
{
public
static async Task Main(string[] args)
=>
await Startup.RunAsync(args);
This class will return an error saying that it cannot find the RunAsync
method. We will create this method in the Startup.cs class. Now
switch to the Startup.cs class and type the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text; using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Brobot.Services;
namespace Brobot
{
public class Startup
{
public IConfigurationRoot configuration {
get; }
public Startup(string[] args)
{
var
builder = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddYamlFile("_config.yml");
configuration = builder.Build();
}
public static async Task RunAsync(string[]
args)
{
var startup =
new Startup(args);
await
startup.RunAsync();
}
public async Task RunAsync()
{
var services = new ServiceCollection();
ConfigureServices(services);
var provider =
services.BuildServiceProvider();
provider.GetRequiredService();
var logging =
provider.GetRequiredService();
await
provider.GetRequiredService().StartAsync();
await
Task.Delay(-1);
}
private void ConfigureServices(ServiceCollection
services)
{
services.AddSingleton(new
DiscordSocketClient(new DiscordSocketConfig
{
LogLevel =
Discord.LogSeverity.Verbose,
MessageCacheSize =
1000
}))
.AddSingleton(new
CommandService(new CommandServiceConfig
{
LogLevel = Discord.LogSeverity.Verbose,
DefaultRunMode =
RunMode.Async,
CaseSensitiveCommands =
false
}))
.AddSingleton<CommandHandler>()
.AddSingleton<LoggerService()
.AddSingleton<StartupService>()
.AddSingleton(configuration);
}
}
}
This is basically the class that initialises the basics of the bot
and how it is supposed to work. This class will also start up the Logger
Service, Command Handler and the Startup service.
Now create a
new folder and call it Services. Add a class StartupService.cs and type the
following code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
namespace Brobot.Services
{
public class StartupService
{
public static IServiceProvider _provider;
private readonly DiscordSocketClient
_discord;
private readonly CommandService _commands;
private readonly IConfigurationRoot _config;
public StartupService(IServiceProvider
provider, DiscordSocketClient discord, CommandService commands,
IConfigurationRoot config)
{
_provider = provider;
_discord = discord;
_config = config;
_commands = commands;
}
public async Task StartAsync()
{
string token =
_config["token:discord"];
if
(string.IsNullOrEmpty(token))
{
Console.WriteLine("Provide the Discord token in _config.yml. If the file
is not present, make it!");
return;
}
await
_discord.LoginAsync(TokenType.Bot, token);
await _discord.StartAsync();
await
_commands.AddModulesAsync(Assembly.GetEntryAssembly(), _provider);
}
}
}
This class will require you to set up a config.yml file. In this file
you will paste the bot's token and prefix
prefix: 'bot's prefix!'
token:
discord: 'the token goes here'
now you're all set. the bot will start but not do anything.
In the service folder add a new class and call it
CommandHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
namespace Brobot.Services
{
public class CommandHandler
{
public static IServiceProvider
_provider;
public static DiscordSocketClient
_discord;
public static CommandService
_commands;
public static IConfigurationRoot
_config;
public
CommandHandler(DiscordSocketClient discord, CommandService
commands, IConfigurationRoot config, IServiceProvider provider)
{
_provider = provider;
_discord = discord;
_config = config;
_commands = commands;
_discord.Ready +=
OnReady;
_discord.MessageReceived
+= OnMessageReceived;
}
private async Task
OnMessageReceived(SocketMessage arg)
{
var msg = arg as
SocketUserMessage;
if (msg.Author.IsBot)
return;
var context = new
SocketCommandContext(_discord, msg);
int pos = 0;
//msg.HasStringPrefix(_config["prefix"], ref pos) || just
for the prefix
if
(msg.HasStringPrefix(_config["prefix"], ref pos) ||
msg.HasMentionPrefix(_discord.CurrentUser, ref pos))
{
var result
= await _commands.ExecuteAsync(context, pos, _provider);
if
(!result.IsSuccess)
{
var reason = result.Error;
Console.WriteLine("There has been an error!");
Console.WriteLine(reason);
Console.WriteLine(result);
await context.Channel.SendMessageAsync($"There has been an
error in executing your command. Here's the error: \n
```{result}``` \n Please contact if you encounter further
problems." +
$"\n Run for error
definitions");//error message
}
}
}
public Task OnReady()
{
_discord.SetGameAsync("Version 1.1.0");
Console.WriteLine("Brobot is up and running!");//login message
Console.WriteLine($"Connected as:
{_discord.CurrentUser.Username}#{_discord.CurrentUser.Discriminator}");
return
Task.CompletedTask;
}
}
}
This class will handle all the commands and post errors in the
console and in the discord channels. You can change the error
message and the Login message and the Activity to whatever you
want!
Now in the same folder make a class and call it
LoggerService.cs
Type the following code
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
public class LoggingService
{
public
LoggingService(DiscordSocketClient client, CommandService
command)
{
client.Log +=
LogAsync;
command.Log +=
LogAsync;
}
private Task
LogAsync(LogMessage message)
{
if (message.Exception
is CommandException cmdException)
{
Console.WriteLine($"[Command/{message.Severity}]
{cmdException.Command.Aliases.First()}"
+ $" failed to execute
in {cmdException.Context.Channel}.");
Console.WriteLine(cmdException);
}
else
Console.WriteLine($"[General/{message.Severity}] {message}");
return
Task.CompletedTask;
}
}
This will create a logger that will post in the console when the bot connects, ping time, where the bot connects and any errors.
The bot is ready to run but it is missing the commands. Create a Commands folder in the main directory of the project and add a class. Name it whatever you want
The process is equal for most commands:
1. Make the main class public and inherit ModuleBase (just add " : ModuleBase" at the end of the class declaration
2. Create an async Task
3. add the command content and action!
Congratulations! you have built your first Discord bot in C#