Page MenuHomePhabricator

D17756.id.diff
No OneTemporary

D17756.id.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2089,20 +2089,6 @@
'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php',
'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php',
'PhabricatorBoolEditField' => 'applications/transactions/editfield/PhabricatorBoolEditField.php',
- 'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
- 'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
- 'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
- 'PhabricatorBotFeedNotificationHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php',
- 'PhabricatorBotFlowdockProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorBotFlowdockProtocolAdapter.php',
- 'PhabricatorBotHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotHandler.php',
- 'PhabricatorBotLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotLogHandler.php',
- 'PhabricatorBotMacroHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotMacroHandler.php',
- 'PhabricatorBotMessage' => 'infrastructure/daemon/bot/PhabricatorBotMessage.php',
- 'PhabricatorBotObjectNameHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotObjectNameHandler.php',
- 'PhabricatorBotSymbolHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotSymbolHandler.php',
- 'PhabricatorBotTarget' => 'infrastructure/daemon/bot/target/PhabricatorBotTarget.php',
- 'PhabricatorBotUser' => 'infrastructure/daemon/bot/target/PhabricatorBotUser.php',
- 'PhabricatorBotWhatsNewHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php',
'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php',
'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php',
'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php',
@@ -2261,7 +2247,6 @@
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
- 'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php',
'PhabricatorCelerityApplication' => 'applications/celerity/application/PhabricatorCelerityApplication.php',
'PhabricatorCelerityTestCase' => '__tests__/PhabricatorCelerityTestCase.php',
'PhabricatorChangeParserTestCase' => 'applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php',
@@ -2921,7 +2906,6 @@
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
- 'PhabricatorIRCProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php',
'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
'PhabricatorIconRemarkupRule' => 'applications/macro/markup/PhabricatorIconRemarkupRule.php',
'PhabricatorIconSet' => 'applications/files/iconset/PhabricatorIconSet.php',
@@ -3654,7 +3638,6 @@
'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
'PhabricatorPronounSetting' => 'applications/settings/setting/PhabricatorPronounSetting.php',
- 'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php',
'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php',
@@ -3978,7 +3961,6 @@
'PhabricatorStoragePatch' => 'infrastructure/storage/management/PhabricatorStoragePatch.php',
'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
- 'PhabricatorStreamingProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php',
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
'PhabricatorSubmitEditField' => 'applications/transactions/editfield/PhabricatorSubmitEditField.php',
@@ -7150,20 +7132,6 @@
'PhabricatorBoardRenderingEngine' => 'Phobject',
'PhabricatorBoardResponseEngine' => 'Phobject',
'PhabricatorBoolEditField' => 'PhabricatorEditField',
- 'PhabricatorBot' => 'PhabricatorDaemon',
- 'PhabricatorBotChannel' => 'PhabricatorBotTarget',
- 'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
- 'PhabricatorBotFeedNotificationHandler' => 'PhabricatorBotHandler',
- 'PhabricatorBotFlowdockProtocolAdapter' => 'PhabricatorStreamingProtocolAdapter',
- 'PhabricatorBotHandler' => 'Phobject',
- 'PhabricatorBotLogHandler' => 'PhabricatorBotHandler',
- 'PhabricatorBotMacroHandler' => 'PhabricatorBotHandler',
- 'PhabricatorBotMessage' => 'Phobject',
- 'PhabricatorBotObjectNameHandler' => 'PhabricatorBotHandler',
- 'PhabricatorBotSymbolHandler' => 'PhabricatorBotHandler',
- 'PhabricatorBotTarget' => 'Phobject',
- 'PhabricatorBotUser' => 'PhabricatorBotTarget',
- 'PhabricatorBotWhatsNewHandler' => 'PhabricatorBotHandler',
'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation',
'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine',
'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList',
@@ -7358,7 +7326,6 @@
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
- 'PhabricatorCampfireProtocolAdapter' => 'PhabricatorStreamingProtocolAdapter',
'PhabricatorCelerityApplication' => 'PhabricatorApplication',
'PhabricatorCelerityTestCase' => 'PhabricatorTestCase',
'PhabricatorChangeParserTestCase' => 'PhabricatorWorkingCopyTestCase',
@@ -8115,7 +8082,6 @@
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorIDsSearchField' => 'PhabricatorSearchField',
- 'PhabricatorIRCProtocolAdapter' => 'PhabricatorProtocolAdapter',
'PhabricatorIconDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorIconRemarkupRule' => 'PhutilRemarkupRule',
'PhabricatorIconSet' => 'Phobject',
@@ -8973,7 +8939,6 @@
'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
- 'PhabricatorProtocolAdapter' => 'Phobject',
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
'PhabricatorQuery' => 'Phobject',
'PhabricatorQueryConstraint' => 'Phobject',
@@ -9377,7 +9342,6 @@
'PhabricatorStoragePatch' => 'Phobject',
'PhabricatorStorageSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck',
- 'PhabricatorStreamingProtocolAdapter' => 'PhabricatorProtocolAdapter',
'PhabricatorStringListEditField' => 'PhabricatorEditField',
'PhabricatorStringSetting' => 'PhabricatorSetting',
'PhabricatorSubmitEditField' => 'PhabricatorEditField',
diff --git a/src/docs/tech/chatbot.diviner b/src/docs/tech/chatbot.diviner
deleted file mode 100644
--- a/src/docs/tech/chatbot.diviner
+++ /dev/null
@@ -1,87 +0,0 @@
-@title Chat Bot Technical Documentation
-@group bot
-
-Configuring and extending the chat bot.
-
-= Overview =
-
-Phabricator includes a simple chat bot daemon, which is primarily intended as
-an example of how you can write an external script that interfaces with
-Phabricator over Conduit and does some kind of useful work. If you use IRC or
-another supported chat protocol, you can also have the bot hang out in your
-channel.
-
-NOTE: The chat bot is somewhat experimental and not very mature.
-
-= Configuring the Bot =
-
-The bot reads a JSON configuration file. You can find an example in:
-
- resources/chatbot/example_config.json
-
-These are the configuration values it reads:
-
- - `server` String, required, the server to connect to.
- - `port` Int, optional, the port to connect to (defaults to 6667).
- - `ssl` Bool, optional, whether to connect via SSL or not (defaults to
- false).
- - `nick` String, nickname to use.
- - `user` String, optional, username to use (defaults to `nick`).
- - `pass` String, optional, password for server.
- - `nickpass` String, optional, password for NickServ.
- - `join` Array, list of channels to join.
- - `handlers` Array, list of handlers to run. These are like plugins for the
- bot.
- - `conduit.uri`, `conduit.token` Conduit configuration,
- see below.
- - `notification.channels` Notification configuration, see below.
-
-= Handlers =
-
-You specify a list of "handlers", which are basically plugins or modules for
-the bot. These are the default handlers available:
-
- - @{class:PhabricatorBotObjectNameHandler} This handler looks for users
- mentioning Phabricator objects like "T123" and "D345" in chat, looks them
- up, and says their name with a link to the object. Requires conduit.
- - @{class:PhabricatorBotFeedNotificationHandler} This handler posts
- notifications about changes to revisions to the channels listed in
- `notification.channels`.
- - @{class:PhabricatorBotLogHandler} This handler records chatlogs which can
- be browsed in the Phabricator web interface.
- - @{class:PhabricatorBotSymbolHandler} This handler posts responses to lookups
- for symbols in Diffusion
- - @{class:PhabricatorBotMacroHandler} This handler looks for users mentioning
- macros, if found will convert image to ASCII and output in chat. Configure
- with `macro.size` and `macro.aspect`
-
-You can also write your own handlers, by extending
-@{class:PhabricatorBotHandler}.
-
-= Conduit =
-
-Some handlers (e.g., @{class:PhabricatorBotObjectNameHandler}) need to read data
-from Phabricator over Conduit, Phabricator's HTTP API. You can use this method
-to allow other scripts or programs to access Phabricator's data from different
-servers and in different languages.
-
-To allow the bot to access Conduit, you need to create a user that it can login
-with. To do this, login to Phabricator as an administrator and go to
-`People -> Create New Account`. Create a new account and flag them as a
-"Bot/Script". Then in your configuration file, set these parameters:
-
- - `conduit.uri` The URI for your Phabricator install, like
- `http://phabricator.example.com/`
- - `conduit.token` The user's conduit API token, from the "Conduit API Tokens"
- tab in the user's administrative view.
-
-Now the bot should be able to connect to Phabricator via Conduit.
-
-= Starting the Bot =
-
-The bot is a Phabricator daemon, so start it with `phd`:
-
- ./bin/phd launch phabricatorbot <absolute_path_to_config_file>
-
-If you have issues you can try `debug` instead of `launch`, see
-@{article:Managing Daemons with phd} for more information.
diff --git a/src/infrastructure/daemon/bot/PhabricatorBot.php b/src/infrastructure/daemon/bot/PhabricatorBot.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/PhabricatorBot.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-/**
- * Simple IRC bot which runs as a Phabricator daemon. Although this bot is
- * somewhat useful, it is also intended to serve as a demo of how to write
- * "system agents" which communicate with Phabricator over Conduit, so you can
- * script system interactions and integrate with other systems.
- *
- * NOTE: This is super janky and experimental right now.
- */
-final class PhabricatorBot extends PhabricatorDaemon {
-
- private $handlers;
-
- private $conduit;
- private $config;
- private $pollFrequency;
- private $protocolAdapter;
-
- protected function run() {
- $argv = $this->getArgv();
- if (count($argv) !== 1) {
- throw new Exception(
- pht(
- 'Usage: %s %s',
- __CLASS__,
- '<json_config_file>'));
- }
-
- $json_raw = Filesystem::readFile($argv[0]);
- try {
- $config = phutil_json_decode($json_raw);
- } catch (PhutilJSONParserException $ex) {
- throw new PhutilProxyException(
- pht("File '%s' is not valid JSON!", $argv[0]),
- $ex);
- }
-
- $nick = idx($config, 'nick', 'phabot');
- $handlers = idx($config, 'handlers', array());
- $protocol_adapter_class = idx(
- $config,
- 'protocol-adapter',
- 'PhabricatorIRCProtocolAdapter');
- $this->pollFrequency = idx($config, 'poll-frequency', 1);
-
- $this->config = $config;
-
- foreach ($handlers as $handler) {
- $obj = newv($handler, array($this));
- $this->handlers[] = $obj;
- }
-
- $ca_bundle = idx($config, 'https.cabundle');
- if ($ca_bundle) {
- HTTPSFuture::setGlobalCABundleFromPath($ca_bundle);
- }
-
- $conduit_uri = idx($config, 'conduit.uri');
- if ($conduit_uri) {
- $conduit_token = idx($config, 'conduit.token');
-
- // Normalize the path component of the URI so users can enter the
- // domain without the "/api/" part.
- $conduit_uri = new PhutilURI($conduit_uri);
-
- $conduit_host = (string)$conduit_uri->setPath('/');
- $conduit_uri = (string)$conduit_uri->setPath('/api/');
-
- $conduit = new ConduitClient($conduit_uri);
- if ($conduit_token) {
- $conduit->setConduitToken($conduit_token);
- } else {
- $conduit_user = idx($config, 'conduit.user');
- $conduit_cert = idx($config, 'conduit.cert');
-
- $response = $conduit->callMethodSynchronous(
- 'conduit.connect',
- array(
- 'client' => __CLASS__,
- 'clientVersion' => '1.0',
- 'clientDescription' => php_uname('n').':'.$nick,
- 'host' => $conduit_host,
- 'user' => $conduit_user,
- 'certificate' => $conduit_cert,
- ));
- }
-
- $this->conduit = $conduit;
- }
-
- // Instantiate Protocol Adapter, for now follow same technique as
- // handler instantiation
- $this->protocolAdapter = newv($protocol_adapter_class, array());
- $this->protocolAdapter
- ->setConfig($this->config)
- ->connect();
-
- $this->runLoop();
-
- $this->protocolAdapter->disconnect();
- }
-
- public function getConfig($key, $default = null) {
- return idx($this->config, $key, $default);
- }
-
- private function runLoop() {
- do {
- PhabricatorCaches::destroyRequestCache();
-
- $this->stillWorking();
-
- $messages = $this->protocolAdapter->getNextMessages($this->pollFrequency);
- if (count($messages) > 0) {
- foreach ($messages as $message) {
- $this->routeMessage($message);
- }
- }
-
- foreach ($this->handlers as $handler) {
- $handler->runBackgroundTasks();
- }
- } while (!$this->shouldExit());
-
- }
-
- public function writeMessage(PhabricatorBotMessage $message) {
- return $this->protocolAdapter->writeMessage($message);
- }
-
- private function routeMessage(PhabricatorBotMessage $message) {
- $ignore = $this->getConfig('ignore');
- if ($ignore) {
- $sender = $message->getSender();
- if ($sender && in_array($sender->getName(), $ignore)) {
- return;
- }
- }
-
- if ($message->getCommand() == 'LOG') {
- $this->log('[LOG] '.$message->getBody());
- }
-
- foreach ($this->handlers as $handler) {
- try {
- $handler->receiveMessage($message);
- } catch (Exception $ex) {
- phlog($ex);
- }
- }
- }
-
- public function getAdapter() {
- return $this->protocolAdapter;
- }
-
- public function getConduit() {
- if (empty($this->conduit)) {
- throw new Exception(
- pht(
- "This bot is not configured with a Conduit uplink. Set '%s' and ".
- "'%s' in the configuration to connect.",
- 'conduit.uri',
- 'conduit.token'));
- }
- return $this->conduit;
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/PhabricatorBotMessage.php b/src/infrastructure/daemon/bot/PhabricatorBotMessage.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/PhabricatorBotMessage.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-final class PhabricatorBotMessage extends Phobject {
-
- private $sender;
- private $command;
- private $body;
- private $target;
- private $public;
-
- public function __construct() {
- // By default messages are public
- $this->public = true;
- }
-
- public function setSender(PhabricatorBotTarget $sender = null) {
- $this->sender = $sender;
- return $this;
- }
-
- public function getSender() {
- return $this->sender;
- }
-
- public function setCommand($command) {
- $this->command = $command;
- return $this;
- }
-
- public function getCommand() {
- return $this->command;
- }
-
- public function setBody($body) {
- $this->body = $body;
- return $this;
- }
-
- public function getBody() {
- return $this->body;
- }
-
- public function setTarget(PhabricatorBotTarget $target = null) {
- $this->target = $target;
- return $this;
- }
-
- public function getTarget() {
- return $this->target;
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorBotFlowdockProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorBotFlowdockProtocolAdapter.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/adapter/PhabricatorBotFlowdockProtocolAdapter.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-final class PhabricatorBotFlowdockProtocolAdapter
- extends PhabricatorStreamingProtocolAdapter {
-
- public function getServiceType() {
- return 'Flowdock';
- }
-
- protected function buildStreamingUrl($channel) {
- $organization = $this->getConfig('flowdock.organization');
- if (empty($organization)) {
- $this->getConfig('organization');
- }
- if (empty($organization)) {
- throw new Exception(
- '"flowdock.organization" configuration variable not set');
- }
-
-
- $ssl = $this->getConfig('ssl');
-
- $url = ($ssl) ? 'https://' : 'http://';
- $url .= "{$this->authtoken}@stream.flowdock.com";
- $url .= "/flows/{$organization}/{$channel}";
- return $url;
- }
-
- protected function processMessage(array $m_obj) {
- $command = null;
- switch ($m_obj['event']) {
- case 'message':
- $command = 'MESSAGE';
- break;
- default:
- // For now, ignore anything which we don't otherwise know about.
- break;
- }
-
- if ($command === null) {
- return false;
- }
-
- // TODO: These should be usernames, not user IDs.
- $sender = id(new PhabricatorBotUser())
- ->setName($m_obj['user']);
-
- $target = id(new PhabricatorBotChannel())
- ->setName($m_obj['flow']);
-
- return id(new PhabricatorBotMessage())
- ->setCommand($command)
- ->setSender($sender)
- ->setTarget($target)
- ->setBody($m_obj['content']);
- }
-
- public function writeMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'MESSAGE':
- $this->speak(
- $message->getBody(),
- $message->getTarget());
- break;
- }
- }
-
- private function speak(
- $body,
- PhabricatorBotTarget $flow) {
- // The $flow->getName() returns the flow's UUID,
- // as such, the Flowdock API does not require the organization
- // to be specified in the URI
- $this->performPost(
- '/messages',
- array(
- 'flow' => $flow->getName(),
- 'event' => 'message',
- 'content' => $body,
- ));
- }
-
- public function __destruct() {
- if ($this->readHandles) {
- foreach ($this->readHandles as $read_handle) {
- curl_multi_remove_handle($this->multiHandle, $read_handle);
- curl_close($read_handle);
- }
- }
-
- curl_multi_close($this->multiHandle);
- }
-}
diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-
-final class PhabricatorCampfireProtocolAdapter
- extends PhabricatorStreamingProtocolAdapter {
-
- public function getServiceType() {
- return 'Campfire';
- }
-
- protected function buildStreamingUrl($channel) {
- $ssl = $this->getConfig('ssl');
-
- $url = ($ssl) ? 'https://' : 'http://';
- $url .= "streaming.campfirenow.com/room/{$channel}/live.json";
-
- return $url;
- }
-
- protected function processMessage(array $m_obj) {
- $command = null;
- switch ($m_obj['type']) {
- case 'TextMessage':
- $command = 'MESSAGE';
- break;
- case 'PasteMessage':
- $command = 'PASTE';
- break;
- default:
- // For now, ignore anything which we don't otherwise know about.
- break;
- }
-
- if ($command === null) {
- return false;
- }
-
- // TODO: These should be usernames, not user IDs.
- $sender = id(new PhabricatorBotUser())
- ->setName($m_obj['user_id']);
-
- $target = id(new PhabricatorBotChannel())
- ->setName($m_obj['room_id']);
-
- return id(new PhabricatorBotMessage())
- ->setCommand($command)
- ->setSender($sender)
- ->setTarget($target)
- ->setBody($m_obj['body']);
- }
-
- public function writeMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'MESSAGE':
- $this->speak(
- $message->getBody(),
- $message->getTarget());
- break;
- case 'SOUND':
- $this->speak(
- $message->getBody(),
- $message->getTarget(),
- 'SoundMessage');
- break;
- case 'PASTE':
- $this->speak(
- $message->getBody(),
- $message->getTarget(),
- 'PasteMessage');
- break;
- }
- }
-
- protected function joinRoom($room_id) {
- $this->performPost("/room/{$room_id}/join.json");
- $this->inRooms[$room_id] = true;
- }
-
- private function leaveRoom($room_id) {
- $this->performPost("/room/{$room_id}/leave.json");
- unset($this->inRooms[$room_id]);
- }
-
- private function speak(
- $message,
- PhabricatorBotTarget $channel,
- $type = 'TextMessage') {
-
- $room_id = $channel->getName();
-
- $this->performPost(
- "/room/{$room_id}/speak.json",
- array(
- 'message' => array(
- 'type' => $type,
- 'body' => $message,
- ),
- ));
- }
-
- public function __destruct() {
- foreach ($this->inRooms as $room_id => $ignored) {
- $this->leaveRoom($room_id);
- }
-
- if ($this->readHandles) {
- foreach ($this->readHandles as $read_handle) {
- curl_multi_remove_handle($this->multiHandle, $read_handle);
- curl_close($read_handle);
- }
- }
-
- curl_multi_close($this->multiHandle);
- }
-}
diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php
+++ /dev/null
@@ -1,282 +0,0 @@
-<?php
-
-final class PhabricatorIRCProtocolAdapter extends PhabricatorProtocolAdapter {
-
- private $socket;
-
- private $writeBuffer;
- private $readBuffer;
-
- private $nickIncrement = 0;
-
- public function getServiceType() {
- return 'IRC';
- }
-
- public function getServiceName() {
- return $this->getConfig('network', $this->getConfig('server'));
- }
-
- // Hash map of command translations
- public static $commandTranslations = array(
- 'PRIVMSG' => 'MESSAGE',
- );
-
- public function connect() {
- $nick = $this->getConfig('nick', 'phabot');
- $server = $this->getConfig('server');
- $port = $this->getConfig('port', 6667);
- $pass = $this->getConfig('pass');
- $ssl = $this->getConfig('ssl', false);
- $user = $this->getConfig('user', $nick);
-
- if (!preg_match('/^[A-Za-z0-9_`[{}^|\]\\-]+$/', $nick)) {
- throw new Exception(
- pht(
- "Nickname '%s' is invalid!",
- $nick));
- }
-
- $errno = null;
- $error = null;
- if (!$ssl) {
- $socket = fsockopen($server, $port, $errno, $error);
- } else {
- $socket = fsockopen('ssl://'.$server, $port, $errno, $error);
- }
- if (!$socket) {
- throw new Exception(pht('Failed to connect, #%d: %s', $errno, $error));
- }
- $ok = stream_set_blocking($socket, false);
- if (!$ok) {
- throw new Exception(pht('Failed to set stream nonblocking.'));
- }
-
- $this->socket = $socket;
- if ($pass) {
- $this->write("PASS {$pass}");
- }
- $this->write("NICK {$nick}");
- $this->write("USER {$user} 0 * :{$user}");
- }
-
- public function getNextMessages($poll_frequency) {
- $messages = array();
-
- $read = array($this->socket);
- if (strlen($this->writeBuffer)) {
- $write = array($this->socket);
- } else {
- $write = array();
- }
- $except = array();
-
- $ok = @stream_select($read, $write, $except, $timeout_sec = 1);
- if ($ok === false) {
- // We may have been interrupted by a signal, like a SIGINT. Try
- // selecting again. If the second select works, conclude that the failure
- // was most likely because we were signaled.
- $ok = @stream_select($read, $write, $except, $timeout_sec = 0);
- if ($ok === false) {
- throw new Exception(pht('%s failed!', 'stream_select()'));
- }
- }
-
- if ($read) {
- // Test for connection termination; in PHP, fread() off a nonblocking,
- // closed socket is empty string.
- if (feof($this->socket)) {
- // This indicates the connection was terminated on the other side,
- // just exit via exception and let the overseer restart us after a
- // delay so we can reconnect.
- throw new Exception(pht('Remote host closed connection.'));
- }
- do {
- $data = fread($this->socket, 4096);
- if ($data === false) {
- throw new Exception(pht('%s failed!', 'fread()'));
- } else {
- $messages[] = id(new PhabricatorBotMessage())
- ->setCommand('LOG')
- ->setBody('>>> '.$data);
- $this->readBuffer .= $data;
- }
- } while (strlen($data));
- }
-
- if ($write) {
- do {
- $len = fwrite($this->socket, $this->writeBuffer);
- if ($len === false) {
- throw new Exception(pht('%s failed!', 'fwrite()'));
- } else if ($len === 0) {
- break;
- } else {
- $messages[] = id(new PhabricatorBotMessage())
- ->setCommand('LOG')
- ->setBody('>>> '.substr($this->writeBuffer, 0, $len));
- $this->writeBuffer = substr($this->writeBuffer, $len);
- }
- } while (strlen($this->writeBuffer));
- }
-
- while (($m = $this->processReadBuffer()) !== false) {
- if ($m !== null) {
- $messages[] = $m;
- }
- }
-
- return $messages;
- }
-
- private function write($message) {
- $this->writeBuffer .= $message."\r\n";
- return $this;
- }
-
- public function writeMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'MESSAGE':
- case 'PASTE':
- $name = $message->getTarget()->getName();
- $body = $message->getBody();
- $this->write("PRIVMSG {$name} :{$body}");
- return true;
- default:
- return false;
- }
- }
-
- private function processReadBuffer() {
- $until = strpos($this->readBuffer, "\r\n");
- if ($until === false) {
- return false;
- }
-
- $message = substr($this->readBuffer, 0, $until);
- $this->readBuffer = substr($this->readBuffer, $until + 2);
-
- $pattern =
- '/^'.
- '(?::(?P<sender>(\S+?))(?:!\S*)? )?'. // This may not be present.
- '(?P<command>[A-Z0-9]+) '.
- '(?P<data>.*)'.
- '$/';
-
- $matches = null;
- if (!preg_match($pattern, $message, $matches)) {
- throw new Exception("Unexpected message from server: {$message}");
- }
-
- if ($this->handleIRCProtocol($matches)) {
- return null;
- }
-
- $command = $this->getBotCommand($matches['command']);
- list($target, $body) = $this->parseMessageData($command, $matches['data']);
-
- if (!strlen($matches['sender'])) {
- $sender = null;
- } else {
- $sender = id(new PhabricatorBotUser())
- ->setName($matches['sender']);
- }
-
- $bot_message = id(new PhabricatorBotMessage())
- ->setSender($sender)
- ->setCommand($command)
- ->setTarget($target)
- ->setBody($body);
-
- return $bot_message;
- }
-
- private function handleIRCProtocol(array $matches) {
- $data = $matches['data'];
- switch ($matches['command']) {
- case '433': // Nickname already in use
- // If we receive this error, try appending "-1", "-2", etc. to the nick
- $this->nickIncrement++;
- $nick = $this->getConfig('nick', 'phabot').'-'.$this->nickIncrement;
- $this->write("NICK {$nick}");
- return true;
- case '422': // Error - no MOTD
- case '376': // End of MOTD
- $nickpass = $this->getConfig('nickpass');
- if ($nickpass) {
- $this->write("PRIVMSG nickserv :IDENTIFY {$nickpass}");
- }
- $join = $this->getConfig('join');
- if (!$join) {
- throw new Exception(pht('Not configured to join any channels!'));
- }
- foreach ($join as $channel) {
- $this->write("JOIN {$channel}");
- }
- return true;
- case 'PING':
- $this->write("PONG {$data}");
- return true;
- }
-
- return false;
- }
-
- private function getBotCommand($irc_command) {
- if (isset(self::$commandTranslations[$irc_command])) {
- return self::$commandTranslations[$irc_command];
- }
-
- // We have no translation for this command, use as-is
- return $irc_command;
- }
-
- private function parseMessageData($command, $data) {
- switch ($command) {
- case 'MESSAGE':
- $matches = null;
- if (preg_match('/^(\S+)\s+:?(.*)$/', $data, $matches)) {
-
- $target_name = $matches[1];
- if (strncmp($target_name, '#', 1) === 0) {
- $target = id(new PhabricatorBotChannel())
- ->setName($target_name);
- } else {
- $target = id(new PhabricatorBotUser())
- ->setName($target_name);
- }
-
- return array(
- $target,
- rtrim($matches[2], "\r\n"),
- );
- }
- break;
- }
-
- // By default we assume there is no target, only a body
- return array(
- null,
- $data,
- );
- }
-
- public function disconnect() {
- // NOTE: FreeNode doesn't show quit messages if you've recently joined a
- // channel, presumably to prevent some kind of abuse. If you're testing
- // this, you may need to stay connected to the network for a few minutes
- // before it works. If you disconnect too quickly, the server will replace
- // your message with a "Client Quit" message.
-
- $quit = $this->getConfig('quit', pht('Shutting down.'));
- $this->write("QUIT :{$quit}");
-
- // Flush the write buffer.
- while (strlen($this->writeBuffer)) {
- $this->getNextMessages(0);
- }
-
- @fclose($this->socket);
- $this->socket = null;
- }
-}
diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * Defines the api for protocol adapters for @{class:PhabricatorBot}
- */
-abstract class PhabricatorProtocolAdapter extends Phobject {
-
- private $config;
-
- public function setConfig($config) {
- $this->config = $config;
- return $this;
- }
-
- public function getConfig($key, $default = null) {
- return idx($this->config, $key, $default);
- }
-
- /**
- * Performs any connection logic necessary for the protocol
- */
- abstract public function connect();
-
- /**
- * Disconnect from the service.
- */
- public function disconnect() {
- return;
- }
-
- /**
- * This is the spout for messages coming in from the protocol.
- * This will be called in the main event loop of the bot daemon
- * So if if doesn't implement some sort of blocking timeout
- * (e.g. select-based socket polling), it should at least sleep
- * for some period of time in order to not overwhelm the processor.
- *
- * @param Int $poll_frequency The number of seconds between polls
- */
- abstract public function getNextMessages($poll_frequency);
-
- /**
- * This is the output mechanism for the protocol.
- *
- * @param PhabricatorBotMessage $message The message to write
- */
- abstract public function writeMessage(PhabricatorBotMessage $message);
-
- /**
- * String identifying the service type the adapter provides access to, like
- * "irc", "campfire", "flowdock", "hipchat", etc.
- */
- abstract public function getServiceType();
-
- /**
- * String identifying the service name the adapter is connecting to. This is
- * used to distinguish between instances of a service. For example, for IRC,
- * this should return the IRC network the client is connecting to.
- */
- abstract public function getServiceName();
-
-}
diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-abstract class PhabricatorStreamingProtocolAdapter
- extends PhabricatorProtocolAdapter {
-
- protected $readHandles;
- protected $multiHandle;
- protected $authtoken;
- protected $inRooms = array();
-
- private $readBuffers;
- private $server;
- private $active;
-
- public function getServiceName() {
- $uri = new PhutilURI($this->server);
- return $uri->getDomain();
- }
-
- public function connect() {
- $this->server = $this->getConfig('server');
- $this->authtoken = $this->getConfig('authtoken');
- $rooms = $this->getConfig('join');
-
- // First, join the room
- if (!$rooms) {
- throw new Exception(pht('Not configured to join any rooms!'));
- }
-
- $this->readBuffers = array();
-
- // Set up our long poll in a curl multi request so we can
- // continue running while it executes in the background
- $this->multiHandle = curl_multi_init();
- $this->readHandles = array();
-
- foreach ($rooms as $room_id) {
- $this->joinRoom($room_id);
-
- // Set up the curl stream for reading
- $url = $this->buildStreamingUrl($room_id);
- $ch = $this->readHandles[$url] = curl_init();
-
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt(
- $ch,
- CURLOPT_USERPWD,
- $this->authtoken.':x');
-
- curl_setopt(
- $ch,
- CURLOPT_HTTPHEADER,
- array('Content-type: application/json'));
- curl_setopt(
- $ch,
- CURLOPT_WRITEFUNCTION,
- array($this, 'read'));
- curl_setopt($ch, CURLOPT_BUFFERSIZE, 128);
- curl_setopt($ch, CURLOPT_TIMEOUT, 0);
-
- curl_multi_add_handle($this->multiHandle, $ch);
-
- // Initialize read buffer
- $this->readBuffers[$url] = '';
- }
-
- $this->active = null;
- $this->blockingMultiExec();
- }
-
- protected function joinRoom($room_id) {
- // Optional hook, by default, do nothing
- }
-
- // This is our callback for the background curl multi-request.
- // Puts the data read in on the readBuffer for processing.
- private function read($ch, $data) {
- $info = curl_getinfo($ch);
- $length = strlen($data);
- $this->readBuffers[$info['url']] .= $data;
- return $length;
- }
-
- private function blockingMultiExec() {
- do {
- $status = curl_multi_exec($this->multiHandle, $this->active);
- } while ($status == CURLM_CALL_MULTI_PERFORM);
-
- // Check for errors
- if ($status != CURLM_OK) {
- throw new Exception(
- pht('Phabricator Bot had a problem reading from stream.'));
- }
- }
-
- public function getNextMessages($poll_frequency) {
- $messages = array();
-
- if (!$this->active) {
- throw new Exception(pht('Phabricator Bot stopped reading from stream.'));
- }
-
- // Prod our http request
- curl_multi_select($this->multiHandle, $poll_frequency);
- $this->blockingMultiExec();
-
- // Process anything waiting on the read buffer
- while ($m = $this->processReadBuffer()) {
- $messages[] = $m;
- }
-
- return $messages;
- }
-
- private function processReadBuffer() {
- foreach ($this->readBuffers as $url => &$buffer) {
- $until = strpos($buffer, "}\r");
- if ($until == false) {
- continue;
- }
-
- $message = substr($buffer, 0, $until + 1);
- $buffer = substr($buffer, $until + 2);
-
- $m_obj = phutil_json_decode($message);
- if ($message = $this->processMessage($m_obj)) {
- return $message;
- }
- }
-
- // If we're here, there's nothing to process
- return false;
- }
-
- protected function performPost($endpoint, $data = null) {
- $uri = new PhutilURI($this->server);
- $uri->setPath($endpoint);
-
- $payload = json_encode($data);
-
- list($output) = id(new HTTPSFuture($uri))
- ->setMethod('POST')
- ->addHeader('Content-Type', 'application/json')
- ->addHeader('Authorization', $this->getAuthorizationHeader())
- ->setData($payload)
- ->resolvex();
-
- $output = trim($output);
- if (strlen($output)) {
- return phutil_json_decode($output);
- }
-
- return true;
- }
-
- protected function getAuthorizationHeader() {
- return 'Basic '.$this->getEncodedAuthToken();
- }
-
- protected function getEncodedAuthToken() {
- return base64_encode($this->authtoken.':x');
- }
-
- abstract protected function buildStreamingUrl($channel);
-
- abstract protected function processMessage(array $raw_object);
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * Logs messages to stdout.
- */
-final class PhabricatorBotDebugLogHandler extends PhabricatorBotHandler {
- public function receiveMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'LOG':
- echo addcslashes(
- $message->getBody(),
- "\0..\37\177..\377");
- echo "\n";
- break;
- }
- }
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php
+++ /dev/null
@@ -1,180 +0,0 @@
-<?php
-
-/**
- * Watches the feed and puts notifications into channel(s) of choice.
- */
-final class PhabricatorBotFeedNotificationHandler
- extends PhabricatorBotHandler {
-
- private $startupDelay = 30;
- private $lastSeenChronoKey = 0;
-
- private $typesNeedURI = array('DREV', 'TASK');
-
- private function shouldShowStory($story) {
- $story_objectphid = $story['objectPHID'];
- $story_text = $story['text'];
-
- $show = $this->getConfig('notification.types');
-
- if ($show) {
- $obj_type = phid_get_type($story_objectphid);
- if (!in_array(strtolower($obj_type), $show)) {
- return false;
- }
- }
-
- $verbosity = $this->getConfig('notification.verbosity', 3);
-
- $verbs = array();
-
- switch ($verbosity) {
- case 2:
- $verbs[] = array(
- 'commented',
- 'added',
- 'changed',
- 'resigned',
- 'explained',
- 'modified',
- 'attached',
- 'edited',
- 'joined',
- 'left',
- 'removed',
- );
- // fallthrough
- case 1:
- $verbs[] = array(
- 'updated',
- 'accepted',
- 'requested',
- 'planned',
- 'claimed',
- 'summarized',
- 'commandeered',
- 'assigned',
- );
- // fallthrough
- case 0:
- $verbs[] = array(
- 'created',
- 'closed',
- 'raised',
- 'committed',
- 'abandoned',
- 'reclaimed',
- 'reopened',
- 'deleted',
- );
- break;
-
- case 3:
- default:
- return true;
- }
-
- $verbs = '/('.implode('|', array_mergev($verbs)).')/';
-
- if (preg_match($verbs, $story_text)) {
- return true;
- }
-
- return false;
- }
-
- public function receiveMessage(PhabricatorBotMessage $message) {
- return;
- }
-
- public function runBackgroundTasks() {
- if ($this->startupDelay > 0) {
- // the event loop runs every 1s so delay enough to fully conenct
- $this->startupDelay--;
-
- return;
- }
- if ($this->lastSeenChronoKey == 0) {
- // Since we only want to post notifications about new stories, skip
- // everything that's happened in the past when we start up so we'll
- // only process real-time stories.
- $latest = $this->getConduit()->callMethodSynchronous(
- 'feed.query',
- array(
- 'limit' => 1,
- ));
-
- foreach ($latest as $story) {
- if ($story['chronologicalKey'] > $this->lastSeenChronoKey) {
- $this->lastSeenChronoKey = $story['chronologicalKey'];
- }
- }
-
- return;
- }
-
- $config_max_pages = $this->getConfig('notification.max_pages', 5);
- $config_page_size = $this->getConfig('notification.page_size', 10);
-
- $last_seen_chrono_key = $this->lastSeenChronoKey;
- $chrono_key_cursor = 0;
-
- // Not efficient but works due to feed.query API
- for ($max_pages = $config_max_pages; $max_pages > 0; $max_pages--) {
- $stories = $this->getConduit()->callMethodSynchronous(
- 'feed.query',
- array(
- 'limit' => $config_page_size,
- 'after' => $chrono_key_cursor,
- 'view' => 'text',
- ));
-
- foreach ($stories as $story) {
- if ($story['chronologicalKey'] == $last_seen_chrono_key) {
- // Caught up on feed
- return;
- }
- if ($story['chronologicalKey'] > $this->lastSeenChronoKey) {
- // Keep track of newest seen story
- $this->lastSeenChronoKey = $story['chronologicalKey'];
- }
- if (!$chrono_key_cursor ||
- $story['chronologicalKey'] < $chrono_key_cursor) {
- // Keep track of oldest story on this page
- $chrono_key_cursor = $story['chronologicalKey'];
- }
-
- if (!$story['text'] ||
- !$this->shouldShowStory($story)) {
- continue;
- }
-
- $message = $story['text'];
-
- $story_object_type = phid_get_type($story['objectPHID']);
- if (in_array($story_object_type, $this->typesNeedURI)) {
- $objects = $this->getConduit()->callMethodSynchronous(
- 'phid.lookup',
- array(
- 'names' => array($story['objectPHID']),
- ));
- $message .= ' '.$objects[$story['objectPHID']]['uri'];
- }
-
- $channels = $this->getConfig('join');
- foreach ($channels as $channel_name) {
-
- $channel = id(new PhabricatorBotChannel())
- ->setName($channel_name);
-
- $this->writeMessage(
- id(new PhabricatorBotMessage())
- ->setCommand('MESSAGE')
- ->setTarget($channel)
- ->setBody($message));
- }
- }
- }
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotHandler.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-/**
- * Responds to IRC messages. You plug a bunch of these into a
- * @{class:PhabricatorBot} to give it special behavior.
- */
-abstract class PhabricatorBotHandler extends Phobject {
-
- private $bot;
-
- final public function __construct(PhabricatorBot $irc_bot) {
- $this->bot = $irc_bot;
- }
-
- final protected function writeMessage(PhabricatorBotMessage $message) {
- $this->bot->writeMessage($message);
- return $this;
- }
-
- final protected function getConduit() {
- return $this->bot->getConduit();
- }
-
- final protected function getConfig($key, $default = null) {
- return $this->bot->getConfig($key, $default);
- }
-
- final protected function getURI($path) {
- $base_uri = new PhutilURI($this->bot->getConfig('conduit.uri'));
- $base_uri->setPath($path);
- return (string)$base_uri;
- }
-
- final protected function getServiceName() {
- return $this->bot->getAdapter()->getServiceName();
- }
-
- final protected function getServiceType() {
- return $this->bot->getAdapter()->getServiceType();
- }
-
- abstract public function receiveMessage(PhabricatorBotMessage $message);
-
- public function runBackgroundTasks() {
- return;
- }
-
- public function replyTo(PhabricatorBotMessage $original_message, $body) {
- if ($original_message->getCommand() != 'MESSAGE') {
- throw new Exception(
- pht('Handler is trying to reply to something which is not a message!'));
- }
-
- $reply = id(new PhabricatorBotMessage())
- ->setCommand('MESSAGE');
-
- if ($original_message->getTarget()->isPublic()) {
- // This is a public target, like a chatroom. Send the response to the
- // chatroom.
- $reply->setTarget($original_message->getTarget());
- } else {
- // This is a private target, like a private message. Send the response
- // back to the sender (presumably, we are the target).
- $reply->setTarget($original_message->getSender());
- }
-
- $reply->setBody($body);
-
- return $this->writeMessage($reply);
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotLogHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotLogHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotLogHandler.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * Logs chatter.
- */
-final class PhabricatorBotLogHandler extends PhabricatorBotHandler {
-
- private $futures = array();
-
- public function receiveMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'MESSAGE':
- $target = $message->getTarget();
- if (!$target->isPublic()) {
- // Don't log private messages, although maybe we should for debugging?
- break;
- }
-
- $target_name = $target->getName();
-
- $logs = array(
- array(
- 'channel' => $target_name,
- 'type' => 'mesg',
- 'epoch' => time(),
- 'author' => $message->getSender()->getName(),
- 'message' => $message->getBody(),
- 'serviceName' => $this->getServiceName(),
- 'serviceType' => $this->getServiceType(),
- ),
- );
-
- $this->futures[] = $this->getConduit()->callMethod(
- 'chatlog.record',
- array(
- 'logs' => $logs,
- ));
-
- $prompts = array(
- '/where is the (chat)?log\?/i',
- '/where am i\?/i',
- '/what year is (this|it)\?/i',
- );
-
- $tell = false;
- foreach ($prompts as $prompt) {
- if (preg_match($prompt, $message->getBody())) {
- $tell = true;
- break;
- }
- }
-
- if ($tell) {
- $response = $this->getURI(
- '/chatlog/channel/'.phutil_escape_uri($target_name).'/');
-
- $this->replyTo($message, $response);
- }
-
- break;
- }
- }
-
- public function runBackgroundTasks() {
- foreach ($this->futures as $key => $future) {
- try {
- if ($future->isReady()) {
- unset($this->futures[$key]);
- }
- } catch (Exception $ex) {
- unset($this->futures[$key]);
- phlog($ex);
- }
- }
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotMacroHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotMacroHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotMacroHandler.php
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-
-final class PhabricatorBotMacroHandler extends PhabricatorBotHandler {
-
- private $macros;
- private $regexp;
-
- private $next = 0;
-
- private function init() {
- if ($this->macros === false) {
- return false;
- }
-
- if ($this->macros !== null) {
- return true;
- }
-
- $macros = $this->getConduit()->callMethodSynchronous(
- 'macro.query',
- array());
-
- // If we have no macros, cache `false` (meaning "no macros") and return
- // immediately.
- if (!$macros) {
- $this->macros = false;
- return false;
- }
-
- $regexp = array();
- foreach ($macros as $macro_name => $macro) {
- $regexp[] = preg_quote($macro_name, '/');
- }
- $regexp = '/^('.implode('|', $regexp).')\z/';
-
- $this->macros = $macros;
- $this->regexp = $regexp;
-
- return true;
- }
-
- public function receiveMessage(PhabricatorBotMessage $message) {
- if (!$this->init()) {
- return;
- }
-
- switch ($message->getCommand()) {
- case 'MESSAGE':
- $message_body = $message->getBody();
-
- $matches = null;
- if (!preg_match($this->regexp, trim($message_body), $matches)) {
- return;
- }
-
- $macro = $matches[1];
-
- $ascii = idx($this->macros[$macro], 'ascii');
- if ($ascii === false) {
- return;
- }
-
- if (!$ascii) {
- $this->macros[$macro]['ascii'] = $this->rasterize(
- $this->macros[$macro],
- $this->getConfig('macro.size', 48),
- $this->getConfig('macro.aspect', 0.66));
- $ascii = $this->macros[$macro]['ascii'];
- }
-
- if ($ascii === false) {
- // If we failed to rasterize the macro, bail out.
- return;
- }
-
- $target_name = $message->getTarget()->getName();
- foreach ($ascii as $line) {
- $this->replyTo($message, $line);
- }
- break;
- }
- }
-
- public function rasterize($macro, $size, $aspect) {
- try {
- $image = $this->getConduit()->callMethodSynchronous(
- 'file.download',
- array(
- 'phid' => $macro['filePHID'],
- ));
- $image = base64_decode($image);
- } catch (Exception $ex) {
- return false;
- }
-
- if (!$image) {
- return false;
- }
-
- $img = @imagecreatefromstring($image);
- if (!$img) {
- return false;
- }
-
- $sx = imagesx($img);
- $sy = imagesy($img);
-
- if ($sx > $size || $sy > $size) {
- $scale = max($sx, $sy) / $size;
- $dx = floor($sx / $scale);
- $dy = floor($sy / $scale);
- } else {
- $dx = $sx;
- $dy = $sy;
- }
-
- $dy = floor($dy * $aspect);
-
- $dst = imagecreatetruecolor($dx, $dy);
- if (!$dst) {
- return false;
- }
- imagealphablending($dst, false);
-
- $ok = imagecopyresampled(
- $dst, $img,
- 0, 0,
- 0, 0,
- $dx, $dy,
- $sx, $sy);
-
- if (!$ok) {
- return false;
- }
-
- $map = array(
- ' ',
- '.',
- ',',
- ':',
- ';',
- '!',
- '|',
- '*',
- '=',
- '@',
- '$',
- '#',
- );
-
- $lines = array();
-
- for ($ii = 0; $ii < $dy; $ii++) {
- $buf = '';
- for ($jj = 0; $jj < $dx; $jj++) {
- $c = imagecolorat($dst, $jj, $ii);
-
- $a = ($c >> 24) & 0xFF;
- $r = ($c >> 16) & 0xFF;
- $g = ($c >> 8) & 0xFF;
- $b = ($c) & 0xFF;
-
- $luma = (255 - ((0.30 * $r) + (0.59 * $g) + (0.11 * $b))) / 256;
- $luma *= ((127 - $a) / 127);
-
- $char = $map[max(0, floor($luma * count($map)))];
- $buf .= $char;
- }
-
- $lines[] = $buf;
- }
-
- return $lines;
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotObjectNameHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotObjectNameHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotObjectNameHandler.php
+++ /dev/null
@@ -1,206 +0,0 @@
-<?php
-
-/**
- * Looks for Dxxxx, Txxxx and links to them.
- */
-final class PhabricatorBotObjectNameHandler extends PhabricatorBotHandler {
-
- /**
- * Map of PHIDs to the last mention of them (as an epoch timestamp); prevents
- * us from spamming chat when a single object is discussed.
- */
- private $recentlyMentioned = array();
-
- public function receiveMessage(PhabricatorBotMessage $original_message) {
- switch ($original_message->getCommand()) {
- case 'MESSAGE':
- $message = $original_message->getBody();
- $matches = null;
-
- $paste_ids = array();
- $commit_names = array();
- $vote_ids = array();
- $file_ids = array();
- $object_names = array();
- $output = array();
-
- $pattern =
- '@'.
- '(?<![/:#-])(?:^|\b)'.
- '(R2D2)'.
- '(?:\b|$)'.
- '@';
-
- if (preg_match_all($pattern, $message, $matches, PREG_SET_ORDER)) {
- foreach ($matches as $match) {
- switch ($match[1]) {
- case 'R2D2':
- $output[$match[1]] = pht('beep boop bop');
- break;
- }
- }
- }
-
- // Use a negative lookbehind to prevent matching "/D123", "#D123",
- // ":D123", etc.
- $pattern =
- '@'.
- '(?<![/:#-])(?:^|\b)'.
- '([A-Z])(\d+)'.
- '(?:\b|$)'.
- '@';
-
- $regex = trim(
- PhabricatorEnv::getEnvConfig('remarkup.ignored-object-names'));
-
- if (preg_match_all($pattern, $message, $matches, PREG_SET_ORDER)) {
- foreach ($matches as $match) {
- if ($regex && preg_match($regex, $match[0])) {
- continue;
- }
- switch ($match[1]) {
- case 'P':
- $paste_ids[] = $match[2];
- break;
- case 'V':
- $vote_ids[] = $match[2];
- break;
- case 'F':
- $file_ids[] = $match[2];
- break;
- default:
- $name = $match[1].$match[2];
- switch ($name) {
- case 'T1000':
- $output[$name] = pht(
- 'T1000: A mimetic poly-alloy assassin controlled by '.
- 'Skynet');
- break;
- default:
- $object_names[] = $name;
- break;
- }
- break;
- }
- }
- }
-
- $pattern =
- '@'.
- '(?<!/)(?:^|\b)'.
- '(r[A-Z]+)([0-9a-z]{0,40})'.
- '(?:\b|$)'.
- '@';
- if (preg_match_all($pattern, $message, $matches, PREG_SET_ORDER)) {
- foreach ($matches as $match) {
- if ($match[2]) {
- $commit_names[] = $match[1].$match[2];
- } else {
- $object_names[] = $match[1];
- }
- }
- }
-
- if ($object_names) {
- $objects = $this->getConduit()->callMethodSynchronous(
- 'phid.lookup',
- array(
- 'names' => $object_names,
- ));
- foreach ($objects as $object) {
- $output[$object['phid']] = $object['fullName'].' - '.$object['uri'];
- }
- }
-
- if ($vote_ids) {
- foreach ($vote_ids as $vote_id) {
- $vote = $this->getConduit()->callMethodSynchronous(
- 'slowvote.info',
- array(
- 'poll_id' => $vote_id,
- ));
- $output[$vote['phid']] = 'V'.$vote['id'].': '.$vote['question'].
- ' '.pht('Come Vote').' '.$vote['uri'];
- }
- }
-
- if ($file_ids) {
- foreach ($file_ids as $file_id) {
- $file = $this->getConduit()->callMethodSynchronous(
- 'file.info',
- array(
- 'id' => $file_id,
- ));
- $output[$file['phid']] = $file['objectName'].': '.
- $file['uri'].' - '.$file['name'];
- }
- }
-
- if ($paste_ids) {
- foreach ($paste_ids as $paste_id) {
- $paste = $this->getConduit()->callMethodSynchronous(
- 'paste.query',
- array(
- 'ids' => array($paste_id),
- ));
- $paste = head($paste);
-
- $output[$paste['phid']] = 'P'.$paste['id'].': '.$paste['uri'].' - '.
- $paste['title'];
-
- if ($paste['language']) {
- $output[$paste['phid']] .= ' ('.$paste['language'].')';
- }
-
- $user = $this->getConduit()->callMethodSynchronous(
- 'user.query',
- array(
- 'phids' => array($paste['authorPHID']),
- ));
- $user = head($user);
- if ($user) {
- $output[$paste['phid']] .= ' by '.$user['userName'];
- }
- }
- }
-
- if ($commit_names) {
- $commits = $this->getConduit()->callMethodSynchronous(
- 'diffusion.querycommits',
- array(
- 'names' => $commit_names,
- ));
- foreach ($commits['data'] as $commit) {
- $output[$commit['phid']] = $commit['uri'];
- }
- }
-
- foreach ($output as $phid => $description) {
-
- // Don't mention the same object more than once every 10 minutes
- // in public channels, so we avoid spamming the chat over and over
- // again for discussions of a specific revision, for example.
-
- $target_name = $original_message->getTarget()->getName();
- if (empty($this->recentlyMentioned[$target_name])) {
- $this->recentlyMentioned[$target_name] = array();
- }
-
- $quiet_until = idx(
- $this->recentlyMentioned[$target_name],
- $phid,
- 0) + (60 * 10);
-
- if (time() < $quiet_until) {
- // Remain quiet on this channel.
- continue;
- }
-
- $this->recentlyMentioned[$target_name][$phid] = time();
- $this->replyTo($original_message, $description);
- }
- break;
- }
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotSymbolHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotSymbolHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotSymbolHandler.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * Watches for "where is <symbol>?"
- */
-final class PhabricatorBotSymbolHandler extends PhabricatorBotHandler {
-
- public function receiveMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'MESSAGE':
- $text = $message->getBody();
-
- $matches = null;
- if (!preg_match('/where(?: in the world)? is (\S+?)\?/i',
- $text, $matches)) {
- break;
- }
-
- $symbol = $matches[1];
- $results = $this->getConduit()->callMethodSynchronous(
- 'diffusion.findsymbols',
- array(
- 'name' => $symbol,
- ));
-
- $default_uri = $this->getURI('/diffusion/symbol/'.$symbol.'/');
-
- if (count($results) > 1) {
- $response = pht(
- "Multiple symbols named '%s': %s",
- $symbol,
- $default_uri);
- } else if (count($results) == 1) {
- $result = head($results);
- $response =
- $result['type'].' '.
- $result['name'].' '.
- '('.$result['language'].'): '.
- nonempty($result['uri'], $default_uri);
- } else {
- $response = pht("No symbol '%s' found anywhere.", $symbol);
- }
-
- $this->replyTo($message, $response);
-
- break;
- }
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php b/src/infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * Responds to "Whats new?" with some recent feed content.
- */
-final class PhabricatorBotWhatsNewHandler extends PhabricatorBotHandler {
-
- private $floodblock = 0;
-
- public function receiveMessage(PhabricatorBotMessage $message) {
- switch ($message->getCommand()) {
- case 'MESSAGE':
- $message_body = $message->getBody();
- $now = time();
-
- $prompt = '~what( i|\')?s new\?~i';
- if (preg_match($prompt, $message_body)) {
- if ($now < $this->floodblock) {
- return;
- }
- $this->floodblock = $now + 60;
- $this->reportNew($message);
- }
- break;
- }
- }
-
- public function reportNew(PhabricatorBotMessage $message) {
- $latest = $this->getConduit()->callMethodSynchronous(
- 'feed.query',
- array(
- 'limit' => 5,
- 'view' => 'text',
- ));
-
- foreach ($latest as $feed_item) {
- if (isset($feed_item['text'])) {
- $this->replyTo($message, html_entity_decode($feed_item['text']));
- }
- }
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/target/PhabricatorBotChannel.php b/src/infrastructure/daemon/bot/target/PhabricatorBotChannel.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/target/PhabricatorBotChannel.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * Represents a group/public space, like an IRC channel or a Campfire room.
- */
-final class PhabricatorBotChannel extends PhabricatorBotTarget {
-
- public function isPublic() {
- return true;
- }
-
-}
diff --git a/src/infrastructure/daemon/bot/target/PhabricatorBotTarget.php b/src/infrastructure/daemon/bot/target/PhabricatorBotTarget.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/target/PhabricatorBotTarget.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * Represents something which can be the target of messages, like a user or
- * channel.
- */
-abstract class PhabricatorBotTarget extends Phobject {
-
- private $name;
-
- public function setName($name) {
- $this->name = $name;
- return $this;
- }
-
- public function getName() {
- return $this->name;
- }
-
- abstract public function isPublic();
-
-}
diff --git a/src/infrastructure/daemon/bot/target/PhabricatorBotUser.php b/src/infrastructure/daemon/bot/target/PhabricatorBotUser.php
deleted file mode 100644
--- a/src/infrastructure/daemon/bot/target/PhabricatorBotUser.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-/**
- * Represents an individual user.
- */
-final class PhabricatorBotUser extends PhabricatorBotTarget {
-
- public function isPublic() {
- return false;
- }
-
-}

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 17, 11:08 PM (4 d, 16 h ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/j6/rl/jwemklnfaaxuzj4q
Default Alt Text
D17756.id.diff (64 KB)

Event Timeline