convert to support multiple queries
This commit is contained in:
commit
26c0032053
3
.env.example
Normal file
3
.env.example
Normal file
@ -0,0 +1,3 @@
|
||||
REDMINE_URL='https://redmine.example.com'
|
||||
REDMINE_API_KEY='abcdefg12345'
|
||||
DEBUG_MODE=
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/vendor/
|
||||
.env
|
||||
contact.txt
|
||||
users.csv
|
16
composer.json
Normal file
16
composer.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "megaphone/oncall",
|
||||
"description": "A script to report on Redmine tickets that need attention.",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"kbsali/redmine-api": "^1.5",
|
||||
"vlucas/phpdotenv": "^4.1"
|
||||
},
|
||||
"license": "GPL",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jon Goldberg",
|
||||
"email": "jon@megaphonetech.com"
|
||||
}
|
||||
]
|
||||
}
|
269
composer.lock
generated
Normal file
269
composer.lock
generated
Normal file
@ -0,0 +1,269 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9d1110731b1633c82d10df7e5b5077a6",
|
||||
"packages": [
|
||||
{
|
||||
"name": "kbsali/redmine-api",
|
||||
"version": "v1.5.21",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/kbsali/php-redmine-api.git",
|
||||
"reference": "e75295b81e5dc4c858007d8924408e11e49e080c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/kbsali/php-redmine-api/zipball/e75295b81e5dc4c858007d8924408e11e49e080c",
|
||||
"reference": "e75295b81e5dc4c858007d8924408e11e49e080c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-simplexml": "*",
|
||||
"php": "^5.4 || ^7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.0",
|
||||
"phpunit/phpunit": "^4.8.36 || ^5.4.8 || ^6.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Redmine\\": "src/Redmine/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kevin Saliou",
|
||||
"email": "kevin@saliou.name",
|
||||
"homepage": "http://kevin.saliou.name"
|
||||
}
|
||||
],
|
||||
"description": "Redmine API client",
|
||||
"homepage": "https://github.com/kbsali/php-redmine-api",
|
||||
"keywords": [
|
||||
"api",
|
||||
"redmine"
|
||||
],
|
||||
"time": "2019-03-15T17:37:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/schmittjoh/php-option.git",
|
||||
"reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/4acfd6a4b33a509d8c88f50e5222f734b6aeebae",
|
||||
"reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.3",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.7-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOption\\": "src/PhpOption/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Johannes M. Schmitt",
|
||||
"email": "schmittjoh@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com"
|
||||
}
|
||||
],
|
||||
"description": "Option Type for PHP",
|
||||
"keywords": [
|
||||
"language",
|
||||
"option",
|
||||
"php",
|
||||
"type"
|
||||
],
|
||||
"time": "2020-03-21T18:07:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.15.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
|
||||
"reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.15-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-02-27T09:26:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
"version": "v4.1.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||
"reference": "539bb6927c101a5605d31d11a2d17185a2ce2bf1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/539bb6927c101a5605d31d11a2d17185a2ce2bf1",
|
||||
"reference": "539bb6927c101a5605d31d11a2d17185a2ce2bf1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9 || ^7.0 || ^8.0",
|
||||
"phpoption/phpoption": "^1.7.2",
|
||||
"symfony/polyfill-ctype": "^1.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.3",
|
||||
"ext-filter": "*",
|
||||
"ext-pcre": "*",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-filter": "Required to use the boolean validator.",
|
||||
"ext-pcre": "Required to use most of the library."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dotenv\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com",
|
||||
"homepage": "https://gjcampbell.co.uk/"
|
||||
},
|
||||
{
|
||||
"name": "Vance Lucas",
|
||||
"email": "vance@vancelucas.com",
|
||||
"homepage": "https://vancelucas.com/"
|
||||
}
|
||||
],
|
||||
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||
"keywords": [
|
||||
"dotenv",
|
||||
"env",
|
||||
"environment"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/GrahamCampbell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-05-02T14:08:57+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
12
index.php
Normal file
12
index.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
$file = 'contact.txt';
|
||||
|
||||
if(isset($_GET["contact"])) {
|
||||
file_put_contents($file, $_GET['contact']);
|
||||
}
|
||||
|
||||
$oncall = file_get_contents('./contact.txt');
|
||||
|
||||
echo("Current person on call is now $oncall");
|
||||
|
||||
?>
|
244
oncall.php
Normal file
244
oncall.php
Normal file
@ -0,0 +1,244 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
|
||||
/* On Call Ticket Notification Script */
|
||||
|
||||
$today = date('Ymd');
|
||||
$holidays = array(20151126, 20151127, 20151225, 20160101, 20160118, 20160215, 20160530, 20160704, 20160905, 20161010, 20161111, 20161124);
|
||||
|
||||
if (in_array($today, $holidays)) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$oncall = new oncall();
|
||||
$oncall->retrieveItems();
|
||||
$oncall->setEmail();
|
||||
$table = $oncall->buildMattermostTable();
|
||||
if ($table) {
|
||||
$oncall->sendToMattermost($table);
|
||||
}
|
||||
|
||||
//email($item, $contact);
|
||||
|
||||
/**
|
||||
* The class that does all the magic.
|
||||
*/
|
||||
class oncall {
|
||||
|
||||
/**
|
||||
* Are we in debug mode?
|
||||
* @var bool
|
||||
*/
|
||||
private $debugMode = FALSE;
|
||||
|
||||
/**
|
||||
* A flag to indicate at least one ticket is overdue.
|
||||
* @var bool
|
||||
*/
|
||||
private $overdue = 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;
|
||||
|
||||
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('KEY');
|
||||
|
||||
$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 overdue flag if necessary.
|
||||
*/
|
||||
private function getMinutes(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);
|
||||
|
||||
// Store the time in minutes in the array.
|
||||
$item['minutes'] = $td_min;
|
||||
// Flag if any items are overdue.
|
||||
if ($td_min > 60) {
|
||||
$this->overdue = TRUE;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = $this->getMinutes($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 |\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 .= "|\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 overdue ticket.
|
||||
* @return string
|
||||
*/
|
||||
private function findMattermostName() {
|
||||
if ($this->overdue) {
|
||||
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 = './contact.txt';
|
||||
// set the email of the person on call
|
||||
$this->email = trim(file_get_contents("$contactfile"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user