diff --git a/AlertHandler.php b/AlertHandler.php index 4c33995..e847642 100644 --- a/AlertHandler.php +++ b/AlertHandler.php @@ -11,7 +11,14 @@ class AlertHandler { - public static function sendAlertMail(Exception $e) { + /** + * @param Exception $e + * @throws \Twig_Error_Loader + * @throws \Twig_Error_Runtime + * @throws \Twig_Error_Syntax + */ + public static function sendAlertMail(Exception $e) + { $transport = (new Swift_SmtpTransport(Settings::$SMTP_SERVER, Settings::$SMTP_PORT, Settings::$SECURITY)) ->setUsername(Settings::$SMTP_USER) @@ -22,8 +29,8 @@ public static function sendAlertMail(Exception $e) { array( 'ssl' => array( 'allow_self_signed' => true, - 'verify_peer' => false - ) + 'verify_peer' => false, + ), ) ); } @@ -39,9 +46,17 @@ public static function sendAlertMail(Exception $e) { if ($mailer->send($message)) { echo 'Alert mail sent' . PHP_EOL; } else { - echo 'Failed to send alert mail' . PHP_EOL; + echo 'Failed to send alert mail' . PHP_EOL; } } + + /** + * @param Exception $e + * @return string + * @throws \Twig_Error_Loader + * @throws \Twig_Error_Runtime + * @throws \Twig_Error_Syntax + */ protected static function generateBody(Exception $e) { $loader = new \Twig_Loader_Filesystem('.'); diff --git a/Killbot.php b/Killbot.php index a085e91..4a09b71 100644 --- a/Killbot.php +++ b/Killbot.php @@ -4,13 +4,18 @@ use stdClass; -class Killbot { +class Killbot +{ protected $knownShips = array(); protected $knownCharacters = array(); protected $knownCorporations = array(); - public function run() { + /** + * Runs the bot + */ + public function run() + { $isFeedEmpty = false; $timeout = time() + Settings::$MAX_RUN_TIME; @@ -21,11 +26,12 @@ public function run() { } } - /* + /** * Fetch and process one kill from RedisQ * Return false if the queue is empty, true otherwise */ - private function processKill() { + private function processKill() + { $rawOutput = $this->curlRequest(Settings::$REDISQ_URL); @@ -35,128 +41,66 @@ private function processKill() { return false; } - $killmail = $data->{'package'}->{'killmail'}; - - // Only process kill that match settings entities - if ($this->isWatchedKill($killmail)) { - - if (Settings::$DEBUG) { - $this->storeKillJson($data->{'package'}->{'killID'}, $rawOutput); - } - - $zkb = $data->{'package'}->{'zkb'}; - $jsonKill = new stdClass(); - $jsonAttachments = new stdClass(); - - $killer = null; - $noVictim = false; - $attackerCount = count($killmail->{'attackers'}); - $killId = $killmail->{'killmail_id'}; - - // Looking up final blow - foreach ($killmail->{'attackers'} as $attacker) { - if ($attacker->{'final_blow'} == true) { - $killer = $attacker; - } - } - - // Handling NPC kills - if (!isset($killer->{'character_id'})) { - $killerName = $this->getShipName($killer->{'ship_type_id'}); - } else { - $killerName = $this->getCharacterName($killer->{'character_id'}); - } + foreach (Settings::$watchingConfigurations as $watchingConfiguration) { - // Handling case with no pilots victims - $victim = $killmail->{'victim'}; - if (!isset($victim->{'character_id'})) { - $noVictim = true; - $victimName = $this->getCorporationName($victim->{'corporation_id'}); - } else { - $victimName = $this->getCharacterName($victim->{'character_id'}); - } + $slackHook = $watchingConfiguration['SLACK_HOOK']; + $watchedEntities = $watchingConfiguration['WATCHED_ENTITIES']; - $victimShipId = $victim->{'ship_type_id'}; - $victimShipName = $this->getShipName($victimShipId); + // Only process kill that match settings entities + if ($this->isWatchedKill($data, $watchedEntities)) { - $isVictimCorpWatched = in_array($victim->{'corporation_id'}, Settings::$WATCHED_ENTITIES['corporations']); - $isVictimAllianceWatched = - isset($victim->{'alliance'}) && - in_array($victim->{'alliance_id'}, Settings::$WATCHED_ENTITIES['alliances']); - - // Dissociating kill and loss - if ($isVictimAllianceWatched || $isVictimCorpWatched) { - - $jsonKill->fallback = "$victimName's $victimShipName got killed by $killerName"; - if (isset($killer->{'corporation_id'})) { - $jsonKill->fallback .= ' (' . $this->getCorporationName($killer->{'corporation_id'}) . ')'; + if (Settings::$DEBUG) { + $this->storeKillJson($data->{'package'}->{'killID'}, $rawOutput); } - $jsonKill->color = 'danger'; - } else { - $victimCorpName = $this->getCorporationName($victim->{'corporation_id'}); - - if($noVictim) { - $jsonKill->fallback = "$killerName killed $victimName's $victimShipName"; - } else { - $jsonKill->fallback = "$killerName killed $victimName's $victimShipName ($victimCorpName)"; - } + $jsonAttachments = $this->formatKillData($data, $watchedEntities); - $jsonKill->color = 'good'; + $this->pushToSlack($jsonAttachments, $slackHook); } - - - /* - * Formatting data for slack - */ - - $jsonKill->title = $jsonKill->fallback; - $jsonKill->title_link = "https://zkillboard.com/kill/$killId/"; - $jsonKill->thumb_url = "https://imageserver.eveonline.com/Render/".$victimShipId."_64.png"; - - $killValue = number_format($zkb->{'totalValue'}, 2, ',', ' '); - - $jsonKillValue = new stdClass(); - $jsonKillValue->title = 'Value'; - $jsonKillValue->value = $killValue . ' ISK'; - $jsonKillValue->short = 'true'; - - $jsonTotalAttackers = new stdClass(); - $jsonTotalAttackers->title = 'Pilots involved'; - $jsonTotalAttackers->value = $attackerCount; - $jsonTotalAttackers->short = 'true'; - - $jsonKill->fields = [$jsonKillValue, $jsonTotalAttackers]; - $jsonAttachments->attachments = [$jsonKill]; - - $this->pushToSlack($jsonAttachments); } - return true; } - private function isWatchedKill($killmail) { + /** + * Compare each entities from kill to each watched entities + * + * @param $data + * @param $watchedEntities + * @return bool + */ + private function isWatchedKill($data, $watchedEntities) + { - $isVictimWatched = $this->isVictimWatched($killmail); - $isAttackerWatched = $this->isAttackerWatched($killmail); - $isSystemWatched = $this->isSystemWatched($killmail); + $killmail = $data->{'package'}->{'killmail'}; + + $isVictimWatched = $this->isVictimWatched($killmail, $watchedEntities); + $isAttackerWatched = $this->isAttackerWatched($killmail, $watchedEntities); + $isSystemWatched = $this->isSystemWatched($killmail, $watchedEntities); return $isVictimWatched || $isAttackerWatched || $isSystemWatched; } - private function isVictimWatched($killmail) { + /** + * Compares victim to watched entities + * + * @param $killmail + * @param $watchedEntities + * @return bool + */ + private function isVictimWatched($killmail, $watchedEntities) + { $victim = $killmail->{'victim'}; if (isset($victim->{'corporation_id'})) { - if (in_array($victim->{'corporation_id'}, Settings::$WATCHED_ENTITIES['corporations'])){ + if (in_array($victim->{'corporation_id'}, $watchedEntities['corporations'])) { return true; } } if (isset($victim->{'alliance_id'})) { - if (in_array($victim->{'alliance_id'}, Settings::$WATCHED_ENTITIES['alliances'])){ + if (in_array($victim->{'alliance_id'}, $watchedEntities['alliances'])) { return true; } } @@ -164,20 +108,28 @@ private function isVictimWatched($killmail) { return false; } - private function isAttackerWatched($killmail) { + /** + * Compares attackers to watched entities + * + * @param $killmail + * @param $watchedEntities + * @return bool + */ + private function isAttackerWatched($killmail, $watchedEntities) + { $attackers = $killmail->{'attackers'}; foreach ($attackers as $attacker) { if (isset($attacker->{'corporation_id'})) { - if (in_array($attacker->{'corporation_id'}, Settings::$WATCHED_ENTITIES['corporations'])){ + if (in_array($attacker->{'corporation_id'}, $watchedEntities['corporations'])) { return true; } } if (isset($attacker->{'alliance_id'})) { - if (in_array($attacker->{'alliance_id'}, Settings::$WATCHED_ENTITIES['alliances'])){ + if (in_array($attacker->{'alliance_id'}, $watchedEntities['alliances'])) { return true; } } @@ -186,16 +138,32 @@ private function isAttackerWatched($killmail) { return false; } - private function isSystemWatched($killmail) + /** + * Compare watched systems to kill's system + * + * @param $killmail + * @param $watchedEntities + * + * @return bool + */ + private function isSystemWatched($killmail, $watchedEntities) { - if (in_array($killmail->{'solar_system_id'}, Settings::$WATCHED_ENTITIES['systems'])) { + if (in_array($killmail->{'solar_system_id'}, $watchedEntities['systems'])) { return true; } return false; } - private function getShipName($victimShipId) { + /** + * Retrieves ship name from ESI + * + * @param $victimShipId + * + * @return string + */ + private function getShipName($victimShipId) + { if (in_array($victimShipId, $this->knownShips)) { return $this->knownShips[$victimShipId]; @@ -210,7 +178,15 @@ private function getShipName($victimShipId) { return $shipName; } - private function getCharacterName($characterId) { + /** + * Retrieves character name from ESI + * + * @param $characterId + * + * @return string + */ + private function getCharacterName($characterId) + { if (in_array($characterId, $this->knownCharacters)) { return $this->knownCharacters[$characterId]; @@ -225,7 +201,15 @@ private function getCharacterName($characterId) { return $characterName; } - private function getCorporationName($corporationId) { + /** + * Retrieves corporation name from ESI + * + * @param $corporationId + * + * @return string + */ + private function getCorporationName($corporationId) + { if (in_array($corporationId, $this->knownCorporations)) { return $this->knownCorporations[$corporationId]; @@ -240,7 +224,13 @@ private function getCorporationName($corporationId) { return $corporationName; } - private function curlRequest($url) { + /** + * @param $url + * + * @return bool|string + */ + private function curlRequest($url) + { $ch = curl_init(); @@ -256,15 +246,22 @@ private function curlRequest($url) { return $output; } - private function pushToSlack($killData) { + /** + * Push the formatted kill to slack + * + * @param $killData + * @param $slackHook + */ + private function pushToSlack($killData, $slackHook) + { if (Settings::$DEBUG) { print_r($killData); } - $data = "payload=" .json_encode($killData); + $data = "payload=" . json_encode($killData); - $ch = curl_init(Settings::$SLACK_HOOK); + $ch = curl_init($slackHook); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @@ -275,8 +272,14 @@ private function pushToSlack($killData) { curl_close($ch); } - - private function storeKillJson($killId, $data) { + /** + * Stores the kill in a file as json + * + * @param $killId + * @param $data + */ + private function storeKillJson($killId, $data) + { $directoryName = __DIR__ . DIRECTORY_SEPARATOR . Settings::$KILL_LOG_FOLDER; @@ -291,4 +294,104 @@ private function storeKillJson($killId, $data) { fclose($file); } + /** + * Retrieves missing information and format kill data for slack + * + * @param $data + * @param $watchedEntities + * + * @return stdClass + */ + protected function formatKillData($data, $watchedEntities): stdClass + { + + $killmail = $data->{'package'}->{'killmail'}; + $zkb = $data->{'package'}->{'zkb'}; + $jsonKill = new stdClass(); + $jsonAttachments = new stdClass(); + + $killer = null; + $noVictim = false; + $attackerCount = count($killmail->{'attackers'}); + $killId = $killmail->{'killmail_id'}; + + // Looking up final blow + foreach ($killmail->{'attackers'} as $attacker) { + if ($attacker->{'final_blow'} == true) { + $killer = $attacker; + } + } + + // Handling NPC kills + if (!isset($killer->{'character_id'})) { + $killerName = $this->getShipName($killer->{'ship_type_id'}); + } else { + $killerName = $this->getCharacterName($killer->{'character_id'}); + } + + // Handling case with no pilots victims + $victim = $killmail->{'victim'}; + if (!isset($victim->{'character_id'})) { + $noVictim = true; + $victimName = $this->getCorporationName($victim->{'corporation_id'}); + } else { + $victimName = $this->getCharacterName($victim->{'character_id'}); + } + + $victimShipId = $victim->{'ship_type_id'}; + $victimShipName = $this->getShipName($victimShipId); + + $isVictimCorpWatched = in_array($victim->{'corporation_id'}, $watchedEntities['corporations']); + $isVictimAllianceWatched = + isset($victim->{'alliance'}) && + in_array($victim->{'alliance_id'}, $watchedEntities['alliances']); + + // Dissociating kill and loss + if ($isVictimAllianceWatched || $isVictimCorpWatched) { + + $jsonKill->fallback = "$victimName's $victimShipName got killed by $killerName"; + if (isset($killer->{'corporation_id'})) { + $jsonKill->fallback .= ' (' . $this->getCorporationName($killer->{'corporation_id'}) . ')'; + } + $jsonKill->color = 'danger'; + + } else { + $victimCorpName = $this->getCorporationName($victim->{'corporation_id'}); + + if ($noVictim) { + $jsonKill->fallback = "$killerName killed $victimName's $victimShipName"; + } else { + $jsonKill->fallback = "$killerName killed $victimName's $victimShipName ($victimCorpName)"; + } + + $jsonKill->color = 'good'; + } + + + /* + * Formatting data for slack + */ + + $jsonKill->title = $jsonKill->fallback; + $jsonKill->title_link = "https://zkillboard.com/kill/$killId/"; + $jsonKill->thumb_url = "https://imageserver.eveonline.com/Render/" . $victimShipId . "_64.png"; + + $killValue = number_format($zkb->{'totalValue'}, 2, ',', ' '); + + $jsonKillValue = new stdClass(); + $jsonKillValue->title = 'Value'; + $jsonKillValue->value = $killValue . ' ISK'; + $jsonKillValue->short = 'true'; + + $jsonTotalAttackers = new stdClass(); + $jsonTotalAttackers->title = 'Pilots involved'; + $jsonTotalAttackers->value = $attackerCount; + $jsonTotalAttackers->short = 'true'; + + $jsonKill->fields = [$jsonKillValue, $jsonTotalAttackers]; + $jsonAttachments->attachments = [$jsonKill]; + + return $jsonAttachments; + } + } diff --git a/Settings.php b/Settings.php index e2ca031..5e3f5a0 100644 --- a/Settings.php +++ b/Settings.php @@ -2,34 +2,38 @@ namespace Killbot; -class Settings { +class Settings +{ /******************************************************************************************************************* * Basic configuration * Should be enough to get your bot running. - * Dont forget to setup a cronjob executing cron.php every $MAX_RUN_TIME + a small offset + * Dont forget to setup a cron job executing cron.php every $MAX_RUN_TIME + a small offset ******************************************************************************************************************/ - // Slack webhook URL - public static $SLACK_HOOK = 'https://hooks.slack.com/services/ABC/DEF/GHI'; + // Entity ids you want to display killmails for. + // You may have different hooks used for watching different entities, just duplicate 'default'. + public static $watchingConfigurations = [ + 'default' => [ + 'SLACK_HOOK' => 'https://hooks.slack.com/services/ABC/ABC/ABC', + 'WATCHED_ENTITIES' => [ + 'corporations' => [ + 123, + ], + 'alliances' => [ + 1234, + 12345, + ], + 'systems' => [ + 30000142, + ], + ], + ], + ]; // 4 minutes default max run time public static $MAX_RUN_TIME = 4 * 60; - // Entities you want to display killmails for - public static $WATCHED_ENTITIES = [ - 'corporations' => [ - 123456, - 1324567 - ], - 'alliances' => [ - 13245678 - ], - 'systems' => [ - 30003504 - ] - ]; - /******************************************************************************************************************* * Mail settings * Allow script to send mail alert when something goes wrong @@ -57,7 +61,7 @@ class Settings { ******************************************************************************************************************/ // HTTP header sent with each bot request - public static $HTTP_HEADER = 'RedisQ-Slack-Bot https://github.com/Slivo-fr/RedisQ-Slack-Bot'; + public static $HTTP_HEADER = 'RedisQ-Slack-Bot https://github.com/Slivo-fr/RedisQ-Slack-Bot'; // URL to redisQ public static $REDISQ_URL = 'https://redisq.zkillboard.com/listen.php?ttw=1'; diff --git a/composer.json b/composer.json index 21a6a29..47fe367 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,9 @@ "require": { "php": "^7.0", "swiftmailer/swiftmailer": "^6.0", - "twig/twig": "^2.0" + "twig/twig": "^2.0", + "ext-json": "*", + "ext-curl": "*" }, "autoload": { "psr-4": { diff --git a/cron.php b/cron.php index 970ad4e..fc1f014 100644 --- a/cron.php +++ b/cron.php @@ -1,17 +1,20 @@ run(); } @@ -21,8 +24,7 @@ function runBot() { try { runBot(); - } - catch (Exception $e) { + } catch (Exception $e) { if (Settings::$SEND_MAIL) { AlertHandler::sendAlertMail($e); diff --git a/mail.html.twig b/mail.html.twig index 1732d84..7b2262d 100644 --- a/mail.html.twig +++ b/mail.html.twig @@ -7,6 +7,6 @@ Error : {{ message }} File : {{ path }} Line : {{ line }} -It may be a lonely fail, but if you keep getting more of there mail, you should double check your configuration and/or report the issue ! +It may be a lonely fail, but if you keep getting more of these mail, you should double check your configuration and/or report the issue ! Fly safe o7 \ No newline at end of file