This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Sovereign; |
||
3 | |||
4 | |||
5 | use Discord\Cache\Cache; |
||
6 | use Discord\Cache\Drivers\ArrayCacheDriver; |
||
7 | use Discord\Discord; |
||
8 | use Discord\Parts\Channel\Message; |
||
9 | use Discord\Parts\Guild\Guild; |
||
10 | use Discord\Parts\User\Game; |
||
11 | use Discord\Parts\WebSockets\PresenceUpdate; |
||
12 | use Discord\WebSockets\Event; |
||
13 | use Discord\WebSockets\WebSocket; |
||
14 | use Monolog\Logger; |
||
15 | use League\Container\Container; |
||
16 | use Sovereign\Lib\Config as globalConfig; |
||
17 | use Sovereign\Lib\cURL; |
||
18 | use Sovereign\Lib\Db; |
||
19 | use Sovereign\Lib\Permissions; |
||
20 | use Sovereign\Lib\Settings; |
||
21 | use Sovereign\Lib\Users; |
||
22 | use Sovereign\Plugins\onMessage\cleverBotMessage; |
||
23 | |||
24 | /** |
||
25 | * Class Sovereign |
||
26 | * @package Sovereign |
||
27 | */ |
||
28 | class Sovereign |
||
29 | { |
||
30 | /** |
||
31 | * @var WebSocket |
||
32 | */ |
||
33 | public $websocket; |
||
34 | /** |
||
35 | * @var Discord |
||
36 | */ |
||
37 | protected $discord; |
||
38 | /** |
||
39 | * @var Container |
||
40 | */ |
||
41 | protected $container; |
||
42 | /** |
||
43 | * @var Logger |
||
44 | */ |
||
45 | protected $log; |
||
46 | /** |
||
47 | * @var globalConfig |
||
48 | */ |
||
49 | protected $globalConfig; |
||
50 | /** |
||
51 | * @var Db |
||
52 | */ |
||
53 | protected $db; |
||
54 | /** |
||
55 | * @var cURL |
||
56 | */ |
||
57 | protected $curl; |
||
58 | /** |
||
59 | * @var Settings |
||
60 | */ |
||
61 | protected $settings; |
||
62 | /** |
||
63 | * @var Permissions |
||
64 | */ |
||
65 | protected $permissions; |
||
66 | /** |
||
67 | * @var Users |
||
68 | */ |
||
69 | protected $users; |
||
70 | /** |
||
71 | * @var array |
||
72 | */ |
||
73 | private $onMessage = []; |
||
74 | /** |
||
75 | * @var array |
||
76 | */ |
||
77 | private $onVoice = []; |
||
78 | /** |
||
79 | * @var array |
||
80 | */ |
||
81 | private $onTimer = []; |
||
82 | /** |
||
83 | * @var \Pool |
||
84 | */ |
||
85 | private $pool; |
||
86 | /** |
||
87 | * @var \Pool |
||
88 | */ |
||
89 | private $timers; |
||
90 | /** |
||
91 | * @var array |
||
92 | */ |
||
93 | private $audioStreams; |
||
94 | /** |
||
95 | * @var array |
||
96 | */ |
||
97 | private $extras = []; |
||
98 | |||
99 | /** |
||
100 | * Sovereign constructor. |
||
101 | * @param Container $container |
||
102 | */ |
||
103 | public function __construct(Container $container) |
||
104 | { |
||
105 | $this->container = $container; |
||
106 | $this->log = $container->get('log'); |
||
107 | $this->globalConfig = $container->get('config'); |
||
108 | $this->db = $container->get('db'); |
||
109 | $this->curl = $container->get('curl'); |
||
110 | $this->settings = $container->get('settings'); |
||
111 | $this->permissions = $container->get('permissions'); |
||
112 | $this->users = $container->get('users'); |
||
113 | $this->extras['startTime'] = time(); |
||
114 | $this->extras['memberCount'] = 0; |
||
115 | $this->extras['guildCount'] = 0; |
||
116 | $this->pool = new \Pool(count($this->onMessage), \Worker::class); |
||
117 | $this->timers = new \Pool(count($this->onTimer), \Worker::class); |
||
118 | |||
119 | // Init Discord and Websocket |
||
120 | $this->log->addInfo('Initializing Discord and Websocket connections..'); |
||
121 | $this->discord = Discord::createWithBotToken($this->globalConfig->get('token', 'bot')); |
||
122 | Cache::setCache(new ArrayCacheDriver()); |
||
123 | $this->websocket = new WebSocket($this->discord); |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * @param $type |
||
128 | * @param $command |
||
129 | * @param $class |
||
130 | * @param $perms |
||
131 | * @param $description |
||
132 | * @param $usage |
||
133 | * @param $timer |
||
134 | */ |
||
135 | public function addPlugin($type, $command, $class, $perms, $description, $usage, $timer) |
||
136 | { |
||
137 | $this->log->addInfo("Adding plugin: {$command}"); |
||
138 | $this->$type[$command] = [ |
||
139 | 'permissions' => $perms, |
||
140 | 'class' => $class, |
||
141 | 'description' => $description, |
||
142 | 'usage' => $usage, |
||
143 | 'timer' => $timer |
||
144 | ]; |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * |
||
149 | */ |
||
150 | public function run() |
||
151 | { |
||
152 | // Reap the threads! |
||
153 | $this->websocket->loop->addPeriodicTimer(600, function () { |
||
154 | $this->log->addInfo('Restarting the threading pool, to clear out old threads..'); |
||
155 | |||
156 | // Shutdown the pool |
||
157 | $this->pool->shutdown(); |
||
158 | $this->timers->shutdown(); |
||
159 | |||
160 | // Startup the pool again |
||
161 | $this->pool = new \Pool(count($this->onMessage), \Worker::class); |
||
162 | $this->timers = new \Pool(count($this->onTimer), \Worker::class); |
||
163 | }); |
||
164 | |||
165 | // Handle the onReady event, and setup some timers and so forth |
||
166 | $this->websocket->on('ready', function (Discord $discord) { |
||
167 | $this->log->addInfo('Websocket connected..'); |
||
168 | |||
169 | // Update our presence status |
||
170 | $game = new Game(array('name' => $this->globalConfig->get('presence', 'bot', "table flippin'"), 'url' => null, 'type' => null), true); |
||
171 | $this->websocket->updatePresence($game, false); |
||
172 | |||
173 | // Count the amount of people we are available to.. |
||
174 | /** @var Guild $guild */ |
||
175 | View Code Duplication | foreach ($this->discord->getClient()->getGuildsAttribute()->all() as $guild) { |
|
176 | $this->extras['memberCount'] += $guild->member_count; |
||
177 | $this->extras['guildCount']++; |
||
178 | $this->extras['guild']['memberCount']["id{$guild->id}"] = $guild->member_count; |
||
179 | $this->extras['onMessagePlugins'] = $this->onMessage; |
||
180 | $this->extras['onVoicePlugins'] = $this->onVoice; |
||
181 | } |
||
182 | |||
183 | $this->log->addInfo("Member count, currently available to: {$this->extras['memberCount']} people"); |
||
184 | |||
185 | // Setup the timers for the timer plugins |
||
186 | foreach ($this->onTimer as $command => $data) { |
||
187 | $this->websocket->loop->addPeriodicTimer($data['timer'], function () use ($data, $discord) { |
||
188 | try { |
||
189 | $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); |
||
190 | $this->timers->submit($plugin); |
||
191 | } catch (\Exception $e) { |
||
192 | $this->log->addError("Error running the periodic timer: {$e->getMessage()}"); |
||
193 | } |
||
194 | }); |
||
195 | } |
||
196 | |||
197 | // Issue periodically recounting and other things (Needed because of pthreads not putting the entire context into children - leading to some weirdness in some plugins) |
||
198 | $this->websocket->loop->addPeriodicTimer(600, function () { |
||
199 | $this->extras['memberCount'] = 0; |
||
200 | $this->extras['guildCount'] = 0; |
||
201 | /** @var Guild $guild */ |
||
202 | View Code Duplication | foreach ($this->discord->getClient()->getGuildsAttribute()->all() as $guild) { |
|
203 | $this->extras['memberCount'] += $guild->member_count; |
||
204 | $this->extras['guildCount']++; |
||
205 | $this->extras['guild']['memberCount']["id{$guild->id}"] = $guild->member_count; |
||
206 | $this->extras['onMessagePlugins'] = $this->onMessage; |
||
207 | $this->extras['onVoicePlugins'] = $this->onVoice; |
||
208 | } |
||
209 | |||
210 | // Output periodic information while doing the recounting stuff |
||
211 | $this->log->addInfo('Currently running audio streams: ' . count($this->audioStreams)); |
||
212 | $this->log->addInfo("Member recount, currently available to: {$this->extras['memberCount']} people"); |
||
213 | }); |
||
214 | |||
215 | // @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 |
||
216 | // If not, stop the session and leave the channel (To save some bandwidth) |
||
217 | }); |
||
218 | |||
219 | $this->websocket->on('error', function ($error, $websocket) { |
||
220 | $this->log->addError('An error occurred on the websocket', [$error->getMessage()]); |
||
221 | die(1); |
||
222 | }); |
||
223 | |||
224 | $this->websocket->on('close', function ($opCode, $reason) { |
||
225 | $this->log->addWarning('Websocket got closed', ['code' => $opCode, 'reason' => $reason]); |
||
226 | die(1); |
||
227 | }); |
||
228 | |||
229 | $this->websocket->on('reconnecting', function () { |
||
230 | $this->log->addInfo('Websocket is reconnecting..'); |
||
231 | }); |
||
232 | |||
233 | $this->websocket->on('reconnected', function () { |
||
234 | $this->log->addInfo('Websocket was reconnected..'); |
||
235 | }); |
||
236 | |||
237 | // Handle incoming message logging |
||
238 | $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { |
||
239 | $this->log->addInfo("Message from {$message->author->username}", [$message->content]); |
||
240 | |||
241 | // Don't update data for ourselves.. |
||
242 | if ($message->author->id !== $discord->getClient()->id) { |
||
243 | $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); |
||
244 | } |
||
245 | |||
246 | // @todo Create text logs |
||
247 | }); |
||
248 | |||
249 | // Handle plugin running |
||
250 | $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { |
||
251 | $guildID = $message->getChannelAttribute()->guild_id; |
||
252 | |||
253 | // Get server config |
||
254 | $config = $this->settings->get($guildID); |
||
255 | |||
256 | // Is the person admin? |
||
257 | $userDiscordID = $message->author->id; |
||
258 | foreach ($this->globalConfig->get('admins', 'permissions') as $admins) { |
||
259 | $message->isAdmin = $admins === $userDiscordID; |
||
260 | } |
||
261 | |||
262 | // Define the prefix if it isn't already set.. |
||
263 | @$config->prefix = $config->prefix ?? $this->globalConfig->get('prefix', 'bot'); |
||
0 ignored issues
–
show
|
|||
264 | |||
265 | // Check if the user requested an onMessage plugin |
||
266 | if (substr($message->content, 0, strlen($config->prefix)) === $config->prefix) { |
||
267 | $content = explode(' ', $message->content); |
||
268 | foreach ($this->onMessage as $command => $data) { |
||
269 | $parts = []; |
||
270 | foreach ($content as $index => $c) |
||
271 | foreach (explode("\n", $c) as $p) |
||
272 | $parts[] = $p; |
||
273 | |||
274 | if ($parts[0] === $config->prefix . $command) { |
||
275 | // If they are listed under the admins array in the bot config, they're the super admins |
||
276 | if (in_array($message->author->id, $this->globalConfig->get('admins', 'permissions'))) |
||
277 | $userPerms = 3; |
||
278 | // If they are guild owner, they're automatically getting permission level 2 |
||
279 | elseif (null !== $message->getChannelAttribute()->getGuildAttribute()->owner_id && ($message->author->id === $message->getChannelAttribute()->getGuildAttribute()->owner_id)) |
||
280 | $userPerms = 2; |
||
281 | // Everyone else are just users |
||
282 | else |
||
283 | $userPerms = 1; |
||
284 | |||
285 | if ($userPerms >= $data['permissions']) { |
||
286 | try { |
||
287 | $message->getChannelAttribute()->broadcastTyping(); |
||
288 | if ($data['class'] === "\\Sovereign\\Plugins\\onMessage\\auth") { |
||
289 | /** @var \Threaded $plugin */ |
||
290 | $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); |
||
291 | $plugin->run(); |
||
292 | } else { |
||
293 | /** @var \Threaded $plugin */ |
||
294 | $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); |
||
295 | $this->pool->submit($plugin); |
||
296 | } |
||
297 | $this->log->addInfo("{$message->author->username}#{$message->author->discriminator} ({$message->author}) ran command {$config->prefix}{$command}", $content); |
||
298 | } catch (\Exception $e) { |
||
299 | $this->log->addError("Error running command {$config->prefix}{$command}. Command run by {$message->author->username} in {$message->getChannelAttribute()->name}. Error: {$e->getMessage()}"); |
||
300 | $message->reply("**Error:** There was a problem running the command: {$e->getMessage()}"); |
||
301 | } |
||
302 | } |
||
303 | } |
||
304 | } |
||
305 | } |
||
306 | }); |
||
307 | |||
308 | // Handle joining a voice channel, and playing.. stuff.... |
||
309 | $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { |
||
310 | // Get the guildID |
||
311 | $guildID = $message->getChannelAttribute()->guild_id; |
||
312 | |||
313 | // Get this guilds settings |
||
314 | $config = $this->settings->get($guildID); |
||
315 | |||
316 | // Get the prefix for this guild |
||
317 | @$config->prefix = $config->prefix ?? $this->globalConfig->get('prefix', 'bot'); |
||
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||
318 | |||
319 | if (substr($message->content, 0, strlen($config->prefix)) === $config->prefix) { |
||
320 | $content = explode(' ', $message->content); |
||
321 | foreach ($this->onVoice as $command => $data) { |
||
322 | $parts = []; |
||
323 | foreach ($content as $index => $c) { |
||
324 | foreach (explode("\n", $c) as $p) |
||
325 | $parts[] = $p; |
||
326 | } |
||
327 | |||
328 | if ($parts[0] === $config->prefix . $command) { |
||
329 | try { |
||
330 | $voiceChannels = $message->getFullChannelAttribute()->getGuildAttribute()->channels->getAll('type', 'voice'); |
||
331 | foreach ($voiceChannels as $channel) { |
||
332 | if (!empty($channel->members[$message->author->id])) { |
||
333 | $voice = new $data['class'](); |
||
334 | $voice->run($message, $discord, $this->websocket, $this->log, $this->audioStreams, $channel, $this->curl); |
||
335 | } |
||
336 | } |
||
337 | } catch (\Exception $e) { |
||
338 | $this->log->addError("Error running voice command {$config->prefix}{$command}. Command run by {$message->author->username} in {$message->getChannelAttribute()->name}. Error: {$e->getMessage()}"); |
||
339 | $message->reply("**Error:** There was a problem running the command: {$e->getMessage()}"); |
||
340 | } |
||
341 | } |
||
342 | } |
||
343 | } |
||
344 | }); |
||
345 | |||
346 | // Handle if it's a message for the bot (CleverBot invocation) |
||
347 | $this->websocket->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) { |
||
348 | // If we got highlighted we should probably answer back |
||
349 | if (stristr($message->content, $discord->getClient()->id)) { |
||
350 | try { |
||
351 | $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)); |
||
352 | } catch (\Exception $e) { |
||
353 | $message->reply("**Error:** There was an error with CleverBot: {$e->getMessage()}"); |
||
354 | } |
||
355 | } |
||
356 | }); |
||
357 | |||
358 | // Handle presence updates |
||
359 | $this->websocket->on(Event::PRESENCE_UPDATE, function (PresenceUpdate $presenceUpdate) { |
||
360 | if ($presenceUpdate->user->id && $presenceUpdate->user->username) { |
||
361 | try { |
||
362 | $this->log->addInfo("Updating presence info for {$presenceUpdate->user->username}"); |
||
363 | $game = $presenceUpdate->getGameAttribute()->name ?? null; |
||
364 | $this->users->set($presenceUpdate->user->id, $presenceUpdate->user->username, $presenceUpdate->status, $game, date('Y-m-d H:i:s'), null, null); |
||
365 | } catch (\Exception $e) { |
||
366 | $this->log->addError("Error: {$e->getMessage()}"); |
||
367 | } |
||
368 | } |
||
369 | }); |
||
370 | |||
371 | // Create a new cleverbot \nick\ for this new guild |
||
372 | $this->websocket->on(Event::GUILD_CREATE, function (Guild $guild) { |
||
373 | $cleverBotExists = $this->db->queryField("SELECT serverID FROM cleverbot WHERE serverID = :serverID", "serverID", array(":serverID" => $guild->id)); |
||
374 | $guildExists = $this->db->queryField("SELECT guildID FROM guilds WHERE guildID = :serverID", "guildID", array(":serverID" => $guild->id)); |
||
375 | |||
376 | // Only create a new server nick if the cleverbot instance doesn't exist.. (Hopefully cleverbot.io is done deleting them at random) |
||
377 | if(!isset($cleverBotExists)) { |
||
378 | $this->log->addInfo("Setting up Cleverbot for {$guild->name}"); |
||
379 | $serverID = $guild->id; |
||
380 | $result = $this->curl->post('https://cleverbot.io/1.0/create', ['user' => $this->globalConfig->get('user', 'cleverbot'), 'key' => $this->globalConfig->get('key', 'cleverbot')]); |
||
381 | |||
382 | if ($result) { |
||
383 | $result = @json_decode($result); |
||
384 | $nick = $result->nick ?? false; |
||
385 | |||
386 | if ($nick) { |
||
387 | $this->db->execute('INSERT INTO cleverbot (serverID, nick) VALUES (:serverID, :nick) ON DUPLICATE KEY UPDATE nick = :nick', [':serverID' => $serverID, ':nick' => $nick]); |
||
388 | } |
||
389 | } |
||
390 | } |
||
391 | |||
392 | if(!isset($guildExists)) { |
||
393 | $this->db->execute("INSERT IGNORE INTO guilds (guildID) VALUES (:guildID)", array(":guildID" => $guild->id)); |
||
394 | |||
395 | // Send a hello message to the channel (Only if it's new!) |
||
396 | //$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"; |
||
397 | //$message .= "I am sorry if i am triggering other bots aswell, you can change my trigger with %config setTrigger newTrigger (Example: %config setTrigger *)\n"; |
||
398 | //$message .= "If you for some reason don't want me here after all, just kick me ;)"; |
||
399 | // Get the first channel in the list (usually the default channel) |
||
400 | //$channel = $guild->channels->first(); |
||
401 | //$channel->sendMessage($message); |
||
402 | } |
||
403 | }); |
||
404 | |||
405 | // Run the websocket, and in turn, the bot! |
||
406 | $this->websocket->run(); |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * Get the configuration/settings container and return it upstream to the calling code. |
||
411 | * |
||
412 | * @return globalConfig |
||
413 | */ |
||
414 | public function getGlobalConfig() { |
||
415 | return $this->globalConfig; |
||
416 | } |
||
417 | } |
||
418 |
If you suppress an error, we recommend checking for the error condition explicitly: