* @author George Goldberg * * @license MIT * * @file The hooks of the Mediawiki Slack integration extension. * For more information on Slack, see http://www.slack.com. * @ingroup Extensions */ class SlackHooks { public static function isCreate() { global $wgSlackIsCreate; if ($wgSlackIsCreate === true) { return true; } $wgSlackIsCreate = true; return false; } public static function encodeSlackChars($in) { // This function encodes chars that the Slack API expects to be encoded in the JSON values. // See https://api.slack.com/docs/formatting for details. $o = str_replace("&", "&", $in); $o = str_replace("<", "<", $o); $o = str_replace(">", ">", $o); $o = str_replace('"', """, $o); return $o; } public static function sendToRedis($payload) { /*global $wgSlackWebhookURL; wfDebug("Slack URL: ".$wgSlackWebhookURL."\n"); wfDebug("Slack Payload: ".$payload."\n"); $post = "payload=".urlencode($payload); // POST it to Slack. $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => $post, 'timeout' => 0.5, // don't wait for Slack's response ), ); */ try { global $redis; $redis->lPush('slack:recent-changes', $payload); } catch (Exception $e) { wfDebug('sendToRedis failed: '.$e->getMessage()."\n"); } /*$context = stream_context_create($options); $result = file_get_contents($wgSlackWebhookURL, false, $context); if (!$result) { wfDebug("API call failed!\n"); } else { wfDebug("Slack Result: ".$result."\n"); }*/ } public static function buildMessage($wikiPage, $user, $summary, $verb) { global $wgSlackLinkUsers; // Build the message we're going to post to Slack. $message = '*<'.SlackHooks::encodeSlackChars($wikiPage->getTitle()->getFullURL()) .'|'.SlackHooks::encodeSlackChars($wikiPage->getTitle()).'>* ' .$verb.' by *'; if ($wgSlackLinkUsers) { $message .= '@'; } $message .= SlackHooks::encodeSlackChars(strtolower($user->getName())).'*'; if (!empty($summary)) { $message .= ': '.$summary; } $message .= '.'; return $message; } public static function buildPayload($message) { global $wgSlackChannel, $wgSlackLinkUsers, $wgSlackUserName, $wgSlackIconURL; // Build the WebHook Payload. // NB: The Slack parser chokes if there is a trailing , at the end of the list of items // in the payload. Make sure any optional items are in the middle to avoid this. $payload = '{"channel": "'.$wgSlackChannel.'",'; if ($wgSlackLinkUsers) { $payload .= '"link_names": "1",'; } if ($wgSlackIconURL) { $payload .= '"icon_url": "'.$wgSlackIconURL.'",'; } $payload .= '"username": "'.$wgSlackUserName.'",' .'"text": "'.$message.'"' .'}'; return $payload; } public static function buildRichPayload($wikiPage, $user, $summary, $diff, $verb) { global $wgSlackUserName, $wgSlackIconURL, $wgSlackLinkUsers; $attachment = array(); $attachment['fallback'] = sprintf('%s %s by %s', $wikiPage->getTitle(), $verb, strtolower($user->getName())); $attachment['title'] = $wikiPage->getTitle()->getFullText(); $attachment['title_link'] = $wikiPage->getTitle()->GetFullURL(); if (!empty($summary)) { $attachment['fallback'] .= ': '.SlackHooks::encodeSlackChars($summary); } $attachment['fallback'] .= '.'; $attachment['pretext'] = SlackHooks::encodeSlackChars($summary); $attachment['text'] = $diff; $attachment['author_name'] = strtolower($user->getName()); $attachment['author_link'] = sprintf('https://wiki.metakgp.org/w/User:%s', $user->getName()); $attachment['color'] = 'good'; $payload['attachments'] = array($attachment); $payload['username'] = $wgSlackUserName; $payload['icon_url'] = $wgSlackIconURL; $payload['link_names'] = "1"; return json_encode($payload, JSON_UNESCAPED_SLASHES|JSON_HEX_APOS|JSON_HEX_QUOTE); } private static function isBot($user) { if (in_array("bot", $user->getGroups())) { return true; } return false; } public static function onPageContentSaveComplete($wikiPage, $user, $content, $summary, $isMinor, $isWatch, $section, $flags, $revision, $status, $baseRevId) { global $wgSlackIgnoreMinor; // If this is a bot, return now if (SlackHooks::isBot($user)) { return true; } // If this is a minor edit and we want to ignore minor edits, return now. if ($wgSlackIgnoreMinor && $isMinor) { return true; } // If this is a page creation, don't notify it as being modified too. if (true === SlackHooks::isCreate()) { return true; } // Build the Slack Message. $message = SlackHooks::buildMessage($wikiPage, $user, $summary, "modified"); //$oldRevision = $revision->getPrevious(); // Build the Slack Payload. //$payload = SlackHooks::buildPayload($wikiPage, $user, $summary, $content->getNativeData(), "modified"); $payload = SlackHooks::buildPayload($message); // Send the message to Slack. SlackHooks::sendToRedis($payload); return true; } public static function onPageContentInsertComplete($wikiPage, $user, $content, $summary, $isMinor, $isWatch, $section, $flags, $revision) { global $wgSlackIsCreate, $wgSlackIgnoreMinor; // If this is a bot, return now if (SlackHooks::isBot($user)) { return true; } // If this is a minor edit and we want to ignore minor edits, return now. if ($wgSlackIgnoreMinor && $isMinor) { return true; } // Flag this as a page creation so we don't notify it's been modified as well. $wgSlackIsCreate = true; // Build the Slack Message. $message = SlackHooks::buildMessage($wikiPage, $user, $summary, "created"); // Build the Slack Payload. $payload = SlackHooks::buildPayload($message); // Send the message to Slack. SlackHooks::sendToRedis($payload); return true; } }