nopolabs /
yabot
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 | |||
| 3 | namespace Nopolabs\Yabot; |
||
| 4 | |||
| 5 | use DateTime; |
||
| 6 | use Exception; |
||
| 7 | use Nopolabs\Yabot\Helpers\ConfigTrait; |
||
| 8 | use Nopolabs\Yabot\Helpers\LogTrait; |
||
| 9 | use Nopolabs\Yabot\Helpers\LoopTrait; |
||
| 10 | use Nopolabs\Yabot\Helpers\SlackTrait; |
||
| 11 | use Nopolabs\Yabot\Message\MessageFactory; |
||
| 12 | use Nopolabs\Yabot\Plugin\PluginInterface; |
||
| 13 | use Nopolabs\Yabot\Plugin\PluginManager; |
||
| 14 | use Nopolabs\Yabot\Slack\Client; |
||
| 15 | use Psr\Log\LoggerInterface; |
||
| 16 | use React\EventLoop\LoopInterface; |
||
| 17 | use React\EventLoop\Timer\TimerInterface; |
||
| 18 | use React\Promise\Timer; |
||
| 19 | use Slack\Payload; |
||
| 20 | use Slack\User; |
||
| 21 | use Throwable; |
||
| 22 | |||
| 23 | class Yabot |
||
| 24 | { |
||
| 25 | use LogTrait; |
||
| 26 | use LoopTrait; |
||
| 27 | use SlackTrait; |
||
| 28 | use ConfigTrait; |
||
| 29 | |||
| 30 | /** @var MessageFactory */ |
||
| 31 | private $messageFactory; |
||
| 32 | |||
| 33 | /** @var PluginManager */ |
||
| 34 | private $pluginManager; |
||
| 35 | |||
| 36 | /** @var string */ |
||
| 37 | private $messageLog; |
||
| 38 | |||
| 39 | /** @var TimerInterface */ |
||
| 40 | private $monitor; |
||
| 41 | |||
| 42 | /** @var bool */ |
||
| 43 | private $pong; |
||
| 44 | |||
| 45 | public function __construct( |
||
| 46 | LoggerInterface $logger, |
||
| 47 | LoopInterface $eventLoop, |
||
| 48 | Client $slackClient, |
||
| 49 | MessageFactory $messageFactory, |
||
| 50 | PluginManager $pluginManager, |
||
| 51 | array $config = [] |
||
| 52 | ) { |
||
| 53 | $this->setLog($logger); |
||
| 54 | $this->setLoop($eventLoop); |
||
| 55 | $this->setSlack($slackClient); |
||
| 56 | $this->setConfig($config); |
||
| 57 | $this->messageFactory = $messageFactory; |
||
| 58 | $this->pluginManager = $pluginManager; |
||
| 59 | $this->messageLog = null; |
||
| 60 | } |
||
| 61 | |||
| 62 | public function getMessageLog() |
||
| 63 | { |
||
| 64 | return $this->messageLog; |
||
| 65 | } |
||
| 66 | |||
| 67 | public function setMessageLog(string $messageLog = null) |
||
| 68 | { |
||
| 69 | $this->messageLog = $messageLog ?? null; |
||
| 70 | } |
||
| 71 | |||
| 72 | public function init(array $plugins) |
||
| 73 | { |
||
| 74 | foreach ($plugins as $pluginId => $plugin) { |
||
| 75 | /** @var PluginInterface $plugin */ |
||
| 76 | |||
| 77 | $this->info("loading $pluginId"); |
||
| 78 | |||
| 79 | try { |
||
| 80 | $this->pluginManager->loadPlugin($pluginId, $plugin); |
||
| 81 | } catch (Exception $e) { |
||
| 82 | $this->warning("Unhandled Exception while loading $pluginId: ".$e->getMessage()); |
||
| 83 | $this->warning($e->getTraceAsString()); |
||
| 84 | } |
||
| 85 | } |
||
| 86 | } |
||
| 87 | |||
| 88 | public function run() |
||
| 89 | { |
||
| 90 | $this->getSlack()->init(); |
||
| 91 | |||
| 92 | $this->getLog()->info('Connecting...'); |
||
| 93 | |||
| 94 | $this->connect(); |
||
| 95 | |||
| 96 | $this->addMemoryReporting(); |
||
| 97 | |||
| 98 | $this->getLoop()->run(); |
||
| 99 | } |
||
| 100 | |||
| 101 | public function shutDown() |
||
| 102 | { |
||
| 103 | $this->getLog()->error('Shutting down...'); |
||
| 104 | |||
| 105 | $this->getSlack()->disconnect(); |
||
| 106 | $this->getLoop()->stop(); |
||
| 107 | } |
||
| 108 | |||
| 109 | public function reconnect() |
||
| 110 | { |
||
| 111 | $this->getLog()->error('Reconnecting...'); |
||
| 112 | |||
| 113 | if ($this->monitor) { |
||
| 114 | $this->loop->cancelTimer($this->monitor); |
||
| 115 | } |
||
| 116 | |||
| 117 | $this->getSlack()->reconnect()->then( |
||
| 118 | function () { |
||
| 119 | $this->getLog()->info('Reconnected'); |
||
| 120 | $this->monitor = $this->startConnectionMonitor(); |
||
| 121 | }, |
||
| 122 | function () { |
||
| 123 | $this->getLog()->error('Reconnect failed, shutting down.'); |
||
| 124 | $this->shutDown(); |
||
| 125 | } |
||
| 126 | ); |
||
| 127 | } |
||
| 128 | |||
| 129 | public function connected() |
||
| 130 | { |
||
| 131 | $slack = $this->getSlack(); |
||
| 132 | |||
| 133 | $slack->update(function(User $authedUser) { |
||
| 134 | $this->pluginManager->setAuthedUser($authedUser); |
||
| 135 | }); |
||
| 136 | |||
| 137 | $slack->onEvent('message', [$this, 'onMessage']); |
||
| 138 | $slack->onEvent('team_join', [$this, 'onTeamJoin']); |
||
| 139 | |||
| 140 | $this->monitor = $this->startConnectionMonitor(); |
||
| 141 | } |
||
| 142 | |||
| 143 | public function onMessage(Payload $payload) |
||
| 144 | { |
||
| 145 | $data = $payload->getData(); |
||
| 146 | |||
| 147 | $this->debug('Received message', $data); |
||
| 148 | |||
| 149 | try { |
||
| 150 | $this->logMessage($data); |
||
| 151 | $message = $this->messageFactory->create($data); |
||
| 152 | } catch (Throwable $throwable) { |
||
| 153 | $errmsg = $throwable->getMessage()."\n" |
||
| 154 | .$throwable->getTraceAsString()."\n" |
||
| 155 | ."Payload data: ".json_encode($data); |
||
| 156 | $this->warning($errmsg); |
||
| 157 | return; |
||
| 158 | } |
||
| 159 | |||
| 160 | if ($message->isSelf()) { |
||
| 161 | return; |
||
| 162 | } |
||
| 163 | |||
| 164 | $this->pluginManager->dispatchMessage($message); |
||
| 165 | } |
||
| 166 | |||
| 167 | public function onTeamJoin(Payload $payload) |
||
|
0 ignored issues
–
show
|
|||
| 168 | { |
||
| 169 | $this->getSlack()->updateUsers(); |
||
| 170 | } |
||
| 171 | |||
| 172 | public function getHelp() : string |
||
| 173 | { |
||
| 174 | return implode("\n", $this->pluginManager->getHelp()); |
||
| 175 | } |
||
| 176 | |||
| 177 | public function getStatus() : string |
||
| 178 | { |
||
| 179 | $statuses = $this->pluginManager->getStatuses(); |
||
| 180 | |||
| 181 | array_unshift($statuses, $this->getFormattedMemoryUsage()); |
||
| 182 | |||
| 183 | return implode("\n", $statuses); |
||
| 184 | } |
||
| 185 | |||
| 186 | protected function addMemoryReporting() |
||
| 187 | { |
||
| 188 | $now = new DateTime(); |
||
| 189 | $then = new DateTime('+1 hour'); |
||
| 190 | $then->setTime($then->format('H'), 0, 0); |
||
| 191 | $delay = $then->getTimestamp() - $now->getTimestamp(); |
||
| 192 | |||
| 193 | $this->addTimer($delay, function() { |
||
| 194 | $this->info($this->getFormattedMemoryUsage()); |
||
| 195 | $this->addPeriodicTimer(3600, function() { |
||
| 196 | $this->info($this->getFormattedMemoryUsage()); |
||
| 197 | }); |
||
| 198 | }); |
||
| 199 | } |
||
| 200 | |||
| 201 | protected function getFormattedMemoryUsage() : string |
||
| 202 | { |
||
| 203 | $memory = memory_get_usage() / 1024; |
||
| 204 | $formatted = number_format($memory, 3).'K'; |
||
| 205 | return "Current memory usage: {$formatted}"; |
||
| 206 | } |
||
| 207 | |||
| 208 | protected function logMessage($data) |
||
| 209 | { |
||
| 210 | if ($this->messageLog !== null) { |
||
| 211 | file_put_contents($this->messageLog, json_encode($data) . "\n", FILE_APPEND); |
||
| 212 | } |
||
| 213 | } |
||
| 214 | |||
| 215 | /** |
||
| 216 | * @return TimerInterface|null |
||
| 217 | */ |
||
| 218 | protected function startConnectionMonitor() |
||
| 219 | { |
||
| 220 | if ($interval = $this->get('connection_monitor.interval')) { |
||
| 221 | |||
| 222 | $this->getLog()->info("Monitoring websocket connection every $interval seconds."); |
||
| 223 | $this->notify("Monitoring websocket connection every $interval seconds."); |
||
| 224 | |||
| 225 | $this->ping(); |
||
| 226 | |||
| 227 | return $this->loop->addPeriodicTimer($interval, function () { |
||
| 228 | $this->checkPong(); |
||
| 229 | $this->ping(); |
||
| 230 | }); |
||
| 231 | } |
||
| 232 | } |
||
| 233 | |||
| 234 | protected function checkPong() |
||
| 235 | { |
||
| 236 | if (!$this->pong) { |
||
| 237 | $this->getLog()->error('No pong.'); |
||
| 238 | |||
| 239 | $failureStrategy = $this->get('connection_monitor.failure_strategy', 'reconnect'); |
||
| 240 | |||
| 241 | if ($failureStrategy === 'reconnect') { |
||
| 242 | $this->reconnect(); |
||
| 243 | return; |
||
| 244 | } |
||
| 245 | |||
| 246 | if ($failureStrategy !== 'shutdown') { |
||
| 247 | $this->getLog()->error("Unknown connection_monitor.failure_strategy '$failureStrategy'"); |
||
| 248 | } |
||
| 249 | |||
| 250 | $this->shutDown(); |
||
| 251 | } |
||
| 252 | } |
||
| 253 | |||
| 254 | protected function ping() |
||
| 255 | { |
||
| 256 | $this->pong = false; |
||
| 257 | |||
| 258 | $this->getSlack()->ping() |
||
| 259 | ->then( |
||
| 260 | function (Payload $payload) { |
||
| 261 | $this->getLog()->info($payload->toJson()); |
||
| 262 | $this->pong = true; |
||
| 263 | } |
||
| 264 | ); |
||
| 265 | } |
||
| 266 | |||
| 267 | protected function notify(string $message) |
||
| 268 | { |
||
| 269 | if ($user = $this->get('notify.user')) { |
||
| 270 | $this->getSlack()->directMessage($message, $user); |
||
| 271 | } |
||
| 272 | } |
||
| 273 | |||
| 274 | protected function connect() |
||
| 275 | { |
||
| 276 | Timer\timeout($this->getSlack()->connect(), 30, $this->getLoop()) |
||
| 277 | ->then(function () { |
||
| 278 | $this->getLog()->info('Connected.'); |
||
| 279 | $this->connected(); |
||
| 280 | }) |
||
| 281 | ->otherwise(function (Timer\TimeoutException $error) { |
||
| 282 | $this->getLog()->error($error->getMessage()); |
||
| 283 | $this->getLog()->error('Connection failed, shutting down.'); |
||
| 284 | $this->shutDown(); |
||
| 285 | }) |
||
| 286 | ->otherwise(function ($error) { |
||
|
0 ignored issues
–
show
|
|||
| 287 | $this->getLog()->error('Connection failed, shutting down.'); |
||
| 288 | $this->shutDown(); |
||
| 289 | }); |
||
| 290 | } |
||
| 291 | } |
||
| 292 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.