Slackのbotから投稿するmicroblog

先日SlackとWebを連携させるアプリを作ったので、そのときに書いたコードからサンプルを作ってみた。

1. Slack hubotを動かす

Yeomanを使ってhubotの雛形をジェネレートする。
起動スクリプトを書く。

#!/bin/sh
ARG1=$1
if [ "${ARG1}" == "" ]; then
echo "Usage: bin/hubot (start|stop|restart|status)"
exit 1;
fi
export HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN
if [ "${HUBOT_SLACK_TOKEN}" == "" ]; then
echo "env HUBOT_SLACK_TOKEN is not found"
exit 1;
fi
set -e
npm install
export PATH="node_modules/.bin:node_modules/hubot/node_modules/.bin:$PATH"
# exec node_modules/.bin/hubot --name "mybot" "$@"
start() {
forever start -c coffee node_modules/.bin/hubot -a slack
}
stop () {
forever stop -c coffee node_modules/.bin/hubot
}
status() {
forever list
}
restart() {
forever restartall
}
case "$ARG1" in
"stop" )
stop
;;
"restart" )
restart
;;
"start" )
start
;;
"status" )
status
;;
esac

https://raw.githubusercontent.com/chantera/microblog/master/app/bin/hubot

2. hubotからSQLを叩いてsqliteを操作する

CoffeeScriptを書く。JSはよく書くがCoffeeScriptは全く書かないのでつらい。

module.exports = (robot) ->
robot.hear /hi/, (res) ->
res.send res.random [
'hello',
'what?'
]
robot.enter (res) ->
res.send "Hi, #{res.message.user.name}"
robot.leave (res) ->
res.send "Goodbye, #{res.message.user.name}"
robot.respond /post (.+)/i, (res) ->
message = res.match[1]
sqlite3 = require 'sqlite3'
db = new (sqlite3.Database)('../db/app.db')
db.serialize ->
stmt = db.prepare('INSERT INTO message (body, user, post_date) VALUES (?, ?, CURRENT_TIMESTAMP)')
stmt.run message, res.message.user.name
stmt.finalize()
res.reply "Post your message successfully"
return
db.close()
robot.respond /select/i, (res) ->
sqlite3 = require 'sqlite3'
db = new (sqlite3.Database)('../db/app.db')
db.serialize ->
db.each 'SELECT * FROM message WHERE status=1 ORDER BY id DESC', (err, row) ->
res.send row.id + ': ' + row.user + ', ' + row.body + ', ' + row.post_date
return
return
db.close()
robot.respond /delete (\d+)/i, (res) ->
id = res.match[1]
sqlite3 = require 'sqlite3'
db = new (sqlite3.Database)('../db/app.db')
db.serialize ->
stmt = db.prepare('UPDATE message SET status=2 WHERE id=?')
stmt.run id
stmt.finalize()
res.send "Delete your message successfully"
return
db.close()

https://raw.githubusercontent.com/chantera/microblog/master/app/scripts/app.coffee

3. オレオレPHPフレームワークでsqliteから投稿を表示

サンプルで使うだけのミニマルなフレームワークがないので自分で書く。勉強になって良い。

<?php
function bootstrap() {
$script = $_SERVER['SCRIPT_NAME'];
define('ENTRY_POINT', '/index.php');
define('WEBROOT', substr($script, 0, strrpos($script, ENTRY_POINT)));
session_start();
return true;
}
class App
{
protected $_routes = array(
'GET'    => array(),
'POST'   => array(),
'PUT'    => array(),
'DELETE' => array(),
'ERROR'  => array(),
);
public function __construct()
{
}
public function run()
{
$dispatcher = new Dispatcher();
return $dispatcher->dispatch(new Request(), $this->_routes);
}
public function get($url, $callback)
{
$this->_addRoute('GET', $url, $callback);
}
public function post($url, $callback)
{
$this->_addRoute('POST', $url, $callback);
}
public function put($url, $callback)
{
$this->_addRoute('PUT', $url, $callback);
}
public function delete($url, $callback)
{
$this->_addRoute('DELETE', $url, $callback);
}
public function error($code, $callback)
{
$this->_addRoute('ERROR', (string) $code, $callback);
}
protected function _addRoute($method, $url, $callback)
{
if ( !is_callable($callback) && !is_string($callback) ) {
throw new InvalidArgumentException('invalid callback type');
}
$this->_routes[$method][$url] = $callback;
}
}
class Dispatcher
{
public function dispatch(Request $request, $routes)
{
if ( isset($routes[$request->method][$request->path]) ) {
$callback = $routes[$request->method][$request->path];
} else {
$callback = $routes['ERROR']['404'];
}
if ( is_callable($callback) ) {
$func = $callback;
$args = array($request);
} else if ( is_string($callback) ) {
$names = explode(':', $callback);
if ( count($names) == 2 ) {
$class = $names[0] . 'Controller';
$func = array(new $class($request), $names[1]);
$args = array();
} else {
$func = $names[0];
$args = array($request);
}
} else {
throw new Exception('cannot resolve callback');
}
call_user_func_array($func, $args);
return true;
}
}
class Request
{
protected $_values;
public function __construct()
{
$s = $_SERVER;
$path = array_key_exists('PATH_INFO', $s) ? $s['PATH_INFO'] : '/';
$this->_values = array(
'method' => $s['REQUEST_METHOD'],
'uri'    => $s['REQUEST_URI'],
'path'   => $path,
'get'    => $_GET,
'post'   => $_POST,
);
}
public function __get($name)
{
return $this->_values[$name];
}
}
class Controller
{
protected $_request;
protected $_vars = array();
public function __construct(Request $request)
{
$this->_request = $request;
}
protected function _set(array $vars)
{
$this->_vars = $vars;
}
protected function _render($template)
{
extract($this->_vars, EXTR_OVERWRITE);
include_once($template);
}
}
bootstrap();

https://raw.githubusercontent.com/chantera/microblog/master/www/core.php

ちなみにアプリケーションはこんな感じで書く。

<?php
require __DIR__ . '/core.php';
define('APP_DIR', __DIR__);
define('DB', APP_DIR . '/../db/app.db');
class MyController extends Controller
{
public function index()
{
$this->_set([
'author' => 'Hiroki Teranishi',
'message' => 'Hello World!',
]);
$this->_render(APP_DIR . '/templates/index.php');
}
public function posts()
{
$model = new Post();
$messages = $model->find();
$this->_set(compact('messages'));
$this->_render(APP_DIR . '/templates/posts.php');
}
}
class Post
{
public function find()
{
$posts = [];
try {
$pdo = new PDO('sqlite:' . DB);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT * FROM message WHERE status=1 ORDER BY id DESC");
$posts = $stmt->fetchAll();
} catch (Exception $e) {
echo $e->getMessage();
}
return $posts;
}
}
function execute() {
try {
$app = new App();
$app->get('/', 'My:index');             // routing '/' to MyController::index action
$app->get('/posts', 'My:posts');        // routing '/posts' to MyController::posts action
$app->get('/about', function() {        // routing '/about' to a callback function
echo '<a href="https://github.com/chantera" target="_blank">See my GitHub page.</a>';
});
$app->error(404, 'error404');           // routing 404 error to a function by name
$app->run();
} catch (Exception $e) {
echo $e->getMessage();
echo $e->getTraceAsString();
}
}
function error404($request) {
?>
<h1>Not Found</h1>
<p>The requested URL <?= $request->path ?> was not found on this server.</p>
<hr>
<p>This is generated by myblog app.</p>
<?php
}
function h($string) {
return htmlspecialchars($string);
}

https://raw.githubusercontent.com/chantera/microblog/master/www/app.php

投稿してみる。

screenshot1

うまく表示された。

screenshot2

ソースコードは以下に置いてます。

https://github.com/chantera/microblog/

数年ぶりに技術者っぽい投稿をしたなあ。

週末にSlackでbotと会話していると心がすさみます。

LINEで送る
Pocket

コメントを残す

*