You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

post-execution.md 4.5 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. ---
  2. uid: Guides.Commands.PostExecution
  3. title: Post-command Execution Handling
  4. ---
  5. # Preface
  6. When developing commands, you may want to consider building a
  7. post-execution handling system so you can have a finer control
  8. over commands. Discord.Net offers several post-execution workflows
  9. for you to work with.
  10. If you recall, in the [Command Guide], we've shown the following
  11. example for executing and handling commands,
  12. [!code[Command Handler](samples/command_handler.cs)]
  13. You may notice that after we perform [ExecuteAsync], we store the
  14. result and print it to the chat. This is essentially the most
  15. basic post-execution handling.
  16. With this in mind, we could start doing things like the following,
  17. [!code[Basic Command Handler](samples/post-execution_basic.cs)]
  18. However, this may not always be preferred, because you are
  19. creating your post-execution logic *with* the essential command
  20. handler. This could lead to messy code and could potentially be a
  21. violation of the SRP (Single Responsibility Principle).
  22. Another major issue is if your command is marked with
  23. `RunMode.Async`, [ExecuteAsync] will **always** return a successful
  24. [ExecuteResult] instead of the actual result. You can learn more
  25. about the impact in the [FAQ](xref:FAQ.Commands).
  26. ## CommandExecuted Event
  27. Enter [CommandExecuted], an event that was introduced in
  28. Discord.Net 2.0. This event is raised whenever a command is
  29. successfully executed **without any run-time exceptions** or **without
  30. any parsing or precondition failure**. This means this event can be
  31. used to streamline your post-execution design, and the best thing
  32. about this event is that it is not prone to `RunMode.Async`'s
  33. [ExecuteAsync] drawbacks.
  34. Thus, we can begin working on code such as:
  35. [!code[CommandExecuted demo](samples/command_executed_demo.cs)]
  36. So now we have a streamlined post-execution pipeline, great! What's
  37. next? We can take this further by using [RuntimeResult].
  38. ### RuntimeResult
  39. `RuntimeResult` was originally introduced in 1.0 to allow
  40. developers to centralize their command result logic.
  41. In other words, it is a result type that is designed to be
  42. returned when the command has finished its execution.
  43. However, it wasn't widely adopted due to the aforementioned
  44. [ExecuteAsync] drawback. Since we now have access to a proper
  45. result-handler via the [CommandExecuted] event, we can start
  46. making use of this class.
  47. The best way to make use of it is to create your own version of
  48. `RuntimeResult`. You can achieve this by inheriting the `RuntimeResult`
  49. class.
  50. The following creates a bare-minimum required for a sub-class
  51. of `RuntimeResult`,
  52. [!code[Base Use](samples/customresult_base.cs)]
  53. The sky's the limit from here. You can add any additional information
  54. you'd like regarding the execution result.
  55. For example, you may want to add your own result type or other
  56. helpful information regarding the execution, or something
  57. simple like static methods to help you create return types easily.
  58. [!code[Extended Use](samples/customresult_extended.cs)]
  59. After you're done creating your own [RuntimeResult], you can
  60. implement it in your command by marking the command return type to
  61. `Task<RuntimeResult>`.
  62. > [!NOTE]
  63. > You must mark the return type as `Task<RuntimeResult>` instead of
  64. > `Task<MyCustomResult>`. Only the former will be picked up when
  65. > building the module.
  66. Here's an example of a command that utilizes such logic:
  67. [!code[Usage](samples/customresult_usage.cs)]
  68. And now we can check for it in our [CommandExecuted] handler:
  69. [!code[Usage](samples/command_executed_adv_demo.cs)]
  70. ## CommandService.Log Event
  71. We have so far covered the handling of various result types, but we
  72. haven't talked about what to do if the command enters a catastrophic
  73. failure (i.e. exceptions). To resolve this, we can make use of the
  74. [CommandService.Log] event.
  75. All exceptions thrown during a command execution will be caught and
  76. be sent to the Log event under the [LogMessage.Exception] property
  77. as a [CommandException] type. The [CommandException] class allows
  78. us to access the exception thrown, as well as the context
  79. of the command.
  80. [!code[Logger Sample](samples/command_exception_log.cs)]
  81. [CommandException]: xref:Discord.Commands.CommandException
  82. [LogMessage.Exception]: xref:Discord.LogMessage.Exception
  83. [CommandService.Log]: xref:Discord.Commands.CommandService.Log
  84. [RuntimeResult]: xref:Discord.Commands.RuntimeResult
  85. [CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
  86. [ExecuteAsync]: xref:Discord.Commands.CommandService.ExecuteAsync*
  87. [ExecuteResult]: xref:Discord.Commands.ExecuteResult
  88. [Command Guide]: xref:Guides.Commands.Intro