From 3a60c58697b6940b9e99f8c6804c795146f95c4a Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 3 Apr 2017 18:46:00 -0400 Subject: [PATCH] Rewrite all concepts documentation hello RC documentation --- docs/guides/commands/commands.md | 4 + docs/guides/concepts/connections.md | 58 ++++++++++++++ docs/guides/concepts/entities.md | 71 +++++++++++++++++ docs/guides/concepts/events.md | 86 +++++++++++++++++---- docs/guides/concepts/samples/connections.cs | 23 ++++++ docs/guides/concepts/samples/entities.cs | 13 ++++ docs/guides/concepts/samples/events.cs | 31 ++++++++ docs/guides/getting_started/intro.md | 2 +- docs/guides/toc.yml | 9 +++ docs/guides/voice/sending-voice.md | 3 +- docs/index.md | 2 +- 11 files changed, 284 insertions(+), 18 deletions(-) create mode 100644 docs/guides/concepts/connections.md create mode 100644 docs/guides/concepts/entities.md create mode 100644 docs/guides/concepts/samples/connections.cs create mode 100644 docs/guides/concepts/samples/entities.cs create mode 100644 docs/guides/concepts/samples/events.cs diff --git a/docs/guides/commands/commands.md b/docs/guides/commands/commands.md index 8f1a34db9..6dd595861 100644 --- a/docs/guides/commands/commands.md +++ b/docs/guides/commands/commands.md @@ -1,5 +1,9 @@ # The Command Service +>[!WARNING] +>This article is out of date, and has not been rewritten yet. +Information is not guaranteed to be accurate. + [Discord.Commands](xref:Discord.Commands) provides an Attribute-based Command Parser. diff --git a/docs/guides/concepts/connections.md b/docs/guides/concepts/connections.md new file mode 100644 index 000000000..30e5e55cd --- /dev/null +++ b/docs/guides/concepts/connections.md @@ -0,0 +1,58 @@ +--- +title: Managing Connections +--- + +In Discord.Net, once a client has been started, it will automatically +maintain a connection to Discord's gateway, until it is manually +stopped. + +### Usage + +To start a connection, invoke the `StartAsync` method on a client that +supports a WebSocket connection. + +These clients include the [DiscordSocketClient] and +[DiscordRpcClient], as well as Audio clients. + +To end a connection, invoke the `StopAsync` method. This will +gracefully close any open WebSocket or UdpSocket connections. + +Since the Start/Stop methods only signal to an underlying connection +manager that a connection needs to be started, **they return before a +connection is actually made.** + +As a result, you will need to hook into one of the connection-state +based events to have an accurate representation of when a client is +ready for use. + +All clients provide a `Connected` and `Disconnected` event, which is +raised respectively when a connection opens or closes. In the case of +the DiscordSocketClient, this does **not** mean that the client is +ready to be used. + +A separate event, `Ready`, is provided on DiscordSocketClient, which +is raised only when the client has finished guild stream or guild +sync, and has a complete guild cache. + +[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient +[DiscordRpcClient]: xref:Discord.Rpc.DiscordRpcClient + +### Samples + +[!code-csharp[Connection Sample](samples/events.cs)] + +### Tips + +Avoid running long-running code on the gateway! If you deadlock the +gateway (as explained in [events]), the connection manager will be +unable to recover and reconnect. + +Assuming the client disconnected because of a fault on Discord's end, +and not a deadlock on your end, we will always attempt to reconnect +and resume a connection. + +Don't worry about trying to maintain your own connections, the +connection manager is designed to be bulletproof and never fail - if +your client doesn't manage to reconnect, you've found a bug! + +[events]: events.md \ No newline at end of file diff --git a/docs/guides/concepts/entities.md b/docs/guides/concepts/entities.md new file mode 100644 index 000000000..b753293bb --- /dev/null +++ b/docs/guides/concepts/entities.md @@ -0,0 +1,71 @@ +--- +title: Entities +--- + +>[!NOTE] +This article is written with the Socket variants of entities in mind, +not the general interfaces or Rest/Rpc entities. + +Discord.Net provides a versatile entity system for navigating the +Discord API. + +### Inheritance + +Due to the nature of the Discord API, some entities are designed with +multiple variants, for example, `SocketUser` and `SocketGuildUser`. + +All models will contain the most detailed version of an entity +possible, even if the type is less detailed. + +For example, in the case of the `MessageReceived` event, a +`SocketMessage` is passed in with a channel property of type +`SocketMessageChannel`. All messages come from channels capable of +messaging, so this is the only variant of a channel that can cover +every single case. + +But that doesn't mean a message _can't_ come from a +`SocketTextChannel`, which is a message channel in a guild. To +retrieve information about a guild from a message entity, you will +need to cast its channel object to a `SocketTextChannel`. + +### Navigation + +All socket entities have navigation properties on them, which allow +you to easily navigate to an entity's parent or children. As explained +above, you will sometimes need to cast to a more detailed version of +an entity to navigate to its parent. + +All socket entities have a `Discord` property, which will allow you +to access the parent `DiscordSocketClient`. + +### Accessing Entities + +The most basic forms of entities, `SocketGuild`, `SocketUser`, and +`SocketChannel` can be pulled from the DiscordSocketClient's global +cache, and can be retrieved using the respective `GetXXX` method on +DiscordSocketClient. + +>[!TIP] +It is **vital** that you use the proper IDs for an entity when using +a GetXXX method. It is recommended that you enable Discord's +_developer mode_ to allow easy access to entity IDs, found in +Settings > Appearance > Advanced + +More detailed versions of entities can be pulled from the basic +entities, e.g. `SocketGuild.GetUser`, which returns a +`SocketGuildUser`, or `SocketGuild.GetChannel`, which returns a +`SocketGuildChannel`. Again, you may need to cast these objects to get +a variant of the type that you need. + +### Samples + +[!code-csharp[Entity Sample](samples/entities.cs)] + +### Tips + +Avoid using boxing-casts to coerce entities into a variant, use the +`as` keyword, and a null-conditional operator. + +This allows you to write safer code, and avoid InvalidCastExceptions. + +For example, `(message.Author as SocketGuildUser)?.Nickname`. \ No newline at end of file diff --git a/docs/guides/concepts/events.md b/docs/guides/concepts/events.md index b10dc7648..f2dfb00f0 100644 --- a/docs/guides/concepts/events.md +++ b/docs/guides/concepts/events.md @@ -1,28 +1,84 @@ --- -title: Events +title: Working with Events --- -# Events +Events in Discord.Net are consumed in a similar manner to the standard +convention, with the exception that every event must be of the type +`System.Threading.Tasks.Task`, and instead of using EventArgs, the +event's parameters are passed directly into the handler. -Messages from Discord are exposed via events, and follow a pattern of `Func<[event params], Task>`, which allows you to easily create either async or sync event handlers. +This allows for events to be handled in an async context directly, +instead of relying on async void. -To hook into events, you must be using the @Discord.WebSocket.DiscordSocketClient, which provides WebSocket capabilities, necessary for receiving events. +### Usage ->[!NOTE] ->The gateway will wait for all registered handlers of an event to finish before raising the next event. As a result of this, it is reccomended that if you need to perform any heavy work in an event handler, it is done on its own thread or Task. +To receive data from an event, hook into it using C#'s delegate +event pattern. -**For further documentation of all events**, it is reccomended to look at the [Events Section](xref:Discord.WebSocket.DiscordSocketClient#events) on the API documentation of @Discord.WebSocket.DiscordSocketClient +You may opt either to hook an event to an anonymous function (lambda) +or a named function. -## Connection State +### Safety -Connection Events will be raised when the Connection State of your client changes. +All events are designed to be thread-safe, in that events are executed +synchronously off the gateway task, in the same context as the gateway +task. -[DiscordSocketClient.Connected](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_Connected) and [Disconnected](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_Disconnected) are raised when the Gateway Socket connects or disconnects, respectively. +As a side effect, this makes it possible to deadlock the gateway task, +and kill a connection. As a general rule of thumb, any task that takes +longer than three seconds should **not** be awaited directly in the +context of an event, but should be wrapped in a `Task.Run` or +offloaded to another task. ->[!WARNING] ->You should not use DiscordClient.Connected to run code when your client first connects to Discord. The client has not received and parsed the READY event and guild stream yet, and will have an incomplete or empty cache. +This also means that you should not await a task that requests data +from Discord's gateway in the same context of an event. Since the +gateway will wait on all invoked event handlers to finish before +processing any additional data from the gateway, this will create +a deadlock that will be impossible to recover from. -[DiscordSocketClient.Ready](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_Ready) is raised when the `READY` packet is parsed and received from Discord. +Exceptions in commands will be swallowed by the gateway and logged out +through the client's log method. ->[!NOTE] ->The [DiscordSocketClient.ConnectAsync](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_ConnectAsync_System_Boolean_) method will not return until the READY packet has been processed. By default, it also will not return until the guild stream has finished. This means it is safe to run bot code directly after awaiting the ConnectAsync method. \ No newline at end of file +### Common Patterns + +As you may know, events in Discord.Net are only given a signature of +`Func`. There is no room for predefined argument names, +so you must either consult IntelliSense, or view the API documentation +directly. + +That being said, there are a variety of common patterns that allow you +to infer what the parameters in an event mean. + +#### Entity, Entity + +An event handler with a signature of `Func` +typically means that the first object will be a clone of the entity +_before_ a change was made, and the latter object will be an attached +model of the entity _after_ the change was made. + +This pattern is typically only found on `EntityUpdated` events. + +#### Cacheable + +An event handler with a signature of `Func` +means that the `before` state of the entity was not provided by the +API, so it can either be pulled from the client's cache, or +downloaded from the API. + +See the documentation for [Cacheable] for more information on this +object. + +[Cacheable]: xref:Discord.Cacheable`2 + +### Samples + +[!code-csharp[Event Sample](samples/events.cs)] + +### Tips + +Many events relating to a Message entity, e.g. `MessageUpdated` +and `ReactionAdded` rely on the client's message cache, which is +**not** enabled by default. Set the `MessageCacheSize` flag in +[DiscordSocketConfig] to enable it. + +[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig \ No newline at end of file diff --git a/docs/guides/concepts/samples/connections.cs b/docs/guides/concepts/samples/connections.cs new file mode 100644 index 000000000..f96251a39 --- /dev/null +++ b/docs/guides/concepts/samples/connections.cs @@ -0,0 +1,23 @@ +using Discord; +using Discord.WebSocket; + +public class Program +{ + private DiscordSocketClient _client; + static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); + + public async Task MainAsync() + { + _client = new DiscordSocketClient(); + + await _client.LoginAsync(TokenType.Bot, "bot token"); + await _client.StartAsync(); + + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); + + await _client.StopAsync(); + // Wait a little for the client to finish disconnecting before allowing the program to return + await Task.Delay(500); + } +} \ No newline at end of file diff --git a/docs/guides/concepts/samples/entities.cs b/docs/guides/concepts/samples/entities.cs new file mode 100644 index 000000000..7655c44e9 --- /dev/null +++ b/docs/guides/concepts/samples/entities.cs @@ -0,0 +1,13 @@ +public string GetChannelTopic(ulong id) +{ + var channel = client.GetChannel(81384956881809408) as SocketTextChannel; + if (channel == null) return ""; + return channel.Topic; +} + +public string GuildOwner(SocketChannel channel) +{ + var guild = (channel as SocketGuildChannel)?.Guild; + if (guild == null) return ""; + return Context.Guild.Owner.Username; +} \ No newline at end of file diff --git a/docs/guides/concepts/samples/events.cs b/docs/guides/concepts/samples/events.cs new file mode 100644 index 000000000..c662b51a9 --- /dev/null +++ b/docs/guides/concepts/samples/events.cs @@ -0,0 +1,31 @@ +using Discord; +using Discord.WebSocket; + +public class Program +{ + private DiscordSocketClient _client; + static void Main(string[] args) => new Program().MainAsync().GetAwaiter().GetResult(); + + public async Task MainAsync() + { + _client = new DiscordSocketClient(); + + await _client.LoginAsync(TokenType.Bot, "bot token"); + await _client.StartAsync(); + + _client.MessageUpdated += MessageUpdated; + _client.Ready += () => + { + Console.WriteLine("Bot is connected!"); + return Task.CompletedTask; + } + + await Task.Delay(-1); + } + + private async Task MessageUpdated(Cacheable before, SocketMessage after, ISocketMessageChannel channel) + { + var message = await before.GetOrDownloadAsync(); + Console.WriteLine($"{message} -> {after}"); + } +} \ No newline at end of file diff --git a/docs/guides/getting_started/intro.md b/docs/guides/getting_started/intro.md index e0b41e22a..8bcfa9086 100644 --- a/docs/guides/getting_started/intro.md +++ b/docs/guides/getting_started/intro.md @@ -205,7 +205,7 @@ For your reference, you may view the [completed program]. [MessageReceived]: xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_MessageReceived [SocketMessage]: xref:Discord.WebSocket.SocketMessage -[SocketMessageChannel]: xref:Discord.WebSocket.SocketMessageChannel +[SocketMessageChannel]: xref:Discord.WebSocket.ISocketMessageChannel [completed program]: samples/intro/complete.cs # Building a bot with commands diff --git a/docs/guides/toc.yml b/docs/guides/toc.yml index 4a2c4d2da..2e3a61e19 100644 --- a/docs/guides/toc.yml +++ b/docs/guides/toc.yml @@ -12,7 +12,16 @@ href: concepts/logging.md - name: Working with Events href: concepts/events.md + - name: Managing Connections + href: concepts/connections.md - name: Entities + href: concepts/entities.md - name: The Command Service + items: + - name: Command Guide + href: commands/commands.md - name: Voice + items: + - name: Voice Guide + href: voice/sending-voice.md - name: Migrating from 0.9 \ No newline at end of file diff --git a/docs/guides/voice/sending-voice.md b/docs/guides/voice/sending-voice.md index f1ca3d0a5..c3ec8d9d7 100644 --- a/docs/guides/voice/sending-voice.md +++ b/docs/guides/voice/sending-voice.md @@ -5,7 +5,8 @@ title: Sending Voice **Information on this page is subject to change!** >[!WARNING] ->Audio in 1.0 is in progress +>This article is out of date, and has not been rewritten yet. +Information is not guaranteed to be accurate. ## Installing diff --git a/docs/index.md b/docs/index.md index 3f0393d4f..ef9ecdfdd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ Discord.Net is an asynchronous, multiplatform .NET Library used to interface with the [Discord API](https://discordapp.com/). -If this is your first time using Discord.Net, you should refer to the [Intro](guides/intro.md) for tutorials. +If this is your first time using Discord.Net, you should refer to the [Intro](guides/getting_started/intro.md) for tutorials. More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library. For additional resources: