Browse Source

replace nps-php with async cboden/ratchet socket server

main
ghost 7 months ago
parent
commit
0033b1f3a3
  1. 6
      README.md
  2. 2
      composer.json
  3. 114
      config/example.json
  4. 262
      src/Server/Ratchet.php
  5. 30
      src/app.php
  6. 310
      src/server.php

6
README.md

@ -9,6 +9,6 @@ To read messages, use KevaChat [webapp](https://github.com/kevachat/webapp), [ge @@ -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
* [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

2
composer.json

@ -19,6 +19,6 @@ @@ -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"
}
}

114
config/example.json

@ -20,93 +20,101 @@ @@ -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}"
}
}
}

262
src/Server/Ratchet.php

@ -0,0 +1,262 @@ @@ -0,0 +1,262 @@
<?php
namespace Kevachat\Npsapp\Server;
use \Ratchet\MessageComponentInterface;
class Ratchet implements MessageComponentInterface
{
private \Kevachat\Kevacoin\Client $_kevacoin;
private object $_config;
public function __construct(
object $config
) {
// Init config
$this->_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();
}
}

30
src/app.php

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
<?php
// Load dependencies
require_once __DIR__ .
DIRECTORY_SEPARATOR . '..'.
DIRECTORY_SEPARATOR . 'vendor' .
DIRECTORY_SEPARATOR . 'autoload.php';
// Init config
$config = json_decode(
file_get_contents(
__DIR__ .
DIRECTORY_SEPARATOR . '..'.
DIRECTORY_SEPARATOR . 'config'.
DIRECTORY_SEPARATOR . (
isset($argv[1]) ? $argv[1] : 'example.json'
)
)
); if (!$config) throw new \Exception();
// Start server
$server = \Ratchet\Server\IoServer::factory(
new \Kevachat\Npsapp\Server\Ratchet(
$config
),
$config->nps->server->port,
$config->nps->server->host
);
$server->run();

310
src/server.php

@ -1,310 +0,0 @@ @@ -1,310 +0,0 @@
<?php
// Load dependencies
require_once __DIR__ .
DIRECTORY_SEPARATOR . '..'.
DIRECTORY_SEPARATOR . 'vendor' .
DIRECTORY_SEPARATOR . 'autoload.php';
// Init config
$config = json_decode(
file_get_contents(
__DIR__ .
DIRECTORY_SEPARATOR . '..'.
DIRECTORY_SEPARATOR . 'config'.
DIRECTORY_SEPARATOR . (
isset($argv[1]) ? $argv[1] : 'example.json'
)
)
); if (!$config) exit;
// Init KevaCoin
$kevacoin = new \Kevachat\Kevacoin\Client(
$config->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();
Loading…
Cancel
Save