diff --git a/.gitignore b/.gitignore index 12c481d..b0def9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.idea/ -vendor/ composer.lock -log/ +composer.phar +vendor diff --git a/CHANGELOG b/CHANGELOG index 72b207f..f07e06f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,12 @@ +SlackBundle v1.2.0 +================== +- Feature: Added FileUploads +- Feature: Added Basic Implementation for Event-Driven Bots + +SlackBundle v1.1.1 +================== +- Feature: Downgraded requirement for GuzzleClient Version to 3.7.0 (by @toooni) + SlackBundle v1.1.0 ================== - Feature: Added Provider for Silex-Integration diff --git a/Command/BotMessagingCommand.php b/Command/BotMessagingCommand.php new file mode 100644 index 0000000..bc4edac --- /dev/null +++ b/Command/BotMessagingCommand.php @@ -0,0 +1,89 @@ +setName('dzunke:slack:run-bot') + ->setDescription('Running the Bot-User to a Channel') + ->addArgument( + 'channel', + InputArgument::REQUIRED, + 'Channel wo Watch over' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $logger = new Logger(new StreamHandler('php://output')); + + $channel = $this->getContainer()->get('dz.slack.channels')->getId($input->getArgument('channel')); + if (empty($channel)) { + $logger->error('channel "' . $channel . '" does not exists'); + return; + } + + $lastTimestamp = time(); + while (true) { + + try { + $latestMessages = $this->getContainer()->get('dz.slack.channels')->history($channel, $lastTimestamp); + + foreach ($latestMessages as $message) { + if ($message->isBot() === true) { + continue; + } + + $logger->debug('Handling Message of Type : "' . $message->getType() . '"'); + + $event = Event::MESSAGE; + switch ($message->getType()) { + case Message::TYPE_MESSAGE : + $event = Event::MESSAGE; + break; + case Message::TYPE_CHANNEL_JOIN: + $event = Event::JOIN; + break; + case Message::TYPE_CHANNEL_LEAVE: + $event = Event::LEAVE; + break; + } + + $logger->debug('Dispatching "' . $event . '"'); + + $this->getContainer()->get('event_dispatcher')->dispatch( + $event, + new Events\MessageEvent($channel, $message) + ); + + $lastTimestamp = $message->getId(); + } + + $logger->debug('Handled ' . count($latestMessages) . ' Messages'); + + } catch (\Exception $e) { + $logger->error($e->getMessage()); + $logger->error($e->getTraceAsString()); + } + + sleep(self::PROCESS_ITERATION_SLEEP); + } + } +} diff --git a/Command/DebugCommand.php b/Command/DebugCommand.php index cb83c8d..d5911cc 100644 --- a/Command/DebugCommand.php +++ b/Command/DebugCommand.php @@ -60,7 +60,7 @@ protected function renderSlackChannels() null ); - if ($channels->getStatus() == false) { + if ($channels->getStatus() === false) { $this->output->writeln('' . $channels->getError() . ''); return; diff --git a/Event.php b/Event.php new file mode 100644 index 0000000..5ee1e5f --- /dev/null +++ b/Event.php @@ -0,0 +1,27 @@ +message = $message; + } + + /** + * @return Message + */ + public function getMessage() + { + return $this->message; + } +} diff --git a/Resources/doc/actions-list.md b/Resources/doc/actions-list.md index 3572ce2..2ca7ea0 100644 --- a/Resources/doc/actions-list.md +++ b/Resources/doc/actions-list.md @@ -84,6 +84,24 @@ protected $parameter = [ 'icon_emoji' => null, 'parse' => 'full', 'link_names' => 1, - 'unfurl_links' => 1 + 'unfurl_links' => 1, + 'attachments' => [] +]; +``` + +## files.upload + +[Slack Documentation](https://api.slack.com/methods/files.upload) + +Constant: \DZunke\SlackBundle\Slack\Client::ACTION_FILES_UPLOAD + +``` php +protected $parameter = [ + 'content' => null, + 'filetype' => null, + 'filename' => null, + 'title' => null, + 'initial_comment' => null, + 'channels' => null # If no Channel is given the File will be private to the API-User ]; ``` diff --git a/Resources/doc/services.md b/Resources/doc/services.md index bc3f8f0..16bd40a 100644 --- a/Resources/doc/services.md +++ b/Resources/doc/services.md @@ -16,7 +16,7 @@ There is also a way to use the "Attachment"-Feature of Slack. In this Case there For this you must create deliver an Array of Attachments to the Message-Method. ``` php -$attachment = new Attachment(); +$attachment = new DZunke\SlackBundle\Slack\Entity\MessageAttachment(); $attachment->setColor('danger'); $attachment->addField('Test1', 'Test works'); @@ -28,6 +28,20 @@ $response = $container->get('dz.slack.messaging')->message( ); ``` +### File Uploads + +The Messaging does include File Uploads. So the Service has an "upload"-Method to publish Reports or other Files to a Channel. +You must note that every uploaded File will be associated with the API-Key-User configured for your Slack-Client. + +``` php +$response = $container->get('dz.slack.messaging')->upload( + '#foo-channel', + 'Title for this File', + '/Path/to/the/file', + 'Optional Comment' +); +``` + ## Channels There are some operations you can do for a Channel. It is necessary to get the ChannelId from the Slack-API before you diff --git a/Slack/Channels.php b/Slack/Channels.php index f0796b4..d423736 100644 --- a/Slack/Channels.php +++ b/Slack/Channels.php @@ -3,6 +3,9 @@ namespace DZunke\SlackBundle\Slack; use DZunke\SlackBundle\Slack\Client\Actions; +use DZunke\SlackBundle\Slack\Entity\Message; +use Monolog\Handler\StreamHandler; +use Monolog\Logger; class Channels { @@ -33,13 +36,15 @@ public function getClient() */ public function getId($channelName) { - $channels = (array)$this->listAll()->getData(); + if ($channelName[0] === 'C') { + return $channelName; + } if (strpos($channelName, '#') !== false) { $channelName = str_replace('#', '', $channelName); } - foreach ($channels as $name => $data) { + foreach ((array)$this->listAll()->getData() as $name => $data) { if ($name == $channelName) { return $data['id']; } @@ -86,7 +91,7 @@ public function setTopic($channel, $topic) Actions::ACTION_CHANNELS_SET_TOPIC, [ 'channel' => (string)$channel, - 'topic' => (string)$topic + 'topic' => (string)$topic ] ); @@ -101,4 +106,40 @@ public function setTopic($channel, $topic) return $response->setData($data); } + /** + * @param string $channel + * @param int $from + * @param int $count + * @return Message[] + */ + public function history($channel, $from = null, $count = 10) + { + $messages = $this->client->send( + Client\Actions::ACTION_CHANNELS_HISTORY, + [ + 'channel' => $this->getId($channel), + 'oldest' => is_null($from) ? time() : $from, + 'count' => $count + ] + ); + + $repository = []; + if (!empty($messages->getData()['messages'])) { + $messages = $messages->getData()['messages']; + foreach (array_reverse($messages) as $message) { + $objMsg = new Message(); + $objMsg->setId($message['ts']); + $objMsg->setChannel($channel); + $objMsg->setType(isset($message['subtype']) ? $message['subtype'] : $message['type']); + $objMsg->setUserId(isset($message['user']) ? $message['user'] : null); + $objMsg->setUsername(isset($message['username']) ? $message['username'] : null); + $objMsg->setContent($message['test']); + + $repository[] = $objMsg; + } + } + + return $repository; + } + } diff --git a/Slack/Client.php b/Slack/Client.php index 793318b..1bd6753 100644 --- a/Slack/Client.php +++ b/Slack/Client.php @@ -54,7 +54,7 @@ public function send($action, array $parameter) if ( $response->getStatus() === true || - ($response->getStatus() == false && $response->getError() != Response::ERROR_RATE_LIMITED) + ($response->getStatus() === false && $response->getError() != Response::ERROR_RATE_LIMITED) ) { break; } diff --git a/Slack/Client/Actions.php b/Slack/Client/Actions.php index 32f295e..163024a 100644 --- a/Slack/Client/Actions.php +++ b/Slack/Client/Actions.php @@ -6,23 +6,27 @@ class Actions { - const ACTION_POST_MESSAGE = 'chat.postMessage'; - const ACTION_CHANNELS_LIST = 'channels.list'; - const ACTION_API_TEST = 'api.test'; - const ACTION_AUTH_TEST = 'auth.test'; + const ACTION_POST_MESSAGE = 'chat.postMessage'; + const ACTION_CHANNELS_LIST = 'channels.list'; + const ACTION_API_TEST = 'api.test'; + const ACTION_AUTH_TEST = 'auth.test'; const ACTION_CHANNELS_SET_TOPIC = 'channels.setTopic'; - const ACTION_CHANNELS_INFO = 'channels.info'; + const ACTION_CHANNELS_INFO = 'channels.info'; + const ACTION_CHANNELS_HISTORY = 'channels.history'; + const ACTION_FILES_UPLOAD = 'files.upload'; /** * @var array */ protected static $classes = [ - self::ACTION_POST_MESSAGE => 'ChatPostMessage', - self::ACTION_CHANNELS_LIST => 'ChannelsList', - self::ACTION_API_TEST => 'ApiTest', - self::ACTION_AUTH_TEST => 'AuthTest', + self::ACTION_POST_MESSAGE => 'ChatPostMessage', + self::ACTION_CHANNELS_LIST => 'ChannelsList', + self::ACTION_API_TEST => 'ApiTest', + self::ACTION_AUTH_TEST => 'AuthTest', self::ACTION_CHANNELS_SET_TOPIC => 'ChannelsSetTopic', - self::ACTION_CHANNELS_INFO => 'ChannelsInfo' + self::ACTION_CHANNELS_INFO => 'ChannelsInfo', + self::ACTION_CHANNELS_HISTORY => 'ChannelsHistory', + self::ACTION_FILES_UPLOAD => 'FilesUpload' ]; /** diff --git a/Slack/Client/Actions/ChannelsHistory.php b/Slack/Client/Actions/ChannelsHistory.php new file mode 100644 index 0000000..2e43d41 --- /dev/null +++ b/Slack/Client/Actions/ChannelsHistory.php @@ -0,0 +1,58 @@ + null, + 'oldest' => null, + 'count' => 10 + ]; + + /** + * @return array + */ + public function getRenderedRequestParams() + { + return $this->parameter; + } + + /** + * @param array $parameter + * @return $this + */ + public function setParameter(array $parameter) + { + foreach ($parameter as $key => $value) { + if (array_key_exists($key, $this->parameter)) { + $this->parameter[$key] = $value; + } + } + + return $this; + } + + /** + * @return string + */ + public function getAction() + { + return Actions::ACTION_CHANNELS_HISTORY; + } + + /** + * @param array $response + * @return array + */ + public function parseResponse(array $response) + { + return $response; + } +} diff --git a/Slack/Client/Actions/ChatPostMessage.php b/Slack/Client/Actions/ChatPostMessage.php index 4c69b4a..cd855bc 100644 --- a/Slack/Client/Actions/ChatPostMessage.php +++ b/Slack/Client/Actions/ChatPostMessage.php @@ -3,7 +3,7 @@ namespace DZunke\SlackBundle\Slack\Client\Actions; use DZunke\SlackBundle\Slack\Client\Actions; -use DZunke\SlackBundle\Slack\Messaging\Attachment; +use DZunke\SlackBundle\Slack\Entity\MessageAttachment as Attachment; use DZunke\SlackBundle\Slack\Messaging\Identity; class ChatPostMessage implements ActionsInterface diff --git a/Slack/Client/Actions/FilesUpload.php b/Slack/Client/Actions/FilesUpload.php new file mode 100644 index 0000000..f2c1779 --- /dev/null +++ b/Slack/Client/Actions/FilesUpload.php @@ -0,0 +1,65 @@ + null, + 'filetype' => null, + 'filename' => null, + 'title' => null, + 'initial_comment' => null, + 'channels' => null + ]; + + /** + * @return array + */ + public function getRenderedRequestParams() + { + return $this->parameter; + } + + /** + * @param array $parameter + * @return $this + */ + public function setParameter(array $parameter) + { + foreach ($parameter as $key => $value) { + if (array_key_exists($key, $this->parameter)) { + $this->parameter[$key] = $value; + } + } + + return $this; + } + + /** + * @return string + */ + public function getAction() + { + return Actions::ACTION_FILES_UPLOAD; + } + + /** + * @param array $response + * @return array + */ + public function parseResponse(array $response) + { + if (isset($response['ok']) && $response['ok'] === true) { + return $response['file']; + } + + return $response; + } +} diff --git a/Slack/Entity/Message.php b/Slack/Entity/Message.php new file mode 100644 index 0000000..eeb1143 --- /dev/null +++ b/Slack/Entity/Message.php @@ -0,0 +1,201 @@ +id; + } + + /** + * @param string $id + * @return $this + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getChannel() + { + return $this->channel; + } + + /** + * @param string $channel + * @return $this + */ + public function setChannel($channel) + { + $this->channel = $channel; + + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @param string $type + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * @return string + */ + public function getUserId() + { + return $this->userId; + } + + /** + * @param string $userId + * @return $this + */ + public function setUserId($userId) + { + $this->userId = $userId; + + return $this; + } + + /** + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * @param string $username + * @return $this + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * @return bool + */ + public function isBot() + { + return !empty($this->id) && empty($this->userId) && !empty($this->username); + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @param string $content + * @return $this + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * @return MessageAttachment[] + */ + public function getAttachments() + { + return $this->attachments; + } + + /** + * @param MessageAttachment $attachment + * @return $this + */ + public function addAttachment(MessageAttachment $attachment) + { + $this->attachments[] = $attachment; + + return $this; + } + + /** + * @param MessageAttachment[] $attachments + * @return $this + */ + public function setAttachments($attachments) + { + $this->attachments = $attachments; + + return $this; + } + +} diff --git a/Slack/Messaging/Attachment.php b/Slack/Entity/MessageAttachment.php similarity index 97% rename from Slack/Messaging/Attachment.php rename to Slack/Entity/MessageAttachment.php index 034b045..1e5a0a1 100644 --- a/Slack/Messaging/Attachment.php +++ b/Slack/Entity/MessageAttachment.php @@ -1,8 +1,8 @@ client); + $channelId = $channelDiscover->getId($channel); + + if (!empty($channelId)) { + $params['channels'] = $channelId; + } + + $params['content'] = file_get_contents($file); + $params['fileType'] = mime_content_type($file); + $params['filename'] = basename($file); + + return $this->client->send( + Actions::ACTION_FILES_UPLOAD, + $params + ); + } } diff --git a/composer.json b/composer.json index 0b80c51..f898737 100644 --- a/composer.json +++ b/composer.json @@ -18,16 +18,8 @@ "php": ">=5.4.0", "guzzle/guzzle": ">=3.7.0" }, - "require-dev": { - "phpunit/phpunit": "4.*" - }, "autoload": { "psr-0": { "DZunke\\SlackBundle": "" } }, - "target-dir": "DZunke/SlackBundle", - "extra": { - "branch-alias": { - "dev-master": "dev-dev" - } - } + "target-dir": "DZunke/SlackBundle" }