@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Initial PhabricatorIRCFeedNotificationHandler

Summary:
Follows Phabricator's feed and puts notifications into channels
that are configured.

~~notification.all - bool - 1:1 stories to messages~~
notification.types - array - Specific story types to notify for - ["differential", "maniphest"]
notification.verbosity - int - Range of 0-3 for verbosity
notification.max_pages - int - Maximum number of pages to go back per poll
notification.page_size - int - Size of pages (limit) to poll
~~notification.channels - array - Array of channels to send messages to~~
~~notification.sleep - int - Seconds to sleep between polls~~

Test Plan: Run phabot with various configuration options

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin, asherkin

Differential Revision: https://secure.phabricator.com/D4418

authored by

John Watson and committed by
epriestley
ff53b794 721071e5

+161
+2
src/__phutil_library_map__.php
··· 883 883 'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php', 884 884 'PhabricatorIRCBot' => 'infrastructure/daemon/irc/PhabricatorIRCBot.php', 885 885 'PhabricatorIRCDifferentialNotificationHandler' => 'infrastructure/daemon/irc/handler/PhabricatorIRCDifferentialNotificationHandler.php', 886 + 'PhabricatorIRCFeedNotificationHandler' => 'infrastructure/daemon/irc/handler/PhabricatorIRCFeedNotificationHandler.php', 886 887 'PhabricatorIRCHandler' => 'infrastructure/daemon/irc/handler/PhabricatorIRCHandler.php', 887 888 'PhabricatorIRCLogHandler' => 'infrastructure/daemon/irc/handler/PhabricatorIRCLogHandler.php', 888 889 'PhabricatorIRCMacroHandler' => 'infrastructure/daemon/irc/handler/PhabricatorIRCMacroHandler.php', ··· 2262 2263 'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController', 2263 2264 'PhabricatorIRCBot' => 'PhabricatorDaemon', 2264 2265 'PhabricatorIRCDifferentialNotificationHandler' => 'PhabricatorIRCHandler', 2266 + 'PhabricatorIRCFeedNotificationHandler' => 'PhabricatorIRCHandler', 2265 2267 'PhabricatorIRCLogHandler' => 'PhabricatorIRCHandler', 2266 2268 'PhabricatorIRCMacroHandler' => 'PhabricatorIRCHandler', 2267 2269 'PhabricatorIRCObjectNameHandler' => 'PhabricatorIRCHandler',
+159
src/infrastructure/daemon/irc/handler/PhabricatorIRCFeedNotificationHandler.php
··· 1 + <?php 2 + 3 + /** 4 + * Watches the feed and puts notifications into channel(s) of choice 5 + * 6 + * @group irc 7 + */ 8 + final class PhabricatorIRCFeedNotificationHandler 9 + extends PhabricatorIRCHandler { 10 + 11 + private $startupDelay = 30; 12 + private $lastSeenChronoKey = 0; 13 + 14 + private function shouldShowStory($story) { 15 + $story_class = $story['class']; 16 + $story_text = $story['text']; 17 + 18 + $show = $this->getConfig('notification.types'); 19 + 20 + if ($show) { 21 + $obj_type = str_replace('PhabricatorFeedStory', '', $story_class); 22 + if (!in_array(strtolower($obj_type), $show)) { 23 + return false; 24 + } 25 + } 26 + 27 + $verbosity = $this->getConfig('notification.verbosity', 3); 28 + 29 + $verbs = array(); 30 + 31 + switch ($verbosity) { 32 + case 2: 33 + $verbs[] = array( 34 + 'commented', 35 + 'added', 36 + 'changed', 37 + 'resigned', 38 + 'explained', 39 + 'modified', 40 + 'attached', 41 + 'edited', 42 + 'joined', 43 + 'left', 44 + 'removed' 45 + ); 46 + // fallthrough 47 + case 1: 48 + $verbs[] = array( 49 + 'updated', 50 + 'accepted', 51 + 'requested', 52 + 'planned', 53 + 'claimed', 54 + 'summarized', 55 + 'commandeered', 56 + 'assigned' 57 + ); 58 + // fallthrough 59 + case 0: 60 + $verbs[] = array( 61 + 'created', 62 + 'closed', 63 + 'raised', 64 + 'committed', 65 + 'reopened', 66 + 'deleted' 67 + ); 68 + break; 69 + 70 + case 3: 71 + default: 72 + return true; 73 + break; 74 + } 75 + 76 + $verbs = '/('.implode('|', array_mergev($verbs)).')/'; 77 + 78 + if (preg_match($verbs, $story_text)) { 79 + return true; 80 + } 81 + 82 + return false; 83 + } 84 + 85 + public function receiveMessage(PhabricatorIRCMessage $message) { 86 + return; 87 + } 88 + 89 + public function runBackgroundTasks() { 90 + if ($this->startupDelay > 0) { 91 + // the event loop runs every 1s so delay enough to fully conenct 92 + $this->startupDelay--; 93 + 94 + return; 95 + } 96 + if ($this->lastSeenChronoKey == 0) { 97 + // Since we only want to post notifications about new stories, skip 98 + // everything that's happened in the past when we start up so we'll 99 + // only process real-time stories. 100 + $latest = $this->getConduit()->callMethodSynchronous( 101 + 'feed.query', 102 + array( 103 + 'limit'=>1 104 + )); 105 + 106 + foreach ($latest as $story) { 107 + if ($story['chronologicalKey'] > $this->lastSeenChronoKey) { 108 + $this->lastSeenChronoKey = $story['chronologicalKey']; 109 + } 110 + } 111 + 112 + return; 113 + } 114 + 115 + $config_max_pages = $this->getConfig('notification.max_pages', 5); 116 + $config_page_size = $this->getConfig('notification.page_size', 10); 117 + 118 + $last_seen_chrono_key = $this->lastSeenChronoKey; 119 + $chrono_key_cursor = 0; 120 + 121 + // Not efficient but works due to feed.query API 122 + for ($max_pages = $config_max_pages; $max_pages > 0; $max_pages--) { 123 + $stories = $this->getConduit()->callMethodSynchronous( 124 + 'feed.query', 125 + array( 126 + 'limit'=>$config_page_size, 127 + 'after'=>$chrono_key_cursor, 128 + 'view'=>'text' 129 + )); 130 + 131 + foreach ($stories as $story) { 132 + if ($story['chronologicalKey'] == $last_seen_chrono_key) { 133 + // Caught up on feed 134 + return; 135 + } 136 + if ($story['chronologicalKey'] > $this->lastSeenChronoKey) { 137 + // Keep track of newest seen story 138 + $this->lastSeenChronoKey = $story['chronologicalKey']; 139 + } 140 + if (!$chrono_key_cursor || 141 + $story['chronologicalKey'] < $chrono_key_cursor) { 142 + // Keep track of oldest story on this page 143 + $chrono_key_cursor = $story['chronologicalKey']; 144 + } 145 + 146 + if (!$story['text'] || 147 + !$this->shouldShowStory($story)) { 148 + continue; 149 + } 150 + 151 + $channels = $this->getConfig('join'); 152 + foreach ($channels as $channel) { 153 + $this->write('PRIVMSG', "{$channel} :{$story['text']}"); 154 + } 155 + } 156 + } 157 + } 158 + 159 + }