TokiCLI 0.0.9 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,224 +0,0 @@
1
- # encoding: utf-8
2
- module TokiCLI
3
- class Toki
4
-
5
- CLIENT_ID = 'm6AccJFM56ENCn58Vde9cSg3uSpbvAAs'
6
- CALLBACK_URL = 'http://aya.io/toki_cli/auth.html'
7
-
8
- attr_reader :bundles
9
-
10
- def initialize(token, channel_id)
11
- @token = token
12
- @channel_id = channel_id
13
- if File.exist? "#{Dir.home}/.TokiCLI/apps.json"
14
- @bundles = JSON.parse(File.read("#{Dir.home}/.TokiCLI/apps.json"))
15
- end
16
- end
17
-
18
- def get_bundle_ids
19
- puts "Analyzing apps in /Applications\n\n"
20
- require 'CFPropertyList'
21
- files = get_plists "/Applications/*/Contents/*"
22
- utils = get_plists "/Applications/Utilities/*/Contents/*"
23
- home = get_plists "#{Dir.home}/Applications/*/Contents/*"
24
- @infos = {}
25
- get_bundles files
26
- get_bundles utils
27
- get_bundles home
28
- @infos
29
- end
30
-
31
-
32
-
33
- def get_content(options)
34
- if options[:adn]
35
- get_adn_data.reverse.map {|obj| make_obj_from_adn(obj)}
36
- else
37
- get_db_data.map {|obj| make_obj_from_db(obj)}
38
- end
39
- end
40
-
41
- def get_app(app, synced)
42
- name_regex = /#{app.downcase}/
43
- app_data = {}
44
- totals = {}
45
- totals[:app] = 0
46
- found = synced.select {|obj| obj.app[:name].downcase =~ name_regex}
47
- found.each do |obj|
48
- totals[:app] += obj.app[:total]
49
- app_data[obj.msg[:id]] = {
50
- from: Time.at(obj.app[:active_from]).strftime("%Y/%m/%d %Hh:%Mm:%Ss"),
51
- to: Time.at(obj.app[:active_to]).strftime("%Y/%m/%d %Hh:%Mm:%Ss"),
52
- duration: humanized_date(obj.app[:total]),
53
- name: obj.app[:name],
54
- raw_from: obj.app[:active_from]
55
- }
56
- end
57
- app_data['total'] = humanized_date(totals[:app])
58
- app_data
59
- end
60
-
61
- def all_data(synced)
62
- sorted = get_sorted_totals(synced)
63
- sorted.inject({}) do |bundles, obj|
64
- bundles.merge(obj[0] => humanized_date(obj[1])) #name, total
65
- end
66
- end
67
-
68
- def top(synced, number = 5)
69
- number = -number
70
- sorted = get_sorted_totals(synced)
71
- humanized = sorted.map {|n,t| [n, humanized_date(t)]}
72
- humanized[number..-1]
73
- end
74
-
75
- def get_day(date, entries, options)
76
- min_epoch = date.to_time.to_i
77
- max_epoch = date.next_day.to_time.to_i
78
- get_between(min_epoch, max_epoch, entries)
79
- end
80
-
81
- def get_range(day1, day2, entries, options)
82
- min_epoch = day1.to_time.to_i
83
- max_epoch = day2.next_day.to_time.to_i # between start of 1 and end of 2
84
- get_between(min_epoch, max_epoch, entries)
85
- end
86
-
87
- private
88
-
89
- def get_between(epoch1, epoch2, entries)
90
- range = entries.select {|obj| obj.app[:active_from] > epoch1 && obj.app[:active_from] < epoch2}
91
- day_totals = sort_totals get_totals(range)
92
- day_totals.map {|n,t| [n, humanized_date(t)]}
93
- end
94
-
95
- def get_sorted_totals(synced)
96
- sort_totals(get_totals(synced))
97
- end
98
-
99
- def humanized_date(epoch)
100
- human = epoch_to_human(epoch)
101
- "#{human[:hours]}h #{human[:minutes]}m #{human[:seconds]}s"
102
- end
103
-
104
- def sort_totals(totals)
105
- totals.sort_by { |k,v| v }
106
- end
107
-
108
- def get_totals(synced)
109
- synced.inject({}) do |result, obj|
110
- result.merge(obj.app[:name] => app_total(obj, result))
111
- end
112
- end
113
-
114
- def app_total(obj, totals)
115
- totals[obj.app[:name]].nil? ? obj.app[:total] : totals[obj.app[:name]] + obj.app[:total]
116
- end
117
-
118
- def epoch_to_human(epoch)
119
- hours = epoch / 3600
120
- minutes = (epoch / 60 - hours * 60)
121
- seconds = (epoch - (minutes * 60 + hours * 3600))
122
- {hours: hours, minutes: minutes, seconds: seconds}
123
- end
124
-
125
- def get_adn_data
126
- channels = ADNChannels::GetMessages.new(@token)
127
- channels.get_messages(@channel_id)
128
- end
129
-
130
- def get_db_data
131
- db = open_db
132
- data = db.execute("SELECT * FROM KKAppActivity")
133
- db.close
134
- data
135
- end
136
-
137
- def open_db
138
- toki_path = "#{Dir.home}/.TokiCLI"
139
- db_path = "#{Dir.home}/Library/Containers/us.kkob.Toki/Data/Documents/toki_data.sqlite3"
140
- abort(Status.no_db) unless File.exist? db_path
141
- FileUtils.mkdir_p(toki_path) unless Dir.exist?(toki_path)
142
- FileUtils.copy(db_path, "#{toki_path}/toki_data.sqlite3.bak")
143
- Amalgalite::Database.new(db_path)
144
- end
145
-
146
- def make_obj_from_adn(obj)
147
- msg = {}
148
- usr = {}
149
- app = {}
150
- d = obj['annotations'][0]['value']['d']
151
- msg[:id] = obj['id']
152
- msg[:thread] = obj['thread_id']
153
- msg[:date] = parsed_date obj['created_at']
154
- usr[:username] = obj['user']['username']
155
- usr[:name] = obj['user']['name']
156
- usr[:id] = obj['user']['id']
157
- app[:id] = d['id']
158
- app[:uuid] = d['UUID']
159
- app[:name] = make_app_name [nil, d['bundleIdentifier']]
160
- app[:active_to] = d['activeTo']
161
- app[:active_from] = d['activeFrom']
162
- app[:total] = d['totalSeconds']
163
- OpenStruct.new(app: app, msg: msg, usr: usr)
164
- end
165
-
166
- def make_obj_from_db(obj)
167
- # content is an array:
168
- # id (INTEGER) 0, bundleIdentifier (VARCHAR) 1, activeFrom (INTEGER) 2, activeTo (INTEGER) 3, totalSeconds (INTEGER) 4, UUID (VARCHAR) 5, synced (INTEGER) 6, availableToSync (INTEGER) 7
169
- msg = {}
170
- usr = {}
171
- app = {}
172
- msg[:id] = obj[0]
173
- msg[:thread] = msg[:id]
174
- msg[:date] = Time.now
175
- usr[:username] = ENV['USERNAME']
176
- usr[:name] = usr[:username]
177
- usr[:id] = obj[5]
178
- app[:id] = msg[:id]
179
- app[:uuid] = obj[5]
180
- app[:name] = make_app_name obj
181
- app[:active_to] = obj[3]
182
- app[:active_from] = obj[2]
183
- app[:total] = obj[4]
184
- OpenStruct.new(app: app, msg: msg, usr: usr)
185
- end
186
-
187
- def make_app_name obj
188
- if @bundles[obj[1]]
189
- @bundles[obj[1]]
190
- else
191
- obj[1]
192
- end
193
- end
194
-
195
- def parsed_date(string)
196
- "#{string[0...10]} #{string[11...19]}"
197
- end
198
-
199
- def get_plists path
200
- Dir.glob(path).select {|f| (File.split f).last == 'Info.plist'}
201
- end
202
-
203
- def get_bundles plists
204
- plists.each do |obj|
205
- puts "Analyzing #{obj} ...\n"
206
- begin
207
- pl = CFPropertyList::List.new(:file => obj)
208
- rescue CFFormatError
209
- puts "Unable to read the file, skipping...\n"
210
- next
211
- end
212
- data = CFPropertyList.native_types(pl.value)
213
- name = data['CFBundleName']
214
- bundle_id = data['CFBundleIdentifier']
215
- if name.nil?
216
- name = data['CFBundleExecutable']
217
- end
218
- next if name.nil?
219
- next if bundle_id.nil? || bundle_id.empty?
220
- @infos[bundle_id] = name
221
- end
222
- end
223
- end
224
- end