Class: ESM::Command::Argument
- Inherits:
-
Object
- Object
- ESM::Command::Argument
- Defined in:
- lib/esm/command/argument.rb
Constant Summary collapse
- DEFAULT_TEMPLATE =
Global default values for all argument You may overwrite these in your argument definition
{ checked_against: :present?, checked_against_if: lambda do |argument, content| argument.required? || content.present? end }.freeze
- TEMPLATES =
Global templates for any argument to use These can be used by defining an argument with the same name, or by providing the
:templateoption during argument definition { # Required: Majority of the time, this is needed. target: { checked_against: ESM::Regex::TARGET, description_extra: "commands.arguments.target.description_extra", description: "commands.arguments.target.description", required: true }, # Required: Majority of the time, this is needed. command: { checked_against: ->(content) { ESM::Command.include?(content) }, description: "commands.arguments.command.description", required: true }, # Required: No functionality to "guess" this territory_id: { checked_against: ESM::Regex::TERRITORY_ID, description_extra: "commands.arguments.territory_id.description_extra", description: "commands.arguments.territory_id.description", required: true, placeholder: "territory" }, # Optional in Discord: UserDefault/CommunityDefault can be used. It will be validated so it is "semi-required" # Required: In the bot community_id: { required: {discord: false, bot: true}, checked_against: ESM::Regex::COMMUNITY_ID, description_extra: "commands.arguments.community_id.description_extra", description: "commands.arguments.community_id.description", optional_text: "commands.arguments.community_id.optional_text", placeholder: "community", modifier: lambda do |content| if content.present? # User alias if (id_alias = current_user.id_aliases.find_community_alias(content)) content = id_alias.community.community_id end return content end # content == nil, attempt to find and use a default user_defaults = current_user.id_defaults return user_defaults.community.community_id if user_defaults.community_id # Community autofill return current_community.community_id if current_channel.text? nil end }, # Optional in Discord: UserDefault/CommunityDefault can be used. It will be validated so it is "semi-required" # Required: In the bot server_id: { required: {discord: false, bot: true}, checked_against: ESM::Regex::SERVER_ID, description_extra: "commands.arguments.server_id.description_extra", description: "commands.arguments.server_id.description", optional_text: "commands.arguments.server_id.optional_text", placeholder: "server", modifier: lambda do |content| if content.present? # User provided - Starts with a community ID return content if content.match("#{ESM::Regex::COMMUNITY_ID_OPTIONAL.source}_") # User alias if (id_alias = current_user.id_aliases.find_server_alias(content)) return id_alias.server.server_id end # Community autofill if current_channel.text? && current_community.servers.by_server_id_fuzzy(content).any? return "#{current_community.community_id}_#{content}" end return content end # content == nil, attempt to find and use a default if current_channel.text? channel_default = current_community.id_defaults.for_channel(current_channel) return channel_default.server.server_id if channel_default&.server_id global_default = current_community.id_defaults.global return global_default.server.server_id if global_default&.server_id end if current_user.id_defaults.server_id user_defaults = current_user.id_defaults return user_defaults.server.server_id if user_defaults.server_id end nil end } }.freeze
Instance Attribute Summary collapse
-
#checked_against ⇒ Object
readonly
Returns the value of attribute checked_against.
-
#checked_against_if ⇒ Object
readonly
Returns the value of attribute checked_against_if.
-
#command_class ⇒ Object
readonly
Returns the value of attribute command_class.
-
#command_name ⇒ Object
readonly
Returns the value of attribute command_name.
-
#default_value ⇒ Object
readonly
Returns the value of attribute default_value.
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#description_extra ⇒ Object
readonly
Returns the value of attribute description_extra.
-
#discord_type ⇒ Object
readonly
Returns the value of attribute discord_type.
-
#display_name ⇒ Object
readonly
Returns the value of attribute display_name.
-
#modifier ⇒ Object
readonly
Returns the value of attribute modifier.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#optional_text ⇒ Object
readonly
Returns the value of attribute optional_text.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#placeholder ⇒ Object
readonly
Returns the value of attribute placeholder.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Instance Method Summary collapse
- #default_value? ⇒ Boolean
- #help_documentation ⇒ Object
-
#initialize(name, type = nil, opts = {}) ⇒ Argument
constructor
A configurable representation of a command argument.
- #modifier? ⇒ Boolean
- #optional? ⇒ Boolean
- #optional_text? ⇒ Boolean
- #preserve_case? ⇒ Boolean
- #required? ⇒ Boolean
- #required_by_bot? ⇒ Boolean
- #required_by_discord? ⇒ Boolean
- #to_h ⇒ Object
- #to_s ⇒ Object
- #transform_and_validate!(input, command) ⇒ Object
Constructor Details
#initialize(name, type = nil, opts = {}) ⇒ Argument
A configurable representation of a command argument
@option opts [TrueClass, FalseClass, Hash] :required
Controls if the argument should be required by Discord and by the Bot
Fine grain control can be achieved by providing a Hash.
For example, not require an argument on Discord but require it on the bot side
required: {discord: false, bot: true}
Optional. Default: false
@option opts [Symbol, String, nil] :template
The name of a default entry in which `opts` are merged into.
Useful for having an argument that acts like another argument, but may have different configuration
@option opts [String] :description
This argument's description, in less than 100 characters.
This description is used in Discord when viewing the argument.
Note: Providing this option is optional, however, all arguments MUST have a non-blank description
This value defaults to the value located at the locale path:
commands.<command_name>.arguments.<argument_name>.description
@option opts [String] :description_extra
Any extra information to be included that wouldn't fit in the 100 character limit
Note: Providing this option is optional, however, this argument MUST have a non-blank description
This description is used in the help documentation with the help command and on the website
This value defaults to the value located at the locale path:
commands.<command_name>.arguments.<argument_name>.description_extra
@option opts [String] :optional_text
Allows for overriding the "this argument is optional" text in the help documentation.
This opt is ignored if `required: true`
Optional.
This value defaults to the value located at the locale path:
commands.<command_name>.arguments.<argument_name>.optional_text
@option opts [Symbol, String] :display_name
Changes how the argument is displayed to the user, but not in the code
Optional.
@option opts [Object] :default
The default value if this argument. This value is ignored if `required: true`
Optional.
Default: nil
@option opts [Boolean] :preserve_case
Controls if this argument's value should be converted to lowercase or not.
Optional.
Default: false
@option opts [Proc] :modifier
A block of code used to modify this argument's value before validation
Optional.
@option opts [Hash] :choices
The key: display_value of choices the user can pick from
Optional.
@option opts [Integer] :min_value
If type is integer/number, this is the minimum value that can be selected
@option opts [Integer] :max_value
If type is integer/number, this is the maximum value that can be selected
@option opts [Regex, String, Proc, Array] :checked_against
Used to perform validation against the content provided to the argument
Regex/String - Content must `match?`
Proc - Content is passed in and can return a truthy value to consider the content as valid
Array - Content must be one of the values
@option opts [Proc] :checked_against_if
Used to determine if the argument should be validated against :checked_against.
Can return a truthy value to continue to validation. Falsey values will cause validation to be skipped
@option opts [String, Symbol] :placeholder
Used when the command usage is displayed and use_placeholders are true. Defaults to the display name
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/esm/command/argument.rb', line 215 def initialize(name, type = nil, opts = {}) template_name = (opts[:template] || name).to_sym # Precedence: # opts -> template -> default template opts = DEFAULT_TEMPLATE.merge(TEMPLATES[template_name] || {}).merge(opts) @name = name @type = type ? type.to_sym : :string @discord_type = Discordrb::Interactions::OptionBuilder::TYPES[@type] @display_name = (opts[:display_name] || name).to_sym @command_class = opts[:command_class] @command_name = command_class.command_name.to_sym @default_value = opts[:default] @preserve_case = !!opts[:preserve_case] @modifier = opts[:modifier] @checked_against = opts[:checked_against] @checked_against_if = opts[:checked_against_if] @placeholder = opts[:placeholder].presence || name if opts[:required].is_a?(Hash) @required_by_discord = !!opts.dig(:required, :discord) @required_by_bot = !!opts.dig(:required, :bot) else required = !!opts[:required] @required_by_discord = required @required_by_bot = required end @options = {required: @required_by_discord} @options[:min_value] = opts[:min_value] if opts[:min_value] @options[:max_value] = opts[:max_value] if opts[:max_value] # I prefer {value: "Display Name"}, Discord/rb wants it to be {"Display Name": "value"} if opts[:choices] @options[:choices] = opts[:choices].map { |k, v| [v.to_s, k.to_s] }.to_h end @description = load_locale_or_provided(opts[:description], "description") @description_extra = load_locale_or_provided(opts[:description_extra], "description_extra").presence @optional_text = load_optional_text(opts[:optional_text]) check_for_valid_configuration! end |
Instance Attribute Details
#checked_against ⇒ Object (readonly)
Returns the value of attribute checked_against.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def checked_against @checked_against end |
#checked_against_if ⇒ Object (readonly)
Returns the value of attribute checked_against_if.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def checked_against_if @checked_against_if end |
#command_class ⇒ Object (readonly)
Returns the value of attribute command_class.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def command_class @command_class end |
#command_name ⇒ Object (readonly)
Returns the value of attribute command_name.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def command_name @command_name end |
#default_value ⇒ Object (readonly)
Returns the value of attribute default_value.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def default_value @default_value end |
#description ⇒ Object (readonly)
Returns the value of attribute description.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def description @description end |
#description_extra ⇒ Object (readonly)
Returns the value of attribute description_extra.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def description_extra @description_extra end |
#discord_type ⇒ Object (readonly)
Returns the value of attribute discord_type.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def discord_type @discord_type end |
#display_name ⇒ Object (readonly)
Returns the value of attribute display_name.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def display_name @display_name end |
#modifier ⇒ Object (readonly)
Returns the value of attribute modifier.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def modifier @modifier end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def name @name end |
#optional_text ⇒ Object (readonly)
Returns the value of attribute optional_text.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def optional_text @optional_text end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def @options end |
#placeholder ⇒ Object (readonly)
Returns the value of attribute placeholder.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def placeholder @placeholder end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
123 124 125 |
# File 'lib/esm/command/argument.rb', line 123 def type @type end |
Instance Method Details
#default_value? ⇒ Boolean
307 308 309 |
# File 'lib/esm/command/argument.rb', line 307 def default_value? !!@default_value end |
#help_documentation ⇒ Object
339 340 341 342 343 344 |
# File 'lib/esm/command/argument.rb', line 339 def help_documentation output = ["**`#{self}:`**", description] output << "#{description_extra}." if description_extra.presence output << "**Note:** #{optional_text}" if optional_text? output.join("\n") end |
#modifier? ⇒ Boolean
303 304 305 |
# File 'lib/esm/command/argument.rb', line 303 def modifier? !!modifier&.respond_to?(:call) end |
#optional? ⇒ Boolean
323 324 325 |
# File 'lib/esm/command/argument.rb', line 323 def optional? !required_by_bot? && !required_by_discord? end |
#optional_text? ⇒ Boolean
327 328 329 |
# File 'lib/esm/command/argument.rb', line 327 def optional_text? optional_text.present? end |
#preserve_case? ⇒ Boolean
299 300 301 |
# File 'lib/esm/command/argument.rb', line 299 def preserve_case? @preserve_case end |
#required? ⇒ Boolean
319 320 321 |
# File 'lib/esm/command/argument.rb', line 319 def required? required_by_bot? || required_by_discord? end |
#required_by_bot? ⇒ Boolean
311 312 313 |
# File 'lib/esm/command/argument.rb', line 311 def required_by_bot? @required_by_bot end |
#required_by_discord? ⇒ Boolean
315 316 317 |
# File 'lib/esm/command/argument.rb', line 315 def required_by_discord? @required_by_discord end |
#to_h ⇒ Object
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/esm/command/argument.rb', line 346 def to_h { name: name, command_name: command_name, display_name: display_name, description: description, description_extra: description_extra, optional_text: optional_text, default_value: default_value, modifier: modifier, checked_against: checked_against, preserve_case: preserve_case?, discord: @options, bot: { required: @required_by_bot } } end |
#to_s ⇒ Object
331 332 333 334 335 336 337 |
# File 'lib/esm/command/argument.rb', line 331 def to_s if display_name.present? display_name.to_s else name.to_s end end |
#transform_and_validate!(input, command) ⇒ Object
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/esm/command/argument.rb', line 261 def transform_and_validate!(input, command) raise ArgumentError, "Invalid command argument" unless command.is_a?(ApplicationCommand) input_present = input.present? input = default_value if !input_present && default_value? sanitized_content = if input.is_a?(String) && input_present preserve_case? ? input.strip : input.downcase.strip else input end content = if modifier? command.instance_exec(sanitized_content, &modifier) else sanitized_content end debug!( argument: to_h.except(:description, :description_extra, :optional_text), input: input, before: { type: sanitized_content.class.name, content: sanitized_content }, after: { type: content.class.name, content: content } ) check_for_valid_content!(command, content) content end |