mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-17 09:18:44 +01:00
Formalize targets (users and channel) into objects
Summary: Make users/channels/rooms into objects, so we can later sort out stuff like Campfire user IDs, Phabricator vs chat accounts, etc. The only change here is that I removed output buffering from the macro handler. We should move throttling/buffering to adapters instead and have it apply globally. Test Plan: Ran IRC and Campfire bots and interacted with them. Reviewers: indiefan Reviewed By: indiefan CC: aran Differential Revision: https://secure.phabricator.com/D4924
This commit is contained in:
parent
ec306497f5
commit
d5995d574d
12 changed files with 151 additions and 115 deletions
|
@ -707,6 +707,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBaseEnglishTranslation' => 'infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php',
|
||||
'PhabricatorBaseProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorBaseProtocolAdapter.php',
|
||||
'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
|
||||
'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
|
||||
'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
|
||||
'PhabricatorBotDifferentialNotificationHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDifferentialNotificationHandler.php',
|
||||
'PhabricatorBotFeedNotificationHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php',
|
||||
|
@ -716,6 +717,8 @@ phutil_register_library_map(array(
|
|||
'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',
|
||||
'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php',
|
||||
'PhabricatorButtonsExample' => 'applications/uiexample/examples/PhabricatorButtonsExample.php',
|
||||
|
@ -2163,6 +2166,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBarePageView' => 'AphrontPageView',
|
||||
'PhabricatorBaseEnglishTranslation' => 'PhabricatorTranslation',
|
||||
'PhabricatorBot' => 'PhabricatorDaemon',
|
||||
'PhabricatorBotChannel' => 'PhabricatorBotTarget',
|
||||
'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotDifferentialNotificationHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotFeedNotificationHandler' => 'PhabricatorBotHandler',
|
||||
|
@ -2170,6 +2174,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBotMacroHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotObjectNameHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotSymbolHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotUser' => 'PhabricatorBotTarget',
|
||||
'PhabricatorBotWhatsNewHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList',
|
||||
'PhabricatorButtonsExample' => 'PhabricatorUIExample',
|
||||
|
|
|
@ -13,7 +13,7 @@ final class PhabricatorBotMessage {
|
|||
$this->public = true;
|
||||
}
|
||||
|
||||
public function setSender($sender) {
|
||||
public function setSender(PhabricatorBotTarget $sender = null) {
|
||||
$this->sender = $sender;
|
||||
return $this;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ final class PhabricatorBotMessage {
|
|||
return $this->body;
|
||||
}
|
||||
|
||||
public function setTarget($target) {
|
||||
public function setTarget(PhabricatorBotTarget $target = null) {
|
||||
$this->target = $target;
|
||||
return $this;
|
||||
}
|
||||
|
@ -49,20 +49,4 @@ final class PhabricatorBotMessage {
|
|||
return $this->target;
|
||||
}
|
||||
|
||||
public function isPublic() {
|
||||
return $this->public;
|
||||
}
|
||||
|
||||
public function setPublic($is_public) {
|
||||
$this->public = $is_public;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReplyTo() {
|
||||
if ($this->public) {
|
||||
return $this->target;
|
||||
} else {
|
||||
return $this->sender;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,11 +115,34 @@ final class PhabricatorCampfireProtocolAdapter
|
|||
$buffer = substr($buffer, $until + 2);
|
||||
|
||||
$m_obj = json_decode($message, true);
|
||||
$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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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('MESSAGE')
|
||||
->setSender($m_obj['user_id'])
|
||||
->setTarget($m_obj['room_id'])
|
||||
->setCommand($command)
|
||||
->setSender($sender)
|
||||
->setTarget($target)
|
||||
->setBody($m_obj['body']);
|
||||
}
|
||||
|
||||
|
@ -159,7 +182,13 @@ final class PhabricatorCampfireProtocolAdapter
|
|||
unset($this->inRooms[$room_id]);
|
||||
}
|
||||
|
||||
private function speak($message, $room_id, $type='TextMessage') {
|
||||
private function speak(
|
||||
$message,
|
||||
PhabricatorBotTarget $channel,
|
||||
$type = 'TextMessage') {
|
||||
|
||||
$room_id = $channel->getName();
|
||||
|
||||
$this->performPost(
|
||||
"/room/{$room_id}/speak.json",
|
||||
array(
|
||||
|
|
|
@ -128,7 +128,7 @@ final class PhabricatorIRCProtocolAdapter
|
|||
switch ($message->getCommand()) {
|
||||
case 'MESSAGE':
|
||||
$data = $irc_command.' '.
|
||||
$message->getTarget().' :'.
|
||||
$message->getTarget()->getName().' :'.
|
||||
$message->getBody()."\r\n";
|
||||
break;
|
||||
default:
|
||||
|
@ -164,16 +164,19 @@ final class PhabricatorIRCProtocolAdapter
|
|||
$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(idx($matches, 'sender'))
|
||||
->setSender($sender)
|
||||
->setCommand($command)
|
||||
->setTarget($target)
|
||||
->setBody($body);
|
||||
|
||||
if (!empty($target) && strncmp($target, '#', 1) !== 0) {
|
||||
$bot_message->setPublic(false);
|
||||
}
|
||||
|
||||
return $bot_message;
|
||||
}
|
||||
|
||||
|
@ -201,8 +204,18 @@ final class PhabricatorIRCProtocolAdapter
|
|||
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(
|
||||
$matches[1],
|
||||
$target,
|
||||
rtrim($matches[2], "\r\n"));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -48,15 +48,14 @@ abstract class PhabricatorBotHandler {
|
|||
$reply = id(new PhabricatorBotMessage())
|
||||
->setCommand('MESSAGE');
|
||||
|
||||
if ($original_message->isPublic()) {
|
||||
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())
|
||||
->setPublic(false);
|
||||
$reply->setTarget($original_message->getSender());
|
||||
}
|
||||
|
||||
$reply->setBody($body);
|
||||
|
|
|
@ -13,21 +13,20 @@ final class PhabricatorBotLogHandler extends PhabricatorBotHandler {
|
|||
|
||||
switch ($message->getCommand()) {
|
||||
case 'MESSAGE':
|
||||
$reply_to = $message->getReplyTo();
|
||||
if (!$reply_to) {
|
||||
break;
|
||||
}
|
||||
if (!$message->isPublic()) {
|
||||
$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' => $reply_to,
|
||||
'channel' => $target_name,
|
||||
'type' => 'mesg',
|
||||
'epoch' => time(),
|
||||
'author' => $message->getSender(),
|
||||
'author' => $message->getSender()->getName(),
|
||||
'message' => $message->getBody(),
|
||||
),
|
||||
);
|
||||
|
@ -54,7 +53,7 @@ final class PhabricatorBotLogHandler extends PhabricatorBotHandler {
|
|||
|
||||
if ($tell) {
|
||||
$response = $this->getURI(
|
||||
'/chatlog/channel/'.phutil_escape_uri($reply_to).'/');
|
||||
'/chatlog/channel/'.phutil_escape_uri($target_name).'/');
|
||||
|
||||
$this->replyTo($message, $response);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ final class PhabricatorBotMacroHandler extends PhabricatorBotHandler {
|
|||
private $macros;
|
||||
private $regexp;
|
||||
|
||||
private $buffer = array();
|
||||
private $next = 0;
|
||||
|
||||
private function init() {
|
||||
|
@ -46,66 +45,37 @@ final class PhabricatorBotMacroHandler extends PhabricatorBotHandler {
|
|||
}
|
||||
|
||||
switch ($message->getCommand()) {
|
||||
case 'MESSAGE':
|
||||
$reply_to = $message->getReplyTo();
|
||||
if (!$reply_to) {
|
||||
case 'MESSAGE':
|
||||
$message_body = $message->getBody();
|
||||
|
||||
$matches = null;
|
||||
if (!preg_match($this->regexp, $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'];
|
||||
}
|
||||
|
||||
$target_name = $message->getTarget()->getName();
|
||||
foreach ($ascii as $line) {
|
||||
$this->replyTo($message, $line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$message_body = $message->getBody();
|
||||
|
||||
$matches = null;
|
||||
if (!preg_match($this->regexp, $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'];
|
||||
}
|
||||
|
||||
foreach ($ascii as $line) {
|
||||
$this->buffer[$reply_to][] = $line;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function runBackgroundTasks() {
|
||||
if (microtime(true) < $this->next) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->buffer as $channel => $lines) {
|
||||
if (empty($lines)) {
|
||||
unset($this->buffer[$channel]);
|
||||
continue;
|
||||
}
|
||||
foreach ($lines as $key => $line) {
|
||||
$this->writeMessage(
|
||||
id(new PhabricatorBotMessage())
|
||||
->setCommand('MESSAGE')
|
||||
->setTarget($channel)
|
||||
->setBody($line));
|
||||
unset($this->buffer[$channel][$key]);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
$sleep = $this->getConfig('macro.sleep', 0.25);
|
||||
$this->next = microtime(true) + ((mt_rand(75, 150) / 100) * $sleep);
|
||||
}
|
||||
|
||||
public function rasterize($macro, $size, $aspect) {
|
||||
$image = HTTPSFuture::loadContent($macro['uri']);
|
||||
if (!$image) {
|
||||
|
|
|
@ -170,13 +170,13 @@ final class PhabricatorBotObjectNameHandler extends PhabricatorBotHandler {
|
|||
// in public channels, so we avoid spamming the chat over and over
|
||||
// again for discsussions of a specific revision, for example.
|
||||
|
||||
$reply_to = $original_message->getReplyTo();
|
||||
if (empty($this->recentlyMentioned[$reply_to])) {
|
||||
$this->recentlyMentioned[$reply_to] = array();
|
||||
$target_name = $original_message->getTarget()->getName();
|
||||
if (empty($this->recentlyMentioned[$target_name])) {
|
||||
$this->recentlyMentioned[$target_name] = array();
|
||||
}
|
||||
|
||||
$quiet_until = idx(
|
||||
$this->recentlyMentioned[$reply_to],
|
||||
$this->recentlyMentioned[$target_name],
|
||||
$phid,
|
||||
0) + (60 * 10);
|
||||
|
||||
|
@ -185,7 +185,7 @@ final class PhabricatorBotObjectNameHandler extends PhabricatorBotHandler {
|
|||
continue;
|
||||
}
|
||||
|
||||
$this->recentlyMentioned[$reply_to][$phid] = time();
|
||||
$this->recentlyMentioned[$target_name][$phid] = time();
|
||||
$this->replyTo($original_message, $description);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -13,11 +13,6 @@ final class PhabricatorBotWhatsNewHandler extends PhabricatorBotHandler {
|
|||
|
||||
switch ($message->getCommand()) {
|
||||
case 'MESSAGE':
|
||||
$reply_to = $message->getReplyTo();
|
||||
if (!$reply_to) {
|
||||
break;
|
||||
}
|
||||
|
||||
$message_body = $message->getBody();
|
||||
|
||||
$prompt = '~what( i|\')?s new\?~i';
|
||||
|
@ -27,17 +22,17 @@ final class PhabricatorBotWhatsNewHandler extends PhabricatorBotHandler {
|
|||
}
|
||||
$this->floodblock = time() + 60;
|
||||
|
||||
$this->getLatest($reply_to);
|
||||
$this->getLatest($message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function getLatest($reply_to) {
|
||||
public function getLatest(PhabricatorBotMessage $message) {
|
||||
$latest = $this->getConduit()->callMethodSynchronous(
|
||||
'feed.query',
|
||||
array(
|
||||
'limit'=>5
|
||||
'limit' => 5
|
||||
));
|
||||
|
||||
$phids = array();
|
||||
|
@ -112,11 +107,7 @@ final class PhabricatorBotWhatsNewHandler extends PhabricatorBotHandler {
|
|||
// $content = "{$bold}{$user}{$reset} {$gray}{$action} {$blue}{$bold}".
|
||||
// "{$title}{$reset} - {$gray}{$uri}{$reset}";
|
||||
$content = "{$user} {$action} {$title} - {$uri}";
|
||||
$this->writeMessage(
|
||||
id(new PhabricatorBotMessage())
|
||||
->setCommand('MESSAGE')
|
||||
->setTarget($reply_to)
|
||||
->setBody($content));
|
||||
$this->replyTo($message, $content);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Represents a group/public space, like an IRC channel or a Campfire room.
|
||||
*/
|
||||
final class PhabricatorBotChannel extends PhabricatorBotTarget {
|
||||
|
||||
public function isPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Represents something which can be the target of messages, like a user or
|
||||
* channel.
|
||||
*/
|
||||
abstract class PhabricatorBotTarget {
|
||||
|
||||
private $name;
|
||||
|
||||
public function setName($name) {
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
abstract public function isPublic();
|
||||
|
||||
}
|
12
src/infrastructure/daemon/bot/target/PhabricatorBotUser.php
Normal file
12
src/infrastructure/daemon/bot/target/PhabricatorBotUser.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Represents an individual user.
|
||||
*/
|
||||
final class PhabricatorBotUser extends PhabricatorBotTarget {
|
||||
|
||||
public function isPublic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue