mess 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +65 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/mess +232 -0
- data/lib/mess.rb +15 -0
- data/lib/mess/chat.rb +77 -0
- data/lib/mess/error.rb +12 -0
- data/lib/mess/plot.rb +30 -0
- data/lib/mess/plot/daily_msgs.rb +32 -0
- data/lib/mess/plot/total_delay.rb +37 -0
- data/lib/mess/plot/total_lettres.rb +22 -0
- data/lib/mess/plot/total_msgs.rb +21 -0
- data/lib/mess/tree.rb +78 -0
- data/lib/mess/version.rb +5 -0
- data/mess.gemspec +25 -0
- data/pkg/mess-0.0.0.gem +0 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 70d63987314f20d83db49e145f32fb23e5bb1b20608d343672c474f3aea128e4
|
4
|
+
data.tar.gz: 3883e201fb6e57978cd55215a9b4951e8c0133d441bd4a9514f4e2cb3f7410df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 98019de61d628b344ab85c05d81e5db2aecbb63cc101e8164cbec8395f871d31b1ac5fa4d56ad859d662d31698747a51f874c6e38a1f580b85198ce537ed90be
|
7
|
+
data.tar.gz: '09922eb1f81a2030e9a1fdfc1e8224b3886ad0048281048b4deb62a078d604df59e456eb5f6cb45f9212aed782e5b1275b449bb658f1c3640adc01c29a1f4a81'
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 patztablook22
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# mess
|
2
|
+
|
3
|
+
Ruby tool for analysis of native Facebook Messenger Archives in JSON format. Output is universal (also JSON) and can thus be used by table processors (like MS Excell / LibreOffice Calc) and more!
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
Ruby is required. You can download mess as a Ruby Gem:
|
7
|
+
```bash
|
8
|
+
gem install mess
|
9
|
+
```
|
10
|
+
Alternatively, after cloning this repo, run:
|
11
|
+
```bash
|
12
|
+
bundle exec rake install
|
13
|
+
```
|
14
|
+
|
15
|
+
## Facebook archive
|
16
|
+
provide the program with **JSON** (!) Facebook archive _(as opposed to HTML format)_
|
17
|
+
|
18
|
+
## Examples
|
19
|
+
Call `main` either directly or via link.
|
20
|
+
```bash
|
21
|
+
|
22
|
+
# print help
|
23
|
+
main --help
|
24
|
+
|
25
|
+
# guided analysis of given archive
|
26
|
+
main --facebook my/facebook/archive
|
27
|
+
|
28
|
+
# analysis specifying chat and plots
|
29
|
+
main --facebook my/facebook/archive --chat 3 --plot asdf
|
30
|
+
|
31
|
+
# list all available chats and plots for given archive
|
32
|
+
main --facebook my/facebook/archive --list --chat --plot
|
33
|
+
|
34
|
+
```
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
Mess provides class `Mess::Plot` to be inherited for custom plots; \
|
38
|
+
quick example (for more, see `mess/plots` dir):
|
39
|
+
```ruby
|
40
|
+
module Mess
|
41
|
+
class MyPlot < Plot
|
42
|
+
|
43
|
+
# this will make MyPlot "available"
|
44
|
+
describe "my plot for magic analysis"
|
45
|
+
|
46
|
+
def initialize chat # constructor
|
47
|
+
# blah blah # whatever is needed
|
48
|
+
# more code # should be only preparation
|
49
|
+
end
|
50
|
+
|
51
|
+
def push msg # being called for every single message chronologically
|
52
|
+
if msg["sender_name"].include? "my"
|
53
|
+
# do stuff
|
54
|
+
else
|
55
|
+
# don't etc.
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def review # called after all messages have been push-ed
|
60
|
+
# body # perhaps reformat the data and so on
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "mess"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/mess
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'io/console'
|
5
|
+
require 'slop'
|
6
|
+
require_relative '../lib/mess'
|
7
|
+
|
8
|
+
Signal.trap('INT') { exit }
|
9
|
+
|
10
|
+
$listing = Array.new
|
11
|
+
|
12
|
+
begin
|
13
|
+
opts = Slop.parse do |o|
|
14
|
+
|
15
|
+
o.banner = "usage: #{$0} [options] target"
|
16
|
+
|
17
|
+
o.on '--help', 'print help' do
|
18
|
+
puts o
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
o.on '--version', 'print version' do
|
23
|
+
puts Mess::VERSION
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
o.string '--output', '-o', 'output file', default: 'mess_out.txt'
|
28
|
+
|
29
|
+
o.bool '--archived', '-a', 'archived threads too', default: false
|
30
|
+
|
31
|
+
o.bool '--list', '-l', 'listing mode'
|
32
|
+
|
33
|
+
o.array '--chat', '-c', 'chat specification' do
|
34
|
+
$listing << :chats
|
35
|
+
end
|
36
|
+
|
37
|
+
o.array '--plot', '-p', 'plot specification' do
|
38
|
+
$listing << :plots
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
rescue Slop::MissingArgument => e
|
43
|
+
p e.message
|
44
|
+
rescue Slop::UnknownOption => e
|
45
|
+
puts e
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
target = opts&.arguments || Array.new
|
50
|
+
target << '.' if target.none?
|
51
|
+
|
52
|
+
if target.one?
|
53
|
+
target = target.first
|
54
|
+
else
|
55
|
+
$stderr.puts 'too many targets'
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def list_chats tree
|
60
|
+
puts "chats"
|
61
|
+
tree.chats.each_with_index do |chat, index|
|
62
|
+
index += 1
|
63
|
+
index = index.to_s.rjust(5)
|
64
|
+
puts "#{index} | #{chat.title}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def list_plots plots
|
69
|
+
puts "plots"
|
70
|
+
index = 1
|
71
|
+
plots.available.each_pair do |name, desc|
|
72
|
+
puts "#{index.to_s.rjust(5)} | #{name.ljust(15)} -> #{desc}"
|
73
|
+
index += 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# listing mode
|
78
|
+
if opts.list?
|
79
|
+
|
80
|
+
# chats
|
81
|
+
if $listing.include? :chats
|
82
|
+
begin
|
83
|
+
tree = Mess::Tree.new(target)
|
84
|
+
puts
|
85
|
+
list_chats tree
|
86
|
+
rescue Mess::TreeInvalidError
|
87
|
+
$stderr.puts "not an archive: #{target}"
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# plots
|
93
|
+
if $listing.include? :plots
|
94
|
+
puts
|
95
|
+
list_plots Mess::Plot
|
96
|
+
end
|
97
|
+
|
98
|
+
# none
|
99
|
+
if $listing.none?
|
100
|
+
$stderr.puts 'no specification flag for listing'
|
101
|
+
exit 1
|
102
|
+
end
|
103
|
+
|
104
|
+
puts
|
105
|
+
exit
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
begin
|
110
|
+
|
111
|
+
# given directly chat
|
112
|
+
chat = Mess::Chat.new(target)
|
113
|
+
|
114
|
+
rescue Mess::ChatInvalidError
|
115
|
+
|
116
|
+
# given tree
|
117
|
+
begin
|
118
|
+
tree = Mess::Tree.new(target, archived: opts.archived?)
|
119
|
+
rescue Mess::TreeInvalidError
|
120
|
+
$stderr.puts "not a chat/archive: #{target}"
|
121
|
+
exit 1
|
122
|
+
end
|
123
|
+
|
124
|
+
# select chat
|
125
|
+
input = opts[:chat].join
|
126
|
+
chat = nil
|
127
|
+
failed = false
|
128
|
+
loop do
|
129
|
+
|
130
|
+
# search for nick or index
|
131
|
+
chat = tree.chats.find { |c| c.title == input }
|
132
|
+
chat ||= tree.chats[input.to_i - 1] if input.to_i.to_s == input and (1..tree.chats.size).include? input.to_i
|
133
|
+
unless chat.nil?
|
134
|
+
puts if failed
|
135
|
+
break
|
136
|
+
end
|
137
|
+
|
138
|
+
# failed
|
139
|
+
puts
|
140
|
+
list_chats tree unless failed
|
141
|
+
failed = true
|
142
|
+
|
143
|
+
print 'select> '
|
144
|
+
input = $stdin.gets.chomp
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
# select plot
|
151
|
+
input = opts[:plot]
|
152
|
+
plot = nil
|
153
|
+
failed = false
|
154
|
+
loop do
|
155
|
+
|
156
|
+
# map indexes to names
|
157
|
+
input.map! do |p|
|
158
|
+
if p.to_i.to_s == p and (1..Mess::Plot.available.size).include? p.to_i
|
159
|
+
Mess::Plot.available.keys[p.to_i - 1]
|
160
|
+
else
|
161
|
+
p
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# validate names
|
166
|
+
if input.any? and input.all? { |p| Mess::Plot.available.include? p }
|
167
|
+
plot = Set.new(input)
|
168
|
+
puts if failed
|
169
|
+
break
|
170
|
+
end
|
171
|
+
|
172
|
+
# failed
|
173
|
+
puts
|
174
|
+
list_plots Mess::Plot unless failed
|
175
|
+
failed = true
|
176
|
+
print 'select> '
|
177
|
+
input = $stdin.gets.strip.split(/[, ]/).compact
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
# prepare
|
182
|
+
puts 'preparing...'
|
183
|
+
i = 1
|
184
|
+
plot.map! do |p|
|
185
|
+
$stdout.cursor_up 1
|
186
|
+
puts "preparing... #{i}/#{plot.size}"
|
187
|
+
Mess.const_get(p).new(chat)
|
188
|
+
end
|
189
|
+
$stdout.cursor_up 1
|
190
|
+
puts 'preparing... done'
|
191
|
+
|
192
|
+
# analyze
|
193
|
+
total = chat.count
|
194
|
+
pcapx = total / 100
|
195
|
+
check = 0
|
196
|
+
chat.msgs.each_with_index do |m, i|
|
197
|
+
if i >= check
|
198
|
+
$stdout.cursor_up 1
|
199
|
+
puts "analyzing... #{100 * i / total}% "
|
200
|
+
check += pcapx
|
201
|
+
end
|
202
|
+
plot.each do |p|
|
203
|
+
p.push m #rescue nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
$stdout.cursor_up 1
|
207
|
+
puts 'analyzing... done'
|
208
|
+
|
209
|
+
# review
|
210
|
+
puts
|
211
|
+
plot.each_with_index do |p, i|
|
212
|
+
$stdout.cursor_up 1
|
213
|
+
puts "reviewing... #{i + 1}/#{plot.size}"
|
214
|
+
p.review
|
215
|
+
end
|
216
|
+
$stdout.cursor_up 1
|
217
|
+
puts 'reviewing... done'
|
218
|
+
|
219
|
+
# export
|
220
|
+
$stdout.cursor_up 1
|
221
|
+
puts 'exporting...'
|
222
|
+
data = Hash.new
|
223
|
+
plot.each do |p|
|
224
|
+
data[p.class.name] = p.data
|
225
|
+
end
|
226
|
+
$stdout.cursor_up 1
|
227
|
+
|
228
|
+
File.open(opts[:output], 'w') do |f|
|
229
|
+
f.puts data.to_json
|
230
|
+
end
|
231
|
+
|
232
|
+
puts 'exporting... done'
|
data/lib/mess.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'json'
|
5
|
+
require 'date'
|
6
|
+
require_relative 'mess/version'
|
7
|
+
|
8
|
+
module Mess
|
9
|
+
class Error < StandardError; end
|
10
|
+
# Your code goes here...
|
11
|
+
end
|
12
|
+
|
13
|
+
require_relative 'mess/error'
|
14
|
+
require_relative 'mess/plot'
|
15
|
+
Dir["#{__dir__}/mess/**/*.rb"].each { |f| require_relative f }
|
data/lib/mess/chat.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mess
|
4
|
+
class Chat
|
5
|
+
|
6
|
+
attr_reader :path, :title, :usrs
|
7
|
+
|
8
|
+
def initialize path
|
9
|
+
@path = File.expand_path path
|
10
|
+
|
11
|
+
# make sure path exists
|
12
|
+
unless File.exists? @path
|
13
|
+
raise ChatInvalidError
|
14
|
+
end
|
15
|
+
|
16
|
+
# if file, scope out to the whole chat dir
|
17
|
+
if File.file? @path
|
18
|
+
@path = File.dirname @path
|
19
|
+
end
|
20
|
+
|
21
|
+
@title = nil
|
22
|
+
@usrs = nil
|
23
|
+
@msgs = Array.new
|
24
|
+
|
25
|
+
# exactly one message_1.ext file should exist
|
26
|
+
tmp = Dir["#@path/message_1\\.*"]
|
27
|
+
raise ChatInvalidError unless tmp.one?
|
28
|
+
tmp = tmp.first
|
29
|
+
|
30
|
+
# also store its extension
|
31
|
+
ext = File.extname tmp # ".json" or ".html"...
|
32
|
+
ext = ext[1..-1] # "json" or "html"...
|
33
|
+
|
34
|
+
# reference the get_ext function
|
35
|
+
get = method("get_#{ext}")
|
36
|
+
|
37
|
+
# chronologically iterate through message_X.ext chat files
|
38
|
+
# decrementally for some reason reee
|
39
|
+
total = Dir["#@path/message_*\\.#{ext}"].size
|
40
|
+
for i in (0...total)
|
41
|
+
file = "#@path/message_#{total - i}.#{ext}"
|
42
|
+
get.call(file) rescue raise ChatInvalidError
|
43
|
+
end
|
44
|
+
|
45
|
+
@title = '*you*' if @title.empty?
|
46
|
+
@ready = false
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def count
|
51
|
+
@msgs.size
|
52
|
+
end
|
53
|
+
|
54
|
+
def prepare
|
55
|
+
@msgs.sort! { |a, b| a['timestamp_ms'] <=> b['timestamp_ms'] }
|
56
|
+
end
|
57
|
+
|
58
|
+
def msgs
|
59
|
+
prepare unless @ready
|
60
|
+
@ready = true
|
61
|
+
@msgs
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def get_json file
|
67
|
+
buffer = JSON.parse(File.read(file))
|
68
|
+
@title ||= buffer['title']
|
69
|
+
@usrs ||= buffer['participants'].map{ |p| p['name'] }.sort
|
70
|
+
@msgs += buffer['messages']
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_html file
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/mess/error.rb
ADDED
data/lib/mess/plot.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Mess
|
2
|
+
class Plot
|
3
|
+
|
4
|
+
@@available = Hash.new
|
5
|
+
|
6
|
+
def self.available
|
7
|
+
@@available
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.describe desc
|
11
|
+
@@available[name] = desc
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.name
|
15
|
+
self.to_s.match(/Mess::(?<name>\w+)$/)[:name]
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :data
|
19
|
+
|
20
|
+
def initialize chat
|
21
|
+
end
|
22
|
+
|
23
|
+
def push msg
|
24
|
+
end
|
25
|
+
|
26
|
+
def review
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mess
|
4
|
+
class DailyMsgs < Plot
|
5
|
+
|
6
|
+
describe 'daily messages by user'
|
7
|
+
|
8
|
+
def initialize chat
|
9
|
+
@data = Hash.new
|
10
|
+
@usrs = chat.usrs
|
11
|
+
oldest = get_day(chat.msgs[0])
|
12
|
+
latest = get_day(chat.msgs[-1])
|
13
|
+
for day in (oldest..latest)
|
14
|
+
@data[day.to_s] = Array.new(@usrs.size, 0)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def push msg
|
19
|
+
usr = msg['sender_name']
|
20
|
+
day = get_day(msg).to_s
|
21
|
+
@data[day][@usrs.index(usr)] += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def get_day msg
|
27
|
+
unix = msg['timestamp_ms'] / 1000
|
28
|
+
Date.parse(Time.at(unix).to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mess
|
4
|
+
class TotalDelay < Plot
|
5
|
+
|
6
|
+
describe 'total delay by user'
|
7
|
+
|
8
|
+
def initialize chat
|
9
|
+
@last = nil
|
10
|
+
@time = Hash.new
|
11
|
+
@data = Hash.new
|
12
|
+
chat.usrs.each do |u|
|
13
|
+
@data[u] = 0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def push msg
|
18
|
+
|
19
|
+
usr = msg["sender_name"]
|
20
|
+
time = msg["timestamp_ms"]
|
21
|
+
|
22
|
+
if @time[usr].to_i < time
|
23
|
+
@time[usr] = time
|
24
|
+
end
|
25
|
+
|
26
|
+
if @last.nil? or @last == usr
|
27
|
+
@last ||= usr
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
@data[usr] += time - @time[@last]
|
32
|
+
@last = usr
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mess
|
4
|
+
class TotalLetters < Plot
|
5
|
+
|
6
|
+
describe 'total letters per user'
|
7
|
+
|
8
|
+
def initialize chat
|
9
|
+
@data = Hash.new
|
10
|
+
chat.usrs.each do |u|
|
11
|
+
@data[u] = 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def push msg
|
16
|
+
usr = msg['sender_name']
|
17
|
+
letters = msg['content'].to_s.length
|
18
|
+
@data[usr] += letters
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mess
|
4
|
+
class TotalMsgs < Plot
|
5
|
+
|
6
|
+
describe 'total messages by user'
|
7
|
+
|
8
|
+
def initialize chat
|
9
|
+
@data = Hash.new
|
10
|
+
chat.usrs.each do |u|
|
11
|
+
@data[u] = 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def push msg
|
16
|
+
usr = msg['sender_name']
|
17
|
+
@data[usr] += 1
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/mess/tree.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mess
|
4
|
+
class Tree
|
5
|
+
|
6
|
+
attr_reader :root, :chats
|
7
|
+
|
8
|
+
def initialize path, flags = Hash.new
|
9
|
+
|
10
|
+
# find the root from given path
|
11
|
+
|
12
|
+
path = File.expand_path(path)
|
13
|
+
begin
|
14
|
+
|
15
|
+
unless File.exists? path
|
16
|
+
break
|
17
|
+
end
|
18
|
+
|
19
|
+
if check(path)
|
20
|
+
@root = path
|
21
|
+
break
|
22
|
+
else
|
23
|
+
next
|
24
|
+
end
|
25
|
+
|
26
|
+
end until (path = File.dirname(path)) == "/"
|
27
|
+
|
28
|
+
raise TreeInvalidError if @root.nil?
|
29
|
+
|
30
|
+
import flags[:archived]
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def check root
|
35
|
+
[
|
36
|
+
'/',
|
37
|
+
'/inbox',
|
38
|
+
'/archived_threads',
|
39
|
+
].all? { |dir| File.directory?(root + dir) }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def import archived
|
45
|
+
|
46
|
+
@chats = Array.new
|
47
|
+
|
48
|
+
box = Array.new
|
49
|
+
box << 'inbox'
|
50
|
+
box << 'archived_threads' if archived
|
51
|
+
|
52
|
+
# find all text chat dirs
|
53
|
+
box.each do |b|
|
54
|
+
Dir["#@root/#{b}/*"].each do |dir|
|
55
|
+
|
56
|
+
# exclude asset dirs (capitalized)
|
57
|
+
base = File.basename dir
|
58
|
+
next unless base.downcase == base
|
59
|
+
|
60
|
+
# list it
|
61
|
+
begin
|
62
|
+
@chats << Chat.new(dir)
|
63
|
+
rescue
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@chats.sort! { |a, b| a.title <=> b.title }
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def select i
|
74
|
+
Chat.new(@index[i.to_i].to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/mess/version.rb
ADDED
data/mess.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'lib/mess/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "mess"
|
5
|
+
spec.version = Mess::VERSION
|
6
|
+
spec.authors = ["patztablook22"]
|
7
|
+
spec.email = ["patz@tuta.io"]
|
8
|
+
|
9
|
+
spec.summary = "Facebook Messenger Statistician"
|
10
|
+
spec.description = "Analyze your archived Messenger chatlogs in JSON format!"
|
11
|
+
spec.homepage = "https://github.com/patztablook22/mess"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
|
15
|
+
# Specify which files should be added to the gem when it is released.
|
16
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
17
|
+
spec.files = Dir.chdir(__dir__) do
|
18
|
+
Dir["**/*"].reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "slop", "~> 4.8"
|
25
|
+
end
|
data/pkg/mess-0.0.0.gem
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mess
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- patztablook22
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-10-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: slop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.8'
|
27
|
+
description: Analyze your archived Messenger chatlogs in JSON format!
|
28
|
+
email:
|
29
|
+
- patz@tuta.io
|
30
|
+
executables:
|
31
|
+
- mess
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- Gemfile
|
36
|
+
- Gemfile.lock
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- bin/console
|
41
|
+
- bin/setup
|
42
|
+
- exe/mess
|
43
|
+
- lib/mess.rb
|
44
|
+
- lib/mess/chat.rb
|
45
|
+
- lib/mess/error.rb
|
46
|
+
- lib/mess/plot.rb
|
47
|
+
- lib/mess/plot/daily_msgs.rb
|
48
|
+
- lib/mess/plot/total_delay.rb
|
49
|
+
- lib/mess/plot/total_lettres.rb
|
50
|
+
- lib/mess/plot/total_msgs.rb
|
51
|
+
- lib/mess/tree.rb
|
52
|
+
- lib/mess/version.rb
|
53
|
+
- mess.gemspec
|
54
|
+
- pkg/mess-0.0.0.gem
|
55
|
+
homepage: https://github.com/patztablook22/mess
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 2.3.0
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.1.4
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Facebook Messenger Statistician
|
78
|
+
test_files: []
|