mess 0.0.0
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/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: []
|