From f817ac199b132173c1660bccc359f8be6633db5b Mon Sep 17 00:00:00 2001 From: S3x0r Date: Tue, 17 Jan 2023 17:36:08 +0100 Subject: [PATCH] 1.1.7 - From now on, you can create 1-998 permissions + normaln user (999) and 'owner' (0) - The entitlement structure changed, As of now The owner directory can have any name but it is important that it must be in configuration file ([USERSLEVELS]) 0 also name must be added in PRIVILEGES. A normal user has userlevel 999 and can only see own commands user levels from (1) to (998) may be assigned other privileges The -lower- number has access to all -higher- level commands eg. if 'owner' is (0), 'admin' (1), 'someone else' (2), 'normal user' (999) 'admin' can use own commands + 'someone else' commands + 'normal user' commands. 'someone else' can use own commands + 'normal user' commands, etc,. - fixed: timezone from config was not set - fixed: wrong time in log filename - fixed: there was missing '' in config file when changing default password - NEW PLUGIN: 'whoami' - Displays user assigned name and privilege level - plugin: 'ban' - now saves ban to ban list in config - new argument '-n', you can now set bot nickname from arg - new logs filename format: 'date-computer name' - bot prints channel user(s) on bot join - added files 'core_cmnds_helpers.php', 'on_numeric.php', 'on_word.php' and changed 'core_commands' to 'core_cmnds' - REMOVED PLUGINS: 'showconfig', 'save', 'addadmin', 'listadmins', 'remadmin', 'panel', 'plugin' plugins will return in future. - disabled for now 'fetch' plugin - removed files: 'debug.php', 'web.php' - code cleanup --- BOT.php | 20 +- CONFIG.INI | 66 +-- DOCS/CHANGELOG.TXT | 27 ++ DOCS/CLI Arguments.txt | 1 + DOCS/config.ini info.txt | 8 - PLUGINS/ADMIN/autoop.php | 30 +- PLUGINS/ADMIN/ban.php | 26 +- PLUGINS/ADMIN/cham.php | 4 +- PLUGINS/ADMIN/deop.php | 6 +- PLUGINS/ADMIN/devoice.php | 6 +- PLUGINS/ADMIN/gethost.php | 16 +- PLUGINS/ADMIN/kick.php | 6 +- PLUGINS/ADMIN/op.php | 4 +- PLUGINS/ADMIN/ping.php | 12 +- PLUGINS/ADMIN/ripe.php | 6 +- PLUGINS/ADMIN/say.php | 4 +- PLUGINS/ADMIN/topic.php | 2 +- PLUGINS/ADMIN/unban.php | 4 +- PLUGINS/ADMIN/uptime.php | 2 +- PLUGINS/ADMIN/voice.php | 4 +- PLUGINS/OWNER/addadmin.php | 61 --- PLUGINS/OWNER/addowner.php | 55 +-- PLUGINS/OWNER/checkupdate.php | 29 +- PLUGINS/OWNER/cluster.php | 12 +- PLUGINS/OWNER/fetch.php | 33 +- PLUGINS/OWNER/info.php | 4 +- PLUGINS/OWNER/join.php | 4 +- PLUGINS/OWNER/leave.php | 4 +- PLUGINS/OWNER/listowners.php | 8 +- PLUGINS/OWNER/memusage.php | 3 +- PLUGINS/OWNER/newnick.php | 8 +- PLUGINS/OWNER/plugin.php | 120 ----- PLUGINS/OWNER/quit.php | 6 +- PLUGINS/OWNER/raw.php | 4 +- PLUGINS/OWNER/remadmin.php | 63 --- PLUGINS/OWNER/remowner.php | 19 +- PLUGINS/OWNER/restart.php | 6 +- PLUGINS/OWNER/save.php | 337 -------------- PLUGINS/OWNER/server.php | 18 +- PLUGINS/OWNER/showconfig.php | 99 ----- PLUGINS/OWNER/update.php | 93 +--- PLUGINS/OWNER/winamp.php | 4 +- PLUGINS/USER/hash.php | 12 +- PLUGINS/USER/help.php | 84 ++-- PLUGINS/USER/md5.php | 2 +- PLUGINS/USER/morse.php | 3 +- PLUGINS/USER/note.php | 22 +- PLUGINS/USER/webstatus.php | 4 +- PLUGINS/USER/webtitle.php | 4 +- PLUGINS/USER/wiki.php | 4 +- .../listadmins.php => plugins/USER/whoami.php | 25 +- readme.md | 175 +++----- src/args.php | 36 +- src/cli.php | 33 +- src/config.php | 254 ++++------- src/core_cmnds.php | 201 +++++++++ src/{web.php => core_cmnds_helpers.php} | 50 ++- src/core_commands.php | 225 ---------- src/ctcp.php | 37 +- src/debug.php | 95 ---- src/define.php | 15 +- src/events.php | 418 +----------------- src/logs.php | 9 +- src/misc.php | 91 +--- src/on_numeric.php | 265 +++++++++++ src/on_word.php | 220 +++++++++ src/panel/cfg/cgi.txt | 1 - src/panel/cfg/forbidden | 0 src/panel/cfg/init.txt | 7 - src/panel/cfg/mime.txt | 63 --- src/panel/cfg/speed.txt | 8 - src/panel/cfg/vdir.txt | 1 - src/panel/forbidden | 0 src/panel/serv.exe | Bin 162816 -> 0 bytes src/panel/web/api.php | 238 ---------- src/panel/web/index.php | 92 ---- src/panel/web/logout.php | 16 - src/panel/web/logs.php | 70 --- src/panel/web/main.php | 75 ---- src/panel/web/plugins.php | 65 --- src/panel/web/src/bullet.png | Bin 989 -> 0 bytes src/panel/web/src/link.png | Bin 911 -> 0 bytes src/panel/web/src/logo.png | Bin 21963 -> 0 bytes src/panel/web/src/style.css | 292 ------------ src/panel/web/src/transparent.png | Bin 199 -> 0 bytes src/panel/web/src/transparent_light.png | Bin 147 -> 0 bytes src/plugins.php | 379 ++++++++-------- src/socket.php | 253 +++++++---- src/start.php | 136 +++--- src/timers.php | 6 +- 90 files changed, 1755 insertions(+), 3475 deletions(-) delete mode 100644 PLUGINS/OWNER/addadmin.php delete mode 100644 PLUGINS/OWNER/plugin.php delete mode 100644 PLUGINS/OWNER/remadmin.php delete mode 100644 PLUGINS/OWNER/save.php delete mode 100644 PLUGINS/OWNER/showconfig.php rename PLUGINS/OWNER/listadmins.php => plugins/USER/whoami.php (72%) create mode 100644 src/core_cmnds.php rename src/{web.php => core_cmnds_helpers.php} (55%) delete mode 100644 src/core_commands.php delete mode 100644 src/debug.php create mode 100644 src/on_numeric.php create mode 100644 src/on_word.php delete mode 100644 src/panel/cfg/cgi.txt delete mode 100644 src/panel/cfg/forbidden delete mode 100644 src/panel/cfg/init.txt delete mode 100644 src/panel/cfg/mime.txt delete mode 100644 src/panel/cfg/speed.txt delete mode 100644 src/panel/cfg/vdir.txt delete mode 100644 src/panel/forbidden delete mode 100644 src/panel/serv.exe delete mode 100644 src/panel/web/api.php delete mode 100644 src/panel/web/index.php delete mode 100644 src/panel/web/logout.php delete mode 100644 src/panel/web/logs.php delete mode 100644 src/panel/web/main.php delete mode 100644 src/panel/web/plugins.php delete mode 100644 src/panel/web/src/bullet.png delete mode 100644 src/panel/web/src/link.png delete mode 100644 src/panel/web/src/logo.png delete mode 100644 src/panel/web/src/style.css delete mode 100644 src/panel/web/src/transparent.png delete mode 100644 src/panel/web/src/transparent_light.png diff --git a/BOT.php b/BOT.php index 97660d9..a20f376 100644 --- a/BOT.php +++ b/BOT.php @@ -25,7 +25,14 @@ /* simple os check */ /* PHP 7.2.0 we have PHP_OS_FAMILY */ - strtoupper(substr(PHP_OS, 0, 3)) != 'WIN' ? $GLOBALS['OS'] = 'Linux' : false; + function ifWindowsOs() + { + if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { + return true; + } else { + return false; + } + } /* hide prompt */ echo "\e[?25l"; @@ -36,14 +43,15 @@ 'cli.php', 'misc.php', 'config.php', - 'core_commands.php', - 'debug.php', + 'core_cmnds.php', + 'core_cmnds_helpers.php', 'events.php', 'logs.php', 'plugins.php', 'socket.php', + 'on_numeric.php', + 'on_word.php', 'timers.php', - 'web.php', 'ctcp.php', 'start.php' ]; @@ -58,7 +66,7 @@ " You can download missing files from:\n", " https://github.com/S3x0r/MINION/releases\n\n", " Terminating program after 10 seconds.\n\n"; - !isset($GLOBALS['os']) ? sleep(10) : false; + (ifWindowsOs()) ? sleep(10) : false; exit; } } @@ -69,5 +77,5 @@ WinSleep(7); exit; } else { - Start(); + StartBot(); } diff --git a/CONFIG.INI b/CONFIG.INI index f6c9128..4ed7cbc 100644 --- a/CONFIG.INI +++ b/CONFIG.INI @@ -1,4 +1,4 @@ -; + [BOT] @@ -13,42 +13,50 @@ ident = 'minion' [SERVER] -; server where to connect -server = 'irc.dal.net' +; irc server +server = 'irc.pirc.pl' -; server port +; irc server port port = '6667' -; if irc server have password +; If the irc server requires a password to connect, set it here server.password = '' -; try connect 'n' (in seconds) times to server, if cannot then quit +; Try to connect to the server the amount set here, if after a certain amount the bot fails to connect then terminate the program try.connect = '99' -; delay (in seconds) after new connection to server +; Pause (seconds) between connections to the server connect.delay = '6' -; show message of the day -show.motd = 'yes' +; show server message of the day +show.motd = 'no' [OWNER] ; bot administrator information -bot.admin = 'S3x0r ' +bot.admin = 'minion ' -; bot will give op's if this hosts join channel -auto.op.list = '' - -; BOT OWNER HOSTS -bot.owners = '' - -; owner password (SHA256) +; owner bot password (SHA256) owner.password = '47a8f9b32ec41bd93d79bf6c1c924aaecaa26d9afe88c39fc3a638f420f251ed' -[ADMIN] +[PRIVILEGES] -; bot admin list -admin.list = '' +; BOT OWNER HOSTS +OWNER = '' +; other users hosts +ADMIN = '' +; other users hosts +USER = '' + +[USERSLEVELS] + +;users access levels +; Owner = 0 +; Normal User = 999 +; others from 1 to 998 +OWNER = '0' +ADMIN = '1' +USER = '999' [RESPONSE] @@ -60,6 +68,9 @@ bot.response = 'notice' ; bot will give op when join to channel from auto.op.list: 'yes', 'no' auto.op = 'yes' +; bot will give op when hosts join channel +auto.op.list = '' + ; bot will auto rejoin channel when kicked: 'yes', 'no' auto.rejoin = 'yes' @@ -99,10 +110,10 @@ command.prefix = '!' ctcp.response = 'yes' ; ctcp version response (please do not change it:) -ctcp.version = 'MINION (1.1.6) powered by minions!' +ctcp.version = 'minion (1.1.7) powered by minions!' ; ctcf finger response -ctcp.finger = 'MINION' +ctcp.finger = 'minion' [DELAYS] @@ -120,14 +131,6 @@ notice.delay = '1' ; log CLI messages to LOGS folder? 'yes', 'no' logging = 'yes' -[PANEL] - -; web panel login -web.login = 'changeme' - -; web panel password -web.password = 'changeme' - [TIME] ; bot time zone @@ -147,4 +150,5 @@ play.sounds = 'yes' ; show raw output on CLI window show.raw = 'no' -show.own.messages.in.raw.mode = 'no' \ No newline at end of file +show.own.messages.in.raw.mode = 'no' +show.debug = 'no' diff --git a/DOCS/CHANGELOG.TXT b/DOCS/CHANGELOG.TXT index 2d5745e..468776a 100644 --- a/DOCS/CHANGELOG.TXT +++ b/DOCS/CHANGELOG.TXT @@ -1,4 +1,31 @@ + v1.1.7 changes (17.01.2023): + - From now on, you can create 1-998 permissions + normaln user (999) and 'owner' (0) + - The entitlement structure changed, As of now The owner directory can have any name + but it is important that it must be in configuration file ([USERSLEVELS]) 0 + also name must be added in PRIVILEGES. + A normal user has userlevel 999 and can only see own commands + user levels from (1) to (998) may be assigned other privileges + The -lower- number has access to all -higher- level commands + eg. if 'owner' is (0), 'admin' (1), 'someone else' (2), 'normal user' (999) + 'admin' can use own commands + 'someone else' commands + 'normal user' commands. + 'someone else' can use own commands + 'normal user' commands, etc,. + - fixed: timezone from config was not set + - fixed: wrong time in log filename + - fixed: there was missing '' in config file + when changing default password + - NEW PLUGIN: 'whoami' - Displays user assigned name and privilege level + - plugin: 'ban' - now saves ban to ban list in config + - new argument '-n', you can now set bot nickname from arg + - new logs filename format: 'date-computer name' + - bot prints channel user(s) on bot join + - added files 'core_cmnds_helpers.php', 'on_numeric.php', 'on_word.php' and changed 'core_commands' to 'core_cmnds' + - REMOVED PLUGINS: 'showconfig', 'save', 'addadmin', 'listadmins', 'remadmin', 'panel', 'plugin' + plugins will return in future. + - disabled for now 'fetch' plugin + - removed files: 'debug.php', 'web.php' + - code cleanup + v1.1.6 changes (28.12.2022): - fixed reconnect delay time - new option in config: 'show_own_messages_in_raw_mode' diff --git a/DOCS/CLI Arguments.txt b/DOCS/CLI Arguments.txt index fa256ba..fac8bfc 100644 --- a/DOCS/CLI Arguments.txt +++ b/DOCS/CLI Arguments.txt @@ -5,6 +5,7 @@ -c # loads config -h # this help + -n # set bot nickname -o # connect to specified server: eg: BOT.php irc.dal.net 6667 -p # hash password to SHA256 -u # check if there is new bot version on server diff --git a/DOCS/config.ini info.txt b/DOCS/config.ini info.txt index d7c1458..f60e32f 100644 --- a/DOCS/config.ini info.txt +++ b/DOCS/config.ini info.txt @@ -115,14 +115,6 @@ notice_delay = '1' ; log CLI messages to LOGS folder? 'yes', 'no' logging = 'yes' -[PANEL] - -; web panel login (for panel plugin) -web_login = 'changeme' - -; web panel password (for panel plugin) -web_password = 'changeme' - [TIME] ; bot time zone diff --git a/PLUGINS/ADMIN/autoop.php b/PLUGINS/ADMIN/autoop.php index 1f2743e..5b327a6 100644 --- a/PLUGINS/ADMIN/autoop.php +++ b/PLUGINS/ADMIN/autoop.php @@ -21,33 +21,31 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Adds host to autoop list in config file: {$GLOBALS['CONFIG.CMD.PREFIX']}autoop "; + $plugin_description = "Adds host to autoop list in config file: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."autoop "; $plugin_command = 'autoop'; function plugin_autoop() { - $nick = explode('!', trim($GLOBALS['args'])); + /* get nick from hostname mask */ + $hostNick = explode('!', trim(msgAsArguments())); + + response($hostNick); if (OnEmptyArg('autoop ')) { - } elseif ($nick[0] != getBotNickname()) { - if (preg_match('/^(.+?)!(.+?)@(.+?)$/', $GLOBALS['args'], $host)) { - LoadData($GLOBALS['configFile'], 'OWNER', 'auto.op.list'); + } elseif ($hostNick[0] != getBotNickname()) { + if (preg_match('/^(.+?)!(.+?)@(.+?)$/', msgAsArguments())) { + $autoOpList = loadValueFromConfigFile('AUTOMATIC', 'auto.op.list'); - if (strpos($GLOBALS['LOADED'], $GLOBALS['args']) !== false) { - response('I already have this host.'); + if (strpos($autoOpList, msgAsArguments()) !== false) { + response('I already have that host in my auto op list.'); } else { - empty($GLOBALS['LOADED']) ? $new_list = $host[0] : $new_list = "{$GLOBALS['LOADED']}, {$host[0]}"; + empty($autoOpList) ? $newList = msgAsArguments() : $newList = "{$autoOpList}, ".msgAsArguments(); - SaveData($GLOBALS['configFile'], 'OWNER', 'auto.op.list', $new_list); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get("OWNER", "auto.op.list"); + SaveValueToConfigFile('AUTOMATIC', 'auto.op.list', $newList); - /* Inform nick about it */ - privateMsg('From now you are on my auto op list, enjoy.'); + privateMsgTo($hostNick[0], 'From now you are on my auto op list, enjoy.'); - response("Host: '{$host[0]}' added to auto op list."); + response("Host: '".msgAsArguments()."' added to auto op list."); } } else { response('Bad input, try: nick!ident@hostname'); diff --git a/PLUGINS/ADMIN/ban.php b/PLUGINS/ADMIN/ban.php index 22663fc..aa5e2f3 100644 --- a/PLUGINS/ADMIN/ban.php +++ b/PLUGINS/ADMIN/ban.php @@ -21,20 +21,30 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Ban specified host: {$GLOBALS['CONFIG.CMD.PREFIX']}ban "; + $plugin_description = "Ban specified host: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."ban "; $plugin_command = 'ban'; function plugin_ban() { - if (OnEmptyArg('ban ')) { + if (OnEmptyArg('ban ')) { } elseif(BotOpped() == true) { - $nickToBan = explode('!', trim($GLOBALS['args'])); + $nickToBan = explode('!', trim(msgAsArguments())); $nickToBan = $nickToBan[0]; - if ($nickToBan != getBotNickname() && $nickToBan != $GLOBALS['USER']) { - toServer("MODE ".getBotChannel()." +b {$GLOBALS['args']}"); - } + if ($nickToBan != getBotNickname() && $nickToBan != userPreg()[0]) { + /* ban host */ + toServer("MODE ".getBotChannel()." +b ".msgAsArguments()); + + /* save host to ban list */ + $banList = loadValueFromConfigFile('BANS', 'ban.list'); + + if (strpos($banList, msgAsArguments()) === false) { + empty($banList) ? $newList = $host[0] : $newList = "{$banList}, ".msgAsArguments(); + + SaveValueToConfigFile('BANS', 'ban.list', $newList); - unset($nickToBan); + response("Host: '".msgAsArguments()."' added to ban list."); + } + } } -} +} \ No newline at end of file diff --git a/PLUGINS/ADMIN/cham.php b/PLUGINS/ADMIN/cham.php index cb79a01..6d49667 100644 --- a/PLUGINS/ADMIN/cham.php +++ b/PLUGINS/ADMIN/cham.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows random text from file: {$GLOBALS['CONFIG.CMD.PREFIX']}cham "; + $plugin_description = "Shows random text from file: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."cham "; $plugin_command = 'cham'; /* @@ -45,7 +45,7 @@ function plugin_cham() shuffle($texts); $text = $texts[$count++]; - $who = trim($GLOBALS['args']); + $who = trim(msgAsArguments()); response("{$who}: {$text}"); } else { diff --git a/PLUGINS/ADMIN/deop.php b/PLUGINS/ADMIN/deop.php index 83930f5..568d7cf 100644 --- a/PLUGINS/ADMIN/deop.php +++ b/PLUGINS/ADMIN/deop.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Deops someone: {$GLOBALS['CONFIG.CMD.PREFIX']}deop "; + $plugin_description = "Deops someone: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."deop "; $plugin_command = 'deop'; function plugin_deop() { if (OnEmptyArg('deop ')) { - } elseif (BotOpped() == true && $GLOBALS['args'] != getBotNickname() && $GLOBALS['args'] != $GLOBALS['USER']) { - toServer("MODE ".getBotChannel()." -o {$GLOBALS['args']}"); + } elseif (BotOpped() == true && msgAsArguments() != getBotNickname() && msgAsArguments() != userPreg()[0]) { + toServer("MODE ".getBotChannel()." -o ".msgAsArguments()); } } diff --git a/PLUGINS/ADMIN/devoice.php b/PLUGINS/ADMIN/devoice.php index 13c35fa..b63ffd7 100644 --- a/PLUGINS/ADMIN/devoice.php +++ b/PLUGINS/ADMIN/devoice.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Devoice user: {$GLOBALS['CONFIG.CMD.PREFIX']}devoice "; + $plugin_description = "Devoice user: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."devoice "; $plugin_command = 'devoice'; function plugin_devoice() { if (OnEmptyArg('devoice ')) { - } elseif (BotOpped() == true && $GLOBALS['args'] != getBotNickname() && $GLOBALS['args'] != $GLOBALS['USER']) { - toServer("MODE ".getBotChannel()." -v {$GLOBALS['args']}"); + } elseif (BotOpped() == true && msgAsArguments() != getBotNickname() && msgAsArguments() != userPreg()[0]) { + toServer("MODE ".getBotChannel()." -v ".msgAsArguments()); } } diff --git a/PLUGINS/ADMIN/gethost.php b/PLUGINS/ADMIN/gethost.php index 71102c7..086dc13 100644 --- a/PLUGINS/ADMIN/gethost.php +++ b/PLUGINS/ADMIN/gethost.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Ip address to hostname change: {$GLOBALS['CONFIG.CMD.PREFIX']}gethost "; + $plugin_description = "Ip address to hostname change: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."gethost "; $plugin_command = 'gethost'; /* @@ -33,16 +33,16 @@ function plugin_gethost() { if (OnEmptyArg('gethost ')) { } else { - $host = @gethostbyaddr(trim($GLOBALS['args'])); + $host = @gethostbyaddr(trim(msgAsArguments())); if (!empty($host) && - $host != $GLOBALS['args'] && - substr($GLOBALS['args'], 0, 3) != '127' && - substr($GLOBALS['args'], 0, 3) != '192' && - substr($GLOBALS['args'], 0, 1) != '0') { + $host != msgAsArguments() && + substr(msgAsArguments(), 0, 3) != '127' && + substr(msgAsArguments(), 0, 3) != '192' && + substr(msgAsArguments(), 0, 1) != '0') { response("hostname: $host"); - } elseif ($host == $GLOBALS['args']) { - response("Cannot resolve {$GLOBALS['args']}"); + } elseif ($host == msgAsArguments()) { + response("Cannot resolve ".msgAsArguments()); } elseif (empty($host)) { response('Address is not a valid IPv4/IPv6 address'); } diff --git a/PLUGINS/ADMIN/kick.php b/PLUGINS/ADMIN/kick.php index f9e71c6..15b594f 100644 --- a/PLUGINS/ADMIN/kick.php +++ b/PLUGINS/ADMIN/kick.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Kicks from channel: {$GLOBALS['CONFIG.CMD.PREFIX']}kick <#channel> "; + $plugin_description = "Kicks from channel: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."kick <#channel> "; $plugin_command = 'kick'; function plugin_kick() { if (OnEmptyArg('kick <#channel> ')) { - } elseif (BotOpped() == true && $GLOBALS['piece2'] != getBotNickname() && $GLOBALS['piece2'] != $GLOBALS['USER']) { - toServer("KICK {$GLOBALS['piece1']} :{$GLOBALS['piece2']}"); + } elseif (BotOpped() == true && msgPieces()[1] != getBotNickname() && msgPieces()[1] != userPreg()[0]) { + toServer("KICK ".msgPieces()[0]." :".msgPieces()[1]); } } diff --git a/PLUGINS/ADMIN/op.php b/PLUGINS/ADMIN/op.php index 8cad816..7d2b19e 100644 --- a/PLUGINS/ADMIN/op.php +++ b/PLUGINS/ADMIN/op.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Gives op: {$GLOBALS['CONFIG.CMD.PREFIX']}op "; + $plugin_description = "Gives op: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."op "; $plugin_command = 'op'; function plugin_op() { if (OnEmptyArg('op ')) { } elseif (BotOpped() == true) { - toServer("MODE ".getBotChannel()." +o {$GLOBALS['args']}"); + toServer("MODE ".getBotChannel()." +o ".msgAsArguments()); } } diff --git a/PLUGINS/ADMIN/ping.php b/PLUGINS/ADMIN/ping.php index 410b6e8..98a36d8 100644 --- a/PLUGINS/ADMIN/ping.php +++ b/PLUGINS/ADMIN/ping.php @@ -21,22 +21,22 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Pings host/ip: {$GLOBALS['CONFIG.CMD.PREFIX']}ping "; + $plugin_description = "Pings host/ip: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."ping "; $plugin_command = 'ping'; function plugin_ping() { if (OnEmptyArg('ping ')) { - } elseif (!isset($GLOBALS['OS'])) { - $ip = gethostbyname($GLOBALS['args']); + } elseif (ifWindowsOs()) { + $ip = gethostbyname(msgAsArguments()); if ((!preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip)) and - (($ip == $GLOBALS['args']) or ($ip === false))) { - response("Unknown host/ip: '{$GLOBALS['args']}'"); + (($ip == msgAsArguments()) or ($ip === false))) { + response("Unknown host/ip: '".msgAsArguments()."'"); } else { $ping = ping($ip); if ($ping) { - $ping[0] = $GLOBALS['USER'].': '.$ping[0]; + $ping[0] = userPreg()[0].': '.$ping[0]; foreach ($ping as $thisline) { response($thisline); } diff --git a/PLUGINS/ADMIN/ripe.php b/PLUGINS/ADMIN/ripe.php index 3874cf6..fac2428 100644 --- a/PLUGINS/ADMIN/ripe.php +++ b/PLUGINS/ADMIN/ripe.php @@ -21,16 +21,16 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Checks ip address and shows results: {$GLOBALS['CONFIG.CMD.PREFIX']}ripe "; + $plugin_description = "Checks ip address and shows results: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."ripe "; $plugin_command = 'ripe'; function plugin_ripe() { if (OnEmptyArg('ripe ')) { } else if (extension_loaded('openssl')) { - if ($GLOBALS['args'] == '127.0.0.1' or $GLOBALS['args'] == '0.0.0.0') { + if (msgAsArguments() == '127.0.0.1' or msgAsArguments() == '0.0.0.0') { } else { - response(ripeCheckAddress($GLOBALS['args'])); + response(ripeCheckAddress(msgAsArguments())); } } else { response('I cannot use this plugin, i need php_openssl extension to work!'); diff --git a/PLUGINS/ADMIN/say.php b/PLUGINS/ADMIN/say.php index 26f9fa1..e869ebe 100644 --- a/PLUGINS/ADMIN/say.php +++ b/PLUGINS/ADMIN/say.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Say specified text to channel: {$GLOBALS['CONFIG.CMD.PREFIX']}say "; + $plugin_description = "Say specified text to channel: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."say "; $plugin_command = 'say'; function plugin_say() { if (OnEmptyArg('say ')) { } else { - toServer("PRIVMSG ".getBotChannel()." ".msg_without_command()); + toServer("PRIVMSG ".getBotChannel()." ".inputFromLine(4)); } } diff --git a/PLUGINS/ADMIN/topic.php b/PLUGINS/ADMIN/topic.php index 767524e..f231a07 100644 --- a/PLUGINS/ADMIN/topic.php +++ b/PLUGINS/ADMIN/topic.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Changing Topic in channel: {$GLOBALS['CONFIG.CMD.PREFIX']}topic "; + $plugin_description = "Changing Topic in channel: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."topic "; $plugin_command = 'topic'; function plugin_topic() diff --git a/PLUGINS/ADMIN/unban.php b/PLUGINS/ADMIN/unban.php index 146d716..4d86349 100644 --- a/PLUGINS/ADMIN/unban.php +++ b/PLUGINS/ADMIN/unban.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Uban specified host: {$GLOBALS['CONFIG.CMD.PREFIX']}unban "; + $plugin_description = "Uban specified host: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."unban "; $plugin_command = 'unban'; function plugin_unban() { if (OnEmptyArg('unban ')) { } else if (BotOpped() == true) { - toServer("MODE ".getBotChannel()." -b {$GLOBALS['args']}"); + toServer("MODE ".getBotChannel()." -b ".msgAsArguments()); } } diff --git a/PLUGINS/ADMIN/uptime.php b/PLUGINS/ADMIN/uptime.php index 1313981..1fa132e 100644 --- a/PLUGINS/ADMIN/uptime.php +++ b/PLUGINS/ADMIN/uptime.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows BOT uptime: {$GLOBALS['CONFIG.CMD.PREFIX']}uptime"; + $plugin_description = "Shows BOT uptime: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."uptime"; $plugin_command = 'uptime'; function plugin_uptime() diff --git a/PLUGINS/ADMIN/voice.php b/PLUGINS/ADMIN/voice.php index f66b8c7..6050812 100644 --- a/PLUGINS/ADMIN/voice.php +++ b/PLUGINS/ADMIN/voice.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Gives voice: {$GLOBALS['CONFIG.CMD.PREFIX']}voice "; + $plugin_description = "Gives voice: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."voice "; $plugin_command = 'voice'; function plugin_voice() { if (OnEmptyArg('voice ')) { } else if (BotOpped() == true) { - toServer("MODE ".getBotChannel()." +v {$GLOBALS['args']}"); + toServer("MODE ".getBotChannel()." +v ".msgAsArguments()); } } diff --git a/PLUGINS/OWNER/addadmin.php b/PLUGINS/OWNER/addadmin.php deleted file mode 100644 index 3328e4c..0000000 --- a/PLUGINS/OWNER/addadmin.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - - $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Add host to admin list in config: {$GLOBALS['CONFIG.CMD.PREFIX']}addadmin "; - $plugin_command = 'addadmin'; - -function plugin_addadmin() -{ - $nick = explode('!', trim($GLOBALS['args'])); - - if (OnEmptyArg('addadmin ')) { - } elseif ($nick[0] != getBotNickname()) { - if (preg_match('/^(.+?)!(.+?)@(.+?)$/', $GLOBALS['args'], $host)) { - LoadData($GLOBALS['configFile'], 'ADMIN', 'admin.list'); - - if (strpos($GLOBALS['LOADED'], $GLOBALS['args']) !== false) { - response('I already have this host.'); - } else { - empty($GLOBALS['LOADED']) ? $new_list = $host[0] : $new_list = "{$GLOBALS['LOADED']}, {$host[0]}"; - - SaveData($GLOBALS['configFile'], 'ADMIN', 'admin.list', $new_list); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.ADMIN.LIST'] = $cfg->get("ADMIN", "admin.list"); - - /* inform user about adding */ - toServer("PRIVMSG {$nick[0]} :From now you are on my ADMIN(S) list, enjoy."); - toServer("PRIVMSG {$nick[0]} :Core Commands: {$GLOBALS['CONFIG.CMD.PREFIX']}seen"); - toServer("PRIVMSG {$nick[0]} :Admin Commands: ".implode(' ', $GLOBALS['ADMIN_PLUGINS'])); - toServer("PRIVMSG {$nick[0]} :User Commands: ".implode(' ', $GLOBALS['USER_PLUGINS'])); - - response("Host: '{$host[0]}' added to admin list."); - } - } else { - response('Bad input, try: nick!ident@hostname'); - } - } else { - response("Iam already a master!"); - } -} diff --git a/PLUGINS/OWNER/addowner.php b/PLUGINS/OWNER/addowner.php index 3c0c75f..79daf59 100644 --- a/PLUGINS/OWNER/addowner.php +++ b/PLUGINS/OWNER/addowner.php @@ -21,57 +21,58 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Add owner host to config: {$GLOBALS['CONFIG.CMD.PREFIX']}addowner "; + $plugin_description = "Add owner host to config: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."addowner "; $plugin_command = 'addowner'; function plugin_addowner() { - $nick_ex = explode('!', trim($GLOBALS['args'])); + $nick_ex = explode('!', trim(msgAsArguments())); if (OnEmptyArg('addowner ')) { } elseif ($nick_ex[0] != getBotNickname()) { - if (preg_match('/^(.+?)!(.+?)@(.+?)$/', $GLOBALS['args'], $host)) { - LoadData($GLOBALS['configFile'], 'OWNER', 'bot.owners'); - - if (strpos($GLOBALS['LOADED'], $GLOBALS['args']) !== false) { + if (preg_match('/^(.+?)!(.+?)@(.+?)$/', msgAsArguments())) { + $botOwners = loadValueFromConfigFile('PRIVILEGES', getOwnerUserName()); + + if (strpos($botOwners, msgAsArguments()) !== false) { response('I already have this host.'); } else { /* add user to owner's host's */ - empty($GLOBALS['LOADED']) ? $new_list = $host[0] : $new_list = "{$GLOBALS['LOADED']}, {$host[0]}"; + empty($botOwners) ? $new_list = msgAsArguments() : $new_list = "{$botOwners}, ".msgAsArguments(); - SaveData($GLOBALS['configFile'], 'OWNER', 'bot.owners', $new_list); + SaveValueToConfigFile('PRIVILEGES', getOwnerUserName(), $new_list); /* add user to auto op list */ - LoadData($GLOBALS['configFile'], 'OWNER', 'auto.op.list'); - - empty($GLOBALS['LOADED']) ? $new_list = $host[0] : $new_list = "{$GLOBALS['LOADED']}, {$host[0]}"; + $autoOpList = loadValueFromConfigFile('AUTOMATIC', 'auto.op.list'); - SaveData($GLOBALS['configFile'], 'OWNER', 'auto.op.list', $new_list); + if (strpos($autoOpList, msgAsArguments()) === false) { + empty($autoOpList) ? $newAutoOpList = msgAsArguments() : $newAutoOpList = $autoOpList.', '.msgAsArguments(); - /* update variables with new owners/autoop list */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get("OWNER", "bot.owners"); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get('OWNER', 'auto.op.list'); + SaveValueToConfigFile('AUTOMATIC', 'auto.op.list', $newAutoOpList); + } /* inform user about it */ - toServer("PRIVMSG {$nick_ex[0]} :From now you are on my owner(s)/auto op(s) lists, enjoy."); + privateMsgTo($nick_ex[0], "From now you are on my owner(s)/auto op(s) lists, enjoy."); - $response = null; - - foreach ( CORECOMMANDSLIST as $coreCommand ) { - $response .= $GLOBALS['CONFIG.CMD.PREFIX'].$coreCommand.' '; + $prefix = loadValueFromConfigFile('COMMAND', 'command.prefix'); + $plugs = null; + + foreach (CORECOMMANDSLIST as $coreCommand => $coreCmdInfo) { + $plugs .= $prefix.$coreCommand.' '; } - - toServer("PRIVMSG {$nick_ex[0]} :Core Commands: ".$response); - toServer("PRIVMSG {$nick_ex[0]} :Owner Commands: ".implode(' ', $GLOBALS['OWNER_PLUGINS'])); - toServer("PRIVMSG {$nick_ex[0]} :User Commands: ".implode(' ', $GLOBALS['USER_PLUGINS'])); + + privateMsgTo($nick_ex[0], "Core Plugins: ".$plugs); + + $allPlugins = implode(' ', $GLOBALS['ALL_PLUGINS']); + $allPlugins = str_replace(' ', " $prefix", $allPlugins); + + privateMsgTo($nick_ex[0], $prefix.$allPlugins); - response("Host: '{$host[0]}' added to owner list."); + response("Host: '".msgAsArguments()."' added to owner list."); } } else { response('Bad input, try: nick!ident@hostname'); } } else { - response('I cannot add myself to owners, im already master :)'); + response('I cannot add myself to owners, iam already master.'); } } diff --git a/PLUGINS/OWNER/checkupdate.php b/PLUGINS/OWNER/checkupdate.php index a601bcb..48bf46c 100644 --- a/PLUGINS/OWNER/checkupdate.php +++ b/PLUGINS/OWNER/checkupdate.php @@ -21,18 +21,24 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Checking for updates: {$GLOBALS['CONFIG.CMD.PREFIX']}checkupdate"; + $plugin_description = "Checking for updates: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."checkupdate"; $plugin_command = 'checkupdate'; //------------------------------------------------------------------------------------------------ function plugin_checkupdate() { if (extension_loaded('openssl')) { - global $CheckVersion; - $CheckVersion = file_get_contents(VERSION_URL); if (!empty($CheckVersion)) { - checkVersion(); + $version = explode("\n", $CheckVersion); + + if ($version[0] > VER) { + response('New version available!'); + response("My version: ".VER.", version on server: {$version[0]} "); + response("To update BOT, use ".loadValueFromConfigFile('COMMAND', 'command.prefix')."update"); + } else { + response('No new update, you have the latest version.'); + } } else { response('Cannot connect to update server, try next time.'); } @@ -40,18 +46,3 @@ function plugin_checkupdate() response('I cannot use this plugin, i need php_openssl extension to work!'); } } -//------------------------------------------------------------------------------------------------ -function checkVersion() -{ - global $CheckVersion; - - $version = explode("\n", $CheckVersion); - - if ($version[0] > VER) { - response('New version available!'); - response("My version: ".VER.", version on server: {$version[0]} "); - response("To update BOT, use {$GLOBALS['CONFIG.CMD.PREFIX']}update"); - } else { - response('No new update, you have the latest version.'); - } -} diff --git a/PLUGINS/OWNER/cluster.php b/PLUGINS/OWNER/cluster.php index 93a6d28..7adf7c2 100644 --- a/PLUGINS/OWNER/cluster.php +++ b/PLUGINS/OWNER/cluster.php @@ -21,25 +21,25 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Clustering plugin: {$GLOBALS['CONFIG.CMD.PREFIX']}cluster help to list commands"; + $plugin_description = "Clustering plugin: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."cluster help to list commands"; $plugin_command = 'cluster'; function plugin_cluster() { if (OnEmptyArg('cluster to list commands')) { } else { - switch ($GLOBALS['args']) { + switch (msgAsArguments()) { case 'help': response('Cluster commands:'); response('cluster help - Shows this help'); - response("cluster shutdown - Bot shutdowns computer: {$GLOBALS['CONFIG.CMD.PREFIX']}". + response("cluster shutdown - Bot shutdowns computer: ".loadValueFromConfigFile('COMMAND', 'command.prefix'). "cluster shutdown "); - response("cluster shutdown * - Bot shutdowns all bots computers: {$GLOBALS['CONFIG.CMD.PREFIX']}". + response("cluster shutdown * - Bot shutdowns all bots computers: ".loadValueFromConfigFile('COMMAND', 'command.prefix'). "cluster shutdown *"); break; } /* me */ - if ($GLOBALS['piece1'] == 'shutdown' && $GLOBALS['piece2'] == getBotNickname()) { + if (msgPieces()[0] == 'shutdown' && msgPieces()[1] == getBotNickname()) { response('Shutting down machine...'); cliLog('SHUTTING DOWN COMPUTER!'); @@ -47,7 +47,7 @@ function plugin_cluster() exec('shutdown -s -t 0'); } /* all */ - if ($GLOBALS['piece1'] == 'shutdown' && $GLOBALS['piece2'] == '*') { + if (msgPieces()[0] == 'shutdown' && msgPieces()[1] == '*') { response('Shutting down machine...'); cliLog('SHUTTING DOWN COMPUTER!'); diff --git a/PLUGINS/OWNER/fetch.php b/PLUGINS/OWNER/fetch.php index db0468b..390884c 100644 --- a/PLUGINS/OWNER/fetch.php +++ b/PLUGINS/OWNER/fetch.php @@ -21,16 +21,17 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Downloads plugins from repository: {$GLOBALS['CONFIG.CMD.PREFIX']}fetch for help"; + $plugin_description = "Downloads plugins from repository: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."fetch for help"; $plugin_command = 'fetch'; function plugin_fetch() { + /* if (OnEmptyArg('fetch list / fetch get ')) { } else { if (extension_loaded('openssl')) { - /* if we have list command */ - if ($GLOBALS['args'] == 'list') { + + if (msgAsArguments() == 'list') { $addr_list = 'https://raw.githubusercontent.com/S3x0r/minion_repository_plugins/master/plugin_list.db'; $list = @file_get_contents($addr_list); @@ -42,33 +43,32 @@ function plugin_fetch() response('Cannot connect to fetch server, aborting.'); } } - /* if we have get command */ - if ($GLOBALS['piece1'] == 'get') { - if (!empty($GLOBALS['piece2'])) { /* if we have plugin */ - if (!empty($GLOBALS['piece3'])) { /* if we have perm */ + + if (msgPieces()[0] == 'get') { + if (!empty(msgPieces()[1])) { + if (!empty(msgPieces()[2])) { $dirs = array_diff(scandir(PLUGINSDIR), array('..', '.')); - if (in_array($GLOBALS['piece3'], ['USER', 'ADMIN', 'OWNER'])) { /* if we have perm from input */ - $check_file = PLUGINSDIR."/{$GLOBALS['piece3']}/{$GLOBALS['piece2']}.php"; + if (in_array(msgPieces()[2], ['USER', 'ADMIN', 'OWNER'])) { + $check_file = PLUGINSDIR."/".msgPieces()[2]."/".msgPieces()[1].".php"; $dir_user = array_diff(scandir(PLUGINSDIR.'/USER/'), array('..', '.')); $dir_admin = array_diff(scandir(PLUGINSDIR.'/ADMIN/'), array('..', '.')); $dir_owner = array_diff(scandir(PLUGINSDIR.'/OWNER/'), array('..', '.')); $all_dirs = array_merge($dir_user, $dir_admin, $dir_owner); - if (in_array($GLOBALS['piece2'].'.php', $all_dirs)) { + if (in_array(msgPieces()[1].'.php', $all_dirs)) { response('I already have this plugin, aborting.'); } else { - $address = "{$GLOBALS['CONFIG.FETCH.SERVER']}/{$GLOBALS['piece2']}.php"; - if (@file_get_contents($address)) { /* if we have that file in repository */ - response("Downloading plugin: '{$GLOBALS['piece2']}' from repository to: '{$GLOBALS['piece3']}'"); + $address = loadValueFromConfigFile('FETCH', 'fetch.server')."/".msgPieces()[1].".php"; + if (@file_get_contents($address)) { + response("Downloading plugin: '".msgPieces()[1]."' from repository to: '".msgPieces()[2]."'"); $file = file_get_contents($address); - $a = fopen(PLUGINSDIR."/{$GLOBALS['piece3']}/{$GLOBALS['piece2']}.php", 'w'); + $a = fopen(PLUGINSDIR."/".msgPieces()[2]."/".msgPieces()[2].".php", 'w'); fwrite($a, $file); fclose($a); - /* Load Plugin */ - LoadPlugin($GLOBALS['piece2']); + LoadPlugin(msgPieces()[1]); response('Plugin added.'); } else { @@ -89,4 +89,5 @@ function plugin_fetch() response('I cannot use this plugin, i need php_openssl extension to work!'); } } + */ } diff --git a/PLUGINS/OWNER/info.php b/PLUGINS/OWNER/info.php index 4dec0d5..e23c944 100644 --- a/PLUGINS/OWNER/info.php +++ b/PLUGINS/OWNER/info.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows info: {$GLOBALS['CONFIG.CMD.PREFIX']}info"; + $plugin_description = "Shows info: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."info"; $plugin_command = 'info'; function plugin_info() @@ -29,5 +29,5 @@ function plugin_info() response('MINION version '.VER); response('Author: S3x0r'); - !empty($GLOBALS['CONFIG.BOT.ADMIN']) ? response("Bot Admin: {$GLOBALS['CONFIG.BOT.ADMIN']}") : false; + !empty(loadValueFromConfigFile('OWNER', 'bot.admin')) ? response("Bot Admin: ".loadValueFromConfigFile('OWNER', 'bot.admin')) : false; } diff --git a/PLUGINS/OWNER/join.php b/PLUGINS/OWNER/join.php index cc68611..69260d7 100644 --- a/PLUGINS/OWNER/join.php +++ b/PLUGINS/OWNER/join.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Joins channel: {$GLOBALS['CONFIG.CMD.PREFIX']}join <#channel>"; + $plugin_description = "Joins channel: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."join <#channel>"; $plugin_command = 'join'; function plugin_join() { if (OnEmptyArg('join <#channel>')) { } else { - joinChannel($GLOBALS['args']); + joinChannel(msgAsArguments()); } } diff --git a/PLUGINS/OWNER/leave.php b/PLUGINS/OWNER/leave.php index 0afa438..3defdfc 100644 --- a/PLUGINS/OWNER/leave.php +++ b/PLUGINS/OWNER/leave.php @@ -21,13 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Leave channel: {$GLOBALS['CONFIG.CMD.PREFIX']}leave <#channel>"; + $plugin_description = "Leave channel: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."leave <#channel>"; $plugin_command = 'leave'; function plugin_leave() { if (OnEmptyArg('leave <#channel>')) { } else { - toServer("PART {$GLOBALS['args']}"); + toServer("PART ".msgAsArguments()); } } diff --git a/PLUGINS/OWNER/listowners.php b/PLUGINS/OWNER/listowners.php index ae39607..de18659 100644 --- a/PLUGINS/OWNER/listowners.php +++ b/PLUGINS/OWNER/listowners.php @@ -21,17 +21,17 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows BOT owners: {$GLOBALS['CONFIG.CMD.PREFIX']}listowners"; + $plugin_description = "Shows BOT owners: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."listowners"; $plugin_command = 'listowners'; function plugin_listowners() { - LoadData($GLOBALS['configFile'], 'OWNER', 'bot.owners'); + $ownersList = loadValueFromConfigFile('PRIVILEGES', getOwnerUserName()); - if (empty($GLOBALS['LOADED'])) { + if (empty($ownersList)) { response('Empty owner(s) list.'); } else { - $owners = explode(", ", $GLOBALS['LOADED']); + $owners = explode(", ", $ownersList); response('My Owner(s) Host(s):'); diff --git a/PLUGINS/OWNER/memusage.php b/PLUGINS/OWNER/memusage.php index 5b52d5c..50c43c4 100644 --- a/PLUGINS/OWNER/memusage.php +++ b/PLUGINS/OWNER/memusage.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows how much ram is being used by bot: {$GLOBALS['CONFIG.CMD.PREFIX']}memusage"; + $plugin_description = "Shows how much ram is being used by bot: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."memusage"; $plugin_command = 'memusage'; function plugin_memusage() @@ -41,5 +41,6 @@ function byte_convert($bytes) $exp = floor(log($bytes)/log(1024)); $convertedVal = ($bytes/pow(1024, floor($exp))); } + return sprintf('%.2f '.$symbol[$exp], $convertedVal); } diff --git a/PLUGINS/OWNER/newnick.php b/PLUGINS/OWNER/newnick.php index c0cc92c..a41f2c3 100644 --- a/PLUGINS/OWNER/newnick.php +++ b/PLUGINS/OWNER/newnick.php @@ -21,13 +21,15 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Changes nickname: {$GLOBALS['CONFIG.CMD.PREFIX']}newnick "; + $plugin_description = "Changes nickname: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."newnick "; $plugin_command = 'newnick'; function plugin_newnick() { if (OnEmptyArg('newnick ')) { - } elseif ($GLOBALS['args'] != getBotNickname()) { - toServer("NICK {$GLOBALS['args']}"); + } elseif (msgAsArguments() != getBotNickname()) { + toServer("NICK ".msgAsArguments()); + + setBotNickname(msgAsArguments()); } } diff --git a/PLUGINS/OWNER/plugin.php b/PLUGINS/OWNER/plugin.php deleted file mode 100644 index 8974c31..0000000 --- a/PLUGINS/OWNER/plugin.php +++ /dev/null @@ -1,120 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - - $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Plugins manipulation: {$GLOBALS['CONFIG.CMD.PREFIX']}plugin help to list commands"; - $plugin_command = 'plugin'; - -function plugin_plugin() -{ -//--------------------------------------------------------------------------------------------------------- - if (OnEmptyArg('plugin to list commands')) { - } else { - switch ($GLOBALS['args']) { -//--------------------------------------------------------------------------------------------------------- - case 'help': - response('Plugin commands:'); - response("plugin delete - Deletes plugin from directory: {$GLOBALS['CONFIG.CMD.PREFIX']}plugin delete "); - response("plugin move - Move plugin from group to group: {$GLOBALS['CONFIG.CMD.PREFIX']}plugin move "); - response("plugin load - Load plugin to BOT: {$GLOBALS['CONFIG.CMD.PREFIX']}plugin load "); - response("plugin unload - Unload plugin from BOT: {$GLOBALS['CONFIG.CMD.PREFIX']}plugin unload "); - break; -//--------------------------------------------------------------------------------------------------------- - } - - switch ($GLOBALS['piece1']) { -//--------------------------------------------------------------------------------------------------------- - case 'delete': - if (is_file(PLUGINSDIR."/USER/{$GLOBALS['piece2']}.php") xor - is_file(PLUGINSDIR."/OWNER/{$GLOBALS['piece2']}.php") xor - is_file(PLUGINSDIR."/ADMIN/{$GLOBALS['piece2']}.php")) { - if (is_file(PLUGINSDIR."/USER/{$GLOBALS['piece2']}.php")) { - unlink(PLUGINSDIR."/USER/{$GLOBALS['piece2']}.php"); - - response("Plugin: {$GLOBALS['piece2']} removed from: USER group."); - } elseif (is_file(PLUGINSDIR."/OWNER/{$GLOBALS['piece2']}.php")) { - unlink(PLUGINSDIR."/OWNER/{$GLOBALS['piece2']}.php"); - - response("Plugin: {$GLOBALS['piece2']} removed from OWNER group."); - } elseif (is_file(PLUGINSDIR."/ADMIN/{$GLOBALS['piece2']}.php")) { - unlink(PLUGINSDIR."/ADMIN/{$GLOBALS['piece2']}.php"); - - response("Plugin: {$GLOBALS['piece2']} removed from: ADMIN group."); - } - } else { - response('No such plugin.'); - } - break; -//--------------------------------------------------------------------------------------------------------- - case 'move': - if (is_dir(PLUGINSDIR)) { - if (!empty($GLOBALS['piece2']) && !empty($GLOBALS['piece3']) && !empty($GLOBALS['piece4'])) { - /* scan directory for groups */ - $directory = PLUGINSDIR; - $groups = array_diff(scandir($directory), array('..', '.')); - $GLOBALS['piece2'] = strtolower($GLOBALS['piece2']); - $GLOBALS['piece3'] = strtoupper($GLOBALS['piece3']); - $GLOBALS['piece4'] = strtoupper($GLOBALS['piece4']); - - /* do we have group from input? */ - if (in_array($GLOBALS['piece3'], $groups)) { - if (in_array($GLOBALS['piece4'], $groups)) { - /* try to move it */ - if (is_file(PLUGINSDIR."/{$GLOBALS['piece3']}/{$GLOBALS['piece2']}.php")) { - rename( - PLUGINSDIR."/{$GLOBALS['piece3']}/{$GLOBALS['piece2']}.php", - PLUGINSDIR."/{$GLOBALS['piece4']}/{$GLOBALS['piece2']}.php" - ); - - response("Plugin: {$GLOBALS['piece2']} moved to '{$GLOBALS['piece4']}' group."); - } elseif (!is_file(PLUGINSDIR."/{$GLOBALS['piece3']}/{$GLOBALS['piece2']}.php")) { - response("No such plugin in '{$GLOBALS['piece3']}' group!"); - } - } else { - response("Group '{$GLOBALS['piece4']}' does not exist."); - } - } else { - response("Group '{$GLOBALS['piece3']}' does not exist."); - } - } else { - response('use: '); - } - } else { - response('Cannot find '.PLUGINSDIR.' directory, deleted?'); - } - break; -//--------------------------------------------------------------------------------------------------------- - case 'unload': - if (!empty($GLOBALS['piece2'])) { - UnloadPlugin($GLOBALS['piece2']); - } - break; -//--------------------------------------------------------------------------------------------------------- - case 'load': - if (!empty($GLOBALS['piece2'])) { - LoadPlugin($GLOBALS['piece2']); - } - break; -//--------------------------------------------------------------------------------------------------------- - } - } -} diff --git a/PLUGINS/OWNER/quit.php b/PLUGINS/OWNER/quit.php index 4843972..fa978da 100644 --- a/PLUGINS/OWNER/quit.php +++ b/PLUGINS/OWNER/quit.php @@ -21,14 +21,14 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shutdown BOT: {$GLOBALS['CONFIG.CMD.PREFIX']}quit"; + $plugin_description = "Shutdown BOT: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."quit"; $plugin_command = 'quit'; function plugin_quit() { - /* give op before restart */ + /* give op before quit */ if (BotOpped() == true) { - toServer("MODE ".getBotChannel()." +o {$GLOBALS['USER']}"); + toServer("MODE ".getBotChannel()." +o ".userPreg()[0]); } response('Bye!'); diff --git a/PLUGINS/OWNER/raw.php b/PLUGINS/OWNER/raw.php index 373d5f3..b9ae200 100644 --- a/PLUGINS/OWNER/raw.php +++ b/PLUGINS/OWNER/raw.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Sends raw string to server: {$GLOBALS['CONFIG.CMD.PREFIX']}raw "; + $plugin_description = "Sends raw string to server: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."raw "; $plugin_command = 'raw'; // TODO: whole message @@ -30,7 +30,7 @@ function plugin_raw() { if (OnEmptyArg('raw ')) { } else { - $msg = "{$GLOBALS['piece1']} {$GLOBALS['piece2']} {$GLOBALS['piece3']} {$GLOBALS['piece4']}".PHP_EOL; + $msg = msgPieces()[0].msgPieces()[1].msgPieces()[2].msgPieces()[3].PHP_EOL; toServer($msg); } diff --git a/PLUGINS/OWNER/remadmin.php b/PLUGINS/OWNER/remadmin.php deleted file mode 100644 index 544e6f0..0000000 --- a/PLUGINS/OWNER/remadmin.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - - $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Removes admin from config file: {$GLOBALS['CONFIG.CMD.PREFIX']}remadmin "; - $plugin_command = 'remadmin'; - -function plugin_remadmin() -{ - if (OnEmptyArg('remadmin ')) { - } else { - if (preg_match('/^(.+?)!(.+?)@(.+?)$/', $GLOBALS['args'], $host)) { - /* read owners from config */ - LoadData($GLOBALS['configFile'], 'ADMIN', 'admin.list'); - $admin_list = $GLOBALS['LOADED']; - $array = explode(" ", str_replace(',', '', $admin_list)); - - $key = array_search($GLOBALS['args'], $array); - - if ($key !== false) { - /* remove from host from array */ - unset($array[$key]); - - /* new owners string */ - $string = implode(' ', $array); - $string2 = str_replace(' ', ', ', $string); - - /* save new list to config */ - SaveData($GLOBALS['configFile'], 'ADMIN', 'admin.list', $string2); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.ADMIN.LIST'] = $cfg->get("ADMIN", "admin.list"); - - /* send info to user */ - response("Host: '{$GLOBALS['args']}' removed from admin list."); - } else { - response('No such host in my list.'); - } - } else { - response('Bad input, try: nick!ident@hostname'); - } - } -} diff --git a/PLUGINS/OWNER/remowner.php b/PLUGINS/OWNER/remowner.php index 28720e5..80ab660 100644 --- a/PLUGINS/OWNER/remowner.php +++ b/PLUGINS/OWNER/remowner.php @@ -21,19 +21,18 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Removes owner from config file: {$GLOBALS['CONFIG.CMD.PREFIX']}remowner "; + $plugin_description = "Removes owner from config file: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."remowner "; $plugin_command = 'remowner'; function plugin_remowner() { if (OnEmptyArg('remowner ')) { } else { - if (preg_match('/^(.+?)!(.+?)@(.+?)$/', $GLOBALS['args'], $host)) { + if (preg_match('/^(.+?)!(.+?)@(.+?)$/', msgAsArguments(), $host)) { /* read owners from config */ - LoadData($GLOBALS['configFile'], 'OWNER', 'bot.owners'); - $owners_list = $GLOBALS['LOADED']; - $array = explode(" ", str_replace(',', '', $owners_list)); - $key = array_search($GLOBALS['args'], $array); + $ownersList = loadValueFromConfigFile('PRIVILEGES', getOwnerUserName()); + $array = explode(" ", str_replace(',', '', $ownersList)); + $key = array_search(msgAsArguments(), $array); if ($key !== false) { /* remove from host from array */ @@ -44,14 +43,10 @@ function plugin_remowner() $string2 = str_replace(' ', ', ', $string); /* save new list to config */ - SaveData($GLOBALS['configFile'], 'OWNER', 'bot.owners', $string2); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get("OWNER", "bot.owners"); + SaveValueToConfigFile('PRIVILEGES', getOwnerUserName(), $string2); /* send info to user */ - response("Host: '{$GLOBALS['args']}' removed from owners."); + response("Host: '".msgAsArguments()."' removed from owners."); } else { response('No such host in my list.'); } diff --git a/PLUGINS/OWNER/restart.php b/PLUGINS/OWNER/restart.php index fccb290..d45efc0 100644 --- a/PLUGINS/OWNER/restart.php +++ b/PLUGINS/OWNER/restart.php @@ -21,14 +21,14 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Restarts Bot: {$GLOBALS['CONFIG.CMD.PREFIX']}restart"; + $plugin_description = "Restarts Bot: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."restart"; $plugin_command = 'restart'; function plugin_restart() { /* give op before restart */ if (BotOpped() == true) { - toServer("MODE ".getBotChannel()." +o {$GLOBALS['USER']}"); + toServer("MODE ".getBotChannel()." +o ".userPreg()[0]); } /* quit from irc server */ @@ -37,7 +37,7 @@ function plugin_restart() /* send cli messages */ cliLog('Restarting BOT...'); - if (!isset($GLOBALS['OS'])) { + if (ifWindowsOs()) { chdir('src/php'); runProgram('start php.exe ../../BOT.php'); } else { diff --git a/PLUGINS/OWNER/save.php b/PLUGINS/OWNER/save.php deleted file mode 100644 index ef1b2c0..0000000 --- a/PLUGINS/OWNER/save.php +++ /dev/null @@ -1,337 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - - $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Saving to config file: {$GLOBALS['CONFIG.CMD.PREFIX']}save help to list commands"; - $plugin_command = 'save'; - -/* TODO: - missing: -server.password -bot.admin -admin.list -keep.chan.modes -keep.nick -channel.modes -channel.key -ban.list -channel.delay -private.delay -notice.delay -web.login -web.password -play.sounds -*/ - -function plugin_save() -{ - if (OnEmptyArg('save to list commands')) { - } else { - switch ($GLOBALS['args']) { - case 'help': - response('save commands:'); - response('save auto.join - Saving auto join on channel when connected: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save auto.join '); - response('save auto.op - Saving auto op when join channel: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save auto.op '); - response('save auto.op.list - Saving auto op list in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save auto.op.list '); - response('save auto.rejoin - Saving auto rejoin when kicked from channel: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save auto.rejoin '); - response('save bot.owners - Saving bot owners list in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save bot.owners '); - response('save response - Saving where bot outputs messages: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save response '); - response('save channel - Saving channel to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save channel <#new_channel>'); - response('save command.prefix - Saving prefix commands: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save command.prefix '); - response('save connect.delay - Saving connect delay value to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save connect.delay '); - response('save ctcp.finger - Saving ctcp finger in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save ctcp.finger '); - response('save ctcp.response - Saving ctcp response in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save ctcp.response '); - response('save ctcp.version - Saving ctcp version in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save ctcp.version '); - response('save fetch.server - Saving fetch server to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save fetch.server '); - response('save ident - Saving ident to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save ident '); - response('save logging - Saving logging in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save logging '); - response('save name - Saving name to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save name '); - response('save nick - Saving nickname to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save nick '); - response('save owner.password - Saving bot owner password in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save owner.password '); - response('save port - Saving port to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save port '); - response('save show.raw - Saving show raw in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save show.raw '); - response('save server - Saving server to config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save server '); - response('save time.zone - Saving time zone in config: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save time.zone '); - response('save try.connect - Saving how many times try connect to server: ' - .$GLOBALS['CONFIG.CMD.PREFIX'].'save try.connect '); - response('End.'); - break; - } - switch ($GLOBALS['piece1']) { - case 'auto.join': - SaveData($GLOBALS['configFile'], 'CHANNEL', 'auto.join', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.AUTO.JOIN'] = $cfg->get("CHANNEL", "auto.join"); - - response('Auto.join Saved.'); - break; - - case 'auto.op': - SaveData($GLOBALS['configFile'], 'AUTOMATIC', 'auto.op', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.AUTO.OP'] = $cfg->get("AUTOMATIC", "auto.op"); - - response('Auto.op Saved.'); - break; - - case 'auto.op.list': - SaveData($GLOBALS['configFile'], 'OWNER', 'auto.op.list', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get("OWNER", "auto.op.list"); - - response('Auto.op.list Saved.'); - break; - - case 'auto.rejoin': - SaveData($GLOBALS['configFile'], 'AUTOMATIC', 'auto.rejoin', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.AUTO.REJOIN'] = $cfg->get("AUTOMATIC", "auto.rejoin"); - - response('Auto.rejoin Saved.'); - break; - - case 'bot.owners': - SaveData($GLOBALS['configFile'], 'OWNER', 'bot.owners', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get("OWNER", "bot.owners"); - - response('Bot.owners Saved.'); - break; - - case 'response': - SaveData($GLOBALS['configFile'], 'RESPONSE', 'response', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.BOT.RESPONSE'] = $cfg->get("RESPONSE", "response"); - - response('response Saved.'); - break; - - case 'channel': - SaveData($GLOBALS['configFile'], 'CHANNEL', 'channel', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.CHANNEL'] = $cfg->get("CHANNEL", "channel"); - - response('Channel Saved.'); - break; - - case 'command.prefix': - /* update plugins array */ - UpdatePrefix('OWNER', $GLOBALS['piece2']); - UpdatePrefix('USER', $GLOBALS['piece2']); - - SaveData($GLOBALS['configFile'], 'COMMAND', 'command.prefix', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.CMD.PREFIX'] = $cfg->get("COMMAND", "command.prefix"); - - response('Command.prefix Saved.'); - break; - - case 'connect.delay': - SaveData($GLOBALS['configFile'], 'SERVER', 'connect.delay', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.CONNECT.DELAY'] = $cfg->get("SERVER", "connect.delay"); - - response('Connect.delay Saved.'); - break; - - case 'ctcp.finger': - SaveData($GLOBALS['configFile'], 'CTCP', 'ctcp.finger', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.CTCP.FINGER'] = $cfg->get("CTCP", "ctcp.finger"); - - response('Ctcp.finger Saved.'); - break; - - case 'ctcp.response': - SaveData($GLOBALS['configFile'], 'CTCP', 'ctcp.response', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.CTCP.RESPONSE'] = $cfg->get("CTCP", "ctcp.response"); - - response('Ctcp.response Saved.'); - break; - - case 'ctcp.version': - SaveData($GLOBALS['configFile'], 'CTCP', 'ctcp.version', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.CTCP.VERSION'] = $cfg->get("CTCP", "ctcp.version"); - - response('Ctcp.version Saved.'); - break; - - case 'fetch.server': - SaveData($GLOBALS['configFile'], 'FETCH', 'fetch.server', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.FETCH.SERVER'] = $cfg->get("FETCH", "fetch.server"); - - response('Server Saved.'); - break; - - case 'ident': - SaveData($GLOBALS['configFile'], 'BOT', 'ident', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.IDENT'] = $cfg->get("BOT", "ident"); - - response('Ident Saved.'); - break; - - case 'logging': - SaveData($GLOBALS['configFile'], 'LOGS', 'logging', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.LOGGING'] = $cfg->get("LOGS", "logging"); - - response('Logging Saved.'); - break; - - case 'name': - SaveData($GLOBALS['configFile'], 'BOT', 'name', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.NAME'] = $cfg->get("BOT", "name"); - - response('Name Saved.'); - break; - - case 'nick': - SaveData($GLOBALS['configFile'], 'BOT', 'nickname', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.NICKNAME'] = $cfg->get("BOT", "nickname"); - - response('Nick Saved.'); - break; - - case 'owner.password': - SaveData($GLOBALS['configFile'], 'OWNER', 'owner.password', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.OWNER.PASSWD'] = $cfg->get("OWNER", "owner.password"); - - response('Owner.password Saved.'); - break; - - case 'port': - SaveData($GLOBALS['configFile'], 'SERVER', 'port', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.PORT'] = $cfg->get("SERVER", "port"); - - response('Port Saved.'); - break; - - case 'show.raw': - SaveData($GLOBALS['configFile'], 'DEBUG', 'show.raw', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.SHOW.RAW'] = $cfg->get("DEBUG", "show.raw"); - - response('Show.raw Saved.'); - break; - - case 'server': - SaveData($GLOBALS['configFile'], 'SERVER', 'server', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.SERVER'] = $cfg->get("SERVER", "server"); - - response('Server Saved.'); - break; - - case 'time.zone': - SaveData($GLOBALS['configFile'], 'TIME', 'time.zone', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.TIMEZONE'] = $cfg->get("TIME", "time.zone"); - - response('Time.zone Saved.'); - break; - - case 'try.connect': - SaveData($GLOBALS['configFile'], 'SERVER', 'try.connect', $GLOBALS['piece2']); - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.TRY.CONNECT'] = $cfg->get("SERVER", "try.connect"); - - response('Try.connect Saved.'); - break; - } - } -} diff --git a/PLUGINS/OWNER/server.php b/PLUGINS/OWNER/server.php index 9727201..6daabcf 100644 --- a/PLUGINS/OWNER/server.php +++ b/PLUGINS/OWNER/server.php @@ -21,33 +21,33 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Connect to specified server: {$GLOBALS['CONFIG.CMD.PREFIX']}server "; + $plugin_description = "Connect to specified server: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."server "; $plugin_command = 'server'; function plugin_server() { if (OnEmptyArg('server ')) { - } elseif (!empty($GLOBALS['args']) && !empty($GLOBALS['piece2']) && is_numeric($GLOBALS['piece2'])) { + } elseif (!empty(msgAsArguments()) && !empty(msgPieces()[1]) && is_numeric(msgPieces()[1])) { $GLOBALS['disconnected'] = 'yes'; - cliLog("[bot] Changing server to: {$GLOBALS['piece1']}:{$GLOBALS['piece2']}"); + cliLog("[bot] Changing server to: ".msgPieces()[0].":".msgPieces()[1]); toServer("QUIT :Changing server..."); - if (!isset($GLOBALS['OS'])) { + if (ifWindowsOs()) { chdir('src/php'); - runProgram('start php.exe ../../BOT.php -o '.$GLOBALS['piece1'].' '.$GLOBALS['piece2']); + runProgram('start php.exe ../../BOT.php -o '.msgPieces()[0].' '.msgPieces()[1]); exit; } else { - runProgram('php BOT.php -o '.$GLOBALS['piece1'].' '.$GLOBALS['piece2']); + runProgram('php BOT.php -o '.msgPieces()[0].' '.msgPieces()[1]); exit; } - } elseif (empty($GLOBALS['args'])) { + } elseif (empty(msgAsArguments())) { response('You need to specify server address.'); - } elseif (empty($GLOBALS['piece2'])) { + } elseif (empty(msgPieces()[1])) { response('You need to specify server port.'); - } elseif (!is_numeric($GLOBALS['piece2'])) { + } elseif (!is_numeric(msgPieces()[1])) { response('Wrong server port.'); } } diff --git a/PLUGINS/OWNER/showconfig.php b/PLUGINS/OWNER/showconfig.php deleted file mode 100644 index d05849b..0000000 --- a/PLUGINS/OWNER/showconfig.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - - $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows BOT configuration: {$GLOBALS['CONFIG.CMD.PREFIX']}showconfig"; - $plugin_command = 'showconfig'; - -function plugin_showconfig() -{ - response('My Config:'); - - response('[BOT]'); - response('Nickname : '.$GLOBALS['CONFIG.NICKNAME'].''); - response('Name : '.$GLOBALS['CONFIG.NAME'].''); - response('Ident : '.$GLOBALS['CONFIG.IDENT'].''); - - response('[Server]'); - response('Server : '.$GLOBALS['CONFIG.SERVER'].''); - response('Port : '.$GLOBALS['CONFIG.PORT'].''); - response('Server Pass : '.$GLOBALS['CONFIG.SERVER.PASSWD'].''); - response('Try Connect : '.$GLOBALS['CONFIG.TRY.CONNECT'].''); - response('Connect Delay : '.$GLOBALS['CONFIG.CONNECT.DELAY'].''); - - response('[OWNER]'); - response('Bot Admin : '.$GLOBALS['CONFIG.BOT.ADMIN'].''); - response('Auto OP List : '.$GLOBALS['CONFIG.AUTO.OP.LIST'].''); - response('Bot Owners : '.$GLOBALS['CONFIG.OWNERS'].''); - - response('[ADMIN]'); - response('Admin List : '.$GLOBALS['CONFIG.ADMIN.LIST'].''); - - response('[RESPONSE]'); - response('Bot Response : '.$GLOBALS['CONFIG.BOT.RESPONSE'].''); - - response('[AUTOMATIC]'); - response('Auto OP : '.$GLOBALS['CONFIG.AUTO.OP'].''); - response('Auto Rejoin : '.$GLOBALS['CONFIG.AUTO.REJOIN'].''); - response('Keep Chan Modes : '.$GLOBALS['CONFIG.KEEPCHAN.MODES'].''); - response('Keep Nick : '.$GLOBALS['CONFIG.KEEP.NICK'].''); - - response('[CHANNEL]'); - response('Channel : '.$GLOBALS['CONFIG.CHANNEL'].''); - response('Auto Join : '.$GLOBALS['CONFIG.AUTO.JOIN'].''); - response('Channel Modes : '.$GLOBALS['CONFIG.CHANNEL.MODES'].''); - response('Channel Key : '.$GLOBALS['CONFIG.CHANNEL.KEY'].''); - - response('[BANS]'); - response('Ban List : '.$GLOBALS['CONFIG.BAN.LIST'].''); - - response('[COMMAND]'); - response('Command Prefix : '.$GLOBALS['CONFIG.CMD.PREFIX'].''); - - response('[CTCP]'); - response('CTCP Response : '.$GLOBALS['CONFIG.CTCP.RESPONSE'].''); - response('CTCP Version : '.$GLOBALS['CONFIG.CTCP.VERSION'].''); - response('CTCP Finger : '.$GLOBALS['CONFIG.CTCP.FINGER'].''); - - response('[DELAYS]'); - response('Channel Delay : '.$GLOBALS['CONFIG.CHANNEL.DELAY'].''); - response('Private Delay : '.$GLOBALS['CONFIG.PRIVATE.DELAY'].''); - response('Notice Delay : '.$GLOBALS['CONFIG.NOTICE.DELAY'].''); - - response('[LOGS]'); - response('Logging : '.$GLOBALS['CONFIG.LOGGING'].''); - - response('[TIME]'); - response('Time Zone : '.$GLOBALS['CONFIG.TIMEZONE'].''); - - response('[FETCH]'); - response('Fetch Server : '.$GLOBALS['CONFIG.FETCH.SERVER'].''); - - response('[PROGRAM]'); - response('Check Update : '.$GLOBALS['CONFIG_CHECK_UPDATE'].''); - response('Play Sounds : '.$GLOBALS['CONFIG.PLAY.SOUNDS'].''); - - response('[DEBUG]'); - response('Show RAW : '.$GLOBALS['CONFIG.SHOW.RAW'].''); - - response('End.'); -} diff --git a/PLUGINS/OWNER/update.php b/PLUGINS/OWNER/update.php index 9d0cd23..ffe734b 100644 --- a/PLUGINS/OWNER/update.php +++ b/PLUGINS/OWNER/update.php @@ -21,12 +21,13 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Updates the BOT if new version is available: {$GLOBALS['CONFIG.CMD.PREFIX']}update"; + $plugin_description = "Updates the BOT if new version is available: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."update"; $plugin_command = 'update'; //------------------------------------------------------------------------------------------------ function plugin_update() { + /* if (extension_loaded('openssl')) { v_connect(); } else { @@ -122,7 +123,6 @@ function v_extract() response('Extracting update'); cliLog('[bot] Extracting update'); - /* Extracting update */ $zip = new ZipArchive; if ($zip->open('update.zip') === true) { $zip->extractTo('.'); @@ -133,96 +133,14 @@ function v_extract() unlink('MINION-master/.gitattributes'); - /* copy from extracted dir to -> new dir */ recurse_copy("MINION-master/", $GLOBALS['newdir']); - /* delete downloaded zip */ unlink('update.zip'); - /* delete extracted dir */ delete_files('MINION-master/'); - //read config and put to new version conf - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.NICKNAME'] = $cfg->get("BOT", "nickname"); - $GLOBALS['CONFIG.NAME'] = $cfg->get("BOT", "name"); - $GLOBALS['CONFIG.IDENT'] = $cfg->get("BOT", "ident"); - $GLOBALS['CONFIG.SERVER'] = $cfg->get("SERVER", "server"); - $GLOBALS['CONFIG.PORT'] = $cfg->get("SERVER", "port"); - $GLOBALS['CONFIG.SERVER.PASSWD'] = $cfg->get("SERVER", "server.password"); - $GLOBALS['CONFIG.TRY.CONNECT'] = $cfg->get("SERVER", "try.connect"); - $GLOBALS['CONFIG.CONNECT.DELAY'] = $cfg->get("SERVER", "connect.delay"); - $GLOBALS['CONFIG.BOT.ADMIN'] = $cfg->get("OWNER", "bot.admin"); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get("OWNER", "auto.op.list"); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get("OWNER", "bot.owners"); - $GLOBALS['CONFIG.OWNER.PASSWD'] = $cfg->get("OWNER", "owner.password"); - $GLOBALS['CONFIG.ADMIN.LIST'] = $cfg->get("ADMIN", "admin.list"); - $GLOBALS['CONFIG.BOT.RESPONSE'] = $cfg->get("RESPONSE", "response"); - $GLOBALS['CONFIG.AUTO.OP'] = $cfg->get("AUTOMATIC", "auto.op"); - $GLOBALS['CONFIG.AUTO.REJOIN'] = $cfg->get("AUTOMATIC", "auto.rejoin"); - $GLOBALS['CONFIG.KEEPCHAN.MODES'] = $cfg->get("AUTOMATIC", "keep.chan.modes"); - $GLOBALS['CONFIG.KEEP.NICK'] = $cfg->get("AUTOMATIC", "keep.nick"); - $GLOBALS['CONFIG.CHANNEL'] = $cfg->get("CHANNEL", "channel"); - $GLOBALS['CONFIG.AUTO.JOIN'] = $cfg->get("CHANNEL", "auto.join"); - $GLOBALS['CONFIG.CHANNEL.MODES'] = $cfg->get("CHANNEL", "channel.modes"); - $GLOBALS['CONFIG.CHANNEL.KEY'] = $cfg->get("CHANNEL", "channel.key"); - $GLOBALS['CONFIG.BAN.LIST'] = $cfg->get("BANS", "ban.list"); - $GLOBALS['CONFIG.CMD.PREFIX'] = $cfg->get("COMMAND", "command.prefix"); - $GLOBALS['CONFIG.CTCP.RESPONSE'] = $cfg->get("CTCP", "ctcp.response"); - $GLOBALS['CONFIG.CTCP.FINGER'] = $cfg->get("CTCP", "ctcp.finger"); - $GLOBALS['CONFIG.CHANNEL.DELAY'] = $cfg->get("DELAYS", "channel.delay"); - $GLOBALS['CONFIG.PRIVATE.DELAY'] = $cfg->get("DELAYS", "private.delay"); - $GLOBALS['CONFIG.NOTICE.DELAY'] = $cfg->get("DELAYS", "notice.delay"); - $GLOBALS['CONFIG.LOGGING'] = $cfg->get("LOGS", "logging"); - $GLOBALS['CONFIG.WEB.LOGIN'] = $cfg->get("PANEL", "web.login"); - $GLOBALS['CONFIG.WEB.PASSWORD'] = $cfg->get("PANEL", "web.password"); - $GLOBALS['CONFIG.TIMEZONE'] = $cfg->get("TIME", "time.zone"); - $GLOBALS['CONFIG.FETCH.SERVER'] = $cfg->get("FETCH", "fetch.server"); - $GLOBALS['CONFIG.PLAY.SOUNDS'] = $cfg->get("PROGRAM", "play.sounds"); - $GLOBALS['CONFIG.SHOW.RAW'] = $cfg->get("DEBUG", "show.raw"); - - // save to new config - $new_cf = $GLOBALS['newdir'].'/CONFIG.INI'; - - SaveData($new_cf, 'BOT', 'nickname', $GLOBALS['CONFIG.NICKNAME']); - SaveData($new_cf, 'BOT', 'name', $GLOBALS['CONFIG.NAME']); - SaveData($new_cf, 'BOT', 'ident', $GLOBALS['CONFIG.IDENT']); - SaveData($new_cf, 'SERVER', 'server', $GLOBALS['CONFIG.SERVER']); - SaveData($new_cf, 'SERVER', 'port', $GLOBALS['CONFIG.PORT']); - SaveData($new_cf, 'SERVER', 'server.password', $GLOBALS['CONFIG.SERVER.PASSWD']); - SaveData($new_cf, 'SERVER', 'try.connect', $GLOBALS['CONFIG.TRY.CONNECT']); - SaveData($new_cf, 'SERVER', 'connect.delay', $GLOBALS['CONFIG.CONNECT.DELAY']); - SaveData($new_cf, 'OWNER', 'bot.admin', $GLOBALS['CONFIG.BOT.ADMIN']); - SaveData($new_cf, 'OWNER', 'auto.op.list', $GLOBALS['CONFIG.AUTO.OP.LIST']); - SaveData($new_cf, 'OWNER', 'bot.owners', $GLOBALS['CONFIG.OWNERS']); - SaveData($new_cf, 'OWNER', 'owner.password', $GLOBALS['CONFIG.OWNER.PASSWD']); - SaveData($new_cf, 'ADMIN', 'admin.list', $GLOBALS['CONFIG.ADMIN.LIST']); - SaveData($new_cf, 'RESPONSE', 'response', $GLOBALS['CONFIG.BOT.RESPONSE']); - SaveData($new_cf, 'AUTOMATIC', 'auto.op', $GLOBALS['CONFIG.AUTO.OP']); - SaveData($new_cf, 'AUTOMATIC', 'auto.rejoin', $GLOBALS['CONFIG.AUTO.REJOIN']); - SaveData($new_cf, 'AUTOMATIC', 'keep.chan.modes', $GLOBALS['CONFIG.KEEPCHAN.MODES']); - SaveData($new_cf, 'AUTOMATIC', 'keep.nick', $GLOBALS['CONFIG.KEEP.NICK']); - SaveData($new_cf, 'CHANNEL', 'channel', $GLOBALS['CONFIG.CHANNEL']); - SaveData($new_cf, 'CHANNEL', 'auto.join', $GLOBALS['CONFIG.AUTO.JOIN']); - SaveData($new_cf, 'CHANNEL', 'channel.modes', $GLOBALS['CONFIG.CHANNEL.MODES']); - SaveData($new_cf, 'CHANNEL', 'channel.key', $GLOBALS['CONFIG.CHANNEL.KEY']); - SaveData($new_cf, 'BANS', 'ban.list', $GLOBALS['CONFIG.BAN.LIST']); - SaveData($new_cf, 'COMMAND', 'command.prefix', $GLOBALS['CONFIG.CMD.PREFIX']); - SaveData($new_cf, 'CTCP', 'ctcp.response', $GLOBALS['CONFIG.CTCP.RESPONSE']); - SaveData($new_cf, 'CTCP', 'ctcp.finger', $GLOBALS['CONFIG.CTCP.FINGER']); - SaveData($new_cf, 'DELAYS', 'channel.delay', $GLOBALS['CONFIG.CHANNEL.DELAY']); - SaveData($new_cf, 'DELAYS', 'private.delay', $GLOBALS['CONFIG.PRIVATE.DELAY']); - SaveData($new_cf, 'DELAYS', 'notice.delay', $GLOBALS['CONFIG.NOTICE.DELAY']); - SaveData($new_cf, 'LOGS', 'logging', $GLOBALS['CONFIG.LOGGING']); - SaveData($new_cf, 'PANEL', 'web.login', $GLOBALS['CONFIG.WEB.LOGIN']); - SaveData($new_cf, 'PANEL', 'web.password', $GLOBALS['CONFIG.WEB.PASSWORD']); - SaveData($new_cf, 'TIME', 'time.zone', $GLOBALS['CONFIG.TIMEZONE']); - SaveData($new_cf, 'FETCH', 'fetch.server', $GLOBALS['CONFIG.FETCH.SERVER']); - SaveData($new_cf, 'PROGRAM', 'play.sounds', $GLOBALS['CONFIG.PLAY.SOUNDS']); - SaveData($new_cf, 'DEBUG', 'show.raw', $GLOBALS['CONFIG.SHOW.RAW']); - // copy CONFIG from older version - copy($GLOBALS['configFile'], $GLOBALS['newdir'].'/OLD_CONFIG.INI'); + copy(getConfigFileName(), $GLOBALS['newdir'].'/OLD_CONFIG.INI'); // copy DATA folder recurse_copy(DATADIR, $GLOBALS['newdir'].'/'.DATADIR); @@ -230,9 +148,8 @@ function v_extract() // copy LOGS folder recurse_copy(LOGSDIR, $GLOBALS['newdir'].'/'.LOGSDIR); - /* give op */ if (BotOpped() == true) { - toServer("MODE ".getBotChannel()." +o {$GLOBALS['USER']}"); + toServer("MODE ".getBotChannel()." +o ".userPreg()[0]); } // reconnect to run new version @@ -251,4 +168,6 @@ function v_extract() response('Failed to extract, aborting.'); cliLog('[bot] Failed to extract update, aborting!'); } + + */ } diff --git a/PLUGINS/OWNER/winamp.php b/PLUGINS/OWNER/winamp.php index 7a372cb..bc2b43f 100644 --- a/PLUGINS/OWNER/winamp.php +++ b/PLUGINS/OWNER/winamp.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Controls winamp: {$GLOBALS['CONFIG.CMD.PREFIX']}winamp "; + $plugin_description = "Controls winamp: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."winamp "; $plugin_command = 'winamp'; /* @@ -36,7 +36,7 @@ function plugin_winamp() { if (OnEmptyArg('winamp to list commands')) { } elseif (!empty($GLOBALS['winamp_loc'])) { - switch ($GLOBALS['args']) { + switch (msgAsArguments()) { case 'help': response('Winamp commands:'); response('winamp stop - Stop music: !winamp stop'); diff --git a/PLUGINS/USER/hash.php b/PLUGINS/USER/hash.php index 51732f4..f655629 100644 --- a/PLUGINS/USER/hash.php +++ b/PLUGINS/USER/hash.php @@ -21,21 +21,21 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Changing string to choosed algorithm: {$GLOBALS['CONFIG.CMD.PREFIX']}hash help to list algorithms"; + $plugin_description = "Changing string to choosed algorithm: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."hash help to list algorithms"; $plugin_command = 'hash'; /* -TODO: server message limit cut, send in parts +TODO: if server message limit -cut and send in parts */ function plugin_hash() { if (OnEmptyArg('hash help to get algorithms list')) { - } elseif ($GLOBALS['args'] == 'help') { - response("Usage: {$GLOBALS['CONFIG.CMD.PREFIX']}hash "); + } elseif (msgAsArguments() == 'help') { + response("Usage: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."hash "); response('Algos: '.implode(' ', hash_algos())); - } elseif (in_array($GLOBALS['piece1'], hash_algos())) { - response("{$GLOBALS['piece1']}: ".hash($GLOBALS['piece1'], inputFromLine(5))); + } elseif (in_array(msgPieces()[0], hash_algos())) { + response(msgPieces()[0].": ".hash(msgPieces()[0], inputFromLine(5))); } else { response('Unknown hashing algorithm.'); } diff --git a/PLUGINS/USER/help.php b/PLUGINS/USER/help.php index 3c74e59..6947a66 100644 --- a/PLUGINS/USER/help.php +++ b/PLUGINS/USER/help.php @@ -21,43 +21,69 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows BOT commands: {$GLOBALS['CONFIG.CMD.PREFIX']}help"; + $plugin_description = "Shows BOT commands: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."help"; $plugin_command = 'help'; -/* TODO: -move plugin to core commands +/* TODO: -if plugin(s) dir empty do not show "commands" txt */ function plugin_help() { - /* if OWNER use help */ - if (HasOwner($GLOBALS['mask'])) { - - $response = null; - - foreach ( CORECOMMANDSLIST as $coreCommand ) { - $response .= $GLOBALS['CONFIG.CMD.PREFIX'].$coreCommand.' '; + $prefix = loadValueFromConfigFile('COMMAND', 'command.prefix'); + + $who = whoIsUser(); + + /* owner */ + if ($who[1] == 0) { + $plugs = null; + + foreach (CORECOMMANDSLIST as $coreCommand => $coreCmdInfo) { + $plugs .= $prefix.$coreCommand.' '; } - response('Core Commands: '.$response); - - response('Owner Commands: '.implode(' ', $GLOBALS['OWNER_PLUGINS'])); - response('Admin Commands: '.implode(' ', $GLOBALS['ADMIN_PLUGINS'])); - response('User Commands: '.implode(' ', $GLOBALS['USER_PLUGINS'])); - - /* if ADMIN use help */ - } elseif (!HasOwner($GLOBALS['mask']) && HasAdmin($GLOBALS['mask'])) { - response("Core Commands: {$GLOBALS['CONFIG.CMD.PREFIX']}seen"); - response('Admin Commands: '.implode(' ', $GLOBALS['ADMIN_PLUGINS'])); - response('User Commands: '.implode(' ', $GLOBALS['USER_PLUGINS'])); - - !empty($GLOBALS['CONFIG.BOT.ADMIN']) ? response("Bot Admin: {$GLOBALS['CONFIG.BOT.ADMIN']}") : false; - - /* if USER use help */ - } elseif (!HasOwner($GLOBALS['mask']) && !HasAdmin($GLOBALS['mask'])) { - response("Core Commands: {$GLOBALS['CONFIG.CMD.PREFIX']}seen"); - response('User Commands: '.implode(' ', $GLOBALS['USER_PLUGINS'])); - - !empty($GLOBALS['CONFIG.BOT.ADMIN']) ? response("Bot Admin: {$GLOBALS['CONFIG.BOT.ADMIN']}") : false; + response('Core Plugins: '.$plugs); + + $allPlugins = implode(' ', $GLOBALS['ALL_PLUGINS']); + $allPlugins = str_replace(' ', " $prefix", $allPlugins); + + response($prefix.$allPlugins); + /* user */ + } else if ($who[1] == 999) { + $userPlugins = implode(' ', $GLOBALS[getStandardUserName().'_PLUGINS']); + $userPlugins = str_replace(' ', " $prefix", $userPlugins); + + response($who[0].' Plugins: '.$prefix.'seen '.$prefix.$userPlugins); + + !empty(loadValueFromConfigFile('OWNER', 'bot.admin')) ? response("Bot Admin: ".loadValueFromConfigFile('OWNER', 'bot.admin')) : false; + + /* all else */ + } else { + $userPlugins = implode(' ', $GLOBALS[getStandardUserName().'_PLUGINS']); + $userPlugins = str_replace(' ', " $prefix", $userPlugins); + + if (!empty($GLOBALS[$who[0].'_PLUGINS'])) { + $ownPlugins = implode(' ', $GLOBALS[$who[0].'_PLUGINS']); + $ownPlugins = str_replace(' ', " $prefix", $ownPlugins); + $ownPlugins = $prefix.$ownPlugins.' '; + } else { + $ownPlugins = ''; + } + + + if (!empty(returnNextUsersCommands($who[1]))) { + $msg = implode(' ', returnNextUsersCommands($who[1])); + $msg = str_replace(' ', " $prefix", $msg); + $msg = $prefix.$msg.' '; + } else { + $msg = ''; + } + + response($who[0].' Plugins: '.$prefix.'seen '. + $ownPlugins. + $msg. + $prefix.$userPlugins); + + !empty(loadValueFromConfigFile('OWNER', 'bot.admin')) ? response("Bot Admin: ".loadValueFromConfigFile('OWNER', 'bot.admin')) : false; } } diff --git a/PLUGINS/USER/md5.php b/PLUGINS/USER/md5.php index 96fdbbb..964ec63 100644 --- a/PLUGINS/USER/md5.php +++ b/PLUGINS/USER/md5.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Changing string to MD5: {$GLOBALS['CONFIG.CMD.PREFIX']}md5 "; + $plugin_description = "Changing string to MD5: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."md5 "; $plugin_command = 'md5'; function plugin_md5() diff --git a/PLUGINS/USER/morse.php b/PLUGINS/USER/morse.php index ad9bb33..9b92fdf 100644 --- a/PLUGINS/USER/morse.php +++ b/PLUGINS/USER/morse.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Converts to morse code: {$GLOBALS['CONFIG.CMD.PREFIX']}morse "; + $plugin_description = "Converts to morse code: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."morse "; $plugin_command = 'morse'; function plugin_morse() @@ -42,6 +42,7 @@ function plugin_morse() $str = strtolower(inputFromLine('4')); $final = null; + for ($pos = 0; $pos < strlen($str); $pos++) { $care = $str[$pos]; if (array_key_exists($care, $morseCode)) { diff --git a/PLUGINS/USER/note.php b/PLUGINS/USER/note.php index 805cfa9..f6faa6b 100644 --- a/PLUGINS/USER/note.php +++ b/PLUGINS/USER/note.php @@ -21,7 +21,7 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Adds a note: {$GLOBALS['CONFIG.CMD.PREFIX']}note help to list commands"; + $plugin_description = "Adds a note: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."note help to list commands"; $plugin_command = 'note'; /* TODO: @@ -35,16 +35,16 @@ function plugin_note() { if (OnEmptyArg('note to list commands')) { } else { - $notesFilename = DATADIR."/".removeIllegalCharsFromNickname($GLOBALS['USER'])."-".$GLOBALS['host'].".txt"; + $notesFilename = DATADIR."/".removeIllegalCharsFromNickname(userPreg()[0])."-".userPreg()[2].".txt"; - switch ($GLOBALS['args']) { + switch (msgAsArguments()) { case 'help': response('Note commands:'); - response($GLOBALS['CONFIG.CMD.PREFIX'].'note add - Adds a note'); - response($GLOBALS['CONFIG.CMD.PREFIX'].'note clear - Delete all notes'); - response($GLOBALS['CONFIG.CMD.PREFIX'].'note del - Delete specified note'); - response($GLOBALS['CONFIG.CMD.PREFIX'].'note help - Shows help'); - response($GLOBALS['CONFIG.CMD.PREFIX'].'note list - List notes'); + response(loadValueFromConfigFile('COMMAND', 'command.prefix').'note add - Adds a note'); + response(loadValueFromConfigFile('COMMAND', 'command.prefix').'note clear - Delete all notes'); + response(loadValueFromConfigFile('COMMAND', 'command.prefix').'note del - Delete specified note'); + response(loadValueFromConfigFile('COMMAND', 'command.prefix').'note help - Shows help'); + response(loadValueFromConfigFile('COMMAND', 'command.prefix').'note list - List notes'); break; case 'list': @@ -70,9 +70,9 @@ function plugin_note() } break; } - switch ($GLOBALS['piece1']) { + switch (msgPieces()[0]) { case 'add': - if (!empty($GLOBALS['piece2'])) { + if (!empty(msgPieces()[1])) { /* if file is empty */ if (is_file($notesFilename) && filesize($notesFilename) == 0) { $note = inputFromLine('5'); @@ -97,7 +97,7 @@ function plugin_note() if (is_file($notesFilename)) { $writeNotes = ''; $notes = file($notesFilename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $i = $GLOBALS['piece2']; + $i = msgPieces()[1]; if (is_numeric((int)$i) && $i > 0) { $j = $i-1; diff --git a/PLUGINS/USER/webstatus.php b/PLUGINS/USER/webstatus.php index 9dea22e..1de4c1b 100644 --- a/PLUGINS/USER/webstatus.php +++ b/PLUGINS/USER/webstatus.php @@ -21,14 +21,14 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows http status: {$GLOBALS['CONFIG.CMD.PREFIX']}webstatus "; + $plugin_description = "Shows http status: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."webstatus "; $plugin_command = 'webstatus'; function plugin_webstatus() { if (OnEmptyArg('webstatus ')) { } else { - response(httpstatus($GLOBALS['args'])); + response(httpstatus(msgAsArguments())); } } diff --git a/PLUGINS/USER/webtitle.php b/PLUGINS/USER/webtitle.php index d473ce6..ce298f6 100644 --- a/PLUGINS/USER/webtitle.php +++ b/PLUGINS/USER/webtitle.php @@ -21,14 +21,14 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows webpage titile: {$GLOBALS['CONFIG.CMD.PREFIX']}webtitle "; + $plugin_description = "Shows webpage titile: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."webtitle "; $plugin_command = 'webtitle'; function plugin_webtitle() { if (OnEmptyArg('webtitle ')) { } elseif (extension_loaded('openssl')) { - $data = str_replace('http://', '', str_replace('https://', '', $GLOBALS['args'])); + $data = str_replace('http://', '', str_replace('https://', '', msgAsArguments())); if ($file = @file_get_contents('http://'.$data)) { if (preg_match('@([^<]{1,256}).*?@mi', $file, $matches)) { if (strlen($matches[1]) == 256) { diff --git a/PLUGINS/USER/wiki.php b/PLUGINS/USER/wiki.php index 5cc1e03..92b6567 100644 --- a/PLUGINS/USER/wiki.php +++ b/PLUGINS/USER/wiki.php @@ -21,14 +21,14 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Searchs wikipedia: {$GLOBALS['CONFIG.CMD.PREFIX']}wiki "; + $plugin_description = "Searchs wikipedia: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."wiki "; $plugin_command = 'wiki'; function plugin_wiki() { if (OnEmptyArg('wikipedia ')) { } elseif (extension_loaded('openssl')) { - $json = @file_get_contents("http://{$GLOBALS['piece1']}.wikipedia.org/w/api.php?action=opensearch&list=search&search=".urlencode(inputFromLine('5'))); + $json = @file_get_contents("http://".msgPieces()[0].".wikipedia.org/w/api.php?action=opensearch&list=search&search=".urlencode(inputFromLine('5'))); if (!empty($json)) { $json = json_decode($json); diff --git a/PLUGINS/OWNER/listadmins.php b/plugins/USER/whoami.php similarity index 72% rename from PLUGINS/OWNER/listadmins.php rename to plugins/USER/whoami.php index 7c02b24..72faa55 100644 --- a/PLUGINS/OWNER/listadmins.php +++ b/plugins/USER/whoami.php @@ -21,23 +21,18 @@ //--------------------------------------------------------------------------------------------------------- $VERIFY = 'bfebd8778dbc9c58975c4f09eae6aea6ad2b621ed6a6ed8a3cbc1096c6041f0c'; - $plugin_description = "Shows BOT admins: {$GLOBALS['CONFIG.CMD.PREFIX']}listadmins"; - $plugin_command = 'listadmins'; + $plugin_description = "Displays user assigned name and privilege level ".loadValueFromConfigFile('COMMAND', 'command.prefix')."whoami"; + $plugin_command = 'whoami'; -function plugin_listadmins() +function plugin_whoami() { - LoadData($GLOBALS['configFile'], 'ADMIN', 'admin.list'); - - if (empty($GLOBALS['LOADED'])) { - response('Empty admin(s) list.'); + if (whoIsUser()[1] == 0) { + $resp = '(OWNER level)'; + } else if (whoIsUser()[1] == 999) { + $resp = '(Normal User level)'; } else { - $admins = explode(", ", $GLOBALS['LOADED']); - - response('My Admin(s) Host(s):'); - - for ($i=0; $i ### Important! -Before running BOT you must configure your BOT by editing CONFIG.INI file. - -To be bot owner msg to bot by typing: /msg register -You will be added to owner hosts. +Before running the Bot, it must be configured in the file 'CONFIG.INI' +To become a Bot owner and have access to all commands +You have to write to Bot in a private message: +/msg register +After entering the correct password, you +will be added to the host and have access to all commands. You can also edit owner host in CONFIG.INI: @@ -22,7 +24,7 @@ example: S3x0r!~ident@hostname.com -Bot was writted to run from Windows systems (tested on Windows 10) +Bot was writted to run from Windows systems (tested on Windows 11) but you can also run it from Linux/Unix by typing: 'php BOT.php' To have almost all plugins working on Linux/Unix you need to enable two extension modules in your 'php.ini' config: @@ -34,18 +36,18 @@ modules: and set: allow_url_fopen=1 in php.ini From Windows systems you don't need to download PHP, just run bot from START_BOT.BAT -There is also silent mode without output to console & no logs, run: START_SILENT.BAT You can also run bot with arguments: On Windows: php.exe "../../BOT.php" -h (to list options) + or: php.exe BOT.php On Linux: php BOT.php -h To run bot with diffrent config file: php.exe "../../BOT.php" -c some_other_config.ini Access Hierarchy: -Owner has access to: CORE commands, ADMIN plugins & USER plugins -Admin has access to: ADMIN plugins & USER plugins -User has access to: USER plugins +Owner has access to: CORE, ADMIN & USER plugins +Admin has access to: ADMIN, USER plugins +User has access to: USER and some CORE plugins If you want to block some plugin(s) from user's or admin's just move it from folder to folder, etc... @@ -60,99 +62,62 @@ You can change prefix in config file. ## BOT Commands: -| Plugin | Description | Command | Permission | OS | -|------------------|------------------------------------------|-----------------------------------|--------------|-----| -| addadmin | Adds user host to ADMIN list in config | !addadmin | OWNER | All | -| addowner | Adds owner host to config file | !addowner | OWNER | All | -| autoop | Adds host to auto op list in config file | !autoop | OWNER/ADMIN | All | -| ban | Ban specified hostname | !ban | OWNER/ADMIN | All | -| cham | Shows random text from file | !cham | OWNER/ADMIN | All | -| checkupdate | Checking for updates | !checkupdate | OWNER | All | -| deop | Deops someone | !deop | OWNER/ADMIN | All | -| devoice | Devoice someone | !devoice | OWNER/ADMIN | All | -| gethost | Ip address to hostname | !gethost | OWNER/ADMIN | All | -| fetch | Plugins repository list / get | !fetch list | OWNER | All | -| | Downloads plugins from repository | !fetch get | OWNER | All | -| hash | Changing string to choosed algorithm | !hash | ALL | All | -| | Lists available algorithms | !hash help | ALL | All | -| help | Shows BOT commands | !help | ALL | All | -| info | Shows BOT information | !info | OWNER | All | -| join | BOT joins given channel | !join <#channel> | OWNER | All | -| kick | BOT kicks given user from channel | !kick <#channel> | OWNER/ADMIN | All | -| leave | BOT parts given channel | !leave <#channel> | OWNER | All | -| listadmins | Shows BOT admin(s) host(s) | !listadmins | OWNER | All | -| listowners | Shows BOT owner(s) host(s) | !listowners | OWNER | All | -| load | Loads plugin to BOT | !load | OWNER | All | -| md5 | Changing string to MD5 hash | !md5 | ALL | All | -| memusage | Shows how much ram is being used by BOT | !memusage | OWNER | All | -| morse | Converts given string to morse code | !morse | ALL | All | -| note | Adds a note | !note | ALL | All | -| | Delete all notes | !note clear | ALL | All | -| | Delete specified note | !note del | ALL | All | -| | Shows help | !note help | ALL | All | -| | Lists notes | !note list | ALL | All | -| newnick | Changes BOT nickname | !newnick | OWNER | All | -| op | BOT gives op to given nick | !op | OWNER/ADMIN | All | -| panel | Starts web admin panel for BOT | !panel | OWNER | WIN | -| | Lists panel commands | !panel help | OWNER | WIN | -| | Starts web panel at specified port | !panel start | OWNER | WIN | -| | Stops web panel | !panel stop | OWNER | WIN | -| pause | Pause BOT activity (plugins use, etc) | !pause | OWNER | All | -| plugin | Plugins manipulation | !plugin | OWNER | All | -| | Deletes plugin from directory | !plugin delete | OWNER | All | -| | Lists plugin commands | !plugin help | OWNER | All | -| | Loads given plugin to BOT | !plugin load | OWNER | All | -| | Move plugin from one group to another | !plugin move | OWNER | All | -| | Unloads plugin from BOT | !plugin unload | OWNER | All | -| ping | Ping given host/ip | !ping | OWNER/ADMIN | WIN | -| quit | Shutdown BOT | !quit | OWNER | All | -| raw | Sends raw string to server | !raw <2> <3> <4> | OWNER | All | -| remadmin | Removes admin from config file | !remadmin | OWNER | All | -| remowner | Removes owner host from config file | !remowner | OWNER | All | -| restart | Restarts BOT | !restart | OWNER | All | -| ripe | Checks ip address and show results | !ripe | OWNER/ADMIN | All | -| save | Saving to config file | !save | OWNER | All | -| | Saving auto join value in config | !save auto_join | OWNER | All | -| | Saving auto op value in config | !save auto_op | OWNER | All | -| | Saving auto op list value in config | !save auto_op_list | OWNER | All | -| | Saving auto rejoin value in config | !save auto_rejoin | OWNER | All | -| | Saving bot owners value in config | !save bot_owners | OWNER | All | -| | Saving bot response value in config | !save bot_response | OWNER | All | -| | Saving command prefix value in config | !save command_prefix | OWNER | All | -| | Saving connect delay value in config | !save connect_delay | OWNER | All | -| | Saving ctcp finger value in config | !save ctcp_finger | OWNER | All | -| | Saving ctcp response value in config | !save ctcp_response | OWNER | All | -| | Saving ctcp version value in config | !save ctcp_version | OWNER | All | -| | Saving channel value in config | !save channel | OWNER | All | -| | Saving fetch server value in config | !save fetch_server | OWNER | All | -| | Saving ident value in config | !save ident | OWNER | All | -| | Saving logging value in config | !save logging | OWNER | All | -| | Saving name value in config | !save name | OWNER | All | -| | Saving nick value in config | !save nick | OWNER | All | -| | Saving owner password value in config | !save owner_password | OWNER | All | -| | Saving port value in config | !save port | OWNER | All | -| | Saving server value in config | !save server | OWNER | All | -| | Saving show raw value in config | !save show_raw | OWNER | All | -| | Saving time zone value in config | !save time_zone | OWNER | All | -| | Saving try connect value in config | !save try_connect | OWNER | All | -| say | Say specified text to channel | !say | OWNER/ADMIN | All | -| seen | Check nick when was last seen on channel | !seen | ALL | All | -| server | Connects to specified server | !server | OWNER | All | -| showconfig | Shows BOT configuration | !showconfig | OWNER | All | -| topic | Changes topic on channel | !topic | OWNER/ADMIN | All | -| unload | Unloads plugin from BOT | !unload | OWNER | All | -| update | Updates BOT if new version is available | !update | OWNER | All | -| uptime | Shows BOT uptime | !uptime | OWNER/ADMIN | All | -| unban | Unban specified user/hostmask | !unban | OWNER/ADMIN | All | -| unpause | Restore BOT from !pause mode | !unpause | OWNER | All | -| voice | BOT gives voice | !voice | OWNER/ADMIN | All | -| webstatus | Shows http status info from given number | !webstatus | ALL | All | -| webtitle | Shows webpage titile | !webtitle | ALL | All | -| wiki | Search wikipedia | !wiki | ALL | All | -| winamp | Controls winamp | !winamp | OWNER | WIN | -| | Next song | !winamp next | OWNER | WIN | -| | Pause song | !winamp pause | OWNER | WIN | -| | Play song | !winamp play | OWNER | WIN | -| | Previous song | !winamp prev | OWNER | WIN | -| | Stop song | !winamp stop | OWNER | WIN | -| | Shows song title | !winamp title | OWNER | WIN | \ No newline at end of file +| Plugin | Description | Command | Permission | OS | +|------------------|-------------------------------------------|-----------------------------------|--------------|-----| +| addowner | Adds owner host to config file | !addowner | OWNER | All | +| autoop | Adds host to auto op list in config file | !autoop | OWNER/ADMIN | All | +| ban | Ban specified hostname | !ban | OWNER/ADMIN | All | +| cham | Shows random text from file | !cham | OWNER/ADMIN | All | +| checkupdate | Checking for updates | !checkupdate | OWNER | All | +| deop | Deops someone | !deop | OWNER/ADMIN | All | +| devoice | Devoice someone | !devoice | OWNER/ADMIN | All | +| gethost | Ip address to hostname | !gethost | OWNER/ADMIN | All | +| fetch | Plugins repository list / get | !fetch list | OWNER | All | +| | Downloads plugins from repository | !fetch get | OWNER | All | +| hash | Changing string to choosed algorithm | !hash | ALL | All | +| | Lists available algorithms | !hash help | ALL | All | +| help | Shows BOT commands | !help | ALL | All | +| info | Shows BOT information | !info | OWNER | All | +| join | BOT joins given channel | !join <#channel> | OWNER | All | +| kick | BOT kicks given user from channel | !kick <#channel> | OWNER/ADMIN | All | +| leave | BOT parts given channel | !leave <#channel> | OWNER | All | +| listowners | Shows BOT owner(s) host(s) | !listowners | OWNER | All | +| load | Loads plugin to BOT | !load | OWNER | All | +| md5 | Changing string to MD5 hash | !md5 | ALL | All | +| memusage | Shows how much ram is being used by BOT | !memusage | OWNER | All | +| morse | Converts given string to morse code | !morse | ALL | All | +| note | Adds a note | !note | ALL | All | +| | Delete all notes | !note clear | ALL | All | +| | Delete specified note | !note del | ALL | All | +| | Shows help | !note help | ALL | All | +| | Lists notes | !note list | ALL | All | +| newnick | Changes BOT nickname | !newnick | OWNER | All | +| op | BOT gives op to given nick | !op | OWNER/ADMIN | All | +| pause | Pause BOT activity (plugins use, etc) | !pause | OWNER | All | +| ping | Ping given host/ip | !ping | OWNER/ADMIN | WIN | +| quit | Shutdown BOT | !quit | OWNER | All | +| raw | Sends raw string to server | !raw <2> <3> <4> | OWNER | All | +| remowner | Removes owner host from config file | !remowner | OWNER | All | +| restart | Restarts BOT | !restart | OWNER | All | +| ripe | Checks ip address and show results | !ripe | OWNER/ADMIN | All | +| say | Say specified text to channel | !say | OWNER/ADMIN | All | +| seen | Check nick when was last seen on channel | !seen | ALL | All | +| server | Connects to specified server | !server | OWNER | All | +| topic | Changes topic on channel | !topic | OWNER/ADMIN | All | +| unload | Unloads plugin from BOT | !unload | OWNER | All | +| update | Updates BOT if new version is available | !update | OWNER | All | +| uptime | Shows BOT uptime | !uptime | OWNER/ADMIN | All | +| unban | Unban specified user/hostmask | !unban | OWNER/ADMIN | All | +| unpause | Restore BOT from !pause mode | !unpause | OWNER | All | +| voice | BOT gives voice | !voice | OWNER/ADMIN | All | +| webstatus | Shows http status info from given number | !webstatus | ALL | All | +| webtitle | Shows webpage titile | !webtitle | ALL | All | +| whoami | Displays user assigned name/privilege lvl | !whoami | ALL | All | +| wiki | Search wikipedia | !wiki | ALL | All | +| winamp | Controls winamp | !winamp | OWNER | WIN | +| | Next song | !winamp next | OWNER | WIN | +| | Pause song | !winamp pause | OWNER | WIN | +| | Play song | !winamp play | OWNER | WIN | +| | Previous song | !winamp prev | OWNER | WIN | +| | Stop song | !winamp stop | OWNER | WIN | +| | Shows song title | !winamp title | OWNER | WIN | \ No newline at end of file diff --git a/src/args.php b/src/args.php index 81ac891..5e243ce 100644 --- a/src/args.php +++ b/src/args.php @@ -25,8 +25,9 @@ function checkArgs() switch ($_SERVER['argv'][1]) { case '-h': /* show help */ echo N.' Bot cli commands usage: php BOT.php -[option]'.NN, - ' -c # loads config from the specified path (eg. C:\my folder\CONFIG.INI)'.N, /* config file */ + ' -c # loads config from the specified path (eg. C:\my folder\\'.getConfigFileName().')'.N, /* config file */ ' -h # this help'.N, /* help */ + ' -n # set bot nickname'.N, /* change bot nickname */ ' -o # connect to specified server: (eg. php BOT.php -o irc.dal.net 6667)'.N, /* server */ ' -p # hash password to SHA256'.N, /* hash */ ' -u # check if there is new bot version'.N, /* update */ @@ -34,12 +35,19 @@ function checkArgs() exit; break; + case '-n': /* set nickname */ + if (!empty($_SERVER['argv'][2])) { + SaveValueToConfigFile('BOT', 'nickname', $_SERVER['argv'][2]); + } else { + echo ' [ERROR] You need to specify BOT nickname! Exiting.'.NN; + exit; + } + break; + case '-c': /* check if config is loaded from -c switch */ - if (!empty($_SERVER['argv'][2]) && is_file($_SERVER['argv'][2])) { - $GLOBALS['configFile'] = $_SERVER['argv'][2]; - } elseif (!empty($_SERVER['argv'][2]) && !is_file($_SERVER['argv'][2])) { - echo ' [ERROR] Config file does not exist, wrong path?'.NN; - exit; + if (!empty($_SERVER['argv'][2]) && !is_file($_SERVER['argv'][2])) { + echo ' [ERROR] Config file does not exist, wrong path?'.NN; + exit; } elseif (empty($_SERVER['argv'][2])) { echo ' [ERROR] You need to specify config file! I need some data :)'.NN; exit; @@ -48,8 +56,8 @@ function checkArgs() case '-o': /* server connect: eg: irc.example.net 6667 */ if (!empty($_SERVER['argv'][2]) && !empty($_SERVER['argv'][3]) && is_numeric($_SERVER['argv'][3])) { - $GLOBALS['CONFIG.SERVER'] = $_SERVER['argv'][2]; - $GLOBALS['CONFIG.PORT'] = $_SERVER['argv'][3]; + SaveValueToConfigFile('SERVER', 'server', $_SERVER['argv'][2]); + SaveValueToConfigFile('SERVER', 'port', $_SERVER['argv'][3]); } elseif (empty($_SERVER['argv'][2])) { echo N.' ERROR: You need to specify server address, Exiting.'; exit; @@ -79,13 +87,13 @@ function checkArgs() $answer = trim(fgets($STDIN)); if ($answer == 'yes' xor $answer == 'y') { - if (is_file('CONFIG.INI')) { - SaveData('CONFIG.INI', 'OWNER', 'owner.password', hash('sha256', rtrim($pwd, "\n\r"))); + if (is_file(getConfigFileName())) { + SaveValueToConfigFile('OWNER', 'owner.password', hash('sha256', rtrim($pwd, "\n\r"))); echo N.' Password saved to config file, Exiting.'; WinSleep(6); exit; } else { - echo N.' Cannot find CONFIG.INI file, exiting!'; + echo N." Cannot find '".getConfigFileName()."' file, exiting!"; WinSleep(5); exit; } @@ -102,11 +110,11 @@ function checkArgs() if (extension_loaded('openssl')) { $file = @file_get_contents(VERSION_URL); if (!empty($file)) { - $serverVersion = explode("\n", $file); - if ($serverVersion[0] > VER) { + $version = explode("\n", $file); + if ($version[0] > VER) { echo N.' New version available!'.N; echo N.' My version: '.VER; - echo N.' Version on server: '.$serverVersion[0].N; + echo N.' Version on server: '.$version[0].N; echo N.' To update BOT msg to bot by typing: !update'.NN; WinSleep(10); exit; diff --git a/src/cli.php b/src/cli.php index fe6a342..1e4b806 100644 --- a/src/cli.php +++ b/src/cli.php @@ -20,33 +20,33 @@ 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- -function line() +function cliLine() { echo '------------------------------------------------------------------------------'.N; } //--------------------------------------------------------------------------------------------------------- -function cli($data) +function cli($data) /* no logging */ { echo $data.N; } //--------------------------------------------------------------------------------------------------------- -function cliDebug($data, $mode) +function cliDebug($data, $mode) /* debug message */ { - if ($GLOBALS['CONFIG.SHOW.RAW'] == 'yes') { + if (loadValueFromConfigFile('DEBUG', 'show.raw') == 'yes') { if ($mode == 0) { echo "[DEBUG <-] $data"; - } else if ($mode == 1 && $GLOBALS['CONFIG.OWN.MSGS.IN.RAW.MODE'] == 'yes') { + } else if ($mode == 1 && loadValueFromConfigFile('DEBUG', 'show.own.messages.in.raw.mode') == 'yes') { echo "[DEBUG ->] $data".N; } } } //--------------------------------------------------------------------------------------------------------- -function cliLog($data) +function cliLog($data) /* log message +time */ { $line = "[".@date('H:i:s')."] {$data}".N; - if (isset($GLOBALS['CONFIG.LOGGING']) && $GLOBALS['CONFIG.LOGGING'] == 'yes') { - SaveToFile($GLOBALS['logFileName'], $line, 'a'); + if (loadValueFromConfigFile('LOGS', 'logging') == 'yes') { + SaveToFile(logFileNameFormat(), $line, 'a'); } echo $line; @@ -58,7 +58,7 @@ function Baner() echo ' ---------------------------------------------------------'.N; /* os var */ - !isset($GLOBALS['OS']) ? $system = 'Windows' : $system = 'Linux'; + (ifWindowsOs()) ? $system = 'Windows' : $system = 'Linux'; /* check if we have needed extensions */ if (extension_loaded('curl') && extension_loaded('openssl')) { @@ -84,9 +84,9 @@ function CheckUpdateInfo() $file = @file_get_contents(VERSION_URL); if (!empty($file)) { - $serverVersion = explode("\n", $file); - if ($serverVersion[0] > VER) { - echo " >>>> New version available! ($serverVersion[0]) <<<<".NN.N; + $version = explode("\n", $file); + if ($version[0] > VER) { + echo " >>>> New version available! ($version[0]) <<<<".NN.N; } else { echo " >>>> No new update, you have the latest version <<<<".NN.N; } @@ -100,5 +100,12 @@ function CheckUpdateInfo() //--------------------------------------------------------------------------------------------------------- function pluginUsageCli($pluginName) { - cliLog("[PLUGIN: {$pluginName}] Used by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); + cliLog("[PLUGIN: {$pluginName}] Used by: ".userPreg()[0]." (".userPreg()[3]."), channel: ".getBotChannel()); } +//--------------------------------------------------------------------------------------------------------- +function debug($data) +{ + if (loadValueFromConfigFile('DEBUG', 'show.debug') == 'yes') { + echo "[".@date('H:i:s')."] [DEBUG] {$data}".N; + } +} \ No newline at end of file diff --git a/src/config.php b/src/config.php index 84c4b87..6762d54 100644 --- a/src/config.php +++ b/src/config.php @@ -20,251 +20,192 @@ 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- -function LoadConfig() -{ - $cfg = new IniParser($GLOBALS['configFile']); - - /* load configuration */ - - /* BOT */ - $GLOBALS['CONFIG.NICKNAME'] = $cfg->get('BOT', 'nickname'); - $GLOBALS['CONFIG.NAME'] = $cfg->get('BOT', 'name'); - $GLOBALS['CONFIG.IDENT'] = $cfg->get('BOT', 'ident'); - - /* SERVER */ - !isset($GLOBALS['CONFIG.SERVER']) ? $GLOBALS['CONFIG.SERVER'] = $cfg->get('SERVER', 'server') : false; - !isset($GLOBALS['CONFIG.PORT']) ? $GLOBALS['CONFIG.PORT'] = $cfg->get('SERVER', 'port') : false; - - $GLOBALS['CONFIG.SERVER.PASSWD'] = $cfg->get('SERVER', 'server.password'); - $GLOBALS['CONFIG.TRY.CONNECT'] = $cfg->get('SERVER', 'try.connect'); - $GLOBALS['CONFIG.CONNECT.DELAY'] = $cfg->get('SERVER', 'connect.delay'); - $GLOBALS['CONFIG.SHOW.MOTD'] = $cfg->get('SERVER', 'show.motd'); - - /* OWNER */ - $GLOBALS['CONFIG.BOT.ADMIN'] = $cfg->get('OWNER', 'bot.admin'); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get('OWNER', 'auto.op.list'); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get('OWNER', 'bot.owners'); - $GLOBALS['CONFIG.OWNER.PASSWD'] = $cfg->get('OWNER', 'owner.password'); - - /* ADMIN */ - $GLOBALS['CONFIG.ADMIN.LIST'] = $cfg->get('ADMIN', 'admin.list'); - - /* BOT RESPONSE */ - $GLOBALS['CONFIG.BOT.RESPONSE'] = $cfg->get('RESPONSE', 'bot.response'); - - /* AUTOMATIC */ - $GLOBALS['CONFIG.AUTO.OP'] = $cfg->get('AUTOMATIC', 'auto.op'); - $GLOBALS['CONFIG.AUTO.REJOIN'] = $cfg->get('AUTOMATIC', 'auto.rejoin'); - $GLOBALS['CONFIG.KEEPCHAN.MODES'] = $cfg->get('AUTOMATIC', 'keep.chan.modes'); - $GLOBALS['CONFIG.KEEP.NICK'] = $cfg->get('AUTOMATIC', 'keep.nick'); - - /* CHANNEL */ - $GLOBALS['CONFIG.CHANNEL'] = $cfg->get('CHANNEL', 'channel'); - $GLOBALS['CONFIG.AUTO.JOIN'] = $cfg->get('CHANNEL', 'auto.join'); - $GLOBALS['CONFIG.CHANNEL.MODES'] = $cfg->get('CHANNEL', 'channel.modes'); - $GLOBALS['CONFIG.CHANNEL.KEY'] = $cfg->get('CHANNEL', 'channel.key'); - - /* BANS */ - $GLOBALS['CONFIG.BAN.LIST'] = $cfg->get('BANS', 'ban.list'); - - /* COMMAND PREFIX */ - $GLOBALS['CONFIG.CMD.PREFIX'] = $cfg->get('COMMAND', 'command.prefix'); - - /* CTCP */ - $GLOBALS['CONFIG.CTCP.RESPONSE'] = $cfg->get('CTCP', 'ctcp.response'); - $GLOBALS['CONFIG.CTCP.VERSION'] = $cfg->get('CTCP', 'ctcp.version'); - $GLOBALS['CONFIG.CTCP.FINGER'] = $cfg->get('CTCP', 'ctcp.finger'); - - /* DELAYS */ - $GLOBALS['CONFIG.CHANNEL.DELAY'] = $cfg->get('DELAYS', 'channel.delay'); - $GLOBALS['CONFIG.PRIVATE.DELAY'] = $cfg->get('DELAYS', 'private.delay'); - $GLOBALS['CONFIG.NOTICE.DELAY'] = $cfg->get('DELAYS', 'notice.delay'); - - /* LOGGING */ - $GLOBALS['CONFIG.LOGGING'] = $cfg->get('LOGS', 'logging'); - - /* PANEL */ - $GLOBALS['CONFIG.WEB.LOGIN'] = $cfg->get('PANEL', 'web.login'); - $GLOBALS['CONFIG.WEB.PASSWORD'] = $cfg->get('PANEL', 'web.password'); - - /* TIMEZONE */ - $GLOBALS['CONFIG.TIMEZONE'] = $cfg->get('TIME', 'time.zone'); - - /* FETCH */ - $GLOBALS['CONFIG.FETCH.SERVER'] = $cfg->get('FETCH', 'fetch.server'); - - /* PROGRAM */ - $GLOBALS['CONFIG.PLAY.SOUNDS'] = $cfg->get('PROGRAM', 'play.sounds'); - - /* DEBUG */ - $GLOBALS['CONFIG.SHOW.RAW'] = $cfg->get('DEBUG', 'show.raw'); - $GLOBALS['CONFIG.OWN.MSGS.IN.RAW.MODE'] = $cfg->get('DEBUG', 'show.own.messages.in.raw.mode'); - - /* from what file config loaded */ - cliLog("[bot] Configuration Loaded from: {$GLOBALS['configFile']}"); - - line(); -} -//--------------------------------------------------------------------------------------------------------- -function CreateDefaultConfig($filename) +function CreateDefaultDataConfigFile() { /* default config */ - $defaultConfigData = '; + $configData = +" [BOT] ; bot nickname -nickname = \'minion\' +nickname = 'minion' ; bot name -name = \'http://github.com/S3x0r/MINION\' +name = 'http://github.com/S3x0r/MINION' ; bot ident -ident = \'minion\' +ident = 'minion' [SERVER] -; server where to connect -server = \'irc.dal.net\' +; irc server +server = 'irc.pirc.pl' -; server port -port = \'6667\' +; irc server port +port = '6667' -; if irc server have password -server.password = \'\' +; If the irc server requires a password to connect, set it here +server.password = '' -; try connect \'n\' (in seconds) times to server, if cannot then quit -try.connect = \'99\' +; Try to connect to the server the amount set here, if after a certain amount the bot fails to connect then terminate the program +try.connect = '99' -; delay (in seconds) after new connection to server -connect.delay = \'6\' +; Pause (seconds) between connections to the server +connect.delay = '6' -; show message of the day -show.motd = \'yes\' +; show server message of the day +show.motd = 'no' [OWNER] ; bot administrator information -bot.admin = \'S3x0r \' - -; bot will give op\'s if this hosts join channel -auto.op.list = \'\' - -; BOT OWNER HOSTS -bot.owners = \'\' +bot.admin = 'minion ' -; owner password (SHA256) -owner.password = \'47a8f9b32ec41bd93d79bf6c1c924aaecaa26d9afe88c39fc3a638f420f251ed\' +; owner bot password (SHA256) +owner.password = '47a8f9b32ec41bd93d79bf6c1c924aaecaa26d9afe88c39fc3a638f420f251ed' -[ADMIN] +[PRIVILEGES] -; bot admin list -admin.list = \'\' +; BOT OWNER HOSTS +OWNER = '' +; other users hosts +ADMIN = '' +; other users hosts +USER = '' + +[USERSLEVELS] + +;users access levels +; Owner = 0 +; Normal User = 999 +; others from 1 to 998 +OWNER = '0' +ADMIN = '1' +USER = '999' [RESPONSE] ; where bot should response, you can choose from: channel, notice, priv -bot.response = \'notice\' +bot.response = 'notice' [AUTOMATIC] -; bot will give op when join to channel from auto.op.list: \'yes\', \'no\' -auto.op = \'yes\' +; bot will give op when join to channel from auto.op.list: 'yes', 'no' +auto.op = 'yes' + +; bot will give op when hosts join channel +auto.op.list = '' -; bot will auto rejoin channel when kicked: \'yes\', \'no\' -auto.rejoin = \'yes\' +; bot will auto rejoin channel when kicked: 'yes', 'no' +auto.rejoin = 'yes' ; do we want to keep channel modes from channel.modes option? -keep.chan.modes = \'yes\' +keep.chan.modes = 'yes' ; this setting makes the bot try to get his original nickname back if its primary nickname is already in use -keep.nick = \'yes\' +keep.nick = 'yes' [CHANNEL] ; channel where to join when connected -channel = \'#minion\' +channel = '#minion' -; auto join channel when connected: \'yes\', \'no\' -auto.join = \'yes\' +; auto join channel when connected: 'yes', 'no' +auto.join = 'yes' ; set channel modes on bot join -channel.modes = \'nt\' +channel.modes = 'nt' ; channel key if exists -channel.key = \'\' +channel.key = '' [BANS] ; ban users from this list -ban.list = \'nick!ident@hostname, *!ident@hostname, *!*@onlyhost\' +ban.list = 'nick!ident@hostname, *!ident@hostname, *!*@onlyhost' [COMMAND] ; bot commands prefix eg. !info, you can change to what you want -command.prefix = \'!\' +command.prefix = '!' [CTCP] -; response to ctcp requests? \'yes\', \'no\' -ctcp.response = \'yes\' +; response to ctcp requests? 'yes', 'no' +ctcp.response = 'yes' ; ctcp version response (please do not change it:) -ctcp.version = \'MINION ('.VER.') powered by minions!\' +ctcp.version = 'minion (".VER.") powered by minions!' ; ctcf finger response -ctcp.finger = \'MINION\' +ctcp.finger = 'minion' [DELAYS] ; bot response delay on channel (in seconds) -channel.delay = \'1.5\' +channel.delay = '1.5' ; bot response delay on private messages (in seconds) -private.delay = \'1\' +private.delay = '1' ; bot response delay on notice messages (in seconds) -notice.delay = \'1\' +notice.delay = '1' [LOGS] -; log CLI messages to LOGS folder? \'yes\', \'no\' -logging = \'yes\' - -[PANEL] - -; web panel login -web.login = \'changeme\' - -; web panel password -web.password = \'changeme\' +; log CLI messages to LOGS folder? 'yes', 'no' +logging = 'yes' [TIME] ; bot time zone -time.zone = \'Europe/Warsaw\' +time.zone = 'Europe/Warsaw' [FETCH] ; bot plugin repository address -fetch.server = \'https://raw.githubusercontent.com/S3x0r/minion_repository_plugins/master\' +fetch.server = 'https://raw.githubusercontent.com/S3x0r/minion_repository_plugins/master' [PROGRAM] -; if we want to play sounds?: \'yes\', \'no\' -play.sounds = \'yes\' +; if we want to play sounds?: 'yes', 'no' +play.sounds = 'yes' [DEBUG] ; show raw output on CLI window -show.raw = \'no\' -show.own.messages.in.raw.mode = \'no\''; +show.raw = 'no' +show.own.messages.in.raw.mode = 'no' +show.debug = 'no' +"; /* Save default config to file if no config */ - SaveToFile($filename, $defaultConfigData, 'w'); - - /* remove variable */ - unset($defaultConfigData); + SaveToFile(getConfigFileName(), $configData, 'w'); +} +//--------------------------------------------------------------------------------------------------------- +function isConfigProvidedFromArgument() +{ + if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '-c' && !empty($_SERVER['argv'][2]) && is_file($_SERVER['argv'][2])) { + return true; + } else { + return false; + } +} +//--------------------------------------------------------------------------------------------------------- +function getConfigFileName() +{ + if (isConfigProvidedFromArgument()) { + return $_SERVER['argv'][2]; + } else { + return CONFIGFILE; + } +} +//--------------------------------------------------------------------------------------------------------- +function loadValueFromConfigFile($section, $value) +{ + $cfg = new IniParser(getConfigFileName()); + return $cfg->get("$section", "$value"); } //--------------------------------------------------------------------------------------------------------- /* configuration file parser */ @@ -334,6 +275,7 @@ public function save($file = null) } if (is_writeable($file)) { $desc = fopen($file, "w"); + fwrite($desc, "\r\n"); foreach ($this->iniParsedArray as $sec => $array) { fwrite($desc, "[" . $sec . "]\r\n"); foreach ($array as $key => $value) { diff --git a/src/core_cmnds.php b/src/core_cmnds.php new file mode 100644 index 0000000..259bc8b --- /dev/null +++ b/src/core_cmnds.php @@ -0,0 +1,201 @@ + + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +//--------------------------------------------------------------------------------------------------------- + !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? + exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. + 'Visit this page for more information.') : false; +//--------------------------------------------------------------------------------------------------------- + +function plugin_seen() +{ + if (OnEmptyArg('seen to check specified user when was last seen on channel')) { + } else { /* prevent directory traversal */ + $data = str_replace('..', '', str_replace('/', '', msgAsArguments())); //to sprawdzić czy ok? + if ($data == getBotNickname()) { + response('Yes im here! :)'); + } elseif ($data == userPreg()[0]) { + response('Look at mirror!'); + } elseif ($data == 'owner') { + !empty(loadValueFromConfigFile('OWNER', 'bot.admin')) ? response('My Owner: '.loadValueFromConfigFile('OWNER', 'bot.admin')) : false; + } else { + /* revert from illegal chars file */ + $data = removeIllegalCharsFromNickname($data); + + is_file(DATADIR.'/'.SEENDIR.'/'.$data) ? + response(file_get_contents(DATADIR.'/'.SEENDIR.'/'.$data)) : response('No such user in my database.'); + + } + } +} +//--------------------------------------------------------------------------------------------------------- +function plugin_pause() +{ + response('Pausing all activity'); + + setPause(); + + cliLog("[PLUGIN: pause] Used by: ".userPreg()[0]." (".userPreg()[3]."), channel: ".getBotChannel()); + cliLog('[bot] Im in Pause mode'); +} +//--------------------------------------------------------------------------------------------------------- +function plugin_unpause() +{ + unsetPause(); + + response('Back to life!'); + + cliLog("[PLUGIN: unpause] Used by: ".userPreg()[0]." (".userPreg()[3]."), channel: ".getBotChannel()); + cliLog('[bot] Unpaused'); +} +//--------------------------------------------------------------------------------------------------------- +function plugin_load() +{ + if (empty(msgAsArguments())) { + response("Usage ".loadValueFromConfigFile('COMMAND', 'command.prefix')."load "); + } else { + if (!empty(msgPieces()[0])) { + $plugin = msgPieces()[0]; + $users = usersDirectoriesToArray(); + $lastKey = end($users); + + foreach($users as $user) { + if (@in_array($plugin, $GLOBALS[$user.'_PLUGINS'])) { + response('Plugin already loaded!'); + break; + } else { + if (@!in_array($plugin, $GLOBALS[$user.'_PLUGINS']) && is_file(PLUGINSDIR.'/'.$user.'/'.$plugin.'.php')) { + include_once(PLUGINSDIR.'/'.$user.'/'.$plugin.'.php'); + + /* add plugin name to plugins array */ + array_push($GLOBALS[$user.'_PLUGINS'], $plugin); + + if (($key = array_search($plugin, $GLOBALS['ALL_PLUGINS'])) !== true) { + array_push($GLOBALS['ALL_PLUGINS'], $plugin); + } + + response("Plugin: '".$plugin."' loaded."); + break; + } else { + if ($user == $lastKey) { + response('No such plugin to load.'); + } + } + } + } + } + } +} +//--------------------------------------------------------------------------------------------------------- +function plugin_unload() +{ + if (empty(msgAsArguments())) { + response("Usage ".loadValueFromConfigFile('COMMAND', 'command.prefix')."unload "); + } else { + $plugin = msgPieces()[0]; + $users = usersDirectoriesToArray(); + $lastKey = end($users); + + foreach($users as $user) { + if (@in_array($plugin, $GLOBALS[$user.'_PLUGINS'])) { + + if (($key = array_search($plugin, $GLOBALS[$user.'_PLUGINS'])) !== false) { + unset($GLOBALS[$user.'_PLUGINS'][$key]); + + if (($key = array_search($plugin, $GLOBALS['ALL_PLUGINS'])) !== false) { + unset($GLOBALS['ALL_PLUGINS'][$key]); + } + + if (!in_array($plugin, $GLOBALS[$user.'_PLUGINS'])) { + response("Plugin: '{$plugin}' unloaded."); + break; + } + } + } + if ($user == $lastKey) { + response('No such plugin to unload.'); + } + } + } +} +//--------------------------------------------------------------------------------------------------------- +function plugin_register() +{ + $owner = getOwnerUserName(); + + /* if owner host is empty */ + if (empty(loadValueFromConfigFile('PRIVILEGES', $owner))) { + if (!isUserOwner()) { + /* hash message from user to use for comparsion */ + $hashed = hash('sha256', msgAsArguments()); + + /* if user password match password in config do the rest */ + if ($hashed == loadValueFromConfigFile('OWNER', 'owner.password')) { + $botOwners = loadValueFromConfigFile('PRIVILEGES', $owner); + + cliLog("[bot] Successful registration as owner from: ".userPreg()[0]." (".userFullMask().")"); + + $new = trim(userFullMask()); + + empty($botOwners) ? $newList = $new : $newList = "{$botOwners}, {$new}"; + + SaveValueToConfigFile('PRIVILEGES', $owner, $newList); + + /* Add host to auto op list */ + $autoOpList = loadValueFromConfigFile('AUTOMATIC', 'auto.op.list'); + + $new = trim(userFullMask()); + + empty($autoOpList) ? $newList = $new : $newList = "{$autoOpList}, {$new}"; + + SaveValueToConfigFile('AUTOMATIC', 'auto.op.list', $newList); + + /* cli msg */ + cliLog("[bot] New Owner added: ".userPreg()[0]." (".userFullMask().")"); + cliLog("[bot] New Auto Op added: ".userPreg()[0]." (".userFullMask().")"); + + /* send information to user about commands */ + response('From now you are my owner, enjoy!'); + + /* show core Plugins */ + $response = null; + + foreach (CORECOMMANDSLIST as $corePlugin => $corePluginDescription) { + $response .= loadValueFromConfigFile('COMMAND', 'command.prefix').$corePlugin.' '; + } + + response("Core Plugins: ".$response); + + $prefix = loadValueFromConfigFile('COMMAND', 'command.prefix'); + + $plug = implode(' ', $GLOBALS['ALL_PLUGINS']); + $plug = str_replace(' ', " $prefix", $plug); + $plug = $prefix.$plug.' '; + + response('All Plugins: '.$plug); + + /* give op */ + if (BotOpped() == true) { + toServer("MODE ".getBotChannel()." +o ".userPreg()[0]); + } + } + } else { + $hashed = hash('sha256', msgAsArguments()); + /* if user is already an owner */ + $hashed == loadValueFromConfigFile('OWNER', 'owner.password') ? response('You are already my owner') : false; + } + } +} diff --git a/src/web.php b/src/core_cmnds_helpers.php similarity index 55% rename from src/web.php rename to src/core_cmnds_helpers.php index 953e6e4..0155553 100644 --- a/src/web.php +++ b/src/core_cmnds_helpers.php @@ -20,34 +20,38 @@ 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- -function WebEntry() +function SeenSave() { - $data = "[MAIN] -WEB_VERSION = ".VER." -WEB_START_TIME = ".START_TIME." -WEB_PHP_VERSION = ".PHP_VER." -WEB_BOT_CONFIG_FILE = ".$GLOBALS['configFile']; + !is_dir(DATADIR.'/'.SEENDIR) ? @mkdir(DATADIR.'/'.SEENDIR) : false; - /* save some variables to web.ini */ - SaveToFile('src/panel/web.ini', $data, 'w'); + $seenDataFull = DATADIR.'/'.SEENDIR.'/'; - $file = $GLOBALS['configFile']; - $cfg = new IniParser($file); - $GLOBALS['CONFIG.WEB.LOGIN'] = $cfg->get('PANEL', 'web.login'); - $GLOBALS['CONFIG.WEB.PASSWORD'] = $cfg->get('PANEL', 'web.password'); - - /* generate random string for cookie salt */ - $string = randomString('16'); + substr(getBotChannel(), 0, 1) != '#' ? $chan = loadValueFromConfigFile('CHANNEL', 'channel') : $chan = getBotChannel(); + + $data = "Last seen user: ".userPreg()[0]." (".userPreg()[3].") On channel: {$chan}, Date: ".date("d.m.Y").", Time: ".date("H:i:s"); + + /* illegal chars for file */ + userPreg()[0] = removeIllegalCharsFromNickname(userPreg()[0]); - /* save data to panel config */ - SaveData('src/panel/web.ini', 'PANEL', 'web.login', $GLOBALS['CONFIG.WEB.LOGIN']); - SaveData('src/panel/web.ini', 'PANEL', 'web.password', $GLOBALS['CONFIG.WEB.PASSWORD']); - SaveData('src/panel/web.ini', 'PANEL', 'web.salt', $string); + is_file($seenDataFull.userPreg()[0]) ? + @file_put_contents($seenDataFull.userPreg()[0], $data) : @file_put_contents($seenDataFull.userPreg()[0], $data); +} +//--------------------------------------------------------------------------------------------------------- +function setPause() +{ + $GLOBALS['pause'] = true; +} +//--------------------------------------------------------------------------------------------------------- +function unsetPause() +{ + unset($GLOBALS['pause']); } //--------------------------------------------------------------------------------------------------------- -function WebSave($v1, $v2) +function getPause() { - $cfg = new IniParser('panel/web.ini'); - $cfg->setValue("MAIN", "$v1", "$v2"); - $cfg->save(); + if (isset($GLOBALS['pause'])) { + return true; + } else { + return false; + } } diff --git a/src/core_commands.php b/src/core_commands.php deleted file mode 100644 index 1f6d3e9..0000000 --- a/src/core_commands.php +++ /dev/null @@ -1,225 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - -function plugin_seen() -{ - if (OnEmptyArg('seen to check specified user when was last seen on channel')) { - } else { /* prevent directory traversal */ - $GLOBALS['args'] = str_replace('..', '', str_replace('/', '', $GLOBALS['args'])); - if ($GLOBALS['args'] == getBotNickname()) { - response('Yes im here! :)'); - } elseif ($GLOBALS['args'] == $GLOBALS['USER']) { - response('Look at mirror!'); - } elseif ($GLOBALS['args'] == 'owner') { - !empty($GLOBALS['CONFIG.BOT.ADMIN']) ? response("My Owner: {$GLOBALS['CONFIG.BOT.ADMIN']}") : false; - } else { - /* revert from illegal chars file */ - $GLOBALS['args'] = removeIllegalCharsFromNickname($GLOBALS['args']); - - is_file(DATADIR."/SEEN/{$GLOBALS['args']}") ? - response(file_get_contents(DATADIR."/SEEN/{$GLOBALS['args']}")) : response('No such user in my database.'); - - } - } -} -//--------------------------------------------------------------------------------------------------------- -function SeenSave() -{ - !is_dir(DATADIR.'/SEEN') ? @mkdir(DATADIR.'/SEEN') : false; - - $seenDataDir = DATADIR.'/SEEN/'; - - substr(getBotChannel(), 0, 1) != '#' ? $chan = $GLOBALS['CONFIG.CHANNEL'] : $chan = getBotChannel(); - - $data = "Last seen user: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) On channel: {$chan}, Date: ".date("d.m.Y").", Time: ".date("H:i:s"); - - /* illegal chars for file */ - $GLOBALS['USER'] = removeIllegalCharsFromNickname($GLOBALS['USER']); - - is_file($seenDataDir.$GLOBALS['USER']) ? - @file_put_contents($seenDataDir.$GLOBALS['USER'], $data) : @file_put_contents($seenDataDir.$GLOBALS['USER'], $data); -} -//--------------------------------------------------------------------------------------------------------- -function plugin_pause() -{ - response('Pausing all activity'); - - /* hah :) */ - $GLOBALS['stop'] = true; - - cliLog("[PLUGIN: pause] Used by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); - cliLog('[bot] Im in Pause mode'); -} -//--------------------------------------------------------------------------------------------------------- -function plugin_unpause() -{ - if (isset($GLOBALS['stop'])) { - unset($GLOBALS['stop']); - response('Back to life!'); - cliLog("[PLUGIN: unpause] Used by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); - cliLog('[bot] Unpaused'); - } else { - response('First i need to be paused, then i can unpause myself :p'); - response('Use !pause first'); - } -} -//--------------------------------------------------------------------------------------------------------- -function plugin_panel() -{ - if (OnEmptyArg('panel to list commands')) { - } else { - switch ($GLOBALS['args']) { - case 'help': - response('Panel commands:'); - response("start - Start panel with specified port: {$GLOBALS['CONFIG.CMD.PREFIX']}panel start "); - response("stop - Stop panel: {$GLOBALS['CONFIG.CMD.PREFIX']}panel stop"); - break; - } - switch ($GLOBALS['piece1']) { - case 'start': - /* if windows system */ - if (!isset($GLOBALS['OS'])) { - $port = $GLOBALS['piece2']; - if (!empty($port)) { - if (!isRunned('serv')) { - if (is_file('src/panel/serv.exe')) { - $command = 'cd src/panel & serv.exe --http-host=0.0.0.0 --http-port='. - $port.' --no-https --hide-window'; - popen($command, 'r'); - response('Runned.'); - cliLog("[PANEL] Panel Runned at port: {$port}"); - } else { - response('Cannot find web server, missing?'); - } - } else { - response('Panel already runned! ...'); - } - } else { - response('I need port to run server!'); - } - } else { - response('This plugin works on windows only at this time.'); - } - break; - case 'stop': - if (!isset($GLOBALS['OS'])) { - if (kill('serv')) { - response('Panel Closed'); - cliLog('[PANEL] Panel Closed'); - } else { - response('Panel Not runned stupid!'); - } - } - break; - } - } - - cliLog("[PLUGIN: panel] Used by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); -} -//--------------------------------------------------------------------------------------------------------- -function plugin_load() -{ - if (empty($GLOBALS['args'])) { - response("Usage {$GLOBALS['CONFIG.CMD.PREFIX']}load "); - } else { - !empty($GLOBALS['piece1']) ? LoadPlugin($GLOBALS['piece1']) : false; - } -} -//--------------------------------------------------------------------------------------------------------- -function plugin_unload() -{ - if (empty($GLOBALS['args'])) { - response("Usage {$GLOBALS['CONFIG.CMD.PREFIX']}unload "); - } else { - !empty($GLOBALS['piece1']) ? UnloadPlugin($GLOBALS['piece1']) : false; - } -} -//--------------------------------------------------------------------------------------------------------- -function CoreCmd_RegisterToBot() -{ - try { - if (empty($GLOBALS['CONFIG.OWNERS'])) { - if (!HasOwner($GLOBALS['mask'])) { - /* hash message from user to use for comparsion */ - $hashed = hash('sha256', $GLOBALS['args']); - - /* if user password match password in config do the rest */ - if ($hashed == $GLOBALS['CONFIG.OWNER.PASSWD']) { - LoadData($GLOBALS['configFile'], 'OWNER', 'bot.owners'); - - cliLog("[bot] Successful registration as owner from: {$GLOBALS['USER']} ({$GLOBALS['mask']})"); - - $new = trim($GLOBALS['mask']); - - empty($GLOBALS['LOADED']) ? $newList = $new : $newList = "{$GLOBALS['LOADED']}, {$new}"; - - SaveData($GLOBALS['configFile'], 'OWNER', 'bot.owners', $newList); - - /* Add host to auto op list */ - LoadData($GLOBALS['configFile'], 'OWNER', 'auto.op.list'); - - $new = trim($GLOBALS['mask']); - - empty($GLOBALS['LOADED']) ? $newList = $new : $newList = "{$GLOBALS['LOADED']}, {$new}"; - - SaveData($GLOBALS['configFile'], 'OWNER', 'auto.op.list', $newList); - - /* cli msg */ - cliLog("[bot] New Owner added: {$GLOBALS['USER']} ({$GLOBALS['mask']})"); - cliLog("[bot] New Auto Op added: {$GLOBALS['USER']} ({$GLOBALS['mask']})"); - - /* send information to user about commands */ - response('From now you are on my owner(s) list, enjoy.'); - - response("Core Commands: {$GLOBALS['CONFIG.CMD.PREFIX']}load ". - "{$GLOBALS['CONFIG.CMD.PREFIX']}panel ". - "{$GLOBALS['CONFIG.CMD.PREFIX']}pause ". - "{$GLOBALS['CONFIG.CMD.PREFIX']}seen ". - "{$GLOBALS['CONFIG.CMD.PREFIX']}unload ". - "{$GLOBALS['CONFIG.CMD.PREFIX']}unpause"); - - response('Owner Commands: '.implode(' ', $GLOBALS['OWNER_PLUGINS'])); - response('Admin Commands: '.implode(' ', $GLOBALS['ADMIN_PLUGINS'])); - response('User Commands: '.implode(' ', $GLOBALS['USER_PLUGINS'])); - - /* send info who is bot admin */ - !empty($GLOBALS['CONFIG.BOT.ADMIN']) ? response("Bot Admin: {$GLOBALS['CONFIG.BOT.ADMIN']}") : false; - - /* give op */ - if (BotOpped() == true) { - toServer("MODE ".getBotChannel()." +o {$GLOBALS['USER']}"); - } - - /* update variable with new owners */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get("OWNER", "bot.owners"); - } - } else { - $hashed = hash('sha256', $GLOBALS['args']); - /* if user is already an owner */ - $hashed == $GLOBALS['CONFIG.OWNER.PASSWD'] ? response('You are already my owner') : false; - } - } - } catch (Exception $e) { - cliLog('[ERROR]: Function: '.__FUNCTION__.' failed'); - } -} diff --git a/src/ctcp.php b/src/ctcp.php index 9aa99d1..6d4106d 100644 --- a/src/ctcp.php +++ b/src/ctcp.php @@ -21,63 +21,54 @@ //--------------------------------------------------------------------------------------------------------- function if_CTCP() -{ - global $rawcmd; - - if (empty($GLOBALS['stop']) && $GLOBALS['CONFIG.CTCP.RESPONSE'] == 'yes' && isset($rawcmd[1][0]) && $rawcmd[1][0] == '') { - on_CTCP(); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_CTCP() { switch ($GLOBALS['rawcmd'][1]) { case 'version': - toServer("NOTICE {$GLOBALS['USER']} :VERSION {$GLOBALS['CONFIG.CTCP.VERSION']}"); + toServer("NOTICE ".userPreg()[0]." :VERSION ".loadValueFromConfigFile('CTCP', 'ctcp.version')); - cliLog("[bot] (ctcp) VERSION by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) VERSION by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; case 'clientinfo': - toServer("NOTICE {$GLOBALS['USER']} :CLIENTINFO {$GLOBALS['CONFIG.CTCP.VERSION']}"); + toServer("NOTICE ".userPreg()[0]." :CLIENTINFO ".loadValueFromConfigFile('CTCP', 'ctcp.version')); - cliLog("[bot] (ctcp) CLIENTINFO by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) CLIENTINFO by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; case 'source': - toServer("NOTICE {$GLOBALS['USER']} :SOURCE http://github.com/S3x0r/MINION"); + toServer("NOTICE ".userPreg()[0]." :SOURCE http://github.com/S3x0r/MINION"); - cliLog("[bot] (ctcp) SOURCE by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) SOURCE by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; case 'userinfo': - toServer("NOTICE {$GLOBALS['USER']} :USERINFO Powered by Minions!"); + toServer("NOTICE ".userPreg()[0]." :USERINFO Powered by Minions!"); - cliLog("[bot] (ctcp) USERINFO by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) USERINFO by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; case 'finger': - toServer("NOTICE {$GLOBALS['USER']} :FINGER {$GLOBALS['CONFIG.CTCP.FINGER']}"); + toServer("NOTICE ".userPreg()[0]." :FINGER ".loadValueFromConfigFile('CTCP', 'ctcp.finger')); - cliLog("[bot] (ctcp) FINGER by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) FINGER by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; case 'ping': - toServer("NOTICE {$GLOBALS['USER']} :PING ".str_replace(' ', '', $GLOBALS['args'])); + toServer("NOTICE ".userPreg()[0]." :PING ".str_replace(' ', '', msgAsArguments())); - cliLog("[bot] (ctcp) PING by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) PING by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; case 'time': - toServer("NOTICE {$GLOBALS['USER']} :TIME ".date("F j, Y, g:i a")); + toServer("NOTICE ".userPreg()[0]." :TIME ".date("F j, Y, g:i a")); - cliLog("[bot] (ctcp) TIME by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); + cliLog("[bot] (ctcp) TIME by: ".userPreg()[0]." (".userPreg()[3].")"); PlaySound('ctcp.mp3'); break; } diff --git a/src/debug.php b/src/debug.php deleted file mode 100644 index 264e90f..0000000 --- a/src/debug.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -//--------------------------------------------------------------------------------------------------------- - !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? - exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. - 'Visit this page for more information.') : false; -//--------------------------------------------------------------------------------------------------------- - -function DEBUG($where) -{ - if (isset($where)) { - echo N.N."[DEBUG] WHERE: $where".N; - } else { - echo N.N.'[DEBUG] ---'.N; - } - if (isset($GLOBALS['rawDataArray'][0])) { - echo '[DEBUG] EX0: '.$GLOBALS['rawDataArray'][0].N; - } else { - echo '[DEBUG] EX0: ---'.N; - } - if (isset($GLOBALS['rawDataArray'][1])) { - echo '[DEBUG] EX1: '.$GLOBALS['rawDataArray'][1].N; - } else { - echo '[DEBUG] EX1: ---'.N; - } - if (isset($GLOBALS['rawDataArray'][2])) { - echo '[DEBUG] EX2: '.$GLOBALS['rawDataArray'][2].N; - } else { - echo '[DEBUG] EX2: ---'.N; - } - if (isset($GLOBALS['rawDataArray'][3])) { - echo '[DEBUG] EX3: '.$GLOBALS['rawDataArray'][3].N; - } else { - echo '[DEBUG] EX3: ---'.N; - } - if (isset($GLOBALS['rawDataArray'][4])) { - echo '[DEBUG] EX4: '.$GLOBALS['rawDataArray'][4].N; - } else { - echo '[DEBUG] EX4: ---'.N; - } - if (isset($GLOBALS['rawDataArray'][5])) { - echo '[DEBUG] EX5: '.$GLOBALS['rawDataArray'][5].N; - } else { - echo '[DEBUG] EX5: ---'.N; - } - if (isset($GLOBALS['rawDataArray'][6])) { - echo '[DEBUG] EX6: '.$GLOBALS['rawDataArray'][6].N; - } else { - echo '[DEBUG] EX6: ---'.N; - } - if (isset($GLOBALS['channel'])) { - echo '[DEBUG] CHANNEL: '.$GLOBALS['channel'].N; - } else { - echo '[DEBUG] CHANNEL: ---'.N; - } - if (isset($GLOBALS['BOT_NICKNAME'])) { - echo '[DEBUG] NICKNAME: '.$GLOBALS['BOT_NICKNAME'].N; - } else { - echo '[DEBUG] NICKNAME: ---'.N; - } - if (isset($GLOBALS['BOT_OPPED'])) { - echo '[DEBUG] BOT_OPPED: '.$GLOBALS['BOT_OPPED'].N; - } else { - echo '[DEBUG] BOT_OPPED: ---'.N; - } - if (isset($GLOBALS['USER'])) { - echo '[DEBUG] USER: '.$GLOBALS['USER'].N; - } else { - echo '[DEBUG] USER: ---'.N; - } - if (isset($GLOBALS['USER_HOST'])) { - echo '[DEBUG] USER_HOST: '.$GLOBALS['USER_HOST'].N; - } else { - echo '[DEBUG] USER_HOST: ---'.N; - } - if (isset($GLOBALS['CHANNEL.MODES'])) { - echo '[DEBUG] CHANNEL.MODES: '.$GLOBALS['CHANNEL.MODES'].NN; - } else { - echo '[DEBUG] CHANNEL.MODES: ---'.NN; - } -} diff --git a/src/define.php b/src/define.php index 08d77bf..1edca55 100644 --- a/src/define.php +++ b/src/define.php @@ -19,7 +19,7 @@ exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- - define('VER', '1.1.6'); + define('VER', '1.1.7'); //--------------------------------------------------------------------------------------------------------- error_reporting(-1); @@ -30,6 +30,7 @@ /* directory names */ define('LOGSDIR', 'LOGS'); define('DATADIR', 'DATA'); + define('SEENDIR', 'SEEN'); define('PLUGINSDIR', 'plugins'); /* config filename */ @@ -41,12 +42,12 @@ define('DEFAULT_PWD', '47a8f9b32ec41bd93d79bf6c1c924aaecaa26d9afe88c39fc3a638f420f251ed'); /* core commands count */ - define('CORECOMMANDSLIST', ['load', 'panel', 'pause', 'seen', 'unload', 'unpause']); - define('CORECOUNT', '6'); + define('CORECOMMANDSLIST', ['load' => 'Loads specified plugins to BOT: !load ', + 'pause' => 'Pause all BOT activity: !pause', + 'seen' => 'Check specified user when was last seen on channel: !seen ', + 'unload' => 'Unloads specified plugin from BOT: !unload ', + 'unpause' => 'Restore BOT from pause mode: !unpause']); + define('CORECOUNT', '5'); /* check version url */ define('VERSION_URL', 'https://raw.githubusercontent.com/S3x0r/version-for-BOT/master/VERSION.TXT'); - - /* logs filename */ - !empty($_SERVER['COMPUTERNAME']) ? $computerNameORhostname = $_SERVER['COMPUTERNAME'] : $computerNameORhostname = gethostname(); - $GLOBALS['logFileName'] = LOGSDIR."/".date('Y.m.d')."+".date('H_i_s').'-'.$computerNameORhostname.".txt"; diff --git a/src/events.php b/src/events.php index fa56d14..c0ab500 100644 --- a/src/events.php +++ b/src/events.php @@ -20,399 +20,10 @@ 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- -function if_NUMERIC_RESPONSE() -{ - global $rawDataArray; - - if (isset($rawDataArray[1]) && is_numeric($rawDataArray[1])) { - function_exists('on_'.$rawDataArray[1]) ? call_user_func('on_'.$rawDataArray[1]) : false; - } -} -//--------------------------------------------------------------------------------------------------------- -function if_WORD_RESPONSE() -{ - global $rawDataArray; - - if (isset($rawDataArray[1]) && in_array($rawDataArray[1], ['JOIN', 'PART', 'KICK', 'TOPIC', 'PRIVMSG', - 'NICK', 'QUIT', 'MODE', 'NOTICE'])) { - function_exists('on_'.$rawDataArray[1]) ? call_user_func('on_'.$rawDataArray[1]) : false; - } -} -//--------------------------------------------------------------------------------------------------------- -function on_NOTICE() -{ - cliLog('[server] (NOTICE) '.msgFromServer()); -} -//--------------------------------------------------------------------------------------------------------- -function on_PING() -{ - /* send PONG */ - toServer("PONG {$GLOBALS['rawDataArray'][1]}"); -} -//--------------------------------------------------------------------------------------------------------- -function on_001() /* server message */ -{ - /* 1.set server name */ - setServerName($GLOBALS['rawDataArray'][0]); - - /* 1.set bot nickname */ - setBotNickname($GLOBALS['rawDataArray'][2]); - - cliLog('[server] '.msgFromServer()); -} -//--------------------------------------------------------------------------------------------------------- -function on_002() /* host, version server */ -{ - /* :server.name 002 minion :Your host is server.name, running version ircd-123.4 */ - - cliLog('[server] '.msgFromServer()); -} -//--------------------------------------------------------------------------------------------------------- -function on_003() /* server creation time */ -{ - /* :server.name 003 minion :This server was created Sat Oct 10 15:08:58 2020 */ - - cliLog('[server] '.msgFromServer()); - - /* show info that we are connected */ - cli(''); - cliLog("[bot] Connected, my nickname is: ".getBotNickname()); -} -//--------------------------------------------------------------------------------------------------------- -function on_375() /* motd start */ -{ - if ($GLOBALS['CONFIG.SHOW.MOTD'] == 'yes') { - cliLog('[server] '.msgFromServer()); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_372() /* motd message */ -{ - if ($GLOBALS['CONFIG.SHOW.MOTD'] == 'yes') { - cliLog('[server] '.msgFromServer()); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_376() /* motd end */ -{ - if ($GLOBALS['CONFIG.SHOW.MOTD'] == 'yes') { - cliLog('[server] '.msgFromServer()); - } - - /* register to bot info */ - if (isset($GLOBALS['defaultPwdChanged'])) { - cli(N.'*********************************************************'); - cli("Register to bot by typing /msg ".getBotNickname()." register "); - cli('*********************************************************'.N); - unset($GLOBALS['defaultPwdChanged']); - } - - /* if autojoin */ - if ($GLOBALS['CONFIG.AUTO.JOIN'] == 'yes') { - cliLog("[bot] Trying to join channel: {$GLOBALS['CONFIG.CHANNEL']}".N); - joinChannel($GLOBALS['CONFIG.CHANNEL']); - } - - /* send anon stats */ - // Statistics(); - - /* play sound :) */ - PlaySound('connected.mp3'); -} -//--------------------------------------------------------------------------------------------------------- -function on_mode() /* on MODE event */ -{ - /* 1. set bot modes */ - if ($GLOBALS['rawDataArray'][0] == ':'.getBotNickname() && $GLOBALS['rawDataArray'][2] == getBotNickname()) { - $GLOBALS['BOT_MODES'] = str_replace(':+', '', $GLOBALS['rawDataArray'][3]); - } - - /* 1. set channel modes */ - if ($GLOBALS['rawDataArray'][0] == getServerName() && isset($GLOBALS['rawDataArray'][3]) && !empty($GLOBALS['rawDataArray'][0])) { - $GLOBALS['CHANNEL.MODES'] = str_replace('+', '', $GLOBALS['rawDataArray'][3]); - } - - if ($GLOBALS['rawDataArray'][0] != getServerName() && isset($GLOBALS['rawDataArray'][3]) && !empty($GLOBALS['rawDataArray'][3]) && $GLOBALS['rawDataArray'][2] != getBotNickname()) { - isset($GLOBALS['rawDataArray'][4]) ? $add = $GLOBALS['rawDataArray'][4] : $add = ''; - cliLog("[".getBotChannel()."] * {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) sets mode: {$GLOBALS['rawDataArray'][3]} {$add}"); - } - - /* if bot opped */ - if (isset($GLOBALS['rawDataArray'][4]) && $GLOBALS['rawDataArray'][4] == getBotNickname()) { - if (isset($GLOBALS['rawDataArray'][3]) && $GLOBALS['rawDataArray'][3] == '+o') { - /* send info */ - cliLog("[bot] I have OP now on: ".getBotChannel().", from: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']})"); - - /* on bot opped event */ - on_bot_opped(); - - /* if bot deoped */ - } elseif (isset($GLOBALS['rawDataArray'][4]) && $GLOBALS['rawDataArray'][4] == getBotNickname()) { - if (isset($GLOBALS['rawDataArray'][3]) && $GLOBALS['rawDataArray'][3] == '-o') { - /* send info */ - cliLog("[bot] User: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) DEOPED ME on channel: ".getBotChannel()); - - /* unset bot opped */ - unset($GLOBALS['BOT_OPPED']); - - /* play sound */ - PlaySound('prompt.mp3'); - } - } - } - - isset($GLOBALS['rawDataArray'][4]) ? $rest = $GLOBALS['rawDataArray'][4] : $rest = ''; -} -//--------------------------------------------------------------------------------------------------------- -function on_join() -{ - /* if bot joined */ - if ($GLOBALS['USER'] == getBotNickname()) { - /* 1. set channel from 353 */ - $GLOBALS['BOT_CHANNEL'] = str_replace(':', '', $GLOBALS['rawDataArray'][2]); - - cliLog("[bot] Joined channel: ".getBotChannel()); - - /* 1.check channel modes for cli message */ - toServer("MODE ".getBotChannel()); - - /* FIX: save data for web panel */ - } - - /* if user joined channel */ - if ($GLOBALS['USER'] != getBotNickname()) { - cliLog("[".getBotChannel()."] * {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) has joined"); - - /* Save Seen */ - SeenSave(); - } - - /* auto op */ - if ($GLOBALS['CONFIG.AUTO.OP'] == 'yes' && BotOpped() == true) { - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get("OWNER", "auto.op.list"); - - $auto_op_list_c = $GLOBALS['CONFIG.AUTO.OP.LIST']; - $pieces = explode(", ", $auto_op_list_c); - - $mask2 = $GLOBALS['USER'].'!'.$GLOBALS['USER_IDENT'].'@'.$GLOBALS['host']; - - if (in_array($mask2, $pieces)) { - cliLog("[bot] I have user: '{$GLOBALS['USER']}' on the auto op list, giving op!"); - - toServer("MODE ".getBotChannel()." +o {$GLOBALS['USER']}"); - - PlaySound('prompt.mp3'); - } - } -} -//--------------------------------------------------------------------------------------------------------- -function on_353() /* on channel join info */ -{ - $nick = str_replace(':', '', $GLOBALS['rawDataArray'][5]); - - /* if bot opped */ - if ($nick == '@'.getBotNickname()) { - on_bot_opped(); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_366() /* end of names list after joining channel */ -{ -} -//--------------------------------------------------------------------------------------------------------- -function on_324() /* channel modes */ -{ - if (isset($GLOBALS['rawDataArray'][4])) { - unset($GLOBALS['CHANNEL.MODES']); - - $GLOBALS['CHANNEL.MODES'] = str_replace('+', '', $GLOBALS['rawDataArray'][4]); - - empty($GLOBALS['rawDataArray'][5]) ? $msg = $GLOBALS['CHANNEL.MODES'] : $msg = $GLOBALS['CHANNEL.MODES'].' '.$GLOBALS['rawDataArray'][5]; - - if (!empty($GLOBALS['CHANNEL.MODES'])) { - cliLog("[".getBotChannel()."] * channel modes: +{$msg}"); - } else { - cliLog("[".getBotChannel()."] * channel modes are not set"); - } - } -} -//--------------------------------------------------------------------------------------------------------- -function on_part() -{ - /* if bot part channel */ - if ($GLOBALS['USER'] == getBotNickname()) { - - /* FIX: add info to panel */ - - /* set to not opped */ - unset($GLOBALS['BOT_OPPED']); - - /* delete channel */ - unset($GLOBALS['BOT_CHANNEL']); - - /* delete channel modes */ - unset($GLOBALS['CHANNEL.MODES']); - } else { - /* if someone part channel */ - cliLog("[".getBotChannel()."] * {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) has leaved"); - - /* Save to database for seen purpose */ - SeenSave(); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_471() /* if +limit on channel */ -{ - cliLog('[bot] I cannot join, channel is full'); -} -//--------------------------------------------------------------------------------------------------------- -function on_473() /* if +invite on channel */ -{ - cliLog('[bot] I cannot join, channel is invite only'); -} -//--------------------------------------------------------------------------------------------------------- -function on_474() /* if bot +banned on channel */ -{ - cliLog('[bot] I cannot join, im banned on channel'); -} -//--------------------------------------------------------------------------------------------------------- -function on_331() /* RPL_NOTOPIC - " :No topic is set" */ -{ -} -//--------------------------------------------------------------------------------------------------------- -function on_332() /* RPL_TOPIC - " :" */ -{ - empty(inputFromLine('4')) ? $msg = 'channel topic is not set' : $msg = 'channel topic: "'.inputFromLine('4').'"'; - - cliLog("[".$GLOBALS['rawDataArray'][3]."] * {$msg}"); -} -//--------------------------------------------------------------------------------------------------------- -function on_303() /* ison */ -{ - if ($GLOBALS['rawDataArray'][3] == ':') { - toServer("NICK {$GLOBALS['CONFIG.NICKNAME']}"); - /* 1.set nickname from config */ - setBotNickname($GLOBALS['CONFIG.NICKNAME']); - - unset($GLOBALS['I_USE_RND_NICKNAME']); - - cliLog('[bot] I recovered my original nickname'); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_quit() -{ - isset($GLOBALS['rawDataArray'][2]) ? $quit = inputFromLine(3) : $quit = ''; - - cliLog("[".getBotChannel()."] * {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) Quit ({$quit})"); - - /* Save to database for seen purpose */ - SeenSave(); -} -//--------------------------------------------------------------------------------------------------------- -function on_TOPIC() /* topic change */ -{ - cliLog('['.getBotChannel().'] * '.$GLOBALS['USER'].' ('.$GLOBALS['USER_HOST'].') sets topic: "'.inputFromLine('3').'"'); -} -//--------------------------------------------------------------------------------------------------------- -function on_privmsg() -{ - /* if register */ - if ($GLOBALS['rawDataArray'][2] == getBotNickname() && isset($GLOBALS['rawDataArray'][3]) && $GLOBALS['rawDataArray'][3] == ':register') { - } elseif ($GLOBALS['rawDataArray'][2] == getBotChannel()) { /* if message in channel */ - cliLog("[".getBotChannel()."] <{$GLOBALS['USER']}> ".inputFromLine('3')); - } elseif ($GLOBALS['rawDataArray'][2] == getBotNickname()) { /* if private message */ - cliLog("<{$GLOBALS['USER']}> ".inputFromLine('3')); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_475() /* if +key on channel */ -{ - if (!empty($GLOBALS['CONFIG.CHANNEL.KEY'])) { - joinChannel("{$GLOBALS['CONFIG.CHANNEL']} {$GLOBALS['CONFIG.CHANNEL.KEY']}"); - } else { - cliLog('[bot] I cannot join, bad channel key in config or key not set'); - - PlaySound('prompt.mp3'); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_433() /* if nick already exists */ -{ - on_432(); -} -//--------------------------------------------------------------------------------------------------------- -function on_432() /* if nick reserved */ -{ - /* vars for keep nick functionality */ - ($GLOBALS['CONFIG.KEEP.NICK'] == 'yes') ? $GLOBALS['I_USE_RND_NICKNAME'] = '1' : false; - - /* add random to nick */ - $randomNick = $GLOBALS['CONFIG.NICKNAME'].'|'.rand(0, 999); - - /* set random nick */ - cliLog("[bot] Nickname already in use, changing nickname to: {$randomNick}"); - - toServer("NICK {$randomNick}"); -} -//--------------------------------------------------------------------------------------------------------- -function on_422() /* join if no motd */ -{ - on_376(); -} -//--------------------------------------------------------------------------------------------------------- -function on_nick() -{ - $a0 = str_replace(':', '', $GLOBALS['rawDataArray'][0]); - $a2 = str_replace(':', '', $GLOBALS['rawDataArray'][2]); - - if ($GLOBALS['USER'] == $GLOBALS['CONFIG.NICKNAME'] && empty($GLOBALS['I_USE_RND_NICKNAME'])) { - /* 1.set nickname */ - setBotNickname($a2); - cliLog("[bot] My new nickname is: ".getBotNickname()); - } elseif (isset($GLOBALS['BOT_NICKNAME']) && $GLOBALS['USER'] == $GLOBALS['BOT_NICKNAME']) { - setBotNickname($a2); - cliLog("[bot] My new nickname is: ".getBotNickname()); - } elseif ($a0 == getBotNickname() && $a2 != getBotNickname()) { - setBotNickname($a2); - cliLog("[bot] My new nickname is: ".getBotNickname()); - } else { - cliLog("[".getBotChannel()."] * ".$GLOBALS['USER']. " changed nick to {$a2}"); - } -} -//--------------------------------------------------------------------------------------------------------- -function on_kick() -{ - cliLog("[".getBotChannel()."] * {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) kicked {$GLOBALS['rawDataArray'][3]}"); - - /* if BOT kicked */ - if (isset($GLOBALS['rawDataArray'][3]) && $GLOBALS['rawDataArray'][3] == getBotNickname()) { - /* set to not opped */ - unset($GLOBALS['BOT_OPPED']); - - /* delete channel modes */ - unset($GLOBALS['CHANNEL.MODES']); - - /* rejoin when kicked? */ - if ($GLOBALS['CONFIG.AUTO.REJOIN'] == 'yes') { - cliLog("[bot] I was kicked from: ".getBotChannel()." by {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}) - rejoining!"); - sleep(2); - toServer("JOIN :{$GLOBALS['rawDataArray'][2]}"); - - PlaySound('prompt.mp3'); - } - - /* delete channel */ - unset($GLOBALS['BOT_CHANNEL']); - } - - /* Save to database for seen purpose */ - SeenSave(); -} -//--------------------------------------------------------------------------------------------------------- function on_bot_opped() { + debug("on_bot_opped()"); + /* 1. set var that we have op */ $GLOBALS['BOT_OPPED'] = 'yes'; @@ -425,26 +36,27 @@ function on_bot_opped() //--------------------------------------------------------------------------------------------------------- function setChannelModesAndBans() { + debug("setChannelModesAndBans()"); + /* set bans from config */ - if (!empty($GLOBALS['CONFIG.BAN.LIST'])) { - $ban_list = explode(', ', $GLOBALS['CONFIG.BAN.LIST']); - foreach ($ban_list as $ban_address) { - toServer("MODE ".getBotChannel()." +b {$ban_address}"); + if (!empty(loadValueFromConfigFile('BANS', 'ban.list'))) { + $banList = explode(', ', loadValueFromConfigFile('BANS', 'ban.list')); + foreach ($banList as $ban_address) { + toServer('MODE '.getBotChannel().' +b '.$ban_address); } } /* set channel modes from config */ - if ($GLOBALS['CONFIG.KEEPCHAN.MODES'] == 'yes' && BotOpped() == true) { //FIX: keep modes - if (isset($GLOBALS['CHANNEL.MODES']) && $GLOBALS['CHANNEL.MODES'] != $GLOBALS['CONFIG.CHANNEL.MODES']) { - sleep(1); - toServer("MODE ".getBotChannel()." -{$GLOBALS['CHANNEL.MODES']}"); + if (loadValueFromConfigFile('AUTOMATIC', 'keep.chan.modes') == 'yes' && BotOpped() == true) { //FIX: keep modes + if (isset($GLOBALS['CHANNEL.MODES']) && $GLOBALS['CHANNEL.MODES'] != loadValueFromConfigFile('CHANNEL', 'channel.modes')) { sleep(1); - toServer("MODE ".getBotChannel()." +{$GLOBALS['CONFIG.CHANNEL.MODES']}"); + toServer("MODE ".getBotChannel()." +".loadValueFromConfigFile('CHANNEL', 'channel.modes')); } - if (empty($GLOBALS['CHANNEL.MODES']) && !empty($GLOBALS['CONFIG.CHANNEL.MODES'])) { + + if (!isset($GLOBALS['CHANNEL.MODES'])) { sleep(1); - toServer("MODE ".getBotChannel()." +{$GLOBALS['CONFIG.CHANNEL.MODES']}"); + toServer("MODE ".getBotChannel()." +".loadValueFromConfigFile('CHANNEL', 'channel.modes')); } } } -//--------------------------------------------------------------------------------------------------------- \ No newline at end of file +//--------------------------------------------------------------------------------------------------------- diff --git a/src/logs.php b/src/logs.php index b921bc3..c6e1c79 100644 --- a/src/logs.php +++ b/src/logs.php @@ -20,9 +20,16 @@ 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- +function logFileNameFormat() +{ + !empty($_SERVER['COMPUTERNAME']) ? $data = $_SERVER['COMPUTERNAME'] : $data = gethostname(); + + return LOGSDIR."/".date('Y.m.d')."-".$data.".txt"; +} +//--------------------------------------------------------------------------------------------------------- function LogsInit() { $data = "-------------------------LOG CREATED: ".date('d.m.Y | H:i:s')."-------------------------\r\n"; - SaveToFile($GLOBALS['logFileName'], $data, 'a'); + SaveToFile(logFileNameFormat(), $data, 'a'); } diff --git a/src/misc.php b/src/misc.php index 3815909..8b56464 100644 --- a/src/misc.php +++ b/src/misc.php @@ -38,47 +38,27 @@ function changeDefaultOwnerPwd() $hashed = hash('sha256', $newPassword); /* save pwd to file */ - SaveData($GLOBALS['configFile'], 'OWNER', 'owner.password', $hashed); - - /* remove pwd checking vars */ - unset($newPassword); - unset($hashed); + SaveValueToConfigFile('OWNER', 'owner.password', $hashed); /* Set first time change variable */ - $GLOBALS['defaultPwdChanged'] = 'yes'; + $GLOBALS['defaultPwdChanged'] = 'yes'; //- to wyjebać i zmienić na funkcje !!! echo N; cliLog('[bot] Password changed!'.N); - - /* update owner(s) password */ - $cfg = new IniParser($GLOBALS['configFile']); - $GLOBALS['CONFIG.OWNER.PASSWD'] = $cfg->get('OWNER', 'owner.password'); } //--------------------------------------------------------------------------------------------------------- -function SaveData($v1, $v2, $v3, $v4) +function SaveValueToConfigFile($v1, $v2, $v3) { - $cfg = new IniParser($v1); - $cfg->setValue("$v2", "$v3", "$v4"); + $cfg = new IniParser(getConfigFileName()); + $cfg->setValue($v1, $v2, $v3); $cfg->save(); } //--------------------------------------------------------------------------------------------------------- -function LoadData($configFile, $section, $config) -{ - $cfg = new IniParser($configFile); - $GLOBALS['LOADED'] = $cfg->get("$section", "$config"); -} -//--------------------------------------------------------------------------------------------------------- -/* update users (OWNER,USER) array */ -function UpdatePrefix($user, $newPrefix) -{ - $GLOBALS[$user.'_PLUGINS'] = str_replace($GLOBALS['CONFIG.CMD.PREFIX'], $newPrefix, $GLOBALS[$user.'_PLUGINS']); -} -//--------------------------------------------------------------------------------------------------------- /* if first arg after !plugin is empty */ function OnEmptyArg($information) { - if (empty($GLOBALS['args'])) { - response("Usage: {$GLOBALS['CONFIG.CMD.PREFIX']}{$information}"); + if (empty(msgAsArguments())) { + response("Usage: ".loadValueFromConfigFile('COMMAND', 'command.prefix')."{$information}"); return true; } else { return false; @@ -107,6 +87,7 @@ function CountLines($exts = ['php']) } $parts = explode('.', $file->getFilename()); $extension = end($parts); + if (in_array($extension, $exts)) { $files[$file->getPathname()] = count(file($file->getPathname())); } @@ -131,7 +112,7 @@ function randomString($length) //--------------------------------------------------------------------------------------------------------- function inputFromLine($position) { - $a = $GLOBALS['rawDataArray']; + $a = rawDataArray(); $current = ''; $index = $position; @@ -148,8 +129,8 @@ function inputFromLine($position) function msg_without_command() { $input = null; - for ($i=3; $i <= (count($GLOBALS['rawDataArray'])); $i++) { - $input .= $GLOBALS['rawDataArray'][$i]." "; + for ($i=3; $i <= (count(rawDataArray())); $i++) { + $input .= rawDataArray()[$i]." "; } $in = rtrim($input); @@ -158,38 +139,6 @@ function msg_without_command() return $data; } //--------------------------------------------------------------------------------------------------------- -function HasAdmin($mask) //TODO: wtf -{ - $admins_c = $GLOBALS['CONFIG.ADMIN.LIST']; - $pieces = explode(", ", $admins_c); - - if ($mask == null) { - return false; - } - foreach ($pieces as $piece) { - if (fnmatch($piece, $mask, 16)) { - return true; - return false; - } - } -} -//--------------------------------------------------------------------------------------------------------- -function HasOwner($mask) //TODO: wtf -{ - $owners_c = $GLOBALS['CONFIG.OWNERS']; - $pieces = explode(", ", $owners_c); - - if ($mask == null) { - return false; - } - foreach ($pieces as $piece) { - if (fnmatch($piece, $mask, 16)) { - return true; - return false; - } - } -} -//--------------------------------------------------------------------------------------------------------- function SaveToFile($file, $data, $method) { $file = @fopen($file, $method); @@ -201,7 +150,7 @@ function SaveToFile($file, $data, $method) //--------------------------------------------------------------------------------------------------------- function PlaySound($sound) { - if ($GLOBALS['CONFIG.PLAY.SOUNDS'] == 'yes' && !isset($GLOBALS['OS'])) { + if (loadValueFromConfigFile('PROGRAM', 'play.sounds') == 'yes' && ifWindowsOs()) { if (is_file('src/php/play.exe') && is_file('src/sounds/'.$sound)) { $command = 'start /b src/php/play.exe src/sounds/'.$sound; pclose(popen($command, 'r')); @@ -213,7 +162,7 @@ function PlaySound($sound) //--------------------------------------------------------------------------------------------------------- function kill($program) { - if (!isset($GLOBALS['OS'])) { + if (ifWindowsOs()) { $pattern = '~('.$program.')\.exe~i'; $tasks = array(); exec("tasklist 2>NUL", $tasks); @@ -229,7 +178,7 @@ function kill($program) //--------------------------------------------------------------------------------------------------------- function isRunned($program) { - if (!isset($GLOBALS['OS'])) { + if (ifWindowsOs()) { $pattern = '~('.$program.')\.exe~i'; $tasks = array(); exec("tasklist 2>NUL", $tasks); @@ -246,7 +195,7 @@ function getPasswd($string = '') { echo $string; - if (!isset($GLOBALS['OS'])) { + if (ifWindowsOs()) { if (is_file('src\php\hide.exe')) { $psw = `src\php\hide.exe`; } else { @@ -275,15 +224,17 @@ function Statistics() $ip = @file_get_contents($ipAddress); /* identify bot session by hashed ip, operating system, bot nickname, name and ident */ - $botID = hash('sha256', $ip.php_uname().getBotNickname().$GLOBALS['CONFIG.NAME']. - $GLOBALS['CONFIG.IDENT'].$GLOBALS['CONFIG.SERVER'].VER); + $botID = hash('sha256', $ip.php_uname().getBotNickname().loadValueFromConfigFile('BOT', 'name'). + loadValueFromConfigFile('BOT', 'ident').loadValueFromConfigFile('SERVER', 'server').VER); - @file($statsAddress."stamp={$botID}&nick=".getBotNickname()."&server={$GLOBALS['CONFIG.SERVER']}&ver={VER}"); + @file($statsAddress."stamp={$botID}&nick=".getBotNickname()."&server=".loadValueFromConfigFile('SERVER', 'server')."&ver={VER}"); } //--------------------------------------------------------------------------------------------------------- function WinSleep($time) { - !isset($GLOBALS['OS']) ? sleep($time) : false; + if (ifWindowsOs()) { + sleep($time); + } } //--------------------------------------------------------------------------------------------------------- function removeIllegalCharsFromNickname($nickname) diff --git a/src/on_numeric.php b/src/on_numeric.php new file mode 100644 index 0000000..fa7c83e --- /dev/null +++ b/src/on_numeric.php @@ -0,0 +1,265 @@ + + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +//--------------------------------------------------------------------------------------------------------- + !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? + exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. + 'Visit this page for more information.') : false; +//--------------------------------------------------------------------------------------------------------- + +function on_001() /* server message */ +{ + /* 1.set server name */ + setServerName(rawDataArray()[0]); + + /* 1.set bot nickname */ + setBotNickname(rawDataArray()[2]); + + cliLog('[server] '.msgFromServer()); +} +//--------------------------------------------------------------------------------------------------------- +function on_002() /* host, version server */ +{ + /* :server.name 002 minion :Your host is server.name, running version ircd-123.4 */ + + cliLog('[server] '.msgFromServer()); +} +//--------------------------------------------------------------------------------------------------------- +function on_003() /* server creation time */ +{ + /* :server.name 003 minion :This server was created Sat Oct 10 15:08:58 2020 */ + + cliLog('[server] '.msgFromServer()); +} +//--------------------------------------------------------------------------------------------------------- +function on_375() /* motd start */ +{ + if (loadValueFromConfigFile('SERVER', 'show.motd') == 'yes') { + cliLog('[server] '.msgFromServer()); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_372() /* motd message */ +{ + if (loadValueFromConfigFile('SERVER', 'show.motd') == 'yes') { + cliLog('[server] '.msgFromServer()); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_376() /* motd end */ +{ + if (loadValueFromConfigFile('SERVER', 'show.motd') == 'yes') { + cliLog('[server] '.msgFromServer()); + } + + /* show info that we are connected */ + cli(''); + cliLog("[bot] Connected! My nickname is: ".getBotNickname()); + + /* register to bot info */ + if (isset($GLOBALS['defaultPwdChanged'])) { + cli(N.'*********************************************************'); + cli("Register to bot by typing /msg ".getBotNickname()." register "); + cli('*********************************************************'.N); + unset($GLOBALS['defaultPwdChanged']); + } + + /* if autojoin */ + if (loadValueFromConfigFile('CHANNEL', 'auto.join') == 'yes') { + cliLog("[bot] Trying to join channel: ".loadValueFromConfigFile('CHANNEL', 'channel')); + + joinChannel(loadValueFromConfigFile('CHANNEL', 'channel')); + } else { + cliLog('[bot] No auto join mode in '.getConfigFileName().', idling...'); + } + + /* send anon stats */ + // Statistics(); + + /* play sound :) */ + PlaySound('connected.mp3'); +} +//--------------------------------------------------------------------------------------------------------- +function on_396() +{ + cliLog('[server] '.msgFromServer()); +} +//--------------------------------------------------------------------------------------------------------- +function on_353() /* on channel join info */ +{ + debug("on_353()"); + + if (!isset($GLOBALS['353_Start'])) { + $GLOBALS['channelUsersOP'] = null; + $GLOBALS['channelUsersHalfOp'] = null; + $GLOBALS['channelUsersVoice'] = null; + $GLOBALS['channelUsersOthers'] = null; + $GLOBALS['channelUsersCount'] = 0; + + $GLOBALS['353_Start'] = true; + } + + $usersData = explode(" ", inputFromLine(5)); + + foreach ($usersData as $user) { + if ($user[0] == '@') { + $GLOBALS['channelUsersOP'] .= $user.' '; + } else if ($user[0] == '%') { + $GLOBALS['channelUsersHalfOp'] .= $user.' '; + } else if ($user[0] == '+') { + $GLOBALS['channelUsersVoice'] .= $user.' '; + } else { + $GLOBALS['channelUsersOthers'] .= $user.' '; + } + + $GLOBALS['channelUsersCount']++; + } + + $nick = str_replace(':', '', rawDataArray()[5]); + + /* check if bot is first in channel and if got OP */ + if ($nick == '@'.getBotNickname()) { + /* do some actions when bot is oped */ + on_bot_opped(); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_366() /* end of channel user(s) nicks list- after joining channel */ +{ + cliLog('[bot] Total Channel Users: '.$GLOBALS['channelUsersCount']); + + // @ + if (!empty($GLOBALS['channelUsersOP'])) { + cliLog('[bot] (@) Op(s): '.$GLOBALS['channelUsersOP']); + } + + // % + if (!empty($GLOBALS['channelUsersHalfOp'])) { + cliLog('[bot] (%) HalfOp(s): '.$GLOBALS['channelUsersHalfOp']); + } + + // + + if (!empty($GLOBALS['channelUsersVoice'])) { + cliLog('[bot] (+) Voice(s): '.$GLOBALS['channelUsersVoice']); + } + + // others + if (!empty($GLOBALS['channelUsersOthers'])) { + cliLog('[bot] Other(s): '.$GLOBALS['channelUsersOthers']); + } + + unset($GLOBALS['353_Start']); + unset($GLOBALS['channelUsersOP']); + unset($GLOBALS['channelUsersHalfOp']); + unset($GLOBALS['channelUsersVoice']); + unset($GLOBALS['channelUsersOthers']); + unset($GLOBALS['channelUsersCount']); +} +//--------------------------------------------------------------------------------------------------------- +function on_324() /* channel modes */ +{ + debug("on_324()"); + + if (isset(rawDataArray()[4])) { + unset($GLOBALS['CHANNEL.MODES']); + + $GLOBALS['CHANNEL.MODES'] = str_replace('+', '', rawDataArray()[4]); + + empty(rawDataArray()[5]) ? $msg = $GLOBALS['CHANNEL.MODES'] : $msg = $GLOBALS['CHANNEL.MODES'].' '.rawDataArray()[5]; + + if (!empty($GLOBALS['CHANNEL.MODES'])) { + cliLog("[".getBotChannel()."] * channel modes: +{$msg}"); + } else { + cliLog("[".getBotChannel()."] * channel modes are not set"); + } + } +} +//--------------------------------------------------------------------------------------------------------- +function on_471() /* if +limit on channel */ +{ + cliLog('[bot] I cannot join, channel is full'); +} +//--------------------------------------------------------------------------------------------------------- +function on_473() /* if +invite on channel */ +{ + cliLog('[bot] I cannot join, channel is invite only'); +} +//--------------------------------------------------------------------------------------------------------- +function on_474() /* if bot +banned on channel */ +{ + cliLog('[bot] I cannot join, im banned on channel'); +} +//--------------------------------------------------------------------------------------------------------- +function on_331() /* RPL_NOTOPIC - " :No topic is set" */ +{ +} +//--------------------------------------------------------------------------------------------------------- +function on_332() /* RPL_TOPIC - " :" */ +{ + debug("on_332()"); + + empty(inputFromLine('4')) ? $msg = 'channel topic is not set' : $msg = 'channel topic: "'.inputFromLine('4').'"'; + + cliLog("[".rawDataArray()[3]."] * {$msg}"); +} +//--------------------------------------------------------------------------------------------------------- +function on_303() /* ison */ +{ + if (rawDataArray()[3] == ':') { + toServer("NICK ".loadValueFromConfigFile('BOT', 'nickname')); + /* 1.set nickname from config */ + setBotNickname(loadValueFromConfigFile('BOT', 'nickname')); + + unset($GLOBALS['I_USE_RND_NICKNAME']); + + cliLog('[bot] I recovered my original nickname'); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_475() /* if +key on channel */ +{ + if (!empty(loadValueFromConfigFile('CHANNEL', 'channel.key'))) { + joinChannel(loadValueFromConfigFile('CHANNEL', 'channel').' '.loadValueFromConfigFile('CHANNEL', 'channel.key')); + } else { + cliLog('[bot] I cannot join, bad channel key in config or key not set'); + + PlaySound('prompt.mp3'); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_433() /* if nick already exists */ +{ + on_432(); +} +//--------------------------------------------------------------------------------------------------------- +function on_432() /* if nick reserved */ +{ + /* vars for keep nick functionality */ + (loadValueFromConfigFile('AUTOMATIC', 'keep.nick') == 'yes') ? $GLOBALS['I_USE_RND_NICKNAME'] = '1' : false; + + /* add random to nick */ + $randomNick = loadValueFromConfigFile('BOT', 'nickname').'|'.rand(0, 999); + + /* set random nick */ + cliLog("[bot] Nickname already in use, changing nickname to: {$randomNick}"); + + toServer("NICK {$randomNick}"); +} +//--------------------------------------------------------------------------------------------------------- +function on_422() /* join if no motd */ +{ + on_376(); +} diff --git a/src/on_word.php b/src/on_word.php new file mode 100644 index 0000000..15a3a56 --- /dev/null +++ b/src/on_word.php @@ -0,0 +1,220 @@ + + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +//--------------------------------------------------------------------------------------------------------- + !in_array(PHP_SAPI, array('cli', 'cli-server', 'phpdbg')) ? + exit('This script can\'t be run from a web browser. Use CLI terminal to run it
'. + 'Visit this page for more information.') : false; +//--------------------------------------------------------------------------------------------------------- + +function on_NOTICE() +{ + debug("on_NOTICE()"); + + cliLog('[server] (NOTICE) '.msgFromServer()); +} +//--------------------------------------------------------------------------------------------------------- +function on_PING() +{ + debug("on_PING()"); + + toServer("PONG ".rawDataArray()[1]); +} +//--------------------------------------------------------------------------------------------------------- +function on_MODE() /* on MODE event */ +{ + debug("on_mode()"); + + /* 1. set bot modes */ + if (rawDataArray()[0] == ':'.getBotNickname() && rawDataArray()[2] == getBotNickname()) { + setBotModes(str_replace(':+', '', rawDataArray()[3])); + } + + /* 1. set channel modes */ + if (rawDataArray()[0] == getServerName() && isset(rawDataArray()[3]) && !empty(rawDataArray()[0])) { + $GLOBALS['CHANNEL.MODES'] = str_replace('+', '', rawDataArray()[3]); + } + + if (rawDataArray()[0] != getServerName() && isset(rawDataArray()[3]) && !empty(rawDataArray()[3]) && rawDataArray()[2] != getBotNickname()) { + isset(rawDataArray()[4]) ? $add = rawDataArray()[4] : $add = ''; + cliLog("[".getBotChannel()."] * ".userPreg()[0]." (".userPreg()[3].") sets mode: ".rawDataArray()[3]." {$add}"); + } + + /* if bot opped */ + if (isset(rawDataArray()[4]) && rawDataArray()[4] == getBotNickname()) { + if (isset(rawDataArray()[3]) && rawDataArray()[3] == '+o') { + /* send info */ + cliLog("[bot] I have OP now on: ".getBotChannel().", from: ".userPreg()[0]." (".userPreg()[3].")"); + + /* on bot opped event */ + on_bot_opped(); + + /* if bot deoped */ + } elseif (isset(rawDataArray()[4]) && rawDataArray()[4] == getBotNickname()) { + if (isset(rawDataArray()[3]) && rawDataArray()[3] == '-o') { + /* send info */ + cliLog("[bot] User: ".userPreg()[0]." (".userPreg()[3].") DEOPED ME on channel: ".getBotChannel()); + + /* unset bot opped */ + unset($GLOBALS['BOT_OPPED']); + + /* play sound */ + PlaySound('prompt.mp3'); + } + } + } + + isset(rawDataArray()[4]) ? $rest = rawDataArray()[4] : $rest = ''; +} +//--------------------------------------------------------------------------------------------------------- +function on_JOIN() +{ + debug("on_join()"); + + /* if bot joined */ + if (userPreg()[0] == getBotNickname()) { + /* 1. set channel from 353 */ + setBotChannel(str_replace(':', '', rawDataArray()[2])); + + cliLog("[bot] Joined channel: ".getBotChannel()); + + /* 1.check channel modes for cli message */ + toServer("MODE ".getBotChannel()); + } + + /* if user joined channel */ + if (userPreg()[0] != getBotNickname()) { + cliLog("[".getBotChannel()."] * ".userPreg()[0]." (".userPreg()[3].") has joined"); + + /* Save Seen */ + SeenSave(); + } + + /* auto op */ + if (loadValueFromConfigFile('AUTOMATIC', 'auto.op') == 'yes' && BotOpped() == true) { + $auto_op_list_c = loadValueFromConfigFile('AUTOMATIC', 'auto.op.list'); + $pieces = explode(", ", $auto_op_list_c); + + $mask2 = userPreg()[0].'!'.userPreg()[1].'@'.userPreg()[2]; + + if (in_array($mask2, $pieces)) { + cliLog("[bot] I have user: '".userPreg()[0]."' on the auto op list, giving op!"); + + toServer("MODE ".getBotChannel()." +o ".userPreg()[0]); + + PlaySound('prompt.mp3'); + } + } +} +//--------------------------------------------------------------------------------------------------------- +function on_PART() +{ + /* if bot part channel */ + if (userPreg()[0] == getBotNickname()) { + + /* set to not opped */ + unset($GLOBALS['BOT_OPPED']); + + /* delete channel */ + unset($GLOBALS['BOT_CHANNEL']); + + /* delete channel modes */ + unset($GLOBALS['CHANNEL.MODES']); + } else { + /* if someone part channel */ + cliLog("[".getBotChannel()."] * ".userPreg()[0]." (".userPreg()[3].") has leaved"); + + /* Save to database for seen purpose */ + SeenSave(); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_QUIT() +{ + isset(rawDataArray()[2]) ? $quit = inputFromLine(3) : $quit = ''; + + cliLog("[".getBotChannel()."] * ".userPreg()[0]." (".userPreg()[3].") Quit ({$quit})"); + + /* Save to database for seen purpose */ + SeenSave(); +} +//--------------------------------------------------------------------------------------------------------- +function on_TOPIC() /* topic change */ +{ + cliLog('['.getBotChannel().'] * '.userPreg()[0].' ('.userPreg()[3].') sets topic: "'.inputFromLine('3').'"'); +} +//--------------------------------------------------------------------------------------------------------- +function on_PRIVMSG() +{ + /* if register */ + if (rawDataArray()[2] == getBotNickname() && isset(rawDataArray()[3]) && rawDataArray()[3] == ':register') { + } elseif (rawDataArray()[2] == getBotChannel()) { /* if message in channel */ + cliLog("[".getBotChannel()."] <".userPreg()[0]."> ".inputFromLine('3')); + } elseif (rawDataArray()[2] == getBotNickname()) { /* if private message */ + cliLog("<".userPreg()[0]."> ".inputFromLine('3')); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_NICK() +{ + debug("on_nick()"); + + $a0 = str_replace(':', '', rawDataArray()[0]); + $a2 = str_replace(':', '', rawDataArray()[2]); + + if (userPreg()[0] == loadValueFromConfigFile('BOT', 'nickname') && empty($GLOBALS['I_USE_RND_NICKNAME'])) { + /* 1.set nickname */ + setBotNickname($a2); + cliLog("[bot] My new nickname is: ".getBotNickname()); + } elseif (isset($GLOBALS['BOT_NICKNAME']) && userPreg()[0] == $GLOBALS['BOT_NICKNAME']) { + setBotNickname($a2); + cliLog("[bot] My new nickname is: ".getBotNickname()); + } elseif ($a0 == getBotNickname() && $a2 != getBotNickname()) { + setBotNickname($a2); + cliLog("[bot] My new nickname is: ".getBotNickname()); + } else { + cliLog("[".getBotChannel()."] * ".userPreg()[0]. " changed nick to {$a2}"); + } +} +//--------------------------------------------------------------------------------------------------------- +function on_KICK() +{ + cliLog("[".getBotChannel()."] * ".userPreg()[0]." (".userPreg()[3].") kicked ".rawDataArray()[3]); + + /* if BOT kicked */ + if (isset(rawDataArray()[3]) && rawDataArray()[3] == getBotNickname()) { + /* set to not opped */ + unset($GLOBALS['BOT_OPPED']); + + /* delete channel modes */ + unset($GLOBALS['CHANNEL.MODES']); + + /* rejoin when kicked? */ + if (loadValueFromConfigFile('AUTOMATIC', 'auto.rejoin') == 'yes') { + cliLog("[bot] I was kicked from: ".getBotChannel()." by ".userPreg()[0]." (".userPreg()[3].") - rejoining!"); + sleep(2); + toServer("JOIN :".rawDataArray()[2]); + + PlaySound('prompt.mp3'); + } + + /* delete channel */ + unset($GLOBALS['BOT_CHANNEL']); + } + + /* Save to database for seen purpose */ + SeenSave(); +} diff --git a/src/panel/cfg/cgi.txt b/src/panel/cfg/cgi.txt deleted file mode 100644 index 996b7c5..0000000 --- a/src/panel/cfg/cgi.txt +++ /dev/null @@ -1 +0,0 @@ -.php;PHPFCGI ..\php\php-cgi.exe -c "..\php\php.ini" diff --git a/src/panel/cfg/forbidden b/src/panel/cfg/forbidden deleted file mode 100644 index e69de29..0000000 diff --git a/src/panel/cfg/init.txt b/src/panel/cfg/init.txt deleted file mode 100644 index f582d0d..0000000 --- a/src/panel/cfg/init.txt +++ /dev/null @@ -1,7 +0,0 @@ -httpHost=localhost -httpPort=80 -httpsPort=443 -noHTTPS=true -docPath=web -openURL= -indexFiles=index.php,index.html,index.htm \ No newline at end of file diff --git a/src/panel/cfg/mime.txt b/src/panel/cfg/mime.txt deleted file mode 100644 index ec31321..0000000 --- a/src/panel/cfg/mime.txt +++ /dev/null @@ -1,63 +0,0 @@ -# []...[] -image/svg+xml svg -image/gif gif -image/jpeg jpeg jpg jpe -application/EDI-Consent -application/EDI-X12 -application/EDIFACT -application/msword doc -application/news-message-id -application/news-transmission -application/octet-stream bin dms lha lzh exe class -application/pdf pdf -application/pkcs7-mime -application/pkcs7-signature -application/postscript ai eps ps -application/rtf rtf -application/set-registration -application/set-registration-initiation -application/x-gzip -application/x-javascript js -application/x-shockwave-flash swf -application/x-texinfo texinfo texi -application/x-wais-source src -application/xml -application/zip zip -audio/mpeg mpga mp2 mp3 -image/bmp bmp -image/png png -message/delivery-status -message/disposition-notification -message/external-body -message/http -message/news -message/partial -message/rfc822 -multipart/alternative -multipart/appledouble -multipart/byteranges -multipart/digest -multipart/encrypted -multipart/form-data -multipart/header-set -multipart/mixed -multipart/parallel -multipart/related -multipart/report -multipart/signed -text/css css -text/directory -text/enriched -text/html html htm -text/plain asc txt -text/prs.lines.tag -text/rfc822-headers -text/richtext rtx -text/rtf rtf -text/sgml sgml sgm -text/uri-list -text/xml xml -video/mpeg mpeg mpg mpe -video/mp4 mp4 -video/x-flv flv f4v -video/x-msvideo avi diff --git a/src/panel/cfg/speed.txt b/src/panel/cfg/speed.txt deleted file mode 100644 index a4d1b7e..0000000 --- a/src/panel/cfg/speed.txt +++ /dev/null @@ -1,8 +0,0 @@ -threads=17 -receive_timeout=10000 -content_encoding=deflate -fcgi_processes=17 -fcgi_gateway_timeout=10000 -cronjob_timeout=3600000 -cronjob_http_timeout=3600000 -convert_cgi_filenames=latin2 \ No newline at end of file diff --git a/src/panel/cfg/vdir.txt b/src/panel/cfg/vdir.txt deleted file mode 100644 index f616e84..0000000 --- a/src/panel/cfg/vdir.txt +++ /dev/null @@ -1 +0,0 @@ -logs/;..\..\LOGS diff --git a/src/panel/forbidden b/src/panel/forbidden deleted file mode 100644 index e69de29..0000000 diff --git a/src/panel/serv.exe b/src/panel/serv.exe deleted file mode 100644 index b11dfe75ebc54f1792f2aee144d5b2f6b6bfea7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162816 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 diff --git a/src/panel/web/api.php b/src/panel/web/api.php deleted file mode 100644 index 905a843..0000000 --- a/src/panel/web/api.php +++ /dev/null @@ -1,238 +0,0 @@ -get("MAIN", "WEB_BOT_CONFIG_FILE"); - - $cfg = new IniParser('../../'.$GLOBALS['WEB_BOT_CONFIG_FILE']); - /* BOT */ - $GLOBALS['CONFIG.NICKNAME'] = $cfg->get("BOT", "nickname"); - $GLOBALS['CONFIG.NAME'] = $cfg->get("BOT", "name"); - $GLOBALS['CONFIG.IDENT'] = $cfg->get("BOT", "ident"); - /* SERVER */ - $GLOBALS['CONFIG.SERVER'] = $cfg->get("SERVER", "server"); - $GLOBALS['CONFIG.PORT'] = $cfg->get("SERVER", "port"); - $GLOBALS['CONFIG.SERVER.PASSWD'] = $cfg->get("SERVER", "server.password"); - $GLOBALS['CONFIG.TRY.CONNECT'] = $cfg->get("SERVER", "try.connect"); - $GLOBALS['CONFIG.CONNECT.DELAY'] = $cfg->get("SERVER", "connect.delay"); - /* OWNER */ - $GLOBALS['CONFIG.BOT.ADMIN'] = $cfg->get("OWNER", "bot.admin"); - $GLOBALS['CONFIG.AUTO.OP.LIST'] = $cfg->get("OWNER", "auto.op.list"); - $GLOBALS['CONFIG.OWNERS'] = $cfg->get("OWNER", "bot.owners"); - $GLOBALS['CONFIG.OWNER.PASSWD'] = $cfg->get("OWNER", "owner.password"); - /* BOT RESPONSE */ - $GLOBALS['CONFIG.BOT.RESPONSE'] = $cfg->get("RESPONSE", "bot.response"); - /* AUTOMATIC */ - $GLOBALS['CONFIG.AUTO.OP'] = $cfg->get("AUTOMATIC", "auto.op"); - $GLOBALS['CONFIG.AUTO.REJOIN'] = $cfg->get("AUTOMATIC", "auto.rejoin"); - $GLOBALS['CONFIG.KEEP.NICK'] = $cfg->get("AUTOMATIC", "keep.nick"); - /* CHANNEL */ - $GLOBALS['CONFIG.CHANNEL'] = $cfg->get("CHANNEL", "channel"); - $GLOBALS['CONFIG.AUTO.JOIN'] = $cfg->get("CHANNEL", "auto.join"); - $GLOBALS['CONFIG.CHANNEL.MODES'] = $cfg->get("CHANNEL", "channel.modes"); - $GLOBALS['CONFIG.CHANNEL.KEY'] = $cfg->get("CHANNEL", "channel.key"); - /* COMMAND PREFIX */ - $GLOBALS['CONFIG.CMD.PREFIX'] = $cfg->get("COMMAND", "command.prefix"); - /* CTCP */ - $GLOBALS['CONFIG.CTCP.RESPONSE'] = $cfg->get("CTCP", "ctcp.response"); - $GLOBALS['CONFIG.CTCP.VERSION'] = $cfg->get("CTCP", "ctcp.version"); - $GLOBALS['CONFIG.CTCP.FINGER'] = $cfg->get("CTCP", "ctcp.finger"); - /* DELAYS */ - $GLOBALS['CONFIG.CHANNEL.DELAY'] = $cfg->get("DELAYS", "channel.delay"); - $GLOBALS['CONFIG.PRIVATE.DELAY'] = $cfg->get("DELAYS", "private.delay"); - $GLOBALS['CONFIG.NOTICE.DELAY'] = $cfg->get("DELAYS", "notice.delay"); - /* LOGGING */ - $GLOBALS['CONFIG.LOGGING'] = $cfg->get("LOGS", "logging"); - /* TIMEZONE */ - $GLOBALS['CONFIG.TIMEZONE'] = $cfg->get("TIME", "time.zone"); - /* FETCH */ - $GLOBALS['CONFIG.FETCH.SERVER'] = $cfg->get("FETCH", "fetch.server"); - /* PROGRAM */ - /* DEBUG */ - $GLOBALS['CONFIG.SHOW.RAW'] = $cfg->get("DEBUG", "show.raw"); -} -//------------------------------------------------------------------------------------------------------- -function GetAllData() -{ - $cfg = new IniParser('../web.ini'); - $GLOBALS['WEB_VERSION'] = $cfg->get("MAIN", "WEB_VERSION"); - $GLOBALS['WEB_PHP_VERSION'] = $cfg->get("MAIN", "WEB_PHP_VERSION"); - $GLOBALS['WEB_START_TIME'] = $cfg->get("MAIN", "WEB_START_TIME"); -} -//------------------------------------------------------------------------------------------------------- -function uptime_parse($seconds) -{ - $weeks = (floor($seconds / (60 * 60) / 24)) / 7; - $days = (floor($seconds / (60 * 60) / 24)) % 7; - $hours = (floor($seconds / (60 * 60))) % 24; - - $divisor_for_minutes = $seconds % (60 * 60); - $minutes = floor($divisor_for_minutes / 60); - - $divisor_for_seconds = $divisor_for_minutes % 60; - $seconds = ceil($divisor_for_seconds); - - $result = ""; - if (!empty($weeks) && $days > 0) { - $result .= $weeks . " week"; - } - if ($weeks > 1) { - $result .= "s"; - } - if (!empty($days) && $days > 0) { - $result .= $days . " day"; - } - if ($days > 1) { - $result .= "s"; - } - if (!empty($hours) && $hours > 0) { - $result .= $hours . " hour"; - } - if ($hours > 1) { - $result .= "s"; - } - if (!empty($minutes) && $minutes > 0) { - $result .= " " . $minutes . " minute"; - } - if ($minutes > 1) { - $result .= "s"; - } - if (!empty($seconds) && $seconds > 0) { - $result .= " " . $seconds . " second"; - } - if ($seconds > 1) { - $result .= "s"; - } - - return trim($result); -} -//------------------------------------------------------------------------------------------------------- -class IniParser -{ - public $iniFilename = ''; - public $iniParsedArray = array(); - - public function iniParser($file) - { - $this->iniFilename = $file; - if ($this->iniParsedArray = parse_ini_file($file, true)) { - return true; - } else { - return false; - } - } - - public function getSection($key) - { - return $this->iniParsedArray[$key]; - } - - public function getValue($sec, $key) - { - if (!isset($this->iniParsedArray[$sec])) { - return false; - } - return $this->iniParsedArray[$sec][$key]; - } - - public function get($sec, $key = null) - { - if (is_null($key)) { - return $this->getSection($sec); - } - return $this->getValue($sec, $key); - } - - public function setSection($sec, $array) - { - if (!is_array($array)) { - return false; - } - return $this->iniParsedArray[$sec] = $array; - } - - public function setValue($sec, $key, $value) - { - if ($this->iniParsedArray[$sec][$key] = $value) { - return true; - } - } - - public function set($sec, $key, $value = null) - { - if (is_array($key) && is_null($value)) { - return $this->setSection($sec, $key); - } - return $this->setValue($sec, $key, $value); - } - - public function save($file = null) - { - if ($file == null) { - $file = $this->iniFilename; - } - if (is_writeable($file)) { - $desc = fopen($file, "w"); - foreach ($this->iniParsedArray as $sec => $array) { - fwrite($desc, "[" . $sec . "]\r\n"); - foreach ($array as $key => $value) { - fwrite($desc, "$key = '$value'\r\n"); - } - fwrite($desc, "\r\n"); - } - fclose($desc); - return true; - } else { - return false; - } - } -} -//------------------------------------------------------------------------------------------------------- -function ListPlugins() -{ - /* count plugins */ - $count1 = count(glob("../../../".PLUGINSDIR."/OWNER/*.php", GLOB_BRACE)); - $count2 = count(glob("../../../".PLUGINSDIR."/ADMIN/*.php", GLOB_BRACE)); - $count3 = count(glob("../../../".PLUGINSDIR."/USER/*.php", GLOB_BRACE)); - - $tot = $count1+$count2+$count3+6; - - echo "

All Plugins: $tot


"; - - /* CORE COMMANDS */ - echo 'Core Commands (6):
'; - echo '- load
'; - echo '- panel
'; - echo '- pause
'; - echo '- seen
'; - echo '- unload
'; - echo '- unpause
'; - echo '
'; - - /* OWNERS PLUGINS */ - echo "Owner Plugins ($count1):
"; - - foreach (glob('../../../'.PLUGINSDIR.'/OWNER/*.php') as $plugin_name) { - $plugin_name = basename($plugin_name, '.php'); - echo "- $plugin_name
"; - } - echo '
'; - - /* ADMIN PLUGINS */ - echo "Admin Plugins ($count2):
"; - - foreach (glob('../../../'.PLUGINSDIR.'/ADMIN/*.php') as $plugin_name) { - $plugin_name = basename($plugin_name, '.php'); - echo "- $plugin_name
"; - } - echo '
'; - - /* USER PLUGINS */ - echo "User Plugins ($count3):
"; - - foreach (glob('../../../'.PLUGINSDIR.'/USER/*.php') as $plugin_name) { - $plugin_name = basename($plugin_name, '.php'); - echo "- $plugin_name
"; - } -} diff --git a/src/panel/web/index.php b/src/panel/web/index.php deleted file mode 100644 index b332c29..0000000 --- a/src/panel/web/index.php +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - -
-
-
-

- -

-Wrong Login or Password'; -} - -if (isset($_GET['d']) && $_GET['d'] == '1') { - echo 'You need to change default login/password in config! No entry'; -} -?> -
-Login: -
- -
-

-Password: -
- -

- -
- -get('PANEL', 'web_login'); - $password = $cfg->get('PANEL', 'web_password'); - $salt = $cfg->get('PANEL', 'web_salt'); - -if (isset($_GET['p']) && $_GET['p'] == "l") { - if ($_POST['user'] == '' && $_POST['keypass'] == '') { - header('Location: '.$_SERVER['PHP_SELF']); - exit; - } elseif ($_POST['user'] != $username) { - header('Location: '.$_SERVER['PHP_SELF'].'?s=1'); - exit; - } elseif ($_POST['keypass'] != $password) { - header('Location: '.$_SERVER['PHP_SELF'].'?s=1'); - exit; - } elseif ($_POST['user'] == 'changeme' && $_POST['keypass'] == 'changeme') { - header('Location: '.$_SERVER['PHP_SELF'].'?d=1'); - exit; - } elseif ($_POST['user'] == $username && $_POST['keypass'] == $password) { - setcookie('xs', hash('sha512', $_POST['keypass'].$salt)); - header('Location: main.php'); - exit; - } else { - echo '

error

'; - } -} -?> \ No newline at end of file diff --git a/src/panel/web/logout.php b/src/panel/web/logout.php deleted file mode 100644 index 88fc590..0000000 --- a/src/panel/web/logout.php +++ /dev/null @@ -1,16 +0,0 @@ -get('PANEL', 'web.login'); - $password = $cfg->get('PANEL', 'web.password'); - $salt = $cfg->get('PANEL', 'web.salt'); - -if (isset($_COOKIE['xs']) && $_COOKIE['xs'] == hash('sha512', $password.$salt)) { - unset($_COOKIE['xs']); - setcookie('xs', '', time() - 3600); - header('Location: index.php'); -} else { - exit; -} diff --git a/src/panel/web/logs.php b/src/panel/web/logs.php deleted file mode 100644 index 3f5ca3a..0000000 --- a/src/panel/web/logs.php +++ /dev/null @@ -1,70 +0,0 @@ -get('PANEL', 'web.login'); - $password = $cfg->get('PANEL', 'web.password'); - $salt = $cfg->get('PANEL', 'web.salt'); - -if (isset($_COOKIE['xs']) && $_COOKIE['xs'] == hash('sha512', $password.$salt)) { -?> - - - - - - - - - - - -
- -
- -
- -

-

Log Files:

-
- -'.basename($file).'
'; -} - -?> -
-
- -
- - - - diff --git a/src/panel/web/main.php b/src/panel/web/main.php deleted file mode 100644 index fc7616d..0000000 --- a/src/panel/web/main.php +++ /dev/null @@ -1,75 +0,0 @@ -get('PANEL', 'web.login'); - $password = $cfg->get('PANEL', 'web.password'); - $salt = $cfg->get('PANEL', 'web.salt'); - -if (isset($_COOKIE['xs']) && $_COOKIE['xs'] == hash('sha512', $password.$salt)) { -?> - - - - - - - - - - - -
- -
- -
- -

-Bot version:
'.$GLOBALS['WEB_VERSION'].'
'; - echo '

Php version:
'.$GLOBALS['WEB_PHP_VERSION'].'


'; - echo '

Uptime:
'; - echo "

Running since (".date('d.m.Y, H:i:s', $GLOBALS['WEB_START_TIME']). - ") and been running for ".$time.'


'; - echo '

Bot admin:
'; - echo '

'.$GLOBALS['CONFIG.BOT.ADMIN'].'


'; - echo '

Bot owners:
'; - echo '

'.$GLOBALS['CONFIG.OWNERS'].'


'; - -?> -
-
- -
- - - - diff --git a/src/panel/web/plugins.php b/src/panel/web/plugins.php deleted file mode 100644 index a0f9894..0000000 --- a/src/panel/web/plugins.php +++ /dev/null @@ -1,65 +0,0 @@ -get('PANEL', 'web.login'); - $password = $cfg->get('PANEL', 'web.password'); - $salt = $cfg->get('PANEL', 'web.salt'); - -if (isset($_COOKIE['xs']) && $_COOKIE['xs'] == hash('sha512', $password.$salt)) { -?> - - - - - - - - - - - -
- -
- -
- -

-

Plugins:

-
- - -
-
- -
- - - - diff --git a/src/panel/web/src/bullet.png b/src/panel/web/src/bullet.png deleted file mode 100644 index ae6094b8a3cdfa37a33fd37ec190d470ad4b3a8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 989 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJdx@v7EBh^OVLm|~sXuZ%fI^%F z9+AZi3|t>Tn9*sC$qWVtW`+Qt5LcjN0~j}esRkggq2T}!+&Qr9)Q7(Pulx7ETDkQ= z|Dg~4hd%Zn{y62p^Vx@=PdNN>(&3L&k9?ed^y8cpALpF>xbF0$wWpsfJNI$z`Hvee zecW>8 z3l7XYB|-rcrcZJZ3kVCJC^*qYC?F_wir6!Ep$Stb3qE!h6A%%9DlFt+Hl@3N`fQ_i ZMuuv2)vrR8sudvLd%F6$taD0e0swk5zWe|H diff --git a/src/panel/web/src/link.png b/src/panel/web/src/link.png deleted file mode 100644 index 8e16400d044670d11d387b21093cfea1962fdfbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 911 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 iE${T%OW0g<&Hn?X(L^#Oh~LHl0000this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- -function if_PLUGIN() +function LoadPlugins() { - global $rawcmd; - global $rawDataArray; - global $mask; - - /* Unpause -OWNER- core command */ - (HasOwner($mask) && isset($rawcmd[1]) && $rawcmd[1] == $GLOBALS['CONFIG.CMD.PREFIX'].'unpause') ? plugin_Unpause() : false; - - if (empty($GLOBALS['stop'])) { - /* register to bot - core command */ - (isset($rawcmd[1]) && $rawcmd[1] == 'register' && $rawDataArray[2] == getBotNickname()) ? CoreCmd_RegisterToBot() : false; - //--------------------------------------------------------------------------------------------------------- - /* response to plugins requests */ - if (isset($rawcmd[1][0]) && $rawcmd[1][0] == $GLOBALS['CONFIG.CMD.PREFIX']) { - $pluginReq = str_replace($GLOBALS['CONFIG.CMD.PREFIX'], '', $rawcmd[1]); - $p = $GLOBALS['CONFIG.CMD.PREFIX']; - - /* OWNER */ - if (HasOwner($mask) && in_array_r($rawcmd[1], [$p.'seen', $p.'panel', $p.'load', $p.'unload', $p.'pause', - $GLOBALS['OWNER_PLUGINS'], $GLOBALS['ADMIN_PLUGINS'], $GLOBALS['USER_PLUGINS']])) { - call_user_func('plugin_'.$pluginReq); - pluginUsageCli($pluginReq); - /* ADMIN */ - } elseif (!HasOwner($mask) && HasAdmin($mask) && in_array_r($rawcmd[1], [$p.'seen', $GLOBALS['ADMIN_PLUGINS'], $GLOBALS['USER_PLUGINS']])) { - call_user_func('plugin_'.$pluginReq); - pluginUsageCli($pluginReq); - /* USER */ - } elseif (!HasOwner($mask) && !HasAdmin($mask) && in_array_r($rawcmd[1], [$p.'seen', $GLOBALS['USER_PLUGINS']])) { - call_user_func('plugin_'.$pluginReq); - pluginUsageCli($pluginReq); - } - - if (!function_exists('plugin_')) { - function plugin_() - { - } - } - } + $GLOBALS['ALL_PLUGINS'] = null; + + /* CORE PLUGINS */ + cli(">>> 'Core' Plugins (".CORECOUNT." Plugins) [lvl: 0] <<<"); + + cliLine(); + + foreach (CORECOMMANDSLIST as $corePlugin => $corePluginDescription) { + cli('['.$corePlugin.'] -- '.$corePluginDescription); } + + cliLine(); + + $pluginsSum = null; + + foreach (usersDirectoriesToArray() as $userDirectory) { + $pluginsSum = $pluginsSum + countPlugins($userDirectory); + + $userLvl = getUserLevelByUserName($userDirectory); + + cli(">>> '".$userDirectory."' Plugins (".countPlugins($userDirectory)." Plugins) [lvl: ".$userLvl."] <<<"); + + cliLine(); + + /* add to $GLOBALS[$user.'_PLUGINS'] & include plugin */ + loadPluginsFromEachGroupDir($userDirectory); + + cliLine(); + } + + $pluginsSum = $pluginsSum + CORECOUNT; + + cli("----------------------------------------------------------Total: (".$pluginsSum.")---------"); } //--------------------------------------------------------------------------------------------------------- -function LoadPlugins() +function countPlugins($user) { - $CountedOwner = count(glob(PLUGINSDIR."/OWNER/*.php", GLOB_BRACE)); - $CountedAdmin = count(glob(PLUGINSDIR."/ADMIN/*.php", GLOB_BRACE)); - $CountedUser = count(glob(PLUGINSDIR."/USER/*.php", GLOB_BRACE)); - - $GLOBALS['OWNER_PLUGINS'] = null; - $GLOBALS['ADMIN_PLUGINS'] = null; - $GLOBALS['USER_PLUGINS'] = null; + $count = count(glob(PLUGINSDIR."/{$user}/*.php")); + if ($count == 0) { + return 0; + } else { + return $count; + } +} //--------------------------------------------------------------------------------------------------------- - /* CORE PLUGINS */ - cli('>>> Core Commands ('.CORECOUNT.') <<<'); - line(); - cli('[load] -- Loads specified plugins to BOT: !load '); - cli('[panel] -- Starts web admin panel for BOT: !panel help'); - cli('[pause] -- Pause all BOT activity: !pause'); - cli('[seen] -- Check specified user when was last seen on channel: !seen '); - cli('[unload] -- Unloads specified plugin from BOT: !unload '); - cli('[unpause] -- Restore BOT from pause mode: !unpause'); - - line(); -//--------------------------------------------------------------------------------------------------------- - /* OWNER PLUGINS */ - cli(">>> Owner Plugins ({$CountedOwner}) <<<"); - line(); +function loadPluginsFromEachGroupDir($user) +{ + $GLOBALS[$user.'_PLUGINS'] = null; - foreach (glob(PLUGINSDIR.'/OWNER/*.php') as $pluginName) { + foreach (glob(PLUGINSDIR."/{$user}/*.php") as $pluginName) { /* simple verify plugin */ if (preg_match("~\b".PLUGIN_HASH."\b~", file_get_contents($pluginName))) { include_once($pluginName); - $GLOBALS['OWNER_PLUGINS'] .= "{$GLOBALS['CONFIG.CMD.PREFIX']}{$plugin_command} "; - $pluginName = basename($pluginName, '.php'); - cli("[{$pluginName}] -- {$plugin_description}"); + + $GLOBALS[$user.'_PLUGINS'][] .= $plugin_command; + $GLOBALS['ALL_PLUGINS'][] .= $plugin_command; + + cli('['.basename($pluginName, '.php').'] -- '.$plugin_description); } else { - $pluginName = basename($pluginName, '.php'); - cli("[ERROR: {$pluginName}] - Incompatible plugin!"); + cli('[ERROR: '.basename($pluginName, '.php').'] - Incompatible plugin!'); } } - (count(glob(PLUGINSDIR."/OWNER/*.php")) === 0) ? cli("(no plugins)") : false; - line(); +} //--------------------------------------------------------------------------------------------------------- - /* ADMIN PLUGINS */ - cli(">>> Admin Plugins ({$CountedAdmin}) <<<"); - line(); +function usersDirectoriesToArray() /* return directory as groups array */ +{ + $a = null; - foreach (glob(PLUGINSDIR.'/ADMIN/*.php') as $pluginName) { - /* simple verify plugin */ - if (preg_match("~\b".PLUGIN_HASH."\b~", file_get_contents($pluginName))) { - include_once($pluginName); - $GLOBALS['ADMIN_PLUGINS'] .= "{$GLOBALS['CONFIG.CMD.PREFIX']}{$plugin_command} "; - $pluginName = basename($pluginName, '.php'); - cli("[{$pluginName}] -- {$plugin_description}"); - } else { - $pluginName = basename($pluginName, '.php'); - cli("[ERROR: {$pluginName}] - Incompatible plugin!"); - } + foreach (glob(PLUGINSDIR."/*", GLOB_ONLYDIR) as $group) { + $a[] .= str_replace(PLUGINSDIR.'/', '', $group); } - (count(glob(PLUGINSDIR."/ADMIN/*.php")) === 0) ? cli("(no plugins)") : false; - line(); + + return $a; +} +//--------------------------------------------------------------------------------------------------------- +function coreCommandsToArray() /* return core commands array */ +{ + return array_keys(CORECOMMANDSLIST); +} //--------------------------------------------------------------------------------------------------------- - /* USER PLUGINS */ - cli(">>> User Plugins ({$CountedUser}) <<<"); - line(); +function ifPrivilegesExecuteCommand() +{ + global $rawcmd; + $who = whoIsUser(); - foreach (glob(PLUGINSDIR.'/USER/*.php') as $pluginName) { - /* simple verify plugin */ - if (preg_match("~\b".PLUGIN_HASH."\b~", file_get_contents($pluginName))) { - include_once($pluginName); - $GLOBALS['USER_PLUGINS'] .= "{$GLOBALS['CONFIG.CMD.PREFIX']}{$plugin_command} "; - $pluginName = basename($pluginName, '.php'); - cli("[{$pluginName}] -- {$plugin_description}"); - } else { - $pluginName = basename($pluginName, '.php'); - cli("[ERROR: {$pluginName}] - Incompatible plugin!"); + /* owner */ + if ($who[1] == 0) { + if (in_array_r(substr($rawcmd[1], 1), [coreCommandsToArray(), $GLOBALS['ALL_PLUGINS']])) { + call_user_func('plugin_'.substr($rawcmd[1], 1)); + pluginUsageCli(substr($rawcmd[1], 1)); } + /* user */ + } else if ($who[1] == 999) { + if (in_array_r(substr($rawcmd[1], 1), ['seen', $GLOBALS[$who[0].'_PLUGINS']])) { + call_user_func('plugin_'.substr($rawcmd[1], 1)); + pluginUsageCli(substr($rawcmd[1], 1)); + } + /* all else */ + } else { + if (in_array_r(substr($rawcmd[1], 1), ['seen', $GLOBALS[$who[0].'_PLUGINS'], $GLOBALS[getStandardUserName().'_PLUGINS'], returnNextUsersCommands($who[1])])) { + call_user_func('plugin_'.substr($rawcmd[1], 1)); + pluginUsageCli(substr($rawcmd[1], 1)); + } } +} +//--------------------------------------------------------------------------------------------------------- +function getStandardUserName() +{ + $cfg = new IniParser(getConfigFileName()); + $section = $cfg->getsection("USERSLEVELS"); - (count(glob(PLUGINSDIR."/USER/*.php")) === 0) ? cli("(no plugins)") : false; - - $allCounted = CORECOUNT+$CountedOwner+$CountedAdmin+$CountedUser; - - cli("----------------------------------------------------------Total: ({$allCounted})---------"); - unset($allCounted); - + /* get user name */ + foreach ($section as $user => $level) { + if ($level == 999) { + return $user; + } + } +} //--------------------------------------------------------------------------------------------------------- - /* OWNER Plugins array */ - $GLOBALS['OWNER_PLUGINS'] = explode(" ", $GLOBALS['OWNER_PLUGINS']); - - /* ADMIN Plugins array */ - $GLOBALS['ADMIN_PLUGINS'] = explode(" ", $GLOBALS['ADMIN_PLUGINS']); +function getOwnerUserName() +{ + $cfg = new IniParser(getConfigFileName()); + $section = $cfg->getsection("USERSLEVELS"); - /* USER Plugins array */ - $GLOBALS['USER_PLUGINS'] = explode(" ", $GLOBALS['USER_PLUGINS']); + foreach ($section as $user => $level) { + if ($level == 0) { + return $user; + } + } } //--------------------------------------------------------------------------------------------------------- -function UnloadPlugin($plugin) +function isUserOwner() { - try { - $withPrefix = $GLOBALS['CONFIG.CMD.PREFIX'].$plugin; - $withoutPrefix = $plugin; - - if (in_array($withPrefix, $GLOBALS['OWNER_PLUGINS']) || in_array($withPrefix, $GLOBALS['ADMIN_PLUGINS']) || - in_array($withPrefix, $GLOBALS['USER_PLUGINS'])) { - if (($key = array_search($withPrefix, $GLOBALS['OWNER_PLUGINS'])) !== false) { - unset($GLOBALS['OWNER_PLUGINS'][$key]); - //TODO: rename function - if (!in_array($withPrefix, $GLOBALS['OWNER_PLUGINS'])) { - cliLog("[Plugin]: '{$withoutPrefix}' unloaded by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); - response("Plugin: '{$withoutPrefix}' unloaded."); - } - } + debug("isUserOwner()"); + + $cfg = new IniParser(getConfigFileName()); + $section = $cfg->getsection("PRIVILEGES"); + + foreach ($section as $user => $mask) { + if (fnmatch($mask, userFullMask(), 16)) { + $level = loadValueFromConfigFile('USERSLEVELS', $user); + + if ($level == 0) { + return true; + } else { + return false; + } + } + } +} //--------------------------------------------------------------------------------------------------------- - if (($key = array_search($withPrefix, $GLOBALS['ADMIN_PLUGINS'])) !== false) { - unset($GLOBALS['ADMIN_PLUGINS'][$key]); - //TODO: rename function - if (!in_array($withPrefix, $GLOBALS['ADMIN_PLUGINS'])) { - cliLog("[Plugin]: '{$withoutPrefix}' unloaded by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); - response("Plugin: '{$withoutPrefix}' unloaded."); - } - } +function whoIsUser() +{ + $cfg = new IniParser(getConfigFileName()); + $section = $cfg->getsection("PRIVILEGES"); + + foreach ($section as $user => $mask) { + $pieces = explode(", ", $mask); + + foreach ($pieces as $piece) { + + if (fnmatch($piece, userFullMask(), 16)) { + $level = loadValueFromConfigFile('USERSLEVELS', $user); + $data = [$user, $level, $mask]; + } + } + } + + if (!empty($data)) { + return $data; + } else { + return [getStandardUserName(), '999']; + } +} //--------------------------------------------------------------------------------------------------------- - if (($key = array_search($withPrefix, $GLOBALS['USER_PLUGINS'])) !== false) { - unset($GLOBALS['USER_PLUGINS'][$key]); - //TODO: rename function - if (!in_array($withPrefix, $GLOBALS['USER_PLUGINS'])) { - cliLog("[Plugin]: '{$withoutPrefix}' unloaded by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); - response("Plugin: '{$withoutPrefix}' unloaded."); - } - } - } else { - cliLog("[PLUGIN]: No such plugin to unload: '{$GLOBALS['piece1']}' by: {$GLOBALS['USER']} ({$GLOBALS['USER_HOST']}), channel: ".getBotChannel()); - response('No such plugin to unload'); +function returnNextUsersCommands($from) /* format: command command2 etc,. */ +{ + $cfg = new IniParser(getConfigFileName()); + $section = $cfg->getsection("USERSLEVELS"); + + $users = []; + + /* get user name */ + foreach ($section as $user => $level) { + if ($level > $from && $level != 999) { + $users[] .= $user; } - } catch (Exception $e) { - cliLog('[ERROR]: Function: '.__FUNCTION__.' failed'); + } + + $commands = null; + + for ($i=0; $igetsection("USERSLEVELS"); + + foreach ($section as $entry => $level) { + if ($entry == $user) { + return $level; } - } catch (Exception $e) { - cliLog('[ERROR]: Function: '.__FUNCTION__.' failed'); } } diff --git a/src/socket.php b/src/socket.php index a8ad674..17fee75 100644 --- a/src/socket.php +++ b/src/socket.php @@ -24,13 +24,18 @@ function tryToConnect() { $i = 0; - while ($i++ <= $GLOBALS['CONFIG.TRY.CONNECT']) { - $GLOBALS['socket'] = @fsockopen($GLOBALS['CONFIG.SERVER'], $GLOBALS['CONFIG.PORT'], $GLOBALS['errno'], $GLOBALS['errstr']); + while ($i++ <= loadValueFromConfigFile('SERVER', 'try.connect')) { + $GLOBALS['socket'] = @fsockopen(loadValueFromConfigFile('SERVER', 'server'), + loadValueFromConfigFile('SERVER', 'port'), + $GLOBALS['errno'], $GLOBALS['errstr']); if ($GLOBALS['socket'] == false) { - cliLog("[bot] Cannot connect to: {$GLOBALS['CONFIG.SERVER']}:{$GLOBALS['CONFIG.PORT']}, trying again ({$i}/{$GLOBALS['CONFIG.TRY.CONNECT']})..."); + cliLog("[bot] Cannot connect to: ".loadValueFromConfigFile('SERVER', 'server'). + ":".loadValueFromConfigFile('SERVER', 'port'). + ", trying again ({$i}/". + loadValueFromConfigFile('SERVER', 'try.connect').")..."); PlaySound('error_conn.mp3'); - usleep($GLOBALS['CONFIG.CONNECT.DELAY'] * 1000000); // reconnect delay - if ($i == $GLOBALS['CONFIG.TRY.CONNECT']) { + usleep(loadValueFromConfigFile('SERVER', 'connect.delay') * 1000000); // reconnect delay + if ($i == loadValueFromConfigFile('SERVER', 'try.connect')) { cliLog('[bot] Unable to connect to server, exiting program.'); PlaySound('error_conn.mp3'); WinSleep(7); @@ -44,108 +49,97 @@ function tryToConnect() //--------------------------------------------------------------------------------------------------------- function Identify() { - if (!empty($GLOBALS['CONFIG.SERVER.PASSWD'])) { - toServer("PASS {$GLOBALS['CONFIG.SERVER.PASSWD']}"); + if (!empty(loadValueFromConfigFile('SERVER', 'server.password'))) { + toServer("PASS ".loadValueFromConfigFile('SERVER', 'server.password')); } - toServer("NICK {$GLOBALS['CONFIG.NICKNAME']}"); + toServer("NICK ".loadValueFromConfigFile('BOT', 'nickname')); - toServer("USER {$GLOBALS['CONFIG.IDENT']} 8 * :{$GLOBALS['CONFIG.NAME']}"); + toServer("USER ".loadValueFromConfigFile('BOT', 'ident')." 8 * :".loadValueFromConfigFile('BOT', 'name')); return true; } //--------------------------------------------------------------------------------------------------------- function SocketLoop() { - global $args; - global $args1; - global $USER; - global $USER_IDENT; - global $USER_HOST; - global $host; - global $piece1; - global $piece2; - global $piece3; - global $piece4; - global $rawDataArray; + global $rawData; global $rawcmd; - global $mask; global $BOT_NICKNAME; global $I_USE_RND_NICKNAME; //--------------------------------------------------------------------------------------------------------- - /* set initial */ - $USER_IDENT = null; - $host = null; $BOT_NICKNAME = null; - $mask = null; $I_USE_RND_NICKNAME = null; - - /* save data for web panel */ - WebEntry(); //--------------------------------------------------------------------------------------------------------- /* main socket loop */ while (1) { while (!feof($GLOBALS['socket'])) { /* start timers */ - empty($GLOBALS['stop']) ? StartTimers() : false; + !getPause() ? StartTimers() : false; /* get raw data */ $rawData = fgets($GLOBALS['socket'], 1024); -//--------------------------------------------------------------------------------------------------------- + /* if raw mode send debug data to cli */ cliDebug($rawData, 0); -//--------------------------------------------------------------------------------------------------------- + /* put raw data to array */ - $rawDataArray = explode(' ', trim($rawData)); -//--------------------------------------------------------------------------------------------------------- + rawDataArray(); + /* if ping -> response */ - (isset($rawDataArray[0]) && $rawDataArray[0] == 'PING') ? on_PING() : false; -//--------------------------------------------------------------------------------------------------------- - /* parse vars from rawDataArray[0] */ - if (preg_match('/^:(.*)\!(.*)\@(.*)$/', $rawDataArray[0], $source)) { - $USER = $source[1]; - $USER_IDENT = $source[2]; - $host = $source[3]; - $USER_HOST = $USER_IDENT.'@'.$host; + if (isset(rawDataArray()[0]) && rawDataArray()[0] == 'PING') { + on_PING(); } -//--------------------------------------------------------------------------------------------------------- + /* if operation (JOIN,PART,etc) -> response */ - if_WORD_RESPONSE(); -//--------------------------------------------------------------------------------------------------------- - if (count($rawDataArray) < 4) { - continue; + if (isset(rawDataArray()[1]) && in_array(rawDataArray()[1], ['JOIN', 'PART', 'KICK', 'TOPIC', 'PRIVMSG', + 'NICK', 'QUIT', 'MODE', 'NOTICE'])) { + function_exists('on_'.rawDataArray()[1]) ? call_user_func('on_'.rawDataArray()[1]) : false; } -//--------------------------------------------------------------------------------------------------------- - isset($rawDataArray[3]) ? $rawcmd = explode(':', $rawDataArray[3]) : false; - /* Case sensitive */ - isset($rawcmd[1]) ? $rawcmd[1] = strtolower($rawcmd[1]) : false; - - $args = null; for ($i=4; $i < count($rawDataArray); $i++) { - $args .= $rawDataArray[$i].''; - } - $args1 = null; for ($i=4; $i < count($rawDataArray); $i++) { - $args1 .= $rawDataArray[$i].' '; + if (count(rawDataArray()) < 4) { + continue; } - isset($USER) ? $mask = $USER.'!'.$USER_IDENT.'@'.$host : false; - - $pieces = explode(" ", $args1); + isset(rawDataArray()[3]) ? $rawcmd = explode(':', rawDataArray()[3]) : false; - isset($pieces[0]) ? $piece1 = $pieces[0] : $piece1 = ''; - isset($pieces[1]) ? $piece2 = $pieces[1] : $piece2 = ''; - isset($pieces[2]) ? $piece3 = $pieces[2] : $piece3 = ''; - isset($pieces[3]) ? $piece4 = $pieces[3] : $piece4 = ''; + /* Case sensitive */ + isset($rawcmd[1]) ? $rawcmd[1] = strtolower($rawcmd[1]) : false; //--------------------------------------------------------------------------------------------------------- - /* if reply (001,002,etc) -> response */ - if_NUMERIC_RESPONSE(); + /* if numeric message from server (001,002,etc) -> response */ + if (isset(rawDataArray()[1]) && is_numeric(rawDataArray()[1])) { + function_exists('on_'.rawDataArray()[1]) ? call_user_func('on_'.rawDataArray()[1]) : false; + } /* if CTCP request -> response */ - if_CTCP(); + if (!getPause() && loadValueFromConfigFile('CTCP', 'ctcp.response') == 'yes' && isset($rawcmd[1][0]) && $rawcmd[1][0] == '') { + if_CTCP(); + } /* if plugin request -> response */ - if_PLUGIN(); + if (getPause() == true) { + /* Command: 'unpause' -> OWNER core command: works only if paused */ + if (isUserOwner() && isset($rawcmd[1]) && $rawcmd[1] == loadValueFromConfigFile('COMMAND', 'command.prefix').'unpause') { + plugin_unpause(); + } + } + + if (getPause() == false) { + + /* Command: 'register' -> register to bot from user */ + if (isset($rawcmd[1]) && $rawcmd[1] == 'register' && rawDataArray()[2] == getBotNickname()) { + plugin_register(); + } + + /* 1. if first char == command prefix (eg. !) */ + if (isset($rawcmd[1][0]) && $rawcmd[1][0] == loadValueFromConfigFile('COMMAND', 'command.prefix')) { + ifPrivilegesExecuteCommand(); + + if (!function_exists('plugin_')) { + function plugin_() { } + } + } + } //--------------------------------------------------------------------------------------------------------- } /* if disconected */ @@ -153,27 +147,27 @@ function SocketLoop() cliDebug($rawData, 0); if (preg_match("~\bToo many connections from your IP\b~", $rawData)) { - cliLog("[bot] Cannot connect to: {$GLOBALS['CONFIG.SERVER']}:{$GLOBALS['CONFIG.PORT']}, too many connections! Exiting."); + cliLog("[bot] Cannot connect to: ".loadValueFromConfigFile('SERVER', 'server').":".loadValueFromConfigFile('SERVER', 'port').", too many connections! Exiting."); WinSleep(10); exit; } if (preg_match("~\bSession limit exceeded\b~", $rawData)) { - cliLog("[bot] Cannot connect to: {$GLOBALS['CONFIG.SERVER']}:{$GLOBALS['CONFIG.PORT']}, too many connections! Exiting."); + cliLog("[bot] Cannot connect to: ".loadValueFromConfigFile('SERVER', 'server').":".loadValueFromConfigFile('SERVER', 'port').", too many connections! Exiting."); WinSleep(10); exit; } if (preg_match("~\bToo many user connections\b~", $rawData)) { - cliLog("[bot] Cannot connect to: {$GLOBALS['CONFIG.SERVER']}:{$GLOBALS['CONFIG.PORT']}, too many connections! Exiting."); + cliLog("[bot] Cannot connect to: ".loadValueFromConfigFile('SERVER', 'server').":".loadValueFromConfigFile('SERVER', 'port').", too many connections! Exiting."); WinSleep(10); exit; } - cliLog("[bot] Cannot connect to: {$GLOBALS['CONFIG.SERVER']}:{$GLOBALS['CONFIG.PORT']}, Exiting!"); + cliLog("[bot] Cannot connect to: ".loadValueFromConfigFile('SERVER', 'server').":".loadValueFromConfigFile('SERVER', 'port').", Exiting!"); WinSleep(10); exit; } @@ -182,28 +176,34 @@ function SocketLoop() //--------------------------------------------------------------------------------------------------------- function response($msg) { - switch ($GLOBALS['CONFIG.BOT.RESPONSE']) { + switch (loadValueFromConfigFile('RESPONSE', 'bot.response')) { case 'channel': toServer("PRIVMSG ".getBotChannel()." :$msg"); - usleep($GLOBALS['CONFIG.CHANNEL.DELAY'] * 1000000); + usleep(loadValueFromConfigFile('DELAYS', 'channel.delay') * 1000000); break; case 'notice': - toServer("NOTICE {$GLOBALS['USER']} :$msg"); - usleep($GLOBALS['CONFIG.NOTICE.DELAY'] * 1000000); + toServer("NOTICE ".userPreg()[0]." :$msg"); + usleep(loadValueFromConfigFile('DELAYS', 'notice.delay') * 1000000); break; case 'priv': - toServer("PRIVMSG {$GLOBALS['USER']} :$msg"); - usleep($GLOBALS['CONFIG.PRIVATE.DELAY'] * 1000000); + toServer("PRIVMSG ".userPreg()[0]." :$msg"); + usleep(loadValueFromConfigFile('DELAYS', 'private.delay') * 1000000); break; } } //--------------------------------------------------------------------------------------------------------- function privateMsg($message) { - toServer("PRIVMSG {$GLOBALS['USER']} :$msg"); - usleep($GLOBALS['CONFIG.PRIVATE.DELAY'] * 1000000); + toServer("PRIVMSG ".userPreg()[0]." :{$message}"); + usleep(loadValueFromConfigFile('DELAYS', 'private.delay') * 1000000); +} +//--------------------------------------------------------------------------------------------------------- +function privateMsgTo($user, $message) +{ + toServer("PRIVMSG {$user} :{$message}"); + usleep(loadValueFromConfigFile('DELAYS', 'private.delay') * 1000000); } //--------------------------------------------------------------------------------------------------------- function joinChannel($channel) @@ -221,10 +221,12 @@ function toServer($data) //--------------------------------------------------------------------------------------------------------- function msgFromServer() { + debug("msgFromServer()"); + $msg = null; - for ($i=3; $i < count($GLOBALS['rawDataArray']); $i++) { - $msg .= str_replace(':', '', $GLOBALS['rawDataArray'][$i]).' '; + for ($i=3; $i < count(rawDataArray()); $i++) { + $msg .= str_replace(':', '', rawDataArray()[$i]).' '; } return $msg; @@ -249,6 +251,11 @@ function getBotModes() } } //--------------------------------------------------------------------------------------------------------- +function setBotModes($modes) +{ + $GLOBALS['BOT_MODES'] = $modes; +} +//--------------------------------------------------------------------------------------------------------- function getServerName() { if (isset($GLOBALS['serverName']) && !empty($GLOBALS['serverName'])) { @@ -265,9 +272,7 @@ function getBotChannel() //--------------------------------------------------------------------------------------------------------- function setBotChannel($channel) { - global $botChannel; - - $botChannel = $channel; + $GLOBALS['BOT_CHANNEL'] = $channel; } //--------------------------------------------------------------------------------------------------------- function setServerName($name) @@ -275,4 +280,82 @@ function setServerName($name) global $serverName; $serverName = $name; -} \ No newline at end of file +} +//--------------------------------------------------------------------------------------------------------- +function msgAsArguments() +{ + $args = null; + + for ($i=4; $i < count(rawDataArray()); $i++) { + $args .= rawDataArray()[$i].''; + } + + return $args; +} +//--------------------------------------------------------------------------------------------------------- +function userPreg() +{ + debug("userPreg()"); + + if (preg_match('/^:(.*)\!(.*)\@(.*)$/', rawDataArray()[0], $source)) { + $USER = $source[1]; + $USER_IDENT = $source[2]; + $host = $source[3]; + $USER_HOST = $USER_IDENT.'@'.$host; + + return [$USER, $USER_IDENT, $host, $USER_HOST]; + } +} +//--------------------------------------------------------------------------------------------------------- +function userFullMask() +{ + debug("userFullMask()"); + + if (isset(userPreg()[0])) { + return userPreg()[0].'!'.userPreg()[1].'@'.userPreg()[2]; + } +} +//--------------------------------------------------------------------------------------------------------- +function msgPieces() +{ + debug("msgPieces()"); + + $args = null; + for ($i=4; $i < count(rawDataArray()); $i++) { + $args .= rawDataArray()[$i].' '; + } + + $pieces = explode(" ", $args); + + if (isset($pieces[0])) { + $piece1 = $pieces[0]; + } else { + $piece1 = ''; + } + + if (isset($pieces[1])) { + $piece2 = $pieces[1]; + } else { + $piece2 = ''; + } + + if (isset($pieces[2])) { + $piece3 = $pieces[2]; + } else { + $piece3 = ''; + } + if (isset($pieces[3])) { + $piece4 = $pieces[3]; + } else { + $piece4 = ''; + } + + return [$piece1, $piece2, $piece3, $piece4]; +} +//--------------------------------------------------------------------------------------------------------- +function rawDataArray() +{ + $rawDataArray = explode(' ', trim($GLOBALS['rawData'])); + + return $rawDataArray; +} diff --git a/src/start.php b/src/start.php index 4796256..943a9da 100644 --- a/src/start.php +++ b/src/start.php @@ -20,78 +20,70 @@ 'Visit this page for more information.') : false; //--------------------------------------------------------------------------------------------------------- -function Start() +function StartBot() { - /* if no dirs -> create */ - !is_dir(LOGSDIR) ? mkdir(LOGSDIR) : false; - !is_dir(DATADIR) ? mkdir(DATADIR) : false; - - /* Check if we got cli args - args.php */ - if (isset($_SERVER['argv'][1])) { - checkArgs(); - } - - /* info */ - Baner(); - - /* Check if there is new version */ - CheckUpdateInfo(); + /* if no dirs -> create */ + !is_dir(LOGSDIR) ? mkdir(LOGSDIR) : false; + !is_dir(DATADIR) ? mkdir(DATADIR) : false; + + /* Check if we got cli args - args.php */ + if (isset($_SERVER['argv'][1])) { + checkArgs(); + } + + /* info */ + Baner(); + + /* Check if there is new version */ + CheckUpdateInfo(); + + /* if no config -> create default one */ + if (!is_file(getConfigFileName())) { + cli('[WARNING] Config file missing! Creating default config in: '.getConfigFileName().N); + CreateDefaultDataConfigFile(); + + if (!is_file(getConfigFileName())) { + cli('[ERROR]: Error! Cannot make default config! Read-Only filesystem? Exiting.'); + WinSleep(6); + exit; + } + } + + /* set timezone from config */ + if (!empty(loadValueFromConfigFile('TIME', 'time.zone'))) { + date_default_timezone_set(loadValueFromConfigFile('TIME', 'time.zone')); + } + + /* Logging init - logs.php */ + if (loadValueFromConfigFile('LOGS', 'logging') == 'yes' && is_dir(LOGSDIR)) { + LogsInit(); + } + + cliLog('Configuration Loaded from: '.getConfigFileName()); + + cliLine(); + + /* check if we got default owner password, if yes -> change it - misc.php */ + if (loadValueFromConfigFile('OWNER', 'owner.password') == DEFAULT_PWD) { + PlaySound('error_conn.mp3'); + + cliLog('[WARNING] Default BOT owner(s) password detected!'); + cliLog('[WARNING] For security please change it (password can not contain spaces)'); - /* Load config - config.php */ - if (isset($GLOBALS['configFile'])) { /* args already checks if file exists */ - LoadConfig(); - } else { - $GLOBALS['configFile'] = CONFIGFILE; - - /* if config file exists */ - if (is_file($GLOBALS['configFile'])) { - LoadConfig(); - } else { - cliLog('[bot] Config file missing! Creating default config in: '.CONFIGFILE.N); - CreateDefaultConfig(CONFIGFILE); - - /* Load config again */ - if (is_file(CONFIGFILE)) { - LoadConfig(); - } else { /* read only file system? */ - cliLog('[bot]: Error! Cannot make default config! Read-Only filesystem? Exiting.'); - WinSleep(6); - exit; - } - } - } - - /* check if we got default owner password, if yes -> change it - misc.php */ - if ($GLOBALS['CONFIG.OWNER.PASSWD'] == DEFAULT_PWD) { - PlaySound('error_conn.mp3'); - - cliLog('[bot] Default BOT owner(s) password detected!'); - cliLog('[bot] For security please change it (password can not contain spaces)'); - - changeDefaultOwnerPwd(); - } - - /* Logging init - logs.php */ - if ($GLOBALS['CONFIG.LOGGING'] == 'yes' && is_dir(LOGSDIR)) { - LogsInit(); - } - - /* Load plugins - plugins.php */ - LoadPlugins(); - - /* check if panel is not closed */ - if (isRunned('serv')) { - kill('serv') ? cliLog('[bot] Detected Panel still running, Terminating.') : false; - } - - /* Time to connect */ - cliLog("[bot] Connecting to: {$GLOBALS['CONFIG.SERVER']}, port: {$GLOBALS['CONFIG.PORT']}\n"); - - /* socket.php */ - if (tryToConnect()) { - if (Identify()) { - /* main loop */ - SocketLoop(); - } - } + changeDefaultOwnerPwd(); + } + + /* Load plugins - plugins.php */ + LoadPlugins(); + + /* Time to connect */ + cliLog("[bot] Connecting to: ".loadValueFromConfigFile('SERVER', 'server').", port: ".loadValueFromConfigFile('SERVER', 'port')."\n"); + + /* socket.php */ + if (tryToConnect()) { + if (Identify()) { + /* main loop */ + SocketLoop(); + } + } } diff --git a/src/timers.php b/src/timers.php index dba0112..f0d3cb3 100644 --- a/src/timers.php +++ b/src/timers.php @@ -107,9 +107,9 @@ function StartTimers() function every_1_minute() { /* check if bot can change nick to original */ - if (empty($GLOBALS['stop'])) { - if ($GLOBALS['CONFIG.KEEP.NICK'] == 'yes' && isset($GLOBALS['I_USE_RND_NICKNAME'])) { - toServer("ISON :{$GLOBALS['CONFIG.NICKNAME']}"); + if (!getPause()) { + if (loadValueFromConfigFile('AUTOMATIC', 'keep.nick') == 'yes' && isset($GLOBALS['I_USE_RND_NICKNAME'])) { + toServer("ISON :".loadValueFromConfigFile('BOT', 'nickname')); } } }