diff --git a/Makefile b/Makefile index 1f30d43..251cfd8 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # # EnergyMech, IRC Bot software -# Copyright (c) 1997-2009 proton +# Copyright (c) 1997-2018 proton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,8 +23,8 @@ MISCFILES = COPYING CREDITS README README.TCL TODO VERSIONS VERSIONS-1 Makefile HELPFILES = help/8BALL help/ACCESS help/ALIAS help/AWAY help/BAN help/BANLIST help/BYE \ help/CCHAN help/CHACCESS help/CHANBAN help/CHANNELS help/CHAT help/CLEARSHIT \ help/CMD help/CORE help/CSERV help/CTCP help/CYCLE help/DEOP help/DIE \ - help/DNS help/DNSSERVER help/DO help/DOWN help/ECHO help/ESAY help/FORGET \ - help/GREET help/HELP help/IDLE help/INSULT help/INVITE help/JOIN help/KB \ + help/DNS help/DNSSERVER help/DO help/DOWN help/ECHO help/ESAY help/FEATURES \ + help/FORGET help/GREET help/HELP help/IDLE help/INSULT help/INVITE help/JOIN help/KB \ help/KICK help/KS help/LAST help/LEVELS help/LINK help/LOAD help/LUSERS help/ME \ help/MODE help/MSG help/NAMES help/NEXTSERVER help/NICK help/NOTIFY help/ONTIME \ help/OP help/PART help/PASSWD help/PROTECTION help/PYTHON help/PYTHONSCRIPT \ @@ -87,6 +87,11 @@ clean: FORCE install: FORCE $(MAKE) -C src install +mega: FORCE + $(MAKE) -C src mega + +mega-install: FORCE + $(MAKE) -C src mega-install # # packing things up for distribution # diff --git a/README b/README index 9cf84a1..5a382cc 100644 --- a/README +++ b/README @@ -48,15 +48,15 @@ Setup? Read the sample.conf file to get an idea of the config file commands and then try to make your own. A basic setup doesnt need -much more than NICK, ALTNICK, SET USERFILE, JOIN and SERVER -entries, the rest is just tweaks of default values. +much more than NICK, SET ALTNICK, SET USERFILE, JOIN and SERVER +entries, the rest is mostly just tweaks of default values. Quick steps: 1) cp sample.conf mech.conf -2) pico mech.conf --- replace ''pico'' with your favourite text editor, look through +2) edit mech.conf +-- replace ''edit'' with your favourite text editor, look through the file for sections to change, you will have to remove lines in order to get the bot to work. Check the file completely! @@ -72,7 +72,7 @@ Quick steps: then 'run' this file with './energymech -f trick.conf'. this will create a userfile with the name you chose ('mech.passwd' - is a good descriptive name which I usually use myself). + is a good descriptive name which I often use myself). re-use the filename you selected in your proper configuration file. and remember to 'rm -f mech.session' if you compiled your @@ -95,6 +95,10 @@ Updated Files? ~~~~~~~~~~~~~~ The main distro-site for the EnergyMech is: + https://github.com/MadCamel/energymech + +Files, documentation and tips can be found at: + http://www.energymech.net ---*--- @@ -131,5 +135,4 @@ IT COMES TO CONFIGURING AND USING IT. ---*--- -proton, July 24th, 2009. - +joonicks, March 10th, 2018. diff --git a/VERSIONS b/VERSIONS index 471808e..553f60b 100644 --- a/VERSIONS +++ b/VERSIONS @@ -1,5 +1,7 @@ 3.0.99p4 -- WORK IN PROGRESS (~March, 2018) + * Fixed: RawDNS, again... + * Fixed: Save procvars only once in session file. * Added: Support for SHA password hashes. * Added: ESAY $cap will display compile time capabilities. * Fixed: Potential memory corruption bug in do_user(). diff --git a/config/endian.c b/config/endian.c new file mode 100644 index 0000000..6463a85 --- /dev/null +++ b/config/endian.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include +int main(int argc, char **argv) +{ + char x[4]; + *((int*)&x) = 1; + printf("%i\n",x[0]); + return(0); +} + diff --git a/configure b/configure index adc20f4..0f1f02d 100755 --- a/configure +++ b/configure @@ -584,6 +584,40 @@ esac rm -f $TESTP +# +# big/little endian +# + +TESTC=config/endian.c + +if [ -z "$endian" ]; then + endian=unknown + echo $ac_n "checking endianness ... "$ac_c + $CC -o $TESTP $TESTC 1> /dev/null 2> /dev/null + if [ -x $TESTP ]; then + endian=`$TESTP` + if [ "$endian" == "1" ]; then + endian="little" + else + endian="big" + fi + fi + echo $ac_t "$endian endian" +fi + +case "$endian" in + little ) + LIT_END="#define LITTLE_ENDIAN" + BIG_END="#undef BIG_ENDIAN" + ;; + * ) + LIT_END="#undef LITTLE_ENDIAN" + BIG_END="#define BIG_ENDIAN" + ;; +esac + +rm -f $TESTP + # # Some processors like Sparc, will cause a bus error (SIGBUS) if you try to access unaligned memory # @@ -1219,6 +1253,8 @@ s|@DEF_REDIRECT@|$def_redirect|; s|@DEF_WEB@|$def_web|; s|@DEF_WINGATE@|$def_wingate|; +s|@LIT_END@|$LIT_END|; +s|@BIG_END@|$BIG_END|; s|@DEF_CRYPT_FUNCTION@|$CRYPT_FUNCTION|; s|@CRYPT_HAS_SHA@|$CRYPT_HAS_SHA|; s|@CRYPT_HAS_MD5@|$CRYPT_HAS_MD5|; diff --git a/help/CORE b/help/CORE index 2d87987..992b516 100644 --- a/help/CORE +++ b/help/CORE @@ -1,4 +1,18 @@ Shows core information about the bot. - -See also: ver, uptime, ontime + +Current nick Nick of the bot you are connected to and its Guid. +Users in userlist Total number of userlist entries, as well as users with + userlevel access (0-100) and bot level access (200). +Active channels Channels that the bot is active on, as well as channels + that the bot is trying to rejoin. +Current server Which server the bot is connected to. +Server ontime How long the bot has been connected to the current server. +Mode User modes of the bot, like +i for invisible, etc. +Current time Time as reported by the machine that the bot is running on. +Started Time when the bot was first started. +Uptime How long (in days, hours, minutes and seconds) the bot has been running. +Version Source code version of the bot. +Features List of which features were compiled into the bot. + +See also: ver, uptime, ontime, features diff --git a/help/FEATURES b/help/FEATURES new file mode 100644 index 0000000..f83ef56 --- /dev/null +++ b/help/FEATURES @@ -0,0 +1,9 @@ +The Energymech contains a list of which features were included +when the source was compiled. + +You can view this list at the command prompt in your shell with +./(name of executable) -v + +Or, once the bot is started, with the command CORE or ESAY $cap + +See also: core, esay diff --git a/help/SETMODES b/help/SETMODES index c6c864b..d0d01d6 100644 --- a/help/SETMODES +++ b/help/SETMODES @@ -4,3 +4,6 @@ Number of MODE operations that can be sent to the IRC server in a single line. This value can be auto-detected on server connect if the bot is compiled with support for 005 replies and the server sends them (most big networks today). + +To support auto-detect of server capabilities, the energymech +needs to be compiled with the ircd-extensions feature enabled. diff --git a/help/SPY b/help/SPY index 56d062b..94fd002 100644 --- a/help/SPY +++ b/help/SPY @@ -17,6 +17,6 @@ excess flood if not careful. (none) Send output to you (default). channel Send output to the specified channel. >file Send output to file. Lines are appended to the end of the file. - If the file does not exist, it is created. + This file needs to exist before logging to it. See also: rspy diff --git a/help/USER b/help/USER index c3fb48a..3bc4612 100644 --- a/help/USER +++ b/help/USER @@ -21,6 +21,11 @@ Add/remove a host mask: Only one channel or one mask can be modified with each USER command. Prefix flags with a "-" to disable them or a "+" to enable them. + +Bots can be added to the userlist to be recognized by other bots. +Use the special access level 200. No password is set for bots and is +not needed because level 200 users can not execute any commands. +They can not access bots by telnet either. User flags: AO auto-op on/off diff --git a/public/README b/public/README new file mode 100644 index 0000000..34ce6f8 --- /dev/null +++ b/public/README @@ -0,0 +1 @@ +Store files here that you want to access with the SEND command. diff --git a/src/Makefile.in b/src/Makefile.in index d068768..2b7d5e1 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,6 +1,6 @@ # # EnergyMech, IRC bot software -# Copyright (c) 1997-2009 proton +# Copyright (c) 1997-2018 proton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -47,7 +47,7 @@ OFILES = alias.o auth.o bounce.o chanban.o channel.o core.o \ kicksay.o main.o net.o net_chan.o note.o notify.o ons.o parse.o \ perl.o prot.o python.o redirect.o reset.o seen.o shit.o socket.o \ spy.o stats.o tcl.o telnet.o toybox.o trivia.o uptime.o \ - user.o vars.o web.o @MD5_O@ + user.o vars.o web.o @MD5_O@ @SHA_O@ SRCFILES = alias.c auth.c bounce.c chanban.c channel.c core.c \ ctcp.c debug.c dns.c dynamode.c function.c greet.c help.c irc.c \ @@ -58,7 +58,12 @@ SRCFILES = alias.c auth.c bounce.c chanban.c channel.c core.c \ all: $(INSTALLNAME) -mcmd.h: gencmd.c config.h structs.h ; +# +# mcmd.h is generated at compile time to wrap up all command attributes more efficiently, +# instead of doing extra parsing and handling while the bot is running. +# + +mcmd.h: gencmd.c config.h structs.h $(CC) $(LFLAGS) -o gencmd gencmd.c ./gencmd > mcmd.h @@ -74,17 +79,24 @@ $(INSTALLNAME): $(OFILES) @oc@ $(CROSS_COMPILE)objcopy -R .note -R .comment $(INSTALLNAME) @sz@ size $(INSTALLNAME) -mega: $(SRCFILES) $(INCS) usage.h - $(CROSS_COMPILE)$(CC) $(CFLAGS) -o $(INSTALLNAME) mega.c $(LPROF) $(LIBS) $(LDSCRIPT) $(PYINCLUDE) $(TCLINCLUDE) +$(INSTALLNAME)-static: $(OFILES) + $(CROSS_COMPILE)$(CC) $(LFLAGS) -o $(INSTALLNAME) $(OFILES) $(LPROF) $(LIBS) $(LDSCRIPT) -static @oc@ $(CROSS_COMPILE)objcopy -R .note -R .comment $(INSTALLNAME) @sz@ size $(INSTALLNAME) + # -# static targets +# 'mega' versions +# compiling the C files all at once (having all the code "visible" during the whole process) enables GCC to make more optimizations, +# resulting in a smaller, more efficient, binary. this process uses lots more memory at compile time. # -$(INSTALLNAME)-static: $(OFILES) - $(CROSS_COMPILE)$(CC) $(LFLAGS) -o $(INSTALLNAME) $(OFILES) $(LPROF) $(LIBS) $(LDSCRIPT) -static +mega-install: mega $(SRCFILES) $(INCS) usage.h + $(CHMOD) $(INSTALLMODE) $(INSTALLNAME) + $(MV) $(INSTALLNAME) $(INSTALLDIR) + +mega: $(SRCFILES) $(INCS) usage.h + $(CROSS_COMPILE)$(CC) $(CFLAGS) -o $(INSTALLNAME) mega.c $(LPROF) $(LIBS) $(LDSCRIPT) $(PYINCLUDE) $(TCLINCLUDE) @oc@ $(CROSS_COMPILE)objcopy -R .note -R .comment $(INSTALLNAME) @sz@ size $(INSTALLNAME) @@ -220,4 +232,7 @@ web.o: web.c $(INCS) md5/md5.o: md5/md5.c $(INCS) $(CROSS_COMPILE)$(CC) $(CFLAGS) -c $< -o $@ -Imd5 $(CPROF) +sha/sha1.o: sha/sha1.c $(INCS) + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c $< -o $@ -Isha1 $(CPROF) + FORCE: diff --git a/src/config.h.in b/src/config.h.in index c718f3b..70bafe6 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -378,6 +378,15 @@ */ #define RANDOM(min,max) (min + (rand() / (RAND_MAX / (max - min + 1)))) +/* endianness */ +#ifndef LITTLE_ENDIAN +@LIT_END@ +#endif /* LITTLE_ENDIAN */ + +#ifndef BIG_ENDIAN +@BIG_END@ +#endif /* BIG_ENDIAN */ + /* unaligned memory access */ @UNALIGNED_MEM@ diff --git a/src/core.c b/src/core.c index 9e71ad5..d9ff40f 100644 --- a/src/core.c +++ b/src/core.c @@ -244,9 +244,9 @@ int write_session(void) */ for(j=0;VarName[j].name;j++) { - varval = &bot->setting[j]; if (IsProc(j)) - varval = varval->proc_var; + continue; + varval = &bot->setting[j]; if (IsChar(j)) { if (VarName[j].v.num != varval->char_var) @@ -1208,8 +1208,10 @@ void do_core(COMMAND_ARGS) } i = Strcmp(current->nick,current->wantnick); - table_buffer((i) ? TEXT_CURRNICKWANT : TEXT_CURRNICKHAS,current->nick,current->wantnick); - table_buffer(TEXT_CURRGUID,current->guid); + if (i) + table_buffer(TEXT_CURRNICKWANT,current->nick,current->wantnick,current->guid); + else + table_buffer(TEXT_CURRNICKHAS,current->nick,current->guid); table_buffer(TEXT_USERLISTSTATS,u,su,EXTRA_CHAR(su),bu,EXTRA_CHAR(bu)); pt = tmp; @@ -1382,12 +1384,12 @@ void do_server(COMMAND_ARGS) ServerGroup *sg; Server *sp,*dp,**spp; char *server,*aport,*pass; - char addc,*last,*quitmsg = "Trying new server, brb..."; + char addc,*last,*quitmsg = TEXT_TRYNEWSERVER; int n,iport,sgi; if (CurrentCmd->name == C_NEXTSERVER) { - quitmsg = "Switching servers..."; + quitmsg = TEXT_SWITCHSERVER; to_user(from,FMT_PLAIN,quitmsg); goto do_server_jump; } @@ -1615,7 +1617,7 @@ void do_nick(COMMAND_ARGS) return; } guid = a2i(nick); - backup = current; + backup = current; if (!errno) { nick = chop(&rest); diff --git a/src/defines.h b/src/defines.h index 61bcca0..3ccf59e 100644 --- a/src/defines.h +++ b/src/defines.h @@ -303,6 +303,7 @@ enum { #define BNAUTH_PLAINTEXT 0 #define BNAUTH_DES 1 #define BNAUTH_MD5 2 +#define BNAUTH_SHA 3 #endif /* BOTNET */ diff --git a/src/dns.c b/src/dns.c index 2a78156..5de5f55 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1,7 +1,7 @@ /* EnergyMech, IRC bot software - Copyright (c) 1997-2009 proton + Copyright (c) 1997-2018 proton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -64,25 +64,7 @@ LS int dnsserver = 0; #ifdef DEBUG char *type_textlist[] = -{ -NULL, -"A", -"NS", -"MD", -"MF", -"CNAME", -"SOA", -"MB", -"MG", -"MR", -"NULL", -"WKS", -"PTR", -"HINFO", -"MINFO", -"MX", -"TXT", -}; +{ NULL, "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", }; #endif /* DEBUG */ void init_rawdns(void) @@ -164,17 +146,17 @@ const char *get_dns_token(const char *src, const char *packet, char *dst, int sz int make_query(char *packet, const char *hostname) { + dnsQuery *h; char *size,*dst; /* * make a packet */ - packet[0] = rand() >> 24; - packet[1] = rand() >> 24; - packet[2] = 1; // RD, recursion desired flag - packet[3] = 0; - ((ulong*)packet)[1] = htonl(0x10000); - ((ulong*)packet)[2] = 0; + memset(packet,0,12); + h = (dnsQuery*)packet; + h->qid = rand() & 0xffff; + h->flags = htons(0x0100);; // Query = 0, Recursion Desired = 1 + h->questions = htons(1); size = packet + 12; dst = size + 1; while(*hostname) @@ -932,7 +914,10 @@ flipstep: return; } - make_ireq(PA_DNS,from,host); + if (to && *to == '#') + make_ireq(PA_DNS,to,host); + else + make_ireq(PA_DNS,from,host); rawdns(host); } diff --git a/src/net.c b/src/net.c index 9318e7c..2d690de 100644 --- a/src/net.c +++ b/src/net.c @@ -30,12 +30,18 @@ #include "text.h" #include "mcmd.h" -#ifdef MD5CRYPT -#define md5banneropt " MD5" +#if defined(SHACRYPT) || defined(MD5CRYPT) char *CRYPT_FUNC(const char *, const char *); -#else -#define md5banneropt +#endif + +const char banneropt[] = "BB%i %i PTA" +#ifdef SHACRYPT + " SHA" +#endif /* SHACRYPT */ +#ifdef MD5CRYPT + " MD5" #endif /* MD5CRYPT */ + "\n"; #ifdef TELNET char *telnetprompt = TEXT_ENTERNICKNAME; @@ -307,6 +313,10 @@ void basicAuth(BotNet *bn, char *rest) { if (!Strcmp(pass,"PTA")) authtype = BNAUTH_PLAINTEXT; +#ifdef SHACRYPT + if (!Strcmp(pass,"SHA")) + authtype = BNAUTH_SHA; +#endif /* SHACRYPT */ #ifdef MD5CRYPT if (!Strcmp(pass,"MD5")) authtype = BNAUTH_MD5; @@ -334,6 +344,26 @@ void basicAuth(BotNet *bn, char *rest) if (Strcmp(pass,rest)) goto badpass; break; +#ifdef SHACRYPT + case BNAUTH_SHA: + if (linkpass && *linkpass) + { + char *enc,temppass[24 + Strlen2(pass,linkpass)]; // linkpass is never NULL + + /* "mypass theirpass REMOTEsid LOCALsid" */ + sprintf(temppass,"%s %s %i %i",linkpass,pass,bn->rsid,bn->lsid); +#ifdef DEBUG + debug(">> sha pass exchange: \"%s\"\n",temppass); +#endif /* DEBUG */ + enc = CRYPT_FUNC(temppass,rest); +#ifdef DEBUG + debug("(basicAuth) their = %s, mypass = %s :: sha = %s\n", + pass,linkpass,enc); +#endif /* DEBUG */ + if (!Strcmp(enc,rest)) + break; + } +#endif /* SHACRYPT */ #ifdef MD5CRYPT case BNAUTH_MD5: if (linkpass && *linkpass) @@ -492,6 +522,10 @@ void basicBanner(BotNet *bn, char *rest) { if (!Strcmp(p,"PTA")) bn->opt.pta = TRUE; +#ifdef SHACRYPT + if (!Strcmp(p,"SHA")) + bn->opt.sha = TRUE; +#endif /* SHACRYPT */ #ifdef MD5CRYPT if (!Strcmp(p,"MD5")) bn->opt.md5 = TRUE; @@ -510,7 +544,7 @@ void basicBanner(BotNet *bn, char *rest) if (bn->status == BN_UNKNOWN) { bn->controller = netbot = get_netbot(); - to_file(bn->sock,"BB%i %i PTA" md5banneropt "\n",netbot->guid,bn->lsid); + to_file(bn->sock,banneropt,netbot->guid,bn->lsid); bn->status = BN_WAITAUTH; return; } @@ -533,12 +567,37 @@ void basicBanner(BotNet *bn, char *rest) if (bn->opt.md5 && (BNAUTH_MD5 > authtype)) authtype = BNAUTH_MD5; #endif /* MD5CRYPT */ +#ifdef SHACRYPT + if (bn->opt.sha && (BNAUTH_SHA > authtype)) + authtype = BNAUTH_SHA; +#endif /* SHACRYPT */ switch(authtype) { case BNAUTH_PLAINTEXT: to_file(bn->sock,"BAPTA %s\n",linkpass); break; +#ifdef SHACRYPT + case BNAUTH_SHA: + if ((cfg = find_netcfg(guid))) + { + if (cfg->pass && *cfg->pass) + { + char *enc,salt[8]; + char temppass[24 + Strlen2(cfg->pass,linkpass)]; // linkpass(procvar) is not NULL + + /* "theirpass mypass LOCALsid REMOTEsid" */ + sprintf(temppass,"%s %s %i %i",cfg->pass,linkpass,bn->lsid,bn->rsid); +#ifdef DEBUG + debug(">> sha pass exchange: \"%s\"\n",temppass); +#endif /* DEBUG */ + sprintf(salt,"$6$%04x",(rand() >> 16)); + enc = CRYPT_FUNC(temppass,salt); + to_file(bn->sock,"BASHA %s\n",enc); + break; + } + } +#endif /* SHACRYPT */ #ifdef MD5CRYPT case BNAUTH_MD5: if ((cfg = find_netcfg(guid))) @@ -1320,7 +1379,7 @@ void process_botnet(void) { bn->lsid = rand(); bn->controller = netbot = get_netbot(); - if (to_file(bn->sock,"BB%i %i PTA" md5banneropt "\n",netbot->guid,bn->lsid) < 0) + if (to_file(bn->sock,banneropt,netbot->guid,bn->lsid) < 0) { botnet_deaduplink(bn); } diff --git a/src/spy.c b/src/spy.c index 3daf563..3af27cd 100644 --- a/src/spy.c +++ b/src/spy.c @@ -163,8 +163,8 @@ struct { char *idstring; int typenum; - -} spy_source_list[] = + +} spy_source_list[] = { { SPYSTR_STATUS, SPY_STATUS }, { SPYSTR_MESSAGE, SPY_MESSAGE }, @@ -471,7 +471,15 @@ rspy_usage: * Dont just open anything. */ if (!is_safepath(dest)) +#ifdef NEWBIE + { + usage(from); + to_user(from,"File/path does not exist or is inaccessible"); + return; + } +#else goto rspy_usage; +#endif /* NEWBIE */ t_dest = SPY_FILE; goto rspy_dest_ok; } diff --git a/src/structs.h b/src/structs.h index 80203ef..37b31b8 100644 --- a/src/structs.h +++ b/src/structs.h @@ -668,8 +668,9 @@ typedef struct BotNet struct { - ulong pta:1; /* plain text auth */ - ulong md5:1; /* md5 */ + ulong pta:1, /* plain text auth */ + sha:1, /* SHA */ + md5:1; /* MD5 */ } opt; diff --git a/src/text.h b/src/text.h index 8188d8e..ce01396 100644 --- a/src/text.h +++ b/src/text.h @@ -1,7 +1,7 @@ /* EnergyMech, IRC bot software - Copyright (c) 2000-2009 proton + Copyright (c) 2000-2018 proton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -74,9 +74,8 @@ #define TEXT_SERVERDELETED "Server has been deleted: %s:%i" #define TEXT_MANYSERVMATCH "Several entries for %s exists, please specify port also" /* do_core() */ -#define TEXT_CURRNICKWANT "Current nick\t%s (Wanted: %s)" -#define TEXT_CURRNICKHAS "Current nick\t%s" -#define TEXT_CURRGUID "Guid\t%i" +#define TEXT_CURRNICKWANT "Current nick\t%s (Wanted: %s) [#%i]" +#define TEXT_CURRNICKHAS "Current nick\t%s [#%i]" #define TEXT_USERLISTSTATS "Users in userlist\t%i (%i Superuser%s, %i Bot%s)" #define TEXT_ACTIVECHANS "Active channels\t%s" #define TEXT_MOREACTIVECHANS "\t%s" @@ -87,6 +86,8 @@ #define TEXT_CURRSERVER "Current Server\t%s:%i" #define TEXT_CURRSERVERNOT "Current Server\t" TEXT_NOTINSERVLIST +#define TEXT_TRYNEWSERVER "Trying new server, brb..." +#define TEXT_SWITCHSERVER "Switching servers..." #define TEXT_SERVERONTIME "Server Ontime\t%s" #define TEXT_BOTMODES "Mode\t+%s"