> chatOps的な話ではなく、slack のことでお願いします。
すみません、書く直前に知りました。。
でも、カレンダー全然埋まってないし、chatOps的なこと書いてる人他にもいるので強行します。
ごめんなさい。
はじめに
slack【有料版】使ってますか?でしたらこの記事はあなたには特に必要ないです。
無料版の制約
有料版にどんな素晴らしい機能があるのかよくわかりませんが、無料版で特に不自由していません。
ただ、1つだけ、過去ログが消えてしまう、という点を除いては。
どうするか?
slackでの発言が消えてしまう前に、全部DBに溜め込んで、そこから検索させればいいじゃない。
構成
slackでのHubotとの連携の記事は腐るほどあるので、省きます。
構成は、こんな感じでやりました。
- 理想
- client > 自鯖(Go aws-sdk) > DynamoDB <> CloudSearch ← AWS Management Console から検索
- 現在
- client > 自鯖(Go aws-sdk) > DynamoDB ← aws-sdk(javascript) で検索
EC2使えばいいじゃない、という話ですが、自前の環境があったのでそちらを使いました。
golangは書いてみたかったので選択しました。
本当は理想の通り、DynamoDB→CloudSearchにデータを入れて、AWSのManagementConsoleから全文検索させてかったのですが、
DynamoDB→CloudSearchにデータを定期的に入れる方法と、いまいちCloudSearchの使い方を理解できておらず
結局のところ、DynamoDBをLIKE検索する方法で落ち着いています。
slackでの発言をDynamoDBにInsert
(slackでの発言を受信して、↓のプログラムに渡してます。hubotスクリプトは省きます)
package main
// Import the AWS SDK for Go
import (
"log"
"flag"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
/**
* Don't hard-code your credentials!
* Export the following environment variables instead:
*
* export AWS_ACCESS_KEY_ID='AKID'
* export AWS_SECRET_ACCESS_KEY='SECRET'
**/
func main() {
var account = flag.String("a", "NULL", "help message for a")
var date_time = flag.String("t", time.Now().Format("2006-01-02 15:04:05"), "help message for t")
var channel = flag.String("c", "", "help message for c")
var message = flag.String("m", "", "help message for m")
flag.Parse()
svc := dynamodb.New(session.New(&aws.Config{Region: aws.String("ap-northeast-1")}))
input := &dynamodb.PutItemInput{
TableName: aws.String("slack_log"),
Item: map[string]*dynamodb.AttributeValue{
"id": &dynamodb.AttributeValue{
S: aws.String(*account),
},
"created": &dynamodb.AttributeValue{
S: aws.String(*date_time),
},
"channel": &dynamodb.AttributeValue{
S: aws.String(*channel),
},
"message": &dynamodb.AttributeValue{
S: aws.String(*message),
},
},
}
_, err2 := svc.PutItem(input)
if err2 != nil {
log.Println(err2)
return
}
}
DynamoDBをWebブラウザから検索
- 検索条件
- チャンネル名(=)
- 発言(LIKE)
- 発言日時(BETWEEN)
function search_slack() {
var attrValues = [];
var attrNames = [];
var filter = '';
var channel = document.getElementById("search_channel").value;
var message = document.getElementById("search_message").value;
var start = document.getElementById("search_date_start").value;
var end = document.getElementById("search_date_end").value;
var dynamodb = new AWS.DynamoDB({
dynamodb: '2012-08-10',
region: 'ap-northeast-1',
accessKeyId: 'hogehoge',
secretAccessKey: 'fugafuga',
});
var params = {
TableName: 'slack_log',
};
if(channel != '') {
attrNames['#channel'] = 'channel';
attrValues[':val1'] = {S: channel};
filter = 'contains (#channel, :val1)';
}
if(message != '') {
attrNames['#message'] = 'message';
attrValues[':val2'] = {S: message};
if(filter != '') filter += ' AND ';
filter += 'contains (#message, :val2)';
}
if(start != '' && end == '') {
attrNames['#created'] = 'created';
attrValues[':val3'] = {S: start + ' 00:00:00'};
if(filter != '') filter += ' AND ';
filter += '#created >= :val3';
}
if(start == '' && end != '') {
attrNames['#created'] = 'created';
attrValues[':val3'] = {S: end + ' 23:59:59'};
if(filter != '') filter += ' AND ';
filter += '#created <= :val3';
}
if(start != '' && end != '') {
attrNames['#created'] = 'created';
attrValues[':val3'] = {S: start + ' 00:00:00'};
attrValues[':val4'] = {S: end + ' 23:59:59'};
if(filter != '') filter += ' AND ';
filter += '#created BETWEEN :val3 AND :val4';
}
if(filter != '') {
params['ExpressionAttributeNames'] = attrNames;
params['ExpressionAttributeValues'] = attrValues;
params['FilterExpression'] = filter;
}
dynamodb.scan(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
console.log(data); // successful response
$('#result').empty();
items = data.Items;
for (var i = items.length - 1; i >= 0; i--) {
var item = items[i]
$('#result').append('<div>' + item.channel.S + '</div>');
$('#result').append('<div>' + item.created.S + '</div>');
$('#result').append('<div>' + item.id.S + '</div>');
$('#result').append('<div>' + item.message.S + '</div>');
$('#result').append('<br />');
};
}
});
}
課題
- 発言が**"編集"**された場合に、検知不可
- 発言が**”削除”**された場合も、検知不可
- 新規発言はbotが拾ってくれるのですが、上記は拾ってくれないようです
- Public, Private 一即多に収集
- 発言を拾うには、channelにbotを招待しなくてはいけません
- Private channelにbotを招待した結果、それを誰もが検索できていいのか?あまりよろしくないと思います
- 誰がどのchannelに参加していて、検索権限があるか、というところまで考えるとだいぶ大変です。。
- 結局、そんなクリティカルな話しをしているわけではないので、誰でも検索可能なchannelのみbotを招待するようにしています
最大の課題
-
DMが検知不可
- DMは1対1のやり取りなので、botを招待することができません
- やるとしたら、botを含んだ3人のPrivateChannelを作成する必要がありますが、面倒臭すぎる・・
DMは別に検索できなくても良くない?と、言われたので、とりあえず諦めました。。
でも・・
DMの利用率だいぶ高いです\(^o^)/(うちのチームでは平均70%がDM)
結局
きちんとログを検索したければ、有料会員になったほうがいいです
もしもっといい方法があれば教えて下さいε≡≡ヘ( ´Д`)ノ