[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/infrastructure/daemon/bot/ -> PhabricatorBot.php (source)

   1  <?php
   2  
   3  /**
   4   * Simple IRC bot which runs as a Phabricator daemon. Although this bot is
   5   * somewhat useful, it is also intended to serve as a demo of how to write
   6   * "system agents" which communicate with Phabricator over Conduit, so you can
   7   * script system interactions and integrate with other systems.
   8   *
   9   * NOTE: This is super janky and experimental right now.
  10   */
  11  final class PhabricatorBot extends PhabricatorDaemon {
  12  
  13    private $handlers;
  14  
  15    private $conduit;
  16    private $config;
  17    private $pollFrequency;
  18  
  19    public function run() {
  20      $argv = $this->getArgv();
  21      if (count($argv) !== 1) {
  22        throw new Exception('usage: PhabricatorBot <json_config_file>');
  23      }
  24  
  25      $json_raw = Filesystem::readFile($argv[0]);
  26      $config = json_decode($json_raw, true);
  27      if (!is_array($config)) {
  28        throw new Exception("File '{$argv[0]}' is not valid JSON!");
  29      }
  30  
  31      $nick                   = idx($config, 'nick', 'phabot');
  32      $handlers               = idx($config, 'handlers', array());
  33      $protocol_adapter_class = idx(
  34        $config,
  35        'protocol-adapter',
  36        'PhabricatorIRCProtocolAdapter');
  37      $this->pollFrequency = idx($config, 'poll-frequency', 1);
  38  
  39      $this->config = $config;
  40  
  41      foreach ($handlers as $handler) {
  42        $obj = newv($handler, array($this));
  43        $this->handlers[] = $obj;
  44      }
  45  
  46      $ca_bundle = idx($config, 'https.cabundle');
  47      if ($ca_bundle) {
  48        HTTPSFuture::setGlobalCABundleFromPath($ca_bundle);
  49      }
  50  
  51      $conduit_uri = idx($config, 'conduit.uri');
  52      if ($conduit_uri) {
  53        $conduit_user = idx($config, 'conduit.user');
  54        $conduit_cert = idx($config, 'conduit.cert');
  55  
  56        // Normalize the path component of the URI so users can enter the
  57        // domain without the "/api/" part.
  58        $conduit_uri = new PhutilURI($conduit_uri);
  59  
  60        $conduit_host = (string)$conduit_uri->setPath('/');
  61        $conduit_uri = (string)$conduit_uri->setPath('/api/');
  62  
  63        $conduit = new ConduitClient($conduit_uri);
  64        $response = $conduit->callMethodSynchronous(
  65          'conduit.connect',
  66          array(
  67            'client'            => 'PhabricatorBot',
  68            'clientVersion'     => '1.0',
  69            'clientDescription' => php_uname('n').':'.$nick,
  70            'host'              => $conduit_host,
  71            'user'              => $conduit_user,
  72            'certificate'       => $conduit_cert,
  73          ));
  74  
  75        $this->conduit = $conduit;
  76      }
  77  
  78      // Instantiate Protocol Adapter, for now follow same technique as
  79      // handler instantiation
  80      $this->protocolAdapter = newv($protocol_adapter_class, array());
  81      $this->protocolAdapter
  82        ->setConfig($this->config)
  83        ->connect();
  84  
  85      $this->runLoop();
  86  
  87      $this->protocolAdapter->disconnect();
  88    }
  89  
  90    public function getConfig($key, $default = null) {
  91      return idx($this->config, $key, $default);
  92    }
  93  
  94    private function runLoop() {
  95      do {
  96        $this->stillWorking();
  97  
  98        $messages = $this->protocolAdapter->getNextMessages($this->pollFrequency);
  99        if (count($messages) > 0) {
 100          foreach ($messages as $message) {
 101            $this->routeMessage($message);
 102          }
 103        }
 104  
 105        foreach ($this->handlers as $handler) {
 106          $handler->runBackgroundTasks();
 107        }
 108      } while (!$this->shouldExit());
 109  
 110    }
 111  
 112    public function writeMessage(PhabricatorBotMessage $message) {
 113      return $this->protocolAdapter->writeMessage($message);
 114    }
 115  
 116    private function routeMessage(PhabricatorBotMessage $message) {
 117      $ignore = $this->getConfig('ignore');
 118      if ($ignore) {
 119        $sender = $message->getSender();
 120        if ($sender && in_array($sender->getName(), $ignore)) {
 121          return;
 122        }
 123      }
 124  
 125      if ($message->getCommand() == 'LOG') {
 126        $this->log('[LOG] '.$message->getBody());
 127      }
 128  
 129      foreach ($this->handlers as $handler) {
 130        try {
 131          $handler->receiveMessage($message);
 132        } catch (Exception $ex) {
 133          phlog($ex);
 134        }
 135      }
 136    }
 137  
 138    public function getAdapter() {
 139      return $this->protocolAdapter;
 140    }
 141  
 142    public function getConduit() {
 143      if (empty($this->conduit)) {
 144        throw new Exception(
 145          "This bot is not configured with a Conduit uplink. Set 'conduit.uri', ".
 146          "'conduit.user' and 'conduit.cert' in the configuration to connect.");
 147      }
 148      return $this->conduit;
 149    }
 150  
 151  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1