Module: ESM::Command::Base::Helpers

Extended by:
ActiveSupport::Concern
Included in:
ESM::Command::Base
Defined in:
lib/esm/command/base/helpers.rb

Instance Method Summary collapse

Instance Method Details

#accept_request_url(uuid) ⇒ Object



454
455
456
# File 'lib/esm/command/base/helpers.rb', line 454

def accept_request_url(uuid)
  "#{request_url}/#{uuid}/accept"
end

#add_request(to:, description: "") ⇒ Object



437
438
439
440
441
442
443
444
445
446
447
448
# File 'lib/esm/command/base/helpers.rb', line 437

def add_request(to:, description: "")
  @request =
    ESM::Request.create!(
      requestor_user_id: current_user.id,
      requestee_user_id: to.id,
      requested_from_channel_id: current_channel.id.to_s,
      command_name: command_name,
      command_arguments: arguments.to_h
    )

  send_request_message(description: description, target: to)
end

#argument?(argument_name) ⇒ Boolean

Returns:

  • (Boolean)


258
259
260
# File 'lib/esm/command/base/helpers.rb', line 258

def argument?(argument_name)
  arguments.key?(argument_name) || arguments.display_name_mapping.key?(argument_name)
end

#call_sqf_function!(function_name, **arguments) ⇒ ESM::Message

Calls the provided missionNamespace variable with the provided arguments

Parameters:

  • function_name (String)

    The missionNamespace variable that holds code

  • arguments (Hash)

    Any additional arguments

Returns:

Raises:



392
393
394
395
396
397
398
# File 'lib/esm/command/base/helpers.rb', line 392

def call_sqf_function!(function_name, **arguments)
  message = ESM::Message.new
    .set_type(:call)
    .set_data(function_name:, **arguments)

  send_to_target_server!(message)
end

#community_permissionsESM::CommandConfiguration?

The community, in which this command is being executed, command permissions

Returns:

  • (ESM::CommandConfiguration, nil)


159
160
161
162
163
164
165
166
# File 'lib/esm/command/base/helpers.rb', line 159

def community_permissions
  @community_permissions ||= lambda do
    community = target_community || current_community
    return unless community

    community.command_configurations.where(command_name: command_name).first
  end.call
end

#create_or_update_cooldownObject



475
476
477
478
# File 'lib/esm/command/base/helpers.rb', line 475

def create_or_update_cooldown
  @current_cooldown = current_cooldown_query.first_or_create
  current_cooldown.update_expiry!(timers.on_execute.started_at, cooldown_time)
end

#current_cooldownESM::Cooldown

The cooldown for this command

Returns:

  • (ESM::Cooldown)


73
74
75
# File 'lib/esm/command/base/helpers.rb', line 73

def current_cooldown
  @current_cooldown ||= current_cooldown_query.first
end

#current_cooldown_queryObject



480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/esm/command/base/helpers.rb', line 480

def current_cooldown_query
  query = ESM::Cooldown.where(command_name: command_name)

  # If the command requires a steam_uid, use it to track the cooldown.
  query =
    if registration_required?
      query.where(steam_uid: current_user.steam_uid)
    else
      query.where(user_id: current_user.id)
    end

  # Check for the target_community
  query = query.where(community_id: target_community.id) if target_community

  # If we don't have a target_community, use the current_community (if applicable)
  query = query.where(community_id: current_community.id) if current_community && target_community.nil?

  # Check for the individual server
  query = query.where(server_id: target_server.id) if target_server

  # Return the query
  query
end

#decline_request_url(uuid) ⇒ Object



458
459
460
# File 'lib/esm/command/base/helpers.rb', line 458

def decline_request_url(uuid)
  "#{request_url}/#{uuid}/decline"
end

#dev_only?Boolean

Is the command limited to developers only?

Returns:

  • (Boolean)


226
227
228
# File 'lib/esm/command/base/helpers.rb', line 226

def dev_only?
  requirements.dev?
end

#dm_channel?TrueClass, FalseClass

Note:

Discordrb has helpers for this, but they're buggy?

Returns if the current channel is a direct message with the user

Returns:

  • (TrueClass, FalseClass)


215
216
217
218
219
# File 'lib/esm/command/base/helpers.rb', line 215

def dm_channel?
  return false if current_channel.nil?

  current_channel.type == Discordrb::Channel::TYPES[:dm]
end

#dm_only?Boolean

Is the command limited to Direct Messages?

Returns:

  • (Boolean)


184
185
186
# File 'lib/esm/command/base/helpers.rb', line 184

def dm_only?
  limited_to == :dm
end

#edit_message(message, content) ⇒ Object



405
406
407
408
409
410
411
412
413
414
# File 'lib/esm/command/base/helpers.rb', line 405

def edit_message(message, content)
  if content.is_a?(ESM::Embed)
    embed = Discordrb::Webhooks::Embed.new
    content.transfer(embed)

    message.edit("", embed)
  else
    message.edit(content)
  end
end

#embed_from_message!(message_or_hash) ⇒ ESM::Embed Also known as: embed_from_hash!

Attempts to create an embed using data from the client. This checks for valid attributes, and invalid attributes

Parameters:

Returns:



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/esm/command/base/helpers.rb', line 512

def embed_from_message!(message_or_hash)
  hash =
    if message_or_hash.is_a?(Message)
      message_or_hash.data.to_h
    else
      message_or_hash
    end

  Embed.from_hash!(hash)
rescue ArgumentError => e
  target_server.connection.send_error(e)

  # Sorry user... The admins need to fix their shit
  raise_error!(
    :error,
    path_prefix: "exceptions.extension",
    user: current_user.mention,
    server_id: target_server.server_id
  )
end

#inspectObject



293
294
295
# File 'lib/esm/command/base/helpers.rb', line 293

def inspect
  "<#{self.class.name}, arguments: #{arguments}>"
end

#on_cooldown?Boolean

Is this command on cooldown?

Returns:

  • (Boolean)


244
245
246
247
248
249
# File 'lib/esm/command/base/helpers.rb', line 244

def on_cooldown?
  # We've never used this command with these arguments before
  return false if current_cooldown.nil?

  current_cooldown.active?
end

#query_exile_database!(name, **arguments) ⇒ ESM::Message Also known as: run_database_query!

Shorthand method for sending a query message to the Exile database

Parameters:

  • name (String, Symbol)

    The name of the query

  • **arguments (Hash)

    The query arguments

Returns:

Raises:



371
372
373
374
375
376
377
378
# File 'lib/esm/command/base/helpers.rb', line 371

def query_exile_database!(name, **arguments)
  message = ESM::Message.new
    .set_type(:query)
    .set_data(query_function_name: name, **arguments)

  response = send_to_target_server!(message)
  response.data.results
end

#raise_error!(error_name = nil, **args, &block) ⇒ Object

Builds a message and raises a CheckFailure with that reason.

Parameters:

  • error_name (String, Symbol, nil) (defaults to: nil)

    The name of the error message located in the locales for "commands.<command_name>.errors". If nil, a block must be provided

  • args (Hash)

    The args to be passed into the translation if an error_name is provided

  • block (Proc)

    If provided, the block must return the error message to be used. This can be a string or an ESM::Embed.

Raises:

  • (exception_class)


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/esm/command/base/helpers.rb', line 304

def raise_error!(error_name = nil, **args, &block)
  exception_class = args.delete(:exception_class) || ESM::Exception::CheckFailure
  path_prefix = args.delete(:path_prefix) || "commands.#{name}.errors"

  reason =
    if block
      yield
    elsif error_name
      ESM::Embed.build(:error, description: I18n.t("#{path_prefix}.#{error_name}", **args))
    end

  warn!(
    exception_class: exception_class,
    author: "#{current_user.distinct} (#{current_user.discord_id})",
    channel: "#{Discordrb::Channel::TYPE_NAMES[current_channel.type]} (#{current_channel.id})",
    reason: reason.is_a?(Embed) ? reason.description : reason,
    command: to_h
  )

  raise exception_class, reason
end

#registration_required?Boolean

Does the command require registration?

Returns:

  • (Boolean)


235
236
237
# File 'lib/esm/command/base/helpers.rb', line 235

def registration_required?
  requirements.registration?
end

#reply(message) ⇒ Object

Convenience method for replying back to the event's channel



401
402
403
# File 'lib/esm/command/base/helpers.rb', line 401

def reply(message)
  ESM.bot.deliver(message, to: current_channel, block: true)
end

#requestObject



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/esm/command/base/helpers.rb', line 416

def request
  @request ||= lambda do
    requestee = target_user || current_user

    # Don't look for the requestor because multiple different people could attempt to invite them
    # requestor_user_id: current_user.esm_user.id,
    query = ESM::Request.where(requestee_user_id: requestee.id, command_name: command_name)

    arguments.to_h.each do |name, value|
      query =
        if value.nil?
          query.where("command_arguments->>'#{name}' IS NULL")
        else
          query.where("command_arguments->>'#{name}' = ?", value)
        end
    end

    query.first
  end.call
end

#request_urlObject



450
451
452
# File 'lib/esm/command/base/helpers.rb', line 450

def request_url
  ESM.config.request_url
end

#same_user?Boolean

Is the current_user also the target_user?

Returns:

  • (Boolean)


173
174
175
176
177
# File 'lib/esm/command/base/helpers.rb', line 173

def same_user?
  return false if target_user.nil?

  current_user.steam_uid == target_user.steam_uid
end

#send_request_message(target:, description: "") ⇒ Object



462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/esm/command/base/helpers.rb', line 462

def send_request_message(target:, description: "")
  embed =
    ESM::Embed.build do |e|
      e.set_author(name: current_user.distinct, icon_url: current_user.avatar_url)
      e.description = description
      e.add_field(name: I18n.t("commands.request.accept_name"), value: I18n.t("commands.request.accept_value", url: accept_request_url(request.uuid)), inline: true)
      e.add_field(name: I18n.t("commands.request.decline_name"), value: I18n.t("commands.request.decline_value", url: decline_request_url(request.uuid)), inline: true)
      e.add_field(name: I18n.t("commands.request.command_usage_name"), value: I18n.t("commands.request.command_usage_value", uuid: request.uuid_short))
    end

  ESM.bot.deliver(embed, to: target)
end

#send_to_target_server!(message, block: true) ⇒ Message, Connection::Promise

Sends a Message to the target server.

Parameters:

  • message (Message)

    The message to send to the target server

  • block (Boolean) (defaults to: true)

    Whether to wait until the server responds back (defaults to true)

Returns:

  • (Message, Connection::Promise)

    If block is false, a promise in a processing status is returned. If block is true, the response as ESM::Message is returned.

Raises:



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/esm/command/base/helpers.rb', line 345

def send_to_target_server!(message, block: true)
  raise ArgumentError, "Message must be a ESM::Message" unless message.is_a?(ESM::Message)

  if target_server.nil?
    raise ESM::Exception::CheckFailure,
      "Command #{name} must define the `server_id` argument in order to use #send_to_target_server!"
  end

  message = message.(
    player: current_user,
    target: target_user
  )

  target_server.send_message(message, block:)
end

#skip_actionObject



326
327
328
# File 'lib/esm/command/base/helpers.rb', line 326

def skip_action(*)
  skipped_actions.set(*, unset: false)
end

#t(translation_name) ⇒ Object

Makes calls to I18n.t shorter



254
255
256
# File 'lib/esm/command/base/helpers.rb', line 254

def t(translation_name, **)
  I18n.t("commands.#{name}.#{translation_name}", **)
end

#target_communityESM::Community?

The ESM representation of a Discord server that is the target of this command

Returns:

  • (ESM::Community, nil)

    The community that the command was executed for



95
96
97
98
99
100
101
# File 'lib/esm/command/base/helpers.rb', line 95

def target_community
  @target_community ||= lambda do
    return ESM::Community.find_by_community_id(arguments.community_id) if arguments.community_id

    target_server&.community
  end.call
end

#target_serverESM::Server?

The ESM representation of a community's Arma 3 Server

Returns:

  • (ESM::Server, nil)

    The server that the command was executed for



82
83
84
85
86
87
88
# File 'lib/esm/command/base/helpers.rb', line 82

def target_server
  @target_server ||= lambda do
    return unless arguments.server_id

    ESM::Server.find_by_server_id(arguments.server_id)
  end.call
end

#target_uidString?

Sometimes we're given a steam UID that may not be linked to a discord user But, the command can work without the registered part.

Returns:

  • (String, nil)

    The steam uid from given argument or the steam uid registered to the target_user (which may be nil)



146
147
148
149
150
151
152
# File 'lib/esm/command/base/helpers.rb', line 146

def target_uid
  return if arguments.target.blank?

  @target_uid ||= lambda do
    arguments.target.steam_uid? ? arguments.target : target_user&.steam_uid
  end.call
end

#target_userESM::User, ...

The ESM representation of a Discord user that is the target of this command This method is expected to only execute the code once. This avoids sending invalid IDs to Discord over and over again

Returns:



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/esm/command/base/helpers.rb', line 110

def target_user
  @target_user ||= lambda do
    return if arguments.target.blank?

    # This could be a steam_uid, discord id, or discord mention
    # Automatically remove the mention characters
    target_string = arguments.target.gsub(/[<@!&>]/, "").strip

    # Attempt to find the target within ESM
    user = ESM::User.parse(target_string)

    # This validates that the user exists and we get a discord user back
    return user if user&.discord_user

    if target_string.discord_id?
      discord_user = ESM.bot.user(target_string)

      # The target_string does not exist in the database
      # but it is a valid discord user
      return ESM::User.from_discord(discord_user) if discord_user
    end

    # The target_string does not exist in the database, nor in discord
    return ESM::User::Ephemeral.new(target_string) if target_string.steam_uid?

    # The provided text was gibberish
    nil
  end.call
end

#text_channel?<Type>

Note:

Discordrb has helpers for this, but they're buggy?

Returns if the current channel is a text channel

Returns:

  • (<Type>)


203
204
205
206
207
# File 'lib/esm/command/base/helpers.rb', line 203

def text_channel?
  return false if current_channel.nil?

  current_channel.type == Discordrb::Channel::TYPES[:text]
end

#text_only?Boolean

Is the command limited to text channels?

Returns:

  • (Boolean)


193
194
195
# File 'lib/esm/command/base/helpers.rb', line 193

def text_only?
  limited_to == :text
end

#to_hObject



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
# File 'lib/esm/command/base/helpers.rb', line 262

def to_h
  {
    name: name,
    arguments: arguments,
    current_community: current_community&.attributes,
    current_channel: current_channel&.attributes,
    current_user: current_user&.attributes,
    current_cooldown: current_cooldown&.attributes,
    target_community: target_community&.attributes,
    target_server: target_server&.attributes&.except("server_key"),
    target_user: target_user&.attributes,
    target_uid: target_uid,
    same_user: same_user?,
    dm_only: dm_only?,
    text_only: text_only?,
    dev_only: dev_only?,
    registration_required: registration_required?,
    on_cooldown: on_cooldown?,
    skipped_actions: skipped_actions.to_h,
    permissions: {
      config: community_permissions&.attributes,
      allowlist_enabled: command_allowlist_enabled?,
      enabled: command_enabled?,
      allowed: command_allowed_in_channel?,
      allowlisted: command_allowed?,
      notify_when_disabled: notify_when_command_disabled?,
      cooldown_time: cooldown_time
    }
  }
end

#usage(**args) ⇒ Object

See class method .usage above



61
62
63
64
65
66
# File 'lib/esm/command/base/helpers.rb', line 61

def usage(**args)
  args[:use_placeholders] ||= false
  args[:arguments] = arguments.merge(args[:arguments] || {})

  self.class.usage(**args)
end