Browse Source

🎉 Hello world

master
Clement Desmidt 2 years ago
commit
eedfbcc2ab
  1. 3
      .gitignore
  2. 107
      bot/Bot.php
  3. 135
      bot/Commands/Helpers/TableHelper.php
  4. 15
      bot/Commands/Icommands.php
  5. 21
      bot/Commands/Lorem.php
  6. 53
      bot/Commands/Tasks.php
  7. 55
      bot/Commands/TasksList.php
  8. 35
      bot/Commands/TasksProjects.php
  9. 38
      bot/Commands/Youtubedl.php
  10. 94
      bot/Request.php
  11. 11
      composer.json
  12. 59
      composer.lock
  13. 17
      config.example.php
  14. 43
      index.php

3
.gitignore

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

107
bot/Bot.php

@ -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;
}
}

135
bot/Commands/Helpers/TableHelper.php

@ -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;
}
}

15
bot/Commands/Icommands.php

@ -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

File diff suppressed because one or more lines are too long

53
bot/Commands/Tasks.php

@ -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';
}
}

55
bot/Commands/TasksList.php

@ -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';
}
}

35
bot/Commands/TasksProjects.php

@ -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';
}
}

38
bot/Commands/Youtubedl.php

@ -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

@ -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

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

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

17
config.example.php

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

43
index.php

@ -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']);
Loading…
Cancel
Save