ローカルでも動くSlackBot管理ツールができるまで 〜 WebUI編 1 〜

2015年6月20日

今回から何回かに分けて、はてブから情報を取得しSlackに投稿するBOTを管理するツールを作成していく。
今までにこのブログ内で紹介しgithubにあげているソースを利用する。

[amazonjs asin="B009Z30HPG" locale="JP" title="Dive Into Python 3 日本語版"]

完成形

[雑][Python] ローカルでも動くSlackのBot管理ツールを作成してみた(Bottle + jinja2 + Slacker)

 

構成

slack_botter

 

開発環境

今回はローカル(MacBookAir)で開発してみた。
brewでpython3をインストールしている前提とする。

[shell]
$ sudo brew install python3
[/shell]

データベースはローカルであることとPython標準ライブラリとして入っているため、
sqlite3で構築する。

 

venvで仮想環境を構築する

pyvenvで仮想環境を構築し、pipをアップデートしておく。

[shell]
$ cd ~/Documents
$ pyvenv slack_botter
$ cd slack_botter
$ source bin/activate
(slack_botter)$ pip install –upgrade pip
[/shell]

 

githubから必要なファイルを取得する

以前、Pythonフレームワーク bottle とテンプレートエンジン jinja2 で作ったものを利用するため、githubからダウンロードする。
ダウンロードしたものをsitesという名前にリネームしておく。

[shell]
(slack_botter)$ cd ~/Documents/slack_botter
(slack_botter)$ git clone https://github.com/umentu/bottle_jinja2
(slack_botter)$ mv bottle_jinja2 sites
[/shell]

 

pipで必要なライブラリをインストール

githubからダウンロードしたものの中にrequirements.txtが入っているため、pipで必要なライブラリをインストールする。

[shell]
(slack_botter)$ pip install -r ./sites/requirements.txt
[/shell]

sites/index.pyを実行し、ブラウザで https://localhost:8080/top/ にアクセスして表示できるかを確認してみる。

[shell]
(slack_botter)$ python sites/index.py
[/shell]

 

Slackのチャンネル名一覧を取得するため、gitからslackbotを取得

SlackBotを管理する際に、存在するチャンネル一覧を表示させたいため、Slackからチャンネル一覧を取得する。
これも以前作ったものを利用するため、
githubからダウンロードする。

今回はslack_bot.pyというファイルのみ必要なため、sites内に移動しておく。
また、requirements.txtがあるため、slackbot用に必要なライブラリをpipでインストールする。
以上が終わったらslackbotディレクトリは必要ないため、slackbotディレクトリを削除する。

[shell]
(slack_botter)$ cd ~/Documents/slack_botter
(slack_botter)$ git clone https://github.com/umentu/slackbot
(slack_botter)$ mv slackbot/slack_bot.py ./
(slack_botter)$ pip install -r slackbox/requirements.txt
(slack_botter)$ rm -rf ./slackbot
[/shell]

 

sqlite3 でデータベース作成

sqlite3のDBファイルを作成するために、slack_botterディレクトリ配下にdbディレクトリを作成しておく。

[shell]
(slack_botter)$ cd ~/Documents/slack_botter
(slack_botter)$ mkdir db
[/shell]

dbディレクトリ配下に、データベース・テーブル・テストデータを作成するためのcreate_db.pyファイルを作成する。

[python title="create_db.pyの内容"]
# -*- coding: utf-8 -*-

import os
import sqlite3

if __name__ == '__main__’:

# データベースアクセス

path = os.path.dirname( os.path.abspath(__file__))
connect = sqlite3.connect(path + "/hatena.db")
cursor = connect.cursor()

# テーブル作成

sql = """
CREATE TABLE hatebu_list (
id INTEGER PRIMARY KEY,
channel VARCHAR(128),
word TEXT,
bookmark_count INTEGER,
last_post TEXT
);
"""

# テーブル確認

cursor.execute(sql)

sql = """
SELECT * FROM sqlite_master;
"""

con = cursor.execute(sql)
for row in con:
print(row)

# テストデータ挿入

sql = """
INSERT INTO hatebu_list (
channel,
word,
bookmark_count,
last_post)
VALUES (
'test_channel’,
'test_word’,
100,
'2000-01-01T00:00:00+09:00’
);
"""

cursor.execute(sql)

# テストデータ確認

sql = """
SELECT * FROM hatebu_list;
"""

con = cursor.execute(sql)
for row in con:
print(row)

# 変更を更新する
connect.commit()

# データベースを閉じる
connect.close()

[/python]

実行すると、dbディレクトリ配下に hatena.db というDBファイルが作成される。

[shell]
(slack_botter)$ python db/create_db.py
('table’, 'hatebu_list’, 'hatebu_list’, 2, 'CREATE TABLE hatebu_list (
id INTEGER PRIMARY KEY,
channel VARCHAR(128),
word TEXT,
bookmark_count INTEGER,
last_post TEXT
)’)
(1, 'test_channel’, 'test_word’, 100, '2000-01-01T00:00:00+09:00’)
(slack_botter)$ ls -l ./db
total 16
-rw-r–r– 1 umentu staff 1236 6 16 23:39 create_db.py
-rw-r–r– 1 umentu staff 2048 6 16 23:40 hatena.db
[/shell]

UIを作成

(以降から若干置いてけぼり感が出てしまうかもしれないのですが。。。)

sitesディレクトリ配下にWebUIの本体となるsites.pyを作成する。
index.pyをコピーして作成すると楽。

[python title="sites.pyの内容"]
# -*- coding: utf-8 -*-

import os
import sys
import csv

# sqlite3をインポート
import sqlite3

# slack_botライブラリもインポート
sys.path.append( os.path.dirname(__file__) )
from slack_bot import Slack

from bottle import route, run
from bottle import TEMPLATE_PATH, jinja2_template as template
from bottle import static_file
from bottle import get, post, request, response
from bottle import redirect

# sites.pyが設置されているディレクトリの絶対パスを取得
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# テンプレートファイルを設置するディレクトリのパスを指定
TEMPLATE_PATH.append(BASE_DIR + "/views")

@route('/css/<filename>’)
def server_css(filename):
""" setting for css file """
return static_file(filename, root=BASE_DIR+"/static/css")

@route('/js/<filename>’)
def server_js(filename):
""" setting for js file """
return static_file(filename, root=BASE_DIR+"/static/js")

@route('/img/<filename>’)
def server_img(filename):
""" setting for img file """
return static_file(filename, root=BASE_DIR+"/static/img")

@route('/font/<filename>’)
def server_font(filename):
""" setting for font file """
return static_file(filename, root=BASE_DIR+"/static/fonts")

@route('/hatebu’)
@route('/hatebu/’)
def hatebu():

return template('hatebu’)

if __name__ == "__main__":
run(host="localhost", port=8080, debug=True, reloader=True)

[/python]

sites/views 配下のtop.htmlをhatebu.htmlという名前でコピー

[shell]
(slack_botter)$ cp sites/views/top.html sites/views/hatebu.html
[/shell]

sites.pyを実行して、ブラウザでhttps://localhost:8080/hatebuにアクセスできるか確かめる。

アクセスできたら、sites/sites.py のhatebu関数を再度編集

[python title="sites.pyの内容"]
@route('/hatebu’)
@route('/hatebu/’)
def hatebu():

# データベースアクセス

path = os.path.dirname(os.path.dirname( os.path.abspath(__file__))) + "/db"
connect = sqlite3.connect(path + "/hatena.db")
cursor = connect.cursor()

# hatebu_list テーブルからデータを取得
sql = """
SELECT * FROM hatebu_list
"""

# 取得したデータをパースしてリストに保存

hatebu_list = []
con = cursor.execute(sql)
for row in con:
tmp_row = {}
for idx, col in enumerate(cursor.description):
tmp_row[col[0]] = row[idx]

hatebu_list.append(tmp_row)

connect.close()

# Slackのチャンネル一覧を取得
slack = Slack("SlackのToken")
channel_list = slack.get_channnel_list()

return template('hatebu’, hatebu_list=hatebu_list, channel_list=channel_list)

[/python]

sites/views/hatebu.html を以下のように編集
(formはここで紹介したように記述している。)
作成ボタンが押された場合は、 /hatebu_create に
更新ボタン、削除ボタンが押された場合は、 /hatebu_updel に移動して処理する。

実際の作成・更新・削除の処理は次回実装する。

[html title="hatebu.html"]
{% extends 'base.html’ %}

{# base.html の title の中に入れるコンテンツ #}
{% block title %}
Slackツール
{% endblock %}

{# ヘッダー情報を追加する場合はここに記述する。 #}
{% block header %}
{% endblock%}

{# base.html の contents の中に入れるコンテンツ #}
{% block contents %}

<h1>Slackツール</h1>
<div>
<form action="/hatebu_create" method="get" accept-charset="utf-8" id="create"></form>

<table class="table">
<tbody>
<tr>
<td>チャンネル</td>
<td>
<select name="channel" form="create">
{% for cl in channel_list %}
<option value="{{cl.channel_name}}">{{cl.channel_name}}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td>キーワード</td>
<td><input type="text" name="word" form="create" placeholder="キーワード" /></td>
</tr>
<tr>
<td>取得するブックマーク数</td>
<td><input type="number" name="bookmark_count" form="create" placeholder="ブックマーク数" /></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="create" value="作成" form="create" class="btn btn-success" /></td>
</tr>

</tbody>
</table>
</div>

<div>
<table class="table table-striped">
<thead>
<tr>
<td>ID</td>
<td>チャンネル</td>
<td>キーワード</td>
<td>取得するブックマーク数</td>
<td>更新</td>
<td>削除</td>
</tr>
</thead>
<tbody>
{% for hl in hatebu_list %}
<tr>
<td>
{{hl.id}}
<input type="hidden" name="id" form="form{{hl.id}}" value="{{hl.id}}" />
</td>
<td>
<select name="channel" form="form{{hl.id}}">
{% for cl in channel_list %}

{# 登録されているBOTのチャンネルと一致している場合は選択されているようにする。 #}

{% if cl.channel_name == hl.channel %}
<option value="{{cl.channel_name}}" selected="selected">{{cl.channel_name}}</option>
{% else %}
<option value="{{cl.channel_name}}">{{cl.channel_name}}</option>
{% endif %}
{% endfor %}
</select>
</td>
<td>
<input type="text" name="word" form="form{{hl.id}}" value="{{hl.word}}" />
</td>
<td>
<input type="number" name="bookmark_count" form="form{{hl.id}}" value="{{hl.bookmark_count}}" />
</td>
<td>
<input type="submit" name="updel" form="form{{hl.id}}" value="更新" class="btn btn-info" />
</td>
<td>
<input type="submit" name="updel" form="form{{hl.id}}" value="削除" class="btn btn-danger" />
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

{% for hl in hatebu_list %}
<form action="/hatebu_updel" method="get" id="form{{hl.id}}" accept-charset="utf-8"></form>
{% endfor %}

{% endblock %}
[/html]

sites.pyを実行し、https://localhost:8080/hatebu にアクセスしてデータが表示されたら、とりあえずUI風完成。
次回、作成・更新・削除の実装を紹介する。

 

続き:

ローカルでも動くSlackBot管理ツールができるまで 〜 WebUI編 2 〜