Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15460253
D17756.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
64 KB
Referenced Files
None
Subscribers
None
D17756.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 1, 9:17 PM (1 w, 4 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/j6/rl/jwemklnfaaxuzj4q
Default Alt Text
D17756.diff (64 KB)
Attached To
Mode
D17756: Remove all "Phabricator Bot" code
Attached
Detach File
Event Timeline
Log In to Comment