Skip to content
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
assets
spec
src
.gitattributes
.gitignore
.travis.yml
Dockerfile
LICENSE
Makefile
README.md
docker-compose.yml
shard.lock
shard.yml

README.md

Amaranth

Amaranth is a Discord bot written Crystal using discordcr and discordcr-middleware.

Anything in src/amaranth could get moved to an external repo at any time.

Functionality

Amaranth now supports plugins and namespaced commands! Available commands are:

Group Commands:

bot: rename
carc: eval, langs
chan: rename
dota: latest
github: add, list, remove
patches: check, disable, enable, games, gids, list, list_all, stop_all
rust: crate?, crates, eval, versions

Regular Commands:

clear, commands, echo, help

So to run a command, use <group>.<command> or <command>.

Writing a Plugin and Commands

Command Creation

Commands have several initialization methods. You must specify a name and desc, but min_args, max_args and permissions are optional.

  • name and desc are of type String.
  • desc shows up when a user types help <command>.
  • permissions are of the type Discord::Permissions.

Example description and output:

desc = <<-DESC
Gets latest dota match.
Usage: dota.latest [dotaID] where ID is optional.
You can save your ID using dota.add. You can find your ID at opendota.com.
DESC

Sample command help.

Initialization Methods

alias ExecType = Proc(Array(String), Discord::Context, CommandReturn)
def initialize(@name, @desc, &@exec : ExecType)
def initialize(@name, @desc, @permissions, &@exec : ExecType)
def initialize(@name, @desc, @min_args : Int32, &@exec : ExecType)
def initialize(@name, @desc, @min_args : Int32, @max_args : Int32, &@exec : ExecType)
def initialize(@name, @desc, @min_args : Int32, @max_args : Int32, @permissions : Discord::Permissions, &@exec : ExecType)
desc = <<-DESC
This command is an example command.
Usage: name
DESC
command "name", desc, do |args, context|
  # etc
end
perms = Discord::Permissions.flags(ReadMessages, SendMessages, KickPeople)
command "example", desc, 1, 2, perms do |args, context|
end

PreCommand Creation

PreCommands are a limited version of Commands:

  • They are not registered (unable to run manually via Discord)
  • They run before every command
  • They currently take a return type of Discord::Message | Nil
  • They do not take args
  • They do not take a description
  • They currently cannot be namespaced.
pre_command do |context|
  # something to be run before any command
end

PreCommands are new to Amaranth so they are prone to change.

Making the plugin

The plugin class needs to inherit from Plugin.

class ExamplePlugin < Plugin::Plugin
end

To make a grouped command that says Hello, world!:

class ExamplePlugin < Plugin::Plugin
  group "example" do
    desc = "My description"
    command "hello", desc do |args, context|
      "Hello, world!"
    end
  end
end

# this registers example.hello as a command!

To make a command without a group, omit group. However, make sure there aren't multiple commands with the same name.

class ExamplePlugin < Plugin::Plugin
  desc = "My description"
  command "hello", desc do |args, context|
    "Hello, world!"
  end
end

# this registers hello as a command!

Command return types

Amaranth allows you to manually run context.client.create_message() or use implicit returns and automatically handle it for you.

Implicit Return Types:

  • String: Amaranth will run context.client.create_message(context.message.channel.id, <string>)
  • Discord::Embed: Amaranth will run context.client.create_message(context.message.channel.id, "", <embed>)
  • NamedTuple(msg: String, embed: Discord::Embed): Amaranth will run context.client.create_message(context.message.channel.id, <string>, <embed>)
  • Discord::Message: No message will be created.
  • Discord::Channel: No message will be created.
  • Discord::Guild: No message will be created.

Return types like Discord::Channel get returned from context.client commands and thus do not warrant a message.

Note: Returning an empty string ("") also causes no message to be created.

Saving data in a config

To get access to helper methods for saving a config, you need two things:

  1. Create a config class with a MessagePack mapping.
  2. Extend your plugin class with the config type of your Config.
class ExampleConfig
  MessagePack.mapping({
    ids: Array(UInt64),
    channels: Array(UInt64)
  })
end

class ExamplePlugin < Plugin::Plugin
  extend Plugin::Config(ExampleConfig)
end

Now you get access to two methods:

  1. get_config(name : String) which returns an existing config or creates a new one
  2. save_config(name : String, config : ConfigType) which saves your config
class ExampleConfig
  MessagePack.mapping({
    ids: Array(UInt64),
    channels: Array(UInt64)
  })
end

class ExamplePlugin < Plugin::Plugin
  extend Plugin::Config(ExampleConfig)

  group "example" do
    command "list" do |args, context|
      config = get_config("example")
      config.channels.each do |channel|
        context.client.create_message(channel, "Hello!")
      end
    end
  end
end

Running your Plugin

You've created a plugin. Great! Now, how do you run it?

Amaranth runs on discordcr-middleware, so what you'll want to do is create a stack.

module ExampleBot
  # in src/main.cr, etc
  class_property client = Discord::Client.new(token)
  class_property cache = Discord::Cache.new(client)
  @@client.cache = @@cache

  @@client.stack(:dota,
    Common.new,
    DiscordMiddleware::Error.new("%exception%"),
    DiscordMiddleware::Prefix.new(prefix),
    ParserMW.new(prefix),
    PluginHandler.new(
      [ExamplePlugin],
    ))
end

ExampleBot.client.run

PluginHandler can take an array of an arbitary amount of plugins. Keep in mind to namespace your commands to avoid overwriting issues with commands of the same name.

Recommended reading for help

Roadmap

  • Improve on PreCommands as a concept
  • Improve min_args, max_args checking
  • Implement a help property for each command, so one can run help command to get more information.

Development

Interested in development? Message @ Andrei#8263 (91329651909074944) on Discord.

Contributing

  1. Fork it ( https://github.com/azah/amaranth/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

  • azah Andrew Zah - creator, maintainer
You can’t perform that action at this time.