From dff6bc86c39ccb2da288120574f9994da8ec08db Mon Sep 17 00:00:00 2001 From: skraetzig Date: Wed, 16 Sep 2026 13:52:13 +0200 Subject: [PATCH] feat: support GreenTeaSpeak protocol greetings --- src/Adapter/MockServerQuery.php | 4 +--- src/Adapter/ServerQuery.php | 18 +++++++++++++++--- src/Adapter/ServerQuery/Reply.php | 2 +- src/TeamSpeak3.php | 10 ++++++++++ src/Transport/MockTCP.php | 2 +- tests/Adapter/ServerQuery/ReplyTest.php | 7 +++++++ tests/Adapter/ServerQueryTest.php | 11 +++++++++++ 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Adapter/MockServerQuery.php b/src/Adapter/MockServerQuery.php index 3a4e71dc..e051cd96 100644 --- a/src/Adapter/MockServerQuery.php +++ b/src/Adapter/MockServerQuery.php @@ -26,9 +26,7 @@ protected function syn(): void $rdy = $this->getTransport()->readLine(); - if (!$rdy->startsWith(TeamSpeak3::TS3_PROTO_IDENT) && !$rdy->startsWith(TeamSpeak3::TEA_PROTO_IDENT) && !(defined("CUSTOM_PROTO_IDENT") && $rdy->startsWith(CUSTOM_PROTO_IDENT))) { - throw new AdapterException("invalid reply from the server (" . $rdy . ")"); - } + $this->validateProtocolGreeting($rdy); Signal::getInstance()->emit("serverqueryConnected", $this); } diff --git a/src/Adapter/ServerQuery.php b/src/Adapter/ServerQuery.php index e0ecde2c..dde43065 100644 --- a/src/Adapter/ServerQuery.php +++ b/src/Adapter/ServerQuery.php @@ -64,13 +64,25 @@ protected function syn(): void $rdy = $this->getTransport()->readLine(); - if (!$rdy->startsWith(TeamSpeak3::TS3_PROTO_IDENT) && !$rdy->startsWith(TeamSpeak3::TEA_PROTO_IDENT) && !(defined("CUSTOM_PROTO_IDENT") && $rdy->startsWith(CUSTOM_PROTO_IDENT))) { - throw new AdapterException("invalid reply from the server (" . $rdy . ")"); - } + $this->validateProtocolGreeting($rdy); Signal::getInstance()->emit("serverqueryConnected", $this); } + /** + * Validates a ServerQuery protocol greeting. + * + * @param StringHelper $greeting + * @return void + * @throws AdapterException + */ + protected function validateProtocolGreeting(StringHelper $greeting): void + { + if (!$greeting->startsWith(TeamSpeak3::TS3_PROTO_IDENT) && !$greeting->startsWith(TeamSpeak3::TEA_PROTO_IDENT) && !$greeting->startsWith(TeamSpeak3::GREENTEA_PROTO_IDENT) && !(defined("CUSTOM_PROTO_IDENT") && $greeting->startsWith(CUSTOM_PROTO_IDENT))) { + throw new AdapterException("invalid reply from the server (" . $greeting . ")"); + } + } + /** * The ServerQuery destructor. * diff --git a/src/Adapter/ServerQuery/Reply.php b/src/Adapter/ServerQuery/Reply.php index 68872f80..0209d99f 100644 --- a/src/Adapter/ServerQuery/Reply.php +++ b/src/Adapter/ServerQuery/Reply.php @@ -300,7 +300,7 @@ protected function fetchError(StringHelper $err): void protected function fetchReply(array $rpl): void { foreach ($rpl as $key => $val) { - if ($val->startsWith(TeamSpeak3::TS3_MOTD_PREFIX) || $val->startsWith(TeamSpeak3::TEA_MOTD_PREFIX) || (defined("CUSTOM_MOTD_PREFIX") && $val->startsWith(CUSTOM_MOTD_PREFIX))) { + if ($val->startsWith(TeamSpeak3::TS3_MOTD_PREFIX) || $val->startsWith(TeamSpeak3::TEA_MOTD_PREFIX) || $val->startsWith(TeamSpeak3::GREENTEA_MOTD_PREFIX) || (defined("CUSTOM_MOTD_PREFIX") && $val->startsWith(CUSTOM_MOTD_PREFIX))) { unset($rpl[$key]); } elseif ($val->startsWith(TeamSpeak3::EVENT)) { $this->evt[] = new Event($val, $this->con); diff --git a/src/TeamSpeak3.php b/src/TeamSpeak3.php index d54a49c2..e00705d4 100644 --- a/src/TeamSpeak3.php +++ b/src/TeamSpeak3.php @@ -69,6 +69,16 @@ class TeamSpeak3 */ public const TEA_MOTD_PREFIX = "Welcome"; + /** + * GreenTeaSpeak protocol welcome message. + */ + public const GREENTEA_PROTO_IDENT = "GreenTeaSpeak"; + + /** + * GreenTeaSpeak protocol greeting message prefix. + */ + public const GREENTEA_MOTD_PREFIX = "Welcome"; + /** * TeamSpeak 3 protocol error message prefix. */ diff --git a/src/Transport/MockTCP.php b/src/Transport/MockTCP.php index 2d7f06c5..5bd38ddc 100644 --- a/src/Transport/MockTCP.php +++ b/src/Transport/MockTCP.php @@ -25,7 +25,7 @@ public function connect(): void return; } - $this->reply = sprintf("%s\n%s\n", self::S_WELCOME_L0, self::S_WELCOME_L1); + $this->reply = sprintf("%s\n%s\n", $this->config['welcome'] ?? self::S_WELCOME_L0, self::S_WELCOME_L1); $this->stream = true; } diff --git a/tests/Adapter/ServerQuery/ReplyTest.php b/tests/Adapter/ServerQuery/ReplyTest.php index c7937920..4188c4c6 100644 --- a/tests/Adapter/ServerQuery/ReplyTest.php +++ b/tests/Adapter/ServerQuery/ReplyTest.php @@ -171,4 +171,11 @@ public function testFetchReply() $this->assertSame(static::$E_SERVERLIST, $reply->toString()->toString()); } + + public function testFetchReplyRemovesGreenTeaSpeakGreeting(): void + { + $reply = new Reply([new StringHelper('Welcome to the GreenTeaSpeak ServerQuery interface.'), new StringHelper(static::$S_SERVERLIST), new StringHelper(static::$S_ERROR_OK)]); + + $this->assertSame(static::$E_SERVERLIST, $reply->toString()->toString()); + } } diff --git a/tests/Adapter/ServerQueryTest.php b/tests/Adapter/ServerQueryTest.php index e12a116d..c4aabfa7 100644 --- a/tests/Adapter/ServerQueryTest.php +++ b/tests/Adapter/ServerQueryTest.php @@ -89,4 +89,15 @@ public function testRequestTracksCountTimestampAndRuntime(): void $this->assertSame('0.0.0.0', $serverQuery->getTransportHost()); $this->assertSame('9987', $serverQuery->getTransportPort()); } + + public function testAcceptsGreenTeaSpeakProtocolGreeting(): void + { + $serverQuery = new MockServerQuery([ + 'host' => '0.0.0.0', + 'port' => 9987, + 'welcome' => 'GreenTeaSpeak ServerQuery', + ]); + + $this->assertTrue($serverQuery->getTransport()->isConnected()); + } }