From 13487d783b58a2c16c4913b0b082df06ce667256 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 2 Feb 2016 23:21:19 +0000 Subject: [PATCH 01/15] The Augmented BNF for messages converted to regex Mostly direct translation of the Augmented BNF for an IRC message to a giant regex. It takes a raw IRC message and returns an array with: `prefix` // The whole prefix string `nickname` // The nickname from the prefix `username` // The username from the prefix `hostname` // The hostname from the prefix `servername` // The servername from the prefix `command` // The command `params` // The parameters I'm pretty sure this can be optimised quite a lot, but for now it parses following the specification and it works ;) --- src/Irc/Parser.php | 106 +++++++++++++++++++++ tests/Irc/ParserTest.php | 198 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 src/Irc/Parser.php create mode 100644 tests/Irc/ParserTest.php diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php new file mode 100644 index 0000000..a825623 --- /dev/null +++ b/src/Irc/Parser.php @@ -0,0 +1,106 @@ +$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/tests/Irc/ParserTest.php b/tests/Irc/ParserTest.php new file mode 100644 index 0000000..034c89e --- /dev/null +++ b/tests/Irc/ParserTest.php @@ -0,0 +1,198 @@ +parse($message); + + assertThat($parse, is(identicalTo($expected))); + } + + public function dataMessages() + { + return [ + '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', + ] + ], + ]; + } +} From 0ed04426062959ab11ee514e71eb01098e4eca4b Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 2 Feb 2016 23:35:19 +0000 Subject: [PATCH 02/15] Code style fix: Some lines were too long --- src/Irc/Parser.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php index a825623..0deab8f 100644 --- a/src/Irc/Parser.php +++ b/src/Irc/Parser.php @@ -50,7 +50,8 @@ public function parse(string $raw): array // = *14( SPACE middle ) [ SPACE ":" trailing ] // =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ] - $params = "(?:(?:$space$middle){0,14}(?:$space$colon$trailing)?|(?:$space$middle){14}(?:$space(?:$colon)?$trailing)?)"; + $params = "(?:(?:$space$middle){0,14}(?:$space$colon$trailing)?" . + "|(?:$space$middle){14}(?:$space(?:$colon)?$trailing)?)"; // = ( letter / digit ) *( letter / digit / "-" ) *( letter / digit ) $shortname = "(?:[$letter$digit][$letter$digit$dash]*[$letter$digit]*)"; @@ -81,7 +82,8 @@ public function parse(string $raw): array $user = "(?:[\x01-\x09|\x0B-\x0C|\x0E-\x1F|\x21-\x3F|\x41-\xFF]+)"; // = servername / ( nickname [ [ "!" user ] "@" host ] ) - $prefix = "(?:(?P$servername)|(?:(?P$nickname)(?:$bang(?P$user))?(?:$at(?P$host))?))"; + $prefix = "(?:(?P$servername)" . + "|(?:(?P$nickname)(?:$bang(?P$user))?(?:$at(?P$host))?))"; // = [ ":" prefix SPACE ] command [ params ] crlf $message = "(?P$colon$prefix$space)?(?P$command)(?P$params)?$crlf"; From 91124989eec7bb5bd553f12aed807557a7ce8d56 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Wed, 3 Feb 2016 00:04:55 +0000 Subject: [PATCH 03/15] Tweak dependency versions to work with --prefer-lowest Needed to explicitly set the minimum version we need of the phpunit/php-token-stream package. Probably somewhere in the phpunit dependency tree it have specified a minimum version of that package that doesn't support PHP 7. --- composer.json | 5 +++-- composer.lock | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) 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": [] } From 45f6762c97d5585c891b134d261273f27719b073 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Thu, 4 Feb 2016 17:25:18 +0000 Subject: [PATCH 04/15] Message and Command objects --- src/Irc/Commands/Command.php | 39 ++++++++ src/Irc/Message.php | 40 ++++++++ src/Irc/Message/Command.php | 12 +++ src/Irc/Message/Prefix.php | 86 +++++++++++++++++ tests/Irc/Commands/CommandTest.php | 64 +++++++++++++ tests/Irc/Message/PrefixTest.php | 149 +++++++++++++++++++++++++++++ tests/Irc/MessageTest.php | 67 +++++++++++++ 7 files changed, 457 insertions(+) create mode 100644 src/Irc/Commands/Command.php create mode 100644 src/Irc/Message.php create mode 100644 src/Irc/Message/Command.php create mode 100644 src/Irc/Message/Prefix.php create mode 100644 tests/Irc/Commands/CommandTest.php create mode 100644 tests/Irc/Message/PrefixTest.php create mode 100644 tests/Irc/MessageTest.php diff --git a/src/Irc/Commands/Command.php b/src/Irc/Commands/Command.php new file mode 100644 index 0000000..376d101 --- /dev/null +++ b/src/Irc/Commands/Command.php @@ -0,0 +1,39 @@ +command = $command; + $this->params = $params ?: ''; + } + + public function command() : string + { + return $this->command; + } + + public function params() : string + { + return $this->params; + } + + public function raw() : string + { + return trim($this->command() . ' ' . $this->params()); + } +} diff --git a/src/Irc/Message.php b/src/Irc/Message.php new file mode 100644 index 0000000..5dc009e --- /dev/null +++ b/src/Irc/Message.php @@ -0,0 +1,40 @@ +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() . ' ' . $this->command->raw()); + } +} diff --git a/src/Irc/Message/Command.php b/src/Irc/Message/Command.php new file mode 100644 index 0000000..96803e8 --- /dev/null +++ b/src/Irc/Message/Command.php @@ -0,0 +1,12 @@ +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/tests/Irc/Commands/CommandTest.php b/tests/Irc/Commands/CommandTest.php new file mode 100644 index 0000000..e4501c0 --- /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::raw + */ + public function can_get_the_raw_command_without_params() + { + $command = new Command('QUIT'); + + assertThat($command->raw(), is(identicalTo('QUIT'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::raw + */ + public function can_get_the_raw_command_with_params() + { + $command = new Command('USER', 'guest 0 * :Ronnie Reagan'); + + assertThat($command->raw(), is(identicalTo('USER guest 0 * :Ronnie Reagan'))); + } +} 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'))); + } +} From 376709749d01d4169a6469962d3fc749a82c438c Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Fri, 5 Feb 2016 17:30:19 +0000 Subject: [PATCH 05/15] Privmsg Command --- src/EMPTY | 0 src/Irc/Commands/Command.php | 4 +-- src/Irc/Commands/Privmsg.php | 51 +++++++++++++++++++++++++++++ tests/Irc/Commands/PrivmsgTest.php | 52 ++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 2 deletions(-) delete mode 100644 src/EMPTY create mode 100644 src/Irc/Commands/Privmsg.php create mode 100644 tests/Irc/Commands/PrivmsgTest.php diff --git a/src/EMPTY b/src/EMPTY deleted file mode 100644 index e69de29..0000000 diff --git a/src/Irc/Commands/Command.php b/src/Irc/Commands/Command.php index 376d101..d12c715 100644 --- a/src/Irc/Commands/Command.php +++ b/src/Irc/Commands/Command.php @@ -9,12 +9,12 @@ class Command implements CommandInterface /** * @var string */ - private $command; + protected $command; /** * @var string */ - private $params; + protected $params; public function __construct(string $command, string $params = null) { diff --git a/src/Irc/Commands/Privmsg.php b/src/Irc/Commands/Privmsg.php new file mode 100644 index 0000000..6e36b02 --- /dev/null +++ b/src/Irc/Commands/Privmsg.php @@ -0,0 +1,51 @@ +target = $target; + $this->text = ltrim($text, ':'); + } + + public function command() : string + { + return $this->command; + } + + public function params() : string + { + return $this->params; + } + + public function raw() : string + { + return trim($this->command() . ' ' . $this->params()); + } + + public function target() : string + { + return $this->target; + } + + public function text() + { + return $this->text; + } +} diff --git a/tests/Irc/Commands/PrivmsgTest.php b/tests/Irc/Commands/PrivmsgTest.php new file mode 100644 index 0000000..f815437 --- /dev/null +++ b/tests/Irc/Commands/PrivmsgTest.php @@ -0,0 +1,52 @@ +command(), is(identicalTo('PRIVMSG'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::params + */ + public function can_get_the_params_string() + { + $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_the_target_string() + { + $command = new Privmsg('#phpoxford :+The lock file does my fucking head in'); + + assertThat($command->target(), is(identicalTo('#phpoxford'))); + } + + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::params + */ + public function can_get_the_text_string() + { + $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'))); + } +} From 65cd6fb62b8c9aad2c0e6b0de7621294cf70c4e6 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Mon, 8 Feb 2016 17:37:19 +0000 Subject: [PATCH 06/15] Basic run script that connects to a channel, and small fix to parser regex --- composer.json | 2 +- run.php | 105 +++++++++++++++++++++++++++++++++++++++ src/Irc/Parser.php | 8 ++- tests/Irc/ParserTest.php | 12 +++++ 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 run.php diff --git a/composer.json b/composer.json index 1fb70d4..82d3f87 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": "^7.0" + "php": "^5.6|^7.0" }, "require-dev": { "phpunit/phpunit": "~5.1.0", diff --git a/run.php b/run.php new file mode 100644 index 0000000..dceaf23 --- /dev/null +++ b/run.php @@ -0,0 +1,105 @@ +parse($raw . "\r\n"); + var_export($message); + + if ($message['command'] === 'PING') { + $response = "PONG {$message['params']}\r\n"; + socket_write($socket, $response); + echo '[response]: ' . $response; + } + + if ($message['command'] === 'PRIVMSG') { + list($target, $msg) = explode(' :', $message['params'], 2); + + if ($target === $channel && strpos($msg, "hi spires") === 0) { + $response = "PRIVMSG {$channel} :Hello {$message['nickname']}\r\n"; + socket_write($socket, $response); + echo '[response]: ' . $response; + } + if ($target === $channel && strpos($msg, '!spires') !== false) { + $response = "PRIVMSG {$channel} :Sorry {$message['nickname']}, I'm stupid and can only say hello :(\r\n"; + socket_write($socket, $response); + echo '[response]: ' . $response; + } + } +} + + + + +//// Edit these settings +//$chan = "#php"; +//$server = "127.0.0.1"; +//$port = 6667; +//$nick = "PHP_Bot"; +// +//// STOP EDITTING NOW. +//$socket = fsockopen("$server", $port); +//fputs($socket,"USER $nick $nick $nick $nick :$nick\n"); +//fputs($socket,"NICK $nick\n"); +//fputs($socket,"JOIN ".$chan."\n"); +// +//while(1) { +// while($data = fgets($socket)) { +// echo nl2br($data); +// flush(); +// +// $ex = explode(' ', $data); +// $rawcmd = explode(':', $ex[3]); +// $oneword = explode('
', $rawcmd); +// $channel = $ex[2]; +// $nicka = explode('@', $ex[0]); +// $nickb = explode('!', $nicka[0]); +// $nickc = explode(':', $nickb[0]); +// +// $host = $nicka[1]; +// $nick = $nickc[1]; +// if($ex[0] == "PING"){ +// fputs($socket, "PONG ".$ex[1]."\n"); +// } +// +// $args = NULL; for ($i = 4; $i < count($ex); $i++) { $args .= $ex[$i] . ' '; } +// +// if ($rawcmd[1] == "!sayit") { +// fputs($socket, "PRIVMSG ".$channel." :".$args." \n"); +// } +// elseif ($rawcmd[1] == "!md5") { +// fputs($socket, "PRIVMSG ".$channel." :MD5 ".md5($args)."\n"); +// } +// } +//} diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php index 0deab8f..b01e342 100644 --- a/src/Irc/Parser.php +++ b/src/Irc/Parser.php @@ -76,7 +76,11 @@ public function parse(string $raw): array $host = "(?:$hostname|$hostaddr)"; // = ( letter / special ) *8( letter / digit / special / "-" ) - $nickname = "(?:[$letter$special][$letter$digit$special$dash]{0,8})"; + // * While the maximum length is limited to nine characters, clients + // * SHOULD accept longer strings as they may become used in future + // * evolutions of the protocol. + // * https://tools.ietf.org/html/rfc2812#section-1.2.1 + $nickname = "(?:[$letter$special][$letter$digit$special$dash]*)"; // = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF ) ; any octet except NUL, CR, LF, " " and "@" $user = "(?:[\x01-\x09|\x0B-\x0C|\x0E-\x1F|\x21-\x3F|\x41-\xFF]+)"; @@ -101,7 +105,7 @@ public function parse(string $raw): array 'username' => $matches['username'] ?? '', 'hostname' => $matches['hostname'] ?? '', 'servername' => $matches['servername'] ?? '', - 'command' => $matches['command'], + 'command' => $matches['command'] ?? '', 'params' => $matches['params'] ?? '', ]; } diff --git a/tests/Irc/ParserTest.php b/tests/Irc/ParserTest.php index 034c89e..798c327 100644 --- a/tests/Irc/ParserTest.php +++ b/tests/Irc/ParserTest.php @@ -23,6 +23,18 @@ public function can_parse_valid_messages_to_an_array_of_data($message, $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", [ From 78720bc5c8280ce62ec83990406d1fa9f99834c0 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 9 Feb 2016 00:34:50 +0000 Subject: [PATCH 07/15] Broken and messy. Very WIP xD --- run.php | 97 +++++++++++++++++-------- src/Irc/Commands/Base.php | 23 ++++++ src/Irc/Commands/Command.php | 10 +-- src/Irc/Commands/Ping.php | 54 ++++++++++++++ src/Irc/Commands/Pong.php | 55 ++++++++++++++ src/Irc/Commands/Privmsg.php | 45 ++++++------ src/Irc/Connection.php | 43 +++++++++++ src/Irc/Message.php | 2 +- src/Irc/Message/Command.php | 6 +- src/Irc/Message/Prefix.php | 5 -- src/Irc/User.php | 54 ++++++++++++++ src/IrcClient.php | 65 +++++++++++++++++ tests/Irc/Commands/CommandTest.php | 12 ++-- tests/Irc/Commands/PingTest.php | 111 +++++++++++++++++++++++++++++ tests/Irc/Commands/PongTest.php | 111 +++++++++++++++++++++++++++++ tests/Irc/Commands/PrivmsgTest.php | 68 +++++++++++++++--- 16 files changed, 679 insertions(+), 82 deletions(-) create mode 100644 src/Irc/Commands/Base.php create mode 100644 src/Irc/Commands/Ping.php create mode 100644 src/Irc/Commands/Pong.php create mode 100644 src/Irc/Connection.php create mode 100644 src/Irc/User.php create mode 100644 src/IrcClient.php create mode 100644 tests/Irc/Commands/PingTest.php create mode 100644 tests/Irc/Commands/PongTest.php diff --git a/run.php b/run.php index dceaf23..f128c2e 100644 --- a/run.php +++ b/run.php @@ -1,5 +1,15 @@ connect(); $parser = new \PHPOxford\SpiresIrc\Irc\Parser(); -while ($raw = socket_read($socket, 2048, PHP_NORMAL_READ)) { +while ($raw = $client->read()) { if (!$raw = trim($raw)) { continue; } echo "\n\n"; + echo "Raw: {$raw}"; + echo "\n"; $message = $parser->parse($raw . "\r\n"); var_export($message); - if ($message['command'] === 'PING') { - $response = "PONG {$message['params']}\r\n"; - socket_write($socket, $response); - echo '[response]: ' . $response; + $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 'PRIVMSG': + $message = new Message( + $prefix, + Privmsg::fromParams($message['params']) + ); + break; + + default: + $message = new Message( + $prefix, + new Command($message['command'], $message['params']) + ); + } + echo "\n"; + var_dump($message); + + + if ($message instanceof Ping) { + $response = Pong::fromParams($message['params']); + $client->write($response . "\r\n"); + echo "\n".'[response]: ' . $response; } - if ($message['command'] === 'PRIVMSG') { - list($target, $msg) = explode(' :', $message['params'], 2); + if ($message instanceof Privmsg) { +// list($target, $msg) = explode(' :', $message['params'], 2); - if ($target === $channel && strpos($msg, "hi spires") === 0) { - $response = "PRIVMSG {$channel} :Hello {$message['nickname']}\r\n"; - socket_write($socket, $response); - echo '[response]: ' . $response; + if (in_array($client->connection()->channel(), $message->targets()) && strpos($message->text(), "hi spires") === 0) { + $response = "PRIVMSG {$client->connection()->channel()} :Hello {$message->prefix()->nickname()}\r\n"; + $client->write($response); + echo "\n".'[response]: ' . $response; } - if ($target === $channel && strpos($msg, '!spires') !== false) { - $response = "PRIVMSG {$channel} :Sorry {$message['nickname']}, I'm stupid and can only say hello :(\r\n"; - socket_write($socket, $response); - echo '[response]: ' . $response; + if (in_array($client->connection()->channel(), $message->targets()) && strpos($message->text(), '!spires') !== false) { + $response = "PRIVMSG {$client->connection()->channel()} :Sorry {$message->prefix()->nickname()}, I'm stupid and can only say hello :(\r\n"; + $client->write($response); + echo "\n".'[response]: ' . $response; } } } diff --git a/src/Irc/Commands/Base.php b/src/Irc/Commands/Base.php new file mode 100644 index 0000000..3470653 --- /dev/null +++ b/src/Irc/Commands/Base.php @@ -0,0 +1,23 @@ +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 index d12c715..4f1127f 100644 --- a/src/Irc/Commands/Command.php +++ b/src/Irc/Commands/Command.php @@ -4,7 +4,7 @@ use PHPOxford\SpiresIrc\Irc\Message\Command as CommandInterface; -class Command implements CommandInterface +class Command extends Base implements CommandInterface { /** * @var string @@ -22,9 +22,9 @@ public function __construct(string $command, string $params = null) $this->params = $params ?: ''; } - public function command() : string + public static function fromParams(string $params) : self { - return $this->command; + // TODO: Implement fromParams() method. } public function params() : string @@ -32,8 +32,4 @@ public function params() : string return $this->params; } - public function raw() : string - { - return trim($this->command() . ' ' . $this->params()); - } } diff --git a/src/Irc/Commands/Ping.php b/src/Irc/Commands/Ping.php new file mode 100644 index 0000000..dbf849e --- /dev/null +++ b/src/Irc/Commands/Ping.php @@ -0,0 +1,54 @@ +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..fab43bc --- /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 index 6e36b02..32d3b00 100644 --- a/src/Irc/Commands/Privmsg.php +++ b/src/Irc/Commands/Privmsg.php @@ -2,50 +2,53 @@ namespace PHPOxford\SpiresIrc\Irc\Commands; -class Privmsg extends Command +use PHPOxford\SpiresIrc\Irc\Message\Command as CommandInterface; + +class Privmsg extends Base implements CommandInterface { /** * @var string */ - private $target; + protected $command = 'PRIVMSG'; + + /** + * @var array + */ + private $targets; /** * @var string */ private $text; - public function __construct(string $params = null) + public function __construct(array $targets, string $text) { - parent::__construct('PRIVMSG', $params); - - list($target, $text) = explode(' ', $params, 2); - - $this->target = $target; - $this->text = ltrim($text, ':'); + $this->targets = $targets; + $this->text = $text; } - public function command() : string + public static function fromParams(string $params) : self { - return $this->command; - } + list($targets, $text) = explode(' ', $params, 2); - public function params() : string - { - return $this->params; - } + $targets = explode(',', $targets); + $text = ltrim($text, ':'); - public function raw() : string - { - return trim($this->command() . ' ' . $this->params()); + return new static($targets, $text); } - public function target() : string + public function targets() : array { - return $this->target; + return $this->targets; } public function text() { return $this->text; } + + public function params() : string + { + return implode(',', $this->targets) . ' :' . $this->text; + } } diff --git a/src/Irc/Connection.php b/src/Irc/Connection.php new file mode 100644 index 0000000..7900045 --- /dev/null +++ b/src/Irc/Connection.php @@ -0,0 +1,43 @@ +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 index 5dc009e..ee9f421 100644 --- a/src/Irc/Message.php +++ b/src/Irc/Message.php @@ -35,6 +35,6 @@ public function command() : Command public function raw() { - return trim($this->prefix->raw() . ' ' . $this->command->raw()); + return trim($this->prefix->raw() . ' ' . (string) $this->command); } } diff --git a/src/Irc/Message/Command.php b/src/Irc/Message/Command.php index 96803e8..281fc1b 100644 --- a/src/Irc/Message/Command.php +++ b/src/Irc/Message/Command.php @@ -2,11 +2,15 @@ namespace PHPOxford\SpiresIrc\Irc\Message; +use PHPOxford\SpiresIrc\Irc\Message\Command as CommandInterface; + interface Command { + public static function fromParams(string $params); + public function command() : string; public function params() : string; - public function raw() : string; + public function __toString() : string; } diff --git a/src/Irc/Message/Prefix.php b/src/Irc/Message/Prefix.php index 8da8312..0ad6429 100644 --- a/src/Irc/Message/Prefix.php +++ b/src/Irc/Message/Prefix.php @@ -4,11 +4,6 @@ class Prefix { - /** - * @var string - */ - private $raw; - /** * @var string */ diff --git a/src/Irc/User.php b/src/Irc/User.php new file mode 100644 index 0000000..b591602 --- /dev/null +++ b/src/Irc/User.php @@ -0,0 +1,54 @@ +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..ae6cd47 --- /dev/null +++ b/src/IrcClient.php @@ -0,0 +1,65 @@ +connection = $connection; + $this->user = $user; + } + + public function connection() : Connection + { + return $this->connection; + } + + 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() + ); + + socket_write($this->socket, "NICK {$this->user()->nickname()}\r\n"); + socket_write($this->socket, "USER {$this->user()->username()} {$this->user()->usermode()} * :{$this->user()->realname()}\r\n"); + socket_write($this->socket, "JOIN {$this->connection()->channel()}\r\n"); + } + + public function read() + { + return socket_read($this->socket, 2048, PHP_NORMAL_READ); + } + + public function write(string $response) + { + return socket_write($this->socket, $response); + } +} diff --git a/tests/Irc/Commands/CommandTest.php b/tests/Irc/Commands/CommandTest.php index e4501c0..c40cb22 100644 --- a/tests/Irc/Commands/CommandTest.php +++ b/tests/Irc/Commands/CommandTest.php @@ -42,23 +42,23 @@ public function can_get_the_params_string() /** * @test - * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::raw + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::__toString */ - public function can_get_the_raw_command_without_params() + public function can_cast_to_string_without_params() { $command = new Command('QUIT'); - assertThat($command->raw(), is(identicalTo('QUIT'))); + assertThat((string) $command, is(identicalTo('QUIT'))); } /** * @test - * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::raw + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Command::__toString */ - public function can_get_the_raw_command_with_params() + public function can_cast_to_string_with_params() { $command = new Command('USER', 'guest 0 * :Ronnie Reagan'); - assertThat($command->raw(), is(identicalTo('USER guest 0 * :Ronnie Reagan'))); + assertThat((string) $command, is(identicalTo('USER guest 0 * :Ronnie Reagan'))); } } 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 index f815437..3668f4f 100644 --- a/tests/Irc/Commands/PrivmsgTest.php +++ b/tests/Irc/Commands/PrivmsgTest.php @@ -3,50 +3,96 @@ namespace PHPOxford\SpiresIrc\Tests\Irc\Commands; use PHPOxford\SpiresIrc\Irc\Commands\Privmsg; +use PHPOxford\SpiresIrc\Irc\Message\Command as CommandInterface; class PrivmsgTest extends \PHPUnit_Framework_TestCase { + /** + * @test + * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg + */ + public function implements_command_interface() + { + $command = new Privmsg(['#phpoxford'], 'The lock file does my fucking head in'); + + assertThat($command, is(anInstanceOf(CommandInterface::class))); + } + /** * @test * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::command */ public function can_get_the_command_string() { - $command = new Privmsg('#phpoxford :+The lock file does my fucking head in'); + $command = new Privmsg(['#phpoxford'], 'The lock file does my fucking head in'); assertThat($command->command(), is(identicalTo('PRIVMSG'))); } /** * @test - * @covers \PHPOxford\SpiresIrc\Irc\Commands\Privmsg::params + * @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_params_string() + public function can_get_the_text() { - $command = new Privmsg('#phpoxford :+The lock file does my fucking head in'); + $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'))); + 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_the_target_string() + public function can_get_params_with_one_target() { - $command = new Privmsg('#phpoxford :+The lock file does my fucking head in'); + $command = new Privmsg(['#phpoxford'], 'The lock file does my fucking head in'); - assertThat($command->target(), is(identicalTo('#phpoxford'))); + 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_the_text_string() + public function can_get_params_with_two_targets() { - $command = new Privmsg('#phpoxford :+The lock file does my fucking head in'); + $command = new Privmsg(['#phpoxford', '#phpoxfordgames'], 'The lock file does my fucking head in'); - assertThat($command->text(), is(identicalTo('+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'))); + } + } From 8cf74d62925712d67771cecf53f46bc0ad6e44fd Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 9 Feb 2016 16:54:19 +0000 Subject: [PATCH 08/15] Got most logic in the client --- run.php | 145 +++++++++++------------------------ src/Irc/Commands/Privmsg.php | 5 ++ src/IrcClient.php | 81 ++++++++++++++++++- 3 files changed, 129 insertions(+), 102 deletions(-) diff --git a/run.php b/run.php index f128c2e..966af40 100644 --- a/run.php +++ b/run.php @@ -1,12 +1,10 @@ connect(); -$parser = new \PHPOxford\SpiresIrc\Irc\Parser(); - -while ($raw = $client->read()) { - - if (!$raw = trim($raw)) { - continue; +// Ping Pong +$client->addAction(function (IrcClient $client, Message $message) { + if ($message->command() instanceof Ping) { + $response = Pong::fromParams($message->command()->params()); + $client->write($response); } - echo "\n\n"; - echo "Raw: {$raw}"; - echo "\n"; - $message = $parser->parse($raw . "\r\n"); - var_export($message); +}); - $prefix = new Prefix($message['nickname'], $message['username'], $message['hostname'], $message['servername']); +// (hi|hello|hey) spires +$client->addAction(function (IrcClient $client, Message $message) { + if ($message->command() instanceof Privmsg) { - switch ($message['command']) { - case 'PING': - $message = new Message( - $prefix, - Ping::fromParams($message['params']) - ); - break; + /** @var Privmsg $command */ + $command = $message->command(); - case 'PRIVMSG': - $message = new Message( - $prefix, - Privmsg::fromParams($message['params']) - ); - break; - - default: - $message = new Message( - $prefix, - new Command($message['command'], $message['params']) - ); - } - echo "\n"; - var_dump($message); - - - if ($message instanceof Ping) { - $response = Pong::fromParams($message['params']); - $client->write($response . "\r\n"); - echo "\n".'[response]: ' . $response; - } - - if ($message instanceof Privmsg) { -// list($target, $msg) = explode(' :', $message['params'], 2); - - if (in_array($client->connection()->channel(), $message->targets()) && strpos($message->text(), "hi spires") === 0) { - $response = "PRIVMSG {$client->connection()->channel()} :Hello {$message->prefix()->nickname()}\r\n"; - $client->write($response); - echo "\n".'[response]: ' . $response; + if ($command->hasTarget($client->channel())) { + if (preg_match('/^(hi|hello|hey) spires$/i', $command->text())) { + $response = new Privmsg([$client->channel()], "Hello {$message->prefix()->nickname()}"); + $client->write($response); + } } - if (in_array($client->connection()->channel(), $message->targets()) && strpos($message->text(), '!spires') !== false) { - $response = "PRIVMSG {$client->connection()->channel()} :Sorry {$message->prefix()->nickname()}, I'm stupid and can only say hello :(\r\n"; - $client->write($response); - echo "\n".'[response]: ' . $response; + } +}); + +// !spires what time is it? +$client->addAction(function (IrcClient $client, Message $message) { + if ($message->command() instanceof Privmsg) { + /** @var Privmsg $command */ + $command = $message->command(); + + if ($command->hasTarget($client->channel())) { + if (preg_match('/^!spires (?P.+)/i', $command->text(), $matches)) { + if (preg_match('/^what time is it\??/i', $matches['match'])) { + $client->write(new Privmsg([$client->channel()], "It's")); + $dancer = ' \o/'; + foreach ([1,2,3] as $i) { + usleep(1000000/$i); + $client->write(new Privmsg([$client->channel()], substr($dancer, -1*$i, $i))); + $dancer = $dancer == ' \o/' ? ' /o\\' : ' \o/'; + } + ++$i; + usleep(1000000/$i); + $client->write(new Privmsg([$client->channel()], substr($dancer, -1*$i, $i) . " HAMMER TIME!")); + } + } } } -} - - - +}); -//// Edit these settings -//$chan = "#php"; -//$server = "127.0.0.1"; -//$port = 6667; -//$nick = "PHP_Bot"; -// -//// STOP EDITTING NOW. -//$socket = fsockopen("$server", $port); -//fputs($socket,"USER $nick $nick $nick $nick :$nick\n"); -//fputs($socket,"NICK $nick\n"); -//fputs($socket,"JOIN ".$chan."\n"); -// -//while(1) { -// while($data = fgets($socket)) { -// echo nl2br($data); -// flush(); -// -// $ex = explode(' ', $data); -// $rawcmd = explode(':', $ex[3]); -// $oneword = explode('
', $rawcmd); -// $channel = $ex[2]; -// $nicka = explode('@', $ex[0]); -// $nickb = explode('!', $nicka[0]); -// $nickc = explode(':', $nickb[0]); -// -// $host = $nicka[1]; -// $nick = $nickc[1]; -// if($ex[0] == "PING"){ -// fputs($socket, "PONG ".$ex[1]."\n"); -// } -// -// $args = NULL; for ($i = 4; $i < count($ex); $i++) { $args .= $ex[$i] . ' '; } -// -// if ($rawcmd[1] == "!sayit") { -// fputs($socket, "PRIVMSG ".$channel." :".$args." \n"); -// } -// elseif ($rawcmd[1] == "!md5") { -// fputs($socket, "PRIVMSG ".$channel." :MD5 ".md5($args)."\n"); -// } -// } -//} +$client->run(); diff --git a/src/Irc/Commands/Privmsg.php b/src/Irc/Commands/Privmsg.php index 32d3b00..540a687 100644 --- a/src/Irc/Commands/Privmsg.php +++ b/src/Irc/Commands/Privmsg.php @@ -51,4 +51,9 @@ public function params() : string { return implode(',', $this->targets) . ' :' . $this->text; } + + public function hasTarget($target) + { + return in_array($target, $this->targets()); + } } diff --git a/src/IrcClient.php b/src/IrcClient.php index ae6cd47..15f5005 100644 --- a/src/IrcClient.php +++ b/src/IrcClient.php @@ -2,7 +2,13 @@ namespace PHPOxford\SpiresIrc; +use PHPOxford\SpiresIrc\Irc\Commands\Command; +use PHPOxford\SpiresIrc\Irc\Commands\Ping; +use PHPOxford\SpiresIrc\Irc\Commands\Privmsg; use PHPOxford\SpiresIrc\Irc\Connection; +use PHPOxford\SpiresIrc\Irc\Message; +use PHPOxford\SpiresIrc\Irc\Message\Prefix; +use PHPOxford\SpiresIrc\Irc\Parser; use PHPOxford\SpiresIrc\Irc\User; class IrcClient @@ -11,11 +17,17 @@ class IrcClient * @var Connection */ private $connection; + /** * @var User */ private $user; + /** + * @var array + */ + private $actions = []; + private $socket; public function __construct(Connection $connection, User $user) @@ -29,6 +41,11 @@ public function connection() : Connection return $this->connection; } + public function channel() : string + { + return $this->connection()->channel(); + } + public function user() : User { return $this->user; @@ -53,6 +70,11 @@ public function connect() socket_write($this->socket, "JOIN {$this->connection()->channel()}\r\n"); } + public function addAction($callback) + { + $this->actions[] = $callback; + } + public function read() { return socket_read($this->socket, 2048, PHP_NORMAL_READ); @@ -60,6 +82,63 @@ public function read() public function write(string $response) { - return socket_write($this->socket, $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 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 '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->actions as $action) { + $action($this, $message); + } + } } } From 1c4726f2558c50dd231994e55f9da58044ae8e7f Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 9 Feb 2016 18:25:02 +0000 Subject: [PATCH 09/15] "what is the time" is less spammy now... and shows the time xD --- run.php | 18 +++++------------- src/IrcClient.php | 5 +++++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/run.php b/run.php index 966af40..f127dd2 100644 --- a/run.php +++ b/run.php @@ -44,14 +44,12 @@ // (hi|hello|hey) spires $client->addAction(function (IrcClient $client, Message $message) { if ($message->command() instanceof Privmsg) { - /** @var Privmsg $command */ $command = $message->command(); if ($command->hasTarget($client->channel())) { if (preg_match('/^(hi|hello|hey) spires$/i', $command->text())) { - $response = new Privmsg([$client->channel()], "Hello {$message->prefix()->nickname()}"); - $client->write($response); + $client->channelMessage("Hello {$message->prefix()->nickname()}"); } } } @@ -66,16 +64,10 @@ if ($command->hasTarget($client->channel())) { if (preg_match('/^!spires (?P.+)/i', $command->text(), $matches)) { if (preg_match('/^what time is it\??/i', $matches['match'])) { - $client->write(new Privmsg([$client->channel()], "It's")); - $dancer = ' \o/'; - foreach ([1,2,3] as $i) { - usleep(1000000/$i); - $client->write(new Privmsg([$client->channel()], substr($dancer, -1*$i, $i))); - $dancer = $dancer == ' \o/' ? ' /o\\' : ' \o/'; - } - ++$i; - usleep(1000000/$i); - $client->write(new Privmsg([$client->channel()], substr($dancer, -1*$i, $i) . " HAMMER TIME!")); + $time = date('H:i'); + $client->channelMessage(" \\o/ / {$time} \\"); + $client->channelMessage(" | \\ It's hammer time! /"); + $client->channelMessage(" / \\"); } } } diff --git a/src/IrcClient.php b/src/IrcClient.php index 15f5005..0542761 100644 --- a/src/IrcClient.php +++ b/src/IrcClient.php @@ -93,6 +93,11 @@ public function debug(string $string) fwrite(STDOUT, $string); } + public function channelMessage(string $message) + { + $this->write(new Privmsg([$this->channel()], $message)); + } + public function run() { $parser = new Parser(); From a4db0581aeb9d9f0cefe7cbe599b1c552e383415 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 9 Feb 2016 22:48:58 +0000 Subject: [PATCH 10/15] Remove 5.6 from composer, not sure why that got added xD --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 82d3f87..1fb70d4 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": "^5.6|^7.0" + "php": "^7.0" }, "require-dev": { "phpunit/phpunit": "~5.1.0", From 0a5e1e82434fbffebcf1bd1067227caba3a95336 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Wed, 10 Feb 2016 13:00:24 +0000 Subject: [PATCH 11/15] Code style --- src/Irc/Commands/Command.php | 1 - src/Irc/Commands/Pong.php | 1 - src/Irc/Message/Prefix.php | 8 ++++++-- src/IrcClient.php | 14 ++++++++++---- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Irc/Commands/Command.php b/src/Irc/Commands/Command.php index 4f1127f..13de00b 100644 --- a/src/Irc/Commands/Command.php +++ b/src/Irc/Commands/Command.php @@ -31,5 +31,4 @@ public function params() : string { return $this->params; } - } diff --git a/src/Irc/Commands/Pong.php b/src/Irc/Commands/Pong.php index fab43bc..25ceedf 100644 --- a/src/Irc/Commands/Pong.php +++ b/src/Irc/Commands/Pong.php @@ -51,5 +51,4 @@ public function params() : string { return trim($this->server . ' ' . $this->server2); } - } diff --git a/src/Irc/Message/Prefix.php b/src/Irc/Message/Prefix.php index 0ad6429..763bbd2 100644 --- a/src/Irc/Message/Prefix.php +++ b/src/Irc/Message/Prefix.php @@ -24,8 +24,12 @@ class Prefix */ private $servername; - public function __construct(string $nickname = null, string $username = null, string $hostname = null, string $servername = '') - { + public function __construct( + string $nickname = null, + string $username = null, + string $hostname = null, + string $servername = '' + ) { $this->nickname = $nickname ?: ''; $this->username = $username ?: ''; $this->hostname = $hostname ?: ''; diff --git a/src/IrcClient.php b/src/IrcClient.php index 0542761..0207cea 100644 --- a/src/IrcClient.php +++ b/src/IrcClient.php @@ -59,15 +59,16 @@ public function 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() ); - socket_write($this->socket, "NICK {$this->user()->nickname()}\r\n"); - socket_write($this->socket, "USER {$this->user()->username()} {$this->user()->usermode()} * :{$this->user()->realname()}\r\n"); - socket_write($this->socket, "JOIN {$this->connection()->channel()}\r\n"); + $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) @@ -110,7 +111,12 @@ public function run() $this->debug("\n\n[ read ]: " . $raw); $message = $parser->parse($raw . "\r\n"); - $prefix = new Prefix($message['nickname'], $message['username'], $message['hostname'], $message['servername']); + $prefix = new Prefix( + $message['nickname'], + $message['username'], + $message['hostname'], + $message['servername'] + ); switch ($message['command']) { case 'PING': From 661a58ad4617d64162b6537dbf477a0b8f877f67 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Wed, 10 Feb 2016 13:23:46 +0000 Subject: [PATCH 12/15] Extract actions to plugins --- run.php | 44 +++++++-------------------------------- src/IrcClient.php | 14 +++++++++++++ src/Plugin.php | 10 +++++++++ src/Plugins/Greetings.php | 25 ++++++++++++++++++++++ src/Plugins/PingPong.php | 20 ++++++++++++++++++ src/Plugins/Time.php | 28 +++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 src/Plugin.php create mode 100644 src/Plugins/Greetings.php create mode 100644 src/Plugins/PingPong.php create mode 100644 src/Plugins/Time.php diff --git a/run.php b/run.php index f127dd2..089c1fc 100644 --- a/run.php +++ b/run.php @@ -1,12 +1,12 @@ connect(); + // Ping Pong -$client->addAction(function (IrcClient $client, Message $message) { - if ($message->command() instanceof Ping) { - $response = Pong::fromParams($message->command()->params()); - $client->write($response); - } -}); +$client->addPlugin(new PingPong()); // (hi|hello|hey) spires -$client->addAction(function (IrcClient $client, Message $message) { - if ($message->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()}"); - } - } - } -}); +$client->addPlugin(new Greetings()); // !spires what time is it? -$client->addAction(function (IrcClient $client, Message $message) { - if ($message->command() instanceof Privmsg) { - /** @var Privmsg $command */ - $command = $message->command(); +$client->addPlugin(new Time()); - if ($command->hasTarget($client->channel())) { - if (preg_match('/^!spires (?P.+)/i', $command->text(), $matches)) { - if (preg_match('/^what time is it\??/i', $matches['match'])) { - $time = date('H:i'); - $client->channelMessage(" \\o/ / {$time} \\"); - $client->channelMessage(" | \\ It's hammer time! /"); - $client->channelMessage(" / \\"); - } - } - } - } -}); $client->run(); diff --git a/src/IrcClient.php b/src/IrcClient.php index 0207cea..fb0a197 100644 --- a/src/IrcClient.php +++ b/src/IrcClient.php @@ -28,6 +28,11 @@ class IrcClient */ private $actions = []; + /** + * @var Plugin[] + */ + private $plugins = []; + private $socket; public function __construct(Connection $connection, User $user) @@ -76,6 +81,11 @@ 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); @@ -147,6 +157,10 @@ public function run() $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..2373232 --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,10 @@ +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..3cb58fb --- /dev/null +++ b/src/Plugins/PingPong.php @@ -0,0 +1,20 @@ +command() instanceof Ping) { + $response = Pong::fromParams($message->command()->params()); + $client->write($response); + } + } +} diff --git a/src/Plugins/Time.php b/src/Plugins/Time.php new file mode 100644 index 0000000..0b11266 --- /dev/null +++ b/src/Plugins/Time.php @@ -0,0 +1,28 @@ +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(" / \\"); + } + } + } + } +} From 95bf8451ad91cb0e918748120ed12714ad8f8417 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Wed, 10 Feb 2016 14:04:49 +0000 Subject: [PATCH 13/15] Join command and Welcome plugin --- run.php | 4 + src/Irc/Commands/Join.php | 55 +++++++++++++ src/IrcClient.php | 8 ++ src/Plugins/Welcome.php | 18 +++++ tests/Irc/Commands/JoinTest.php | 133 ++++++++++++++++++++++++++++++++ 5 files changed, 218 insertions(+) create mode 100644 src/Irc/Commands/Join.php create mode 100644 src/Plugins/Welcome.php create mode 100644 tests/Irc/Commands/JoinTest.php diff --git a/run.php b/run.php index 089c1fc..c9b27d8 100644 --- a/run.php +++ b/run.php @@ -7,6 +7,7 @@ use PHPOxford\SpiresIrc\Plugins\Greetings; use PHPOxford\SpiresIrc\Plugins\PingPong; use PHPOxford\SpiresIrc\Plugins\Time; +use PHPOxford\SpiresIrc\Plugins\Welcome; require_once 'vendor/autoload.php'; @@ -37,6 +38,9 @@ // Ping Pong $client->addPlugin(new PingPong()); +// Welcome users +$client->addPlugin(new Welcome()); + // (hi|hello|hey) spires $client->addPlugin(new Greetings()); diff --git a/src/Irc/Commands/Join.php b/src/Irc/Commands/Join.php new file mode 100644 index 0000000..7957912 --- /dev/null +++ b/src/Irc/Commands/Join.php @@ -0,0 +1,55 @@ +channels = $channels; + $this->keys = $keys ?? []; + } + + // ( *( "," ) [ *( "," ) ] ) / "0" + 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/IrcClient.php b/src/IrcClient.php index fb0a197..cb96e6c 100644 --- a/src/IrcClient.php +++ b/src/IrcClient.php @@ -3,6 +3,7 @@ namespace PHPOxford\SpiresIrc; use PHPOxford\SpiresIrc\Irc\Commands\Command; +use PHPOxford\SpiresIrc\Irc\Commands\Join; use PHPOxford\SpiresIrc\Irc\Commands\Ping; use PHPOxford\SpiresIrc\Irc\Commands\Privmsg; use PHPOxford\SpiresIrc\Irc\Connection; @@ -136,6 +137,13 @@ public function run() ); break; + case 'JOIN': + $message = new Message( + $prefix, + Join::fromParams($message['params']) + ); + break; + case 'PRIVMSG': $message = new Message( $prefix, diff --git a/src/Plugins/Welcome.php b/src/Plugins/Welcome.php new file mode 100644 index 0000000..073a5e6 --- /dev/null +++ b/src/Plugins/Welcome.php @@ -0,0 +1,18 @@ +command() instanceof Join && $message->prefix()->nickname() != $client->user()->nickname()) { + $client->channelMessage("Welcome {$message->prefix()->nickname()} :D"); + } + } +} 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'))); + } +} From 4584661b1289ee1edb8fad16398d0dfcede26576 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Thu, 11 Feb 2016 13:06:16 +0000 Subject: [PATCH 14/15] Strict mode --- run.php | 1 + src/Irc/Commands/Base.php | 1 + src/Irc/Commands/Command.php | 1 + src/Irc/Commands/Join.php | 1 + src/Irc/Commands/Ping.php | 1 + src/Irc/Commands/Pong.php | 1 + src/Irc/Commands/Privmsg.php | 1 + src/Irc/Connection.php | 1 + src/Irc/Message.php | 1 + src/Irc/Message/Command.php | 1 + src/Irc/Message/Prefix.php | 1 + src/Irc/Parser.php | 1 + src/Irc/User.php | 1 + src/IrcClient.php | 3 ++- src/Plugin.php | 1 + src/Plugins/Greetings.php | 1 + src/Plugins/PingPong.php | 4 ++-- src/Plugins/Time.php | 1 + src/Plugins/Welcome.php | 1 + 19 files changed, 21 insertions(+), 3 deletions(-) diff --git a/run.php b/run.php index c9b27d8..bc0a1a7 100644 --- a/run.php +++ b/run.php @@ -1,4 +1,5 @@ write(new Privmsg([$this->channel()], $message)); + $this->write((string) new Privmsg([$this->channel()], $message)); } public function run() diff --git a/src/Plugin.php b/src/Plugin.php index 2373232..f623c8a 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -1,4 +1,5 @@ command() instanceof Ping) { - $response = Pong::fromParams($message->command()->params()); - $client->write($response); + $client->write((string) Pong::fromParams($message->command()->params())); } } } diff --git a/src/Plugins/Time.php b/src/Plugins/Time.php index 0b11266..f107693 100644 --- a/src/Plugins/Time.php +++ b/src/Plugins/Time.php @@ -1,4 +1,5 @@ Date: Thu, 11 Feb 2016 13:18:47 +0000 Subject: [PATCH 15/15] Remove a forgotten comment --- src/Irc/Commands/Join.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Irc/Commands/Join.php b/src/Irc/Commands/Join.php index f5e02ec..690738e 100644 --- a/src/Irc/Commands/Join.php +++ b/src/Irc/Commands/Join.php @@ -28,7 +28,6 @@ public function __construct(array $channels, array $keys = null) $this->keys = $keys ?? []; } - // ( *( "," ) [ *( "," ) ] ) / "0" public static function fromParams(string $params) : self { $arguments = explode(' ', $params);