Clean code and pure ruby version

master
aeris 2 years ago
parent 510d00f312
commit 5908ae9a25
  1. 2
      Gemfile
  2. 30
      Makefile
  3. 124
      config.rb
  4. 292
      generate_html
  5. 39
      gw2ei.conf
  6. 273
      process
  7. 12
      reprocess
  8. 18
      sort_reports

@ -1,7 +1,7 @@
source 'https://rubygems.org'
gem 'oj'
gem 'amazing_print'
gem 'httparty'
gem 'parallel'
gem 'sqlite3'
gem 'rubyzip'

@ -1,30 +1,18 @@
.DEFAULT_GOAL := all
ROOT_DIR := $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
%.zevtc: %.evtc
zip -9 "$@" "$<"
%.html: %.zevtc
mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf "$<"
LOGS := $(shell find arcdps.cbtlogs -name '*.evtc' | sed 's/ /\\ /g' | sort)
ARCHIVES := $(patsubst %.evtc,%.zevtc,$(LOGS))
REPORTS := $(patsubst %.evtc,%.html,$(LOGS))
all: $(REPORTS)
html:
mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf $(ARCHIVES)
# sed -i 's/,-\.,/,0,/g' html/*.html
mv html/*_{LGolem,StdGolem}_*.html html/golem
mv html/*_{arkk,arts,ensol,mama,siax,skorv}_*.html html/fractals
mv html/*_{boneskin,fraenir,icebrood,supkodbros,woj}_*.html html/attack
mv html/*.html html/raids
.PHONY: html
process:
./process
.PHONY: process
rsync:
rsync --progress -ahxvAHX --delete html/ kamino:/srv/www/fr.imirhil/www/gw2/
all:
gw2-post-exit
$(MAKE) process
$(MAKE) rsync
html/static.html: static.html.erb
erb "$<" > "$@"
static: html/static.html
$(MAKE) rsync

@ -0,0 +1,124 @@
ME = 'aeris.5846'
LANG = :fr
IDS = {
amam: 17021, siaxx: 17028, ensolyss: 16948,
skorvald: 17632, artsariiv: 17949, arkk: 17759,
ai: 23254, elemental_ai: 23254, dark_ai: 23254,
vale_guardian: 15438, gorseval: 15429, sabetha: 15375,
slothasor: 16123, trio: 16088, matthias: 16115,
keep_construct: 16235, twisted_castle: 16247, xera: 16246,
cairn: 17194, mursaat: 17172, samarog: 17188, deimos: 17154,
soulless_horror: 19767, dhuum: 19450,
conjured_amalgamate: 43974,
adina: 22006, sabir: 21964,
icebrood: 22154, kodans: 22481, fraenir: 22492, boneskinner: 22521,
whisper_jormag: 22711, freezie: 21333,
standard_golem: 16199, medium_golem: 19645, large_golem: 19676
}.freeze
BOSSES = {
fractals: {
'98cm': %i[amam siaxx ensolyss],
'99cm': %i[skorvald artsariiv arkk],
'100cm': %i[elemental_ai dark_ai]
},
raids: {
spirit_vale: %i[vale_guardian gorseval sabetha],
salvation_pass: %i[slothasor trio matthias],
stronghold_faithfull: %i[keep_construct twisted_castle xera],
bastion_penitent: %i[cairn mursaat samarog deimos],
hall_chains: %i[soulless_horror river_souls statues_grenth dhuum],
mythwright_gambit: %i[conjured_amalgamate twin_largos qadim],
key_ahdashim: %i[adina sabir qadim_peerless]
},
strikes: %i[icebrood kodans fraenir boneskinner whisper_jormag forging_steel cold_war freezie],
golems: %i[standard_golem medium_golem large_golem]
}.freeze
ARCDPS_BOSSES = {
fractals: %i[ai arkk arts drkai elai ensol mama siax skorv],
raids: %i[adina ca cairn dei dhuum gors kc matt mo sab sabir sam sh sloth trio twstcstl vg xera],
strikes: %i[boneskin fraenir freezie icebrood supkodbros woj],
golems: %i[LGolem MedGolem StdGolem]
}.freeze
I18N = {
fr: {
raids: 'Raids',
fractals: 'Fractales des Brumes',
strikes: 'Missons d’attaque',
golems: 'Golems',
'98cm': 'Cauchemars',
'99cm': 'Observatoire détruit',
'100cm': 'Pic de Sunqua',
spirit_vale: 'Vallée des esprits',
salvation_pass: 'Passage de la rédemption',
stronghold_faithfull: 'Forteresse des fidèles',
bastion_penitent: 'Bastion du pénitent',
hall_chains: 'Hall des chaînes',
mythwright_gambit: 'Gambit de Forgeconte',
key_ahdashim: 'Clé d’Ahdashim',
amam: 'AMAM',
siaxx: 'Siaxx',
ensolyss: 'Ensolyss',
skorvald: 'Skorvald',
artsariiv: 'Artsariiv',
arkk: 'Arkk',
ai: 'Ai, Keeper of the Peak',
elemental_ai: 'Elemental Ai, Keeper of the Peak',
dark_ai: 'Dark Ai, Keeper of the Peak',
vale_guardian: 'Gardien de la vallée',
gorseval: 'Gorseval le disparate',
sabetha: 'Sabetha la saboteuse',
slothasor: 'Paressor',
trio: 'Trio de bandits',
matthias: 'Matthias Gabrel',
keep_construct: 'Titan du fort',
twisted_castle: 'Chateau corrompu',
xera: 'Xera',
cairn: 'Cairn',
mursaat: 'Ssurveillant mursaat',
samarog: 'Samarog',
deimos: 'Deimos',
soulless_horror: 'Horreur sans âme',
dhuum: 'Dhuum',
conjured_amalgamate: 'Amalgame conjurée',
adina: 'Cardinale Adina',
sabir: 'Cardinal Sabir',
icebrood: 'Sentinelle couvregivre',
kodans: 'Kodans',
fraenir: 'Fraenir de Jormag',
boneskinner: 'Dessosseur',
whisper_jormag: 'Murmure de Jormag',
freezie: 'Frisquet',
standard_golem: 'Golem chat standard',
medium_golem: 'Golem chat intermédiaire',
large_golem: 'Grand chat golem',
}
}.freeze.fetch LANG

@ -1,292 +0,0 @@
#!/usr/bin/env ruby
require 'oj'
require 'time'
require 'amazing_print'
require 'erb'
require 'parallel'
require 'sqlite3'
class Hash
def transform_leaves(&block)
self.transform_values do |v|
case v
when Hash
v.transform_leaves &block
else
block.call v
end
end
end
def symbolize_keys
self.transform_keys { |k| k.to_sym rescue k }
end
def walk(&block)
self.class._walk self, [], &block
end
private
def self._walk(hash, path, &block)
hash.each do |k, v|
p = [*path, k]
block.call :node, p, v
case v
when Hash
_walk v, p, &block
else
block.call :leaf, p, v
end
end
end
end
DURATION = {
'h' => 60*60*1_000,
'm' => 60*1_000,
's' => 1_000,
'ms' => 1,
}.freeze
def parse_duration(string)
string.split
.collect do |s|
duration, type = s.match(/(\d+)(.+)/).captures
duration = DURATION.fetch(type) * duration.to_i
end
.sum
end
def duration_to_s(duration)
text = []
DURATION.each do |k, v|
part = duration / v
text << "#{part}#{k}" if part > 0
duration -= part * v
end
text.join '&nbsp;'
end
ME = 'aeris.5846'
LANG = :fr
IDS = {
amam: 17021, siaxx: 17028, ensolyss: 16948,
skorvald: 17632, artsariiv: 17949, arkk: 17759,
ai: 23254, elemental_ai: 23254, dark_ai: 23254,
vale_guardian: 15438, gorseval: 15429, sabetha: 15375,
slothasor: 16123, trio: 16088, matthias: 16115,
keep_construct: 16235, twisted_castle: 16247, xera: 16246,
cairn: 17194, mursaat: 17172, samarog: 17188, deimos: 17154,
soulless_horror: 19767, dhuum: 19450,
conjured_amalgamate: 43974,
adina: 22006, sabir: 21964,
icebrood: 22154, kodans: 22481, fraenir: 22492, boneskinner: 22521,
whisper_jormag: 22711, freezie: 21333,
standard_golem: 16199, medium_golem: 19645, large_golem: 19676
}.freeze
BOSSES = {
fractals: {
'98cm': %i[amam siaxx ensolyss],
'99cm': %i[skorvald artsariiv arkk],
'100cm': %i[elemental_ai dark_ai]
},
raids: {
spirit_vale: %i[vale_guardian gorseval sabetha],
salvation_pass: %i[slothasor trio matthias],
stronghold_faithfull: %i[keep_construct twisted_castle xera],
bastion_penitent: %i[cairn mursaat samarog deimos],
hall_chains: %i[soulless_horror river_souls statues_grenth dhuum],
mythwright_gambit: %i[conjured_amalgamate twin_largos qadim],
key_ahdashim: %i[adina sabir qadim_peerless]
},
strikes: %i[icebrood kodans fraenir boneskinner whisper_jormag forging_steel cold_war freezie],
golems: %i[standard_golem medium_golem large_golem]
}.freeze
I18N = {
fr: {
raids: 'Raids',
fractals: 'Fractales des Brumes',
strikes: 'Missons d’attaque',
golems: 'Golems',
'98cm': 'Cauchemars',
'99cm': 'Observatoire détruit',
'100cm': 'Pic de Sunqua',
spirit_vale: 'Vallée des esprits',
salvation_pass: 'Passage de la rédemption',
stronghold_faithfull: 'Forteresse des fidèles',
bastion_penitent: 'Bastion du pénitent',
hall_chains: 'Hall des chaînes',
mythwright_gambit: 'Gambit de Forgeconte',
key_ahdashim: 'Clé d’Ahdashim',
amam: 'AMAM',
siaxx: 'Siaxx',
ensolyss: 'Ensolyss',
skorvald: 'Skorvald',
artsariiv: 'Artsariiv',
arkk: 'Arkk',
ai: 'Ai, Keeper of the Peak',
elemental_ai: 'Elemental Ai, Keeper of the Peak',
dark_ai: 'Dark Ai, Keeper of the Peak',
vale_guardian: 'Gardien de la vallée',
gorseval: 'Gorseval le disparate',
sabetha: 'Sabetha la saboteuse',
slothasor: 'Paressor',
trio: 'Trio de bandits',
matthias: 'Matthias Gabrel',
keep_construct: 'Titan du fort',
twisted_castle: 'Chateau corrompu',
xera: 'Xera',
cairn: 'Cairn',
mursaat: 'Ssurveillant mursaat',
samarog: 'Samarog',
deimos: 'Deimos',
soulless_horror: 'Horreur sans âme',
dhuum: 'Dhuum',
conjured_amalgamate: 'Amalgame conjurée',
adina: 'Cardinale Adina',
sabir: 'Cardinal Sabir',
icebrood: 'Sentinelle couvregivre',
kodans: 'Kodans',
fraenir: 'Fraenir de Jormag',
boneskinner: 'Dessosseur',
whisper_jormag: 'Murmure de Jormag',
freezie: 'Frisquet',
standard_golem: 'Golem chat standard',
medium_golem: 'Golem chat intermédiaire',
large_golem: 'Grand chat golem',
}
}.freeze.fetch LANG
class Reports
def initialize
@reports = Hash.new { |h, k| h[k] = [] }
end
def <<(report)
id = report.fetch :id
@reports[id] << report
end
def [](boss)
id = IDS[boss]
return nil unless id && @reports.has_key?(id)
reports = @reports.fetch(id).sort { |a, b| b.fetch(:date) <=> a.fetch(:date) }
cm, standard = reports.partition { |r| r.fetch :cm }
{ cm: cm, standard: standard }
end
# def to_h
# BOSSES.transform_leaves { |bs| bs.collect { |b| self[b] }.compact.to_h }
# end
end
def parse_arcdps_report(file)
File.open file do |fd|
json = Oj.load fd
id = json.fetch 'triggerID'
name = json.fetch 'fightName'
cm = json.fetch 'isCM'
date = Time.parse json.fetch 'timeStartStd'
duration = parse_duration json.fetch 'duration'
players = json.fetch 'players'
dps = players.collect { |p| p.fetch('dpsTargets').first.first.fetch('dps') }
total_dps = dps.sum
top_dps = dps.max
me = players.find { |p| p.fetch('account') == ME }
name = me.fetch 'name'
own_dps = me.fetch('dpsTargets').first.first.fetch 'dps'
profession = me.fetch 'profession'
{
id: id, cm: cm, date: date, duration: duration,
name: name, profession: profession,
total_dps: total_dps, top_dps: top_dps, own_dps: own_dps,
}
end
end
@reports = Reports.new
SQLite3::Database.new 'reports.db' do |db|
db.execute <<-SQL
CREATE TABLE IF NOT EXISTS reports (
filename TEXT NOT NULL,
id INTEGER NOT NULL,
cm BOOLEAN NOT NULL,
date DATETIME NOT NULL,
duration INTEGER NOT NULL,
name TEXT NOT NULL,
profession TEXT NOT NULL,
total_dps INTEGER NOT NULL,
top_dps INTEGER NOT NULL,
own_dps INTEGER NOT NULL
)
SQL
db.execute <<-SQL
CREATE UNIQUE INDEX IF NOT EXISTS filename_index ON reports(filename)
SQL
db.execute <<-SQL
CREATE INDEX IF NOT EXISTS id_index ON reports(id)
SQL
db.execute <<-SQL
CREATE INDEX IF NOT EXISTS date_index ON reports(id, date DESC)
SQL
db.results_as_hash = true
Parallel.each Dir["html/*_kill.json"].sort, in_threads: 16 do |file|
$stderr.puts file
filename = File.basename file, '.json'
row = db.get_first_row 'SELECT * FROM reports WHERE filename = ?', filename
report = if row
row = row.symbolize_keys
row[:cm] = row.fetch(:cm) == 1
row[:date] = Time.at row.fetch :date
row
else
report = parse_arcdps_report file
id, cm, date, duration, name, profession, total_dps, top_dps, own_dps = report.values_at :id, :cm, :date, :duration, :name, :profession, :total_dps, :top_dps, :own_dps
report[:filename] = filename
db.execute <<-SQL, filename, id, (cm ? 1 : 0), date.to_i, duration, name, profession, total_dps, top_dps, own_dps
INSERT INTO reports ( filename, id, cm, date, duration, name, profession, total_dps, top_dps, own_dps )
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
SQL
report
end
@reports << report
end
end
erb = ERB.new File.read 'index.html.erb'
html = erb.result binding
File.write 'html/index.html', html

@ -1,32 +1,31 @@
AddDuration=True
AddPoVProf=False
Anonymous=False
AutoAdd=True
AutoAddPath=arcdps.cbtlogs
AutoParse=True
CompressRaw=False
ComputeDamageModifiers=True
SaveAtOut=True
OutLocation=html
SaveOutHTML=True
HtmlExternalScripts=True
IndentJSON=False
IndentXML=False
LightTheme=False
SaveOutJSON=True
IndentJSON=True
SaveOutCSV=False
SaveOutTrace=False
SaveOutXML=False
IndentXML=False
Anonymous=False
CompressRaw=True
MultiThreaded=True
Outdated=False
OutLocation=html
ParseCombatReplay=True
ParseMultipleLogs=True
SkipFailedTries=False
AddDuration=True
AddPoVProf=False
ParsePhases=True
ParseCombatReplay=True
ComputeDamageModifiers=True
RawTimelineArrays=True
SaveAtOut=False
SaveOutCSV=False
SaveOutHTML=True
SaveOutJSON=True
SaveOutTrace=False
SaveOutXML=False
SendEmbedToWebhook=False
SendSimpleMessageToWebhook=False
SkipFailedTries=False
UploadToDPSReports=False
UploadToDPSReportsRH=False
UploadToRaidar=False
WebhookURL=
AutoAddPath=arcdps.cbtlogs
AutoAdd=True
AutoParse=True

@ -1,15 +1,258 @@
#!/usr/bin/env bash
set -xe
ARCHIVES=()
while read LOG; do
ARCHIVE="${LOG%.*}.zevtc"
zip -9 "$ARCHIVE" "$LOG"
rm "$LOG"
ARCHIVES+=("$ARCHIVE")
done < <(find -name "*.evtc")
mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf "${ARCHIVES[@]}"
./generate_html
./sort_reports
make rsync
#!/usr/bin/env ruby
require 'parallel'
require 'zip'
require 'oj'
require 'sqlite3'
require 'time'
require 'erb'
require 'fileutils'
require './config'
Zip.setup do |c|
c.default_compression = Zlib::BEST_COMPRESSION
c.continue_on_exists_proc = true
end
class Hash
def leaves(&block)
Enumerator.new do |e|
self.class.leaves self, [], e, &block
end
end
def symbolize_keys
self.transform_keys { |k| k.to_sym rescue k }
end
def walk(&block)
self.class._walk self, [], &block
end
private
def self._walk(hash, path, &block)
hash.each do |k, v|
p = [*path, k]
block.call :node, p, v
case v
when Hash
_walk v, p, &block
else
block.call :leaf, p, v
end
end
end
def self.leaves(hash, path, enumerator, &block)
hash.each do |k, v|
p = [*path, k]
case v
when Hash
self.leaves v, p, enumerator, &block
else
result = block.call p, v
enumerator << result
end
end
end
end
DURATION = {
'h' => 60*60*1_000,
'm' => 60*1_000,
's' => 1_000,
'ms' => 1,
}.freeze
def parse_duration(string)
string.split
.collect do |s|
duration, type = s.match(/(\d+)(.+)/).captures
duration = DURATION.fetch(type) * duration.to_i
end
.sum
end
def duration_to_s(duration)
text = []
DURATION.each do |k, v|
part = duration / v
text << "#{part}#{k}" if part > 0
duration -= part * v
end
text.join '&nbsp;'
end
class Reports
def initialize
@reports = Hash.new { |h, k| h[k] = [] }
end
def <<(report)
id = report.fetch :id
@reports[id] << report
end
def [](boss)
id = IDS[boss]
return nil unless id && @reports.has_key?(id)
reports = @reports.fetch(id).sort { |a, b| b.fetch(:date) <=> a.fetch(:date) }
cm, standard = reports.partition { |r| r.fetch :cm }
{ cm: cm, standard: standard }
end
end
def compress_evtc(evtc)
puts evtc
basename = File.basename evtc, '.evtc'
dirname = File.dirname evtc
zevtc = File.join dirname, basename + '.zevtc'
FileUtils.rm_f zevtc
Zip::File.open zevtc, Zip::File::CREATE do |zip|
zip.add evtc, evtc
end
FileUtils.rm evtc
zevtc
end
def process_evtcs
Parallel.map(Dir['arcdps.cbtlogs/**/*.evtc'].sort) { |evtc| compress_evtc evtc }
end
def process_zevtcs(zevtcs)
return if zevtcs.empty?
system 'mono', 'GW2EI/GuildWars2EliteInsights.exe', '-p', '-c', 'gw2ei.conf', *zevtcs
zevtcs.collect { |f| File.join 'html', File.basename(f, '.zevtc') + '.json.gz' }
end
def reprocess_zevtcs
process_zevtcs Dir['arcdps.cbtlogs/**/*.zevtc'].sort
end
EXTRACT_BOSS_FROM_FILENAME = /\d{8}-\d{6}_([^_]+)_\d+s_(kill|fail).html/.freeze
# BOSS_DIRECTORIES = BOSSES.leaves { |p, bs| d = p.first; bs.collect { |b| [b, d] } }
# .to_a.flatten(1).to_h
# .merge({ai: :fractals}).freeze
BOSS_DIRECTORIES = ARCDPS_BOSSES.collect { |t, bs| bs.collect { |b| [b, t] } }
.flatten(1).to_h.freeze
def sort_html
Dir['html/*.html'].sort.each do |file|
basename = File.basename file
match = EXTRACT_BOSS_FROM_FILENAME.match file
next unless match
boss = match[1].to_sym
directory = BOSS_DIRECTORIES.fetch(boss).to_s
directory = File.join 'html', directory
type = match[2].to_sym
directory = File.join directory, 'fail' if type == :fail
FileUtils.mv file, directory
end
end
def do_in_database
SQLite3::Database.new 'reports.db' do |db|
db.execute <<-SQL
CREATE TABLE IF NOT EXISTS reports (
filename TEXT NOT NULL,
id INTEGER NOT NULL,
cm BOOLEAN NOT NULL,
date DATETIME NOT NULL,
duration INTEGER NOT NULL,
name TEXT NOT NULL,
profession TEXT NOT NULL,
total_dps INTEGER NOT NULL,
top_dps INTEGER NOT NULL,
own_dps INTEGER NOT NULL
)
SQL
db.execute <<-SQL
CREATE UNIQUE INDEX IF NOT EXISTS filename_index ON reports(filename)
SQL
db.execute <<-SQL
CREATE INDEX IF NOT EXISTS id_index ON reports(id)
SQL
db.execute <<-SQL
CREATE INDEX IF NOT EXISTS date_index ON reports(id, date DESC)
SQL
db.results_as_hash = true
yield db
end
end
def parse_json(file)
File.open file do |fd|
json = Oj.load Zlib::GzipReader.wrap fd
id = json.fetch 'triggerID'
name = json.fetch 'fightName'
cm = json.fetch 'isCM'
date = Time.parse json.fetch 'timeStartStd'
duration = parse_duration json.fetch 'duration'
players = json.fetch 'players'
dps = players.collect { |p| p.fetch('dpsTargets').first.first.fetch('dps') }
total_dps = dps.sum
top_dps = dps.max
me = players.find { |p| p.fetch('account') == ME }
name = me.fetch 'name'
own_dps = me.fetch('dpsTargets').first.first.fetch 'dps'
profession = me.fetch 'profession'
{
id: id, cm: cm, date: date, duration: duration,
name: name, profession: profession,
total_dps: total_dps, top_dps: top_dps, own_dps: own_dps,
}
end
end
def process_json(db, json)
# ap json
filename = File.basename json, '.json.gz'
row = db.get_first_row 'SELECT * FROM reports WHERE filename = ?', filename
return if row
report = parse_json json
sql = <<-SQL
INSERT INTO reports ( filename, id, cm, date, duration, name, profession, total_dps, top_dps, own_dps )
VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
SQL
db.execute sql, filename, report.fetch(:id), (report.fetch(:cm) ? 1 : 0),
report.fetch(:date).to_i, report.fetch(:duration), report.fetch(:name),
report.fetch(:profession), report.fetch(:total_dps), report.fetch(:top_dps),
report.fetch(:own_dps)
end
def process_jsons(db, jsons)
Parallel.each jsons, in_threads: 16 do |json|
process_json db, json
end
end
def reprocess_jsons(db)
process_jsons db, Dir["html/*_kill.json.gz"].sort
end
def generate_html(db)
@reports = Reports.new
rows = db.execute 'SELECT * FROM reports'
rows.each do |row|
row = row.symbolize_keys
row[:cm] = row.fetch(:cm) == 1
row[:date] = Time.at row.fetch :date
@reports << row
end
erb = ERB.new File.read 'index.html.erb'
html = erb.result binding
File.write 'html/index.html', html
end
zevtcs = process_evtcs
process_zevtcs zevtcs
sort_html
# # reprocess_zevtcs
do_in_database do |db|
reprocess_jsons db
generate_html db
end

@ -1,12 +0,0 @@
#!/usr/bin/env bash
set -xe
FIND_ARGS=()
if [ -n "$1" ]; then
FIND_ARGS+=(-newermt "$1")
fi
find -name "*.zevtc" "${FIND_ARGS[@]}" -print0 | xargs -0 mono GW2EI/GuildWars2EliteInsights.exe -p -c gw2ei.conf
./sort_reports
./send_reports

@ -1,18 +0,0 @@
#!/usr/bin/env bash
set -e
function move_report() {
TYPE="$1"
REPORT="$2"
[[ "$REPORT" =~ _fail.html ]] && TYPE="$TYPE/fail"
mv "$REPORT" "html/$TYPE"
}
for REPORT in html/*.html; do
if [ "$REPORT" == "html/index.html" ]; then true
elif [[ "$REPORT" =~ _(StdGolem|MedGolem|LGolem)_ ]]; then move_report golem "$REPORT"
elif [[ "$REPORT" =~ _(arkk|arts|ensol|mama|siax|skorv|elai|drkai|ai)_ ]]; then move_report fractals "$REPORT"
elif [[ "$REPORT" =~ _(boneskin|fraenir|icebrood|supkodbros|woj|freezie)_ ]]; then move_report strikes "$REPORT"
else move_report raids "$REPORT";
fi
done
Loading…
Cancel
Save