From af6d3758eb36343b7e042e9edff3a4c8d53beaaa Mon Sep 17 00:00:00 2001 From: JustinAJ Date: Tue, 30 Jun 2015 03:42:22 -0400 Subject: [PATCH] Added GenericCommand class. Added macros for generating ConsoleCommands and IRCCommands from GenericCommands. --- Bot/Bot.vcxproj | 2 + Bot/Bot.vcxproj.filters | 6 ++ Bot/Console_Command.h | 41 +++++++---- Bot/Generic_Command.cpp | 62 +++++++++++++++++ Bot/Generic_Command.h | 125 ++++++++++++++++++++++++++++++++++ Bot/IRC_Command.h | 69 +++++++++++++++---- Jupiter Bot.sln | 3 + Release/Bot.lib | Bin 19858 -> 25088 bytes Release/Plugins/RenX.Core.lib | Bin 129390 -> 129390 bytes 9 files changed, 284 insertions(+), 24 deletions(-) create mode 100644 Bot/Generic_Command.cpp create mode 100644 Bot/Generic_Command.h diff --git a/Bot/Bot.vcxproj b/Bot/Bot.vcxproj index 5d7b0e5..29a4484 100644 --- a/Bot/Bot.vcxproj +++ b/Bot/Bot.vcxproj @@ -88,6 +88,7 @@ + @@ -95,6 +96,7 @@ + diff --git a/Bot/Bot.vcxproj.filters b/Bot/Bot.vcxproj.filters index e11e6f6..9478b74 100644 --- a/Bot/Bot.vcxproj.filters +++ b/Bot/Bot.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + @@ -47,6 +50,9 @@ Header Files + + Header Files + diff --git a/Bot/Console_Command.h b/Bot/Console_Command.h index 94a5b00..e7955ae 100644 --- a/Bot/Console_Command.h +++ b/Bot/Console_Command.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013-2014 Justin James. + * Copyright (C) 2013-2015 Justin James. * * This license must be preserved. * Any applications, libraries, or code which make any use of any @@ -23,21 +23,13 @@ * @brief Provides an extendable command system specialized for console-based commands. */ -#include "Jupiter_Bot.h" #include "Jupiter/Command.h" #include "Jupiter/ArrayList.h" +#include "Jupiter_Bot.h" +#include "Generic_Command.h" -class Command; class ConsoleCommand; -/* -* Initial access level is always 0. Change this using setAccessLevel() -* There are no default triggers. Add triggers using addTrigger(trigger) -* When your command is triggered, trigger(parameters) will be called. -* You must provide command-specific help for your command, through getHelp(). -* The first trigger added is the trigger which is displayed to the user in the help command. -*/ - /** DLL Linkage Nagging */ #if defined _MSC_VER #pragma warning(push) @@ -49,7 +41,10 @@ JUPITER_BOT_API extern Jupiter::ArrayList *consoleCommands; /** * @brief Provides the basis for console commands. -* Note: This will likely be moved to a separate file in the future. +* There are no default triggers. Add triggers using addTrigger(trigger) +* When your command is triggered, trigger(parameters) will be called. +* You must provide command-specific help for your command, through getHelp(). +* The first trigger added is the trigger which is displayed to the user in "help" commands. */ class JUPITER_BOT_API ConsoleCommand : public Jupiter::Command { @@ -99,6 +94,28 @@ class CLASS : public ConsoleCommand { \ #define CONSOLE_COMMAND_INIT(CLASS) \ CLASS CLASS ## _instance; +/** Generates a console command implementation from a generic command. */ +#define GENERIC_COMMAND_AS_CONSOLE_COMMAND_IMPL(CLASS) \ + CLASS ## _AS_CONSOLE_COMMAND :: CLASS ## _AS_CONSOLE_COMMAND() { \ + size_t index = 0; \ + while (index != CLASS ## _instance.getTriggerCount()) this->addTrigger(CLASS ## _instance.getTrigger(index++)); } \ + void CLASS ## _AS_CONSOLE_COMMAND :: trigger(const Jupiter::ReadableString ¶meters) { \ + GenericCommand::ResponseLine *del; \ + GenericCommand::ResponseLine *ret = CLASS ## _instance.trigger(parameters); \ + while (ret != nullptr) { \ + ret->response.println(ret->type == GenericCommand::DisplayType::PublicError || ret->type == GenericCommand::DisplayType::PrivateError ? stderr : stdout); \ + del = ret; ret = ret->next; delete del; } } \ + const Jupiter::ReadableString & CLASS ## _AS_CONSOLE_COMMAND :: getHelp(const Jupiter::ReadableString ¶meters) { \ + return CLASS ## _instance.getHelp(parameters); } \ + CONSOLE_COMMAND_INIT(CLASS ## _AS_CONSOLE_COMMAND) + +/** Generates a console command from a generic command. */ +#define GENERIC_COMMAND_AS_CONSOLE_COMMAND(CLASS) \ + GENERIC_CONSOLE_COMMAND(CLASS ## _AS_CONSOLE_COMMAND) \ + GENERIC_COMMAND_AS_CONSOLE_COMMAND_IMPL(CLASS); + + + /** Re-enable warnings */ #if defined _MSC_VER #pragma warning(pop) diff --git a/Bot/Generic_Command.cpp b/Bot/Generic_Command.cpp new file mode 100644 index 0000000..64b526b --- /dev/null +++ b/Bot/Generic_Command.cpp @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2015 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#include "Generic_Command.h" + +Jupiter::ArrayList _genericCommands; +Jupiter::ArrayList *genericCommands = &_genericCommands; + +GenericCommand::GenericCommand() +{ + _genericCommands.add(this); +} + +GenericCommand::~GenericCommand() +{ + size_t count = _genericCommands.size(); + while (count != 0) + if (_genericCommands.get(--count) == this) + { + _genericCommands.remove(count); + break; + } +} + +GenericCommand *getGenericCommand(const Jupiter::ReadableString &trigger) +{ + size_t count = _genericCommands.size(); + while (count != 0) + { + GenericCommand *cmd = _genericCommands.get(--count); + if (cmd->matches(trigger)) + return cmd; + } + return nullptr; +} + +GenericCommand::ResponseLine::ResponseLine(const Jupiter::ReadableString &response_, GenericCommand::DisplayType type_) +{ + GenericCommand::ResponseLine::response = response_; + GenericCommand::ResponseLine::type = type_; +} + +GenericCommand::ResponseLine *GenericCommand::ResponseLine::set(const Jupiter::ReadableString &response_, GenericCommand::DisplayType type_) +{ + GenericCommand::ResponseLine::response = response_; + GenericCommand::ResponseLine::type = type_; + return this; +} \ No newline at end of file diff --git a/Bot/Generic_Command.h b/Bot/Generic_Command.h new file mode 100644 index 0000000..824f6bf --- /dev/null +++ b/Bot/Generic_Command.h @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2015 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _GENERIC_COMMAND_H_HEADER +#define _GENERIC_COMMAND_H_HEADER + +/** +* @file Generic_Command.h +* @brief Provides an extendable command system. +*/ + +#include "Jupiter/Command.h" +#include "Jupiter/String.h" +#include "Jupiter_Bot.h" + +class GenericCommand; + +/** DLL Linkage Nagging */ +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + +/** Generic command list */ +JUPITER_BOT_API extern Jupiter::ArrayList *genericCommands; + +/** +* @brief Provides the base for generic commands. +*/ +class JUPITER_BOT_API GenericCommand : public Jupiter::Command +{ +public: + /** Enumerated class to guide output in generic command interpreters */ + enum class DisplayType + { + PublicSuccess, + PrivateSuccess, + PublicError, + PrivateError, + }; + + /** Data entry returned by trigger() */ + struct JUPITER_BOT_API ResponseLine + { + Jupiter::StringS response; + GenericCommand::DisplayType type; + ResponseLine *next = nullptr; + + /** + * @brief Sets the response and type of the ResponseLine. + * + * @param in_response Value to set response to. + * @param in_type Value to set type to. + * @return This. + */ + ResponseLine *set(const Jupiter::ReadableString &response, GenericCommand::DisplayType type); + ResponseLine() = default; + ResponseLine(const Jupiter::ReadableString &response, GenericCommand::DisplayType type); + }; + + /** + * @brief Called when the command is to be executed. + * + * @param input Parameters passed to the command by the user. + */ + virtual ResponseLine *trigger(const Jupiter::ReadableString &input) = 0; + + /** + * @brief Default constructor for the generic command class. + */ + GenericCommand(); + + /** + * @brief Destructor for the generic command class. + */ + ~GenericCommand(); +}; + +/** +* @brief Fetches a generic command based on its trigger. +* +* @param trigger Trigger of the command to fetch. +* @return A generic command if it exists, nullptr otherwise. +*/ +JUPITER_BOT_API extern GenericCommand *getGenericCommand(const Jupiter::ReadableString &trigger); + +/** Generic Command Macros */ + +/** Defines the core of a generic command's declaration. This should be included in every generic command. */ +#define BASE_GENERIC_COMMAND(CLASS) \ + public: \ + CLASS(); \ + GenericCommand::ResponseLine *trigger(const Jupiter::ReadableString ¶meters); \ + const Jupiter::ReadableString &getHelp(const Jupiter::ReadableString ¶meters); + +/** Expands to become the entire declaration for a generic command. In most cases, this will be sufficient. */ +#define GENERIC_GENERIC_COMMAND(CLASS) \ +class CLASS : public GenericCommand { \ + BASE_GENERIC_COMMAND(CLASS) \ +}; + +/** Instantiates a generic command. */ +#define GENERIC_COMMAND_INIT(CLASS) \ + CLASS CLASS ## _instance; + +/** Re-enable warnings */ +#if defined _MSC_VER +#pragma warning(pop) +#endif + +#endif // _GENERIC_COMMAND_H_HEADER \ No newline at end of file diff --git a/Bot/IRC_Command.h b/Bot/IRC_Command.h index 209d1af..7540829 100644 --- a/Bot/IRC_Command.h +++ b/Bot/IRC_Command.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013-2014 Justin James. + * Copyright (C) 2013-2015 Justin James. * * This license must be preserved. * Any applications, libraries, or code which make any use of any @@ -27,20 +27,12 @@ #include "Jupiter/IRC_Client.h" #include "Jupiter/ArrayList.h" #include "Jupiter/String.h" -#include "ServerManager.h" #include "Jupiter_Bot.h" +#include "ServerManager.h" +#include "IRC_Bot.h" -class IRC_Bot; class IRCCommand; -/* -* Initial access level is always 0. Change this using setAccessLevel() -* There are no default triggers. Add triggers using addTrigger(trigger) -* When your command is triggered, trigger(channel, nick, parameters) will be called. -* You must provide command-specific help for your command, through getHelp(). -* The first trigger added is the trigger which is displayed to the user in the help command. -*/ - /** DLL Linkage Nagging */ #if defined _MSC_VER #pragma warning(push) @@ -52,7 +44,11 @@ JUPITER_BOT_API extern Jupiter::ArrayList *IRCMasterCommandList; /** * @brief Provides the basis for IRC commands. -* Note: This will likely be moved to a separate file in the future. +* Initial access level is always 0. Change this using setAccessLevel() +* There are no default triggers. Add triggers using addTrigger(trigger) +* When your command is triggered, trigger(channel, nick, parameters) will be called. +* You must provide command-specific help for your command, through getHelp(). +* The first trigger added is the trigger which is displayed to the user in the help command. */ class JUPITER_BOT_API IRCCommand : public Jupiter::Command { @@ -189,6 +185,55 @@ class CLASS : public IRCCommand { \ CLASS CLASS ## _instance; \ CLASS *CLASS::copy() { return new CLASS(*this); } +/** Generates a base IRC command implementation from a generic command. */ +#define GENERIC_COMMAND_AS_IRC_COMMAND_IMPL_BASE(CLASS) \ + void CLASS ## _AS_IRC_COMMAND :: trigger(IRC_Bot *source, const Jupiter::ReadableString &channel, const Jupiter::ReadableString &nick, const Jupiter::ReadableString ¶meters) { \ + GenericCommand::ResponseLine *del; \ + GenericCommand::ResponseLine *ret = CLASS ## _instance.trigger(parameters); \ + while (ret != nullptr) { \ + switch(ret->type) { \ + case GenericCommand::DisplayType::PublicSuccess: \ + case GenericCommand::DisplayType::PublicError: \ + source->sendMessage(channel, ret->response); \ + break; \ + case GenericCommand::DisplayType::PrivateSuccess: \ + case GenericCommand::DisplayType::PrivateError: \ + source->sendNotice(nick, ret->response); \ + break; \ + default: \ + source->sendMessage(nick, ret->response); \ + break; \ + } \ + del = ret; ret = ret->next; delete del; } } \ + const Jupiter::ReadableString & CLASS ## _AS_IRC_COMMAND :: getHelp(const Jupiter::ReadableString ¶meters) { \ + return CLASS ##_instance.getHelp(parameters); } \ + IRC_COMMAND_INIT(CLASS ## _AS_IRC_COMMAND) + +/** Generates an IRC command implementation from a generic command. */ +#define GENERIC_COMMAND_AS_IRC_COMMAND_IMPL(CLASS) \ + void CLASS ## _AS_IRC_COMMAND :: create() { \ + size_t index = 0; \ + while (index != CLASS ## _instance.getTriggerCount()) this->addTrigger(CLASS ## _instance.getTrigger(index++)); } \ + GENERIC_COMMAND_AS_IRC_COMMAND_IMPL_BASE(CLASS) + +/** Generates an IRC command implementation from a generic command. */ +#define GENERIC_COMMAND_AS_IRC_COMMAND_IMPL_2(CLASS, ACCESS_LEVEL) \ + void CLASS ## _AS_IRC_COMMAND :: create() { \ + size_t index = 0; \ + while (index != CLASS ## _instance.getTriggerCount()) this->addTrigger(CLASS ## _instance.getTrigger(index++)); \ + this->setAccessLevel(ACCESS_LEVEL); } \ + GENERIC_COMMAND_AS_IRC_COMMAND_IMPL_BASE(CLASS) + +/** Generates an IRC command from a generic command. */ +#define GENERIC_COMMAND_AS_IRC_COMMAND(CLASS) \ + GENERIC_IRC_COMMAND(CLASS ## _AS_IRC_COMMAND) \ + GENERIC_COMMAND_AS_IRC_COMMAND_IMPL(CLASS); + +/** Generates an IRC command from a generic command. */ +#define GENERIC_COMMAND_AS_IRC_COMMAND_2(CLASS, ACCESS_LEVEL) \ + GENERIC_IRC_COMMAND(CLASS ## _AS_IRC_COMMAND) \ + GENERIC_COMMAND_AS_IRC_COMMAND_IMPL_2(CLASS, ACCESS_LEVEL); + /** Re-enable warnings */ #if defined _MSC_VER #pragma warning(pop) diff --git a/Jupiter Bot.sln b/Jupiter Bot.sln index 88da219..50e2b32 100644 --- a/Jupiter Bot.sln +++ b/Jupiter Bot.sln @@ -158,6 +158,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenX.Listen", "RenX.Listen\ EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChannelRelay", "ChannelRelay\ChannelRelay.vcxproj", "{ADAD973E-EAA8-446D-BCD3-83B53DBC3A3F}" + ProjectSection(ProjectDependencies) = postProject + {C188871B-5F32-4946-B301-24CA2EBB275D} = {C188871B-5F32-4946-B301-24CA2EBB275D} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Release/Bot.lib b/Release/Bot.lib index bf5a487a5fcd960cefae6f16d4de143b4d79b701..9336cfee1c9b29e79e133f44c1f3604f42b4a16f 100644 GIT binary patch literal 25088 zcmcItOK%*-7CugR8VDhnS9m#KlJH7AevO^i^!OD!aU937od=J}U=Q}_I^)P=P=b(P z!GeoeAjAr>xf>R&xhz;A#O5Mhq$?qW5O>3ZAHaeI7m52-S66p+byZh&&)CwT?XIeG zzB+a4)TuhvVc)Up=JMK!&cogDpTa=@;LyOx;NVdFeQ)q*v3EEqxzb5QelMfOxI`#pl$&c_i==d9)PJ#!csdqVfqeMP^ zizjGeKPS8eP1TB5eX%X)kEe&(JFiXT}e1_AB6(XO&?*wS}BBztP(GSq<51dY-Ka5Jy6Qh}* zIh~y+Vl=&n)9Bmi7o*=fomn9A=~p~KSN3vx;W`ncgB`6837UuA7@hw*ph-MIbCAL4GW6&Z`hOWz-Nos}{X{-}gC}SX zI$%_Nk5Q#EeP-_L{6b}X;{4eB^xVSP`O4_V7F8;H9fC?uDn$0DQ|98Z_L!2 zTi()GtcK@#r(U~O->PkTo-9JG#YB`=Yg_93OJ(p^UzSH9a=unwslKvatDN83tT$G@ z@ko$&S-@>0$5N@F;#e$Cco#!i#`03BS8aJN-qKWFDh+5_mdi`M9(2&_MIE(QG@wxV zXrYKoz@yp*dZNc;s|Y-rDv5rjRsx>+TJst#t~OI|)I1Hta(S`0;Pv#J^PjT9GPiV= zRvu+Cv&#zGK{qyZx-MmE8+RGSTYYzEkeA_sG} zV5p$S}@)kbxdt-aC`K0QBn zrrN}c94zAgvYlMVQ7sGrWK$Qepi-1;Nc<5%po9e=OzhZtz1G^R=~V zbB(R(a#c39%FD~OW^<218jRw@H9hWecX_=1=iEY%PZd;%@FtTBhhRh<&MaS}zk~$cu2p1unv+5o+WpEAh zTjmy^fmkGHte|Ndflcyd_$rm^Yg-%3>(yqn-l%W&C1&4rG*C`;5ko2X_0eNmB`qrM zZ zREI{AC3?MB>Onin8c7|c)X{U0jaJi`+3nc|{G7k)aOIwHSSWgXW!>j&56PN=or)kE z%h^g0ENqesZ$<{f#xvsMSeyC2h0XdZf&hut`ErTv+WF48GPAMKtjYW5_=aAdieA4I zk8TI+$X5rtkWfPbK5Xf*A%-nHXwWZn{hrRzZ<83-4-uw>Q~ccM1fa>79agU(-f9~^ z3E6aGrFLC1u+fRBJb?1CDZRQ`6X!@02Id9bLA1o9R9#s~juB>=GDwkIaBsj@_RzD( zj)_8h2-bVeDw4WGjPUr2c5H!hY}kzN4%i`{;z_ZmEfth^YJ8$EE;Z|K)I6mOb9Y*D zUgjFUS@6ah9$9UpHAXfp+)lXuqd{RdnIczdWRoF9GZx0ICNt?2+3pjoWj@T!7?H?2 zkUE&9i8FuqKD&x{;8<$jXo&OkrpAutxnQqZ+N@pOcs+EMT&>~Oo=q(WNWnVN4pw$N ze1L^>m0|{~_+(4jO>4TIY!LKRa@J1GN?U97#^2Pzg3ptpx3nsmGkt|ZnCUxBM1K^C zx{e@Mca-SQX`(mJ5xtM+EtiPa#)xh^PV^vhMIRg^`UuZA@b{l5iQb(;9?K*87PNl? zj7E`rTOj)GCFF?8M4zGkPiX%e>VFSGEU&hHis(ns68?VYWui|iMBSjzm+<={(J-E~ z7kDmjn{-f0xaKx z*E;f5UC6_IkNRJZ6Mcd9bHMm1Fn^4?pP}sQ^WY2m1-Y}|E&~H-R~7wwk?0%HMZA9x zeb(jUJ@^bd2^;i8|>{dWN2(U353~(jn@n z0eYIArNeZTPSH3Wp)!p@X+?UHdguvyiiT-~Zlwokk|t<~rf8b(qsM6B#t-uS({L9ITuDif zB`a1_>NhiGD9uOF`V%Nk^omzjZF39?xG>1IrZREzpgGo#9X(i%kJ3(aP-g2uB3J0# zMlu}R>51+1C;!=n`RpNiR;0{Y|IkcZE1ar^D#~bM?(&M#>MKud&E6{W`rdkw+uGjK z7P8wM1B=sLVtGp)If!N~5x(++qrm63`{X1NpiRWM939wLxSX$}+m;-LO$u{UP?lJl zBJ<31JR@<+c@E-3k#E&_Ufx?T42|StCyL~qUZ0SQ=r~CeSScSeU|b(OK*L5ZMsa!`E=xQVXmjB_mWipzyMW?4l>uz&)%e zcL{bMi|*x0p_S@u*gBaAOzY@vAZF+>e*a+DVjfm9*hXO@vh$ zen}*dwBeSJC5IgVX+2w;o^y4&nGj&n)eUVi^=^TpOR*CQ_`2_hk`0oar_)Y7d38G$|zI~p|raim8h@-lw!??mH^HJ5;+~3)6wNFa8FK#o*h& zE1fePJE?2(!^NE)$oC<~g`bW?E45c%TkSzsjQ=2wNAHV2f~QBjM-SCks$12bK2g+# zU;YL9Z}4I#o*wmn71SFFUi)(XC)H!`J?j2y2cEl-V`D$J2Aw)U_wK$Ge=}a}2cM@= z_oY2&>dTuO&5c*Lx)07j-90ls%kr6tpF8n}HDnA3X)yL1zTcyp{oeu!5?wSz=gJt80Gnt0=Pipkh1-VCrhctK&E!RQTp{|Ng}@WZ$hl6=w6 z4x|Y2xnlAgIu;Y+eU=aGI44v>n{2seF`F~n{NR9I?h}yP0jGf!s=DzA zj?aY1tTvO@(;Zx*8ecJq;#5zFZEH_=n&l$G6(TeDbWpF`C!yP$xI|^$Z$mG|gjDS4 zG041GFSC^%_UU`r4QcG=1*``>4EOVi?q$_z(J)JJg$Ik9R-Wy|RSa%sdny`67uSlg z#`{=oXJ7ImZhjp7`Y~Y6^ma!a;qhB;6orCNN8Eo^fTGhGMdqXC+<1pX5U9+P!@zro zBVK9ZWVW8cVg0D#A0EK{Dc^ed4o<#z*z)`tG}y{MNWIG_wWWThtS`HKiKbI~F|@(c zIlfO@(eCCF)%7JfhR-cA#pmwk5@kQ%qO<{n(l9qdHkoYXL+vQ8NCTWBjW!H9!?EAQE)p8LT#S%8TWv4}D_jB2@4YyCO z*m5a;xCi=vAe0+M5ZcaW^$^2``HG9axW~$TZ}zKeLh)I#6bp<2MvY4RQkOiArXct^XYlWdMuK~CzL6-DHNOCgQZA+JSkoOFA(IGK3LGlRF9+Y-GOw) zgk@D{?(K-dZt;Nt?PKyVsI4S*g(8Bda|ASG@NWl!>j^fKEotJsa>c)-)~(pyDC9oL z<>vg%H!q^JSg?m9GNiKeN*pq*XNRDJr+eXk{yN5z9$y2#SXJ&kkY_G6e3I0&;61OfD#MU<4SRRxl*{ueoQC8_9@a z=fw`#p9zp~1bMG62x!66**}dK#t9!e;X5Jt&DJhM2w0gdc*Nj0@$nbFXM+8-biYEt z%B(HJn#yuM&kI!T8OY2U8#36~bHH}U0b47+F75p7u!C$lvo5*Z%sNQ3=3Zvat1aDB z5oVJaSD{ZBPW(i!j9#uE2Jc<^-h zn}*rOJoz|N|(5yM%*3{aN?)YA34T$cp%VoQs2Mjf!V66d7Z(wK{ES$Jk!8Zw;L_F`1V z1L-bzBlBuL`ac!KJtWJBO!)H6w=MI^OdIVBWl z34)7Nh^gxfqwH7^5~XYsowp)DupzjZHno(SX^c)P5=>~^lu1YxRKWB7U*p5J3lcr~ zo&P;N@9BF#@BFUp;&WveoO?n8;h{)0BH5&4I}!|ejGt^RfOEjXXMw)gfWU`9lCOq# zfa#AlLU#a#;1!L?i-1Bfs4=nzjJ!pgyTF27V=(|IEWD~Qc^Occcv4}T|8#0h#Q=ry zipD@IVBje}iTG`eDBmk2-qjeYA|Ww&Pb0L&Ac^4(;(h9z(1>279WiiUBbEhX+v?AI z8u3R-KrFtjG5aQN*1?rkSNe@{-0VKP!Z(t1x(0W1jMeUn9+@LMos!K0}|x zOzLgEAjTfin2nK?n0ZDceVK;D95Ygwuh$r5H54)~1-UUYle<-3SuQ^{Rm%?p56hie zn;fq9$nT>IM5kQs@#gN;JzgPyYj?=s8=LuTmS0$YD<`eh@|8e?tOv&B2g`fqocW&; zYahw|%o?wC*7ob+$p0M(hf1braAbFS5&)h(2)xz@e8?=-@azl$A5Q~!LckTy$cqs@ zCu<|XTf|+;?F*c>Mc`FFFHZtbP5_;J#-@OkFwi;xJVo4&^K%qfWZUD}!xyU{H%{*x^7s0&rnJ_Fy0S5JU}H;Dr+% z=tLKq;Xy0>h$Di77(^H$^k6S+u%ZdQ@SzGrh~fZ75JMv@Frym-7)AnRDE~~B&0?BL zG@uP`)S><}C{6zdX@4oLe6cMlCpaqo?kf3SqeUF@%Y{=W*+2Aw z*kVnEWfr%KZ`e}tO7q#==u8qY?q+gxV(l$yf zD78yG*;b_CvnyMVBIIu}8Ju64JT>w8FUda=cDX-m%d6f>`i$r}->77C0e4vXkLIH@ zLb*1f$-1diB{wD#d27q0&j`{>$sS5tWMk63D`jSe>vjsO>H1cMQ>La$ZVh1`^(s=8 zM-3yD@kV>861(xyRFlEtq*ATynI7C_5b_%oiocE&zp=N9d8QEk-3K9Xop6{{DG9~2>aye^vQf|DyN2wPQHgyyU`tsN79+17BYcvWOM>t zw{Xb!Gi$pp*nhL?t?GZXn!@ER@mg$>UdoS%D3AQ6T&M%oC={x= zlRbv*ua6aMJ;$5n{;|Er)UVKcuj<53Xn_T&mZf8bt))m_Skv|9Ey_EOig%jEODg3# zt}FEr6%MjeJ>8j4Iiik{i_-B+a^24>M`c?|r)v7|2Q}s8jy`Ip^Hozd{qedEW|Pu@ z3MLg}3{APhc`NPSF^U~}QZKs+QqNIa&UZLQ{Uc-j8KI_ffszbGy)g#F-UHrablHlIn z(*4ZR6z|BWO5WvgN!97X`7I7&NY+nJJDG?7IrBT_>YR8#`Ip^_qAGDQV6*9=%BH6P0oW8-8j zDsE+D3fOkBLNG1YN9CdxX=>O-BSTQv%;(8%=gfD`%=doOpWiui=JK1F-W(mztPQ;Y%QjJS$~2rFKB z+St843KHz%uz0ka9R(fR=-@)VyuALbe52s5yqb=GQ3s=-w-s!*@%fK*i_b0=x-Z(G z{YQyg93R;UK|5_#z-Yv9?{N65wNd(|@v!S?Hyb0&;-=9a>NF^~YqtYYosU<4I}Jgy@D>PIT|{UI}`A{uPPY>etBOm3Q><<@la8}9YM+) zV=yfq1GB9j3X78yKK6H#^U+vwo0CIkd!yOTYTsDstm4MOJ*?B>S3FKmvLPQEu%A~e zuki4Yy!1=mR7tcXifvFOZHbWr&?#)Ij}Jt{UGaEcNT<8>*>);!6m)X8Ph$O1teX}~ z?b}0@$9Rg_ym-fon+*2aSOJ?CZ#ozKl&(_>NJDX_Dt;z)`F%$loe#2psIR(3S+J# zq_v=_!e&nCvZ*bl!={OJ?lhyKd>RZj2J;iG+C*4xonUY464_g|ve-yYeN4KK`n&Qm z=s>Ve!r&i7SgRsOE1$K}DsfB9(TwS^!WyBYFBU-uimLAnNNur0h{mJRFqNJJ9rNO@ z-|x`d{F%|4lH_(e$t-%$gdB;oi>jG#Z0(x~+3p)Nl8yg~Xlf|>zjE-SDtNZ|KP(Md zY)F$i9xw~mxF3&OYc}d;L57W?G##ldb+#$&HM1eb?#x_>%G%8_CbQ6F1e)A6Ax#y` z5tH>>Oca;rK%V31wzqn-M&VC%$S?Rbkvp9I3_{ED2Zi~)Ts3#aqtd1^9 z!$Ua=o6p&!4Gk!zC>eu@R^choIe!7nvG!Rjr*Vvxxl_%Y0S~gEgZ9Hxg!U15SJ79e z!1God0dy_Cun-FE;=n~_arz?IVHYWH^^1-BgBQcv7CYqI0`xylMJ$E^YGx)xI9Ph? z(+EM@lSW5Ls`$aqElT4>#)SY2Oy@#VSP(9+&!od5>jpPQ))IzR@#4KpWpUtkLvTsx zYXTCS3cm${ROu49&v6U0yL7WGM&T(&c&1vpRH#tb0g22&k*S(6Qxk*onnQX!q*xn; zb9tFFPA-g+zlo#led2Oi)ZUYd)=xo}4W5)A$hFn{6ue+}dg25{&c&f~N!q#e*~8Vv zPdsi$O7GeIMuFecu*xbx)h>HSpN5Tgky=(7_q8snG#ffl+_O;W6FTmI zkcldx*duP8JK|fNyZJ~SrF-KYY143%fG(B=UaelL7_ax8^O`145*m*}DX1mBBB~|E zl%!eD7#Nm60~;(11kl~9%ud)U=|!DB%66O8TSmRM2Y`XQc zV%nL>L(fVkHD75f6xehOY$*n|H#x3l_{2_nG9|$EWcs9Tl%8!N%5lfA9I4cD%TQ&` zCnFtF1k=cMCW^#$%*;v?IUQYwN0;N#;Qddu@P$Fn6kmJc>zSz) z`KfPWLjK%(SZWhhj~60&FdIF{L=SpCG`WQbs(zPA+Ta%<*&gL)u6~hSQe6LJDPx25 z3^P8N(--$jG}ruTWJ|2L?@#7R-!r3b+Q>{J=7_3wyRA}NHo^>*n+d+|R81i2JBh68 zJ83mqk3TrEMX+3Rl>3<|xkfizuW)J;9v3O8O#RFLAwKD((pjC9 zvPWq%1jYFRLFo~bhWQH8Z5j|)S3`Nf0?W*zD+VMCidGZGdW`nBLzA={?jg8N&f6qJ zNO9fqswtJgS7pFR+iVP7epR~7K{vU`CjA|3LymCmCjV0kg)eIFLLrCJLSy1gp+wDg z3pYFM5^_mfm@TNCXwiGCxUCHl4A}^SLKu9imZ+E_w`od0L-nl5h4~6ft*nY{kuyaj zb?`RmNcnvj6>ie`t?(V7Zf^4+V*bKVy-lWUeh-i>{T?VlTcthv>GS-|rzYajiZ^6l z<-2?Ob}3*qZYuIkm}<*ZKO3k???b0wQ@x6zm#Tad`q_Q*c*8k!0e3HkB)h|Ji=D;t zwqPJB-}k;H&a(BAx7a$Cq06JeJA^52L(b{|RdY)EqvthqD*0{Jr1<&M z__jQMoX)526z84qUf3m1LpsB++w5KPx@ETYE@8Ii7hX|`RFmcd!gL}=uv=A^LLU`Z z3L`CYn%oJWL#6!MQ9R^ll}YFDtsJk&MA7fK6_I`o@z_=S zj*MMYL|c9`UyEYU<2{IZ{fS|!`rlHxj?wc&-CSE&&;`w`MY!qj1(dsR)bkPQX?#>jt{#-{&02D>vW+sYEAA1t0to zN?rGOptOFEA=#omk{$MloYxb@esunkwj)*LJ`d+PkvBe+MC#jsjwayIPx0u+x-qKy zr~rehbJaxb$KnrJbx`0zJbG?%WJ{|Af5CB66f4SwS)44M8XjD+8S+c4RJgfuaQX;vV z)kZ_cJ`WB0g-%#LiW+JVkm`&G$9eXiq%<4XqEzsyfqGQ7Y|eKv1s}0rUVTLKx>u-& zAIIazRmlPDx%LO(aT|;CE+{@AcY$L@{~QhX=n|Fxp+rZ8sXvPnjQaEJ7aE1&e&DYH zKV$z??s-!YIiK;YEMuhqK^Y?nFMcFd929XUAte4sO3U$C?JM-M!SP)J>GJ5u$u9S1e;rB zl2KmepiWnuv3Tk{o*MY|@Rps))l3G)py&k@jn5eBfP}}Aoi%ci5KjC7Go)5Hj3Y0p zmB^{?`K}H*Cf6N(_uhHTbkzQ1{Qa4~XNDb@@0p~Bxuop4@B*_`#0h695v+$!$hTK^ zF#2bA&0#ngEK#RXMcCnohDI|O_f0)HKWKXPt>%BYhz z*#n;QC0A_ z#*(jumHEz(IW4xeM#8x9H1PjZ?B((^BFSU9-&q;Ud2~5>R<2Ese9_kuIlogIzZQbw z;~VPbxXv&kt0%|XpjDpl_kA358kZwUMW9q`lIrD&1+kPK-zmb-Z`>lZe|qivMqDK5 z0>3rl5#P$EN1hJqz7;=7eA0JDJmou>YdM^D^nFAQ>ely02Om2>*zp|WN-p?b%8H@) zKQGWTW0#*73f9M|7Bo!0>f8nVgS7H{=A4E5~Y;fCMw zF6Ku$&b{0Cqx7zWs=HwH6+et}L{Av;-O}|Z35<61;7IqF9vr&?-eXCj5Z>~IrlBE} z$Dv$zw3E|7QufYwK1iDyh^PAfavX(1D%P(__JEw=smt}g;lOE+;PkMK&_^bG@Qu!4bp!oC5*sBsGzX+~h zm6?-UamRn975VWKb4?yU&TIbUHOVjS@Z&N0XL*nD^XEE7ytkO delta 8375 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