-
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
386 additions
and
378 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,70 @@ | ||
module Tourmaline | ||
# `CommandContext` represents the data passed into a bot command. It gives access to | ||
# the `client`, the full `update`, the `message`, the `command` | ||
# (including the prefix), and the raw message `text` | ||
# (not including the command). | ||
# | ||
# Since it can be annoying and verbose to have to type `ctx.message.method` | ||
# every time, `CommandContext` also forwards missing methods to the message, | ||
# update, and client in that order. So rather than calling | ||
# `ctx.message.reply` you can just do `ctx.reply`. | ||
record CommandContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
message : Tourmaline::Message, command : String, text : String do | ||
macro method_missing(call) | ||
{% if Tourmaline::Message.has_method?(call.name) %} | ||
message.{{call}} | ||
{% elsif Tourmaline::Update.has_method?(call.name) %} | ||
update.{{call}} | ||
{% elsif Tourmaline::Client.has_method?(call.name) %} | ||
client.{{call}} | ||
{% else %} | ||
{% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
{% end %} | ||
end | ||
module Context | ||
end | ||
|
||
# `EventContext` represents the data passed into an `On` event. It wraps the `update`, | ||
# and possibly the `message`. It also includes access to the name of the event that | ||
# triggered it. | ||
# | ||
# Like the other events, missing methods are forwarded to the client in this one. Since | ||
# `message` might be nil, calls are not forwarded to it. | ||
record EventContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
message : Tourmaline::Message?, event : Tourmaline::UpdateAction do | ||
macro method_missing(call) | ||
{% if Tourmaline::Update.has_method?(call.name) %} | ||
update.{{call}} | ||
{% elsif Tourmaline::Client.has_method?(call.name) %} | ||
client.{{call}} | ||
{% else %} | ||
{% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
{% end %} | ||
end | ||
end | ||
# # `CommandContext` represents the data passed into a bot command. It gives access to | ||
# # the `client`, the full `update`, the `message`, the `command` | ||
# # (including the prefix), and the raw message `text` | ||
# # (not including the command). | ||
# # | ||
# # Since it can be annoying and verbose to have to type `ctx.message.method` | ||
# # every time, `CommandContext` also forwards missing methods to the message, | ||
# # update, and client in that order. So rather than calling | ||
# # `ctx.message.reply` you can just do `ctx.reply`. | ||
# record CommandContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
# message : Tourmaline::Message, command : String, text : String do | ||
# macro method_missing(call) | ||
# {% if Tourmaline::Message.has_method?(call.name) %} | ||
# message.{{call}} | ||
# {% elsif Tourmaline::Update.has_method?(call.name) %} | ||
# update.{{call}} | ||
# {% elsif Tourmaline::Client.has_method?(call.name) %} | ||
# client.{{call}} | ||
# {% else %} | ||
# {% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
# {% end %} | ||
# end | ||
# end | ||
|
||
# `CallbackQueryContext` represents the data passed into an `Action` event. It includes | ||
# access to the `client`, the full `update`, the `message`, the callback_query | ||
# (`query`), and the query data. | ||
# | ||
# Missing methods are forwarded to, in order of most important, the `query`, | ||
# `message`, `update`, and then `client`. | ||
record CallbackQueryContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
message : Tourmaline::Message, query : Tourmaline::CallbackQuery, data : String do | ||
macro method_missing(call) | ||
{% if Tourmaline::CallbackQuery.has_method?(call.name) %} | ||
query.{{call}} | ||
{% elsif Tourmaline::Message.has_method?(call.name) %} | ||
message.{{call}} | ||
{% elsif Tourmaline::Update.has_method?(call.name) %} | ||
update.{{call}} | ||
{% elsif Tourmaline::Client.has_method?(call.name) %} | ||
client.{{call}} | ||
{% else %} | ||
{% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
{% end %} | ||
end | ||
end | ||
# # `EventContext` represents the data passed into an `On` event. It wraps the `update`, | ||
# # and possibly the `message`. It also includes access to the name of the event that | ||
# # triggered it. | ||
# # | ||
# # Like the other events, missing methods are forwarded to the client in this one. Since | ||
# # `message` might be nil, calls are not forwarded to it. | ||
# record EventContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
# message : Tourmaline::Message?, event : Tourmaline::UpdateAction do | ||
# macro method_missing(call) | ||
# {% if Tourmaline::Update.has_method?(call.name) %} | ||
# update.{{call}} | ||
# {% elsif Tourmaline::Client.has_method?(call.name) %} | ||
# client.{{call}} | ||
# {% else %} | ||
# {% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
# {% end %} | ||
# end | ||
# end | ||
|
||
# # `CallbackQueryContext` represents the data passed into an `Action` event. It includes | ||
# # access to the `client`, the full `update`, the `message`, the callback_query | ||
# # (`query`), and the query data. | ||
# # | ||
# # Missing methods are forwarded to, in order of most important, the `query`, | ||
# # `message`, `update`, and then `client`. | ||
# record CallbackQueryContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
# message : Tourmaline::Message, query : Tourmaline::CallbackQuery, data : String do | ||
# macro method_missing(call) | ||
# {% if Tourmaline::CallbackQuery.has_method?(call.name) %} | ||
# query.{{call}} | ||
# {% elsif Tourmaline::Message.has_method?(call.name) %} | ||
# message.{{call}} | ||
# {% elsif Tourmaline::Update.has_method?(call.name) %} | ||
# update.{{call}} | ||
# {% elsif Tourmaline::Client.has_method?(call.name) %} | ||
# client.{{call}} | ||
# {% else %} | ||
# {% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
# {% end %} | ||
# end | ||
# end | ||
end |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
require "./handler" | ||
|
||
module Tourmaline | ||
class CommandHandler < Handler | ||
ANNOTATIONS = [ Command ] | ||
|
||
getter commands : Array(String) | ||
getter proc : Proc(CommandContext, Void) | ||
getter prefix : String | ||
getter anywhere : Bool | ||
|
||
def initialize( | ||
commands : String | Array(String), | ||
proc : CommandContext ->, | ||
@prefix : String = "/", | ||
@anywhere : Bool = false, | ||
@private_only : Bool = false | ||
) | ||
@commands = commands.is_a?(Array) ? commands : [commands] | ||
@proc = ->(ctx : CommandContext) { proc.call(ctx); nil } | ||
validate_commands(@commands) | ||
end | ||
|
||
def actions : Array(UpdateAction) | ||
[ UpdateAction::Message ] | ||
end | ||
|
||
def call(client : Client, update : Update) | ||
if message = update.message | ||
if message_text = message.text | ||
return unless message_text.size >= 2 | ||
|
||
if command = command_match(client, message) | ||
return if @private_only && !(message.chat.type == "private") | ||
context = CommandContext.new(client, update, message, command, message_text) | ||
@proc.call(context) | ||
end | ||
end | ||
end | ||
end | ||
|
||
def check_update(client : Client, update : Update) : Bool | ||
if message = update.message | ||
text = message.text | ||
if command_match(client, message) | ||
return true | ||
end | ||
end | ||
false | ||
end | ||
|
||
private def validate_commands(commands) | ||
commands.each do |command| | ||
if command.match(/\s/) | ||
raise InvalidCommandError.new(command) | ||
end | ||
end | ||
end | ||
|
||
private def command_match(client : Client, message : Message) | ||
if text = message.text | ||
tokens = text.split(/\s+/) | ||
|
||
if !@anywhere && !tokens.empty? | ||
tokens = [tokens.first] | ||
end | ||
|
||
tokens.each do |token| | ||
if token.starts_with?(@prefix) | ||
token = token[@prefix.size..-1] | ||
if token.includes?("@") | ||
parts = token.split("@") | ||
if parts[0].in?(@commands) && parts[1]? | ||
if parts[1] == client.bot_name | ||
return parts[0] | ||
end | ||
end | ||
elsif token.in?(@commands) | ||
return token | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
class InvalidCommandError < Exception | ||
def initialize(command) | ||
super("Invalid command format for command '#{command}'") | ||
end | ||
end | ||
end | ||
|
||
# `CommandContext` represents the data passed into a bot command. It gives access to | ||
# the `client`, the full `update`, the `message`, the `command` | ||
# (including the prefix), and the raw message `text` | ||
# (not including the command). | ||
# | ||
# Since it can be annoying and verbose to have to type `ctx.message.method` | ||
# every time, `CommandContext` also forwards missing methods to the message, | ||
# update, and client in that order. So rather than calling | ||
# `ctx.message.reply` you can just do `ctx.reply`. | ||
record CommandContext, client : Tourmaline::Client, update : Tourmaline::Update, | ||
message : Tourmaline::Message, command : String, text : String do | ||
include Tourmaline::Context | ||
|
||
macro method_missing(call) | ||
{% if Tourmaline::Message.has_method?(call.name) %} | ||
message.{{call}} | ||
{% elsif Tourmaline::Update.has_method?(call.name) %} | ||
update.{{call}} | ||
{% elsif Tourmaline::Client.has_method?(call.name) %} | ||
client.{{call}} | ||
{% else %} | ||
{% raise "Unexpected method '##{call.name}' for class #{@type.id}" %} | ||
{% end %} | ||
end | ||
end | ||
end |
Oops, something went wrong.