/** * Starts the bot. * * @return void */ public function start() { set_error_handler(function ($errno, $errstr) { if (!(error_reporting() & $errno)) { return; } echo "[Error] {$errno} {$errstr}\r\n"; throw new \Exception($errstr, $errno); }, E_ALL); foreach ($this->commands as $command => $data) { $this->websocket->on(Event::MESSAGE_CREATE, function ($message, $discord, $new) use($command, $data) { $content = explode(' ', $message->content); $config = Config::getConfig($this->configfile); if ($content[0] == $config['prefix'] . $command) { Arr::forget($content, 0); $user_perms = @$config['perms']['perms'][$message->author->id]; if (empty($user_perms)) { $user_perms = $config['perms']['default']; } if ($user_perms >= $data['perms']) { try { $data['class']::handleMessage($message, $content, $discord, $config, $this); } catch (\Exception $e) { $message->reply("There was an error running the command. `{$e->getMessage()}`"); } } else { $message->reply('You do not have permission to do this!'); echo "[Auth] User {$message->author->username} blocked from running {$config['prefix']}{$command}, <@{$message->author->id}>\r\n"; } } }); } $this->websocket->on('ready', function ($discord) { $discord->updatePresence($this->websocket, 'DiscordPHP ' . Discord::VERSION, false); }); $this->websocket->on('error', function ($error, $ws) { echo "[Error] {$error}\r\n"; }); $this->websocket->on('close', function () { echo "[Close] WebSocket was closed.\r\n"; die; }); $this->websocket->run(); }
/** * Starts the bot. * * @return void */ public function start() { // set_error_handler(function ($errno, $errstr) { // if (!(error_reporting() & $errno)) { // return; // } // echo "[Error] {$errno} {$errstr}\r\n"; // throw new \Exception($errstr, $errno); // }, E_ALL); $this->websocket->on(Event::MESSAGE_CREATE, function ($message, $discord, $new) { $config = Config::getConfig($this->configfile); if (substr($message->content, 0, strlen($config['prefix'])) == $config['prefix']) { foreach ($this->commands as $command => $data) { $parts = []; $content = explode(' ', $message->content); foreach ($content as $index => $c) { foreach (explode("\n", $c) as $p) { $parts[] = $p; } } $content = $parts; if ($content[0] == $config['prefix'] . $command) { array_shift($content); $user_perms = @$config['perms']['perms'][$message->author->id]; if (empty($user_perms)) { $user_perms = $config['perms']['default']; } if ($user_perms >= $data['perms']) { try { $data['class']::handleMessage($message, $content, $new, $config, $this); $this->log->addInfo("{$message->author->username}#{$message->author->discriminator} ({$message->author}) ran command {$config['prefix']}{$command}", $content); } catch (\Throwable $e) { try { $this->log->addError("Error running the command {$config['prefix']}{$command}", ['message' => $e->getMessage()]); $message->reply("There was an error running the command. `{$e->getMessage()}`"); } catch (\Throwable $e2) { } } } else { try { $message->reply('You do not have permission to do this!'); } catch (\Throwable $e2) { } $this->log->addWarning("{$message->author->username}#{$message->author->discriminator} ({$message->author}) attempted to run command {$config['prefix']}{$command}", $content); } } } } }); $this->websocket->on(Event::MESSAGE_CREATE, function ($message, $discord, $new) { $triggers = ['bless up', ':pray:', '🙏']; if (Str::contains(strtolower($message->content), $triggers) && $message->author->id != $discord->id) { $config = Config::getConfig($this->configfile); $content = explode(' ', $message->content); Arr::forget($content, 0); Khaled::handleMessage($message, $content, $new, $config, $this); } }); $this->websocket->on(Event::MESSAGE_CREATE, function ($message, $discord, $new) { if ($message->author->id == '81726071573061632' && strtolower($message->content) == 'we dem') { $message->channel->sendMessage('BOIZ'); } }); $this->websocket->on('ready', function ($discord) { $this->log->addInfo('WebSocket is ready.'); $discord->updatePresence($this->websocket, 'DiscordPHP ' . Discord::VERSION, false); }); $this->websocket->on('error', function ($error, $ws) { $this->log->addError("WebSocket encountered an error", [$error->getMessage()]); }); // $this->websocket->on('heartbeat', function ($epoch) { // echo "Heartbeat at {$epoch}\r\n"; // }); $this->websocket->on('close', function ($op, $reason) { $this->log->addWarning("WebSocket closed.", ['code' => $op, 'reason' => $reason]); }); $this->websocket->on('reconnecting', function () { $this->log->addInfo('WebSocket is reconnecting...'); }); $this->websocket->on('reconnected', function () { $this->log->addInfo('WebSocket has reconnected.'); }); $config = Config::getConfig($this->configfile); if (isset($config['cache']) && $config['cache'] == 'redis') { Cache::setCache(new RedisCacheDriver('localhost')); } if (isset($config['carbon_bot']) && $config['carbon_bot']['enabled']) { $guzzle = new Client(['http_errors' => false]); $body = ['key' => $config['carbon_bot']['key']]; $this->log->addInfo('Enabling Carbon server count updates...'); $carbonHeartbeat = function () use($guzzle, &$body) { $body['servercount'] = $this->discord->guilds->count(); $this->log->addDebug('Sending Carbon server count update...'); $request = new Request('POST', 'https://www.carbonitex.net/discord/data/botdata.php', ['Content-Type' => 'application/json'], json_encode($body)); $response = $guzzle->send($request); if ($response->getStatusCode() !== 200) { $this->log->addWarning('Carbon server count update failed.', ['status' => $response->getStatusCode(), 'reason' => $response->getReasonPhrase()]); } else { $this->log->addDebug('Sent Carbon server count update successfully.'); } }; $carbonHeartbeat(); $this->websocket->loop->addPeriodicTimer(60, $carbonHeartbeat); } $this->websocket->run(); }
/** * */ public function run() { // Reap the threads! $this->websocket->loop->addPeriodicTimer(600, function () { $this->log->addInfo('Restarting the threading pool, to clear out old threads..'); // Shutdown the pool $this->pool->shutdown(); $this->timers->shutdown(); // Startup the pool again $this->pool = new \Pool(count($this->onMessage), \Worker::class); $this->timers = new \Pool(count($this->onTimer), \Worker::class); }); // Handle the onReady event, and setup some timers and so forth $this->websocket->on('ready', function (Discord $discord) { $this->log->addInfo('Websocket connected..'); // Update our presence status $game = new Game(array('name' => $this->globalConfig->get('presence', 'bot', "table flippin'"), 'url' => null, 'type' => null), true); $this->websocket->updatePresence($game, false); // Count the amount of people we are available to.. /** @var Guild $guild */ foreach ($this->discord->getClient()->getGuildsAttribute()->all() as $guild) { $this->extras['memberCount'] += $guild->member_count; $this->extras['guildCount']++; $this->extras['guild']['memberCount']["id{$guild->id}"] = $guild->member_count; $this->extras['onMessagePlugins'] = $this->onMessage; $this->extras['onVoicePlugins'] = $this->onVoice; } $this->log->addInfo("Member count, currently available to: {$this->extras['memberCount']} people"); // Setup the timers for the timer plugins foreach ($this->onTimer as $command => $data) { $this->websocket->loop->addPeriodicTimer($data['timer'], function () use($data, $discord) { try { $plugin = new $data['class']($discord, $this->log, $this->globalConfig, $this->db, $this->curl, $this->settings, $this->permissions, $this->container->get('serverConfig'), $this->users, $this->extras); $this->timers->submit($plugin); } catch (\Exception $e) { $this->log->addError("Error running the periodic timer: {$e->getMessage()}"); } }); } // Issue periodically recounting and other things (Needed because of pthreads not putting the entire context into children - leading to some weirdness in some plugins) $this->websocket->loop->addPeriodicTimer(600, function () { $this->extras['memberCount'] = 0; $this->extras['guildCount'] = 0; /** @var Guild $guild */ foreach ($this->discord->getClient()->getGuildsAttribute()->all() as $guild) { $this->extras['memberCount'] += $guild->member_count; $this->extras['guildCount']++; $this->extras['guild']['memberCount']["id{$guild->id}"] = $guild->member_count; $this->extras['onMessagePlugins'] = $this->onMessage; $this->extras['onVoicePlugins'] = $this->onVoice; } // Output periodic information while doing the recounting stuff $this->log->addInfo('Currently running audio streams: ' . count($this->audioStreams)); $this->log->addInfo("Member recount, currently available to: {$this->extras['memberCount']} people"); }); // @todo run a timer to check if there are any active voice sessions - and if there are, if there are any people in those voice sessions // If not, stop the session and leave the channel (To save some bandwidth) }); $this->websocket->on('error', function ($error, $websocket) { $this->log->addError('An error occurred on the websocket', [$error->getMessage()]); die(1); }); $this->websocket->on('close', function ($opCode, $reason) { $this->log->addWarning('Websocket got closed', ['code' => $opCode, 'reason' => $reason]); die(1); }); $this->websocket->on('reconnecting', function () { $this->log->addInfo('Websocket is reconnecting..'); }); $this->websocket->on('reconnected', function () { $this->log->addInfo('Websocket was reconnected..'); }); // Handle incoming message logging $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { $this->log->addInfo("Message from {$message->author->username}", [$message->content]); // Don't update data for ourselves.. if ($message->author->id !== $discord->getClient()->id) { $this->users->set($message->author->id, $message->author->username, 'online', null, date('Y-m-d H:i:s'), date('Y-m-d H:i:s'), $message->content); } // @todo Create text logs }); // Handle plugin running $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { $guildID = $message->getChannelAttribute()->guild_id; // Get server config $config = $this->settings->get($guildID); // Is the person admin? $userDiscordID = $message->author->id; foreach ($this->globalConfig->get('admins', 'permissions') as $admins) { $message->isAdmin = $admins === $userDiscordID; } // Define the prefix if it isn't already set.. @($config->prefix = $config->prefix ?? $this->globalConfig->get('prefix', 'bot')); // Check if the user requested an onMessage plugin if (substr($message->content, 0, strlen($config->prefix)) === $config->prefix) { $content = explode(' ', $message->content); foreach ($this->onMessage as $command => $data) { $parts = []; foreach ($content as $index => $c) { foreach (explode("\n", $c) as $p) { $parts[] = $p; } } if ($parts[0] === $config->prefix . $command) { // If they are listed under the admins array in the bot config, they're the super admins if (in_array($message->author->id, $this->globalConfig->get('admins', 'permissions'))) { $userPerms = 3; } elseif (null !== $message->getChannelAttribute()->getGuildAttribute()->owner_id && $message->author->id === $message->getChannelAttribute()->getGuildAttribute()->owner_id) { $userPerms = 2; } else { $userPerms = 1; } if ($userPerms >= $data['permissions']) { try { $message->getChannelAttribute()->broadcastTyping(); if ($data['class'] === "\\Sovereign\\Plugins\\onMessage\\auth") { /** @var \Threaded $plugin */ $plugin = new $data['class']($message, $discord, $config, $this->log, $this->globalConfig, $this->db, $this->curl, $this->settings, $this->permissions, $this->container->get('serverConfig'), $this->users, $this->extras); $plugin->run(); } else { /** @var \Threaded $plugin */ $plugin = new $data['class']($message, $discord, $config, $this->log, $this->globalConfig, $this->db, $this->curl, $this->settings, $this->permissions, $this->container->get('serverConfig'), $this->users, $this->extras); $this->pool->submit($plugin); } $this->log->addInfo("{$message->author->username}#{$message->author->discriminator} ({$message->author}) ran command {$config->prefix}{$command}", $content); } catch (\Exception $e) { $this->log->addError("Error running command {$config->prefix}{$command}. Command run by {$message->author->username} in {$message->getChannelAttribute()->name}. Error: {$e->getMessage()}"); $message->reply("**Error:** There was a problem running the command: {$e->getMessage()}"); } } } } } }); // Handle joining a voice channel, and playing.. stuff.... $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { // Get the guildID $guildID = $message->getChannelAttribute()->guild_id; // Get this guilds settings $config = $this->settings->get($guildID); // Get the prefix for this guild @($config->prefix = $config->prefix ?? $this->globalConfig->get('prefix', 'bot')); if (substr($message->content, 0, strlen($config->prefix)) === $config->prefix) { $content = explode(' ', $message->content); foreach ($this->onVoice as $command => $data) { $parts = []; foreach ($content as $index => $c) { foreach (explode("\n", $c) as $p) { $parts[] = $p; } } if ($parts[0] === $config->prefix . $command) { try { $voiceChannels = $message->getFullChannelAttribute()->getGuildAttribute()->channels->getAll('type', 'voice'); foreach ($voiceChannels as $channel) { if (!empty($channel->members[$message->author->id])) { $voice = new $data['class'](); $voice->run($message, $discord, $this->websocket, $this->log, $this->audioStreams, $channel, $this->curl); } } } catch (\Exception $e) { $this->log->addError("Error running voice command {$config->prefix}{$command}. Command run by {$message->author->username} in {$message->getChannelAttribute()->name}. Error: {$e->getMessage()}"); $message->reply("**Error:** There was a problem running the command: {$e->getMessage()}"); } } } } }); // Handle if it's a message for the bot (CleverBot invocation) $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { // If we got highlighted we should probably answer back if (stristr($message->content, $discord->getClient()->id)) { try { $this->pool->submit(new cleverBotMessage($message, $discord, $this->log, $this->globalConfig, $this->db, $this->curl, $this->settings, $this->permissions, $this->container->get('serverConfig'), $this->users)); } catch (\Exception $e) { $message->reply("**Error:** There was an error with CleverBot: {$e->getMessage()}"); } } }); // Handle presence updates $this->websocket->on(Event::PRESENCE_UPDATE, function (PresenceUpdate $presenceUpdate) { if ($presenceUpdate->user->id && $presenceUpdate->user->username) { try { $this->log->addInfo("Updating presence info for {$presenceUpdate->user->username}"); $game = $presenceUpdate->getGameAttribute()->name ?? null; $this->users->set($presenceUpdate->user->id, $presenceUpdate->user->username, $presenceUpdate->status, $game, date('Y-m-d H:i:s'), null, null); } catch (\Exception $e) { $this->log->addError("Error: {$e->getMessage()}"); } } }); // Create a new cleverbot \nick\ for this new guild $this->websocket->on(Event::GUILD_CREATE, function (Guild $guild) { $cleverBotExists = $this->db->queryField("SELECT serverID FROM cleverbot WHERE serverID = :serverID", "serverID", array(":serverID" => $guild->id)); $guildExists = $this->db->queryField("SELECT guildID FROM guilds WHERE guildID = :serverID", "guildID", array(":serverID" => $guild->id)); // Only create a new server nick if the cleverbot instance doesn't exist.. (Hopefully cleverbot.io is done deleting them at random) if (!isset($cleverBotExists)) { $this->log->addInfo("Setting up Cleverbot for {$guild->name}"); $serverID = $guild->id; $result = $this->curl->post('https://cleverbot.io/1.0/create', ['user' => $this->globalConfig->get('user', 'cleverbot'), 'key' => $this->globalConfig->get('key', 'cleverbot')]); if ($result) { $result = @json_decode($result); $nick = $result->nick ?? false; if ($nick) { $this->db->execute('INSERT INTO cleverbot (serverID, nick) VALUES (:serverID, :nick) ON DUPLICATE KEY UPDATE nick = :nick', [':serverID' => $serverID, ':nick' => $nick]); } } } if (!isset($guildExists)) { $this->db->execute("INSERT IGNORE INTO guilds (guildID) VALUES (:guildID)", array(":guildID" => $guild->id)); // Send a hello message to the channel (Only if it's new!) //$message = "Hello, i was invited here by someone with admin permissions, i have quite a few features that you can discover by doing %help\n"; //$message .= "I am sorry if i am triggering other bots aswell, you can change my trigger with %config setTrigger newTrigger (Example: %config setTrigger *)\n"; //$message .= "If you for some reason don't want me here after all, just kick me ;)"; // Get the first channel in the list (usually the default channel) //$channel = $guild->channels->first(); //$channel->sendMessage($message); } }); // Run the websocket, and in turn, the bot! $this->websocket->run(); }