[
  {
    "path": ".gitignore",
    "content": "*~\n.idea/\nbot.php\ncomposer.phar\ncomposer.lock\nvendor"
  },
  {
    "path": ".travis.yml",
    "content": "language: php\nphp:\n  - '7.1'\nbefore_script:\n  # PHP_CodeSniffer\n  - pear install pear/PHP_CodeSniffer\n  - phpenv rehash\nscript:\n  - phpcs --standard=PSR2 src/"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2016 Jean-Charles Le Goff\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# PHP Slack Bot\n\nA simple bot user written in PHP using the Slack Real Time Messaging API https://api.slack.com/rtm\n\n## Installation\nWith Composer\n\n    composer require jclg/php-slack-bot\n\n## Usage\n\nCreate a php file called `bot.php` with the following content\n\n```php\nrequire 'vendor/autoload.php';\nuse PhpSlackBot\\Bot;\n\n// Custom command\nclass MyCommand extends \\PhpSlackBot\\Command\\BaseCommand {\n\n    protected function configure() {\n        $this->setName('mycommand');\n    }\n\n    protected function execute($message, $context) {\n        $this->send($this->getCurrentChannel(), null, 'Hello !');\n    }\n\n}\n\n$bot = new Bot();\n$bot->setToken('TOKEN'); // Get your token here https://my.slack.com/services/new/bot\n$bot->loadCommand(new MyCommand());\n$bot->loadInternalCommands(); // This loads example commands\n$bot->run();\n```\n\nThen run `php bot.php` from the command line (terminal).\n\n## Example commands\n\nExample commands are located in `src/PhpSlackBot/Command/` and can be loaded with `$bot->loadInternalCommands();`\n\n##### Ping Pong Command\n\nType `ping` in a channel and the bot should answer \"Pong\" to you.\n\n##### Count Command\n\nType `count` several times in a channel and the bot should answer with 1 then 2...\n\n##### Date Command\n\nType `date` in a channel and the current date.\n\n##### Planning Poker Command\n\nhttps://en.wikipedia.org/wiki/Planning_poker\n\nType `pokerp start` in a public channel with your team in order to start a planning poker session.\n\nDirect message the bot with `pokerp vote number`. The bot will record your vote.\n\nType `pokerp status` to see the current status of the session (who has voted).\n\nType `pokerp end` in a public channel and the bot will output each vote.\n\n## Load your own commands\n\nYou can load your own commands by implementing the \\PhpSlackBot\\Command\\BaseCommand.\n\nThen call PhpSlackBot\\Bot::loadCommand method for each command you have to load.\n\n## \"Catch All\" command\n\nIf you need to execute a command when an event occurs, you can set up a \"catch all\" command.\n\nThis special command will be triggered on all events.\n\n```php\nrequire 'vendor/autoload.php';\nuse PhpSlackBot\\Bot;\n\n// This special command executes on all events\nclass SuperCommand extends \\PhpSlackBot\\Command\\BaseCommand {\n\n    protected function configure() {\n        // We don't have to configure a command name in this case\n    }\n\n    protected function execute($data, $context) {\n        if ($data['type'] == 'message') {\n            $channel = $this->getChannelNameFromChannelId($data['channel']);\n            $username = $this->getUserNameFromUserId($data['user']);\n            echo $username.' from '.($channel ? $channel : 'DIRECT MESSAGE').' : '.$data['text'].PHP_EOL;\n        }\n    }\n\n}\n\n$bot = new Bot();\n$bot->setToken('TOKEN'); // Get your token here https://my.slack.com/services/new/bot\n$bot->loadCatchAllCommand(new SuperCommand());\n$bot->run();\n```\n\n## Incoming webhooks\n\nThe bot can also listen for incoming webhooks.\n\nCommands are triggered from users messages inside Slack and webhooks are triggered from web post requests.\n\nCustom webhooks can be loaded using the PhpSlackBot\\Bot::loadWebhook method.\n\nThis is useful if you need to control the bot from an external service. For example, with IFTTT https://ifttt.com/maker\n\nTo enable webhooks, use the enableWebserver method before the run method.\n\nYou can also set a secret token to prevent unauthorized requests.\n\n\n```php\n$bot->loadInternalWebhooks(); // Load the internal \"output\" webhook\n$bot->enableWebserver(8080, 'secret'); // This will listen on port 8080\n$bot->run();\n```\n\nThen, use the parameter \"name\" to trigger the corresponding webhook :\n\n```\ncurl -X POST --data-urlencode 'auth=secret' --data-urlencode 'name=output' --data-urlencode 'payload={\"type\" : \"message\", \"text\": \"This is a message\", \"channel\": \"#general\"}' http://localhost:8080\n```\n\n## Active Messaging\n\nThe example provided in the *usage section* above sets the bot to be reactive. The reactive bot is not capable of sending messages to any user on its own. It must be given an input to get a response from. In other words, the bot can only **react** to inputs given to it. \n\n*Active Messaging* means that as a developer, you would be able to **send messages to your users without them sending a message to the bot first**. It is useful when you have to notify your users about something e.g. a bot which can check a user's birthday and wish them, or tell them weather outside every 2 hours without them having to type in a command everytime. There can be many other uses.\n\nYou can use active messaging this way: \n\n```php\n$bot = new Bot();\n$bot->setToken('TOKEN'); // Get your token here https://my.slack.com/services/new/bot\n$bot->loadInternalCommands(); // This loads example commands\n$bot->loadPushNotifier(function () {\n\treturn [\n\t\t'channel' => '#general',\n\t\t'username' => '@slacker',\n\t\t'message' => \"Happy Birthday!! Make sure you have fun today. :-)\"\n\t];\n});\n\n$bot->loadPushNotifier(function () {\n\treturn [\n\t\t'channel' => '@slacker',\n\t\t'username' => null,\n\t\t'message' => \"Current UNIX timestamp is: \" . time()\n\t];\n}, 1800);\n$bot->run();\n\n```\n\nIn the example above, we have set two active messages. \n\n**First message** will send the following message in the '#general' channel: \n\n```\n@slacker Happy Birthday!! Make sure you have fun today. :-)\n```\n\nIt will be triggered only **once**, 10 seconds after launching the script. Slack servers normally establish the connection by then.\n\n**Second message** is a periodic message. It will be sent to the user having the username *slacker* in the team. It won't mention anyone and will be repeated every 30 minutes (1800 seconds). The message should appear as: \n\n```\nCurrent UNIX timestamp is: 1489145707\nCurrent UNIX timestamp is: 1489147507\n```\n\nThat should happen within 1 hour of launching. \n\n*NOTE*: The first message would appear 30 minutes after launching.\n\nThe function you add using `loadPushNotifier` must return an array containing the following keys:\n\n- **channel**: Name of the channel or user to whom the message is to be sent. Channel names should have the `#` prefix while usernames must have the `@` prefix. If you do not set a prefix, the name is assumed to be a channel name. Using prefixes is recommended here.\n- **username**: Any username to be mentioned ahead of the message. You can specify the name without the `@` prefix here, though you might want to use the prefix to maintain uniformity within the code.\n- **message**: The actual message to be sent.\n\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"jclg/php-slack-bot\",\n    \"description\": \"Slack bot user written in PHP\",\n    \"homepage\": \"https://github.com/jclg/php-slack-bot\",\n    \"license\": \"MIT\",\n    \"require\": {\n        \"semako/phpws\": \"1.0.1\",\n        \"react/http\": \"^0.4\",\n        \"ext-curl\": \"*\"\n    },\n    \"authors\": [\n        {\n            \"name\": \"jclg\",\n            \"email\": \"jeancharleslegoff@gmail.com\"\n        }\n    ],\n    \"autoload\": {\n        \"psr-4\": {\n            \"PhpSlackBot\\\\\": \"src/PhpSlackBot\"\n        }\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/ActiveMessenger/Push.php",
    "content": "<?php\nnamespace PhpSlackBot\\ActiveMessenger;\n\nuse PhpSlackBot\\Base;\n\nclass Push extends Base\n{\n    /**\n     * @param string $channelOrUsername  Channel name (not Channel ID assigned by Slack) to which message is to be sent.\n     *                                   Usernames can also be passed.\n     *                                   Channel names should have the '#' prefix. Usernames MUST have the '@' prefix\n     * @param string $usernameForMention If a user is to be mentioned in a message.\n     * @param string $message            Actual message to be sent\n     *\n     * @throws \\Exception\n     */\n    public function sendMessage($channelOrUsername, $usernameForMention, $message)\n    {\n        // if the channelOrUsername is set to null, then do not send the message\n        if (!$channelOrUsername) {\n            return;\n        }\n\n        // See the first character\n        if (strpos($channelOrUsername, '@') === 0) {\n            // User's name was requested\n            $userId = $this->getUserIdFromUserName($channelOrUsername);\n            $channelId = $this->getImIdFromUserId($userId);\n        } elseif (strpos($channelOrUsername, '#') === 0) {\n            // Channel requested\n            $channelId = $this->getChannelIdFromChannelName($channelOrUsername);\n        } else {\n            // Neither user not channel requested\n            // NOTE: We assume it to be a channel name\n            $channelId = $this->getChannelIdFromChannelName($channelOrUsername);\n        }\n\n        $usernameToSend = $this->getUserIdFromUserName($usernameForMention);\n        if (!$usernameToSend) {\n            $usernameToSend = null;\n        }\n\n        if ($channelId) {\n            $this->send($channelId, $usernameToSend, $message);\n        } else {\n            throw new \\Exception('Cannot resolve channel ID to to send the message');\n        }\n    }\n\n    /**\n     * This method is defined here only to satisfy the requirements for extending an abstract class\n     */\n    protected function configure()\n    {\n    }\n\n    /**\n     * This method is defined here only to satisfy the requirements for extending an abstract class\n     */\n    public function execute($message, $context)\n    {\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Base.php",
    "content": "<?php\nnamespace PhpSlackBot;\n\nabstract class Base\n{\n    private $name;\n    private $client;\n    private $user;\n    private $context;\n    private $mentionOnly = false;\n    private $channel;\n    private $caseInsensitive = false;\n    abstract protected function configure();\n    abstract protected function execute($message, $context);\n\n    public function getName()\n    {\n        $this->configure();\n        return $this->name;\n    }\n\n    public function getClient()\n    {\n        return $this->client;\n    }\n\n    public function getMentionOnly()\n    {\n        return $this->mentionOnly;\n    }\n\n    public function setMentionOnly($mentionOnly)\n    {\n        $this->mentionOnly = $mentionOnly;\n    }\n\n    public function setName($name)\n    {\n        $this->name = $name;\n    }\n\n    public function setClient($client)\n    {\n        $this->client = $client;\n    }\n\n    public function setChannel($channel)\n    {\n        $this->channel = $channel;\n    }\n\n    public function setUser($user)\n    {\n        $this->user = $user;\n    }\n\n    public function getCurrentUser()\n    {\n        return $this->user;\n    }\n\n    public function setContext($context)\n    {\n        $this->context = $context;\n    }\n\n    public function getCurrentContext()\n    {\n        return $this->context;\n    }\n\n    public function getCurrentChannel()\n    {\n        return $this->channel;\n    }\n\n    public function setCaseInsensitive($caseInsensitive)\n    {\n        $this->caseInsensitive = $caseInsensitive;\n    }\n\n    public function getCaseInsensitive()\n    {\n        return $this->caseInsensitive;\n    }\n\n    protected function send($channel, $username, $message, $parent_thread = null)\n    {\n        $response = array(\n                          'id' => time(),\n                          'type' => 'message',\n                          'channel' => $channel,\n                          'text' => (!is_null($username) ? '<@'.$username.'> ' : '').$message\n                          );\n        if ($parent_thread) {\n            $response['thread_ts'] = $parent_thread;\n        }\n        $this->client->send(json_encode($response));\n    }\n\n    protected function getUserNameFromUserId($userId)\n    {\n        $username = 'unknown';\n        foreach ($this->context['users'] as $user) {\n            if ($user['id'] == $userId) {\n                $username = $user['name'];\n            }\n        }\n        return $username;\n    }\n\n    protected function getUserIdFromUserName($userName)\n    {\n        $userId = '';\n        $userName = str_replace('@', '', $userName);\n        foreach ($this->context['users'] as $user) {\n            if ($user['name'] == $userName) {\n                $userId = $user['id'];\n            }\n        }\n        return $userId;\n    }\n\n    protected function getChannelIdFromChannelName($channelName)\n    {\n        $channelName = str_replace('#', '', $channelName);\n        foreach ($this->context['channels'] as $channel) {\n            if ($channel['name'] == $channelName) {\n                return $channel['id'];\n            }\n        }\n        foreach ($this->context['groups'] as $group) {\n            if ($group['name'] == $channelName) {\n                return $group['id'];\n            }\n        }\n        return false;\n    }\n\n    protected function getChannelNameFromChannelId($channelId)\n    {\n        foreach ($this->context['channels'] as $channel) {\n            if ($channel['id'] == $channelId) {\n                return $channel['name'];\n            }\n        }\n        foreach ($this->context['groups'] as $group) {\n            if ($group['id'] == $channelId) {\n                return $group['name'];\n            }\n        }\n        return false;\n    }\n\n    protected function getImIdFromUserId($userId)\n    {\n        foreach ($this->context['ims'] as $im) {\n            if ($im['user'] == $userId) {\n                return $im['id'];\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Bot.php",
    "content": "<?php\nnamespace PhpSlackBot;\n\nclass Bot\n{\n\n    private $params = array();\n    private $context = array();\n    private $wsUrl;\n    private $commands = array();\n    private $webhooks = array();\n    private $webserverPort = null;\n    private $webserverHost = null;\n    private $webserverAuthentificationToken = null;\n    private $catchAllCommands = array();\n    private $pushNotifiers = array();\n    private $activeMessenger = null;\n    protected $logger = null;\n\n    public function setToken($token)\n    {\n        $this->params = array('token' => $token);\n    }\n\n    public function loadCommand($command)\n    {\n        if ($command instanceof Command\\BaseCommand) {\n            $this->commands[$command->getName()] = $command;\n        } else {\n            throw new \\Exception('Command must implement PhpSlackBot\\Command\\BaseCommand');\n        }\n    }\n\n    public function loadWebhook($webhook)\n    {\n        if ($webhook instanceof Webhook\\BaseWebhook) {\n            $this->webhooks[$webhook->getName()] = $webhook;\n        } else {\n            throw new \\Exception('Webhook must implement PhpSlackBot\\Webhook\\BaseWebhook');\n        }\n    }\n\n    public function loadCatchAllCommand($command)\n    {\n        if ($command instanceof Command\\BaseCommand) {\n            $this->catchAllCommands[] = $command;\n        } else {\n            throw new \\Exception('Command must implement PhpSlackBot\\Command\\BaseCommand');\n        }\n    }\n\n    public function enableWebserver($port, $authentificationToken = null, $host = '127.0.0.1')\n    {\n        $this->webserverPort = $port;\n        $this->webserverAuthentificationToken = $authentificationToken;\n        $this->webserverHost = $host;\n    }\n\n    public function loadPushNotifier($method, $repeatInterval = null)\n    {\n        if (is_callable($method)) {\n            $this->pushNotifiers[] = ['interval' => (int)$repeatInterval, 'method' => $method];\n        } else {\n            throw new \\Exception('Closure passed as push notifier is not callable.');\n        }\n    }\n\n    public function run()\n    {\n        if (!isset($this->params['token'])) {\n            throw new \\Exception('A token must be set. Please see https://my.slack.com/services/new/bot');\n        }\n\n        $this->init();\n        $logger = $this->logger;\n\n        $loop = \\React\\EventLoop\\Factory::create();\n        $client = new \\Devristo\\Phpws\\Client\\WebSocket($this->wsUrl, $loop, $logger);\n\n        $client->on(\"request\", function ($headers) use ($logger) {\n                $logger->notice(\"Request object created!\");\n        });\n\n        $client->on(\"handshake\", function () use ($logger) {\n                $logger->notice(\"Handshake received!\");\n        });\n\n        $client->on(\"connect\", function () use ($logger, $client) {\n                $logger->notice(\"Connected!\");\n        });\n\n        $client->on(\"message\", function ($message) use ($client, $logger) {\n            $data = $message->getData();\n            $logger->notice(\"Got message: \".$data);\n            $data = json_decode($data, true);\n\n            if (count($this->catchAllCommands)) {\n                foreach ($this->catchAllCommands as $command) {\n                    $command->setClient($client);\n                    $command->setContext($this->context);\n                    $command->executeCommand($data, $this->context);\n                }\n            }\n            $command = $this->getCommand($data);\n            if ($command instanceof Command\\BaseCommand) {\n                $command->setClient($client);\n                if (isset($data['channel'])) {\n                    $command->setChannel($data['channel']);\n                }\n                if (isset($data['user'])) {\n                    $command->setUser($data['user']);\n                }\n                $command->setContext($this->context);\n                $command->executeCommand($data, $this->context);\n            }\n        });\n\n        /* Webserver */\n        if (null !== $this->webserverPort) {\n            $logger->notice(\"Listening on port \".$this->webserverPort);\n            $socket = new \\React\\Socket\\Server($loop);\n            $http = new \\React\\Http\\Server($socket);\n            $http->on('request', function ($request, $response) use ($client) {\n                $request->on('data', function ($data) use ($client, $request, $response) {\n                    parse_str($data, $post);\n                    if ($this->webserverAuthentificationToken === null ||\n                        ($this->webserverAuthentificationToken !== null &&\n                         isset($post['auth']) &&\n                         $post['auth'] === $this->webserverAuthentificationToken)) {\n                        if (isset($post['name']) && is_string($post['name']) && isset($this->webhooks[$post['name']])) {\n                            $hook = $this->webhooks[$post['name']];\n                            $hook->setClient($client);\n                            $hook->setContext($this->context);\n                            $hook->executeWebhook(json_decode($post['payload'], true), $this->context);\n                            $response->writeHead(200, array('Content-Type' => 'text/plain'));\n                            $response->end(\"Ok\\n\");\n                        } else {\n                            $response->writeHead(404, array('Content-Type' => 'text/plain'));\n                            $response->end(\"No webhook found\\n\");\n                        }\n                    } else {\n                        $response->writeHead(403, array('Content-Type' => 'text/plain'));\n                        $response->end(\"\");\n                    }\n                });\n            });\n            $socket->listen($this->webserverPort, $this->webserverHost);\n        }\n\n        /* Notifiers */\n\n        if (!$this->activeMessenger) {\n            $this->activeMessenger = new ActiveMessenger\\Push();\n            $this->activeMessenger->setContext($this->context);\n            $this->activeMessenger->setClient($client);\n        }\n        foreach ($this->pushNotifiers as $notifierArray) {\n            if ($notifierArray['interval'] != 0) {\n                $loop->addPeriodicTimer($notifierArray['interval'], function () use ($notifierArray) {\n                    if ($this->activeMessenger instanceof ActiveMessenger\\Push) {\n                        $resultArray = call_user_func($notifierArray['method']);\n                        $this->activeMessenger->sendMessage(\n                            $resultArray['channel'],\n                            $resultArray['username'],\n                            $resultArray['message']\n                        );\n                    }\n                });\n            } else {\n                $loop->addTimer(10, function () use ($notifierArray) {\n                    if ($this->activeMessenger instanceof ActiveMessenger\\Push) {\n                        $resultArray = call_user_func($notifierArray['method']);\n                        $this->activeMessenger->sendMessage(\n                            $resultArray['channel'],\n                            $resultArray['username'],\n                            $resultArray['message']\n                        );\n                    }\n                });\n            }\n        }\n\n        $client->open();\n\n        $loop->run();\n    }\n\n    public function initLogger(\\Zend\\Log\\LoggerInterface $logger = null)\n    {\n        if (! is_null($logger)) {\n            $this->logger = $logger;\n        } else {\n            $this->logger = new \\Zend\\Log\\Logger();\n            $writer = new \\Zend\\Log\\Writer\\Stream(\"php://output\");\n            $this->logger->addWriter($writer);\n        }\n    }\n\n    private function init()\n    {\n        $url = 'https://slack.com/api/rtm.start';\n        $ch = curl_init();\n        curl_setopt($ch, CURLOPT_URL, $url.'?'.http_build_query($this->params));\n        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\n        $body = curl_exec($ch);\n        if ($body === false) {\n            throw new \\Exception('Error when requesting '.$url.' '.curl_error($ch));\n        }\n        curl_close($ch);\n        $response = json_decode($body, true);\n        if (is_null($response)) {\n            throw new \\Exception('Error when decoding body ('.$body.').');\n        }\n        $this->context = $response;\n        if (isset($response['error'])) {\n            throw new \\Exception($response['error']);\n        }\n        $this->wsUrl = $response['url'];\n\n        if (is_null($this->logger)) {\n            $this->initLogger();\n        }\n    }\n\n    public function loadInternalCommands()\n    {\n        $commands = array(\n                          new \\PhpSlackBot\\Command\\PingPongCommand,\n                          new \\PhpSlackBot\\Command\\CountCommand,\n                          new \\PhpSlackBot\\Command\\DateCommand,\n                          new \\PhpSlackBot\\Command\\PokerPlanningCommand,\n                          );\n        foreach ($commands as $command) {\n            if (!isset($this->commands[$command->getName()])) {\n                $this->commands[$command->getName()] = $command;\n            }\n        }\n    }\n\n    public function loadInternalWebhooks()\n    {\n        $webhooks = array(\n                          new \\PhpSlackBot\\Webhook\\OutputWebhook,\n                          );\n        foreach ($webhooks as $webhook) {\n            if (!isset($this->webhooks[$webhook->getName()])) {\n                $this->webhooks[$webhook->getName()] = $webhook;\n            }\n        }\n    }\n\n    private function getCommand($data)\n    {\n        if (empty($data['text'])) {\n            return null;\n        }\n\n        // Check if bot is mentioned\n        $botMention = false;\n        if (strpos($data['text'], '<@'.$this->context['self']['id'].'>') !== false) {\n            $botMention = true;\n        }\n\n        $find = '/^'.preg_quote('<@'.$this->context['self']['id'].'>', '/').'[ ]*/';\n        $text = preg_replace($find, '', $data['text']);\n\n        if (empty($text)) {\n            return null;\n        }\n\n        foreach ($this->commands as $commandName => $availableCommand) {\n            $find = '/^'.preg_quote($commandName).'/';\n            if ($availableCommand->getCaseInsensitive()) {\n                $find .= 'i';\n            }\n\n            if (preg_match($find, $text) &&\n                (!$availableCommand->getMentionOnly() || $botMention)) {\n                return $availableCommand;\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Command/BaseCommand.php",
    "content": "<?php\nnamespace PhpSlackBot\\Command;\n\nabstract class BaseCommand extends \\PhpSlackBot\\Base\n{\n\n    public function executeCommand($message, $context)\n    {\n        return $this->execute($message, $context);\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Command/CountCommand.php",
    "content": "<?php\nnamespace PhpSlackBot\\Command;\n\nclass CountCommand extends BaseCommand\n{\n\n    private $count = 0;\n\n    protected function configure()\n    {\n        $this->setName('count');\n    }\n\n    protected function execute($message, $context)\n    {\n        $this->send($this->getCurrentChannel(), null, $this->count);\n        $this->count++;\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Command/DateCommand.php",
    "content": "<?php\nnamespace PhpSlackBot\\Command;\n\nclass DateCommand extends BaseCommand\n{\n\n    protected function configure()\n    {\n        $this->setName('date');\n    }\n\n    protected function execute($message, $context)\n    {\n        $this->send($this->getCurrentChannel(), null, date(\"D M j G:i:s T Y\"));\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Command/PingPongCommand.php",
    "content": "<?php\nnamespace PhpSlackBot\\Command;\n\nclass PingPongCommand extends BaseCommand\n{\n\n    protected function configure()\n    {\n        $this->setName('ping');\n    }\n\n    protected function execute($message, $context)\n    {\n        $this->send($this->getCurrentChannel(), $this->getCurrentUser(), 'Pong');\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Command/PokerPlanningCommand.php",
    "content": "<?php\nnamespace PhpSlackBot\\Command;\n\nclass PokerPlanningCommand extends BaseCommand\n{\n\n    private $count = 0;\n    private $initiator;\n    private $scores = array();\n    private $status = 'free';\n\n    protected function configure()\n    {\n        $this->setName('pokerp');\n    }\n\n    protected function execute($message, $context)\n    {\n        $args = $this->getArgs($message);\n        $command = isset($args[1]) ? $args[1] : '';\n\n        switch ($command) {\n            case 'start':\n                $this->start($args);\n                break;\n            case 'status':\n                $this->status();\n                break;\n            case 'vote':\n                $this->vote($args);\n                break;\n            case 'end':\n                $this->end();\n                break;\n            default:\n                $this->send(\n                    $this->getCurrentChannel(),\n                    $this->getCurrentUser(),\n                    'No comprendo. Use \"'.$this->getName().' start\" or \"'.$this->getName().' status\"'\n                );\n        }\n    }\n\n    private function start($args)\n    {\n        if ($this->status == 'free') {\n            $this->subject = isset($args[2]) ? $args[2] : null;\n            if (!is_null($this->subject)) {\n                $this->subject = str_replace(array('<', '>'), '', $this->subject);\n            }\n            $this->status = 'running';\n            $this->initiator = $this->getCurrentUser();\n            $this->scores = array();\n            $this->send(\n                $this->getCurrentChannel(),\n                null,\n                'Poker planning sessions start by '.$this->getUserNameFromUserId($this->initiator).\"\\n\".\n                'Please vote'.(!is_null($this->subject) ? ' for '.$this->subject : '')\n            );\n            $this->send(\n                $this->getCurrentChannel(),\n                $this->getCurrentUser(),\n                'Use \"'.$this->getName().' end\" to end the session'\n            );\n        } else {\n            $this->send(\n                $this->getCurrentChannel(),\n                $this->getCurrentUser(),\n                'A poker session is still active'\n            );\n        }\n    }\n\n    private function status()\n    {\n        $message = 'Current status : '.$this->status;\n        if ($this->status == 'running') {\n            $message .= \"\\n\".'Initiator : '.$this->getUserNameFromUserId($this->initiator);\n        }\n        $this->send($this->getCurrentChannel(), null, $message);\n        if ($this->status == 'running') {\n            if (empty($this->scores)) {\n                $this->send($this->getCurrentChannel(), null, 'No one has voted yet');\n            } else {\n                $message = '';\n                foreach ($this->scores as $user => $score) {\n                    $message .= $this->getUserNameFromUserId($user).' has voted'.\"\\n\";\n                }\n                $this->send($this->getCurrentChannel(), null, $message);\n            }\n        }\n    }\n\n    private function vote($args)\n    {\n        if ($this->status == 'running') {\n            $score = isset($args[2]) ? $args[2] : -1;\n            $sequence = $this->getSequence();\n            if (!in_array($score, $sequence)) {\n                $this->send(\n                    $this->getCurrentChannel(),\n                    $this->getCurrentUser(),\n                    'Use \"'.$this->getName().' vote [number]\". Choose [number] between '.implode(', ', $sequence)\n                );\n            } else {\n                $this->scores[$this->getCurrentUser()] = (int) $score;\n                $this->send(\n                    $this->getCurrentChannel(),\n                    $this->getCurrentUser(),\n                    'Thank you! Your vote ('.$score.\n                    ') has been recorded You can still change your vote until the end of the session'\n                );\n            }\n        } else {\n            $this->send(\n                $this->getCurrentChannel(),\n                $this->getCurrentUser(),\n                'There is no poker session. You can start one with \"'.$this->getName().' start\"'\n            );\n        }\n    }\n\n    private function end()\n    {\n        if ($this->status == 'running') {\n            if ($this->getCurrentUser() == $this->initiator) {\n                $message = 'Ending session'.\n                    (!is_null($this->subject) ? ' for '.$this->subject : '').\"\\n\".'Results : '.\"\\n\";\n                if (empty($this->scores)) {\n                    $message .= 'No vote !';\n                } else {\n                    foreach ($this->scores as $user => $score) {\n                        $message .= $this->getUserNameFromUserId($user).' => '.$score.\"\\n\";\n                    }\n                    $message .= '------------------'.\"\\n\";\n                    $message .= 'Average score : '.$this->getAverageScore().\"\\n\";\n                    $message .= 'Median score : '.$this->getMedianScore().\"\\n\";\n                    $message .= 'Max score : '.$this->getMaxScore().\"\\n\";\n                    $message .= 'Min score : '.$this->getMinScore();\n                }\n                $this->send($this->getCurrentChannel(), null, $message);\n                $this->status = 'free';\n            } else {\n                $this->send(\n                    $this->getCurrentChannel(),\n                    $this->getCurrentUser(),\n                    'Only '.$this->getUserNameFromUserId($this->initiator).' can end the session'\n                );\n            }\n        } else {\n            $this->send(\n                $this->getCurrentChannel(),\n                $this->getCurrentUser(),\n                'There is no poker session. You can start one with \"'.$this->getName().' start\"'\n            );\n        }\n    }\n\n    private function getArgs($message)\n    {\n        $args = array();\n        if (isset($message['text'])) {\n            $args = array_values(array_filter(explode(' ', $message['text'])));\n        }\n        $commandName = $this->getName();\n        // Remove args which are before the command name\n        $finalArgs = array();\n        $remove = true;\n        foreach ($args as $arg) {\n            if ($commandName == $arg) {\n                $remove = false;\n            }\n            if (!$remove) {\n                $finalArgs[] = $arg;\n            }\n        }\n        return $finalArgs;\n    }\n\n    private function getAverageScore()\n    {\n        return array_sum($this->scores) / count($this->scores);\n    }\n\n    private function getMedianScore()\n    {\n        $arr = $this->scores;\n        sort($arr);\n        $count = count($arr);\n        $middleval = floor(($count-1)/2);\n        if ($count % 2) {\n            $median = $arr[$middleval];\n        } else {\n            $low = $arr[$middleval];\n            $high = $arr[$middleval+1];\n            $median = (($low+$high)/2);\n        }\n        return $median;\n    }\n\n    private function getSequence()\n    {\n        return array(0, 1, 2, 3, 5, 8, 13, 20, 40, 100);\n    }\n\n    private function getMaxScore()\n    {\n        return max($this->scores);\n    }\n\n    private function getMinScore()\n    {\n        return min($this->scores);\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Webhook/BaseWebhook.php",
    "content": "<?php\nnamespace PhpSlackBot\\Webhook;\n\nabstract class BaseWebhook extends \\PhpSlackBot\\Base\n{\n\n    public function executeWebhook($payload, $context)\n    {\n        return $this->execute($payload, $context);\n    }\n}\n"
  },
  {
    "path": "src/PhpSlackBot/Webhook/OutputWebhook.php",
    "content": "<?php\nnamespace PhpSlackBot\\Webhook;\n\nclass OutputWebhook extends BaseWebhook\n{\n\n    protected function configure()\n    {\n        $this->setName('output');\n    }\n\n    protected function execute($payload, $context)\n    {\n        $payload['channel'] = $this->getChannelIdFromChannelName($payload['channel']);\n        $this->getClient()->send(json_encode($payload));\n    }\n}\n"
  }
]