extension to discordcr for webserver-like middlewares discordcr-middleware discord-bot discordcr
0.4.0 Latest release released


An extension of discordcr's Client that adds middleware functionality, similar to that of webserver libraries. The goal is to provide a very customizable way to conditionally execute event handlers, and make great reuse of code and state between events.


Add this to your application's shard.yml:

    github: z64/discordcr-middleware


Require the extension:

require "discordcr-middleware"

Creating Middleware

Any class that implements def call(payload, context, &block) can be used as middleware:

class Middleware
  def call(payload, context)
    # Do things with payload and context..

payload is the Discord event payload that triggered the event handler. A middleware can be designed to handle multiple kinds of payloads by overloading call and restricting payload to different types:

class Middleware
  def call(payload : Discord::Message, context)
    # Do things with a message..

  def call(payload : Discord::Gateway::PresenceUpdatePayload, context)
    # Do things with a presence update..

If you try to use a middleware class with an event that it doesn't support, this will result in a compile-time error.

context is an instance of Context. This is a special class that allows you to store arbitrary class objects to be referenced later in the middleware chain. How the library handles context and how it should be used will be outlined in the next section, but in brief:

class Foo

foo = Foo.new
context[Foo] == foo # => true

The &block passed to call is a block to be yielded to that determines whether or not middleware that follow should be executed.

You can define an #initialize for your middleware that will let you configure how you want your middleware to behave for different event handlers, as opposed to creating another middleware class with extremely similar behavior.

Usage with Client

Any event handler can have a middleware chain applied to it.

The event handling process goes like this:

  1. The message event is received by the client
  2. An "empty" Context is initialized, and the receiving Client is added to it
  3. Each middleware in the chain is added to Context. This allows you to access the middleware that have run previously in the chain, either from inside one of the middleware or in the event handler itself.
  4. The event is passed through each middleware, providing that each one successfully yields. If any middleware does not yield, execution of the rest of the chain will stop.
client.on_message_create(MiddlewareA.new, MiddlewareB.new) do |payload, context|
  payload # => Discord::Message
  context # => Discord::Context
  context[Discord::Client] == client # => true
  context[MiddlewareA]               # => MiddlewareA
  context[MiddlewareB]               # => MiddlewareB

It's also worth noting you can share the same instance of a middleware between multiple event handlers:

middleware = Middleware.new

client.on_message_create(middleware) do |payload|
  # ...

client.on_message_update(middleware) do |payload|
  # ...

This is useful, for example, for a middleware that has some kind of state that affects multiple handlers. You could have a single Prefix middleware instance that stores the client's prefix in memory, so that when you update the prefix, the runtime behavior on all of your handlers updates at once.

The event handler block is also optional, making it possible to have pure-middleware chains:

client.on_message_create(MiddlewareA.new, MiddlewareB.new)

Additional Examples

Stock Middleware

A collection of basic, common use-case middleware are provided in discordcr-middleware/middleware.

Require them explicitly to make use of them:

require "discordcr-middleware/middleware/prefix"



  • z64 - creator, maintainer

Inspired by the raze web framework and its middleware system.

  github: z64/discordcr-middleware
  version: ~> 0.4.0
License MIT
Crystal 0.24.1


Dependencies 2

Development Dependencies 0

Dependents 1

Last synced .
search fire star recently