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 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 have 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, essentially creating the most
  15. fundamental form of a post-execution handler.
  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 design could lead to messy code and could potentially
  21. be a 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 **any
  30. 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 initially 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 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 is the limit from here. You can add any additional information
  54. you would like regarding the execution result.
  55. For example, you may want to add your 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 [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. have not 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 are caught and sent
  76. 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