TokiCLI 0.0.9 → 0.1.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.
@@ -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