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 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,6 @@
1
+ module SlackScratcher
2
+ module Adapter
3
+ autoload :Elasticsearch, 'slack_scratcher/adapter/elasticsearch'
4
+ autoload :File, 'slack_scratcher/adapter/file'
5
+ end
6
+ 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,7 @@
1
+ module SlackScratcher
2
+ module Adapter
3
+ class File
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module SlackScratcher
2
+ class Collector
3
+ def initialize(loader, adapter)
4
+ @loader = loader
5
+ @adapter = adapter
6
+ end
7
+
8
+ def collect_loop
9
+ loop do
10
+ @loader.each(@adapter) do |data|
11
+ @adapter.send data
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module SlackScratcher
2
+ module Error
3
+ autoload :UserNotFoundError, 'slack_scratcher/error/user_not_found_error'
4
+ autoload :SlackApiError, 'slack_scratcher/error/slack_api_error'
5
+ autoload :FileNotFound, 'slack_scratcher/error/file_not_found'
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module SlackScratcher
2
+ module Error
3
+ class FileNotFound < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module SlackScratcher
2
+ module Error
3
+ class SlackApiError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module SlackScratcher
2
+ module Error
3
+ class UserNotFoundError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module SlackScratcher
2
+ module Loader
3
+ autoload :File, 'slack_scratcher/loader/file'
4
+ autoload :Api, 'slack_scratcher/loader/api'
5
+ end
6
+ 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,5 @@
1
+ module SlackScratcher
2
+ module Model
3
+ autoload :Chats, 'slack_scratcher/model/chats'
4
+ end
5
+ 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
@@ -0,0 +1,3 @@
1
+ module SlackScratcher
2
+ VERSION = '0.0.1'
3
+ 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: