# frozen_string_literal: true
class WebsocketClient
module Responses
# Per-command response config.
# Valid options:
# send_ignore_message [Boolean] Does this command need to send the ignore message first?
# Matches the expected behavior from the DLL.
CONFIG = {
server_success_command: {},
server_error_command: {},
post_initialization: {send_ignore_message: true},
me: {},
territories: {},
pay: {send_ignore_message: true},
gamble: {send_ignore_message: true},
setterritoryid: {},
add: {send_ignore_message: true},
allterritories: {send_ignore_message: true},
exec: {send_ignore_message: true},
promote: {send_ignore_message: true},
demote: {send_ignore_message: true},
remove: {send_ignore_message: true},
upgrade: {send_ignore_message: true},
restore: {},
player: {send_ignore_message: true},
reward: {send_ignore_message: true},
info: {send_ignore_message: true},
stuck: {},
reset: {},
logs: {},
base_v1: {}
}.freeze
def response_server_success_command
send_response
end
# BaseV1 is the generic test command. The bot sends a base_v1 request, we
# acknowledge with an ignore so ServerRequest doesn't try to dispatch the
# response back into on_response (specs use BaseV1 for plumbing checks
# only and rarely set up a full current_user/command context).
def response_base_v1
send_response(ignore: true)
end
def response_server_error_command
send_response(parameters: [{error: "oops"}])
end
def response_post_initialization
@connected = true
# The data the bot sends to the server to be stored
@server_data = @data.parameters
@server_id = @data.parameters.server_id
end
def response_me
territories = {}
Faker::Number.within(range: 1..3).times do
territories[Faker::FunnyName.two_word_name] = Faker::Crypto.md5[0, 3]
end
send_response(
parameters: {
locker: Faker::Number.within(range: 1..30_000),
score: Faker::Number.within(range: 1..30_000),
name: Faker::Internet.username,
money: Faker::Number.within(range: 1..30_000),
damage: Faker::Number.within(range: 1..100),
hunger: Faker::Number.within(range: 1..100),
thirst: Faker::Number.within(range: 1..100),
kills: Faker::Number.within(range: 1..100),
deaths: Faker::Number.within(range: 1..100),
territories: territories.to_json
}
)
end
def response_territories
return send_response if @flags.RETURN_NO_TERRITORIES
# One static to test large amounts of moderators
territories = [
TerritoryGenerator.generate(moderator_count: 60)
]
# Some random ones
Faker::Number.within(range: 1..5).times do
territories << TerritoryGenerator.generate
end
send_response(parameters: territories)
end
def response_pay
send_response(
parameters: [{
payment: Faker::Number.between(from: 20, to: 600),
locker: Faker::Number.between(from: 2000, to: 1_000_000_000)
}]
)
end
def response_gamble
return send_response(parameters: [{error: "Not enough poptabs!"}]) if @flags.NOT_ENOUGH_MONEY
amount = @data.parameters.amount.to_i
locker_before = amount
if rand > 0.50
type = "won"
locker_after = locker_before + amount
else
type = "loss"
locker_after = locker_before - amount
end
send_response(
parameters: [{
type: type,
amount: amount,
locker_before: locker_before,
locker_after: locker_after
}]
)
end
# The command is actually !setid, but the v1 DLL is expecting this.
def response_setterritoryid
return send_response(parameters: [{success: false, reason: "Some reason"}]) if @flags.FAIL_WITH_REASON
return send_response(parameters: [{success: false}]) if @flags.FAIL_WITHOUT_REASON
send_response(parameters: [{success: true}])
end
def response_add
send_response
end
def response_allterritories
return send_response(parameters: []) if @flags.RETURN_NO_TERRITORIES
territories = []
Faker::Number.between(from: 75, to: 100).times do
territories << {
id: (rand < 0.5) ? SecureRandom.uuid[0..4] : Faker::Lorem.sentence[0..20],
territory_name: Faker::Lorem.sentence,
owner_name: Faker::Name.name,
owner_uid: Faker::Number.number(digits: 17).to_s
}
end
send_response(parameters: territories)
end
# !sqf
def response_exec
message =
if @data.parameters.target == "server"
if @flags.WITH_RETURN
"Executed on server successfully. Returned: ```true```"
else
"Executed code on server"
end
elsif @flags.ERROR # For some reason, I decided not to use the error system
"Invalid target or target is not online"
else
"Executed code on target"
end
send_response(parameters: [{message: message}])
end
def response_promote
send_response
end
def response_demote
send_response
end
def response_remove
send_response
end
def response_upgrade
territory = TerritoryGenerator.generate
send_response(
parameters: [{
cost: Faker::Number.between(from: 0, to: 10_000_000),
level: territory[:level],
range: territory[:radius],
locker: Faker::Number.between(from: 0, to: 10_000_000)
}]
)
end
def response_restore
send_response(
parameters: [{
success: @flags.SUCCESS
}]
)
end
def response_player
# This handles kill and heal
response = {type: @data.parameters.type}
if %w[money locker respect].include?(@data.parameters.type)
modified_amount = @data.parameters.value.to_i
previous_amount = Faker::Number.between(from: 5000, to: 5_000_000)
response = response.merge(
modified_amount: modified_amount,
previous_amount: previous_amount,
new_amount: previous_amount + modified_amount
)
end
send_response(parameters: [response])
end
def response_reward
receipt = @server_data.reward_items.to_h
receipt << ["Poptabs (Player)", @server_data.reward_player_poptabs] if @server_data.reward_player_poptabs.positive?
receipt << ["Poptabs (Locker)", @server_data.reward_locker_poptabs] if @server_data.reward_locker_poptabs.positive?
receipt << ["Respect", @server_data.reward_respect] if @server_data.reward_respect.positive?
send_response(parameters: [{receipt: receipt}])
end
def response_info
response = {}
case @data.parameters.query
when "territory_info"
response = TerritoryGenerator.generate
when "player_info"
territories = {}
if rand < 0.5
Faker::Number.within(range: 1..3).times do
territories[Faker::FunnyName.two_word_name] = Faker::Crypto.md5[0, 3]
end
end
response = {
locker: Faker::Number.within(range: 1..30_000),
score: Faker::Number.within(range: 1..30_000),
name: Faker::Internet.username,
kills: Faker::Number.within(range: 1..100),
deaths: Faker::Number.within(range: 1..100),
territories: territories
}
if @flags.PLAYER_ALIVE
response = response.merge(
money: Faker::Number.within(range: 1..30_000),
damage: Faker::Number.within(range: 0..0.9),
hunger: Faker::Number.within(range: 1..100),
thirst: Faker::Number.within(range: 1..100)
)
end
end
send_response(parameters: [response])
end
def response_stuck
send_response(parameters: [{success: @flags.SUCCESS}])
end
def response_reset
send_response(parameters: [{success: @flags.SUCCESS}])
end
# 0: The search parameters
# 1..-1: The parsed logs
# date: <String> October 11 2020
# file_name (Exile_TradingLog.log): <Array>
# line: <Integer>
# entry: <String>
# date: <String 2020-10-11>
def response_logs
return send_response(parameters: [{}]) if @flags.NO_LOGS
# The first object is not used, but is required
logs = [{}]
Faker::Number.between(from: 1, to: 4).times do
logs << {
:date => Faker::Date.in_date_period,
"ExileTradingLog.log" => LogGenerator.generate_trading_log,
"ExileTerritoryLog.log" => LogGenerator.generate_territory_log,
"ExileDeathLog.log" => LogGenerator.generate_death_log
}
end
send_response(parameters: logs)
end
end
end