mess 0.0.0 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70d63987314f20d83db49e145f32fb23e5bb1b20608d343672c474f3aea128e4
4
- data.tar.gz: 3883e201fb6e57978cd55215a9b4951e8c0133d441bd4a9514f4e2cb3f7410df
3
+ metadata.gz: d71090b50fd3a172418162b58191baa839adedbbc51b5608f63aaf8f162a95a4
4
+ data.tar.gz: b6b8c3df531c2cd6656c69039448229ce065d8a8c1c90ca0cfaf7601eb51cfe1
5
5
  SHA512:
6
- metadata.gz: 98019de61d628b344ab85c05d81e5db2aecbb63cc101e8164cbec8395f871d31b1ac5fa4d56ad859d662d31698747a51f874c6e38a1f580b85198ce537ed90be
7
- data.tar.gz: '09922eb1f81a2030e9a1fdfc1e8224b3886ad0048281048b4deb62a078d604df59e456eb5f6cb45f9212aed782e5b1275b449bb658f1c3640adc01c29a1f4a81'
6
+ metadata.gz: c3ad3c5ae653e7fddfd09957476d7b5a3220ab252e4b0ff79c49116228c338a75d86e96b44ab15ba1ff66e17dc7857b274e9ceb9e7aa3d01c971b03b2b5f8f1e
7
+ data.tar.gz: e6b5e2e7056217c635744c15e9497dbc2d465e14b175403f6a549c308d77c1275bfcf6980053964836f0887036f27453a5cbe14e624757e3bc839cff3faf5043
data/Gemfile.lock CHANGED
@@ -1,14 +1,28 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mess (0.0.0)
5
- slop (~> 4.8)
4
+ mess (0.0.1)
5
+ tty-option (~> 0.1)
6
+ tty-prompt (~> 0.23)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ pastel (0.8.0)
12
+ tty-color (~> 0.5)
10
13
  rake (12.3.3)
11
- slop (4.8.2)
14
+ tty-color (0.6.0)
15
+ tty-cursor (0.7.1)
16
+ tty-option (0.1.0)
17
+ tty-prompt (0.23.0)
18
+ pastel (~> 0.8)
19
+ tty-reader (~> 0.8)
20
+ tty-reader (0.9.0)
21
+ tty-cursor (~> 0.7)
22
+ tty-screen (~> 0.8)
23
+ wisper (~> 2.0)
24
+ tty-screen (0.8.1)
25
+ wisper (2.0.1)
12
26
 
13
27
  PLATFORMS
14
28
  ruby
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mess
2
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!
3
+ Ruby tool for analysis of Facebook Messenger conversations in JSON format. Output is table-processor-friendly (like MS Excell / LibreOffice Calc). Independently of `mess` executable, an API is provided for more nuanced use.
4
4
 
5
5
  ## Installation
6
6
  Ruby is required. You can download mess as a Ruby Gem:
@@ -22,14 +22,11 @@ Call `main` either directly or via link.
22
22
  # print help
23
23
  main --help
24
24
 
25
- # guided analysis of given archive
26
- main --facebook my/facebook/archive
25
+ # interactive prompt for given archive
26
+ main my/facebook/information
27
27
 
28
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
29
+ main my/facebook/information/chat --plot DailyMsgs
33
30
 
34
31
  ```
35
32
 
@@ -38,9 +35,9 @@ Mess provides class `Mess::Plot` to be inherited for custom plots; \
38
35
  quick example (for more, see `mess/plots` dir):
39
36
  ```ruby
40
37
  module Mess
41
- class MyPlot < Plot
38
+ class MyPlotter < Plotter
42
39
 
43
- # this will make MyPlot "available"
40
+ # this will make MyPlotter "available"
44
41
  describe "my plot for magic analysis"
45
42
 
46
43
  def initialize chat # constructor
@@ -49,16 +46,23 @@ module Mess
49
46
  end
50
47
 
51
48
  def push msg # being called for every single message chronologically
52
- if msg["sender_name"].include? "my"
49
+ if msg.body.include? "my"
53
50
  # do stuff
54
51
  else
55
52
  # don't etc.
56
53
  end
57
54
  end
58
55
 
59
- def review # called after all messages have been push-ed
56
+ def finalize # called after all messages have been push-ed
60
57
  # body # perhaps reformat the data and so on
61
58
  end
59
+
60
+ def generate_plot # output a unified-format plot for further manipulation
61
+ p = Plot.new
62
+ p.data = @data
63
+ p.head = @myHead
64
+ return p
65
+ end
62
66
 
63
67
  end
64
68
  end
data/exe/mess CHANGED
@@ -1,232 +1,125 @@
1
- #! /usr/bin/env ruby
1
+ #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'io/console'
5
- require 'slop'
4
+ require 'tty-option'
5
+ require 'tty-prompt'
6
6
  require_relative '../lib/mess'
7
7
 
8
- Signal.trap('INT') { exit }
8
+ class Command
9
+ include TTY::Option
9
10
 
10
- $listing = Array.new
11
+ usage do
12
+ program 'mess'
13
+ command "\b"
14
+ desc 'analyze conversation'
11
15
 
12
- begin
13
- opts = Slop.parse do |o|
16
+ end
14
17
 
15
- o.banner = "usage: #{$0} [options] target"
18
+ argument :path do
19
+ required
20
+ desc 'inbox or chat directory path'
21
+ end
16
22
 
17
- o.on '--help', 'print help' do
18
- puts o
19
- exit
20
- end
23
+ flag :help do
24
+ long '--help'
25
+ desc 'Print usage'
26
+ end
21
27
 
22
- o.on '--version', 'print version' do
23
- puts Mess::VERSION
24
- exit
25
- end
28
+ flag :archived do
29
+ short '-a'
30
+ long '--archived'
31
+ desc 'list archived conversations too'
32
+ end
26
33
 
27
- o.string '--output', '-o', 'output file', default: 'mess_out.txt'
34
+ flag :version do
35
+ long '--version'
36
+ desc 'print version'
37
+ end
28
38
 
29
- o.bool '--archived', '-a', 'archived threads too', default: false
39
+ option :plot do
40
+ short '-p'
41
+ long '--plot name'
42
+ desc 'plot to be generated'
43
+ end
30
44
 
31
- o.bool '--list', '-l', 'listing mode'
45
+ option :output do
46
+ short '-o'
47
+ long '--output path'
48
+ desc 'output data path'
49
+ end
32
50
 
33
- o.array '--chat', '-c', 'chat specification' do
34
- $listing << :chats
51
+ def run
52
+ if params[:help]
53
+ print help
54
+ exit
35
55
  end
36
56
 
37
- o.array '--plot', '-p', 'plot specification' do
38
- $listing << :plots
57
+ if params[:version]
58
+ puts Mess::VERSION
59
+ exit
39
60
  end
40
61
 
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}"
62
+ if params[:path].nil?
63
+ print help
88
64
  exit 1
89
65
  end
90
66
  end
67
+ end
91
68
 
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
69
+ cmd = Command.new
70
+ cmd.parse.run
103
71
 
104
- puts
105
- exit
72
+ prompt = TTY::Prompt.new
106
73
 
107
- end
74
+ todo = nil
108
75
 
109
76
  begin
110
-
111
- # given directly chat
112
- chat = Mess::Chat.new(target)
113
-
77
+ todo = Mess::Chat.new(cmd.params[:path])
114
78
  rescue Mess::ChatInvalidError
115
-
116
- # given tree
117
79
  begin
118
- tree = Mess::Tree.new(target, archived: opts.archived?)
119
- rescue Mess::TreeInvalidError
120
- $stderr.puts "not a chat/archive: #{target}"
80
+ todo = Mess::FacebookInformation.new(cmd.params[:path], cmd.params[:archived])
81
+ rescue Mess::FacebookInformationInvalidError
82
+ puts 'path is not a chat nor an inbox'
121
83
  exit 1
122
84
  end
85
+ end
123
86
 
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
87
+ begin
145
88
 
89
+ if todo.class == Mess::FacebookInformation
90
+ todo = prompt.select(
91
+ 'Select chat:',
92
+ todo.chats.map { |c| [c.title, c] }.to_h
93
+ )
94
+ #todo = todo.chats.find { |c| c.title == temp }
146
95
  end
147
96
 
148
- end
97
+ plot = cmd.params[:plot]
149
98
 
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
99
+ if Mess::Plotter.available.keys.include? plot
100
+ plot = Mess.const_get(plot)
101
+ else
102
+ plot = prompt.select(
103
+ 'Select plot:',
104
+ Mess::Plotter.available.keys.map { |p| [p, Mess.const_get(p)] }.to_h
105
+ )
163
106
  end
164
107
 
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
108
+ cmd.params[:output] ||= prompt.ask('Output path:', default: "./mess-out-#{Time.now.to_i}.txt")
171
109
 
172
- # failed
110
+ rescue TTY::Reader::InputInterrupt
173
111
  puts
174
- list_plots Mess::Plot unless failed
175
- failed = true
176
- print 'select> '
177
- input = $stdin.gets.strip.split(/[, ]/).compact
178
-
112
+ exit 1
179
113
  end
180
114
 
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
115
+ p = plot.new(todo)
116
+ r = p.run
227
117
 
228
- File.open(opts[:output], 'w') do |f|
229
- f.puts data.to_json
118
+ File.open(cmd.params[:output], 'w') do |f|
119
+ ([r.head] + r.data).each do |row|
120
+ row.each do |col|
121
+ f << col << "\t"
122
+ end
123
+ f << "\n"
124
+ end
230
125
  end
231
-
232
- puts 'exporting... done'
data/lib/mess.rb CHANGED
@@ -1,15 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
3
  require 'json'
4
+ require 'csv'
5
5
  require 'date'
6
- require_relative 'mess/version'
6
+ require 'iconv'
7
7
 
8
8
  module Mess
9
9
  class Error < StandardError; end
10
10
  # Your code goes here...
11
11
  end
12
12
 
13
+ require_relative 'mess/version'
13
14
  require_relative 'mess/error'
15
+ require_relative 'mess/msg'
16
+ require_relative 'mess/chat'
14
17
  require_relative 'mess/plot'
15
- Dir["#{__dir__}/mess/**/*.rb"].each { |f| require_relative f }
18
+ require_relative 'mess/plotter'
19
+ require_relative 'mess/facebook_information'
20
+
21
+ Dir["#{__dir__}/mess/plotter/*.rb"].each { |f| require_relative f }
data/lib/mess/chat.rb CHANGED
@@ -1,77 +1,86 @@
1
- # frozen_string_literal: true
1
+ # frozen string_literal: true
2
+
3
+ # Mess::Chat
4
+ # class wrapping a single Messenger conversation (Group/Person)
5
+ # initializer takes single conversation directory path as an argument
6
+ # (contains text files, attachmed photos and other files etc.)
7
+ # chat.title -> get conversation tytle
8
+ # chat.size -> count messages
9
+ # chat.usrs -> participant list
10
+ # chat.msgs -> all messages
2
11
 
3
12
  module Mess
4
13
  class Chat
5
-
6
- attr_reader :path, :title, :usrs
7
-
14
+ attr_reader :path, :title, :usrs, :msgs
15
+
8
16
  def initialize path
9
- @path = File.expand_path path
17
+ @path = File.expand_path(path)
10
18
 
11
- # make sure path exists
12
- unless File.exists? @path
13
- raise ChatInvalidError
14
- end
19
+ # make sure the path is somewhat valid
20
+ raise ChatInvalidError unless File.exists? @path
15
21
 
16
- # if file, scope out to the whole chat dir
17
- if File.file? @path
18
- @path = File.dirname @path
19
- end
22
+ # maybe given a file within the directory instead? -> scope out
23
+ @path = File.dirname(@path) if File.file? @path
20
24
 
25
+ # prepare data buffers
21
26
  @title = nil
22
27
  @usrs = nil
23
28
  @msgs = Array.new
24
29
 
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
30
+ # either .json or .html -> message_1.ext to find out the format
31
+ message_1 = Dir["#@path/message_1\\.*"]
32
+ raise ChatInvalidError unless message_1.one?
33
+ message_1 = message_1.first
34
+ extension = File.extname(message_1)
35
+ extension = extension[1..-1] # remove the dot in .ext
36
+
37
+ # reference the get_extension function
38
+ get = method("get_#{extension}")
39
+
40
+ # iterate through message_X.ext and use get method to parse the files
41
+ # do it chronologically -> descending X for messenger (reeee)
42
+ total = Dir["#@path/message_*\\.#{extension}"].size
43
+ total.downto(1) do |i|
44
+ file = "#@path/message_#{i}.#{extension}"
45
+ get.call(file) #rescue raise ChatInvalidError
43
46
  end
44
47
 
45
- @title = '*you*' if @title.empty?
46
- @ready = false
47
-
48
+ @title = '*you*' if @title.nil? or @title.empty?
49
+ @msgs.sort_by!(&:time)
50
+ @usrs.sort!
48
51
  end
49
52
 
50
- def count
53
+ def size
51
54
  @msgs.size
52
55
  end
53
56
 
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
57
  private
65
58
 
59
+ # parse json file
66
60
  def get_json file
67
- buffer = JSON.parse(File.read(file))
61
+ # get data
62
+ buffer = File.read(file)
63
+ buffer = JSON.parse(buffer)
64
+
65
+ # get metadata if not already done before (each file contains it)
68
66
  @title ||= buffer['title']
69
67
  @usrs ||= buffer['participants'].map{ |p| p['name'] }.sort
70
- @msgs += buffer['messages']
68
+
69
+ # append messages
70
+ buffer['messages'].each do |hash|
71
+ msg = Msg.new
72
+ msg.from = hash['sender_name']
73
+ msg.body = hash['content']
74
+ msg.time = hash['timestamp_ms']
75
+ msg.type = hash['type']
76
+ @msgs << msg
77
+ @usrs << msg.from unless @usrs.include? msg.from
78
+ end
71
79
  end
72
80
 
81
+ # parse html file
73
82
  def get_html file
83
+ raise ChatInvalidError
74
84
  end
75
-
76
85
  end
77
86
  end
data/lib/mess/error.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Mess
2
4
 
3
5
  class Error < StandardError
@@ -6,7 +8,7 @@ module Mess
6
8
  class ChatInvalidError < Error
7
9
  end
8
10
 
9
- class TreeInvalidError < Error
11
+ class FacebookInformationInvalidError < Error
10
12
  end
11
13
 
12
14
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mess
4
+ class FacebookInformation
5
+ attr_reader :root, :chats
6
+
7
+ def initialize path, archived_too = false
8
+ path = File.expand_path(path)
9
+
10
+ # find the closest thing to facebook information copy root
11
+ # while gradually ascending
12
+ begin
13
+ break unless File.exists? path
14
+ next unless is_root? path
15
+ @root = path
16
+ end until (path = File.dirname(path)) == '/'
17
+
18
+ raise FacebookInformationInvalidError if @root.nil?
19
+
20
+ @chats = Array.new
21
+ to_list = Array.new
22
+ to_list << "inbox"
23
+ to_list << "archived_threads" if archived_too
24
+ to_list.each do |i|
25
+ list_chats "#@root/#{i}"
26
+ end
27
+ @chats.sort_by!(&:title)
28
+ end
29
+
30
+ private
31
+
32
+ # checks if given path is a root of a facebook information copy
33
+ def is_root? path
34
+ [
35
+ '/',
36
+ '/inbox',
37
+ '/archived_threads',
38
+ ].all? { |dir| File.directory?(path + dir) }
39
+ end
40
+
41
+ def list_chats inbox_dir
42
+ Dir["#{inbox_dir}/*"].each do |chat_dir|
43
+ base = File.basename(chat_dir)
44
+ @chats << Chat.new(chat_dir) rescue nil
45
+ end
46
+ end
47
+
48
+ end
49
+ end
data/lib/mess/msg.rb ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # structure holding message data
4
+
5
+ module Mess
6
+ class Msg
7
+ attr_accessor :from, :body, :time, :type
8
+ end
9
+ end
data/lib/mess/plot.rb CHANGED
@@ -1,29 +1,17 @@
1
1
  module Mess
2
2
  class Plot
3
+ attr_accessor :head, :data
3
4
 
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
5
+ def initialize
6
+ @head = Array.new
7
+ @data = Array.new
24
8
  end
25
9
 
26
- def review
10
+ def export_data
11
+ puts @head.join("\t");
12
+ @data.each do |d|
13
+ puts d.join("\t");
14
+ end
27
15
  end
28
16
 
29
17
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # base class for plotters
4
+ # initializer should take in Chat class to be analyzed
5
+ #
6
+ # method push is feeded all messages chronologically
7
+ # after pushing all messages, finalize is called to format data etc.
8
+
9
+ module Mess
10
+ class Plotter
11
+
12
+ # stores all available plotters
13
+ @@available = Hash.new
14
+
15
+ # lists all available plotters
16
+ def self.available
17
+ @@available
18
+ end
19
+
20
+ attr_reader :data
21
+
22
+ # needs to be described in order to be "centrally" available
23
+ # adds plotter class to available plotters with custom description
24
+ # name is derived from plotter class name
25
+ def self.describe desc
26
+ name = self.to_s.match(/Mess::(?<name>\w+)$/)[:name]
27
+ @@available[name] = desc
28
+ end
29
+
30
+ def initialize chat
31
+ @chat = chat
32
+ end
33
+
34
+ # should return Mess::Plot
35
+ def run
36
+ @chat.msgs.each { |msg| push(msg) }
37
+ finalize
38
+ generate_plot
39
+ end
40
+
41
+ private
42
+
43
+ # called for every message in chronological order
44
+ def push msg
45
+ end
46
+
47
+ def finalize
48
+ end
49
+
50
+ def generate_plot
51
+ Plot.new
52
+ end
53
+
54
+ end
55
+ end
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mess
4
- class DailyMsgs < Plot
4
+ class DailyMsgs < Plotter
5
5
 
6
6
  describe 'daily messages by user'
7
7
 
8
8
  def initialize chat
9
+ super
9
10
  @data = Hash.new
10
- @usrs = chat.usrs
11
+ @usrs = @chat.usrs
11
12
  oldest = get_day(chat.msgs[0])
12
13
  latest = get_day(chat.msgs[-1])
13
14
  for day in (oldest..latest)
@@ -15,16 +16,26 @@ module Mess
15
16
  end
16
17
  end
17
18
 
19
+ private
20
+
21
+ def generate_plot
22
+ p = Plot.new
23
+ p.head << 'Date'
24
+ p.head += @usrs
25
+ @data.each do |date, msgs|
26
+ p.data << [date] + msgs
27
+ end
28
+ p
29
+ end
30
+
18
31
  def push msg
19
- usr = msg['sender_name']
32
+ usr = msg.from
20
33
  day = get_day(msg).to_s
21
34
  @data[day][@usrs.index(usr)] += 1
22
35
  end
23
36
 
24
- private
25
-
26
37
  def get_day msg
27
- unix = msg['timestamp_ms'] / 1000
38
+ unix = msg.time / 1000
28
39
  Date.parse(Time.at(unix).to_s)
29
40
  end
30
41
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mess
4
+ class TotalMsgs < Plotter
5
+
6
+ describe 'total messages by user'
7
+
8
+ def initialize chat
9
+ super
10
+ @data = Hash.new
11
+ chat.usrs.each do |u|
12
+ @data[u] = 0
13
+ puts u
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def push msg
20
+ unless @data.include? msg.from
21
+ puts msg.from
22
+ end
23
+ @data[msg.from] += 1
24
+ end
25
+
26
+ def generate_plot
27
+ p = Plot.new
28
+ p.head = ['User', 'Total Messages']
29
+ p.data = @data.to_a
30
+ p
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mess
4
+ class WordFrequency < Plotter
5
+
6
+ describe 'most frequent words by user'
7
+
8
+ def initialize chat
9
+ super
10
+ @data = Hash.new
11
+ chat.usrs.each do |u|
12
+ @data[u] = Hash.new
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def generate_plot
19
+ p = Plot.new
20
+ p.head << 'Index'
21
+ @data.each_key do |usr|
22
+ p.head += ["Word - #{usr}", "Frequency - #{usr}"]
23
+ end
24
+ i = 0
25
+ loop do
26
+ line = [i + 1]
27
+ @data.each_value do |data|
28
+ buff = Array.new(2)
29
+ word = data.to_a[i]
30
+ buff = word unless word.nil?
31
+ line += buff
32
+ end
33
+ break if line.compact.size == 1
34
+ p.data << line
35
+ i += 1
36
+ end
37
+ p
38
+ end
39
+
40
+ def push msg
41
+ return if msg.body.nil?
42
+ words = msg.body.split(/[ \t\n]/)
43
+ words.each do |w|
44
+ w.strip!
45
+ next if w.empty?
46
+ w.downcase!
47
+ @data[msg.from][w] = @data[msg.from][w].to_i + 1
48
+ end
49
+ end
50
+
51
+ def finalize
52
+ @data.each_key do |usr|
53
+ @data[usr] = @data[usr].sort_by { |key, value| -value }
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mess
4
+ class WordsPerMessage < Plotter
5
+
6
+ describe 'average words per message by user'
7
+
8
+ def initialize chat
9
+ super
10
+ @data = Hash.new
11
+ @msgs = Hash.new
12
+ @words = Hash.new
13
+ chat.usrs.each do |u|
14
+ @data[u] = nil
15
+ @msgs[u] = 0
16
+ @words[u] = 0
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def generate_plot
23
+ p = Plot.new
24
+ p.head = ['User', 'Words per Message']
25
+ p.data = @data.to_a
26
+ p
27
+ end
28
+
29
+ def push msg
30
+ @msgs[msg.from] += 1
31
+ return if msg.body.class != String
32
+ @words[msg.from] += msg.body.strip.squeeze.count("\n\t ")
33
+ end
34
+
35
+ def finalize
36
+ @data.each_key do |u|
37
+ @data[u] = @words[u].to_f / @msgs[u]
38
+ end
39
+ end
40
+
41
+ end
42
+ end
data/lib/mess/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mess
4
- VERSION = '0.0.0'
4
+ VERSION = "0.0.2"
5
5
  end
data/mess.gemspec CHANGED
@@ -21,5 +21,6 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.add_dependency "slop", "~> 4.8"
24
+ spec.add_dependency "tty-prompt", "~> 0.23"
25
+ spec.add_dependency "tty-option", "~> 0.1"
25
26
  end
Binary file
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mess
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - patztablook22
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-26 00:00:00.000000000 Z
11
+ date: 2021-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: slop
14
+ name: tty-prompt
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.8'
19
+ version: '0.23'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.8'
26
+ version: '0.23'
27
+ - !ruby/object:Gem::Dependency
28
+ name: tty-option
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.1'
27
41
  description: Analyze your archived Messenger chatlogs in JSON format!
28
42
  email:
29
43
  - patz@tuta.io
@@ -43,15 +57,17 @@ files:
43
57
  - lib/mess.rb
44
58
  - lib/mess/chat.rb
45
59
  - lib/mess/error.rb
60
+ - lib/mess/facebook_information.rb
61
+ - lib/mess/msg.rb
46
62
  - 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
63
+ - lib/mess/plotter.rb
64
+ - lib/mess/plotter/daily_msgs.rb
65
+ - lib/mess/plotter/total_msgs.rb
66
+ - lib/mess/plotter/word_frequency.rb
67
+ - lib/mess/plotter/words_per_msg.rb
52
68
  - lib/mess/version.rb
53
69
  - mess.gemspec
54
- - pkg/mess-0.0.0.gem
70
+ - pkg/mess-0.0.1.gem
55
71
  homepage: https://github.com/patztablook22/mess
56
72
  licenses:
57
73
  - MIT
@@ -1,37 +0,0 @@
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
@@ -1,22 +0,0 @@
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
@@ -1,21 +0,0 @@
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 DELETED
@@ -1,78 +0,0 @@
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/pkg/mess-0.0.0.gem DELETED
Binary file