From eedfbcc2abb1d2143cc8b6c4241a53cf44f5af18 Mon Sep 17 00:00:00 2001 From: Clement Desmidt Date: Thu, 26 Mar 2020 00:11:37 +0100 Subject: [PATCH] :tada: Hello world --- .gitignore | 3 + bot/Bot.php | 107 +++++++++++++++++++++ bot/Commands/Helpers/TableHelper.php | 135 +++++++++++++++++++++++++++ bot/Commands/Icommands.php | 15 +++ bot/Commands/Lorem.php | 21 +++++ bot/Commands/Tasks.php | 53 +++++++++++ bot/Commands/TasksList.php | 55 +++++++++++ bot/Commands/TasksProjects.php | 35 +++++++ bot/Commands/Youtubedl.php | 38 ++++++++ bot/Request.php | 94 +++++++++++++++++++ composer.json | 11 +++ composer.lock | 59 ++++++++++++ config.example.php | 17 ++++ index.php | 43 +++++++++ 14 files changed, 686 insertions(+) create mode 100644 .gitignore create mode 100644 bot/Bot.php create mode 100644 bot/Commands/Helpers/TableHelper.php create mode 100644 bot/Commands/Icommands.php create mode 100644 bot/Commands/Lorem.php create mode 100644 bot/Commands/Tasks.php create mode 100644 bot/Commands/TasksList.php create mode 100644 bot/Commands/TasksProjects.php create mode 100644 bot/Commands/Youtubedl.php create mode 100644 bot/Request.php create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config.example.php create mode 100644 index.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397cbd5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor +/.idea +config.php diff --git a/bot/Bot.php b/bot/Bot.php new file mode 100644 index 0000000..7aae9b8 --- /dev/null +++ b/bot/Bot.php @@ -0,0 +1,107 @@ +getConfig()['masters']); + return $masters[array_rand($masters, 1)]; + } + + /** + * Bot constructor. + * @param array $config + */ + public function __construct(array $config = []) + { + $this->config = $config; + } + + /** + * @param Request $request + * @return bool + */ + public function isValid(Request $request) + { + $this->request = $request; + return $this->config['token'] === $request->getToken(); + } + + /** + * @param string $pattern the pattern to listen for + * @param \Closure|string $callback the callback to execute. Either a closure or a Class@method notation + * + * @return $this + */ + public function hears($pattern, $callback) + { + if (!array_key_exists($pattern, $this->events)) { + $this->events[$pattern] = $callback; + } else { + error_log(sprintf('Event %s déjà en place', $pattern)); + } + +// $this->events[$pattern][] = $callback; + + return $this; + } + + /** + * Try to match messages with the ones we should + * listen to. + */ + public function listen($sentence) + { + foreach ($this->events as $pattern => $command) { + $preg_pattern = sprintf('#%s#i', $pattern); + if (preg_match($preg_pattern, $sentence, $matches)) { + if (is_callable($command)) { + call_user_func($command, $this); + } else { + call_user_func([$command, 'getMessage'], $this, $matches); + } + } else { + error_log(sprintf('%s ne match pas %s', $preg_pattern, $sentence)); + } + } + $this->replyPolitely('Je n\'ai pas compris'); + } + + /** + * @param string $message + */ + public function replyPolitely($message) + { + $message .= ', '.$this->getMaster().'.'; + $this->reply($message); + } + /** + * @param string $message + */ + public function reply($message) + { + header('Content-type: application/json'); + die('{"text": "'.$message.'"}'); + } + + public function getConfig() + { + return $this->config; + } +} diff --git a/bot/Commands/Helpers/TableHelper.php b/bot/Commands/Helpers/TableHelper.php new file mode 100644 index 0000000..5106f07 --- /dev/null +++ b/bot/Commands/Helpers/TableHelper.php @@ -0,0 +1,135 @@ + + * @package Apidoc + * @version 1.00 (2014-04-04) + * @license GNU Lesser Public License + */ +class TableHelper { + + /** @var int The source path */ + public $maxlen = 50; + /** @var array The source path */ + private $data = []; + /** @var array The source path */ + private $header = []; + /** @var array The source path */ + private $len = []; + /** @var array The source path */ + private $align = [ + 'name' => 'L', + 'type' => 'C' + ]; + + /** + * @param array $header The header array [key => label, ...] + * @param array $content Content + * @param array|bool $align Alignment options [key => L|R|C, ...] + */ + public function __construct($header = null, $content = [], $align=false) + { + if ($header) { + $this->header = $header; + } elseif ($content) { + foreach ($content[0] as $key => $value) { + $this->header[$key] = $key; + } + } + + foreach ($this->header as $key => $label) { + $this->len[$key] = strlen($label); + } + + if (is_array($align)) { + $this->setAlign($align); + } + + $this->addData($content); + } + + /** + * Overwrite the alignment array + * + * @param array $align Alignment options [key => L|R|C, ...] + */ + public function setAlign($align) { + $this->align = $align; + } + + /** + * Add data to the table + * + * @param array $content Content + * @return TableHelper + */ + public function addData($content) { + foreach ($content as &$row) { + foreach ($this->header as $key => $value) { + if (!isset($row[$key])) { + $row[$key] = '-'; + } elseif (strlen($row[$key]) > $this->maxlen) { + $this->len[$key] = $this->maxlen; + $row[$key] = substr($row[$key], 0, $this->maxlen-3).'...'; + } elseif (strlen($row[$key]) > $this->len[$key]) { + $this->len[$key] = strlen($row[$key]); + } + } + } + unset($row); + $this->data += $content; + return $this; + } + + /** + * Add a delimiter + * + * @return string + */ + private function renderDelimiter() { + $res = '|'; + foreach ($this->len as $key => $l) { + $res .= (isset($this->align[$key]) && ($this->align[$key] === 'C' || $this->align[$key] === 'L') ? ':' : ' ') + . str_repeat('-', $l) + . (isset($this->align[$key]) && ($this->align[$key] === 'C' || $this->align[$key] === 'R') ? ':' : ' ') + . '|'; + } + return $res."\r\n"; + } + + /** + * Render a single row + * + * @param array $row + * @return string + */ + private function renderRow($row) { + $res = '|'; + foreach ($this->len as $key => $l) { + $res .= ' '.$row[$key].($l > strlen($row[$key]) ? str_repeat(' ', $l - strlen($row[$key])) : '').' |'; + } + + return $res."\r\n"; + } + + /** + * Render the table + * + * @param array $content Additional table content + * @return string + */ + public function render($content=array()) { + $this->addData($content); + + $res = $this->renderRow($this->header) . $this->renderDelimiter(); + foreach ($this->data as $row) { + $res .= $this->renderRow($row); + } + + return $res; + } +} diff --git a/bot/Commands/Icommands.php b/bot/Commands/Icommands.php new file mode 100644 index 0000000..dc35474 --- /dev/null +++ b/bot/Commands/Icommands.php @@ -0,0 +1,15 @@ +reply('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ac lectus ac lacus auctor porttitor nec non sem. Etiam tempor in turpis eleifend vehicula. Mauris congue tempus faucibus. Vestibulum varius lacinia ornare. Sed eu velit aliquet, tempor tortor eu, porttitor turpis. Ut urna ligula, finibus ac vehicula sit amet, semper non quam. Integer non lectus luctus, dictum ex ut, tempus enim.'."\n".'Aenean venenatis, nulla efficitur iaculis rutrum, velit dui ultrices lorem, sed fermentum orci ligula hendrerit erat. Phasellus porta est nec nisl ornare eleifend. Vivamus in placerat odio. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris eu euismod lectus. Aenean tristique nec odio in sollicitudin. Pellentesque egestas venenatis eros, non auctor magna hendrerit ac. Aenean bibendum rhoncus congue. Duis faucibus, tortor et euismod rhoncus, mi ipsum sollicitudin tellus, a condimentum ligula purus sit amet nibh. Nulla facilisi. Nullam sit amet ultrices ante. Vivamus dapibus congue libero in commodo. Nunc hendrerit sodales euismod. Praesent sapien ipsum, congue vel neque eget, condimentum venenatis felis. Phasellus et scelerisque odio. Aenean cursus quam nec purus hendrerit rutrum.'."\n".'Nunc leo purus, bibendum quis eros in, malesuada tincidunt nisi. Donec laoreet iaculis odio, sed efficitur libero condimentum vel. Suspendisse semper aliquam fringilla. Fusce posuere leo nulla. Sed fermentum arcu et dui scelerisque placerat. Vivamus ullamcorper orci in gravida porttitor. Nulla eu commodo neque. Cras eu risus in turpis commodo molestie. Donec lacinia, erat nec aliquet congue, risus ligula rhoncus neque, in ornare ipsum neque eget eros. Donec erat leo, porttitor in augue quis, pellentesque porta purus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin nec tortor vestibulum, egestas purus sit amet, aliquam justo. Vivamus vestibulum ligula quis enim efficitur semper.'."\n".'Aliquam erat mi, porttitor vel scelerisque at, lacinia et lacus. Ut et viverra ante. Vivamus accumsan nunc a tristique placerat. Duis fringilla, lorem nec fringilla pulvinar, orci justo efficitur sem, vulputate congue ipsum ex nec nunc. Nullam congue, urna lacinia pulvinar fermentum, nisl metus dapibus nulla, eu condimentum elit magna sit amet ante. Pellentesque venenatis dapibus erat a molestie. Vivamus mollis est non sagittis mollis. Cras odio nisl, viverra in pretium non, placerat at nibh.'."\n".'Sed quis rhoncus enim. Integer tellus diam, faucibus sit amet venenatis non, fringilla sit amet est. Vestibulum sed justo a ante porta tempor. Quisque gravida id ante vitae ultricies. Praesent a malesuada massa. Quisque feugiat, enim non volutpat ornare, nisi sem malesuada felis, et cursus mi justo quis turpis. Aenean id euismod erat, in ullamcorper ligula. Maecenas quis felis id lorem ornare ullamcorper. Donec augue dolor, euismod id sapien non, facilisis posuere nunc. Aenean lectus enim, laoreet non urna eu, pellentesque consectetur turpis.'); + } + + /** + * @return string + */ + public static function getDescription() + { + return 'Génère du lorem ipsum'; + } +} diff --git a/bot/Commands/Tasks.php b/bot/Commands/Tasks.php new file mode 100644 index 0000000..e78be24 --- /dev/null +++ b/bot/Commands/Tasks.php @@ -0,0 +1,53 @@ +getConfig(); + if (!array_key_exists('kanboard', $config)) { + $bot->replyPolitely('Je n\'ai pas la configuration nécessaire'); + } + $config = $config['kanboard']; + $client = new Client($config['url']); + $client->authentication('jsonrpc', $config['token']); + + $task = $data[1]; + $project = $data[2]; + + if (!is_numeric($project)) { + $asked_project = strtolower($project); + $projects = $client->getAllProjects(); + foreach ($projects as $project) { + if (strtolower($project['name']) === $asked_project) { + $project = (int) $project['id']; + break; + } + } + } + + $new_task = $client->createTask([ + 'title' => $task, + 'project_id' => $project, + ]); + + if ($new_task === false) { + $bot->replyPolitely('La tâche n\'a pas pu être ajoutée.'); + } else { + $bot->replyPolitely(sprintf('Tâche ajoutée. Elle porte le n°%u', $new_task)); + } + } + + /** + * @return string + */ + public static function getDescription() + { + return 'Ajoute une tâche dans Kanboard'; + } +} diff --git a/bot/Commands/TasksList.php b/bot/Commands/TasksList.php new file mode 100644 index 0000000..c853680 --- /dev/null +++ b/bot/Commands/TasksList.php @@ -0,0 +1,55 @@ +getConfig(); + if (!array_key_exists('kanboard', $config)) { + $bot->replyPolitely('Je n\'ai pas la configuration nécessaire'); + } + $config = $config['kanboard']; + $client = new Client($config['url']); + $client->authentication('jsonrpc', $config['token']); + + $project = $data[1]; + + if (!is_numeric($project)) { + $asked_project = strtolower($project); + $projects = $client->getAllProjects(); + foreach ($projects as $project) { + if (strtolower($project['name']) === $asked_project) { + $project = (int) $project['id']; + break; + } + } + } + + $task_list = $client->getAllTasks([ + 'project_id' => $project, + ]); + + if ($task_list === false) { + $bot->replyPolitely('Je n\'ai pas réussi à récupérer la liste'); + } else { + if (empty($task_list)) { + $bot->replyPolitely('Il n\'y a pas de tâches dans ce projet'); + } + $task_list = array_map(function($task) { return sprintf('%u: %s', $task['id'], $task['title']); }, $task_list); + $bot->replyPolitely(sprintf('Voici la liste des tâches du projet %u : %s', $project, implode('\n', $task_list))); + } + } + + /** + * @return string + */ + public static function getDescription() + { + return 'Liste les tâches d\'un projet Kanboard'; + } +} diff --git a/bot/Commands/TasksProjects.php b/bot/Commands/TasksProjects.php new file mode 100644 index 0000000..dcb6359 --- /dev/null +++ b/bot/Commands/TasksProjects.php @@ -0,0 +1,35 @@ +getConfig(); + if (!array_key_exists('kanboard', $config)) { + $bot->replyPolitely('Je n\'ai pas la configuration nécessaire'); + } + $config = $config['kanboard']; + $client = new Client($config['url']); + $client->authentication('jsonrpc', $config['token']); + + $projects = $client->getAllProjects(); + + $projects = array_filter($projects, function($project) { return $project['is_active']; }); + $projects = array_map(function($project) { return sprintf('%u: %s', $project['id'], $project['name']); }, $projects); + + $bot->replyPolitely(sprintf('Voici vos différents projets : %s', implode(', ', $projects))); + } + + /** + * @return string + */ + public static function getDescription() + { + return 'Liste les projets de Kanboard'; + } +} diff --git a/bot/Commands/Youtubedl.php b/bot/Commands/Youtubedl.php new file mode 100644 index 0000000..8f8b26c --- /dev/null +++ b/bot/Commands/Youtubedl.php @@ -0,0 +1,38 @@ +replyPolitely('L\'url n\'est pas conforme'); + } + + if (!in_array($type, ['', 'audio', 'video'], true)) { + $bot->replyPolitely('Je n\'ai pas compris ce que je devais faire'); + } + + if (in_array($type, ['', 'audio'], true)) { + $type = 'l\'audio'; + exec("youtube-dl --extract-audio --audio-format mp3 -o '/volume1/music/Podcast/Youtube/%(upload_date)s-%(uploader)s-%(title)s.%(ext)s' $url &> /dev/null &"); + } else { + $type = 'la vidéo'; + exec("youtube-dl -f bestvideo+bestaudio/best -o '/volume1/music/Podcast/Youtube/%(upload_date)s-%(uploader)s-%(title)s.%(ext)s' $url &> /dev/null &"); + } + $bot->replyPolitely(sprintf('Je %s %s', $action, $type)); + } + + public static function getDescription() + { + return 'Youtubedl wrapper'; + } +} diff --git a/bot/Request.php b/bot/Request.php new file mode 100644 index 0000000..0adc2de --- /dev/null +++ b/bot/Request.php @@ -0,0 +1,94 @@ +token = $data['token']; + } + + if (isset($data['user_id'])) { + $this->user_id = $data['user_id']; + } + + if (isset($data['username'])) { + $this->username = $data['username']; + } + + if (isset($data['post_id'])) { + $this->post_id = $data['post_id']; + } + + if (isset($data['timestamp'])) { + $this->timestamp = $data['timestamp']; + } + + if (isset($data['text'])) { + $this->text = $data['text']; + } + } + + /** + * @return mixed + */ + public function getToken() + { + return $this->token; + } + + /** + * @return mixed + */ + public function getUserId() + { + return $this->user_id; + } + + /** + * @return mixed + */ + public function getUsername() + { + return $this->username; + } + + /** + * @return mixed + */ + public function getPostId() + { + return $this->post_id; + } + + /** + * @return mixed + */ + public function getTimestamp() + { + return $this->timestamp; + } + + /** + * @return mixed + */ + public function getText() + { + return $this->text; + } + + +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..008a650 --- /dev/null +++ b/composer.json @@ -0,0 +1,11 @@ +{ + "require": { + "fguillot/json-rpc": "@stable", + "ext-soap": "*" + }, + "autoload": { + "psr-4": { + "Shikiryu\\Bot\\": "bot/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..4881de2 --- /dev/null +++ b/composer.lock @@ -0,0 +1,59 @@ +{ + "_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#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "5ba319f496d82db0def48509079b247a", + "packages": [ + { + "name": "fguillot/json-rpc", + "version": "v1.2.4", + "source": { + "type": "git", + "url": "https://github.com/fguillot/JsonRPC.git", + "reference": "ea2720b13e986bbf23e832b135c84c91ead7259f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/ea2720b13e986bbf23e832b135c84c91ead7259f", + "reference": "ea2720b13e986bbf23e832b135c84c91ead7259f", + "shasum": "" + }, + "require": { + "php": ">=5.3.4" + }, + "require-dev": { + "phpunit/phpunit": "4.8.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonRPC": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frédéric Guillot" + } + ], + "description": "Simple Json-RPC client/server library that just works", + "homepage": "https://github.com/fguillot/JsonRPC", + "time": "2017-01-12T21:31:30+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "fguillot/json-rpc": 0 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/config.example.php b/config.example.php new file mode 100644 index 0000000..730fc85 --- /dev/null +++ b/config.example.php @@ -0,0 +1,17 @@ + '', + 'token' => '', + 'kanboard' => [ + 'url' => '', + 'token'=> '', + ], + 'masters' => [ + 'Maître', + 'Monsieur', + 'Seigneur', + 'Doyen', + 'Monseigneur', + ], +]; diff --git a/index.php b/index.php new file mode 100644 index 0000000..f876356 --- /dev/null +++ b/index.php @@ -0,0 +1,43 @@ +isValid($request)) { + exit(1); +} + +// Give the bot something to listen for. +$bot->hears('(convertis|télécharges?) (https?:\/\/[^\s]+) en (audio|video|vidéo)', Youtubedl::class); + +$bot->hears('lorem( ipsum)?', Lorem::class); + +$bot->hears('liste.*projets?', TasksProjects::class); + +$bot->hears('ajoute.*t(a|â)che (.*) dans "(.*)"', Tasks::class); + +$bot->hears('(?:donne|liste).*t(?:a|â)ches? de "(.*)"', TasksList::class); + +$bot->hears('(hello|hi|bonjour|salut)', function (Bot $bot) { + $bot->replyPolitely('Bonjour'); +}); + +// Start listening +$bot->listen($_POST['text']); +