From 92ce37dda361677f9b55a1de98f23d0cbca99264 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Wed, 26 Sep 2018 20:40:41 +0300 Subject: [PATCH 1/7] [Fix] Patch System column addition fixes This affects KillsServerIDPatch and IPHashPatch issues. Affected issues: #732 --- .../databases/sql/patches/GeoInfoLastUsedPatch.java | 2 +- .../system/database/databases/sql/patches/IPHashPatch.java | 2 +- .../database/databases/sql/patches/KillsServerIDPatch.java | 2 +- .../databases/sql/patches/NicknameLastSeenPatch.java | 2 +- .../plan/system/database/databases/sql/patches/Patch.java | 7 ++----- .../databases/sql/patches/SessionAFKTimePatch.java | 2 +- .../databases/sql/patches/TransferPartitionPatch.java | 2 +- 7 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java index 80b0d2ef5..d4ab90098 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/GeoInfoLastUsedPatch.java @@ -16,7 +16,7 @@ public class GeoInfoLastUsedPatch extends Patch { @Override public void apply() { - addColumns(GeoInfoTable.TABLE_NAME, + addColumn(GeoInfoTable.TABLE_NAME, GeoInfoTable.Col.LAST_USED + " bigint NOT NULL DEFAULT 0" ); } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java index a83134639..8f4b675ea 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/IPHashPatch.java @@ -16,6 +16,6 @@ public class IPHashPatch extends Patch { @Override public void apply() { - addColumns(GeoInfoTable.Col.IP_HASH.get() + " varchar(200) DEFAULT ''"); + addColumn(GeoInfoTable.TABLE_NAME, GeoInfoTable.Col.IP_HASH.get() + " varchar(200) DEFAULT ''"); } } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java index e6832a7fd..8428524da 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/KillsServerIDPatch.java @@ -40,7 +40,7 @@ public class KillsServerIDPatch extends Patch { @Override public void apply() { - addColumns(KillsTable.Col.SERVER_ID + " integer NOT NULL DEFAULT 0"); + addColumn(KillsTable.TABLE_NAME, KillsTable.Col.SERVER_ID + " integer NOT NULL DEFAULT 0"); Map sessionIDServerIDRelation = db.getSessionsTable().getIDServerIDRelation(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java index a45bc697b..a9155de2f 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/NicknameLastSeenPatch.java @@ -25,7 +25,7 @@ public class NicknameLastSeenPatch extends Patch { @Override public void apply() { - addColumns(NicknamesTable.TABLE_NAME, + addColumn(NicknamesTable.TABLE_NAME, NicknamesTable.Col.LAST_USED + " bigint NOT NULL DEFAULT '0'" ); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java index cb4fcbb28..9ad3c26b5 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/Patch.java @@ -78,11 +78,8 @@ public abstract class Patch { }); } - protected void addColumns(String tableName, String... columnInfo) { - for (int i = 0; i < columnInfo.length; i++) { - columnInfo[i] = "ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo[i]; - } - db.executeUnsafe(columnInfo); + protected void addColumn(String tableName, String columnInfo) { + db.executeUnsafe("ALTER TABLE " + tableName + " ADD " + (usingMySQL ? "" : "COLUMN ") + columnInfo); } protected void dropTable(String name) { diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java index 672780d3c..5e8a28032 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/SessionAFKTimePatch.java @@ -16,7 +16,7 @@ public class SessionAFKTimePatch extends Patch { @Override public void apply() { - addColumns(SessionsTable.TABLE_NAME, + addColumn(SessionsTable.TABLE_NAME, SessionsTable.Col.AFK_TIME + " bigint NOT NULL DEFAULT 0" ); } diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java index 147d8e47a..eea4affe9 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/databases/sql/patches/TransferPartitionPatch.java @@ -16,6 +16,6 @@ public class TransferPartitionPatch extends Patch { @Override public void apply() { - addColumns(TransferTable.TABLE_NAME, TransferTable.Col.PART + " bigint NOT NULL DEFAULT 0"); + addColumn(TransferTable.TABLE_NAME, TransferTable.Col.PART + " bigint NOT NULL DEFAULT 0"); } } From 93951f1f254145ef20aec5eee75d73b2074081d1 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Wed, 26 Sep 2018 20:43:49 +0300 Subject: [PATCH 2/7] [Fix] Increased database clean task start delay 1 second delay is not enough when a patch system has not applied all patches, and some data requiring patching is scheduled for removal. Affected issues: #732 --- .../main/java/com/djrapitops/plan/system/database/DBSystem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java index 21eb8bfca..a6aa0970d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/database/DBSystem.java @@ -81,7 +81,7 @@ public abstract class DBSystem implements SubSystem { try { Benchmark.start("Init Database"); initDatabase(); - db.scheduleClean(1L); + db.scheduleClean(20L); Log.info(locale.get().getString(PluginLang.ENABLED_DATABASE, db.getName())); Benchmark.stop("Enable", "Init Database"); } catch (DBInitException e) { From a18dc2d3f24773817c9cb6507a5c712c183b4fb3 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 29 Sep 2018 12:16:07 +0300 Subject: [PATCH 3/7] [Fix] Prevented NPE due in PluginData This exception was caused by analysisData being null during PluginData analysis - This should not occur unless two analysis are being performed concurrently & first analysis finishes and clears the variable. Additional work is required for preventing two concurrent analysis. Affected issues: #711 --- .../pluginbridge/plan/factions/FactionsData.java | 3 ++- .../pluginbridge/plan/kingdoms/KingdomsData.java | 5 +++-- .../pluginbridge/plan/litebans/LiteBansData.java | 3 ++- .../djrapitops/pluginbridge/plan/towny/TownyData.java | 3 ++- .../pluginbridge/plan/vault/VaultEcoData.java | 10 +++++----- .../pluginbridge/plan/vault/VaultPermData.java | 10 +++++----- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java index b6b63c650..b3e017a88 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/factions/FactionsData.java @@ -72,7 +72,8 @@ public class FactionsData extends PluginData { if (!factions.isEmpty()) { FactionsAccordion factionsAccordion = new FactionsAccordion( factions, - analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) + Optional.ofNullable(analysisData).flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .orElse(new PlayersMutator(new ArrayList<>())) ); analysisContainer.addHtml("factionAccordion", factionsAccordion.toHtml()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java index 53e97c965..7e842e0cf 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/kingdoms/KingdomsData.java @@ -1,4 +1,4 @@ -/* +/* * Licence is provided in the jar as license.yml also here: * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml */ @@ -62,7 +62,8 @@ public class KingdomsData extends PluginData { if (!kingdoms.isEmpty()) { KingdomsAccordion kingdomsAccordion = new KingdomsAccordion( kingdoms, - analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) + Optional.ofNullable(analysisData).flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .orElse(new PlayersMutator(new ArrayList<>())) ); analysisContainer.addHtml("kingdomsAccordion", kingdomsAccordion.toHtml()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java index f58704be9..92dfadfa9 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/litebans/LiteBansData.java @@ -179,7 +179,8 @@ public class LiteBansData extends PluginData implements BanData { if (objects.isEmpty()) { table.addRow("No Data"); } else { - Map playerNames = analysisData.getValue(AnalysisKeys.PLAYER_NAMES).orElse(new HashMap<>()); + Map playerNames = Optional.ofNullable(analysisData) + .flatMap(c -> c.getValue(AnalysisKeys.PLAYER_NAMES)).orElse(new HashMap<>()); for (LiteBansDBObj object : objects) { UUID uuid = object.getUuid(); String name = playerNames.getOrDefault(uuid, uuid.toString()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java index bacdd88ec..83c902822 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/towny/TownyData.java @@ -97,7 +97,8 @@ public class TownyData extends PluginData { TownsAccordion townsAccordion = new TownsAccordion( towns, - analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) + Optional.ofNullable(analysisData).flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .orElse(new PlayersMutator(new ArrayList<>())) ); analysisContainer.addHtml("townAccordion", townsAccordion.toHtml()); diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java index 175672de1..92c5047fc 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultEcoData.java @@ -9,7 +9,6 @@ import com.djrapitops.plan.data.element.InspectContainer; import com.djrapitops.plan.data.plugin.ContainerSize; import com.djrapitops.plan.data.plugin.PluginData; import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; import com.djrapitops.plan.system.cache.DataCache; import com.djrapitops.plan.utilities.FormatUtils; import com.djrapitops.plan.utilities.html.icon.Color; @@ -50,10 +49,11 @@ public class VaultEcoData extends PluginData { @Override public AnalysisContainer getServerData(Collection collection, AnalysisContainer analysisContainer) { - List offlinePlayers = analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR) - .map(PlayersMutator::all).orElse(new ArrayList<>()) - .stream().map(FakeOfflinePlayer::new) - .collect(Collectors.toList()); + List offlinePlayers = Optional.ofNullable(analysisData) + .flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .map(mutator -> mutator.all().stream().map(FakeOfflinePlayer::new) + .collect(Collectors.toList())) + .orElse(new ArrayList<>()); Map balances = new HashMap<>(); double totalBalance = 0.0; diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java index 1e9d83dce..e2701c6b2 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/vault/VaultPermData.java @@ -9,7 +9,6 @@ import com.djrapitops.plan.data.element.InspectContainer; import com.djrapitops.plan.data.plugin.ContainerSize; import com.djrapitops.plan.data.plugin.PluginData; import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; import com.djrapitops.plan.system.cache.DataCache; import com.djrapitops.pluginbridge.plan.FakeOfflinePlayer; import net.milkbowl.vault.permission.Permission; @@ -49,10 +48,11 @@ public class VaultPermData extends PluginData { @Override public AnalysisContainer getServerData(Collection collection, AnalysisContainer analysisContainer) { - List offlinePlayers = analysisData.getValue(AnalysisKeys.PLAYERS_MUTATOR) - .map(PlayersMutator::all).orElse(new ArrayList<>()) - .stream().map(FakeOfflinePlayer::new) - .collect(Collectors.toList()); + List offlinePlayers = Optional.ofNullable(analysisData) + .flatMap(c -> c.getValue(AnalysisKeys.PLAYERS_MUTATOR)) + .map(mutator -> mutator.all().stream().map(FakeOfflinePlayer::new) + .collect(Collectors.toList())) + .orElse(new ArrayList<>()); Map groups = new HashMap<>(); for (FakeOfflinePlayer p : offlinePlayers) { From bca13717741a69582931580b2b8c15169ba9bdc5 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 29 Sep 2018 12:17:28 +0300 Subject: [PATCH 4/7] [V] Updated PluginBridge jar --- PlanPluginBridge/PlanPluginBridge-4.4.0.jar | Bin 169161 -> 170155 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/PlanPluginBridge/PlanPluginBridge-4.4.0.jar b/PlanPluginBridge/PlanPluginBridge-4.4.0.jar index 25b26975718cde71b772644ae2520761400953a7..6e1a2c5da8518244256f153e76e77a19f4848133 100644 GIT binary patch delta 26261 zcmZs?V{~Lu*EO1oZQC8&?AW$#cg%`y+qR8P$F}XHW3$t@pYG=!_xs*^Yn)MQpSk8< z8|O#W+-I$^3fQU!SOi5GFmPCq|9r5G`4bSx!La@qwrmPaV}7~6@PEI+|5y_TJI4Q) z|A&$ANQ3gfiTFuE&_qcJ09dd;o+MoW-(Q;r`1;pw1C;(hJ?N8ASn1MY%|RjI{%0=# z>FNtA{KtH_WWfH7^dFj^`~NTPvjQe*>%TPKKeSv+ zP}C#_aOnTeh$Sg@6*&#T3KZ)98b1bH=r3UoTN>n?>5{cA5F zWd2%SNZG&E2!bUIIRX^&e|4LIl=$n$ffD&^4WRzr|A+2)2l(IgKX4K;H1c2Se^#m> z016NP-$w@x@@GGqe4#1-ZbVZlDmVDwJzh*#COP@@+$!NfK)C+wF-6)}M^MBxd0luLHo#aX{%%^+953ZR@|yq` z++P>FwCF#wkE|lXUt~rr}Zf)rH(xoG0oAh;eW`yU}%`;!FZ zhE;mib~UZmHc?wTv@I=|6$IhkPiwu;iy9jz8*fj(J88Ww0=W;stDG^@4lt4J>*vO7 z@9B<{_vyBezH8o-&)u4|UO2;~ZpE0S7i*9iNT3sl0o?An5MP*s>Vc;ZYq*Ms5PMlr zJKhfLzL<{=JNHsH%sw3@sD~8)OvUlZ;zNhE57f@m%{@tX^mWDYyU_H9Fi0QvcSdS~ zI`CT>eYo8;u$O@KV^^^!k);pGfjaO8R0r_esu%X9_eT&v5q==6KM~Z8PUJ@$*h>!b z6Y#)2=!O5o4;kpcvUr2U{-MNf@Y@L)7$}IK($c5i(>Hh1hWp77E*EJ7_Ng!TDS~`{ z69@Q6^7?EtzGcMyWDhs*M){#|<3RGk`zgZyyJD{o^yQk^FOn1NB@bje?558{Cc-Je zG%!D0wY)TuH(cSdk}Bp!O+iI2U%Qe58)%BA%=o}0YAxKT_#=}JwlGiUTRj(*#whBN zPURR&3zIUv#`s;nE`^58w-`!V@K1AEtF;aAl({8uh~DMJdTPyCD~hQrTjN>TXPh9X z>0n+J5pkcogw{na!LueLZJhq89FYo9+ZV-k?52xFO-c^TnONu-kSCP1>` zc)OCsjMZ45vZ?wQ%KmPpg{ug>19NRT=vZ-HBpiuo&E?kphR@H~*3z}^G5ho=6Z1{k zbSd8Dgvsa(5jPteD?D`Gt!6Jowm?PwVSBQ@)ALcGA9I>@IjxCB((50I@(b7;3xOdE z!_8|(0jYUti`>TK(Xzul#)u^`D8MdD$Z^B90|=Y=7RIcWPzZL`rITcK5p|(~Z1hFc z+LDRH>IprW#>$4po=C}c@PJ^a<`II>Y^xQvX4yJBc!DdIVSH1|M&%?M_Ks;+$GQvw zWyc#Z6@tyo$Pu{}(6r@p3ah~pR`uh?55BDM__&iTEEnF|0l41DW_W~>dEh`SRNFLQ zHR*6uYQ}uhWiKSMvhXKcN@L9(&ko%!2W$z8ISuhD8)pj`SyG^^m{wUzLsl~Q!oIgp zHSz(%LWq4XMqbmtDc+i=wxlU5ju)3A<1pJ6;&Ddk+=67(~@~al{ zq|h8Wm9UWs%#+_Jq4zjAzQBqGQobK^OHIE}8_O7O37S>CYIDq36u#JI4c#=_(rV&z zq6Q6!U%E)ya5AwJvWn5?-T(X*tdro(%)3~N^&l>z!)j7CZ*E&|I1pXN7-@V-sIr0M zL_;c)+wX%Dok8!Vvi1<)F`%r};Al;tQ}(oYD6Uu7m>Hn%QFf4{JOUigWEy7EDw@!a zuxiv#Lugb=g4E*}GuN|EY&-kbWNS3NSfZ5aVwGCcX`7^zF6`Q&)~fvBrKfG$#ZWU} zD%Hindw3Z*6kDlIBMzr6zR7c3x}DSMGHTa(ktna%S)3IClqev772h8&XXq-CNZN2+ zBkPe33$Z>{S(8o^SOb>J^j$2O<=u$ZyHGZ_TGVi@sBkNuxu3ESzq@vbTosqnnyBtG ztW8g*GNSeNt8OkkfhwTvUPTz2S(_deM;zL7=a2EaGMmO5RWGQhwx9iucX!1ZUdT-E zjE1IFnR-$kpI$XxGFyjI8OVU}>q0mE=(M=1UkSVo_jprD>K{@EmZm7M8lx$QA@asd_A`W}t96DBme-c` zQn=+)**x8J)rP>NuZy};30kt)=Ov6giJw6=2AJ!VoUW$y1vUp4U1oS zO;69sR|Vkj+}!CWWpR3`>0caEV)8;_)}l;5FXQJorq*cGf+}e>iF)`je+H%~tq6r6 zuH@m3W3BJy6RV7|hVZ=0-risJi$qs0-sb&+Jg%8-rih=RwFT=LEX%9aj z*6AgoVU9F3G@#4Ckx5_Q{Z`#$sk7)0Jlkmy%27<0M;ax}M1=3(c)XR}p8chkd z^Dt+BeiI}rz87^~F5ZToilwEt-_d2dia6&dNVkosRbSloe%JdvTQRdK+w3kxX|lf_ zPK?jdIsjWfJTo0*YeJW&AIF^>-a1yIbTgRulvT*}BvV1Y&$T5iAhb6^tizv3d^3*& zwwZM}L0Vb$bQhX1#~k3!yteKSa){N?k2&4R%pEF~XImE`CJz17VF+O%Y!Zno)Z>^! zSP+mDllJLTL9KjipOGYT{+TUkN2x3!%X#vEOLp8~{OatLE73LSG0cI+ObBM1Niwi` z?B>C?x;D4$XDA_KV=!`MudlP>x!-~g^!-$6veAQZhaHtEup$SaD6=}BrV+f^3%=CI zadFeilrR|R$# zdge!`JINs3?Xz6P2a=AFjlYZggv;nHU#r^BcZg_?ZMiHx^8$A$xZpVQm^P{j44b;$ zFU!`$hsJzxX@zgTKI5NV+F6-z^NxE3N!gevp&T})S$1z$GtrC?GOL=oM43z#%l2COcAO+lFx}Yt z;b?s|#_HIYbitN5;KZtOUi%;kbSi;fYCGuYacS%3y8OB3pyPRRZiZ0cD-To4C__~E zuz&7Ie>Ds!ClVC(JvZhZRpO*s1FNm%MS#)B7BbUYb;ndI+nW)ZAc+@wg!3^7AZfkt zz->od(hNB&A7YZ|ltDrx^SLt*4PtO0L8=A!Yd)q6+-PD$zF0{| z2TLDUpF?a@vX~}}mb-_~CxzA5)h}}@W%D7EUZ9n>^<~EB=sS*XS9GU-+CZfveU_P@ zd;Gv-4B)Pv#OHvG5HR!xM8~LfiEbcZw=F6cCRox9LS80`#!fZjuN{W`v=>FgW%8fG z9+2~Zj`0X)RmjI-G^W)bp^pWONOl0cjrNc|*$dFZ*6x$+3HC3n4%3A}rCySm??csl z(5;5fwu0F7R6|zc1LR%_`)fw`p{a9sYo8lWPI6K3Df&Q$rJNyvJ|`&iju(8jbB^6| z1HKN%L~A{8*Oatxb^Cm2(?vvewY))=oCu9xoi*oQ6u)pqv2$0O_37WB`+Y+^hx~c2 zFM2_@+$Y)-KQQcUiOm%z$M$l}+D;wT))O7d+TJnO#?dX-(a9WE>TG%ZUK68cLs=i= zIaJr}8?_5gsM4bYgkUTxQHru0R5tng8pt(XUbd}CP~nI1nS`|+u8PJP(=U005lkus zdP-jTV*^jbx;MPRI)s^D8DkFp^@k)?*fN@CLYQcK408A>Eczt__9%Iwf}sS(Q%<+z z>X0s?gNEY9*>3;It`VahZ1Wr(FE$}>117=42Scdl7vD8GpuzKORNH~kMgPHRQNvrt z>(f3?<}a;Uu8?0Zgje6=oYKIZz8?3-9~CQOb3>+3&_h8iO^?5yoe;0M?*iV~L@TEd z16}R}e&!Q@&$)7EDol8Pz9sBVOQgKv+YWnHk>+Xjr+6Y9ghEeU^@b)=)l4*dK^Bmu z-iJv-C)e zC)=JjLE)tr-SV8YsJd|v@XtGu;*~mXe!fM*n9lmbs2JX8MWcsnQB|2}+#3;@hx(l7 z(zspr0t~GEMhXg)7gK{|!^oT|Bn#V^HqG&4pa}>Eq?W?Tc>~x;nWzE1P#g zPWpCw1bFjIKiKPTqcYv@x{s(+SV;QuSapl@M1!Blu(#m*e0tb5^KDw~!1*oe!Vrak=z)NF^Hsy}mJAOhr^uRktbrxPfsCQ)xRaTZyizr*E*l?px0$l#Z z_d7*JVX*Q4`C+_RT6cTdex(&zOd7k6n3Fv zoHbMScCfY3we#rbfIs?xYM*lUEdvS`E`Hsf8O^^JpPq>i{^`$Wo7nrT67*jVoG=ibJU*%VPcyFUTxB}5zU`QY@siXjYE60a3*mh&b z?IW?_Xkxt(ScMz9@T1e9_)v#5SskUt@EqTdYr-N31i8k zffRoq;x6Y)P2kSnGbj-Gg{*(Yuo%G`t)RKmi?N`3P4+Zc3;5?BZJUM4mX~?ka?W8& zWIE1DqIXL)$?p@ai3@=NQrqyMDOD~f;vxj>R&K)(sKhmWvaK9u)E=JSbMt$CWLsHG zD(da*f@_6q+Wbc1b@tcN)x(lZLjLPo^n@u~m>(CnpSOw3)7U@WtUQ^>>NXLm3To36z!3Yn@bnlk z(I%anZK$Vjqvds?p4+Uz?IF!rpDxDE)g2S+$yG!uWS1POO~0`nMs*uXVJ^ACUjSv21;;%A?4eX%enX;vuct3|Qh-T7P zN+CXL_60rEceycU4j=-4S8|OFNs3a?q$p?Pe@pQp2Y*$AjsKx1S3+E|xu+Ax+r4gL zexZG-y~U!^nU6y=Rjj2vZJTne2%)oP&oHI}msvH>msvLt$|Eu>oFeQ}Djg+>;gTwC zu>j-__LOgjXPwkyYA$x_S&+ACkd`^sO-Y?+NufF8+osaa@s*5tK1)@jRjQc(rrpn* z)J4L`>>c{39W9wsS+1=ZBTZ|C5u{q8tu0p$M2V3pVY0_;7B6XOj(skpTxh1x5^Fhg z%gd-Yf3^H&TX`_@+T@fZjWruWW0xNVjRnl6ysfCNxgCV<%8FS!{`@jY0HI<+S%feQ zKanc6H3lZ8T3N=!ZKG&KRzYJ|7=?pV#J9m-6<4KKGPOumE>&W_{=>CW8Lmd%iq?*H zmT7)Lb)CUrig1TaHFErMo!S*P#Z9qFn%Np{Q9fNu^MU5FSTC4$xXOAQcP#kgqy&ht zm8IGl%!3U;Fyg*W&b=z-r@1Wg4P;QOh+;r<>1ej^)2RO9dP=&8E|*9fm_$azaNAiy zL=xH{P>ax5UBSsDV1<4dQ)T!7TwG>l@Ud1nnGgRif8b?LQ__W(bx2|_ZM&J#nq6q& z)hN5^cS!A>l7@ViXoS~^an+$%Q}vX5YOYCN9q+Dw zBb~e7KQJA)Oyg{2Z*f77GEfp^iZ9n!N z4IY@)*^Eu7>9*WDx7<5!g*5Hc!TzsI2I5~@$2MM zZ8|x%JA?X>-lfi$&9%xh_uKR_8tsv7{$n*Y@2z2V1&(5!mgIzF&VbU`;V1g+S*b4r zN2YqdNeYI!7VGtJjqmU9T{B|SesQpQxu!GNwK;bXNpU*bqzxJ}=B^PeNax-1T!@7y z(UflB3RUtE3&KC&R4|7;#+-qa#3{2EYm8-HVMxcn9OpWWQP)!snn~8ZXVPmFtIu;uoHg!dbU^@5O(LG1ULwy^EYK}ho zx_d{8^SqC%vTxjqDw7Jz$$;0E*?OZIn(0Wy@j-Pz9G1dj_53D@Zvml?edQyHyPwL0l|9hDRG5 zgaEHMPKv{|3Qg;e=jokvmwd49i9XV+_7T!K3wtM0y)6-Z$~d$T`}YRtpW+BReY2#%ENlS@$_eof~*kuBcp}EH@5|_oWPA(fa8>PUm!EmC{#!3W2TuKNf zM6|LQxw?Dm(Fo`bT`jgWveSeJU1wbI$Mc{fEI-7q57JiJ<<+J6IZw5X+^v`{_q&3| zI~Oj~VXwOP%R@nCWYV%w6 z4fEa{mv2hnEU|-5-1bc7cm+-X228i0Ci4*gzN8fV?D&Lqo^Oe~_Rsi49W zenolthRprq481vXE#n9i$C`nw83;0=6I^n-3#^=olfM8Se=$OO>TINjCHm-gn?X)a z;RfHZaaJTVD`*_4w^frHijK^PE!5(kBvj`tDuQ`P|uC90UQ*frzSR>QT~Za1iYoDSi3B8!OvoD`7qyOD2AlUyCe}dxS zMw4_vn`Ka!m8;tozX*TY%3=o7yFTbmC9w5l4Zv9);sw z&>uPNa?=dte1QlkD5xWj3E2{)e%y6w_o0%+=%Yp^q6u+wBaX~)(4m2zfXi>k(5v`L zBG!_?v4UH9)v$4qpH6HIKaV5nxtoKr98zNZUnJsD(`Zoq&Ern1w;)mQe`(Jyocpb58xW>gE z(9`A?EXzO`3SLi(LssRY>cf_uuEisQ{U8a<#XWvJ6|2*nX+YK6o}!z?f-=mPS!O9>1Q@l6A9k6lXiCgyc< zR*uYcgM^M<&gv|@A{g=Z$?b!YJtDr#Kfe&D=}H^PP^ zs2`S<>sGM}K8X*<%sY4KD_^fa<0sPpAvyji zj*=H?g#Rdxb}$eSJ`j+$ViW+)U!Bl4hX(NcD>&NZumJ;*eew=*t7k zCNd2AGI~%9vh@SPj}MkT7dzhv@h6|uc9Lr@={2dR?9%HYNSH_F^n$Cv&`jAKe1?EZ zDb_ik%*h(h%$#TKaG8~lm%sdxCr{dF7u$C;J_Ev|yYl#vKp(_=99+gl&xF}*>Yb?j zS0J5$4xtR2K5jm&eyOvWe^tBoiSwa%$MnhDD_`x{iMBoozw3M@QocjUbd`&K%{^|+ z3Zw@CCwO~87HD@a=c!NcFrKUZ(>>)j9gD-9XO(>1Xzn)Y4*gyGSHg`$r-JHl3BwLc z9$nKlq{ce_neBQOGAD>RyslUC+=chCAVBszc~4Q-D@4cWgFF91FPC0T4+>K^Ww)Hf z8T}mhvu>T<=hMxydy4sP)sqPqzb-b$l~>rDy2V$ToVw*#0-ipV3uih1ouL)Th?k!E zPNjn^(B`P1yIX;n9me%`?(c0ki#|UObdK)9cz}xAMDwp4ISyC^KrYEiqN$Uv7obUD zM$i14M?89;hW6J{-);r%XPb`*pH8Jy{ty4*yyX{GKxagd-zVKQ(oK-h*Mk|WkN9CE z)`}IqvIddCy{{B#p zESxQ4O47Sax8ssdQmN9C16_RH^jWJC4$&zAS`q?TZ)se&^sY%q2O3lahwbjtT5B$c zdK)OG)ky6vbvg`vG?^z&y9$h3&GF#Xh{}1V$pDrbe*OeeBD6ZQ`3odkY9L-41Zdc_ zqcaUkglph(P!AJ5-b}B}O4}7kSohU-vZZ5N21r?VPC{#r2Mo1}Q7R%X=@|NGk8mtW z-Ry)82q}1rbp0Hed~f_ap~A^O3$X z2bvT~d2pBV*4&LXU%foB`Bh~^%LLX!v0Vo~?QH6t|0-jdc{*jLhz`p!S6K-M=MxkHNHCa8z=&SRgqDe1@Uu2I*cp0Om1ZhV@{`op7@~ zuE-2NplCREGGs9X@xU6q?@o;P{+4mMHU=3GbJkhGCL%}p{4T|0HVEt=KBLdw9++=Z$`Y-8;+xI1&N zq{+l@K5O?;4jeXMdSUj{Jz&K7F#C)gHei3T`EU*D>)&B}(fJ4#A}U5=mLrIqT;G;# z-jaq+b0E8K$;7s4qDdSbPwn5?`x5Z9zvOiI!}y)qf%fiqxACF>_LU}ceC8?bRk%rg z(ZG*EQI`NwWzl2r zwe~8_m>s4UK_6KaQ>tSNwi5UwB~Rl`QPSLUI-q%`XErRBcD)t*5qq+Nm4D=X#&M~F z)K`ihDFiT)WMli9d}{Ea3FYY{ihdYOr`&H1LV_tH_jeuuq!a~(J%6@PTIyG}=OQ7V7%xCOsXTdhD%IO2dsaaPN zXkh!3@NZL1kbvprDGiWi#X>!_YG*`}fbHU=w`eB5F-3?<CU&vpP`fQ8jpB&x7avY}=UTY~(E}g$HSw2HD;URSvNzT-e0lWVD0JeseYM_1{k{`YzY3%t2k2++4;ed&$QdoGCKQxbCIxJ-d+vj<#K z8uh>8gld;#8a*TmyOtJURyDD?wicw8mxOAU(m>J2TxjlUsD(Ua)>p`nWRY~(uW~TS zshnV2ckpPaoZwW7FR|qC_nHW*tc5!*8KG<&_Tle)j*vkbgxwYQ!{CMubNWIP42njQ zlb0%ov zpWeS6g|ditXL3DW{urHz(ZSnRw_?d*{-I<~j`@u`AvuaYAi$(O!~|>om>9TvlRUa$ z@$`^S6KL0n(6cR2TU@oY$8NBM{07e?Ub>>aWBEgjOZ%0 zHW1iw@7(Ulpba7RwEO${?8rlz* z#OmuSsmC?LzOUcyi4N0CQ=*aga?tE4QP5LN>$2sMzvW-EB*`u3CT~Ko*7c{m{eoJ8 z3a)T>$R!!jE}fsFZ)M5-O3{k56AzRQ`_%%|f;GN&m1_>CZi~fQJ!J(H)}q%#K9a5G zw!f+m3T2l&R}_v@&3E`(WIli^F(X+)&3()~g+pR%CWq7BBFz1ztTBmI5W2m>Gic3< zUDTLrRh~$NFQIZR-tQUZ5FV0In zaZEt*98z>U?I_Z%V&4Hgc8$b1K75s;-bpsY?TqOFNbiHKio89y)D_F z?o^SMcHVYn^tw;$p7t&A4v1A*S&u@&dnZ@qv7lG2qTnViNazJJy8k?^j^ywi0B;%M z&Gaf#8o{O@D|_q5QUSCGFCjQh;g@ZF>!9?xri$!Hhu<3SrL_BYqJDfv>KaDHS6)2qhGT(3`@;MO!y*yF6a11h7R;E z(8ufWxQ!xUSiIGfJlhf|kY8HH-Lox3>spQ?!=`T<3>*T%nQ+Yz$&VKhtSRKh*X|E% zKiaXm4!GPWd24zDHjZ3h{GV+t%dIMQ+IuQW8+tRWu5N29nBm~ma=x3jCS`6FeLZqw z+%)UYMiEx2f^X5TVLcW67PXKst)zo^%7VIqv11j2cftE9PHX0tUg!{9`*9V(C`VHC zhP!{8)AJQLg|;^9j>DBEUNxo3Yy6-a#v<_($Ext-8-K5m#n{|QDk70yfQX;-U}(@) zCP_ubqik7>mCe_uQx~=<`vRSaGq4w|WFn;S(Xa0uodh#T2NUE-)ZG>-1jLAyL5I|E z#{{I;fV;<}Wd$$6*NJ06*#Ib^;!`pYNNS{+g)s%-y(LeIB6G?-!724O?75r=CDJYm zIF`?@D1l?T6CFta4f$RAkpxxW5iw3bj8;?*I^Aj655G)o_@{;9#@)kRbnoW!ywazI zF*&n-MMqq(+1g23IC-SpM1I{fe&Mp#m)QAeOa(D#8GWj{jv>4|4x-|Wl-_to&CNJH z0nq^9>o2x0WoI2{8rL@Vw)yK;`6~F}<^dNT5t%6A&J7xv*RADf#qV}8VtSTjNl{nlxDq^(EYH9nimb z(#+R9N^XBM7FXccu?74NVN*LuHYch9rF=ij_R}VuN1W@hGA+^IR!~PFLb+lIi==6} zYc#wBXwNCQ_Ir8AzJA{AjwAb6slEDf9oQ=rSfHwHT=P^0Y(%~bNLUn&P}U;sdNo5K{ZE$W zx_nGmcD?+kc@9@Wz4F*uv7c66RbCmbKyOf&1Pjm>FIX!mxB06K;ZzGuR}bqOJ&By$ zl*OYpS(F*|^|Lj3C0_U*d6H49IsG>-Isaz7GyBdkSYlxERmuCPfAZN}*L+dDfb_d5 zI!k9!{31cfg;M7ZVUHlI7ddMuR#9HOQm6EmXkM{GLTKeKt``V>jzB&`i9*p4u&irw z=mDo0bX9`eH}xqglt8u^>I}r3n5tCdi*{a77&1%ABh%wRs)gtH8L|4kVvPkQfiP_x zhc`S=bZVN{BJBe-0SS>W;lc#dvE(;4vaz{natYQ%d@-if-?Yz&nLhB8(<`TNxcsGa zSnOVWzn&>#db3~Aj(+0$9wE@o0Z*$uUGqI+TBEzJvF%9cPYOj_aD8heit3;WLL6o} z3joDPehHLcy~35;;?+6#8X!G)iEC;e_B(3l-TiTCTF4ex3kganZ*xjh8+W~U3rK{5 z*+xoe-mHRNc7%9{u1=Bd;5jT#64|V%7_*koXYFC_2`8Bjr%La*RdoV&fMJTSjpc~* zI&T#v%8W?*yIvlW&ywIUXP!~n*z*b-Pjkhk{Q`a@e0c5oJF_x4akVMQ(lEQkQm@IN zghm^?0x|Cg_FGV zUr;!OX}VZq*g*S@9iqeGzB(ctm@*&C6x(rXgt(AXk z>A|t$&SJL2>(*fzl2!b@JdGX&Qqbd8%7q2-em#2^a7W=$O`x$(YUwSqlX=srRu*#~?N;b;L`SHX+#?Et_s|if-^HfskHB z+e%)Kz5V6R9Lr$4dLQmm%s2wZIKp2OlwV^r7Nc%cVtZFj-cFrCg6PXU#~eAIV3_73 z^7o)5&~JBZv+tCLdv>GWw?LF&Z!fhEm+?@Ro{Wp2fTvO6ad8WIr!tcdY7F@!T#^PS z)T7qMbA+PncDrOaJ5Y$v^Vdd12tESV8n`^|TZS6AfE%ALzW~0k*d42_m*CaEsD7a+ zJ_{$FG>qp_e122HC9#w;`|fs*K|CJO_f6CJ^!NUJA81AjLBw;C-~;5;(rMl3%1FHV zhg+hXG!Sqm0@~*Mbl^xQb?7i_`s(=2D>)ivYzvrdf%N-NT0eXt0Wi?rFi;#Q2r1^t z;!NC-#PkVJuba22eb)~m920e(x9A;kVHd%=CdNL`i6pd4{?_$@tasq~-7UW&9C|68 z#c|jdm0zKMR^fo|z1ptYI6BNQ5z4C1cReS`2{_1YKSl}rfYJx?2-o`=acJFz{fmHA zEc?>ldOY=554Ogp#5D&rvsO^{TK(%ZLd-qVf_M734)N)-1TrnbIv^77*@D9<*gjCA zccuanH(=2ktEq{Efh0p-lqDy&T_?I)J$7&o{Jc&gc{|cF8!_o9{}bjH9%#x9<(uF=-R{;eL~2 zzq92NxA83Xx%eCKe;pyW6|eyg{~Z~Ja{|KumSMD^@d6C~=AE>;iUA7#d2u8GJ%4L6 z+SXJ6?Ef$nb$}7!f6`mpTwMTs|FC8^fXBZUqbDFAt+Cl#wURQ0u?aYCKfG9(z z`HKO*{4Kai`{?@>>O)mjd{&~q)0gC_T=6(P$_!oKY_cKXA#y!j3E|{K@~Ra0G9=k0+l3(VTLjMIyZJjj?k#pD@O zQ~bBvfq>6Ih(jgxo({ui9dIAuvk?Pz8y^{V8_w5TL>exk=7SYCcHm9Z8#ki%0V1e+ z3!a-i5~r#gSax`8k|A(3tZ<_Op^$c9@zTNPS8zAZsOKSW`Ad4J?zS4P&uOnM@SYcA zX73sHGv637*L^SU?lUP;@PLuUt3L$G>@HDO-)*msp_>%-Q?9=%5cv80?O~sY;bS4> zfaWs;Kq|Bore!{9&seTH|>D-}M(xG|~5L7yD*(>Y{ORwm} zovSe?1ZOr^WsFAu4LE1)$%t}MQZ1)lIx9xgy1_#4z5uITad%*hl-(>{veYczu2(p# z4X|`kL1V9Kj$N*54q$Md6US;vUNCExDo_tuSG6vif_0^G;V!UE!C^VDfi7XcZ$-tQ zGU^jb-?R>meiE%;33og10qEa0hx&RMRuQtBWyuEdqgUvMPn57I z5_pVdA0eCZHgJ1zHy=mgl6bQ#Dxqg;&v9`k{YU^OorK?hP*h?WG2N`Mjh2vI1?2|Z23fo7gXr4!D?Hl;vQ#{+T;OVjV%ky87QQ8Z4}N)Tlnc(> zmK9cqQFRns7#P6ZI8;Q8OWH&d34UmDHJFbAD`FSEbdfQBs?)*0O(iW&)s_mAGf+S8 zyT?jN0p2_&2E;uV$^o=V(;)dVOP0wN(;+#tX7$_&8WCI*T1~oSz8SLE?_MQW6!Sw3 zGNVWVKj1{LSy@GSdrwt^{F3$X;Fh^*Sz>`CPY&JG^7qQ@;CM8Bn{-{jvQZ(-S+#>; zEj1s-TC3fM%2~`1E}71*Nx#@v(d3YA4YuFEi?ULk4_(5@sAujLCns7!PtqUUNBpU! z`gkOGJs*20!`6crCDSoygL?dWyrQF*WMHanhJkQed~2Q?Yc(vUtWrf@Fa=em^{6Jd zj*B;M^OaFY>_XzzQz#8Jgu8n2abll&&{(LR%tm@s?WT@D6|s>>miPpJi3Ojrn?T#P zqFbHKc38AMZY8~EI7@19p*-T?$wn+m{i$Ur@TX~(E21qAlhPz&3ywIeh=?DvH8J;* zEC3SV>PczPy2Oa{JklI4j4FhR`!3eafn>m5&RfQV;zCdtD{2-&>s2YL7GV!{uvjMN zZ{g}mc!L{D7WF*vgy`4J;hlOt(a0FHUL56QdVSxtSxM!D9m8YuLhI2gYMa$r+YtL2 zvn}p$l36qt^MIJDvRtWLvP2@KRpdV3g%0#;6X%@shPH@ZaaZ{P|AGT=B--w|u3}I! z=Y8f2+hb6KG%Jwd6ECH9nSw-=7=pBbTqY8wbiZ0F1oJZi0wqc84f+u^HE;1OhF@Ud zu0MTCwnkxw{f!FI+J*H}X05c6b%B`6OoBPYe(qHL4Li)Sfp2}_R=z0DWG)={QwgZw zU8m1KIF;DMnK^$n;z@M5M1jLYX#qiQzJ0`o*^) zu(x;)z*9b(#{LGoRWV1x!?zaEf&mQl^Y7ITK!NP53cOmTl9FJIkmyv z5_z_MaP3qm`lE(muyWMtmrr%L3TJ=e@_*eipGQ*nU*i`=Dx(7?nxe`Mx)Kg{yKUgx zG@S%j;Dm697rn)kE5Ll0V0%N{A}NyS$UwCk4!_ll$R9cN#`QIrWKY;)3j+qB@Y8g& zdu99O&mysV1?DcEN^p(pcvS}a(cykX2n#NqTD@h63ziww>zJ3h$M{8{gyhbhYCPD4 zPSlFe%w4bb9t<$qj7_V;{t_xWIg1MxL*q|% z%+jBQG43*kDiQZGcsn2 zYj7poEz>(`mHzmQ?_$6}mi&=FdGWFHxK>{dp3Bl{c2?`CBA3{RKGFdDFwa|Q7IH_hT-yRL!V&X^fG^yJ*l3vX;7^=oqdl{ zSLv*QyU(PG%golz8EQISxv-pC!8jQ~1+JP8LtV^vvmiAsOke|F=_}rU={$b@k=QtE zL@8m52#hJ&Oz)`G;REzFD$c|7Sb}eySI6ckMVY4c645P?p^%x9V48HvCgy0cVLhp* zUc=HJu1=UQHD^cHez$K!{C)OW(d_(-N2kUF5k5A32}^bi*&1dj#;<~7+@5*EoPnFQ zQLBU)_bzS6Sp5#@HJ%t@;9nnaZtKg0dIFXN~D z!w=G>C6)MIvaOEAak>h($RZGp?G?|&8r9K51+sc4iWVo&l-pmXRlCF(kqnm}BkD#aE#-5shVFcJkM~sj__;ZBBtPQ|fJP zQ-8VFjbKo-_e0(UA|HKZaqdT-uFWMpW)7g5pPkDM&g#Q$f!@Ws1?2v|71qg?JLH2Y z-i$X~lNz}Xb6v0wMZwD)n4Y0h6G>cFYDe?PpGp8%K$5@ z+gR>nHwWu!v*Hq8b;YkV=cOO+DQJS-8>R zF)nY_`Y+`vu;ljB2eRzLVk?C82pp}|IzU&>Jc~}?d><`FRQ6lMOsc9Xd8+4HWiVRj zvR<-HgbKvTWOlsF)l0V9+Z(GJmOhZvv*?{ zh&EMZ!kIy8xFc-8DDvr#Qq@YLunlWQGVWjkEPI|a_x!f$*hV_Anq@fh;g7lxaYK0oBkrDzEa4Kl zfJNI%XOhPA^d8#VN#{8!)8sTiCl1*uU=(`3VXNdk8)_?@F_mi2NH7+#1*m(WJA zp6j``6J3J=y9xNYOP|bO4&p-?bSA03FM802tKk8)j7S5HRIgkj}{{x%>lirAJpB~ z${b{w5nE=lxkYFvLNXT6*%Rvy9I95BJ6D%Iu^_1PVh+)uZ2(u(9M{$#Zi=eMp`8s& ziQe+ZYk-An$UM{ik-WTX#I`TRwgK%x8pbamBnQ9e!lcob=*L%O7q77(O6q(SMB z2I+>QOS&Ffkq(g%K?Id{5Y~3y- z8LI`5y^Xjo@R3A3ylMHdYmBny-N$NLjU}9HSfTnr(^?-#%y(l^tNQ9x zRB`X>hnH3r9C!3?+pb+F+HASH%$B37x&2Yua_N~>wMW1Tg>c`N_RsZlUCs=vS+;{r zqFqV1iSo5Low^+};V^Xx9Js?LnH}EwL^m$R*kPuvEz0)yT&|mhwVgiiq7#{BwLeuK zIFbHZ$m}BBRWTQ^HZIE_o((-1>(`hZ#&1A3Ik|h|S$h(v`J-ryrUptMbGn+YUN=&F zb5{Hnhmq--WwgUpzNkR_OQt=rpW)JjUX0Zag>G%{iGrW5bY`k%wN}8BmWy_Yw7ffV z=T0~d7VMwvPQ8)=hO>|{e@p!WyI6zSfW3F zr+=F@wqW`j)9lFnk{b!!Ak#I!g(EiK`GEVyG>ui{>@-!6kYXWNYjqe`v&Wl1d^&R1mrCpO z?Xq6Ewv-NgOPYZSQlc?4&8}Musbcs%&PtNH1Vcfdvw`GFgItycJlts4JfVx%x?(ss zmEAFR+ap6`%&cQMYhv3qJ+z+eq*838Nn6Ht>A9RpD4H;b;_Z`T&%(0GL z+IKxm_cqt>^ui0!f?d7uW{ZXmY3uLQI7jzjy_#6nah#|Z$DH1>xzUDDI;ritqC8Od z!yH{jV;NAUZZe7uhadE-v(?$MGON_zOivu~#aXN`epZ{8jg2$xcKK3(isOqYLJ?jK zJc9(S@Tey%w5lfo+~rRuG=c+s4|!=HRxOvl8k5}C(;a6vH4Ep@iPx@t$iA+eFjCx5 zJvJwpW^=npR9Akp^yNs={N#+k1M+W$7gSjh^)xK#eY6cIi}2wE=rn@ADTDy{j)pqT z$XmRYfew|9ak>J7i9+W#EMP4Wa0Tv5(o6bNaeV1*J{g0CjFNdW%t9lfgdB?#jg*Ot zIX)Q^KenO8NcsMeOVWZwsB1!+2y{GpwM`74J!@7bEMi%Z6@Rv}`f>0!H#=Z=`+ywH$!02;*&LJiLZ9?_G{aAcf+S*!qZMZOJt%j< zd+|f3GumR}jgw~LHkYb3?E!xK zH*1!E2DZ{&Mj}FC8f)QbftX+6h}BNCz>BuX6O8bSzjSZXc66e}FH=8B_|6w~fM)!< z_hO*&YxyW~mg-&#js>eYc6mMLS_8=%Wm$J^0hXJ^iqR!x#|y8QmnwR68&WKs?SyeE z{gtHCXtgNFo>lRcFj4O55n;SHP4Exf^EaIKUym4A(6B8KeRu`eXui{57am8g-sd(o zxfVq-llsG<`g)JX zJ+Q;(iK;SgLW9qy^-+$eI@6Cz+FbPL7nb$HPUSs$N<+q7$qSs0)@lNs8`&n0^k{WU zb0{%yE!(iUHAP#bi0d^~b^II3)pE2dg)Ai zF9?WiJmsZKOp1;>-q_9ia4&(m-U~Y-|6nB*XFfeqSoU@)tFW+P_C{)mVs*rkGTH-6 zgipl}zB#q4eTf0K&V9eeKB)+b&QqmuPB1=lztH(s(>pY(f|Z0lNAPYXMd}*b@vbmK zKjK1r_F!$$OYH_}lQ*M6Oz=rWb9WS{2YYM2+e?=E9%9w3m6j?ZHR-t$R$_}1z4yVb zcJBCQy0v)GjaqT;k&M;i-AU6J_r-Scb)LB^3ahSN!^7n zKrr)aFA@v7q%yL~VtBPV}?2PfaZEG|v+iz|X-)yy}nkgd_^1re5C z){Cqd_?nxF@^+$PdKp~osILbmUmwG|Plv5ecx&YqV?hsTilH^h6UIS{=ggf)kFk46 zLf^*@bvpaXS({$P9Af2{#EId05Q`J!B+bA{Fe}s1gN^=7$y!>Ux!&@bp*ahqQHKh; z{?+;tC(8Hfe$-!Zr*2D(n%;8jkUU!NsBr1h+|Be@6!z3!a8eNvoQryun@B-$kZ|C)ODZh>a1c(v2=MBK?&W98rSi3PQ>(f!euVMPJ4% zD%c5WgPz%Y)<5f3g3G5}rMe{P|FwlkfSfynecHHb#+k;C$_L}k7mPc1#Oj^zO-B~n z9|RKcuWtq6{`hE=VzSiw4RQFHqx1Xdq8uW(qW#+b;2ml7-zBG|FkQiBC>c*6wMpYXvEd|=v&&0 zt?W1Y$(=ecr`QDGnbB7VcU60;)8rkzI8LT#%KA&aG@Y>3Z%olpS8$dw)4c~;{U6B- z^a(!@a~n-BMx&4nX?}|~x236(HI3y)48M6N;qc-|in}i?eAFZ|b?4dQ!|i4Aqn%vD zm3Nw6j0$qDQdE}utm_XhwN$9jiAm-O=4RAh$?&VnYVA2vgYO7gizcUFcdK2_@iljw znG<_8D12MZc0iXu??o$E;}l1I*DzDq;j25>Vc{^RB`I$2hPyx$pW5-G>|t47Wiqu2 zyu3*AQX|=CHn^f7d;Y7ywo;dO*A1Gr`1#Ualpd&TZgzVLn6%mbz%|C_=&Z2q!<&3a z-v0B*B-#kya|z!13`V2w z>6lTZhk1x3+LhTxPY>0UqMoQjs4z%VLDXSl^0BoB{rj>LyGoW?f<5gQHJ7ilXB!t# zizmX4vFa5Qe!#hL-L5Jx*jaDGju;(oNB&-BzOttZyPh^wZ!BAa zQ>L%I>_K$KY}6i%%?AOpOo{8>d#%Coq-gqapDccmkNyMn7a@6_o5@r|RH0=yxTcpX zFQRiOEZ@L@d-3wdoiq?A)|y{LXEHQ2IpD>nEB+#CSIn<8E+xiUhiz1sbEvyYIjLj0 z^ID(|r`CZQj?;EfOQnvVswJ~gc<+FXY$~39R_iO9c%v=P!!1|!B~g-@%_)5h@-hat zieSvhlUH2#a8=!TqsEQHjP(Pe*4OXEWOk_D#k{F2EhmJnd7pnW#k61M+1#{fXm$3_ zJD7w|8dv>0_L!aqj4FH{&9kPfnIK+c&}{Dz?QarhXyz>2<=4 z%LKRchAzkr5T;v6xB>^H7v& z=1eFOCmqtIwlddx9^dXo2yqs>yOQ($&_m8S zJj>KerDA&dc4Zy!@R!1T=yBe>5kkMrn~d%U-TB9gqSS!)1?$kje3rat3gvmZ@&i7HEP4}*`yAC)WGl9T%()wJ9zsE`E%r3 zo4D`KAqkjpT}j4hN~(DuTPUT>9r2{TTJz}T7!V#G&*>%@k=u1(hMm8Q{%o@^6fo=`+OF zX>HZ-*kNup?QzJ`GmN~hyW-udb<(Qble{r4%3(Dagx57$5rQ(^?JmvPDdkn zZ@fs+Pr>KWs&0exu;8QP(#(8ZQX?T*j~0qMj0d)k)aGnE!f70`>S_X$FV!408}Sj+ z`Q9CT0_|VWT75s%BN#eXi}Efah41?%CaWgX`Gyu!JRA&wnNAiI3C? zcEXM3POObb7E^O&2h|R-cu`gvarL;jBmAY#8`=3@!=Y#C>6-GX=b*$ z(+MAIAKEJ4ko$^{x81err`XK#5z)0pTi|VS&0^@l)VMKG!~av}_S+p{ul&RN%_Df; zx$R%7AJ37mcUfMI`fM>cl+2#HF%n}EsLL&=t)W(EQ-1kbRAC~y1HP^ZUF+0_=SA<} z6RVuPm^<#y_3~QlBWwiufTgLH^+Z}EA$2VLab*9LJij-o-C_&D43@O>H$+@IX_<*Z z*+Op*OP)=6ykqAjAAYj-SI%#dzmlHoJvo>(Pkquq_+5yK!duaX%{M@ZcVJxP|+O3LS3PZ&vI-&PwRvG*Q%g8PwUmy#@KCM~IBc-{Cu!CcwDakf1pAFah>NDqr z(XB8zNPgQ^e9YZ6*11Q>VTCT)bE{m6^n^rG2jOud({YXT1pAdfg6br*(xCklA^@(k zxR>X2OY?9*r?k)}QWTzyP(+6|rYtA&?PmMe(c!(44l+nv?w>3x?|>C1qMt>}S|}~A3A$9B1Pa52@?E5S+DFg5#LB`S|>;jD{g>jmyxE6b>u}m|HF52u*1PX4& zrDL1;*s6@(jLQ0be60CTtunod?37>*{Hk?&W#IP1q@x@ z)83Q1g6+gi4!UwpM%5IReOx)Q)Ys&VnA+(=&0YBY)yJ$UwsIT0FIYch2ZrI&!<75D zb2R3NKyQFyGA4U^yu8f%2d#%!GLllUO{$QJ$MpX5Ep60k>VK9jJmvi1 z0H)^6Q0Nn}5cGks*O_?czVy4G{wHSd*i`Nk`WJZXazoj9G)NP-6L<&1jrrx-E4yE<^HnDeDNtqw z>}(?5m*)%*H_cS8(yWp#pv1}-tld(&wMeLg8qtYJfjIE}3 ziK04EY`9J;L{-{Z9S)?pu~;6OY8f=ui56ZXqB(Ap)OtmHUq_^i*y&a~LMQpFw4Yy( zTz?e~~=U--GXukY7 zd3%z^!G%(>xyZ>x$JfnQ*^st#y!3_U@D)s}9~7C7Z^%29@WiNUO20Comm?q%d~{{l z?zNZ`y@*MTr9n4IvaqKW1{y(C{;WK&Y?m9Jzmn+%<(+O9_mM_VS~w6uGDT!5*M<=! zr~L5t<@~6v!;5JCi}r$f47Bkp;{v{|zBgC6GSjXb#9O>?Go11vtJ>bHPpr?xsxHJ# zzn*@}oV#<+@R~j-&|*66rR&?1vbl3>Ee^k;P_3E?Z~WpFRX4=C)n*@|y2a-)v6WD= znG^n*6Mfz1rEJz)L{9J?_qgLbpFKI{Zz$})43(n0IzS@2X5${}s(hJw{z<#34=GcD z>8{6;ujPO`t1HdX3m36a5&YzR$9?NVtB}P>l{}2?$GFj3fh>Ezhcm=&ie8;0Q#ygR z(}nBe^DN?e9TUupNKBTki6yz!#`-PZU=?G<8#3E*rF>5{=m)mo-(S&Yy^bQv>*>LI znvvYApPw_(?qCpbgU`9g(jWVnnX&wZ0RK8Wt5L{rI!g{)8c3 z%41@;R#j-gVT(PyPM&M1lKr;ns?jg)?*Wvii3Z3jgB4-;m*Bu<___UX0SVB zUvq?n{OkZ!A!`t7zyc-2U=L{hC!K76CJpK8q1cJ1YEllsb<~x=1E7l{#vA}66akqv z{v)W3B2d2lbUgD-!U@p%k6q(*#zvXNbUrhSx-$85g)&g@d?vZ?3^<|;n7N!0!!Cdo ziX(9EjL7~&{JMA6Eh|^R93|7^3OJz%AvXYyBAWgXqV9k*ic|52p!5LjQJly>#J7FBpp!S?fP&_{0ZSBd)8|aR`VVo%_sp?ah(JxP|0`I1 z!4EK|KmAVhw^G>e_xAr*VJ{9X>Q1Y>z?G)aO$4_20jRoQ;E*3c1uXZbNdrtEu0Ozp zG9jx(*`tgLgAvfdU@CtNz+lCpH-=6np^E2pX(xl&;LlV58y$7h4_5j^hIVJqV0IA4 z7r;dsi}(IH+yW(MexiZFbj}&8SU8g=NHbbs1lz*SG}!lo-`PV8hBB1ad0u03^Zzs` z(h4@QQSH#3_B--e^=2ca6%A=|o@=KDlnOi>q(Om@m(_cxRzSKS01NC11jta~es_wb zA^#wBD5i7}#BBeLVj_Y7LNFC#pnCORi8NGogum?-hf-pIg~5<{NS+mCjs6a6bs3_cDy8#%2ZkSQgJzYq>c9SRVkTHyC0ypsOQ5 zaGFef73C5e-_rvkXe?7v|2r}KQvXkWI|5)vIY$r)nL5Zn58;FNBG26X-2dZ~IppRP zc*2|I<`tGSDRXYdXEm~w{q zpW+W5|BKT`1Ei>)?;9zrwLmkg_cD}xe9lv~y7Nl-U|jUsICu~Z88vGO_ zA^`8dNEmhKj-f$IkUO;6ogWWgbf@xis7U-i^n+GSihtLI0mk1}2kD?6sz^>ei^PLO zC=z~Lrw}u^oOrf@xRL~MGx+{;alv2Q03C>vbmseiyVieOsSy6Hq_m66(9j|Qvqs}1DvUlsV^G;2Zj8B9-Kn=b^b!X zchb<-6KdNOYN+r~*_+qRR&Mq}HyjmEhBrWgO;Jul8@_L*mAXLsN1 z&dyZS!i zhZ5=kPsB|SfFekc1BUs}Qi3)xH|Sr(2^qlrU*An&1*E_2|1nLIfW$O&=OfWJ(6OUuK5S(wrY`e$5C z`x5#K5jPe2hs2tnLVS^Z>@eWIR4HEHK);Z3U&ep)+X_vE|3W;I1^;={oyH6MA5vG8 z3i8z{qlEUKFkVItkMLI`qaYDS*98v*B=|jr0rIbpA7w}hyv^_ccboyVj~p7SqJ=|6 z|1c;qAkaD#F%F_yncuZ|hK5`}T#+*i0~!V(2$^*11R|%BzNN_-cJpPIY{2TRC^NMlOa>D*Wk4i7~m@@!gq;*B_`toOpINT zzd|?bdAZ55|7ukTbieJ>y<9f}y9Ye!Jw@l}0hVH}c3v5M{RkwV z;sOcA)W2iW4$2w7q4Fiv0`WcWklwrn<$e?e>W!H9GXcM0cSCPCKV_Nw((NGfc2Mud zJS89k9(L$}-_U+RL4W)h{-E6>cq%dfAa!+Dxz|PUf77~0h&bf)?T993)Fr!y%_rnn z^z+_N5l;rpjA`8x5+(>L3EPzm&-M47AWy`Si(j*zKyPSDl)^39lDKO|0!PtT3CWr3 zoVa%zo2PwIW+A(_dt1(iI`AI^KvnCRYCzU33<%JHdg!!uebk&!quED@ws|@g3#Y2h(T$~GqQE*|VlFU;1 zd$^E~=4DwNka}F3Jv5@!#T@AFMp5Y~me&3A6GpdA3`jz}$u2qP&bT2*-WIH41)`R} zAGTW)fF3Y8t_llfXp+2$@+p6DW=Jlpx@Q5ll-qHFiWJEx`PDf&8hrwc{3FFQLTojo z1pWP=@Fflny(RLdWkhZ?P|2v$vf5K3LI-Axp?jH=2w16bE|^tmW^OW<*tw*bwer7} zsQ7n_5lEJ*at?5=Yc;Wdiebtg3?H+^d$@!3H4gYftM8?_2fM^&TZKy>o(3j~z{LPY zngaKXEIR=FRTPO0rbJ?*?)jw$PSqRZc}qrxN(wfOw1liiZ+SKR*Gb18*?XTG62NW9=Z-Sr3>jxrs~> zwWHg1Gx}l#iiU7a5=)iRC{-uh5j=Q&q!$<+elzlh;8nD?*wvL>l=lY8sv&nCst_p> z%?f!)7s`2&GxfRFxK;R`BX<>{R&oF=|>S+-? zE((kdDh%5PqJ(swjB~B94~$JQ4YG~Ov5YhH&Ut(&8x3)h$gYLdK{S?wEK>a(7K-Vi z!d#|g88HZFZ~Yocn=(<-%K+gh7A4gzy8uMs6f?GQ$9`I+!o-g3&rr*tVDr=fJtCbJ zZ>}2+?ne49=xrv6Jj{WW4jI6^_8<53@9$I`R^*9CWSsFHKCM6(Al z7x@D)QS#b(8~JWQzvq)+bVSssoZ<16k2Tw(!SzfqlPzjZI9aTgG3iS@PF_w>kA~pq zPDF!j^D{`>MDnywnnqfR2SUjf66uQto9FP5H_E1UoSOyhiknf%t+fEjmq_L4UKN$M zk!OMNwyx?mUITp^xU|j1XGL}`oAG)l=NyBI+mwawJp1>JyF+1PBX zwXs!RU7Ayw%*XF{Y|V}gOO(>H&%m{E72dlhG)by-A0wj&$3z9SB&P1dkWTMBIVn5~ zuHk|iQ$7b`{OM)q`*pT88JDzBI$q(=U72naH-;hUFwJySTlNgtVO&1A64u=~XGF%8 zM`m&;zt?DuFTOTBJOXb`Xs(pGWz10tu3fb6GZPW>TGABMKtM(ve?x69;{JAInBST~ zBb@@BdpOmC)EPLg?7r~akZG-ylq<`nrQXOKEW=hMX%UZ>fgsZ3Tk$4N#ea! z7F?BSV>u_ZsTc#8CcpwMO`zl5k*17^xajK_U`wr~w~8NCQ;PYqr5Jekw#c>)K-s!D zH<58J=$yY7oHehC52#py#&Fm{Eu!pSrgS*70yYlN zwbd^~oF3S8=s01w{JcY0Lp=NDAw(==@H6JUMYSs1- zE@}EkY>@$C(Q{lC#@o`B%-%2RQOY{@OXCZ&AgbO!oGsd)+;2$E#SCBwl*TG_k|zhl zB8Z+y%A||aJX^=s7~z{cxI5PkozZcI7_#40`UjyRG?n7_mf5mv{VOw>vOYowuAlUB=V5;HUk596E_QsUDryg*opuHQCaB;&z|!FHf|r^n!KvPi#ZH6 zHMx5SLI+IgIr=#9Xyq3hnwxC2Hr8j?x~eNeYJQ{rDmtzw2*W#rX{q<~?w(cdS{|pE zUQ9#t-4-j5ew_V#yg26Ns>h#Of{rN)^Fc=sxoHXOaqcY+buMWD<|)_A59I3#WT(E* z#jghl$7>+*awg1DLQg~`l3M-|37Q<=fR2&mE;&{~2hYSl7v5HXG-fM^u0ln^bAdpC zQ*M}Lf$~{_vzB!37DSxZ1xMVw2%NjM`qM9K6oP(I)6H{3qls3{sOU`(zWW>}Y=@Gg z1!p9deq}Qi71K`*dIZuXDg)QgTtBFBf_x1qf*N3(9_e&$;^wrmxmiQ+3i)F%YLj?u z(2Heq(s#-E!kHOPvkY^i1dg1O*0M2JK&$MC$X;v9N3QMwl;k2XC7^r}!UjqKst55A zbN33PJ$j+63!e$WCp&~Y&wmiNJ6%Aa;EN2A%h3_$aV@$1>;XySw!jxv{v5Ml_H7vu zq4Oq>%}q3=6dJ;fCA|WxXMf83)UU58l;kcwr7s%b#^H}$iYQ6ZU z0ZtZi(Vl7T433e#pl67C5-wZicOMx*oo^6EWpz+J=wv;3CD*0$kQ=N9NI;k#2i5@@ z{pQM$*%11r22LU~N^9D$-Q5&1{hEDppq_n-RX?5r!+;oqp}zxr=BL4>N183L^QFZ2 z%x~9oKQT-uEAyEMB3qnK4-$6~a3#LJby}n6%id@49^-JHj*0;ux+^Tf^ygz!XFS0X?6Mz0mWaUO05T-rX)C_1-33PoObI_E!mNUOy*n?z@1opcFOZX?v)s zx3020%T4mhgY+>f+|KwKjK@&Grbi%|gKq$8Jf{ z5#CYU7v^W|`0`3fxxSMS|M=0)0mikWpbC*h>=YC@qk1DoiSf)gYlEv0)Sn@$kdWgf zg^~GH$QciKD?7hw0v13_FqY-7m*v%CG}fEyKvkWLD|@^fj5rqaF|w=yBQEFDw(F4* z%QQSKIc5Zd5FHshAH3R;F*OBzK@;vsT4gXPv4|;(EbZ$2Mr^{m^EOAX9tWn{BbP}Y zK3vs?Y7nWGY4Er~9#?f)S9@92wRuE0vN=Nw+_yDgF}s@DL&$Y}PTzJn|Fj73hkCK? z{s1OQAGvR)541f1cX~bn$ZsYWb>KJOd73TYy{Yn_Xn;juFUALQk*<^O*h1SSU!y}m zinn06<4KX68x%5(BN+W~OUJSfVMx*fZYPesqo{5$GIr(a1(eooopbaGCdw$COfPGx zB%$81D0ht7cTdT>KHhzTx=%xcb@Yda_m@DE5cH?Rts-vsMtisd+%CvDDQiZsoTCYx zIRf6*Qv}?|0$#tMJ~%EvI&33et50}F1dV&TQHtGZtt9lHS#c!W8khviRXm?`v>Tmo z2yq{ge>9!cv4_FKhq0Ctw<+jiWHrB6iBDl)& zr&y;d=NS$@YyaNEdyu^nR-we6-O^4;<5l5WSLdu6ZQYK>M^e7GQ_yqrd;uBCn z|J`A01~~uSE-!}E!M<*e;^WnDU&8Zp+doy)8u>qD=V<94(ti5-PiVc^{|YbE=RQ&W zyH!rUsK9;QD&d@3(Q5w&PL^^0yHyGUpxtp6kUoZz{}e79*5JbJN)rC4#p99`Clqi+ z_9j*ECTUFYBcjn6!|_7VKwee(-OykmMYk#lTV#`q>RC)fBPN-YnwY4|e^Y8{b1XNipwu=sa)!Xjl9)k$^)`He7k@hH=w&vrW~g$uV`@RZu&t-X`h?)?;qn3HJe_G&K&^tj6l9&$|q{kefd*A%iA#$YB<`#60h|W9DRI+Y07ha^PtSJN zZ%1kRYK+ALZk858yhlHH7HbTU7hMP9iFbN7*H?bR50 zm}@K484WZwdl~PPcB
    Z7yQs+r?PFarXgl^%zb22stRDoh&#I9dm?{E$^KoES}D zrEg|t44HmripH?^VosgZH!N@3(wG?0UMrdA1Zd@CZ>9{{d>XH>0^)Dj+f7MONOPCs zr*{jA4JM(wP9(?ok#ug8}$6uR8l-B{A~!d$^BuZr8Hd$IzEdCmYEGI>m zDu!I~>TT1Y_tMU?DaTTh8^gGG6D(HgnWfI{A2bpvb&VlvFP7Q(1?HxzaC`8cG=^4q zZ%&!Bn9?Rv>fxz74xoozWRoz&NVUyQzL-Og)4beIFQyjp(!p?7&|NFKH3Q+pcmiK? z`KjkfbhtV;ki31x6pWmnB;mAfOSv-jX{1T+Ga}7dY{yJemy|Ge<#>|(`za58<;mN)V^v|ubC!P=TYce#zpdQ#q`7Xtq%)r(SA^`2PzZ>kMYM1Zn*Y7#X z?4Ek<@5@ae>0;{oV)Np`tq@q6>n(%TGR%5Kk}&1UBCyJwLFKKduB#$C zVen_gE969~Cje#6sIiVa7p=tgEWIUTcNLhK6aPv16KT!}B()Q3O>uKiBo+Q55$EV! zClyU;7XL{l3uqcrZFN-XImzNb*W!b-?)t%5b=Iz2Q-S5sjE#kJtL7;Nn#i@ZUGi1A zoJpq^Y2N1YWk^FRnz9C>zJfOOrU*`!r49b7Fr0ctDu9M`iKr4SaDj6)Iw~1eT(AU z*7lWsGpUgoDcuGRSzJ^MlhHJdw8=U?RHjWo%qq&n@7M1!rLiu;lP?#eHw|y7%7>|{ zvZF!*>-KGXW4+EJ=-AHOmEneo3`8!*rH4P}69E%lB>tcqqqA8}G~%xF=!NT0Y@$M$ z7DDUg!E850YuT|Uy-AjD);r^lmC3bp1MsL&_1#iZs0We*QmtmMgBFnKZ7VJZP99;C zBT>&Fn-OCQCLU!(8>La04l6&JADZqPm~u%2cFb=z`-(tQ=h9d%Ksw%~;H;JnCTUaP zasXK-OPJM}HzyHmWH$EpCFoPf@g}sJtuEB81yp2yct=Wou(u8JZ71LfhUEvN7Sf~* zt&3x6`1YW!KJ4e(njqL2&%FfK)#h?!c_gOd@Vb_H8RUG!4blzP5uB64HYrBu*z{J#2tCSB zlo&9WIG_1tY9ZI#h2}do7_gnxng?Jq5}}n~?*M0Y>dqg*J#7L2GiKpZCf3zbu$!|h z>b{RgqNnh|w&wLx>@Fx$@btjT8A-6{??xQd$4{b?X;Jn`Rk>*73ICwhAIFux$T7D* zteavH0mdzn-jK}|F zL~UD&I0-%3h>@N$@b>1I)T58C0$KYAg&xSQk%?RerNSMllNU~1PP{3^-nc7}xv1w0 zmKW;=SCW$VYYXM}!NfsS7!^=Y0k3j1#uQ*Cv#MbtK78VJ5j#9VBj)!@YWbk3V}?^X z55HpC&&6Drsypg?BvIvW7xl0;`=6c9y2;VEE?lnHrXe-hTplFqMGgq zr$Z*LEw)1k%L4*`cF;Q#O~yP1a2&wTJg*(OrcH)wCHYY>-IFhn4KV@69tc(|Je4dV zeK?1hCJwHMSJruJO*lg}q4r=$#tyVG7$~*_PGSmkTj~J5Nv=Rr4Qih7)sKSGGrq`| zKK)2+e8CF?W>IeVo&!ef9coqF0ZCtgZ=ruw<@ZPfmL;iQaG&6Tpe+*7v?SbtEJ#Sm zM(R{U7$V+|xN&>Mp?rY-seF$7;k7n~N;A-(@`Pr&V$s&1INvSB;VCqzanN#vdMHxvkMU%;yl#i_D5(?vG`+QM`uVWT|484! zA824X$XIDM&AM&aKu>sqdm1HffMPpny52LrIiTSbdUpUlnMwwjk-@hvcs?$oyjNgp zQX=l74vRIs_gfhreCKNvNrL~Tpr%HCaH;G!F#Sy*PRV7lHJyX z`dI-k3SN!m9`sv*>f)^NH#9=P>5J^P%DC`PHQ4iJksKjN#2kU`Kb+DT0sN|-PR!dE2hi#vg6&U&|2aE1%b)=NJ3KdsqXBz-ot~Q^uz~v^ zzRtfvG?LxSXn$XqSiW9W{(Blj1JD{-8#p<2YCwByt1R#x$(n9vjKIp2*o?}X&w-~G zON1l~S}4LuWXzIENX-q@B&F<=rnxernEtQ<9sWTcFKcpn|G^v<`&C zhN_@|%7*&W_w(+rEs`_|q4KR?6Fx_qUYD&$+3%ar53jHIr1{UMsC=DbfU7fSLjEPD z6K6TUw4@v!?Qi;YuZ1%p&Uk0O%)U{ez?A!fS@6&3C*G;vL*h!6cgsMK9+{JSF4@~h z$l2s$h$#m|SuxWd@snbw-id<=EZNAp1ue=)Ww%-A+b7&Y=>gh%-**Lk9fb5bx2jJ|fp)+s)dyHf6{2UBN6(Y5}?^qlXxdXtFS(SGb zoJ+^r6*uufbO&ufbbCsm9hqv!9g2G@oJ+5*KXHPN7C3*+STtvzFrWNNDwarhnXOFG zVKwhi*h4(=4z6)G^}~Lq&YD{ENSHi2wmx0Yce3)sZjr&3I(h(d1K5`^e^VAdnh>;# zsEi&hYcIr^{v}$U!=`jk=;Jv;xz+@xk8ubrTKb;}6T?%fB~e@#gVkVX?QN zu#06A&2}x5zMZ(t-HmY_@8Tr1UlTd52>}+52|Zd=Q8fZfD)Cd&oX_9jtN6-C4w-GC>|XeRe)jE3i5z>4ezF0~Izp@$0i1RKv+Yod(XXSi)>53Jpv7d#P#=EIg1=gj6NdC^BGd1ced`X=BRX^OO zeAN^*sUWrf_zhJnO9}2|#va#v&qeY@{?x^XOY!`aFf~RC*CEN#9-CnPJmU34Nnu00 z-+#8LrD4UKE&;0xG#LVUxfNuj$m^y*_{;RA3SD4%{YRBENdsQQ8Nr=@4dO zKT%q7DcNG>p)@r$Yi^W@rE>xVVsAyC@d` z(Pg$2p+(0DF4b&iPTgR2S)1$5y$OIiaOMBZA4DYjL0tDeniGA?n6#;w=4017LJv9Q zTFd4q!CMCl1Ji0oBne;a4x&!reQ-c){7pnwYYkBogqNIL*hp(l@;R#(!$6{!6o)iP zP%iSNvyE4 z&fo*WiYtY&Eo8Hy*r{?}&7^*f#NV^KY>Uf2>TnsROmsTP=Km~qm&;$!L`R|`$k16I zN3xD`!+tZ_1kWoxf@@3b&c9KE_f2)P>KNMY?2y}ghX6zoFgz*vj&Bb=LkHLvW~BZ= zP<6GDBUv>i4IE7n|3UNPP_t^31Ubv(dg&QJfhm7-%RVbrwlTgQn#fs?YS36hr3!OE z<|(zdmuhX-TvIde5Lrn59^A9FUy=qjGPZ4Z!$iNunH%AN?W{moOl$+L z?hu(j@_P7o32@&0ndv0&kUufgmbP=Q$DlY_gT!%*e<~7Ub4*p~;_NXDpO?Ia_weoI z5X<1a!TPRj>)rlJMUR|?hVxAVz$-9oe(N6WW_V)xpkZ!=qF_=@4=>DLl$e9_)giHw z){1nA=w)nF-`m1^^YT>PQuyJd>sB85eGAiAd@;XLDX8IMgYbmI;&%uxX|^({`3 zx#!}=$UIf?zXpoqU7~#$Xs|D4vHT3&T&*gB&ca9)n9@+|X=`+}7??%bsDcvM*ulMW z2f{_E5`Lcvf3yD+baOM!KYz}6aGgYN1vMjEUy<3yJDt#=orC0$FN8);_cr4iU0CcL zJZp@j#~J#9m)p>^l&d#W5j+ccmT(Wc#kuEvpFDtcZ+k~$_7!**&^O}r;_~CU%jc;d zomlaOtTEHAGg~aKKPi)C{nI46=Loo2Xwg{M3aeQr>iyZtJSZiy7YtJ>D1#MDsTg8m z8JQQppbj?iQQC$pC^$H$JT1RB@1A<+Cv9E|T@KdRnD?W+4;~GfZEg`T)YQMbW*E#V z$h|Q~&8QaTAS9zG)+=JUcZ2F|;mW37HtDYA7%7LRQu!n1W;STRGCWUmL0LwIY2KLy z^sp+Lb)Lb}v=$pNaMMu5v{a*fUA|r=sF)I-MJx#3r81q%k7+-H!I$M%>Icz&v{Xoj zz>3kZKyBnn;EYaY+UOl1SRq8%J?h;d)(jsG%NcLH52Bb|yPfx%#TE#Og3LbXl0f}u zhh6dR@ATuv&wyS=N6Ukr<%#nJRE{a+kjgR&lZ2jADPd^1W1o&*4&3c~1^BA=glO+!q%CG!tZ<9b+|9Z!_1B zTanF7F8j&e9~YuD@OBY?(nE)>a0kRhOD~1oGGSQk;Tc?x#Wpa8IN6LAKBjJL;e^LO zv}P(U{P}dO5om78A$QE+i21s*!?}c@1WkmHz(JR?;7uDKZJD^PvaD*PMzIbf@6X4^ ztHn_pbDNqj=l}cxuvihjB8^RGz_N0Q(OS7pl+p;(PT&xi$%>7b9U_mGk{_W0Bs8Mc z5ML+3jGXWs1!;>wiw8y zIWLK*UBj>}CLX;Nc0Yj3(S>Y-{3DgebnlLVCBHt~YC4 z*Th>aUSn&a%jI5c#_&GnOxr##=FJQKSzXvL7KpIP4^HAxA#X1_%gGF58Ct`TmG91X zK%+eL{_}(l#A2n&?kFlLGg1|8;h9~N)L8hr0lAQ&-HR~AUDjUa5w&8lAZA!P6^*69 z9apnr^CR9B5IJ1Zr(F>aNj1cfNGglRZtM4OF3>uLi0x?IFykwPRW8a!F}8z8^cY-l z6K(I)A+#B`_t5P9lv3!D753B=i5AVS7H$@?Lab)Hs$SoObaNe0oZu3It>HMP4bt{H z++H!3H-o~{g6VvN6?`b%Ec!eRfIwntz#if_Q5W{4=wP-5ZBp;p zg+BGVJykdvk@E@`CAB!}goNWoD$l7#rCedc@jWlj*TMC4KjC(aC2ZRyNNetu)TwJ8 z-z)Y&t8()rsA3uz((2SI{cO?JveKf|s+aUdA6FlZwVW4>7w^iYBB1;-Ub}ID}hBISS?(26uKrkOy%({Ej8LnW;vWta)K7hnjcPk88~L zTlDPwQHv*DddtFxUUlr;Fr8W7$xPWAIp@?b{Z}+WV&5DW5eF^y!d1h=S~)cxKjq+v zBqaUxCQ7|s>JAHZ1fYu1vIKAAN-pBbETBZT!-=U?hMQcP}AWy0!V!{kz2x+3O%5 zN6L-yw}FRsz2EzjjJT!T=AEp!-d7NrktL_Vgbn^Lj`5dcsg>@Y}<6?bnn8- zP*0{WB&%|HX7M4YT}oqaNRSJ*w~3IjfQMK&jOQ2eGvdyew}X0u;@>Gh3y;wo1St8v z;GQjh$mBCp7HK$BiAv%MI^+1H4+k3y4R)`cAeeD&X~W1f#0^z0%LPrek3gZD*tn|O zOB0|y&MfQK#0(}u4ndMF*OU4%*Tx{&Q6mQXi|K>es|a}5D_#$upHN=;a+a?=0-$7U zACy^HFalxH(>sam=4{gy4`84Di+tD*)F|lNV~SNQXqh`D-SV(hd@(rND8J1<%rZn8 zO~9VeDx=@FPw&hB4jK&c29KE@HL{kYA{)!L_KQS8ho$g`ErnQK9FNO;QmZ zJ}%&Z4VMgmW7`n$r0)p0M;6~+29z@p03)UR%;BZeO!6Gz99D3Pw@Hlr! z=hUR2lgH0VLP}?nhu|AIIc*CXd~{Du^v*c$ed^gOv1r`u7 z*^O-qk(Jd_^-NJR#<)SsqqQn6l&l~}T)fOD^=V*Mhm0v()=J3u#Z4HG0vINmqjQ=Ab#^Hbd!~diu;^xy=ULEn@X_ z<(!T!X0?DcUr1?@r73u}0MKX<&;fe-ZmF6wdva>aDa6BXR{rjs{KA`6wV5lE4U~02 zk&iCNU^6JO%8fF7D_!I&2mc~f)>j|N$#X`ryCSZ|ok4CHlIKPN#!J(NvX1d{t??eK zEo0E{lYB$yF&>z9uHLQ4tj8yjm#ThyCJVgelG;6H*5~0IG;QTQ4Y1PA;MF}oOM0rg zDu5U(s#Y;};^gPtrjaF=1G!()KHqu|&JUbf;E!KO7axogSrl?CrT#NF6(P&dbv6Zw z5qwod?<2Xm=`IvnjUEfF- z9NFu)$VbpmS8M5u0bBYKF}}wwKKyKyntcTU2Bl-#YQC_#nz$l@Rf~I7vmfp`zToy|hbm zO%8rym3W@KYi?N^U)zR^nIts!utnK~pbF`8O{Sy=53jJX2T**j9WM%xX1wQ#x9-yO z2NXKEV?~`8(n`pFkC0jqebM$so`6N|1g&RI2NCfEKa7y5Q$HI;=;b&PkGa|*Hzo&q zDp+Uw>^>w?Y)MkG2<)6N>^$3FSRciF2CGN$O%^4w=+86 zponAm@eZMsRY3L}g4gwI#XXIBJY=FS@FD&=L$YqJ9q6t9>D@iv+GKS9WBWbz`59Ka z^j5)B8mGooH@3J`fVsW3oT2@4%D0MfHJK9dq+@rW-fU+5TW%h>WQsI$yqqkRX zgu2u@3V77GK*D|aQ!9vgR)n?!#N`*mlbehHjXKm zfL*{0LMUgo!pz+2b1z?@m-CiMMr74Fv+l}(19qWCm2whGut(=idG-^q8$bC;ubBl4 zS-%yO5Xy}U6|-acl7?tp)+F9kBfK>FVE)YP!*qstADl#0@_SFuqB!gcylh|^7$IK< zzz#!>FP*r_YO9o9ozV9$)uP!NaM?C#EoGn0V_)&#hUp}p7tH&f3bd@Qiml<7TtuPC z0+50X*J$#yF@|aKJCHHGD!h}3a=^Op9%i5MrKPK9jSVM3I)$gotDVcLK^UES{CndC z%?|q*mB^Vy>Laq+toRLzOv))ID9RufbvDo2#xhjgfV0UY$&pFHgrPB8uT{_8XAo{7 z=v+tZU|RpM3%HV62gZiKi}w)~ZnTQG04fZaaK24)#!2o$8K%rGeN)8$sl^SE#1jql z4Ca5H0Oho`4a74ja?gy>$0riE0ab&;FNEBQh_@ zf!xm8N9+_+Vt#?0y$3eN=+4T>g5e*GZN5-`$9$8xee`11<&>q2iI2nBcrO6)dZBqc z0@c6RhV6KVWqvsWh8j)K0FBP#zF~`f;d<};vnoaZ9opRUrMx+X)*$mn(I1F6|F{RD ztd8no+9Vs&raLW^zXnR`5%O%(NKzOR9f$^|F*2rTT;leEs9>f;8HMzVjaM zOH0IBjKjp_IMKr#*>=ja_2-4*pA!45CmTytB=&R?ENeq}B+k8%KqekIR0tMCBpePz!#H;V!1{nKhm0(X9;%{Swy0<(a8 zrSfB~xDvwQ0|7Z`r}VIbkfmG(gCI61ssj_^|CcV&-0TX>{jc+bJFxpd$-xUa7wJoj z8U<_z{v`#+1G~ZgmxdH}5VZ#bNj|AzbPX*2B=aNj@P{S9!~KUe+kf&2c| z5A--{cAg;u0j-dyD1dRT@9k7!#PB9L?t}G@-z;38$H5xO`@k3u$ELl7kOT z;+qP=M@Pmb9Me4*oQjzTAbF7nFv4Ldp+eEXf;3C$1onyi$#ZvMl~K3Ce^uz(U3Jbk zPNqpPrX@JfzWOxlIZbZ9p8r|4dbhf0eB{1+nGpk`F5!D|02A|J2NWAZ>D}xYpv7!g z_;QE;DszX68QX(KYriQB|FuWq;Vm>U=Po+Hc2gN{x3hNZE;~T#u8U76{^-Hi6>vrQ zq|2e(|9Ef%nk43X+!qtL6r<7+nBwykhRBDOo>S%y&Zl+_i{^Awj@X^}`1JxGw`k|{ zl;!M8m&NyVEk*#)MS=e;+@2ErNO3%Vsz3zX?eHnT;eE52>p=LX^cuB zd`odVm6qUC;l;;BiXq7GS7s{6f?|hWWuD}J= zr-BRrW_l~moRy;7&lzgw-x^r};kStwp*1LiRVf}DrcLK65?4LUU=vi)lt`Ip3Ch4! zsg8w*%7;^?->NpcP>O?U7VQb#GTK)e&3MSrU_|f&;(a zS(SZ-{S(z3>Z8YfUY#H;Bp3?T8UdOInI#_7b6y+({yHlpRGg$E&|$}h0-3RQW1LDc zCNxWL)MirHFZuwZzRzE?CYfIQQ?wQd1^G05mkPoVI|r`mj(U-%9C0XNr3tQCB4IJ0^z~&r@B(Hk76toLWq&9g@~=4;YfjjzIl!!n%-4P($Ln~!5Sx^mq@DBCYNl9$c5H5(TbOk%rtdF)2sd7pBdr zk!<&K0USDyuw#*t%S|5j+t~cVUZRGMw|uqDeqs5v35<s7XZQoOIsPZ#Zr&L+RL#BuS`$m5x=8eJyQq4c6amc~6m z5_R^)8y;MRLQ{c6fhn|s&_&BICr9<*RBY8u)~8`_0v!@)l@N4hLcT z-ih(>vN6ncNtAVDgmJlG6?UTnSA7kid8j;OLZQ)*2${6S8o?~=&YzyBtK~DStJTv7 z*negbqFiUuuyrKVC>o>+DkPRlV+3IUK`e}=dU?Q65F`7{6*IQjv*dybX-ISAvd}@* zGvpGHp^{ogO!)gr>7M=TMcuRpLQ9qnDZyBr`MIL6$DFd;*qw2k{hp)hF0MJZ z;=yyr6Zhyr)l0`!_pH4qdl#L&Hln@U@JPa~$SZ5|RWoZRcrcwx1#UC&vmR`K`tnUO z0^7u&kKc}JVYKA$mTA<(PQ!wr6I49uY{)88Q3VD!W8-4=WjK;X2DSAHGVF6iuL^ML zy2>uGH*7lV@)HHGmX1O11)@U?O&W%(XpQ`S+K@zDhf?dR<>2p)_N}FiuigrEL_fGU z+Gwsvyt>=jTh^Ysck_(S+ASUf`udfbMB~~t?HLOW2 zoT)a7XUL8dtT+GMv?SHB|7pNl)aTAj*A>kv&s{6=I*EpPX%Kar)q&ZlnlU@ZkK&bj zE%Byj7H+pmSBIwp!jm`-8l6xUk>kPC%CVPwq? z7S9-P=1(g`MbD_Ada4(Q9am`lYCjxLM3ckrF<+f+jf3I*V^%(Kf?^ttT~CoVA3_Q% zam`acy~OMpX`3>+r=1$gXw={?2y!g((k3bw)?8eddg#uaM<&uCx|Lgk<|$fWH(QVG z1N&H8+odBg6$YH6b_^Z`=n~pmIQF{#UcktwVtMa_yH6=uK(I|HHhs!leaur4;A7=6 zHhUa$Pwaa1R62&^h@2qEw|GqInO5UoPRf(gf|9;LWa>0Fmc~uP*N2=e-CDUnYDF1D z+fHMLv)#|VYPXZ?OkZL@|<^vd{+QsN(XSf<8rp%84<@mH4pmkW)VYUQ&#*f-$ z4x-PeYKte)d!JEq?Z!zCod$=K6kRKh=OH6imBK4vb#(R(f6!eG`d5NCa%pXZ>$_3* z^tsMqI}6^yU>KAnGpzHJh4ii6i?1#^=GRO8T*dvswrcgbFSi;&wKqxFNA{cnLpVJL z$&!dbO=;Jn0?eU66Pu!-@3qg6aCMtgM5-=cIdbh|nwTv#Ne`+WUe+K^w!V}#SnWX! zRT@n~W+&=kjANS2BiBa)uh=B=>^326I+`9uj82+6Z}~=GIf#tTmg(Ww#G6_gF&o_M zOqxY2i`3sCyti6XWHhZtb!LL|FuW$^_=--p`?ALG1AJHPtbVv2FOLl|@_J!%5$o^l)JX^t~| zRBP8N03^h8n1)Ur?=JZ=8A8v}y6QY0&yS#AdY;tDFu%1{1h0|g1@4c_9A~}+#h&G} zn=77_;x$PExLf07$RSdl%gGf-px0MQ&LiZwQcBkQ3^Vgx(Wi7~IU&nfPzZ=K=WNL_ zRw+~Y^>KA>{a9Q2D%*iic==D z170(-3(8W({ckWH9(anp)(3(vIW$Dmlw!&1Fm9{?YpcD%_R(v+jEqY8?5|WK?4&TJ zZI||4)Se>w4lsS=A5xO z5))vAnBOW@&-aa%d=wy?SuJQKR@6T@lI)TibId@ijqPNr#j#3eSuXLdptJr-uBJi;tojPa}@*|=xgHl*V zayvda8v}?E7WeYM)gJ9=bR%A#0nMWs0wO|b9j3i)P2GsJ!tegaD4fCP#5hFQ!nd&CKj&13obiAMgcTw%6e%D{jr~ zeIZzW!q#QMwkGs+KqRfqLmaMIpVnC`o7NL-LUdo3^Z5q_qWNYwlYhgdvQ7S^N00c9 zIBFbIbvXOG4lWszwk~8P+^!eJ9R_y0Pw(1|+Q2JZr`Z6V@63Za6pLFks0dIx$k=r2 zc&QVJP$|r(%HTq5``eP+k7fJ_`LF?9t^k_Okmn=P^bIYAaVhGFj8zFs)B5w=2yx^0 z-Q04YuNP3sp5}Sb=$qbedcdE$L^FH5n55cDu^Ula3hEo$3CY<-b;n9ZtfQSd8;Yh7?Q3iq6i z*iS<6s}hvTP_uJt{Z4`@=HcE1YPt4j;^n9SOMFFYt4@`HTV>aE> zR&s?df|`%zzGHHF#^fcq`1BDun@qoxb0XxG^k@+*Q%r=+u0+L z>W#bi(8Wsm#-0JV@PRxdvC5pVJ6hWy-ooEz5c{N4F0HdOWpcuZNxb$l#XFh|CqAY! z2q4dXzfRob%hyb^nJ%pA{lB`n?m(*EH_ov~xc0U8D#_lvvS%SXiR_(~TuSy{$0|Yy z5i*j!cVoBVIx&Ujsm6Itx=0nO7cxv95xQ#@uQUcU6KjTFN(h`a@3DqY91V)i8%(x*qcamh z0Jp21T+4xKOeJsu@~YN3mr8g!?NsUZ)6a z1zUT$UnL&q>iSga$qRn}pkA13==qJXkTP4a!m`)qS0no0lNjV7!go?Y^yGX3V@M0l z3J(n}jvC~r02B~@WaIE-*~x8{{8ka@6&pV@N1T)x?m!cVQ!}d=EclLtYMO|&%Dvy4 ztD0Xlu}5-Ml!iYI=U)6IY04eM0Vt}is;B2Y*oa%NS4s=^-_T6H^i68#_s{&yjhT0Q z{l|Ot8xP+TOZy*O3ec|-2xTTDpoYyqrffn#Vz?l$#Dn%2p`{LcrHtdEeHB}uM5LUh z!zEOfC}JXVi%B{<)LX=9ImQ@MN~ulYBTbW|->+haEw=1%?@x*Dhiu-ltDvpYLe(FuQ-JP`OyH+<`g)0ogzWrdQ6C?|l zDYz>n^Bz(BMoB1QKKxdTdUrPloc-0L^;G63Zp+fLw5 zX4uADeiS0rZC%NxxaNWJB|#)A&0=TaCbeo0`ml~XU8`H;)1gQUBTL(0C~->gQfEa@ zP8#n94b5;E;{CM{#WClLVM9+M#5Lv9op{t)I@z9=H7}u{y49$w2KSNdEBS za4}Zkt${Xen1P2|Uc~eJ>xo}4#y<5Vc7S{|lvtvr;D}|ubU!_)agmWco~4tho#weo;-=PN zM7&x{<5oe?@f<(iarpM6{MT@;8W9`OGtxH$ zmQuaqsaW}DU^8epxmVxV4eCsga`O=nm23U_qxk*X^7<7n_tn0y?$OOlac&lMAEuVs zMor_eYUeEY#7Dog2R$yHOtb4|^;89g%eB_;8zG8#cxG$UKBxEHk5ct3mFJPT-(J2C zcX`Trgnota;vqZIw&`1JR)tvOX%lKQywP>O-j`YAEl*6 zL|((Xl|+^x!TbJ$@1TqqN(V4&1Xq>fypot8^(jLMk75-7Ge7g`@b+qClPi@3)Y%o_ z3`4#g(O^aZA$z$em^P@3pUImqu@XG?R#}2}f`@@&Nx0NSG>QmcJ@Hnk9zpT-J5WHT zjJHxGDp~TaBU5t<>2Bz5uarh_?%i>Fa=o#5+h^y#vpvf?qs znBnVU0PTKa zW3Bj*(S#)~wJLNO)A0Tfe3Y!#-o0j@?t{i0Y48Xp3|>E@G+-_f*z1>-)F2p2V*2J) zxo@s@i&_<#^iBg z@f-@88XgQ}vLAldJ=FiLWnoE_j^8z0EfIdSMD!?$=#gU(oXbp@yv(;J<3ggO$7XOFVd!?5Q4LiA- zBrZ7t_i(-Q0@5+1iWC9>DOHY+r=K-QEQjGQ+2T3Ni7mV4EO7VBxVJ$lH&pV>TX7W00Z@A z>EqoyK@GT2DKtvtJ1{OVCJw*{$yossgqI)f82$0H@$MBh2LhL`+N5L|H=ijj2))pE zas5u6-jx%#Mq;Fz`J&?C(5mKJ$y6?SZ6l*Ex$C(P^3zG0q8F2gRge#H^?Od-7Y=7s zl8fiNS4I$;8|!~M_C#klR=@ue+{2jQ#a;5|ND1Y~yN2sSvfNl0WK~ zZqREX*kXZItJGjJAGy5?r83AO;@eVadPU(aZpJ)MR2N^Gcdbsfg(yg@x#;31O{K4U#QQ^gHU6+08+`S^T#PqDjYobfx&@ zl&F{0taJGpR5fR-#9$g`S+1oZx7nw}*OV@MMan zc^$f7+YcRU+e7J}Wo@aWxKq#Mx*~bzSt%oIqu~`vDGZr|k^E`|S_MB^A`@p2I!B*r z-lewUE(ha{!G{eMD)({FJN*J#9m2U?l}3`{;u@R~4R=W?IqqB=8!NCAQ_ZyK9E}~Z zq9I}(Om4KGm%6JeRq_Pu<`R&lWkggNQ1$cWm0P1fbBi8{aU^D0+IC>B;qjxVW8?`x zk8Ed!_3L1FcHUo5^9^~&^X0X6E+VttWIV##VEBRjSenu1On*fExE1v1w&H3EcgS!4 z5%g-(-tCS|LclOyZ)5`ZJA;8x21*_Gs6++ZQNRx0m^CYH$?kH2i?Fw7S_Euja*Noe zs@1-`vU~Yj++>d2zIa=4wsIRKevS*FVz*zWE8r1Q3Vs$3_w_Ugl}ThI6`7an`4+iX`3+j=WJ5qWgH#H^N%1 zkfP#dNN!IJ)n3;`6lZviRtF)%`fcP0;Q31Wk;yFGYL{HmI|)P8;S}5J*?r+}QUnHW zn{_B{>cIAT_o*3Tw_N=c+BmKJ@G~j5;BexF3O4IZTZt+~v;)0+!-UgGR{JIt1NzAe zJ9=WAiv;`Gp?ux@0u3y4kJN8U@b)yTY@lIaEstjx1)5^zDE|`*zCMo zJl)wP${g-wJ1R7aDZ1yy;?K88yw94n?@I)i5Czq%a?z~1{-}@|ELjs-sp;$KEL+X| z=Aufm)ctL1sG=ef_jn57>NMx#JlpJq-ZT6{J2b8^Yoaq|Re&<$<@E{a*`1zR=8YRX z$6=qUr)&dST3@{5*eXFPb|u~VSOh*?=`O8< zjEKw24DT?KX}oEj9M#Y#9C}Zbsv}AUwtlASO7D`@$o;sU2YE94tmm>qaKs+ zEN%TFyu^qv<6QF=^a5y$o#+>R*-BV0sJBL0-5 z7&y@Sn;6^q3nP4;jzJ2cyk(Au$sd3D*fKzsA!qP~)Q1LjD=Tu5OFdQ1)UrjX>)Wy9^_pq(XCdxZ~G^pvJjUC;XBuJ{m9Yk&2|z!O={=_bstBPgP%Ptm2^?Ka3FV zTnv`f{=raWthWIUWsXnFA zW4wvv`O!#j!E7FMN_qmpG@(hc#Ys6Vb69^}n0JuwWw{%dWFLP<$UKg{zuH$N9`7Cz zm;0Ih-Ul=5p!;JbS^}35gx37q_7evp2|VD6W7q83omz%mlYFK%L-UQ?J{I^}70@o5 zKISENRJcce^RAwFDox9_M-BPALG0M8e2sg9g()8%Y_j>9$vbyyKVmsrFdSP#fOO3CVC5UVFeK6&c0i8&*p^PQo5YS(0x`yrxN( z)%jFmjZh@BxjoB%X>r9BPu#7Suu3MNR@{VVu-%6CN=R?G3(ZgW2{X(ocuFTx_5+0n zD!p`mLK2A|k_Vn6WR%iiWfwZ}sTrxy7l=pYd6rcLm8RRbi@zaX91K$$i1RY4 z?;&)JGxn^N;dXsE9F9M}t?KqmAOFEctcItawO_is$oRM0HONDaxyZd@MQbvBnTPjO z>6o-auU0;hg1J1De7gHd=zxFjz=`HDHA~E!MI$K+oNh1EA4cq<77AAuIIg*d`zL-$ zxEV5hb39MVBr1DHjA`L@=(npa1P(ADWctUK1fpTl67S`&L;Vk(NnSl z>*w{BF^e^-C+B-W{`cStnb{M334mi=KvwgV7BHfC2~`c91!FG(!k|h5z*r>!RGeh- zREYr6Cy6t=;z0XJt3eu&IYE!*ft8cQnboVnqmx7&CsiO069&1^=ua{;6o3p9K?PH; zL-k?#s%5nReN-vZ>XaLRCMsbFTr~rDkohpjG5+u8xAf9RFihQ zT?-wA-viVT&G;=$)M#_><|GeayJy>LU+}zjrLa}ih41!A+by1BkHP72uRnzm7bx#n zVLvmc{%MWDK1Pl4qh0HVjcQ~%0JPw&?MdBRkjoCZ zg%Uru1N2diXnP3Xvjb2ib-_|c=t{>PkoZ4lfNG$`7!Hsa84b)CA{jdX@~A7wfdz_$ z;RxvbWxF|^vLRhP6ww8##^MBsqDUrAz)h66!U-@&Ng%T(|0n9AB&gWtoKIt8bOCPs z700@qicx02x}2ItUCFwhUZD&mx}GveTmgHO19>;7fm{@&Zh$4KLE(Pdc<}$my!&al zu6Y2rQCbBafCGw5bss>W8ma$p(0T%nC`kmefgGdXo`5Y%a?cBDASEMSr%vU)Ps1qn zh9(0E;rjshP-G8e11XvDIkltd3)rC~?|gwfC=SzuQ}t+M1KEqi2d9DA`vDH9{~Z66 zr(N;`Oc)`P8gD5Fzb-;$bt|F2EK>heVa^T225&zE)KFeZ9s;z0VrPOpz?#6=MFryC z2MC#v+KS5X4i!Q)G!jNMG}Uw3&w4?A|I>~{`a|kk6G(vq_{|@nM0t+%JnFB7iUx1c zqoLh6r%MPjA%Ht568D-vv`V3bLnsOFJV_LM4v|n%Jo&9AQwfnmAkxKimSA5|F|U2a z2ektLRP80uJOGM@cnt}FnE?PJYOtD-!ri_93-J=B_whkOPpF&7-3p+Q>iq?UdKU^+ z{+t801Efm)ZzizU?{rN5mTUU&i27lJLorA;3|tHZC{aUu7zjBIft>-dWay`kk>jfX z6!OqPyO}0*#;N4D6>sCjpO=P6G&>;u5olk~FJ{{;MQM{g09g4Wwio zcB=IEBm4hGjm>zbL;#Vr-qSL&NEK*i`=K$AK4;J7+8Lh|{H+8Hn)8!W zd;U)#dxXj-c*7Hb8D$DeI+y(nE%YQPJdtyz`fi+2aY3G70FIjTze%cSXcFi6nEEGt zWZEj26nr|7CBcwa8;>)H9p%;R*KZXHr~|H02b9lQ$@fH26Bw~cKrtV5D)3>*Y5b8P zkR=wMzwpNpXhVmVLvkntw1=O#kPC%&Ge`*fBBi3SXHpp08w#+Y`VjS)QfdL}FC1Dv z=Woxl^z(coP$%rPBX(hsz2BK9ctAKn0XBvKWT@U0%^b`QLA}w1R@eFIsLK9_&l3)i zp+b`jhfFQJID@!Q(juhPwCGGqksJX~pzLoMtEksPH*qJF11ouMwD3#M=;UB#89Fu? z4lQ?-xxfg>9DU^(4g*W8(Q(14h*NwOiT5?0;RK*iV@)Z7KY0W+td^AqX9}(=+uczX_e3@4nz0< z`6&99Pn2$Npi_XnF%TaawbK-ujSV!bCXmi~{_OWNKI6$&f#j1qM_w0%_&U&BL%!*-4AH5Iol!sraAxjR@^&2;Sa_FRH(7|3R%GrApsKRFaoEqaR4nda&P?m?h_47{M=x>kfQi!M{i(i8~|rVhW__(?!TTd(Vf?Uj_Xc-rv4t( z{dXS-1c%}PKJaclKy$JX`6ADtP~aX88eTa4jAnMD=BZ zX13}JG^C@@ke=UcIt9*@$iSY2)7^JA0kVf9at6UcvBcARPBRgL-ix0>Okhdk>B6Z; zLL@S05FDgTI@P+A1VIOKXAm_Qlmz*L#tckFLWt`pP?jpdj{2oR_4qqzC45t8RZ$QVKbIDLZNYl{cDlKqe1QLP(JxXv1gcLZ90kG5@U;=w= z00Pvs{WH60d}kLVXvfEvC)_K%+G_uQNXA8j9^*C2dPmopanR88x&J%UuTNA&L7o(V d@b81ZObT>UvRk5D3xN?S01fuBB{U^y{{xF?*XaNN From a33ca5b004f7ce1b222421db789e4f8b7df4c424 Mon Sep 17 00:00:00 2001 From: Vankka Date: Thu, 4 Oct 2018 20:32:34 +0300 Subject: [PATCH 5/7] LuckPerms Plugin Data by Vankka (#742) --- .../java/com/djrapitops/plan/PlanSponge.java | 13 +- Plan/src/main/resources/bungee.yml | 7 +- Plan/src/main/resources/plugin.yml | 1 + PlanPluginBridge/pom.xml | 12 ++ .../djrapitops/pluginbridge/plan/Bridge.java | 4 + .../plan/luckperms/LuckPermsData.java | 125 ++++++++++++++++++ .../plan/luckperms/LuckPermsHook.java | 27 ++++ 7 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java diff --git a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java index aa5dc60fd..e914ed0a8 100644 --- a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java +++ b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java @@ -20,12 +20,23 @@ import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.game.state.GameStartedServerEvent; import org.spongepowered.api.event.game.state.GameStoppingServerEvent; +import org.spongepowered.api.plugin.Dependency; import org.spongepowered.api.plugin.Plugin; import java.io.File; import java.io.InputStream; -@Plugin(id = "plan", name = "Plan", version = "4.4.6", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"}) +@Plugin( + id = "plan", + name = "Plan", + version = "4.4.6", + description = "Player Analytics Plugin by Rsl1122", + authors = {"Rsl1122"}, + dependencies = { + @Dependency(id = "nucleus", optional = true), + @Dependency(id = "luckperms", optional = true) + } +) public class PlanSponge extends SpongePlugin implements PlanPlugin { @Inject diff --git a/Plan/src/main/resources/bungee.yml b/Plan/src/main/resources/bungee.yml index 048b498cc..dd8f8c9ce 100644 --- a/Plan/src/main/resources/bungee.yml +++ b/Plan/src/main/resources/bungee.yml @@ -1,4 +1,9 @@ name: Plan author: Rsl1122 main: com.djrapitops.plan.PlanBungee -version: 4.4.6 \ No newline at end of file +version: 4.4.6 +softdepend: +- AdvancedBan +- LiteBans +- LuckPerms +- ViaVersion \ No newline at end of file diff --git a/Plan/src/main/resources/plugin.yml b/Plan/src/main/resources/plugin.yml index 56ef3675e..d66bbf18c 100644 --- a/Plan/src/main/resources/plugin.yml +++ b/Plan/src/main/resources/plugin.yml @@ -20,6 +20,7 @@ softdepend: - Kingdoms - RedProtect - AdvancedBan +- LuckPerms commands: plan: diff --git a/PlanPluginBridge/pom.xml b/PlanPluginBridge/pom.xml index a4f68f61d..d61b0fe23 100644 --- a/PlanPluginBridge/pom.xml +++ b/PlanPluginBridge/pom.xml @@ -94,6 +94,12 @@ 4.3.0-SNAPSHOT provided + + org.apache.commons + commons-text + 1.3 + + @@ -167,6 +173,12 @@ 1.6.0-PR1-S7.0 provided + + me.lucko.luckperms + luckperms-api + 4.3 + provided + diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java index 2adf0dfbd..fc835bacd 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java @@ -18,6 +18,7 @@ import com.djrapitops.pluginbridge.plan.jobs.JobsHook; import com.djrapitops.pluginbridge.plan.kingdoms.KingdomsHook; import com.djrapitops.pluginbridge.plan.litebans.LiteBansBukkitHook; import com.djrapitops.pluginbridge.plan.litebans.LiteBansBungeeHook; +import com.djrapitops.pluginbridge.plan.luckperms.LuckPermsHook; import com.djrapitops.pluginbridge.plan.mcmmo.McmmoHook; import com.djrapitops.pluginbridge.plan.nucleus.NucleusHook; import com.djrapitops.pluginbridge.plan.protocolsupport.ProtocolSupportHook; @@ -75,6 +76,7 @@ public class Bridge { private static Hook[] getSpongeHooks(HookHandler h) { return new Hook[]{ new BuyCraftHook(h), + new LuckPermsHook(h), new SpongeEconomyHook(h), new NucleusHook(h) }; @@ -85,6 +87,7 @@ public class Bridge { new AdvancedBanHook(h), new BuyCraftHook(h), new LiteBansBungeeHook(h), + new LuckPermsHook(h), new ViaVersionBungeeHook(h) }; } @@ -104,6 +107,7 @@ public class Bridge { new JobsHook(h), new KingdomsHook(h), new LiteBansBukkitHook(h), + new LuckPermsHook(h), new McmmoHook(h), new SuperbVoteHook(h), new ProtocolSupportHook(h), diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java new file mode 100644 index 000000000..9c10055ec --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsData.java @@ -0,0 +1,125 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package com.djrapitops.pluginbridge.plan.luckperms; + +import com.djrapitops.plan.data.element.AnalysisContainer; +import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.element.TableContainer; +import com.djrapitops.plan.data.plugin.ContainerSize; +import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.utilities.html.icon.Color; +import com.djrapitops.plan.utilities.html.icon.Family; +import com.djrapitops.plan.utilities.html.icon.Icon; +import java.util.*; +import java.util.stream.Collectors; +import me.lucko.luckperms.api.*; +import me.lucko.luckperms.api.caching.MetaData; +import org.apache.commons.text.TextStringBuilder; + +/** + * PluginData for LuckPerms plugin. + * + * @author Vankka + */ +public class LuckPermsData extends PluginData { + private LuckPermsApi api; + + public LuckPermsData(LuckPermsApi api) { + super(ContainerSize.THIRD, "LuckPerms"); + setPluginIcon(Icon.called("exclamation-triangle").of(Color.LIGHT_GREEN).build()); + + this.api = api; + } + + @Override + public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { + User user = api.getUser(uuid); + + if (user == null) { + inspectContainer.addValue("Data unavailable", "Could not get user data"); + return inspectContainer; + } + + MetaData metaData = user.getCachedData().getMetaData(Contexts.allowAll()); + String prefix = metaData.getPrefix(); + String suffix = metaData.getSuffix(); + + inspectContainer.addValue(getWithIcon("Primary group", Icon.called("user-friends").of(Family.SOLID)), user.getPrimaryGroup()); + inspectContainer.addValue(getWithIcon("Prefix", Icon.called("file-signature").of(Family.SOLID).of(Color.GREEN)), prefix != null ? prefix : "None"); + inspectContainer.addValue(getWithIcon("Suffix", Icon.called("file-signature").of(Family.SOLID).of(Color.BLUE)),suffix != null ? suffix : "None"); + + if (!metaData.getMeta().isEmpty()) { + TableContainer metaTable = new TableContainer( + getWithIcon("Meta", Icon.called("info-circle").of(Family.SOLID)), + getWithIcon("Value", Icon.called("file-alt").of(Family.SOLID)) + ); + metaData.getMeta().forEach((key, value) -> metaTable.addRow(key, value)); + inspectContainer.addTable("Meta", metaTable); + } + + List groups = user.getPermissions().stream() + .filter(Node::isGroupNode) + .map(Node::getGroupName) + .sorted() + .collect(Collectors.toList()); + + inspectContainer.addValue( + getWithIcon("Groups", Icon.called("user-friends").of(Family.SOLID)), + new TextStringBuilder().appendWithSeparators(groups, ", ").build() + ); + + Set tracks = api.getTracks(); + if (!tracks.isEmpty()) { + TableContainer trackTable = new TableContainer( + getWithIcon("Track", Icon.called("ellipsis-h").of(Family.SOLID)), + getWithIcon("Group", Icon.called("user-friends").of(Family.SOLID)) + ); + for (Track track : tracks) { + // reduce is used to get the last element + String currentGroup = api.getGroups().stream() + .map(this::getGroupDisplayName).filter(groups::contains) + .reduce((first, second) -> second).orElse("None"); + trackTable.addRow(track.getName(), currentGroup); + } + inspectContainer.addTable("Tracks", trackTable); + } + + return inspectContainer; + } + + @Override + public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { + // There will *always* be atleast 1 group + TableContainer groupTable = new TableContainer( + getWithIcon("Group", Icon.called("user-friends").of(Family.SOLID)), + getWithIcon("Weight", Icon.called("weight-hanging").of(Family.SOLID)), + getWithIcon("Permissions", Icon.called("list").of(Family.SOLID)) + ); + + api.getGroups().stream().sorted(Comparator.comparing(Group::getName)).forEach(group -> { + OptionalInt weight = group.getWeight(); + + groupTable.addRow(getGroupDisplayName(group), weight.isPresent() ? weight.getAsInt() : "None", group.getPermissions().size()); + }); + analysisContainer.addTable("Groups", groupTable); + + Set tracks = api.getTracks(); + if (!tracks.isEmpty()) { + TableContainer trackTable = new TableContainer( + getWithIcon("Track", Icon.called("ellipsis-h").of(Family.SOLID)), + getWithIcon("Size", Icon.called("list").of(Family.SOLID)) + ); + tracks.forEach(track -> trackTable.addRow(track.getName(), track.getSize())); + analysisContainer.addTable("Tracks", trackTable); + } + + return analysisContainer; + } + + private String getGroupDisplayName(Group group) { + String displayName = group.getDisplayName(); + return displayName != null ? displayName : group.getName(); + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java new file mode 100644 index 000000000..341ed1853 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/luckperms/LuckPermsHook.java @@ -0,0 +1,27 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package com.djrapitops.pluginbridge.plan.luckperms; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.pluginbridge.plan.Hook; +import me.lucko.luckperms.LuckPerms; + +/** + * Hook for LuckPerms plugin. + * + * @author Vankka + */ +public class LuckPermsHook extends Hook { + public LuckPermsHook(HookHandler hookHandler) { + super("me.lucko.luckperms.LuckPerms", hookHandler); + } + + @Override + public void hook() throws IllegalStateException { + if (enabled) { + addPluginDataSource(new LuckPermsData(LuckPerms.getApi())); + } + } +} From 27c2492599631c4e2475dc0f2dad51f49842d60d Mon Sep 17 00:00:00 2001 From: Vankka Date: Fri, 5 Oct 2018 22:30:57 +0300 Subject: [PATCH 6/7] DiscordSRV plugin data by Vankka (#743) --- Plan/src/main/resources/plugin.yml | 3 +- PlanPluginBridge/pom.xml | 10 ++ .../djrapitops/pluginbridge/plan/Bridge.java | 2 + .../plan/discordsrv/DiscordSRVData.java | 124 ++++++++++++++++++ .../plan/discordsrv/DiscordSRVHook.java | 26 ++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java create mode 100644 PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java diff --git a/Plan/src/main/resources/plugin.yml b/Plan/src/main/resources/plugin.yml index d66bbf18c..436213abd 100644 --- a/Plan/src/main/resources/plugin.yml +++ b/Plan/src/main/resources/plugin.yml @@ -21,6 +21,7 @@ softdepend: - RedProtect - AdvancedBan - LuckPerms +- DiscordSRV commands: plan: @@ -89,7 +90,7 @@ permissions: default: op plan.webmanage: description: Manage the webusers, delete, list, check - default: op; + default: op plan.basic: children: diff --git a/PlanPluginBridge/pom.xml b/PlanPluginBridge/pom.xml index d61b0fe23..329b230d8 100644 --- a/PlanPluginBridge/pom.xml +++ b/PlanPluginBridge/pom.xml @@ -85,6 +85,10 @@ nucleus-repo http://repo.drnaylor.co.uk/artifactory/list/minecraft + + discordsrv-repo + https://ci.scarsz.me/plugin/repository/everything/ + @@ -179,6 +183,12 @@ 4.3 provided + + github.scarsz.discordsrv + DiscordSRV + 1.16.4 + provided + diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java index fc835bacd..4ac01895d 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/Bridge.java @@ -10,6 +10,7 @@ import com.djrapitops.pluginbridge.plan.advancedban.AdvancedBanHook; import com.djrapitops.pluginbridge.plan.askyblock.ASkyBlockHook; import com.djrapitops.pluginbridge.plan.banmanager.BanManagerHook; import com.djrapitops.pluginbridge.plan.buycraft.BuyCraftHook; +import com.djrapitops.pluginbridge.plan.discordsrv.DiscordSRVHook; import com.djrapitops.pluginbridge.plan.essentials.EssentialsHook; import com.djrapitops.pluginbridge.plan.factions.FactionsHook; import com.djrapitops.pluginbridge.plan.griefprevention.GriefPreventionHook; @@ -100,6 +101,7 @@ public class Bridge { new ASkyBlockHook(h), new BanManagerHook(h), new BuyCraftHook(h), + new DiscordSRVHook(h), new EssentialsHook(h), new FactionsHook(h), new GriefPreventionHook(h), diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java new file mode 100644 index 000000000..0223833ca --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVData.java @@ -0,0 +1,124 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package com.djrapitops.pluginbridge.plan.discordsrv; + +import com.djrapitops.plan.data.element.AnalysisContainer; +import com.djrapitops.plan.data.element.InspectContainer; +import com.djrapitops.plan.data.plugin.ContainerSize; +import com.djrapitops.plan.data.plugin.PluginData; +import com.djrapitops.plan.utilities.FormatUtils; +import com.djrapitops.plan.utilities.html.icon.Color; +import com.djrapitops.plan.utilities.html.icon.Family; +import com.djrapitops.plan.utilities.html.icon.Icon; +import github.scarsz.discordsrv.DiscordSRV; +import github.scarsz.discordsrv.dependencies.jda.core.entities.Member; +import github.scarsz.discordsrv.dependencies.jda.core.entities.Role; +import github.scarsz.discordsrv.dependencies.jda.core.entities.User; +import github.scarsz.discordsrv.util.DiscordUtil; +import java.util.Collection; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import org.apache.commons.text.TextStringBuilder; + +/** + * PluginData for DiscordSRV plugin. + * + * @author Vankka + */ +public class DiscordSRVData extends PluginData { + public DiscordSRVData() { + super(ContainerSize.THIRD, "DiscordSRV"); + setPluginIcon(Icon.called("discord").of(Family.BRAND).build()); + } + + @Override + public InspectContainer getPlayerData(UUID uuid, InspectContainer inspectContainer) { + if (!DiscordSRV.isReady) { + return inspectContainer; + } + + String userId = DiscordSRV.getPlugin().getAccountLinkManager().getDiscordId(uuid); + User user = userId != null ? DiscordUtil.getUserById(userId) : null; + + if (user == null) { + return inspectContainer; + } + + inspectContainer.addValue( + getWithIcon("Username", Icon.called("user").of(Family.SOLID).of(Color.CYAN)), + "@" + user.getName() + "#" + user.getDiscriminator() + ); + inspectContainer.addValue( + getWithIcon("Account creation date", Icon.called("plus").of(Family.SOLID).of(Color.BLUE)), + FormatUtils.formatTimeStampYear(user.getCreationTime().toEpochSecond() * 1000L) + ); + + Member member = DiscordSRV.getPlugin().getMainGuild().getMember(user); + + if (member != null) { + addMemberData(member, inspectContainer); + } + + return inspectContainer; + } + + private void addMemberData(Member member, InspectContainer inspectContainer) { + String nickname = member.getNickname(); + + inspectContainer.addValue( + getWithIcon("Nickname", Icon.called("user-ninja").of(Family.SOLID).of(Color.ORANGE)), + nickname != null ? nickname : "None" + ); + inspectContainer.addValue( + getWithIcon("Join Date", Icon.called("plus").of(Family.SOLID).of(Color.GREEN)), + FormatUtils.formatTimeStampYear(member.getJoinDate().toEpochSecond() * 1000L) + ); + + List roles = member.getRoles().stream().map(Role::getName).collect(Collectors.toList()); // Ordered list of role names + if (!roles.isEmpty()) { + inspectContainer.addValue( + getWithIcon("Roles", Icon.called("user-circle").of(Family.SOLID).of(Color.RED)), + new TextStringBuilder().appendWithSeparators(roles, ", ").build() + ); + } + } + + @Override + public AnalysisContainer getServerData(Collection uuids, AnalysisContainer analysisContainer) { + if (!DiscordSRV.isReady) { + return analysisContainer; + } + + int accountsLinked = DiscordSRV.getPlugin().getAccountLinkManager().getLinkedAccounts().size(); + int guildUsers = DiscordSRV.getPlugin().getMainGuild().getMembers().size(); + + analysisContainer.addValue( + getWithIcon("Accounts linked", Icon.called("link").of(Family.SOLID).of(Color.CYAN)), + accountsLinked + ); + analysisContainer.addValue( + getWithIcon("Users in main guild", Icon.called("users").of(Family.SOLID).of(Color.GREEN)), + guildUsers + ); + analysisContainer.addValue( + getWithIcon("Accounts linked / Total players", Icon.called("percentage").of(Family.SOLID).of(Color.TEAL)), + calculatePercentage(accountsLinked, uuids.size()) + "%" + ); + analysisContainer.addValue( + getWithIcon("Accounts linked / Users in main guild", Icon.called("percentage").of(Family.SOLID).of(Color.LIGHT_GREEN)), + calculatePercentage(accountsLinked, guildUsers) + "%" + ); + + return analysisContainer; + } + + private double calculatePercentage(int input1, int input2) { + if (input1 == 0 || input2 == 0) + return 0D; + + return Math.round((double) input1 / input2 * 10000D) / 100D; // 2 decimals + } +} diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java new file mode 100644 index 000000000..0c951b7c6 --- /dev/null +++ b/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/discordsrv/DiscordSRVHook.java @@ -0,0 +1,26 @@ +/* + * Licence is provided in the jar as license.yml also here: + * https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml + */ +package com.djrapitops.pluginbridge.plan.discordsrv; + +import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.pluginbridge.plan.Hook; + +/** + * Hook for DiscordSRV plugin. + * + * @author Vankka + */ +public class DiscordSRVHook extends Hook { + public DiscordSRVHook(HookHandler hookHandler) { + super("github.scarsz.discordsrv.DiscordSRV", hookHandler); + } + + @Override + public void hook() throws NoClassDefFoundError { + if (enabled) { + addPluginDataSource(new DiscordSRVData()); + } + } +} From dd3c297fd0a6d2ea4472df1e0d4e47d8772e70eb Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 6 Oct 2018 21:56:58 +0300 Subject: [PATCH 7/7] [V] Increased version to 4.4.7 --- Plan/src/main/java/com/djrapitops/plan/PlanSponge.java | 2 +- Plan/src/main/resources/bungee.yml | 2 +- Plan/src/main/resources/plugin.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java index e914ed0a8..858ed6b6c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java +++ b/Plan/src/main/java/com/djrapitops/plan/PlanSponge.java @@ -29,7 +29,7 @@ import java.io.InputStream; @Plugin( id = "plan", name = "Plan", - version = "4.4.6", + version = "4.4.7", description = "Player Analytics Plugin by Rsl1122", authors = {"Rsl1122"}, dependencies = { diff --git a/Plan/src/main/resources/bungee.yml b/Plan/src/main/resources/bungee.yml index dd8f8c9ce..b165e7633 100644 --- a/Plan/src/main/resources/bungee.yml +++ b/Plan/src/main/resources/bungee.yml @@ -1,7 +1,7 @@ name: Plan author: Rsl1122 main: com.djrapitops.plan.PlanBungee -version: 4.4.6 +version: 4.4.7 softdepend: - AdvancedBan - LiteBans diff --git a/Plan/src/main/resources/plugin.yml b/Plan/src/main/resources/plugin.yml index 436213abd..f83a18fa4 100644 --- a/Plan/src/main/resources/plugin.yml +++ b/Plan/src/main/resources/plugin.yml @@ -1,7 +1,7 @@ name: Plan author: Rsl1122 main: com.djrapitops.plan.Plan -version: 4.4.6 +version: 4.4.7 softdepend: - EssentialsX - Towny