diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35a4909 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor/ +/storage/ +/config.json \ No newline at end of file diff --git a/README.md b/README.md index d1eadf8..0c7e18e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ # twister-rss-bot-php + RSS Bot for Twister P2P + +## Requirements + +* `php-8.2` +* `php-curl` +* `php-mbstring` +* `php-pdo` +* `php-sqlite3` + +## Install + +### Production + +* `composer create-project twisterarmy/twister-rss-bot` + +### Development + +* `git clone https://github.com/twisterarmy/twister-rss-bot-php.git` +* `cd twister-rss-bot-php` +* `composer install` + +## Config + +* `cp config.example.json config.json` +* `nano config.json` add twister connection and RSS feeds + +## Usage + +* `@hourly php src/cli/bot.php` \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..225770b --- /dev/null +++ b/composer.json @@ -0,0 +1,14 @@ +{ + "name": "twisterarmy/twister-rss-bot", + "description": "RSS Bot for Twister P2P", + "type": "project", + "license": "MIT", + "autoload": { + "psr-4": { + "Twisterarmy\\TwisterRssBot\\": "src/" + } + }, + "require": { + "twisterarmy/twister": "^1.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..6547d0e --- /dev/null +++ b/composer.lock @@ -0,0 +1,50 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "70fb72082709cc5f798645656e8c4865", + "packages": [ + { + "name": "twisterarmy/twister", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/twisterarmy/twister-php.git", + "reference": "5761f1b90b25caf58fef7a1274b91af9c25a1831" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twisterarmy/twister-php/zipball/5761f1b90b25caf58fef7a1274b91af9c25a1831", + "reference": "5761f1b90b25caf58fef7a1274b91af9c25a1831", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twisterarmy\\Twister\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP 8 / Composer Tools for Twister API", + "support": { + "issues": "https://github.com/twisterarmy/twister-php/issues", + "source": "https://github.com/twisterarmy/twister-php/tree/1.0.0" + }, + "time": "2023-12-21T03:22:50+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..e5f0718 --- /dev/null +++ b/config.example.json @@ -0,0 +1,35 @@ +{ + "twister": + { + "protocol":"http", + "host":"localhost", + "port":28332, + "username":"user", + "password":"pwd" + }, + "sqlite": + { + "database":"database.sqlite", + "username":null, + "password":null + }, + "feed": + [ + { + "source":"https://...", + "target":"user1", + "queue": + { + "limit":5 + } + }, + { + "source":"https://...", + "target":"user2", + "queue": + { + "limit":5 + } + } + ] +} \ No newline at end of file diff --git a/src/cli/bot.php b/src/cli/bot.php new file mode 100644 index 0000000..12c4065 --- /dev/null +++ b/src/cli/bot.php @@ -0,0 +1,169 @@ +twister->protocol, + $config->twister->host, + $config->twister->port, + $config->twister->username, + $config->twister->password + ); +} + +catch (Exception $e) +{ + var_dump( + $e->getMessage() + ); + + exit; +} + +// Connect DB +try +{ + $database = new PDO( + sprintf( + 'sqlite:%s', + sprintf( + '%s/../../storage/%s', + __DIR__, + $config->sqlite->database + ) + ), + $config->sqlite->username, + $config->sqlite->password + ); + + $database->setAttribute( + PDO::ATTR_DEFAULT_FETCH_MODE, + PDO::FETCH_OBJ + ); +} + +catch (Exception $e) +{ + var_dump( + $e->getMessage() + ); + + exit; +} + +// Collect feeds data +foreach ($config->feed as $feed) +{ + // Create account database if not exists + $database->query( + sprintf( + 'CREATE TABLE IF NOT EXISTS "%s" + ( + "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL CHECK("id" >= 0), + "hash" CHAR(32) NOT NULL, + "message" VARCHAR(256) NOT NULL, + "time" INTEGER NOT NULL, + "sent" INTEGER NULL, + CONSTRAINT "UNIQUE" + UNIQUE("hash") + )', + $feed->target + ) + ); + + // Save feed to the database pool + foreach ((array) \Twisterarmy\Twister\Tools\Rss::feed($feed->source) as $data) + { + // Generate record hash + $hash = md5( + $data['message'] + ); + + // Check record not exist + $query = $database->prepare( + sprintf( + 'SELECT COUNT(*) AS `total` FROM %s WHERE `hash` = ?', + $feed->target + ) + ); + + $query->execute( + [ + $hash + ] + ); + + if ($query->fetch()->total) + { + continue; + } + + // Add new record + $query = $database->prepare( + sprintf( + 'INSERT INTO %s (`hash`, `message`, `time`) VALUES (?, ?, ?)', + $feed->target + ) + ); + + $query->execute( + [ + $hash, + $data['message'], + $data['time'], + ] + ); + } + + // Process messages queue by time ASC + $query = $database->query( + sprintf( + 'SELECT `id`, `message` FROM %s WHERE `sent` IS NULL ORDER BY `time` ASC LIMIT %s', + $feed->target, + $feed->queue->limit, + ) + ); + + // Send each message to the twister account + foreach ($query->fetchAll() as $queue) + { + $errors = []; + + $twister->newPostMessage( + $feed->target, + time(), //int $k, + $queue->message, + $errors + ); + + if ($errors) + { + var_dump( + $errors + ); + + continue; + } + + // Update time sent on success + $database->query( + sprintf( + 'UPDATE %s SET `sent` = %s WHERE `id` = %s LIMIT 1', + $feed->target, + time(), + $queue->id + ) + ); + } +}