🎉 Hello world

This commit is contained in:
Clement Desmidt 2020-03-26 00:11:37 +01:00
commit eedfbcc2ab
14 changed files with 686 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/vendor
/.idea
config.php

107
bot/Bot.php Normal file
View File

@ -0,0 +1,107 @@
<?php
namespace Shikiryu\Bot;
class Bot
{
/**
* @var Request
*/
protected $request;
protected $events = [];
protected $config = [];
/**
* @var string[]
*/
protected static $masters = [];
/**
* @return string
*/
protected function getMaster()
{
$masters = array_merge(self::$masters, $this->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;
}
}

View File

@ -0,0 +1,135 @@
<?php
namespace Shikiryu\Bot\Commands\Helpers;
/**
* Creates a markdown document based on the parsed documentation
*
* @author Peter-Christoph Haider <peter.haider@zeyon.net>
* @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;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Shikiryu\Bot\Commands;
use Shikiryu\Bot\Bot;
interface Icommands
{
public static function getMessage(Bot $bot, array $data);
/**
* @return string
*/
public static function getDescription();
}

21
bot/Commands/Lorem.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace Shikiryu\Bot\Commands;
use Shikiryu\Bot\Bot;
class Lorem implements Icommands
{
public static function getMessage(Bot $bot, array $data)
{
$bot->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';
}
}

53
bot/Commands/Tasks.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace Shikiryu\Bot\Commands;
use JsonRPC\Client;
use Shikiryu\Bot\Bot;
class Tasks implements Icommands
{
public static function getMessage(Bot $bot, array $data)
{
$config = $bot->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';
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Shikiryu\Bot\Commands;
use JsonRPC\Client;
use Shikiryu\Bot\Bot;
class TasksList implements Icommands
{
public static function getMessage(Bot $bot, array $data)
{
$config = $bot->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';
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Shikiryu\Bot\Commands;
use JsonRPC\Client;
use Shikiryu\Bot\Bot;
class TasksProjects implements Icommands
{
public static function getMessage(Bot $bot, array $data)
{
$config = $bot->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';
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Shikiryu\Bot\Commands;
use Shikiryu\Bot\Bot;
class Youtubedl implements Icommands
{
public static function getMessage(Bot $bot, array $data)
{
$action = strtolower($data[1]); // ex: télécharge
$url = filter_var($data[2], FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED); // ex: https://www.youtube.com/watch?v=3BXDsVD6O10
$type = $data[3]; // ex: audio
if (empty($url)) {
$bot->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';
}
}

94
bot/Request.php Normal file
View File

@ -0,0 +1,94 @@
<?php
namespace Shikiryu\Bot;
class Request
{
protected $token;
protected $user_id;
protected $username;
protected $post_id;
protected $timestamp;
protected $text;
/**
* Request constructor.
* @param array $data
*/
public function __construct(array $data)
{
if (isset($data['token'])) {
$this->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;
}
}

11
composer.json Normal file
View File

@ -0,0 +1,11 @@
{
"require": {
"fguillot/json-rpc": "@stable",
"ext-soap": "*"
},
"autoload": {
"psr-4": {
"Shikiryu\\Bot\\": "bot/"
}
}
}

59
composer.lock generated Normal file
View File

@ -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": []
}

17
config.example.php Normal file
View File

@ -0,0 +1,17 @@
<?php
return [
'url' => '',
'token' => '',
'kanboard' => [
'url' => '',
'token'=> '',
],
'masters' => [
'Maître',
'Monsieur',
'Seigneur',
'Doyen',
'Monseigneur',
],
];

43
index.php Normal file
View File

@ -0,0 +1,43 @@
<?php
use Shikiryu\Bot\Bot;
use Shikiryu\Bot\Commands\Lorem;
use Shikiryu\Bot\Commands\Tasks;
use Shikiryu\Bot\Commands\TasksList;
use Shikiryu\Bot\Commands\TasksProjects;
use Shikiryu\Bot\Commands\Youtubedl;
use Shikiryu\Bot\Request;
require 'vendor/autoload.php';
error_reporting(E_ALL);
ini_set('log_errors', 1);
$config = include 'config.php';
$bot = new Bot($config);
$request = new Request($_POST);
if (!$bot->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']);