diff --git a/composer.json b/composer.json index d955633..1fb70d4 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,9 @@ "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.1", - "hamcrest/hamcrest-php": "^1.2", + "phpunit/phpunit": "~5.1.0", + "hamcrest/hamcrest-php": "~1.2.0", + "phpunit/php-token-stream": "~1.4.8", "scrutinizer/ocular": "~1.3", "squizlabs/php_codesniffer": "~2.5" }, diff --git a/composer.lock b/composer.lock index a8da027..c277c42 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ff51f8e1f5db391280674365e25a3f15", - "content-hash": "7ac05cb5230f735300c99bc448bac42f", + "hash": "89ff393a4b4ed3944d9f92c4561f3c94", + "content-hash": "536260a2dedbd7c6e809ef0c8f88c677", "packages": [], "packages-dev": [ { @@ -977,16 +977,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.1.4", + "version": "5.1.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "676c25c4ac563869572c878fdaf3db21587f5f3b" + "reference": "d0f7ae467dcbe7a6ad050540c9d1d39a7aefff26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/676c25c4ac563869572c878fdaf3db21587f5f3b", - "reference": "676c25c4ac563869572c878fdaf3db21587f5f3b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0f7ae467dcbe7a6ad050540c9d1d39a7aefff26", + "reference": "d0f7ae467dcbe7a6ad050540c9d1d39a7aefff26", "shasum": "" }, "require": { @@ -1047,7 +1047,7 @@ "testing", "xunit" ], - "time": "2016-01-11 10:11:10" + "time": "2016-02-02 09:03:29" }, { "name": "phpunit/phpunit-mock-objects", @@ -1916,7 +1916,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^5.6|^7.0" + "php": "^7.0" }, "platform-dev": [] } diff --git a/run.php b/run.php new file mode 100644 index 0000000..bc0a1a7 --- /dev/null +++ b/run.php @@ -0,0 +1,52 @@ +connect(); + + +// Ping Pong +$client->addPlugin(new PingPong()); + +// Welcome users +$client->addPlugin(new Welcome()); + +// (hi|hello|hey) spires +$client->addPlugin(new Greetings()); + +// !spires what time is it? +$client->addPlugin(new Time()); + + +$client->run(); diff --git a/src/EMPTY b/src/EMPTY deleted file mode 100644 index e69de29..0000000 diff --git a/src/Irc/Commands/Base.php b/src/Irc/Commands/Base.php new file mode 100644 index 0000000..863da41 --- /dev/null +++ b/src/Irc/Commands/Base.php @@ -0,0 +1,24 @@ +command; + } + + public function __toString() : string + { + return trim($this->command() . ' ' . $this->params()); + } +} diff --git a/src/Irc/Commands/Command.php b/src/Irc/Commands/Command.php new file mode 100644 index 0000000..e1dd3e8 --- /dev/null +++ b/src/Irc/Commands/Command.php @@ -0,0 +1,35 @@ +command = $command; + $this->params = $params ?: ''; + } + + public static function fromParams(string $params) : self + { + // TODO: Implement fromParams() method. + } + + public function params() : string + { + return $this->params; + } +} diff --git a/src/Irc/Commands/Join.php b/src/Irc/Commands/Join.php new file mode 100644 index 0000000..690738e --- /dev/null +++ b/src/Irc/Commands/Join.php @@ -0,0 +1,55 @@ +channels = $channels; + $this->keys = $keys ?? []; + } + + public static function fromParams(string $params) : self + { + $arguments = explode(' ', $params); + + $channels = explode(',', $arguments[0]); + $keys = isset($arguments[1]) ? explode(',', $arguments[1]) : []; + + return new static($channels, $keys); + } + + public function channels() : array + { + return $this->channels; + } + + public function keys() : array + { + return $this->keys; + } + + public function params() : string + { + return trim(implode(',', $this->channels) . ' ' . implode(',', $this->keys)); + } +} diff --git a/src/Irc/Commands/Ping.php b/src/Irc/Commands/Ping.php new file mode 100644 index 0000000..a435b08 --- /dev/null +++ b/src/Irc/Commands/Ping.php @@ -0,0 +1,55 @@ +server1 = $server1; + $this->server2 = $server2 ?? ''; + } + + public static function fromParams(string $params) : self + { + $servers = explode(' ', $params); + + $server1 = ltrim($servers[0], ':'); + $server2 = ltrim(($servers[1] ?? ''), ':'); + + return new static($server1, $server2); + } + + public function server1() : string + { + return $this->server1; + } + + public function server2() + { + return $this->server2; + } + + public function params() : string + { + return trim($this->server1 . ' ' . $this->server2); + } +} diff --git a/src/Irc/Commands/Pong.php b/src/Irc/Commands/Pong.php new file mode 100644 index 0000000..b05278b --- /dev/null +++ b/src/Irc/Commands/Pong.php @@ -0,0 +1,55 @@ +server = $server; + $this->server2 = $server2 ?? ''; + } + + public static function fromParams(string $params) : self + { + $servers = explode(' ', $params); + + $server = ltrim($servers[0], ':'); + $server2 = ltrim(($servers[1] ?? ''), ':'); + + return new static($server, $server2); + } + + public function server() : string + { + return $this->server; + } + + public function server2() + { + return $this->server2; + } + + public function params() : string + { + return trim($this->server . ' ' . $this->server2); + } +} diff --git a/src/Irc/Commands/Privmsg.php b/src/Irc/Commands/Privmsg.php new file mode 100644 index 0000000..1815ef9 --- /dev/null +++ b/src/Irc/Commands/Privmsg.php @@ -0,0 +1,60 @@ +targets = $targets; + $this->text = $text; + } + + public static function fromParams(string $params) : self + { + list($targets, $text) = explode(' ', $params, 2); + + $targets = explode(',', $targets); + $text = ltrim($text, ':'); + + return new static($targets, $text); + } + + public function targets() : array + { + return $this->targets; + } + + public function text() + { + return $this->text; + } + + public function params() : string + { + return implode(',', $this->targets) . ' :' . $this->text; + } + + public function hasTarget($target) + { + return in_array($target, $this->targets()); + } +} diff --git a/src/Irc/Connection.php b/src/Irc/Connection.php new file mode 100644 index 0000000..3842c7d --- /dev/null +++ b/src/Irc/Connection.php @@ -0,0 +1,44 @@ +channel = $channel; + $this->server = $server; + $this->port = $port; + } + + public function channel() : string + { + return $this->channel; + } + + public function server() : string + { + return $this->server; + } + + public function port() : int + { + return $this->port; + } +} diff --git a/src/Irc/Message.php b/src/Irc/Message.php new file mode 100644 index 0000000..35e0084 --- /dev/null +++ b/src/Irc/Message.php @@ -0,0 +1,41 @@ +prefix = $prefix; + $this->command = $command; + } + + public function prefix() : Prefix + { + return $this->prefix; + } + + public function command() : Command + { + return $this->command; + } + + public function raw() + { + return trim($this->prefix->raw() . ' ' . (string) $this->command); + } +} diff --git a/src/Irc/Message/Command.php b/src/Irc/Message/Command.php new file mode 100644 index 0000000..6584663 --- /dev/null +++ b/src/Irc/Message/Command.php @@ -0,0 +1,17 @@ +nickname = $nickname ?: ''; + $this->username = $username ?: ''; + $this->hostname = $hostname ?: ''; + $this->servername = $servername ?: ''; + } + + public static function none() : self + { + return new static(); + } + + public static function user(string $nickname, string $username = null, string $hostname = null) : self + { + return new static($nickname, $username, $hostname); + } + + public static function server(string $servername) : self + { + return new static(null, null, null, $servername); + } + + public function nickname() : string + { + return $this->nickname; + } + + public function username() : string + { + return $this->username; + } + + public function hostname() : string + { + return $this->hostname; + } + + public function servername() : string + { + return $this->servername; + } + + public function raw() : string + { + $raw = ''; + + $raw .= empty($this->nickname) ? '' : ":{$this->nickname}"; + $raw .= empty($this->username) ? '' : "!{$this->username}"; + $raw .= empty($this->hostname) ? '' : "@{$this->hostname}"; + $raw .= empty($this->servername) ? '' : ":{$this->servername}"; + + return trim($raw); + } +} diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php new file mode 100644 index 0000000..a8a382b --- /dev/null +++ b/src/Irc/Parser.php @@ -0,0 +1,113 @@ +$servername)" . + "|(?:(?P$nickname)(?:$bang(?P$user))?(?:$at(?P$host))?))"; + + // = [ ":" prefix SPACE ] command [ params ] crlf + $message = "(?P$colon$prefix$space)?(?P$command)(?P$params)?$crlf"; + + // Do the thing + preg_match("/^$message\$/SU", $raw, $matches); + + // Trim whitespace + $matches = array_map('trim', $matches); + + // Return only the named matches we want in the order we want + return [ + 'prefix' => $matches['prefix'] ?? '', + 'nickname' => $matches['nickname'] ?? '', + 'username' => $matches['username'] ?? '', + 'hostname' => $matches['hostname'] ?? '', + 'servername' => $matches['servername'] ?? '', + 'command' => $matches['command'] ?? '', + 'params' => $matches['params'] ?? '', + ]; + } +} diff --git a/src/Irc/User.php b/src/Irc/User.php new file mode 100644 index 0000000..c72cac4 --- /dev/null +++ b/src/Irc/User.php @@ -0,0 +1,55 @@ +nickname = $nickname; + $this->username = $username; + $this->realname = $realname; + $this->usermode = $usermode; + } + + public function nickname() : string + { + return $this->nickname; + } + + public function username() : string + { + return $this->username; + } + + public function realname() : string + { + return $this->realname; + } + + public function usermode() : int + { + return $this->usermode; + } +} diff --git a/src/IrcClient.php b/src/IrcClient.php new file mode 100644 index 0000000..ce3547e --- /dev/null +++ b/src/IrcClient.php @@ -0,0 +1,178 @@ +connection = $connection; + $this->user = $user; + } + + public function connection() : Connection + { + return $this->connection; + } + + public function channel() : string + { + return $this->connection()->channel(); + } + + public function user() : User + { + return $this->user; + } + + public function socket() + { + return $this->socket; + } + + public function connect() + { + $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + + $isConnected = socket_connect( + $this->socket, + $this->connection()->server(), + $this->connection()->port() + ); + + $this->write("NICK {$this->user()->nickname()}\r\n"); + $this->write("USER {$this->user()->username()} {$this->user()->usermode()} * :{$this->user()->realname()}\r\n"); + $this->write("JOIN {$this->connection()->channel()}\r\n"); + } + + public function addAction($callback) + { + $this->actions[] = $callback; + } + + public function addPlugin(Plugin $plugin) + { + $this->plugins[] = $plugin; + } + + public function read() + { + return socket_read($this->socket, 2048, PHP_NORMAL_READ); + } + + public function write(string $response) + { + $response = trim($response); + + $this->debug("[ write ]: " . $response . "\n"); + return socket_write($this->socket, $response . "\r\n"); + } + + public function debug(string $string) + { + fwrite(STDOUT, $string); + } + + public function channelMessage(string $message) + { + $this->write((string) new Privmsg([$this->channel()], $message)); + } + + public function run() + { + $parser = new Parser(); + + while ($raw = $this->read()) { + + if (!$raw = trim($raw)) { + continue; + } + $this->debug("\n\n[ read ]: " . $raw); + $message = $parser->parse($raw . "\r\n"); + + $prefix = new Prefix( + $message['nickname'], + $message['username'], + $message['hostname'], + $message['servername'] + ); + + switch ($message['command']) { + case 'PING': + $message = new Message( + $prefix, + Ping::fromParams($message['params']) + ); + break; + + case 'JOIN': + $message = new Message( + $prefix, + Join::fromParams($message['params']) + ); + break; + + case 'PRIVMSG': + $message = new Message( + $prefix, + Privmsg::fromParams($message['params']) + ); + break; + + default: + $message = new Message( + $prefix, + new Command($message['command'], $message['params']) + ); + } + + ob_start(); + var_dump($message); + $messageDump = ob_get_contents(); + ob_end_clean(); + $this->debug("\n" . $messageDump); + + + foreach ($this->plugins as $plugin) { + $plugin->handle($this, $message); + } + + foreach ($this->actions as $action) { + $action($this, $message); + } + } + } +} diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 0000000..f623c8a --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,11 @@ +command() instanceof Privmsg) { + /** @var Privmsg $command */ + $command = $message->command(); + + if ($command->hasTarget($client->channel())) { + if (preg_match('/^(hi|hello|hey) spires$/i', $command->text())) { + $client->channelMessage("Hello {$message->prefix()->nickname()}"); + } + } + } + } +} diff --git a/src/Plugins/PingPong.php b/src/Plugins/PingPong.php new file mode 100644 index 0000000..c9d3efb --- /dev/null +++ b/src/Plugins/PingPong.php @@ -0,0 +1,20 @@ +command() instanceof Ping) { + $client->write((string) Pong::fromParams($message->command()->params())); + } + } +} diff --git a/src/Plugins/Time.php b/src/Plugins/Time.php new file mode 100644 index 0000000..f107693 --- /dev/null +++ b/src/Plugins/Time.php @@ -0,0 +1,29 @@ +command() instanceof Privmsg) { + /** @var Privmsg $command */ + $command = $message->command(); + + if ($command->hasTarget($client->channel())) { + if (preg_match('/^!?spires,? what time is it\??/i', $command->text())) { + $time = date('H:i'); + $client->channelMessage(" \\o/ / {$time} \\"); + $client->channelMessage(" | \\ It's hammer time! /"); + $client->channelMessage(" / \\"); + } + } + } + } +} diff --git a/src/Plugins/Welcome.php b/src/Plugins/Welcome.php new file mode 100644 index 0000000..a63184f --- /dev/null +++ b/src/Plugins/Welcome.php @@ -0,0 +1,19 @@ +command() instanceof Join && $message->prefix()->nickname() != $client->user()->nickname()) { + $client->channelMessage("Welcome {$message->prefix()->nickname()} :D"); + } + } +} diff --git a/tests/Irc/Commands/CommandTest.php b/tests/Irc/Commands/CommandTest.php new file mode 100644 index 0000000..c40cb22 --- /dev/null +++ b/tests/Irc/Commands/CommandTest.php @@ -0,0 +1,64 @@ +command(), is(identicalTo('PING'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::params + */ + public function can_get_the_params_string() + { + $command = new Command('USER', 'guest 0 * :Ronnie Reagan'); + + assertThat($command->params(), is(identicalTo('guest 0 * :Ronnie Reagan'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::__toString + */ + public function can_cast_to_string_without_params() + { + $command = new Command('QUIT'); + + assertThat((string) $command, is(identicalTo('QUIT'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::__toString + */ + public function can_cast_to_string_with_params() + { + $command = new Command('USER', 'guest 0 * :Ronnie Reagan'); + + assertThat((string) $command, is(identicalTo('USER guest 0 * :Ronnie Reagan'))); + } +} diff --git a/tests/Irc/Commands/JoinTest.php b/tests/Irc/Commands/JoinTest.php new file mode 100644 index 0000000..cd065f4 --- /dev/null +++ b/tests/Irc/Commands/JoinTest.php @@ -0,0 +1,133 @@ +command(), is(identicalTo('JOIN'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::channels + */ + public function can_get_channels() + { + $command = new Join(['#phpoxford', '#phpoxfordgames']); + + assertThat($command->channels(), is(identicalTo(['#phpoxford', '#phpoxfordgames']))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::keys + */ + public function can_get_keys() + { + $command = new Join(['#phpoxford'], ['firstkey', 'secondkey']); + + assertThat($command->keys(), is(identicalTo(['firstkey', 'secondkey']))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::params + */ + public function can_get_params_with_one_channel() + { + $command = new Join(['#phpoxford']); + + assertThat($command->params(), is(identicalTo('#phpoxford'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::params + */ + public function can_get_params_with_more_channels() + { + $command = new Join(['#phpoxford', '#phpoxfordgames']); + + assertThat($command->params(), is(identicalTo('#phpoxford,#phpoxfordgames'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::params + */ + public function can_get_params_with_one_key() + { + $command = new Join(['#phpoxford'], ['firstkey']); + + assertThat($command->params(), is(identicalTo('#phpoxford firstkey'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::params + */ + public function can_get_params_with_more_keys() + { + $command = new Join(['#phpoxford', '#phpoxfordgames'], ['firstkey', 'secondkey']); + + assertThat($command->params(), is(identicalTo('#phpoxford,#phpoxfordgames firstkey,secondkey'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::__toString + */ + public function can_cast_to_string_with_channels() + { + $command = new Join(['#phpoxford', '#phpoxfordgames']); + + assertThat((string) $command, is(identicalTo('JOIN #phpoxford,#phpoxfordgames'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::__toString + */ + public function can_cast_to_string_with_channels_and_keys() + { + $command = new Join(['#phpoxford', '#phpoxfordgames'], ['firstkey', 'secondkey']); + + assertThat((string) $command, is(identicalTo('JOIN #phpoxford,#phpoxfordgames firstkey,secondkey'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Join::fromParams + */ + public function can_construct_from_params() + { + $command = Join::fromParams('#phpoxford,#phpoxfordgames firstkey,secondkey'); + + assertThat($command->command(), is(identicalTo('JOIN'))); + assertThat($command->channels(), is(identicalTo(['#phpoxford', '#phpoxfordgames']))); + assertThat($command->keys(), is(identicalTo(['firstkey', 'secondkey']))); + assertThat((string) $command, is(identicalTo('JOIN #phpoxford,#phpoxfordgames firstkey,secondkey'))); + } +} diff --git a/tests/Irc/Commands/PingTest.php b/tests/Irc/Commands/PingTest.php new file mode 100644 index 0000000..88927d5 --- /dev/null +++ b/tests/Irc/Commands/PingTest.php @@ -0,0 +1,111 @@ +command(), is(identicalTo('PING'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::server1 + */ + public function can_get_server1() + { + $command = new Ping('wolfe.freenode.net'); + + assertThat($command->server1(), is(identicalTo('wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::server2 + */ + public function can_get_server2() + { + $command = new Ping('cable.virginm.net', 'wolfe.freenode.net'); + + assertThat($command->server2(), is(identicalTo('wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::params + */ + public function can_get_params_with_one_server() + { + $command = new Ping('cable.virginm.net'); + + assertThat($command->params(), is(identicalTo('cable.virginm.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::params + */ + public function can_get_params_with_two_servers() + { + $command = new Ping('cable.virginm.net', 'wolfe.freenode.net'); + + assertThat($command->params(), is(identicalTo('cable.virginm.net wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::__toString + */ + public function can_cast_to_string_with_server1() + { + $command = new Ping('cable.virginm.net'); + + assertThat((string) $command, is(identicalTo('PING cable.virginm.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::__toString + */ + public function can_cast_to_string_with_server1_and_server2() + { + $command = new Ping('cable.virginm.net', 'wolfe.freenode.net'); + + assertThat((string) $command, is(identicalTo('PING cable.virginm.net wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Ping::fromParams + */ + public function can_construct_from_params() + { + $command = Ping::fromParams('cable.virginm.net wolfe.freenode.net'); + + assertThat($command->command(), is(identicalTo('PING'))); + assertThat($command->server1(), is(identicalTo('cable.virginm.net'))); + assertThat($command->server2(), is(identicalTo('wolfe.freenode.net'))); + assertThat((string) $command, is(identicalTo('PING cable.virginm.net wolfe.freenode.net'))); + } +} diff --git a/tests/Irc/Commands/PongTest.php b/tests/Irc/Commands/PongTest.php new file mode 100644 index 0000000..6be1ebe --- /dev/null +++ b/tests/Irc/Commands/PongTest.php @@ -0,0 +1,111 @@ +command(), is(identicalTo('PONG'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::server + */ + public function can_get_server() + { + $command = new Pong('wolfe.freenode.net'); + + assertThat($command->server(), is(identicalTo('wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::server2 + */ + public function can_get_server2() + { + $command = new Pong('cable.virginm.net', 'wolfe.freenode.net'); + + assertThat($command->server2(), is(identicalTo('wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::params + */ + public function can_get_params_with_one_server() + { + $command = new Pong('cable.virginm.net'); + + assertThat($command->params(), is(identicalTo('cable.virginm.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::params + */ + public function can_get_params_with_two_servers() + { + $command = new Pong('cable.virginm.net', 'wolfe.freenode.net'); + + assertThat($command->params(), is(identicalTo('cable.virginm.net wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::__toString + */ + public function can_cast_to_string_with_server1() + { + $command = new Pong('cable.virginm.net'); + + assertThat((string) $command, is(identicalTo('PONG cable.virginm.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::__toString + */ + public function can_cast_to_string_with_server1_and_server2() + { + $command = new Pong('cable.virginm.net', 'wolfe.freenode.net'); + + assertThat((string) $command, is(identicalTo('PONG cable.virginm.net wolfe.freenode.net'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Pong::fromParams + */ + public function can_construct_from_params() + { + $command = Pong::fromParams('cable.virginm.net wolfe.freenode.net'); + + assertThat($command->command(), is(identicalTo('PONG'))); + assertThat($command->server(), is(identicalTo('cable.virginm.net'))); + assertThat($command->server2(), is(identicalTo('wolfe.freenode.net'))); + assertThat((string) $command, is(identicalTo('PONG cable.virginm.net wolfe.freenode.net'))); + } +} diff --git a/tests/Irc/Commands/PrivmsgTest.php b/tests/Irc/Commands/PrivmsgTest.php new file mode 100644 index 0000000..3668f4f --- /dev/null +++ b/tests/Irc/Commands/PrivmsgTest.php @@ -0,0 +1,98 @@ +command(), is(identicalTo('PRIVMSG'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::targets + */ + public function can_get_the_targets() + { + $command = new Privmsg(['#phpoxford', '#phpoxfordgames'], 'The lock file does my fucking head in'); + + assertThat($command->targets(), is(identicalTo(['#phpoxford', '#phpoxfordgames']))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::text + */ + public function can_get_the_text() + { + $command = new Privmsg(['#phpoxford'], 'The lock file does my fucking head in'); + + assertThat($command->text(), is(identicalTo('The lock file does my fucking head in'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::params + */ + public function can_get_params_with_one_target() + { + $command = new Privmsg(['#phpoxford'], 'The lock file does my fucking head in'); + + assertThat($command->params(), is(identicalTo('#phpoxford :The lock file does my fucking head in'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::params + */ + public function can_get_params_with_two_targets() + { + $command = new Privmsg(['#phpoxford', '#phpoxfordgames'], 'The lock file does my fucking head in'); + + assertThat($command->params(), is(identicalTo('#phpoxford,#phpoxfordgames :The lock file does my fucking head in'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::__toString + */ + public function can_cast_to_string_with_one_target() + { + $command = new Privmsg(['#phpoxford'], 'The lock file does my fucking head in'); + + assertThat((string) $command, is(identicalTo('PRIVMSG #phpoxford :The lock file does my fucking head in'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::__toString + */ + public function can_cast_to_string_with_two_targets() + { + $command = new Privmsg(['#phpoxford', '#phpoxfordgames'], 'The lock file does my fucking head in'); + + assertThat((string) $command, is(identicalTo('PRIVMSG #phpoxford,#phpoxfordgames :The lock file does my fucking head in'))); + } + +} diff --git a/tests/Irc/Message/PrefixTest.php b/tests/Irc/Message/PrefixTest.php new file mode 100644 index 0000000..beae868 --- /dev/null +++ b/tests/Irc/Message/PrefixTest.php @@ -0,0 +1,149 @@ +nickname(), is(identicalTo('nickname'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::username + */ + public function can_get_username() + { + $prefix = new Prefix(null, 'username'); + + assertThat($prefix->username(), is(identicalTo('username'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::hostname + */ + public function can_get_hostname() + { + $prefix = new Prefix(null, null, 'hostname.com'); + + assertThat($prefix->hostname(), is(identicalTo('hostname.com'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::servername + */ + public function can_get_servername() + { + $prefix = new Prefix(null, null, null, 'servername.com'); + + assertThat($prefix->servername(), is(identicalTo('servername.com'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::none + */ + public function factory_method_for_empty_prefix() + { + $prefix = Prefix::none(); + + assertThat($prefix->nickname(), is(emptyString())); + assertThat($prefix->username(), is(emptyString())); + assertThat($prefix->hostname(), is(emptyString())); + assertThat($prefix->servername(), is(emptyString())); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::user + */ + public function factory_method_for_user_prefix() + { + $prefix = Prefix::user('nickname', 'username', 'hostname.com'); + + assertThat($prefix->nickname(), is(identicalTo('nickname'))); + assertThat($prefix->username(), is(identicalTo('username'))); + assertThat($prefix->hostname(), is(identicalTo('hostname.com'))); + assertThat($prefix->servername(), is(emptyString())); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::server + */ + public function factory_method_for_server_prefix() + { + $prefix = Prefix::server('servername.com'); + + assertThat($prefix->nickname(), is(emptyString())); + assertThat($prefix->username(), is(emptyString())); + assertThat($prefix->hostname(), is(emptyString())); + assertThat($prefix->servername(), is(identicalTo('servername.com'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::raw + */ + public function can_get_the_raw_prefix_string_for_empty_prefix() + { + $prefix = Prefix::none(); + + assertThat($prefix->raw(), is(emptyString())); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::raw + */ + public function can_get_the_raw_prefix_string_for_user_prefix_with_all_set() + { + $prefix = Prefix::user('nickname', 'username', 'hostname.com'); + + assertThat($prefix->raw(), is(identicalTo(':nickname!username@hostname.com'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::raw + */ + public function can_get_the_raw_prefix_string_for_user_prefix_with_nick_and_user_set() + { + $prefix = Prefix::user('nickname', 'username', null); + + assertThat($prefix->raw(), is(identicalTo(':nickname!username'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::raw + */ + public function can_get_the_raw_prefix_string_for_user_prefix_with_nick_and_host_set() + { + $prefix = Prefix::user('nickname', null, 'hostname.com'); + + assertThat($prefix->raw(), is(identicalTo(':nickname@hostname.com'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message\Prefix::raw + */ + public function can_get_the_raw_prefix_string_for_server_prefix() + { + $prefix = Prefix::server('servername.com'); + + assertThat($prefix->raw(), is(identicalTo(':servername.com'))); + } +} diff --git a/tests/Irc/MessageTest.php b/tests/Irc/MessageTest.php new file mode 100644 index 0000000..730c668 --- /dev/null +++ b/tests/Irc/MessageTest.php @@ -0,0 +1,67 @@ +prefix(), is(anInstanceOf(Prefix::class))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message::command + */ + public function holds_command_instance() + { + $message = new Message( + Prefix::none(), + new Command('PING', 'blah.freenode.com') + ); + + assertThat($message->command(), is(anInstanceOf(CommandInterface::class))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message::raw + */ + public function can_get_the_raw_message_without_prefix() + { + $message = new Message( + Prefix::none(), + new Command('PING', 'blah.freenode.com') + ); + + assertThat($message->raw(), is(identicalTo('PING blah.freenode.com'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Message::raw + */ + public function can_get_the_raw_message_with_prefix() + { + $message = new Message( + Prefix::user('nickname', 'username', 'hostname.com'), + new Command('PING', 'blah.freenode.com') + ); + + assertThat($message->raw(), is(identicalTo(':nickname!username@hostname.com PING blah.freenode.com'))); + } +} diff --git a/tests/Irc/ParserTest.php b/tests/Irc/ParserTest.php new file mode 100644 index 0000000..798c327 --- /dev/null +++ b/tests/Irc/ParserTest.php @@ -0,0 +1,210 @@ +parse($message); + + assertThat($parse, is(identicalTo($expected))); + } + + public function dataMessages() + { + return [ + 'Notice' => [ + ":sendak.freenode.net NOTICE * :*** Found your hostname\r\n", + [ + 'prefix' => ':sendak.freenode.net', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => 'sendak.freenode.net', + 'command' => 'NOTICE', + 'params' => '* :*** Found your hostname', + ] + ], + 'Command with no params' => [ + "QUIT\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'QUIT', + 'params' => '', + ] + ], + 'Command with only one param' => [ + "NICK dilling\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'dilling', + ] + ], + 'Command with multiple params' => [ + "USER guest 0 * :Ronnie Reagan\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'USER', + 'params' => 'guest 0 * :Ronnie Reagan', + ] + ], + 'Only first part of the prefix present, then it is the servername' => [ + ":asimov.freenode.net QUIT\r\n", + [ + 'prefix' => ':asimov.freenode.net', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => 'asimov.freenode.net', + 'command' => 'QUIT', + 'params' => '', + ] + ], + 'First two parts of the prefix present, then it is the nickname and username' => [ + ":dilling!~dilling JOIN #phpoxford\r\n", + [ + 'prefix' => ':dilling!~dilling', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => '', + 'servername' => '', + 'command' => 'JOIN', + 'params' => '#phpoxford', + ] + ], + 'All three parts of the prefix present, then it is the nickname, username and hostname' => [ + ":dilling!~dilling@cable.virginm.net NICK martindilling\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'martindilling', + ] + ], + 'The hostname can be a IPv4 address' => [ + ":dilling!~dilling@168.12.8.204 NICK martindilling\r\n", + [ + 'prefix' => ':dilling!~dilling@168.12.8.204', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => '168.12.8.204', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'martindilling', + ] + ], + 'The hostname can be a IPv6 address' => [ + ":dilling!~dilling@2001:0db8:0a0b:12f0:0000:0000:0000:0001 NICK martindilling\r\n", + [ + 'prefix' => ':dilling!~dilling@2001:0db8:0a0b:12f0:0000:0000:0000:0001', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => '2001:0db8:0a0b:12f0:0000:0000:0000:0001', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'martindilling', + ] + ], + 'Recognises really long messages' => [ + ":dilling!~dilling@cable.virginm.net PRIVMSG ascii-soup :This is a really really long message! " . + "It is longer than most message, so I really hope this works as it should\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'PRIVMSG', + 'params' => 'ascii-soup :This is a really really long message! ' . + 'It is longer than most message, so I really hope this works as it should', + ] + ], + 'Safety: PING command' => [ + "PING :wolfe.freenode.net\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'PING', + 'params' => ':wolfe.freenode.net', + ] + ], + 'Safety: NICK command' => [ + ":dilling!~dilling@cable.virginm.net NICK :imchanged\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'NICK', + 'params' => ':imchanged', + ] + ], + 'Safety: JOIN command' => [ + ":dilling!~dilling@cable.virginm.net JOIN #phpoxford\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'JOIN', + 'params' => '#phpoxford', + ] + ], + 'Safety: PART command' => [ + ":dilling!~dilling@cable.virginm.net PART #phpoxford :Leaving\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'PART', + 'params' => '#phpoxford :Leaving', + ] + ], + 'Safety: QUIT command' => [ + ":dilling!~dilling@cable.virginm.net QUIT :Quit: I'm outta here\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'QUIT', + 'params' => ':Quit: I\'m outta here', + ] + ], + ]; + } +}