@@ -3,4 +3,10 @@ | |||||
This is where you will find documentation for all members and objects in Discord.Net | This is where you will find documentation for all members and objects in Discord.Net | ||||
**TODO:** Think of something useful to put on this page. | |||||
__Commonly Used Entities__ | |||||
@Discord.WebSocket | |||||
@Discord.WebSocket.DiscordSocketClient | |||||
@Discord.WebSocket.SocketGuildChannel | |||||
@Discord.WebSocket.SocketGuildUser | |||||
@Discord.WebSocket.SocketMessage | |||||
@Discord.WebSocket.SocketRole |
@@ -1,37 +1,60 @@ | |||||
# The Command Service | # The Command Service | ||||
[Discord.Commands](xref:Discord.Commands) provides an Attribute-based Command Parser. | |||||
[Discord.Commands](xref:Discord.Commands) provides an Attribute-based | |||||
Command Parser. | |||||
### Setup | ### Setup | ||||
To use Commands, you must create a [Commands Service](xref:Discord.Commands.CommandService) and a Command Handler. | |||||
To use Commands, you must create a | |||||
[Commands Service](xref:Discord.Commands.CommandService) | |||||
and a Command Handler. | |||||
Included below is a very bare-bones Command Handler. You can extend your Command Handler as much as you like, however the below is the bare minimum. | |||||
Included below is a very bare-bones Command Handler. You can extend | |||||
your Command Handler as much as you like, however the below is the | |||||
bare minimum. | |||||
[!code-csharp[Barebones Command Handler](samples/command_handler.cs)] | [!code-csharp[Barebones Command Handler](samples/command_handler.cs)] | ||||
## Commands | ## Commands | ||||
In 1.0, Commands are no longer implemented at runtime with a builder pattern. | |||||
While a builder pattern may be provided later, commands are created primarily with | |||||
attributes. | |||||
In 1.0, Commands are no longer implemented at runtime with a builder | |||||
pattern. While a builder pattern may be provided later, commands are | |||||
created primarily with attributes. | |||||
### Basic Structure | ### Basic Structure | ||||
All commands belong to a Module. (See the below section for creating modules.) | |||||
All commands belong to a Module. (See the below section for creating | |||||
modules). | |||||
All commands in a module must be defined as an `Task`, with at least one argument, | |||||
being the @Discord.IUserMessage representing the context of the command. | |||||
All commands in a module must be defined as a `Task`. | |||||
To add parameters to your command, add additional arguments to the `Task` of the command. | |||||
You are _not_ required to accept all arguments as `String`, they will be automatically parsed | |||||
into the type you specify for the arument. See the Example Module for an example of command parameters. | |||||
To add parameters to your command, you simply need to add parameters | |||||
to the Task that represents the command. You are _not_ required to | |||||
accept all arguments as `String`, they will be automatically parsed | |||||
into the type you specify for the arument. See the Example Module | |||||
for an example of command parameters. | |||||
## Modules | ## Modules | ||||
Modules serve as a host for commands you create. | |||||
Modules are an organizational pattern that allow you to write your | |||||
commands in different classes, and have them automatically loaded. | |||||
To create a module, create a class that you will place commands in. Flag this class with the `[Module]` attribute. You may optionally pass in a string to the `Module` attribute to set a prefix for all of the commands inside the module. | |||||
Discord.Net's implementation of Modules is influenced heavily from | |||||
ASP.Net Core's Controller pattern. This means that the lifetime of a | |||||
module instance is only as long as the command being ran in it. | |||||
**Avoid using long-running code** in your modules whereever possible. | |||||
You should **not** be implementing very much logic into your modules; | |||||
outsource to a service for that. | |||||
If you are unfamiliar with Inversion of Control, it is recommended to | |||||
read the MSDN article on [IoC] and [Dependency Injection]. | |||||
To create a module, create a class that inherits from | |||||
@Discord.Commands.ModuleBase. | |||||
[IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx | |||||
[Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx | |||||
### Example Module | ### Example Module | ||||
@@ -39,68 +62,95 @@ To create a module, create a class that you will place commands in. Flag this cl | |||||
#### Loading Modules Automatically | #### Loading Modules Automatically | ||||
The Command Service can automatically discover all classes in an Assembly that are flagged with the `Module` attribute, and load them. | |||||
The Command Service can automatically discover all classes in an | |||||
Assembly that inherit @Discord.Commands.ModuleBase, and load them. | |||||
To have a module opt-out of auto-loading, pass `autoload: false` in the Module attribute. | |||||
To have a module opt-out of auto-loading, pass `autoload: false` in | |||||
the Module attribute. | |||||
Invoke [CommandService.LoadAssembly](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_LoadAssembly) to discover modules and install them. | |||||
Invoke [CommandService.AddModules] to discover modules and install them. | |||||
[CommandService.AddModules]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModules | |||||
#### Loading Modules Manually | #### Loading Modules Manually | ||||
To manually load a module, invoke [CommandService.Load](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_Load), and pass in an instance of your module. | |||||
To manually load a module, invoke [CommandService.AddModule], | |||||
by passing in the generic type of your module, and optionally | |||||
a dependency map. | |||||
### Module Constructors | |||||
[CommandService.AddModule]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModule__1_Discord_Commands_IDependencyMap_ | |||||
When automatically loading modules, you are limited in your constructor. Using a constructor that accepts _no arguments_, or a constructor that accepts a @Discord.Commands.CommandService will always work. | |||||
### Module Constructors | |||||
Alternatively, you can use an @Discord.Commands.IDependencyMap, as shown below. | |||||
Modules are constructed using Dependency Injection. Any parameters | |||||
that are placed in the constructor must be injected into an | |||||
@Discord.Commands.IDependencyMap. Alternatively, you may accept an | |||||
IDependencyMap as an argument and extract services yourself. | |||||
### Command Groups | ### Command Groups | ||||
Command groups function similarly to Modules, but they must be contained inside a module. Simply create a **public** class inside a module, and flag it with the @Discord.Commands.GroupAttribute | |||||
Command Groups allow you to create a module where commands are prefixed. | |||||
To create a group, create a new module and flag it with the | |||||
@Discord.Commands.GroupAttribute. | |||||
>![NOTE] | |||||
>Groups do not _need_ to be modules. Only classes with commands should | |||||
>inherit from ModuleBase. If you plan on using a group for strictly | |||||
>organizational purposes, there is no reason to make it a module. | |||||
[!code-csharp[Groups Sample](samples/groups.cs)] | [!code-csharp[Groups Sample](samples/groups.cs)] | ||||
## Dependency Injection | ## Dependency Injection | ||||
The Commands Service includes a very basic implementation of Dependency Injection that allows you to have completely custom constructors, within certain limitations. | |||||
The commands service is bundled with a very barebones Dependency | |||||
Injection service for your convienence. It is recommended that | |||||
you use DI when writing your modules. | |||||
### Setup | ### Setup | ||||
First, you need to create an @Discord.Commands.IDependencyMap . The library includes @Discord.Commands.DependencyMap to help with this, however you may create your own IDependencyMap if you wish. | |||||
First, you need to create an @Discord.Commands.IDependencyMap. | |||||
The library includes @Discord.Commands.DependencyMap to help with | |||||
this, however you may create your own IDependencyMap if you wish. | |||||
Next, add the dependencies your modules will use to the map. | Next, add the dependencies your modules will use to the map. | ||||
Finally, pass the map into the `LoadAssembly` method. Your modules will automatically be loaded with this dependency map. | |||||
Finally, pass the map into the `LoadAssembly` method. | |||||
Your modules will automatically be loaded with this dependency map. | |||||
[!code-csharp[DependencyMap Setup](samples/dependency_map_setup.cs)] | [!code-csharp[DependencyMap Setup](samples/dependency_map_setup.cs)] | ||||
### Usage in Modules | ### Usage in Modules | ||||
In the constructor of your module, any parameters will be filled in by the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. | |||||
In the constructor of your module, any parameters will be filled in by | |||||
the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. | |||||
>[!NOTE] | >[!NOTE] | ||||
>If you accept `CommandService` or `IDependencyMap` as a parameter in your constructor, these parameters will be filled by the CommandService the module was loaded from, and the DependencyMap passed into it, respectively. | |||||
>If you accept `CommandService` or `IDependencyMap` as a parameter in | |||||
your constructor, these parameters will be filled by the | |||||
CommandService the module was loaded from, and the DependencyMap passed | |||||
into it, respectively. | |||||
[!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] | [!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] | ||||
# Preconditions | # Preconditions | ||||
Preconditions serve as a permissions system for your commands. Keep in mind, however, that they are | |||||
not limited to _just_ permissions, and can be as complex as you want them to be. | |||||
Preconditions serve as a permissions system for your commands. Keep in | |||||
mind, however, that they are not limited to _just_ permissions, and | |||||
can be as complex as you want them to be. | |||||
>[!NOTE] | >[!NOTE] | ||||
>Preconditions can be applied to Modules, Groups, or Commands. | >Preconditions can be applied to Modules, Groups, or Commands. | ||||
## Bundled Preconditions | ## Bundled Preconditions | ||||
@Discord.Commands ships with two built-in preconditions, @Discord.Commands.RequireContextAttribute | |||||
and @Discord.Commands.RequirePermissionAttribute. | |||||
@Discord.Commands ships with two built-in preconditions, | |||||
@Discord.Commands.RequireContextAttribute and | |||||
@Discord.Commands.RequirePermissionAttribute. | |||||
### RequireContext | ### RequireContext | ||||
@Discord.Commands.RequireContextAttribute is a precondition that requires your command to be | |||||
executed in the specified context. | |||||
@Discord.Commands.RequireContextAttribute is a precondition that | |||||
requires your command to be executed in the specified context. | |||||
You may require three different types of context: | You may require three different types of context: | ||||
* Guild | * Guild | ||||
@@ -113,29 +163,34 @@ Since these are `Flags`, you may OR them together. | |||||
### RequirePermission | ### RequirePermission | ||||
@Discord.Commands.RequirePermissionAttribute is a precondition that allows you to quickly | |||||
specfiy that a user must poesess a permission to execute a command. | |||||
@Discord.Commands.RequirePermissionAttribute is a precondition that | |||||
allows you to quickly specfiy that a user must poesess a permission | |||||
to execute a command. | |||||
You may require either a @Discord.GuildPermission or @Discord.ChannelPermission | |||||
You may require either a @Discord.GuildPermission or | |||||
@Discord.ChannelPermission | |||||
[!code-csharp[RequireContext](samples/require_permission.cs)] | [!code-csharp[RequireContext](samples/require_permission.cs)] | ||||
## Custom Preconditions | ## Custom Preconditions | ||||
To write your own preconditions, create a new class that inherits from @Discord.Commands.PreconditionAttribute | |||||
To write your own preconditions, create a new class that inherits from | |||||
@Discord.Commands.PreconditionAttribute | |||||
In order for your precondition to function, you will need to override `CheckPermissions`, | |||||
which is a `Task<PreconditionResult>`. | |||||
In order for your precondition to function, you will need to override | |||||
`CheckPermissions`, which is a `Task<PreconditionResult>`. | |||||
Your IDE should provide an option to fill this in for you. | Your IDE should provide an option to fill this in for you. | ||||
Return `PreconditionResult.FromSuccess()` if the context met the required parameters, otherwise | |||||
return `PreconditionResult.FromError()`, optionally including an error message. | |||||
Return `PreconditionResult.FromSuccess()` if the context met the | |||||
required parameters, otherwise return `PreconditionResult.FromError()`, | |||||
optionally including an error message. | |||||
[!code-csharp[Custom Precondition](samples/require_owner.cs)] | [!code-csharp[Custom Precondition](samples/require_owner.cs)] | ||||
# Type Readers | # Type Readers | ||||
Type Readers allow you to parse different types of arguments in your commands. | |||||
Type Readers allow you to parse different types of arguments in | |||||
your commands. | |||||
By default, the following Types are supported arguments: | By default, the following Types are supported arguments: | ||||
@@ -153,16 +208,20 @@ By default, the following Types are supported arguments: | |||||
### Creating a Type Readers | ### Creating a Type Readers | ||||
To create a TypeReader, create a new class that imports @Discord and @Discord.Commands . Ensure your class inherits from @Discord.Commands.TypeReader | |||||
To create a TypeReader, create a new class that imports @Discord and | |||||
@Discord.Commands. Ensure your class inherits from @Discord.Commands.TypeReader | |||||
Next, satisfy the `TypeReader` class by overriding `Task<TypeReaderResult> Read(IUserMessage context, string input)`. | |||||
Next, satisfy the `TypeReader` class by overriding `Task<TypeReaderResult> Read(CommandContext context, string input)`. | |||||
>[!NOTE] | >[!NOTE] | ||||
>In many cases, Visual Studio can fill this in for you, using the "Implement Abstract Class" IntelliSense hint. | |||||
>In many cases, Visual Studio can fill this in for you, using the | |||||
>"Implement Abstract Class" IntelliSense hint. | |||||
Inside this task, add whatever logic you need to parse the input string. | Inside this task, add whatever logic you need to parse the input string. | ||||
Finally, return a `TypeReaderResult`. If you were able to successfully parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. Otherwise, return `TypeReaderResult.FromError`. | |||||
Finally, return a `TypeReaderResult`. If you were able to successfully | |||||
parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. | |||||
Otherwise, return `TypeReaderResult.FromError`. | |||||
#### Sample | #### Sample | ||||
@@ -170,4 +229,5 @@ Finally, return a `TypeReaderResult`. If you were able to successfully parse the | |||||
### Installing TypeReaders | ### Installing TypeReaders | ||||
TypeReaders are not automatically discovered by the Command Service, and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). | |||||
TypeReaders are not automatically discovered by the Command Service, | |||||
and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). |
@@ -16,6 +16,8 @@ Bot accounts must be added to a server, you must use the [OAuth 2 Flow](https:// | |||||
You can install Discord.Net 1.0 from our [MyGet Feed](https://www.myget.org/feed/Packages/discord-net). | You can install Discord.Net 1.0 from our [MyGet Feed](https://www.myget.org/feed/Packages/discord-net). | ||||
**For most users writing bots, install only `Discord.Net.WebSocket`.** | |||||
You may add the MyGet feed to Visual Studio directly from `https://www.myget.org/F/discord-net/api/v3/index.json`. | You may add the MyGet feed to Visual Studio directly from `https://www.myget.org/F/discord-net/api/v3/index.json`. | ||||
You can also pull the latest source from [GitHub](https://github.com/RogueException/Discord.Net). | You can also pull the latest source from [GitHub](https://github.com/RogueException/Discord.Net). | ||||
@@ -17,8 +17,4 @@ title: Samples | |||||
#### Sending a message to a channel | #### Sending a message to a channel | ||||
[!code-csharp[Message to Channel](samples/faq/send_message.cs)] | |||||
#### Retrieving an IGuild from an IUserMessage | |||||
[!code-csharp[Message to Guild](samples/faq/guild_from_message.cs)] | |||||
[!code-csharp[Message to Channel](samples/faq/send_message.cs)] |
@@ -33,21 +33,21 @@ public class Program | |||||
// Discover all of the commands in this assembly and load them. | // Discover all of the commands in this assembly and load them. | ||||
await commands.LoadAssembly(Assembly.GetEntryAssembly()); | await commands.LoadAssembly(Assembly.GetEntryAssembly()); | ||||
} | } | ||||
public async Task HandleCommand(IMessage paramMessage) | |||||
public async Task HandleCommand(SocketMessage messageParam) | |||||
{ | { | ||||
// Cast paramMessage to an IUserMessage, return if the message was a System message. | |||||
var msg = paramMessage as IUserMessage; | |||||
if (msg == null) return; | |||||
// Internal integer, marks where the command begins | |||||
// Don't process the command if it was a System Message | |||||
var message = messageParam as SocketUserMessage; | |||||
if (message == null) return; | |||||
// Create a number to track where the prefix ends and the command begins | |||||
int argPos = 0; | int argPos = 0; | ||||
// Get the current user (used for Mention parsing) | |||||
var currentUser = await client.GetCurrentUserAsync(); | |||||
// Determine if the message is a command, based on if it starts with '!' or a mention prefix | // Determine if the message is a command, based on if it starts with '!' or a mention prefix | ||||
if (msg.HasCharPrefix('!', ref argPos) || msg.HasMentionPrefix(currentUser, ref argPos)) | |||||
if (message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos)) | |||||
{ | { | ||||
// Execute the command. (result does not indicate a return value, | |||||
// Create a Command Context | |||||
var context = new CommandContext(client, message); | |||||
// Execute the command. (result does not indicate a return value, | |||||
// rather an object stating if the command executed succesfully) | // rather an object stating if the command executed succesfully) | ||||
var result = await _commands.Execute(msg, argPos); | |||||
var result = await _commands.Execute(context, argPos); | |||||
if (!result.IsSuccess) | if (!result.IsSuccess) | ||||
await msg.Channel.SendMessageAsync(result.ErrorReason); | await msg.Channel.SendMessageAsync(result.ErrorReason); | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
using Discord; | using Discord; | ||||
using Discord.Commands; | using Discord.Commands; | ||||
using Discord.WebSocket; | using Discord.WebSocket; | ||||
using foxboat.Services; | |||||
public class Commands | public class Commands | ||||
{ | { | ||||
@@ -8,10 +9,16 @@ public class Commands | |||||
{ | { | ||||
var commands = new CommandService(); | var commands = new CommandService(); | ||||
var map = new DependencyMap(); | var map = new DependencyMap(); | ||||
map.Add<IDiscordClient>(client); | |||||
var self = await client.GetCurrentUserAsync(); | |||||
map.Add<ISelfUser>(self); | |||||
map.Add(client); | |||||
map.Add(commands); | |||||
await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); | await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); | ||||
} | } | ||||
// In ConfigureServices, we will inject the Dependency Map with | |||||
// all of the services our client will use. | |||||
public Task ConfigureServices(IDependencyMap map) | |||||
{ | |||||
map.Add(new NotificationService(map)); | |||||
map.Add(new DatabaseService(map)); | |||||
} | |||||
// ... | // ... | ||||
} | } |
@@ -1,5 +1,5 @@ | |||||
public async Task ChangeAvatar() | public async Task ChangeAvatar() | ||||
{ | { | ||||
var fileStream = new FileStream("./newAvatar.png", FileMode.Open); | var fileStream = new FileStream("./newAvatar.png", FileMode.Open); | ||||
await (await _client.GetCurrentUserAsync()).ModifyAsync(x => x.Avatar = fileStream); | |||||
await _client.CurrentUser.ModifyAsync(x => x.Avatar = fileStream); | |||||
} | } |
@@ -1,4 +0,0 @@ | |||||
public async Task MessageReceived(IMessage msg) | |||||
{ | |||||
var guild = (msg.Channel as IGuildChannel)?.Guild; | |||||
} |
@@ -1,6 +1,6 @@ | |||||
public async Task SendMessageToChannel(ulong ChannelId) | public async Task SendMessageToChannel(ulong ChannelId) | ||||
{ | { | ||||
var channel = await _client.GetChannelAsync(ChannelId) as IMessageChannel; | |||||
var channel = _client.GetChannel(ChannelId) as ISocketMessageChannel; | |||||
await channel?.SendMessageAsync("aaaaaaaaahhh!!!") | await channel?.SendMessageAsync("aaaaaaaaahhh!!!") | ||||
/* ^ This question mark is used to indicate that 'channel' may sometimes be null, and in cases that it is null, we will do nothing here. */ | /* ^ This question mark is used to indicate that 'channel' may sometimes be null, and in cases that it is null, we will do nothing here. */ | ||||
} | } |
@@ -1,15 +1,15 @@ | |||||
[Module("admin")] | |||||
public class AdminModule | |||||
[Group("admin")] | |||||
public class AdminModule : ModuleBase | |||||
{ | { | ||||
[Group("mod")] | [Group("mod")] | ||||
public class ModerationGroup | |||||
public class ModerationGroup : ModuleBase | |||||
{ | { | ||||
// ~admin mod ban foxbot#0282 | // ~admin mod ban foxbot#0282 | ||||
[Command("ban")] | [Command("ban")] | ||||
public async Task Ban(IUserMessage msg, IGuildUser user) { } | |||||
public async Task Ban(IGuildUser user) { } | |||||
} | } | ||||
// ~admin clean 100 | // ~admin clean 100 | ||||
[Command("clean")] | [Command("clean")] | ||||
public async Task Clean(IUserMessage msg, int count = 100) { } | |||||
public async Task Clean(int count = 100) { } | |||||
} | } |
@@ -1,29 +1,29 @@ | |||||
using Discord; | |||||
using Discord.Commands; | using Discord.Commands; | ||||
using Discord.WebSocket; | using Discord.WebSocket; | ||||
// Create a module with no prefix | // Create a module with no prefix | ||||
[Module] | |||||
public class Info | |||||
public class Info : ModuleBase | |||||
{ | { | ||||
// ~say hello -> hello | // ~say hello -> hello | ||||
[Command("say"), Summary("Echos a message.")] | [Command("say"), Summary("Echos a message.")] | ||||
public async Task Say(IUserMessage msg, | |||||
[Unparsed, Summary("The text to echo")] string echo) | |||||
public async Task Say([Unparsed, Summary("The text to echo")] string echo) | |||||
{ | { | ||||
await msg.Channel.SendMessageAsync(echo); | |||||
// ReplyAsync is a method on ModuleBase | |||||
await ReplyAsync(echo); | |||||
} | } | ||||
} | } | ||||
// Create a module with the 'sample' prefix | // Create a module with the 'sample' prefix | ||||
[Module("sample")] | |||||
public class Sample | |||||
[Group("sample")] | |||||
public class Sample : ModuleBase | |||||
{ | { | ||||
// ~sample square 20 -> | |||||
// ~sample square 20 -> 400 | |||||
[Command("square"), Summary("Squares a number.")] | [Command("square"), Summary("Squares a number.")] | ||||
public async Task Square(IUserMessage msg, | |||||
[Summary("The number to square.")] int num) | |||||
public async Task Square([Summary("The number to square.")] int num) | |||||
{ | { | ||||
await msg.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); | |||||
// We can also access the channel from the Command Context. | |||||
await Context.Channel.SendMessageAsync($"{num}^2 = {Math.Pow(num, 2)}"); | |||||
} | } | ||||
// ~sample userinfo --> foxbot#0282 | // ~sample userinfo --> foxbot#0282 | ||||
@@ -34,10 +34,9 @@ public class Sample | |||||
// ~sample whois 96642168176807936 --> Khionu#8708 | // ~sample whois 96642168176807936 --> Khionu#8708 | ||||
[Command("userinfo"), Summary("Returns info about the current user, or the user parameter, if one passed.")] | [Command("userinfo"), Summary("Returns info about the current user, or the user parameter, if one passed.")] | ||||
[Alias("user", "whois")] | [Alias("user", "whois")] | ||||
public async Task UserInfo(IUserMessage msg, | |||||
[Summary("The (optional) user to get info for")] IUser user = null) | |||||
public async Task UserInfo([Summary("The (optional) user to get info for")] IUser user = null) | |||||
{ | { | ||||
var userInfo = user ?? await Program.Client.GetCurrentUserAsync(); | |||||
await msg.Channel.SendMessageAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | |||||
var userInfo = user ?? Context.Client.CurrentUser; | |||||
await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); | |||||
} | } | ||||
} | } |
@@ -1,11 +1,10 @@ | |||||
[Module] | |||||
public class InfoModule | |||||
public class InfoModule : ModuleBase | |||||
{ | { | ||||
// Constrain this command to Guilds | // Constrain this command to Guilds | ||||
[RequireContext(ContextType.Guild)] | [RequireContext(ContextType.Guild)] | ||||
public async Task Whois(IUserMessage msg, IGuildUser user) { } | |||||
public async Task Whois(IGuildUser user) { } | |||||
// Constrain this command to either Guilds or DMs | // Constrain this command to either Guilds or DMs | ||||
[RequireContext(ContextType.Guild | ContextType.DM)] | [RequireContext(ContextType.Guild | ContextType.DM)] | ||||
public async Task Info(IUserMessage msg) { } | |||||
public async Task Info() { } | |||||
} | } |
@@ -1,16 +1,18 @@ | |||||
// Defining the Precondition | // Defining the Precondition | ||||
// Specify that this precondition can target a Class (Module/Group) or Method (Command) | |||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | |||||
// Inherit from PreconditionAttribute | // Inherit from PreconditionAttribute | ||||
public class RequireOwnerAttribute : PreconditionAttribute | public class RequireOwnerAttribute : PreconditionAttribute | ||||
{ | { | ||||
public readonly ulong OwnerId = 66078337084162048; | |||||
// Override the CheckPermissions method | // Override the CheckPermissions method | ||||
public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) | |||||
public override Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) | |||||
{ | { | ||||
// If the author of the message is '66078337084162048', return success; otherwise fail. | |||||
return Task.FromResult(context.Author.Id == OwnerId ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("You must be the owner of the bot.")); | |||||
// Get the ID of the bot's owner | |||||
var ownerId = (await map.Get<DiscordSocketClient>().GetApplicationInfoAsync()).Owner.Id; | |||||
// If this command was executed by that user, return a success | |||||
if (context.User.Id == ownerId) | |||||
return PreconditionResult.FromSuccess(); | |||||
// Since it wasn't, fail | |||||
else | |||||
return PreconditionResult.FromError("You must be the owner of the bot to run this command."); | |||||
} | } | ||||
} | } |
@@ -1,7 +1,6 @@ | |||||
[Module] | |||||
public class AdminModule | |||||
public class AdminModule : ModuleBase | |||||
{ | { | ||||
[Command("ban")] | [Command("ban")] | ||||
[RequirePermission(GuildPermission.BanMembers)] | [RequirePermission(GuildPermission.BanMembers)] | ||||
public async Task Ban(IUserMessage msg) { } | |||||
public async Task Ban(IGuildUser target) { } | |||||
} | } |
@@ -3,7 +3,7 @@ using Discord.Commands; | |||||
public class BooleanTypeReader : TypeReader | public class BooleanTypeReader : TypeReader | ||||
{ | { | ||||
public override Task<TypeReaderResult> Read(IUserMessage context, string input) | |||||
public override Task<TypeReaderResult> Read(CommandContext context, string input) | |||||
{ | { | ||||
bool result; | bool result; | ||||
if (bool.TryParse(input, out result)) | if (bool.TryParse(input, out result)) | ||||
@@ -9,17 +9,30 @@ title: Terminology | |||||
Most terms for objects remain the same between 0.9 and 1.0. The major difference is that the ``Server`` is now called ``Guild``, to stay in line with Discord internally | Most terms for objects remain the same between 0.9 and 1.0. The major difference is that the ``Server`` is now called ``Guild``, to stay in line with Discord internally | ||||
## Introduction to Interfaces | |||||
## Implementation Specific Entities | |||||
Discord.Net 1.0 is built strictly around Interfaces. There are no methods that return a concrete object, only an interface. | |||||
Discord.Net 1.0 is split into a core library, and three different | |||||
implementations - Discord.Net.Core, Discord.Net.Rest, Discord.Net.Rpc, | |||||
and Discord.Net.WebSockets. | |||||
Many of the interfaces in Discord.Net are linked through inheritance. For example, @Discord.IChannel represents any channel in Discord. @Discord.IGuildChannel inherits from IChannel, and represents all channels belonging to a Guild. As a result, @Discord.IChannel can sometimes be cast to @Discord.IGuildChannel, and you may find yourself doing this frequently in order to properly utilize the library. | |||||
As a bot developer, you will only need to use Discord.Net.WebSockets, | |||||
but you should be aware of the differences between them. | |||||
### The Inheritance Tree | |||||
`Discord.Net.Core` provides a set of interfaces that model Discord's | |||||
API. These interfaces are consistent throughout all implementations of | |||||
Discord.Net, and if you are writing an implementation-agnostic library | |||||
or addon, you can rely on the core interfaces to ensure that your | |||||
addon will run on all platforms. | |||||
You may want to familiarize yourself with the inheritance in Discord.Net. An inheritance tree is provided below. | |||||
`Discord.Net.Rest` provides a set of concrete classes to be used | |||||
**strictly** with the REST portion of Discord's API. Entities in | |||||
this implementation are prefixed with `Rest`, e.g. `RestChannel`. | |||||
 | |||||
 | |||||
 | |||||
 | |||||
`Discord.Net.Rpc` provides a set of concrete classes that are used with | |||||
Discord's RPC API. Entities in this implementation are prefixed with | |||||
`Rpc`, e.g. `RpcChannel`. | |||||
`Discord.Net.WebSocket` provides a set of concrete classes that are used | |||||
primarily with Discord's WebSocket API, or entities that are kept in | |||||
cache. When developing bots, you will be using this implementation. All | |||||
entities are prefixed with `Socket`, e.g. `SocketChannel`. |
@@ -5,11 +5,6 @@ changes in the design of the library.** | |||||
>A medium to advanced understanding is recommended when working with this library. | >A medium to advanced understanding is recommended when working with this library. | ||||
One of the biggest major changes from `0.9.x` is the exclusive use of interfaces. | |||||
For the most part, your usability will be very similar to the 0.9 approach of concrete | |||||
classes. You **will** be required to cast some entities; this is outlined in a later | |||||
section. | |||||
It is recommended to familiarize yourself with the entities in 1.0 before continuing. | It is recommended to familiarize yourself with the entities in 1.0 before continuing. | ||||
Feel free to look through the library's source directly, look through IntelliSense, or | Feel free to look through the library's source directly, look through IntelliSense, or | ||||
look through our hosted [API Documentation](xref:Discord). | look through our hosted [API Documentation](xref:Discord). | ||||
@@ -19,8 +14,7 @@ look through our hosted [API Documentation](xref:Discord). | |||||
Most API models function _similarly_ to 0.9, however their names have been changed. | Most API models function _similarly_ to 0.9, however their names have been changed. | ||||
You should also keep in mind that we now separate different types of Channels and Users. | You should also keep in mind that we now separate different types of Channels and Users. | ||||
Take a look at inheritance section of @Terminology for an example of how inheritance and interfaces | |||||
work in 1.0 | |||||
Before proceeding, please read over @Terminology to understand the naming behind some objects. | |||||
Below is a table that compares most common 0.9 entities to their 1.0 counterparts. | Below is a table that compares most common 0.9 entities to their 1.0 counterparts. | ||||
@@ -29,18 +23,16 @@ Below is a table that compares most common 0.9 entities to their 1.0 counterpart | |||||
| 0.9 | 1.0 | Notice | | | 0.9 | 1.0 | Notice | | ||||
| --- | --- | ------ | | | --- | --- | ------ | | ||||
| Server | @Discord.IGuild | | |||||
| Channel | @Discord.IGuildChannel | Applies only to channels that are members of a Guild | | |||||
| Channel.IsPrivate | @Discord.IDMChannel | |||||
| ChannelType.Text | @Discord.ITextChannel | This applies only to Text Channels in Guilds | |||||
| ChannelType.Voice | @Discord.IVoiceChannel | This applies only to Voice Channels in Guilds | |||||
| User | @Discord.IGuildUser | This applies only to users belonging to a Guild* | |||||
| Profile | @Discord.ISelfUser | |||||
| Message | @Discord.IUserMessage | |||||
\* To retrieve an @Discord.IGuildUser, you must retrieve the user from an @Discord.IGuild. | |||||
[IDiscordClient.GetUserAsync](xref:Discord.IDiscordClient#Discord_IDiscordClient_GetUserAsync_System_UInt64_) | |||||
returns a @Discord.IUser, which only contains the information that Discord exposes for public users. | |||||
| Server | @Discord.WebSocket.SocketGuild | | |||||
| Channel | @Discord.WebSocket.SocketGuildChannel | Applies only to channels that are members of a Guild | | |||||
| Channel.IsPrivate | @Discord.WebSocket.SocketDMChannel | |||||
| ChannelType.Text | @Discord.WebSocket.SocketTextChannel | This applies only to Text Channels in Guilds | |||||
| ChannelType.Voice | @Discord.WebSocket.SocketVoiceChannel | This applies only to Voice Channels in Guilds | |||||
| User | @Discord.WebSocket.SocketGuildUser | This applies only to users belonging to a Guild* | |||||
| Profile | @Discord.WebSocket.SocketGuildUser | |||||
| Message | @Discord.WebSocket.SocketUserMessage | |||||
\* To retrieve an @Discord.WebSocket.SocketGuildUser, you must retrieve the user from an @Discord.WebSocket.SocketGuild. | |||||
## Event Registration | ## Event Registration | ||||
@@ -67,15 +59,3 @@ API docs before implementing it. | |||||
## Async | ## Async | ||||
Nearly everything in 1.0 is an async Task. You should always await any tasks you invoke. | Nearly everything in 1.0 is an async Task. You should always await any tasks you invoke. | ||||
However, when using WebSockets, you may find this both inconvienent, and unnecessary, as many of the | |||||
WebSocket implementations of the interfaces keep their own local cache of objects, rendering the use | |||||
of async redundant. | |||||
**As of right now,** there are extension methods you can use, located in `Discord.WebSocket` that will | |||||
provide java-esque, synchronus `GetXXX` methods to replace the asynchronus methods on WebSocket entities. | |||||
This functionality may be changed at a later date, we are currently reviewing this implementation and | |||||
alternative methods. | |||||
For your reference, you may want to look through the extension classes located in @Discord.WebSocket |