slack_scratcher 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/slack_scratcher.rb +15 -0
- data/lib/slack_scratcher/adapter.rb +6 -0
- data/lib/slack_scratcher/adapter/elasticsearch.rb +82 -0
- data/lib/slack_scratcher/adapter/file.rb +7 -0
- data/lib/slack_scratcher/collector.rb +16 -0
- data/lib/slack_scratcher/error.rb +7 -0
- data/lib/slack_scratcher/error/file_not_found.rb +6 -0
- data/lib/slack_scratcher/error/slack_api_error.rb +6 -0
- data/lib/slack_scratcher/error/user_not_found_error.rb +6 -0
- data/lib/slack_scratcher/loader.rb +6 -0
- data/lib/slack_scratcher/loader/api.rb +65 -0
- data/lib/slack_scratcher/loader/file.rb +77 -0
- data/lib/slack_scratcher/model.rb +5 -0
- data/lib/slack_scratcher/model/chats.rb +64 -0
- data/lib/slack_scratcher/router.rb +17 -0
- data/lib/slack_scratcher/version.rb +3 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bed4b833ec200a7e4269205379ff12118ee364c9
|
4
|
+
data.tar.gz: 1bf60094a6d36fc79843f6ed0cecf6670d1ea78e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: df7817192ebf16acc8fac4dcea7029d2f8bfb44c3d95dbcff8606913000802b281f46d511aa242907908ea7bb5c876d24e67b4ab8a5a5e1c0595251525853ff2
|
7
|
+
data.tar.gz: 335bea07ac31c3252cce066f3cc8fba41e75e39153652d469d47d34d9a6d115b6931d99f54d296bda0328e1dcb92514a2e982c9f7c170852f9e9cb93fa8010d9
|
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module SlackScratcher
|
5
|
+
autoload :Model, 'slack_scratcher/model'
|
6
|
+
autoload :Loader, 'slack_scratcher/loader'
|
7
|
+
autoload :Adapter, 'slack_scratcher/adapter'
|
8
|
+
autoload :Router, 'slack_scratcher/router'
|
9
|
+
autoload :Collector, 'slack_scratcher/collector'
|
10
|
+
autoload :Error, 'slack_scratcher/error'
|
11
|
+
|
12
|
+
def self.logger
|
13
|
+
@logger ||= ::Logger.new(STDOUT)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'elasticsearch'
|
2
|
+
|
3
|
+
module SlackScratcher
|
4
|
+
module Adapter
|
5
|
+
class Elasticsearch
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize(hosts, metadata)
|
9
|
+
@client = ::Elasticsearch::Client.new hosts: hosts
|
10
|
+
@metadata = metadata
|
11
|
+
end
|
12
|
+
|
13
|
+
def send(raw_data)
|
14
|
+
data = format_bulk(raw_data)
|
15
|
+
@client.bulk data unless raw_data.empty?
|
16
|
+
rescue ::Elasticsearch::Transport::Transport::Errors::BadRequest => error
|
17
|
+
puts error
|
18
|
+
end
|
19
|
+
|
20
|
+
def timestamp_of_last_channel_log(channel_name)
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def ready_index
|
25
|
+
unless index?
|
26
|
+
create_index
|
27
|
+
put_mapping
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def index
|
34
|
+
@metadata[:index]
|
35
|
+
end
|
36
|
+
|
37
|
+
def type
|
38
|
+
@metadata[:type]
|
39
|
+
end
|
40
|
+
|
41
|
+
def index?
|
42
|
+
@client.indices.exists(index: index)
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_index
|
46
|
+
@client.indices.create(index: index)
|
47
|
+
end
|
48
|
+
|
49
|
+
def put_mapping
|
50
|
+
@client.indices.put_mapping create_body(mapping)
|
51
|
+
end
|
52
|
+
|
53
|
+
def mapping
|
54
|
+
{ type =>
|
55
|
+
{ '_timestamp' => { 'enabled' => true, 'path' => 'dataetime' },
|
56
|
+
'_id' => { 'path' => 'uid' } } }
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_body(body = {})
|
60
|
+
{
|
61
|
+
index: index,
|
62
|
+
type: type,
|
63
|
+
body: body
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_bulk(data)
|
68
|
+
{ body: data.map { |log| format_log(log) } }
|
69
|
+
end
|
70
|
+
|
71
|
+
def format_log(log)
|
72
|
+
{ index:
|
73
|
+
{
|
74
|
+
_index: index,
|
75
|
+
_type: type,
|
76
|
+
data: log
|
77
|
+
}
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'slack'
|
2
|
+
|
3
|
+
module SlackScratcher
|
4
|
+
module Loader
|
5
|
+
class Api
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
WAIT_TIME = 15
|
9
|
+
|
10
|
+
def initalize
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(adapter)
|
15
|
+
active_channels do |channel|
|
16
|
+
wait
|
17
|
+
from = adapter.time_of_channel_last_log(channel.name)
|
18
|
+
to = 'now'
|
19
|
+
yield channel_history(channel, from, to)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def channels
|
26
|
+
result = Slack.channels_list['channels']
|
27
|
+
fail SlackScratcher::Error::ApiError unless result['ok'] == true
|
28
|
+
|
29
|
+
result.map { |ch| { id: ch['id'], name: ch['name'] } }
|
30
|
+
end
|
31
|
+
|
32
|
+
def active_channels
|
33
|
+
result = Slack.channels_list['channels']
|
34
|
+
fail SlackScratcher::Error::ApiError unless result['ok'] == true
|
35
|
+
|
36
|
+
result.select { |ch| ch['is_archived'] == false }
|
37
|
+
.map { |ch| { id: ch['id'], name: ch['name'] } }
|
38
|
+
end
|
39
|
+
|
40
|
+
def user_info(user_id)
|
41
|
+
result = Slack.users_info(user: user_id)
|
42
|
+
fail SlackScratcher::Error::ApiError unless result['ok'] == true
|
43
|
+
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
def channel_history(channel, from, to = Time.now)
|
48
|
+
attrs = {
|
49
|
+
channel: channel,
|
50
|
+
oldest: from.to_i,
|
51
|
+
latest: to.to_i
|
52
|
+
}
|
53
|
+
|
54
|
+
result = Slack.channels_history(attrs)
|
55
|
+
fail SlackScratcher::Error::ApiError unless result['ok'] == true
|
56
|
+
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def wait
|
61
|
+
sleep WAIT_TIME
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'oj'
|
2
|
+
|
3
|
+
module SlackScratcher
|
4
|
+
module Loader
|
5
|
+
class File
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(target_dir)
|
9
|
+
fail ArgumentError unless ::File.directory?(target_dir)
|
10
|
+
|
11
|
+
@target = target_dir
|
12
|
+
@users = users
|
13
|
+
@channels = channels
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
files.each do |file|
|
18
|
+
yield parse_log_file(file), file
|
19
|
+
end
|
20
|
+
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def get_channel_dir(path)
|
27
|
+
::File.dirname(path).split('/').last
|
28
|
+
end
|
29
|
+
|
30
|
+
def users
|
31
|
+
load "#{@target}/users.json", 'id'
|
32
|
+
end
|
33
|
+
|
34
|
+
def channels
|
35
|
+
load "#{@target}/channels.json", 'name'
|
36
|
+
end
|
37
|
+
|
38
|
+
def load(target, index_column)
|
39
|
+
fail SlackScratcher::Error::FileNotFound unless ::File.exist? target
|
40
|
+
|
41
|
+
channels = Oj.load(::File.read(target))
|
42
|
+
index_data channels, index_column
|
43
|
+
end
|
44
|
+
|
45
|
+
def index_data(dataset, column)
|
46
|
+
dataset.map { |data| { data[column] => data } }.inject({}, :merge)
|
47
|
+
end
|
48
|
+
|
49
|
+
def channel_info(log_file)
|
50
|
+
name = get_channel_dir(log_file)
|
51
|
+
{ name: name, id: @channels[name]['id'] }
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_log_file(log_file)
|
55
|
+
channel = channel_info(log_file)
|
56
|
+
logs = Oj.load(::File.read(log_file))
|
57
|
+
chats = SlackScratcher::Model::Chats.new(logs, channel, @users)
|
58
|
+
|
59
|
+
chats.refined_data
|
60
|
+
end
|
61
|
+
|
62
|
+
def files
|
63
|
+
channels.inject([]) do |arr, channel|
|
64
|
+
arr + log_files(channel)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def channels
|
69
|
+
Dir["#{@target}/*/"]
|
70
|
+
end
|
71
|
+
|
72
|
+
def log_files(channel)
|
73
|
+
Dir["#{channel}*.json"]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module SlackScratcher
|
2
|
+
module Model
|
3
|
+
class Chats
|
4
|
+
attr_reader :refined_data, :data
|
5
|
+
|
6
|
+
def initialize(data, channel, users)
|
7
|
+
if !data.is_a?(Array) || !users.is_a?(Hash) || !channel.is_a?(Hash)
|
8
|
+
fail ArgumentError
|
9
|
+
end
|
10
|
+
|
11
|
+
@data = data
|
12
|
+
@users = users
|
13
|
+
@channel = channel
|
14
|
+
|
15
|
+
@refined_data = refine
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def refine
|
21
|
+
@data
|
22
|
+
.map { |log| refine_data(log) }
|
23
|
+
.select { |log| !log['uid'].nil? }
|
24
|
+
end
|
25
|
+
|
26
|
+
def refine_data(log)
|
27
|
+
user = find_user(log['user']) unless user
|
28
|
+
|
29
|
+
log['name'] = user['name']
|
30
|
+
log['profile_image'] = user['profile']['image_32']
|
31
|
+
log['text'] = refine_text(log['text'])
|
32
|
+
log['channel'] = @channel[:name]
|
33
|
+
log['channel_id'] = @channel[:id]
|
34
|
+
log['datetime'] = Time.at(log['ts'].to_f).iso8601
|
35
|
+
|
36
|
+
log['uid'] = create_uid(log)
|
37
|
+
|
38
|
+
log
|
39
|
+
rescue SlackScratcher::Error::UserNotFoundError
|
40
|
+
user = { 'user' => 'undefined' }
|
41
|
+
user['profile'] = { 'image_32' => '' }
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_uid(log)
|
45
|
+
"#{log['datetime']}-#{log['channel_id']}-#{log['user']}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def refine_text(text)
|
49
|
+
text = ":#{text}" if text.is_a?(Symbol)
|
50
|
+
|
51
|
+
text
|
52
|
+
.gsub(%r{<@([A-Z0-9]{3,10})>}) { '@' + @users[$1]['name'] }
|
53
|
+
.gsub(%r{<(http(s)?://.*?)>}) { $1 }
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_user(user)
|
57
|
+
result = @users[user]
|
58
|
+
|
59
|
+
fail SlackScratcher::Error::UserNotFoundError if result.nil?
|
60
|
+
result
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SlackScratcher
|
2
|
+
class Router
|
3
|
+
def initialize(loader, adapter)
|
4
|
+
@loader = loader
|
5
|
+
@adapter = adapter
|
6
|
+
end
|
7
|
+
|
8
|
+
def route
|
9
|
+
@adapter.ready_index
|
10
|
+
|
11
|
+
@loader.each do |data, file|
|
12
|
+
@adapter.send data
|
13
|
+
SlackScratcher.logger.info "* #{file} is routed."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: slack_scratcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daekwon Kim
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: elasticsearch
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oj
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: slack-api
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: guard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: codeclimate-test-reporter
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Importing slack log from exported files or API to elasticsearch or other
|
140
|
+
datastores
|
141
|
+
email:
|
142
|
+
- propellerheaven@gmail.com
|
143
|
+
executables: []
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files: []
|
146
|
+
files:
|
147
|
+
- lib/slack_scratcher.rb
|
148
|
+
- lib/slack_scratcher/adapter.rb
|
149
|
+
- lib/slack_scratcher/adapter/elasticsearch.rb
|
150
|
+
- lib/slack_scratcher/adapter/file.rb
|
151
|
+
- lib/slack_scratcher/collector.rb
|
152
|
+
- lib/slack_scratcher/error.rb
|
153
|
+
- lib/slack_scratcher/error/file_not_found.rb
|
154
|
+
- lib/slack_scratcher/error/slack_api_error.rb
|
155
|
+
- lib/slack_scratcher/error/user_not_found_error.rb
|
156
|
+
- lib/slack_scratcher/loader.rb
|
157
|
+
- lib/slack_scratcher/loader/api.rb
|
158
|
+
- lib/slack_scratcher/loader/file.rb
|
159
|
+
- lib/slack_scratcher/model.rb
|
160
|
+
- lib/slack_scratcher/model/chats.rb
|
161
|
+
- lib/slack_scratcher/router.rb
|
162
|
+
- lib/slack_scratcher/version.rb
|
163
|
+
homepage: http://slack-scratcher.nacyot.com
|
164
|
+
licenses:
|
165
|
+
- MIT
|
166
|
+
metadata: {}
|
167
|
+
post_install_message:
|
168
|
+
rdoc_options: []
|
169
|
+
require_paths:
|
170
|
+
- lib
|
171
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: 2.0.0
|
176
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
requirements: []
|
182
|
+
rubyforge_project:
|
183
|
+
rubygems_version: 2.2.2
|
184
|
+
signing_key:
|
185
|
+
specification_version: 4
|
186
|
+
summary: Slack log import tool
|
187
|
+
test_files: []
|
188
|
+
has_rdoc:
|