289 lines
8.0 KiB
PHP
Executable File
289 lines
8.0 KiB
PHP
Executable File
#!/usr/bin/php
|
|
<?php
|
|
declare(strict_types = 1);
|
|
require_once 'vendor/autoload.php';
|
|
|
|
use Yasumi\Holiday;
|
|
|
|
/* On Call Ticket Notification Script */
|
|
$oncall = new oncall();
|
|
$oncall->dateAndTimeCheck();
|
|
$oncall->retrieveItems();
|
|
if (!$oncall->notify) {
|
|
exit(0);
|
|
}
|
|
$oncall->setEmail();
|
|
$table = $oncall->buildMattermostTable();
|
|
if ($table) {
|
|
$oncall->sendToMattermost($table);
|
|
}
|
|
|
|
/**
|
|
* The class that does all the magic.
|
|
*/
|
|
class oncall {
|
|
|
|
/**
|
|
* Are we in debug mode?
|
|
* @var bool
|
|
*/
|
|
private $debugMode = FALSE;
|
|
|
|
/**
|
|
* A flag to indicate that everyone should be notified.
|
|
* @var bool
|
|
*/
|
|
private $everyone = FALSE;
|
|
|
|
/**
|
|
* The URL of the Redmine install.
|
|
* @var string
|
|
*/
|
|
private $baseUrl;
|
|
|
|
/**
|
|
* The Redmine API key.
|
|
* @var string
|
|
*/
|
|
private $apiKey;
|
|
|
|
/**
|
|
* An array containing queries we want to run, with a label as a key.
|
|
* @var array
|
|
*/
|
|
private $queries;
|
|
|
|
/**
|
|
* An array of the query results from Redmine. Key is the query label.
|
|
* @var array
|
|
*/
|
|
private $queryResults = [];
|
|
|
|
/**
|
|
* The email of the person on call.
|
|
* @var string;
|
|
*/
|
|
private $email;
|
|
|
|
/**
|
|
* A flag of whether Mattermost should be contacted.
|
|
* @var bool
|
|
*/
|
|
public $notify;
|
|
|
|
public function __construct() {
|
|
// Load the .env file
|
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
|
$dotenv->load();
|
|
|
|
// Debug Mode will show more information.
|
|
$this->debugMode = (bool) getenv('DEBUG_MODE');
|
|
$this->baseUrl = getenv('REDMINE_URL');
|
|
$this->apiKey = getenv('REDMINE_API_KEY');
|
|
|
|
// FIXME: Not using $this->apiKey.
|
|
$this->queries = [
|
|
'New Support Tickets' => 'https://hq.megaphonetech.com/issues.json?query_id=17&key=7ebe204bef5804f4effb9b4160a295487dde15f1',
|
|
'New Maintenance Tickets' => 'https://hq.megaphonetech.com/issues.json?query_id=18&key=7ebe204bef5804f4effb9b4160a295487dde15f1',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Add the number of minutes since a ticket was created.
|
|
* Also set the everyone and notify flags if necessary.
|
|
* @return int
|
|
*/
|
|
private function calculateMinutes(array $item) {
|
|
// find the time since the ticket was created
|
|
$created = $item['created_on'];
|
|
$time = strtotime("$created");
|
|
$time_difference = strtotime('now') - $time;
|
|
|
|
// put it into minutes
|
|
$td_min = (int) ($time_difference / 60);
|
|
|
|
// Flag if any items are overdue.
|
|
if ($td_min >= 60) {
|
|
$this->everyone = TRUE;
|
|
}
|
|
// Notify every 30 minutes.
|
|
if (($td_min % 30) < 3) {
|
|
$this->notify = TRUE;
|
|
}
|
|
return $td_min;
|
|
}
|
|
|
|
/**
|
|
* Get items from Redmine.
|
|
*/
|
|
public function retrieveItems() {
|
|
foreach ($this->queries as $title => $url) {
|
|
// get the contents of the query in a usable format
|
|
$json = json_decode(file_get_contents($url), TRUE);
|
|
// if the query has any results
|
|
if (!empty($json)) {
|
|
// go through this loop for each ticket found
|
|
foreach ($json['issues'] as $k => $item) {
|
|
$item['minutes'] = $this->calculateMinutes($item);
|
|
$this->queryResults[$title][] = $item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render the $items property in Markdown.
|
|
* @return string
|
|
*/
|
|
public function buildMattermostTable() {
|
|
$table = '';
|
|
foreach ($this->queryResults as $label => $queryResult) {
|
|
|
|
//Add header row
|
|
$table .= "\n### $label\n";
|
|
$table .= "| Issue | Project | Author | Subject | Minutes Elapsed | Acknowledge |\n| :----- | :------- | :----- | :-------| ----: | ----: |\n";
|
|
foreach ($queryResult as $item) {
|
|
$table .= "| [{$item['id']}](https://hq.megaphonetech.com/issues/{$item['id']}) ";
|
|
$table .= "| {$item['project']['name']} ";
|
|
$table .= "| {$item['author']['name']} ";
|
|
$table .= "| {$item['subject']} ";
|
|
$table .= "| {$item['minutes']} ";
|
|
$table .= "| [Do Not Alert](https://oncall.megaphonetech.com/update_redmine.php?action=do_not_alert&issue={$item['id']}) ";
|
|
$table .= "|\n";
|
|
}
|
|
}
|
|
return $table;
|
|
}
|
|
|
|
public function sendToMattermost($table) {
|
|
$mattermostName = $this->findMattermostName();
|
|
$url = 'https://chat.civicrm.org/hooks/4d7wtzzmj3rjmqpjg9z9nfqwjo';
|
|
$data['channel'] = 'Megaphone';
|
|
$data['username'] = 'megaphone-bot';
|
|
$data['icon_url'] = 'http://oncall.megaphonetech.com/oncallbot.jpg';
|
|
$data['text'] = "@$mattermostName\n\n$table";
|
|
$json = json_encode($data);
|
|
|
|
// use key 'http' even if you send the request to https://...
|
|
$options = [
|
|
'http' => [
|
|
'header' => "Content-type: application/json\r\n",
|
|
'method' => 'POST',
|
|
'content' => $json,
|
|
],
|
|
];
|
|
$context = stream_context_create($options);
|
|
$result = file_get_contents($url, FALSE, $context);
|
|
if ($result === FALSE) {
|
|
print_r($result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a Mattermost name from email, or "@channel" if there's an everyone ticket.
|
|
* @return string
|
|
*/
|
|
private function findMattermostName() {
|
|
if ($this->everyone) {
|
|
return 'channel';
|
|
}
|
|
// Check users.csv column 1 for email; return column 2 if it matches.
|
|
if (($handle = fopen(__DIR__ . "/users.csv", "r")) !== FALSE) {
|
|
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
|
|
if ($data[0] == $this->email) {
|
|
fclose($handle);
|
|
return $data[1];
|
|
}
|
|
}
|
|
}
|
|
fclose($handle);
|
|
return '';
|
|
}
|
|
|
|
public function setEmail() {
|
|
// path to file containing email of person on call
|
|
$contactfile = __DIR__ . "/contact.txt";
|
|
// set the email of the person on call
|
|
$this->email = trim(file_get_contents("$contactfile"));
|
|
}
|
|
|
|
/**
|
|
* Checks whether we're responsible for on-call on this date and time.
|
|
*/
|
|
public function dateAndTimeCheck() {
|
|
$today = new DateTime("now", new DateTimeZone('America/New_York'));
|
|
$thisYear = (int) $today->format('Y');
|
|
$thisHour = (int) $today->format('H');
|
|
|
|
// Time check.
|
|
if ($thisHour < 10 || $thisHour > 20) {
|
|
// After hours.
|
|
exit(0);
|
|
}
|
|
|
|
$holidays = Yasumi\Yasumi::create('USA', $thisYear);
|
|
$holidays->removeHoliday('columbusDay');
|
|
$holidays->addHoliday(new Holiday('indigenousPeoplesDay', [
|
|
'en' => 'Indigenous People\'s Day',
|
|
], new DateTime("second monday of october $thisYear", new DateTimeZone('America/New_York'))));
|
|
$holidays->addHoliday(new Holiday('dayAfterThanksgiving', [
|
|
'en' => 'Day After Thanksgiving',
|
|
], new DateTime("fourth friday of november $thisYear", new DateTimeZone('America/New_York'))));
|
|
|
|
// Is today a holiday?
|
|
if ($holidays->isHoliday($today)) {
|
|
$this->everyone = TRUE;
|
|
$todaysHolidayIterator = $holidays->on($today);
|
|
foreach ($todaysHolidayIterator as $todaysHoliday) {
|
|
$holidayName = $todaysHoliday->getName();
|
|
}
|
|
// $this->sendToMattermost("Happy $holidayName Megaphone Tech! There is no on-call today.");
|
|
}
|
|
// Don't do anything else if it's a holiday or weekend.
|
|
if (!$holidays->isWorkingDay($today)) {
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Send an email for this item.
|
|
*/
|
|
function email($item, $contact) {
|
|
// if it's been an hour, email everyone instead
|
|
if ($item['minutes'] > 60) {
|
|
$contact = 'team@megaphonetech.com';
|
|
}
|
|
|
|
// if it's been 25 minutes, make an email
|
|
if ($item['minutes'] > 0) {
|
|
|
|
// email body
|
|
$email = "New issue to attend to:" . "\n";
|
|
$email .= "\n";
|
|
$email .= "Project: " . $item['project']['name'] . "\n";
|
|
$email .= "Author: " . $item['author']['name'] . "\n";
|
|
$email .= "Subject: " . $item['subject'] . "\n";
|
|
$email .= "\n";
|
|
$email .= $item['description'] . "\n";
|
|
$email .= "\n";
|
|
$email .= "Ticket created {$item['minutes']} minutes ago";
|
|
$email .= "\n";
|
|
$email .= 'https://hq.megaphonetech.com/issues/' . $item['id'] . "\n";
|
|
$email .= "\n";
|
|
|
|
// email headers
|
|
$to = $contact;
|
|
$subject = "[#{$item['id']}]";
|
|
$message = $email;
|
|
$headers = 'From: support@megaphonetech.com' . "\r\n" .
|
|
'Reply-To: support@megaphonetech.com' . "\r\n" .
|
|
'X-Mailer: PHP/' . phpversion();
|
|
|
|
// send email
|
|
mail($to, $subject, $message, $headers);
|
|
|
|
}
|
|
}
|