Browse Source

implement post commission

main
ghost 3 months ago
parent
commit
d7f751c6c2
  1. 2
      README.md
  2. 2
      composer.json
  3. 11
      data.sql
  4. 19
      example/config.json
  5. 186
      src/controller/room.php
  6. 237
      src/crontab.php
  7. 46
      src/server.php
  8. 15
      src/view/pool.gemini

2
README.md

@ -31,6 +31,7 @@ KevaChat is distributed chat platform for open, uncensored and privacy respectab @@ -31,6 +31,7 @@ KevaChat is distributed chat platform for open, uncensored and privacy respectab
## Install
* `apt install git composer memcached sqlite3 php-curl php-memcached php-sqlite3 php-mbstring`
* `git clone https://github.com/kevachat/geminiapp.git`
* `cd geminiapp`
* `composer update`
@ -41,6 +42,7 @@ KevaChat is distributed chat platform for open, uncensored and privacy respectab @@ -41,6 +42,7 @@ KevaChat is distributed chat platform for open, uncensored and privacy respectab
* `cp example/config.json host/127.0.0.1/config.json`
* `cd host/127.0.0.1`
* `openssl req -x509 -newkey rsa:4096 -keyout key.rsa -out cert.pem -days 365 -nodes -subj "/CN=127.0.0.1"`
* `* * * * * php src/crontab.php 127.0.0.1` - if post commission enabled
## Start

2
composer.json

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
},
"require": {
"yggverse/titan-ii": "^1.0",
"kevachat/kevacoin": "^1.6",
"kevachat/kevacoin": "^1.7",
"clitor-is-protocol/kevacoin": "^1.0",
"yggverse/cache": "^0.4"
}

11
data.sql

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS "pool" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"time" INTEGER NOT NULL,
"sent" INTEGER NOT NULL,
"expired" INTEGER NOT NULL,
"cost" INTEGER NOT NULL,
"address" VARCHAR NOT NULL,
"namespace" VARCHAR NOT NULL,
"key" VARCHAR NOT NULL,
"value" TEXT NOT NULL
);

19
example/config.json

@ -36,6 +36,16 @@ @@ -36,6 +36,16 @@
"namespace":"geminiapp"
}
},
"sqlite":
{
"server":
{
"name":"127.0.0.1/data.db",
"user":"",
"password":"",
"timeout":600
}
},
"kevachat":
{
"about":
@ -62,6 +72,15 @@ @@ -62,6 +72,15 @@
"value":
{
"regex":"/.*/ui"
},
"pool":
{
"timeout":3600,
"confirmations":1
},
"cost":
{
"kva":1
}
},
"user":

186
src/controller/room.php

@ -10,6 +10,7 @@ class Room @@ -10,6 +10,7 @@ class Room
private \Kevachat\Kevacoin\Client $_kevacoin;
private \Yggverse\Cache\Memory $_memory;
private \PDO $_database;
public function __construct($config)
{
@ -40,6 +41,38 @@ class Room @@ -40,6 +41,38 @@ class Room
$this->_config->kevacoin->server->username,
$this->_config->kevacoin->server->password
);
// Init database
$this->_database = new \PDO(
sprintf(
'sqlite:%s/../../host/%s',
__DIR__,
$config->sqlite->server->name
),
$config->sqlite->server->user,
$config->sqlite->server->password
);
$this->_database->setAttribute(
\PDO::ATTR_ERRMODE,
\PDO::ERRMODE_EXCEPTION
);
$this->_database->setAttribute(
\PDO::ATTR_DEFAULT_FETCH_MODE,
\PDO::FETCH_OBJ
);
$this->_database->setAttribute(
\PDO::ATTR_TIMEOUT,
$config->sqlite->server->timeout
);
$this->_database->query(
file_get_contents(
__DIR__ . '/../../data.sql'
)
);
}
public function list(): string
@ -294,7 +327,7 @@ class Room @@ -294,7 +327,7 @@ class Room
);
}
public function post(string $namespace, ?string $txid, int $session, string $message): ?string
public function post(string $namespace, ?string $mention, int $session, string $message, ?string &$address = null): null|int|string
{
// Validate funds available yet
if (1 > $this->_kevacoin->getBalance())
@ -322,9 +355,9 @@ class Room @@ -322,9 +355,9 @@ class Room
);
// Append mention if provided
if ($txid)
if ($mention)
{
$message = '@' . $txid . PHP_EOL . $message;
$message = '@' . $mention . PHP_EOL . $message;
}
// Validate final message length
@ -333,55 +366,132 @@ class Room @@ -333,55 +366,132 @@ class Room
return null;
}
// Send message
if
(
$txid = $this->_kevacoin->kevaPut(
// Cleanup session
$this->_memory->delete(
$session
);
// Payment required, get new address and save message to the pending pool
if ($this->_config->kevachat->post->cost->kva > 0)
{
$time = time();
$query = $this->_database->prepare(
'INSERT INTO `pool` (
`time`,
`sent`,
`expired`,
`cost`,
`address`,
`namespace`,
`key`,
`value`
) VALUES (
:time,
:sent,
:expired,
:cost,
:address,
:namespace,
:key,
:value
)'
);
$query->execute(
[
':time' => $time,
':sent' => 0,
':expired' => 0,
':cost' => $this->_config->kevachat->post->cost->kva,
':address' => $address = $this->_kevacoin->getNewAddress(),
':namespace' => $namespace,
':key' => sprintf('%s@anon', $time),
':value' => $message
]
);
return $this->_database->lastInsertId();
}
// Publications free, send post immediately
else
{
return $this->_kevacoin->kevaPut(
$namespace,
sprintf(
'%s@anon',
time()
),
$message
)
)
{
// Cleanup session
$this->_memory->delete(
$session
);
// Success
return $txid;
}
return null;
}
public function sent(string $namespace, string $txid)
public function sent(string $namespace, mixed $id, ?string $address)
{
return str_replace(
[
'{logo}',
'{txid}',
'{room}'
],
[
// Transaction requires payment
if ($address)
{
return str_replace(
[
'{logo}',
'{id}',
'{address}',
'{amount}',
'{expires}',
'{room}'
],
[
file_get_contents(
__DIR__ . '/../../logo.ascii'
),
$id,
$address,
$this->_config->kevachat->post->cost->kva,
date(
'c',
$this->_config->kevachat->post->pool->timeout + time()
),
$this->_link(
sprintf(
'/room/%s',
$namespace
)
)
],
file_get_contents(
__DIR__ . '/../../logo.ascii'
),
$txid,
$this->_link(
sprintf(
'/room/%s',
$namespace
__DIR__ . '/../view/pool.gemini'
)
);
}
// Free post
else
{
return str_replace(
[
'{logo}',
'{txid}',
'{room}'
],
[
file_get_contents(
__DIR__ . '/../../logo.ascii'
),
$id,
$this->_link(
sprintf(
'/room/%s',
$namespace
)
)
],
file_get_contents(
__DIR__ . '/../view/sent.gemini'
)
],
file_get_contents(
__DIR__ . '/../view/sent.gemini'
)
);
);
}
}
private function _post(

237
src/crontab.php

@ -0,0 +1,237 @@ @@ -0,0 +1,237 @@
<?php
// Check arguments
if (empty($argv[1]))
{
exit(
sprintf(
_('[%s] [error] Configured hostname required as argument!'),
date('c')
) . PHP_EOL
);
}
// Check host configured
if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/config.json'))
{
exit(
sprintf(
_('[%s] [error] Host "%s" not configured!'),
date('c'),
$argv[1]
) . PHP_EOL
);
}
// Prevent multi-thread execution
$semaphore = sem_get(
crc32(__FILE__ . $argv[1]), 1
);
if (false === sem_acquire($semaphore, true))
{
exit(
sprintf(
_('[%s] [warning] Process locked by another thread!'),
date('c')
) . PHP_EOL
);
}
// Init config
$config = json_decode(
file_get_contents(
__DIR__ . '/../host/' . $argv[1] . '/config.json'
)
);
// Load dependencies
require_once __DIR__ . '/../vendor/autoload.php';
// Init kevacoin
try
{
$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
);
}
catch (Exception $exception)
{
exit(
print_r(
$exception,
true
)
);
}
// Init database
try
{
$database = new \PDO(
sprintf(
'sqlite:%s/../host/%s',
__DIR__,
$config->sqlite->server->name
),
$config->sqlite->server->user,
$config->sqlite->server->password
);
$database->setAttribute(
\PDO::ATTR_ERRMODE,
\PDO::ERRMODE_EXCEPTION
);
$database->setAttribute(
\PDO::ATTR_DEFAULT_FETCH_MODE,
\PDO::FETCH_OBJ
);
$database->setAttribute(
\PDO::ATTR_TIMEOUT,
$config->sqlite->server->timeout
);
$database->query(
file_get_contents(
__DIR__ . '/../data.sql'
)
);
}
catch (Exception $exception)
{
exit(
print_r(
$exception,
true
)
);
}
// Init room list
$rooms = [];
foreach ((array) $kevacoin->kevaListNamespaces() as $value)
{
$rooms[$value['namespaceId']] = mb_strtolower($value['displayName']);
}
// Skip room lock events
if (empty($rooms))
{
exit(
sprintf(
_('[%s] [error] Could not init room list!'),
date('c')
) . PHP_EOL
);
}
// Process pool queue
$total = 0;
foreach ($database->query('SELECT * FROM `pool` WHERE `sent` = 0 AND `expired` = 0')->fetchAll() as $pool)
{
// Payment received, send to blockchain
if ($kevacoin->getReceivedByAddress($pool->address, $config->kevachat->post->pool->confirmations) >= $pool->cost)
{
// Check physical wallet balance
if ($kevacoin->getBalance() <= $pool->getCost())
{
exit(
sprintf(
_('[%s] [error] Insufficient wallet funds!'),
date('c')
) . PHP_EOL
);
}
// Check namespace is valid
if (!isset($rooms[$pool->namespace]))
{
print(
sprintf(
_('[%s] [warning] Could not found "%s" in room list!'),
date('c'),
$pool->namespace
) . PHP_EOL
);
continue;
}
// Send to blockchain
if ($txid = $kevacoin->kevaPut($pool->namespace, $pool->key, $pool->value))
{
// Update status
$database->query(
sprintf(
'UPDATE `pool` SET `sent` = %d WHERE `id` = %d LIMIT 1',
time(),
$pool->id
)
);
print(
sprintf(
_('[%s] [notice] Record ID "%d" sent to blockchain with transaction ID "%s"'),
date('c'),
$pool->id,
$txid
) . PHP_EOL
);
}
else
{
print(
sprintf(
_('[%s] [error] Could not send record ID "%d" to blockchain!'),
date('c'),
$pool->id,
$txid
) . PHP_EOL
);
}
}
// Record expired
else if (time() >= $pool->time + $this->_config->kevachat->post->pool->timeout)
{
// Update status
$database->query(
sprintf(
'UPDATE `pool` SET `expired` = %d WHERE `id` = %d LIMIT 1',
time(),
$pool->id
)
);
print(
sprintf(
_('[%s] [notice] Record ID "%d" expired.'),
date('c'),
$pool->id,
$txid
) . PHP_EOL
);
}
// Update counter
$total++;
}
print(
sprintf(
_('[%s] [notice] Records processed: %d'),
date('c'),
$total
) . PHP_EOL
);

46
src/server.php

@ -161,9 +161,22 @@ $server->setHandler( @@ -161,9 +161,22 @@ $server->setHandler(
// Request post message
if (empty($request->getQuery()))
{
$response->setMeta(
'text/plain'
);
if ($config->kevachat->post->cost->kva)
{
$response->setMeta(
sprintf(
_('Enter your message text (cost: %s KVA)'),
$config->kevachat->post->cost->kva
)
);
}
else
{
$response->setMeta(
_('Enter your message text:')
);
}
$response->setCode(
10
@ -182,9 +195,9 @@ $server->setHandler( @@ -182,9 +195,9 @@ $server->setHandler(
);
// Success, redirect to this room page
if ($txid = $room->post($matches[1], null, $matches[2], $request->getQuery()))
if ($id = $room->post($matches[1], null, $matches[2], $request->getQuery(), $address))
{
if ($result = $room->sent($matches[1], $txid))
if ($result = $room->sent($matches[1], $id, $address))
{
$response->setContent(
$result
@ -205,9 +218,22 @@ $server->setHandler( @@ -205,9 +218,22 @@ $server->setHandler(
// Request post message
if (empty($request->getQuery()))
{
$response->setMeta(
'text/plain'
);
if ($config->kevachat->post->cost->kva)
{
$response->setMeta(
sprintf(
_('Enter your message text (cost: %s KVA)'),
$config->kevachat->post->cost->kva
)
);
}
else
{
$response->setMeta(
_('Enter your message text:')
);
}
$response->setCode(
10
@ -226,9 +252,9 @@ $server->setHandler( @@ -226,9 +252,9 @@ $server->setHandler(
);
// Success, redirect to this room page
if ($txid = $room->post($matches[1], $matches[2], $matches[3], $request->getQuery()))
if ($id = $room->post($matches[1], $matches[2], $matches[3], $request->getQuery(), $address))
{
if ($result = $room->sent($matches[1], $txid))
if ($result = $room->sent($matches[1], $id, $address))
{
$response->setContent(
$result

15
src/view/pool.gemini

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
```{logo}```
# Success!
Your message saved in pool with ID #{id}
Send exactly {amount} KVA to this address:
```
{address}
```
Invoice expires at {expires}
=> {room} Done!
Loading…
Cancel
Save