diff --git a/README.md b/README.md index 76e5efc..90a5138 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,6 @@ To read messages, use KevaChat [webapp](https://github.com/kevachat/webapp), [ge ## Components * [kevachat/kevacoin-php](https://github.com/kevachat/kevacoin-php) - KevaCoin library for PHP 8 -* [yggverse/nps-php](https://github.com/YGGverse/nps-php) - PHP 8 / Composer Library for NPS Protocol -* [gregwar/captcha](https://github.com/Gregwar/Captcha) - Captcha library to prevent spam abuses -* [ixnode/php-cli-image](https://github.com/ixnode/php-cli-image) - Library converts captcha to ASCII format \ No newline at end of file +* [cboden/ratchet](https://github.com/ratchetphp/Ratchet) - Asynchronous Socket server +* [gregwar/captcha](https://github.com/Gregwar/Captcha) - Captcha library to prevent spam abuse +* [ixnode/php-cli-image](https://github.com/ixnode/php-cli-image) - Library converts captcha to ASCII/CLI format \ No newline at end of file diff --git a/composer.json b/composer.json index 253d5f3..b078e88 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,6 @@ "kevachat/kevacoin": "^1.10", "gregwar/captcha": "^1.2", "ixnode/php-cli-image": "^0.1.2", - "yggverse/nps": "^1.3" + "cboden/ratchet": "^0.4.4" } } diff --git a/config/example.json b/config/example.json index ca7557c..db33132 100644 --- a/config/example.json +++ b/config/example.json @@ -20,93 +20,101 @@ "server": { "host":"127.0.0.1", - "port":1915, - "size":3072, - "line":1024 + "port":1915 }, - "session": + "captcha": { - "timeout":3600, - "captcha": + "length":3, + "chars":"1234567890", + "dimensions": { - "length":3, - "chars":"1234567890", - "dimensions": - { - "width":100, - "height":40 - }, - "background": - { - "r":0, - "g":0, - "b":0 - }, - "ascii": - { - "width": 50 - } + "width":100, + "height":40 + }, + "background": + { + "r":0, + "g":0, + "b":0 + }, + "ascii": + { + "width": 50 } }, - "action": + "event": { - "start": + "init": { "debug": { "enabled":true, - "template":"[{time}] server started at {host}:{port} balance: {keva}" + "template":"[{time}] [init] listen on {host}:{port} balance: {keva}" } }, - "welcome": + "open": { - "message": + "response": [ "\u001b[34m\u001b[1mWelcome to KevaChat!\u001b[0m", - "\u001b[34mEnter captcha to confirm you are human:\u001b[0m" + "\u001b[34mEnter captcha to confirm you are human\u001b[0m" ], "debug": { "enabled":true, - "template":"[{time}] [welcome] {host}:{port} captcha: {code}" + "template":"[{time}] [open] {host}#{crid} captcha: {code}" } }, - "pending": + "message": { - "message": + "response": { - "success": - [ - "\u001b[34mLooks good, now enter your message:\u001b[0m" - ], - "failure": - [ - "\u001b[31mSomething went wrong, try again later!\u001b[0m" - ] + "captcha": + { + "success": + [ + "\u001b[34mGood, now enter your message!\u001b[0m" + ], + "failure": + [ + "\u001b[31mIncorrect captcha code, try again later!\u001b[0m" + ] + }, + "submit": + { + "success": + [ + "\u001b[32mThanks, your message successfully sent!\u001b[0m" + ], + "failure": + { + "internal": + [ + "\u001b[31mSomething went wrong, please make your feedback!\u001b[0m" + ] + } + } }, "debug": { "enabled":true, - "template":"[{time}] [pending] {host}:{port} captcha: {code} sent: {sent}" + "template":"[{time}] [message] {host}#{crid} captcha: {code} size: {size} bytes \n\r{sent}." } }, - "handler": + "close": { - "message": + "debug": { - "success": - [ - "\u001b[32mThanks, your message successfully sent!\u001b[0m" - ], - "failure": - [ - "\u001b[31mSomething went wrong, please make your feedback!\u001b[0m" - ] - }, + "enabled":true, + "template":"[{time}] [close] {host}#{crid}" + } + }, + "error": + { "debug": { "enabled":true, - "template":"[{time}] [handler] {host}:{port} captcha: {code} sent: {sent} length: {size} bytes {data}" + "template":"[{time}] [error] {host}#{crid} {info}" } } } diff --git a/src/Server/Ratchet.php b/src/Server/Ratchet.php new file mode 100644 index 0000000..f0daa66 --- /dev/null +++ b/src/Server/Ratchet.php @@ -0,0 +1,262 @@ +_config = $config; + + // Init KevaCoin + $this->_kevacoin = new \Kevachat\Kevacoin\Client( + $this->_config->kevacoin->server->protocol, + $this->_config->kevacoin->server->host, + $this->_config->kevacoin->server->port, + $this->_config->kevacoin->server->username, + $this->_config->kevacoin->server->password + ); + + // Validate funds + if ((float) $this->_kevacoin->getBalance($this->_config->kevacoin->wallet->account) <= 0) + { + throw new \Exception(); // @TODO + } + + // Dump event on enabled + if ($this->_config->nps->event->init->debug->enabled) + { + print( + str_ireplace( + [ + '{time}', + '{host}', + '{port}', + '{keva}' + ], + [ + (string) date('c'), + (string) $this->_config->nps->server->host, + (string) $this->_config->nps->server->port, + (float) $this->_kevacoin->getBalance( + $this->_config->kevacoin->wallet->account + ) + ], + $this->_config->nps->event->init->debug->template + ) . PHP_EOL + ); + } + } + + public function onOpen( + \Ratchet\ConnectionInterface $connection + ) { + // Init config namespace + $config = $this->_config->nps; + + // Build captcha + $captcha = new \Gregwar\Captcha\CaptchaBuilder( + null, + new \Gregwar\Captcha\PhraseBuilder( + $config->captcha->length, + $config->captcha->chars + ) + ); + + $captcha->setBackgroundColor( + $config->captcha->background->r, + $config->captcha->background->g, + $config->captcha->background->b + ); + + $captcha->build( + $config->captcha->dimensions->width, + $config->captcha->dimensions->height + ); + + // Convert captcha image to ASCII response + $image = new \Ixnode\PhpCliImage\CliImage( + $captcha->get(), + $config->captcha->ascii->width + ); + + // Send response + $connection->send( + sprintf( + implode( + PHP_EOL, + $config->event->open->response + ) . PHP_EOL . $image->getAsciiString() . PHP_EOL + ) + ); + + // Keep captcha phrase in connection + $connection->captcha = $captcha->getPhrase(); + + // Init connection confirmed + $connection->confirmed = false; + + // Debug open event on enabled + if ($config->event->open->debug->enabled) + { + // Print debug from template + print( + str_ireplace( + [ + '{time}', + '{host}', + '{crid}', + '{code}' + ], + [ + (string) date('c'), + (string) $connection->remoteAddress, + (string) $connection->resourceId, + (string) $connection->captcha + ], + $config->event->open->debug->template + ) . PHP_EOL + ); + } + } + + public function onMessage( + \Ratchet\ConnectionInterface $connection, + $request + ) { + // Init config namespace + $config = $this->_config->nps->event->message; + + // Captcha request first for unconfirmed connections + if (!$connection->confirmed) + { + // Request match captcha + if ($request == $connection->captcha) + { + $connection->confirmed = true; + + $connection->send( + implode( + PHP_EOL, + $config->response->captcha->success + ) . PHP_EOL + ); + } + + // Captcha request invalid + else + { + $connection->confirmed = false; + + $connection->send( + implode( + PHP_EOL, + $config->response->captcha->failure + ) . PHP_EOL + ); + + // Drop connection or do something else.. + $connection->close(); + } + } + + // @TODO compose request to KevaCoin, return transaction ID + else + { + // Save massage to kevacoin + /* + $connection->send( + implode( + PHP_EOL, + $config->response->captcha->failure + ) . PHP_EOL + ); + */ + } + + // Debug message event on enabled + if ($config->debug->enabled) + { + print( + str_ireplace( + [ + '{time}', + '{host}', + '{crid}', + '{code}', + '{sent}', + '{size}' + ], + [ + (string) date('c'), + (string) $connection->remoteAddress, + (string) $connection->resourceId, + (string) $connection->captcha, + (string) str_replace('%', '%%', $request), + (string) mb_strlen($request) + ], + $config->debug->template + ) . PHP_EOL + ); + } + } + + public function onClose( + \Ratchet\ConnectionInterface $connection + ) { + if ($this->_config->nps->event->close->debug->enabled) + { + print( + str_ireplace( + [ + '{time}', + '{host}', + '{crid}' + ], + [ + (string) date('c'), + (string) $connection->remoteAddress, + (string) $connection->resourceId + ], + $this->_config->nps->event->close->debug->template + ) . PHP_EOL + ); + } + } + + public function onError( + \Ratchet\ConnectionInterface $connection, + \Exception $exception + ) { + if ($this->_config->nps->event->close->debug->enabled) + { + print( + str_ireplace( + [ + '{time}', + '{host}', + '{crid}', + '{info}' + ], + [ + (string) date('c'), + (string) $connection->remoteAddress, + (string) $connection->resourceId, + (string) str_replace('%', '%%', $exception->getMessage()) + ], + $this->_config->nps->event->error->debug->template + ) . PHP_EOL + ); + } + + $connection->close(); + } +} \ No newline at end of file diff --git a/src/app.php b/src/app.php new file mode 100644 index 0000000..c62a2f4 --- /dev/null +++ b/src/app.php @@ -0,0 +1,30 @@ +nps->server->port, + $config->nps->server->host +); + +$server->run(); diff --git a/src/server.php b/src/server.php deleted file mode 100644 index b538b94..0000000 --- a/src/server.php +++ /dev/null @@ -1,310 +0,0 @@ -kevacoin->server->protocol, - $config->kevacoin->server->host, - $config->kevacoin->server->port, - $config->kevacoin->server->username, - $config->kevacoin->server->password -); - -if ((float) $kevacoin->getBalance($config->kevacoin->wallet->account) <= 0) exit; - -// Init session -$session = []; - -// Init server -$server = new \Yggverse\Nps\Server( - $config->nps->server->host, - $config->nps->server->port, - $config->nps->server->size, - $config->nps->server->line -); - -// Init welcome function -$server->setWelcome( - function ( - string $connect - ): ?string - { - global $config, - $session; - - // Cleanup expired sessions - foreach ($session as $key => $value) - { - if ($value['time'] + $config->nps->session->timeout < time()) - { - unset( - $session[$key] - ); - } - } - - // Build connection URL #72811 - $url = sprintf( - 'nex://%s', - $connect - ); - - // Init new session - $session[$connect] = - [ - 'time' => time(), - 'host' => parse_url( - $url, - PHP_URL_HOST - ), - 'port' => parse_url( - $url, - PHP_URL_PORT - ), - 'code' => null - ]; - - // Build captcha - $captcha = new \Gregwar\Captcha\CaptchaBuilder( - null, - new \Gregwar\Captcha\PhraseBuilder( - $config->nps->session->captcha->length, - $config->nps->session->captcha->chars - ) - ); - - $captcha->setBackgroundColor( - $config->nps->session->captcha->background->r, - $config->nps->session->captcha->background->g, - $config->nps->session->captcha->background->b - ); - - $captcha->build( - $config->nps->session->captcha->dimensions->width, - $config->nps->session->captcha->dimensions->height - ); - - // Set captcha value to the session code - $session[$connect]['code'] = $captcha->getPhrase(); - - // Create ASCII confirmation code - $image = new \Ixnode\PhpCliImage\CliImage( - $captcha->get(), - $config->nps->session->captcha->ascii->width - ); - - // Debug request on enabled - if ($config->nps->action->welcome->debug->enabled) - { - // Print debug from template - print( - str_ireplace( - [ - '{time}', - '{host}', - '{port}', - '{code}' - ], - [ - (string) date('c'), - (string) $session[$connect]['host'], - (string) $session[$connect]['port'], - (string) $session[$connect]['code'] - ], - $config->nps->action->welcome->debug->template - ) . PHP_EOL - ); - } - - return sprintf( - implode( - PHP_EOL, - $config->nps->action->welcome->message - ) . PHP_EOL . $image->getAsciiString() . PHP_EOL - ); - } -); - -// Init pending function -$server->setPending( - function ( - string $request, - string $connect - ): ?string - { - global $config, - $session; - - // Filter request - $request = trim( - $request - ); - - // Debug request on enabled - if ($config->nps->action->pending->debug->enabled) - { - // Print debug from template - print( - str_ireplace( - [ - '{time}', - '{host}', - '{port}', - '{sent}', - '{code}' - ], - [ - (string) date('c'), - (string) $session[$connect]['host'], - (string) $session[$connect]['port'], - (string) $request, - (string) $session[$connect]['code'] - ], - $config->nps->action->pending->debug->template - ) . PHP_EOL - ); - } - - // Session valid - if ($session[$connect]['code'] == $request) - { - // Return success message - return implode( - PHP_EOL, - $config->nps->action->pending->message->success - ) . PHP_EOL; - } - - // @TODO disconnect, stop handler - - // Cleanup session - unset($session[$connect]); - - // Return failure message - return implode( - PHP_EOL, - $config->nps->action->pending->message->failure - ) . PHP_EOL; - } -); - -// Init handler function -$server->setHandler( - function ( - bool $success, - string $content, - string $request, - string $connect - ): ?string - { - global $config, - $session; - - // Filter request - $request = trim( - $request - ); - - // Filter content - $content = trim( - $content - ); - - // Build response - if ($session[$connect]['code'] == $request) - { - // @TODO save content in blockchain with kevacoin-php - - $response = implode( - PHP_EOL, - $config->nps->action->handler->message->success - ) . PHP_EOL; - } - - else - { - $response = implode( - PHP_EOL, - $config->nps->action->handler->message->failure - ) . PHP_EOL; - } - - // Debug request on enabled - if ($config->nps->action->handler->debug->enabled) - { - // Print debug from template - print( - str_ireplace( - [ - '{time}', - '{host}', - '{port}', - '{code}', - '{sent}', - '{size}', - '{data}' - ], - [ - (string) date('c'), - (string) $session[$connect]['host'], - (string) $session[$connect]['port'], - (string) $session[$connect]['code'], - (string) str_replace('%', '%%', $request), - (string) mb_strlen($content), - (string) PHP_EOL . $content, - ], - $config->nps->action->handler->debug->template - ) . PHP_EOL - ); - } - - // Cleanup session - unset($session[$connect]); - - // Result - return $response; - } -); - -// Start server -if ($config->nps->action->start->debug->enabled) -{ - print( - str_ireplace( - [ - '{time}', - '{host}', - '{port}', - '{keva}' - ], - [ - date('c'), - $config->nps->server->host, - $config->nps->server->port, - $kevacoin->getBalance( - $config->kevacoin->wallet->account - ) - ], - $config->nps->action->start->debug->template - ) . PHP_EOL - ); -} - -$server->start();