chronicle-imessage 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9aa3bc95443845a57aca895eb7dce6486c89f8458ec2f061dbe84a784ab89dd5
4
- data.tar.gz: a909ca881d4bbbbdd0bfd227bcc4d96f3d84ad5ec63182424e991c3c5e0e5ae7
3
+ metadata.gz: 03a69e13d41a855786b08bea6c849d1b9d8e8d599c7891fe79c8d68dd133bd0d
4
+ data.tar.gz: 50bcf0913088dc140b6665ed40b404402c121b53f90fdd82277163928285ccaf
5
5
  SHA512:
6
- metadata.gz: b787634292b964a2b1309eba7838fd3c8c06bcfcd55a9c6fd30187d9c24a3a0288936dceb6145fc467e2215864f264198c2538a41396e9adda188821b96de5c9
7
- data.tar.gz: a62153bc50f02665f33f5833fafdda3c80accaff1ca75852b0e53270233c65c38094474c251625a80547d613bff8dd7b2d0859d12a7adf718623ccdf68449463
6
+ metadata.gz: 4c98ad41fdafab157edaa9549e7ef560ac851997bf2773dd1351fbcbae2f5f2dea2376dedae94aa6a7d1fc7f4a5960b6fc3677ba3b27424455435251dcd87c63
7
+ data.tar.gz: 2197b73726b4d55f9e2d5e2b991f52a3b6524e377203cd389663ad744dfdb39d8346e1c4b12a042503bac08d2278a8e6bde221f48d2774189fe2b239f18d5f78
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ inherit_gem:
2
+ chronicle-core: .rubocop-plugin.yml
data/README.md CHANGED
@@ -10,21 +10,18 @@ Access your iMessage messages and attachments using the command line with this p
10
10
  $ gem install chronicle-etl
11
11
  $ chronicle-etl plugins:install imessage
12
12
 
13
- # Load messages since February 7
14
- $ chronicle-etl --extractor imessage --transformer imessage --since "2022-02-07"
13
+ # Load messages from the last week
14
+ $ chronicle-etl --extractor imessage --schema chronicle --since 1w
15
15
 
16
16
  # Of the latest 1000 messages received, who were the top senders?
17
- $ chronicle-etl -e imessage -t imessage --limit 1000 --no-header-row --fields actor.title | sort | uniq -c | sort -nr
18
-
19
- # Get the raw query results for the latest 10 messages and save as a CSV
20
- $ chronicle-etl -e imessage --loader csv --limit 10 -o imessages.csv
17
+ $ chronicle-etl -e imessage --schema chronicle --limit 1000 --fields agent.name | sort | uniq -c | sort -nr
21
18
  ```
22
19
 
23
20
  ## Available Connectors
24
21
  ### Extractors
25
22
 
26
- #### `messages`
27
- Extractor for importing messages and attachments from local macOS iMessage install (`~/Library/Messages/chat.db`)
23
+ #### `message`
24
+ Extractor for importing messages and attachments from local macOS iMessage install (via local cache at `~/Library/Messages/chat.db`)
28
25
 
29
26
  ##### Settings
30
27
  - `input`: (default: ~/Library/Messages/chat.db) Local iMessage sqlite database
@@ -37,7 +34,3 @@ We want messages to have sender/receiver information set properly so we try to i
37
34
  - `icloud_account_id`: Specify an email address that represents your iCloud account ID
38
35
  - `icloud_account_dsid`: Specify iCloud DSID
39
36
  - Can find in Keychain or by running `$ defaults read MobileMeAccounts Accounts`
40
- ### Transformers
41
-
42
- #### `message`
43
- Transform an iMessage message into Chronicle Schema
data/bin/console CHANGED
@@ -1,14 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "chronicle/imessage"
3
+ require 'bundler/setup'
4
+ require 'chronicle/imessage'
5
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__)
6
+ require 'pry'
7
+ Pry.start
@@ -1,44 +1,49 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "chronicle/imessage/version"
3
+ require 'chronicle/imessage/version'
5
4
 
6
5
  Gem::Specification.new do |spec|
7
- spec.name = "chronicle-imessage"
6
+ spec.name = 'chronicle-imessage'
8
7
  spec.version = Chronicle::Imessage::VERSION
9
- spec.authors = ["Andrew Louis"]
10
- spec.email = ["andrew@hyfen.net"]
8
+ spec.authors = ['Andrew Louis']
9
+ spec.email = ['andrew@hyfen.net']
10
+
11
+ spec.summary = 'iMessage importer for Chronicle'
12
+ spec.description = 'Connectors for iMessage'
13
+ spec.homepage = 'https://github.com/chronicle-app/chronicle-imessage'
11
14
 
12
- spec.summary = "iMessage importer for Chronicle"
13
- spec.description = "Connectors for iMessage"
14
- spec.homepage = "https://github.com/chronicle-app/chronicle-imessage"
15
+ spec.required_ruby_version = '>= 3.1'
15
16
 
16
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
18
  # to allow pushing to a single host or delete this section to allow pushing to any host.
18
19
  if spec.respond_to?(:metadata)
19
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
21
 
21
- spec.metadata["homepage_uri"] = spec.homepage
22
- spec.metadata["source_code_uri"] = "https://github.com/chronicle-app/chronicle-imessage"
23
- spec.metadata["changelog_uri"] = "https://github.com/chronicle-app/chronicle-imessage"
22
+ spec.metadata['homepage_uri'] = spec.homepage
23
+ spec.metadata['source_code_uri'] = 'https://github.com/chronicle-app/chronicle-imessage'
24
+ spec.metadata['changelog_uri'] = 'https://github.com/chronicle-app/chronicle-imessage'
24
25
  else
25
- raise "RubyGems 2.0 or newer is required to protect against " \
26
- "public gem pushes."
26
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
27
+ 'public gem pushes.'
27
28
  end
28
29
 
29
30
  # Specify which files should be added to the gem when it is released.
30
31
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
32
33
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
34
  end
34
- spec.bindir = "exe"
35
+ spec.bindir = 'exe'
35
36
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
- spec.require_paths = ["lib"]
37
-
38
- spec.add_dependency "chronicle-etl", "~> 0.5"
39
- spec.add_dependency "sqlite3", "~> 1.4"
40
- spec.add_dependency "phonelib", "~> 0.6"
41
-
42
- spec.add_development_dependency "bundler", "~> 2.3"
43
- spec.add_development_dependency "rake", "~> 13.0.6"
37
+ spec.require_paths = ['lib']
38
+ spec.metadata['rubygems_mfa_required'] = 'true'
39
+
40
+ spec.add_dependency 'chronicle-core', '~> 0.3'
41
+ spec.add_dependency 'chronicle-etl', '~> 0.6'
42
+ spec.add_dependency 'phonelib', '~> 0.6'
43
+ spec.add_dependency 'sqlite3', '~> 1.4'
44
+
45
+ spec.add_development_dependency 'bundler', '~> 2.3'
46
+ spec.add_development_dependency 'pry-byebug', '~> 3.10'
47
+ spec.add_development_dependency 'rake', '~> 13.0'
48
+ spec.add_development_dependency 'rubocop', '~> 1.63'
44
49
  end
@@ -5,9 +5,10 @@ module Chronicle
5
5
  module Imessage
6
6
  class ImessageExtractor < Chronicle::ETL::Extractor
7
7
  register_connector do |r|
8
- r.provider = 'imessage'
8
+ r.source = :imessage
9
+ r.type = :message
10
+ r.strategy = :local_db
9
11
  r.description = 'a local imessage database'
10
- r.identifier = 'messages'
11
12
  end
12
13
 
13
14
  setting :input, default: File.join(Dir.home, 'Library', 'Messages', 'chat.db'), required: true
@@ -31,7 +32,7 @@ module Chronicle
31
32
  meta[:my_phone_contact] = @my_phone_contact if @my_phone_contact.values.all?
32
33
  meta[:my_icloud_account] = @my_icloud_account if @my_icloud_account.values.all?
33
34
 
34
- yield Chronicle::ETL::Extraction.new(data: message, meta: meta)
35
+ yield build_extraction(data: message, meta:)
35
36
  end
36
37
  end
37
38
 
@@ -1,165 +1,114 @@
1
1
  require 'chronicle/etl'
2
+ require 'chronicle/models'
2
3
 
3
4
  module Chronicle
4
- module Imessage
5
+ module Imessage
5
6
  class ImessageTransformer < Chronicle::ETL::Transformer
6
7
  register_connector do |r|
7
- r.provider = 'imessage'
8
+ r.source = :imessage
9
+ r.type = :message
10
+ r.strategy = :local_db
8
11
  r.description = 'a row from a local imessage database'
9
- r.identifier = 'message'
12
+ r.from_schema = :extraction
13
+ r.to_schema = :chronicle
10
14
  end
11
15
 
12
- def transform
13
- @message = @extraction.data
14
- @participants = @extraction.meta[:participants]
15
- @attachments = @extraction.meta[:attachments] || []
16
+ def transform(record)
17
+ participants = record.extraction.meta[:participants].map { |p| build_identity(record.data, p) }
18
+ my_identity = build_identity_mine(record.data, record.extraction.meta[:my_icloud_account],
19
+ record.extraction.meta[:my_phone_contact])
16
20
 
17
- set_actors
18
- record = build_messaged
19
- record
20
- end
21
-
22
- def timestamp
23
- Time.at(ios_timestamp_to_unix(@message['date'].to_i / 1000000000))
24
- end
25
-
26
- def id
27
- @message['guid']
21
+ build_action(record.data, my_identity, participants)
28
22
  end
29
23
 
30
24
  private
31
25
 
32
- def set_actors
33
- me = build_identity_mine
34
- # Figure out the sender / receiver(s) of a message
35
- case @message['is_from_me']
36
- when 1
37
- @actor = me
38
- @consumers = @participants.collect{|p| build_identity(p)}
26
+ def build_action(message, my_identity, participants)
27
+ if message['is_from_me'] == 1
28
+ participants -= [my_identity]
29
+ agent = my_identity
30
+ consumers = participants
39
31
  else
40
- sender = @participants.select{|p| p['id'] == @message['id']}.first
41
- receivers = @participants - [sender]
42
-
43
- @consumers = receivers.collect{|p| build_identity(p)}
44
- @consumers << me
45
- @actor = build_identity(sender)
32
+ agent = participants.select { |p| p.slug == message['id'] }.first
33
+ consumers = participants - [agent] + [my_identity]
46
34
  end
47
- end
48
35
 
49
- def build_messaged
50
- record = ::Chronicle::ETL::Models::Activity.new
51
- record.end_at = timestamp
52
- record.verb = 'messaged'
53
- record.provider_id = id
54
- record.provider = build_provider(@message['service'])
55
- record.dedupe_on = [[:provider, :verb, :provider_id]]
36
+ raise Chronicle::ETL::UntransformableRecordError, 'Could not find agent' unless agent
56
37
 
57
- record.involved = build_message
58
- record.actor = @actor
38
+ Chronicle::Models::CommunicateAction.new do |r|
39
+ r.end_time = Time.at(ios_timestamp_to_unix(message['date'].to_i / 1_000_000_000))
40
+ r.source = entity_source(message['service'])
41
+ r.source_id = message['guid']
59
42
 
60
- record
43
+ r.agent = agent
44
+ r.object = build_message(message, consumers)
45
+ r.dedupe_on = [%i[source source_id type]]
46
+ end
61
47
  end
62
48
 
63
- def build_message
64
- record = ::Chronicle::ETL::Models::Entity.new
65
- record.body = @message['text']
66
- record.provider_id = id
67
- record.represents = 'message'
68
- record.provider = build_provider(@message['service'])
69
- record.dedupe_on = [[:represents, :provider, :provider_id]]
70
-
71
- record.consumers = @consumers
72
- record.contains = @attachments.map{ |a| build_attachment(a)}.compact
73
-
74
- record
49
+ def build_message(message, consumers)
50
+ Chronicle::Models::Message.new do |r|
51
+ # TODO: handle text that's in `attributedBody`
52
+ r.text = normalized_body(message)
53
+ r.source = 'imessage'
54
+ r.source_id = message['guid']
55
+ r.recipient = consumers
56
+ r.dedupe_on = [%i[source source_id type]]
57
+ end
75
58
  end
76
59
 
77
- def build_attachment(attachment)
78
- return unless attachment['mime_type']
79
-
80
- type, subtype = attachment['mime_type'].split("/")
81
- return unless ['image', 'audio', 'video'].include?(type)
82
- return unless attachment['filename']
83
-
84
- attachment_filename = attachment['filename'].gsub("~", Dir.home)
85
- return unless File.exist?(attachment_filename)
86
-
87
- attachment_data = ::Chronicle::ETL::Utils::BinaryAttachments.filename_to_base64(filename: attachment_filename, mimetype: attachment['mime_type'])
88
- recognized_text = ::Chronicle::ETL::Utils::TextRecognition.recognize_in_image(filename: attachment_filename) if type == 'image'
89
-
90
- record = ::Chronicle::ETL::Models::Entity.new
91
- record.provider = 'imessage'
92
- record.provider_id = attachment['guid']
93
- record.represents = type
94
- record.title = File.basename(attachment['filename'])
95
- record.metadata[:ocr_text] = recognized_text if recognized_text
96
- record.dedupe_on = [[:provider, :provider_id, :represents]]
97
-
98
- attachment = ::Chronicle::ETL::Models::Attachment.new
99
- attachment.data = attachment_data
100
- record.attachments = [attachment]
101
-
102
- record
60
+ def build_identity_mine(message, my_icloud_account, my_phone_contact)
61
+ case agent_source(message['service'])
62
+ when 'icloud'
63
+ build_identity_mine_icloud(my_icloud_account)
64
+ when 'phone'
65
+ build_identity_mine_phone(my_phone_contact)
66
+ end
103
67
  end
104
68
 
105
- def build_identity identity
106
- raise(Chronicle::ETL::UntransformableRecordError, "Could not build record due to missing identity data") unless identity
107
-
108
- record = ::Chronicle::ETL::Models::Entity.new({
109
- represents: 'identity',
110
- slug: identity['id'],
111
- title: identity['full_name'],
112
- provider: identity_provider(@message['service']),
113
- })
114
- record.dedupe_on = [[:represents, :slug, :provider]]
115
- record
116
- end
69
+ def build_identity_mine_icloud(icloud_account)
70
+ unless icloud_account
71
+ raise(Chronicle::ETL::UntransformableRecordError,
72
+ 'Could not build record due to missing iCloud details. Please provide them through the extractor settings.')
73
+ end
117
74
 
118
- def build_identity_mine
119
- case identity_provider(@message['service'])
120
- when 'icloud'
121
- build_identity_mine_icloud
122
- when 'phone'
123
- build_identity_mine_phone
75
+ Chronicle::Models::Person.new do |r|
76
+ r.name = icloud_account[:display_name]
77
+ r.source = 'icloud'
78
+ r.slug = icloud_account[:id]
79
+ r.source_id = icloud_account[:dsid]
80
+ r.dedupe_on = [%i[type source source_id]]
124
81
  end
125
82
  end
126
83
 
127
- def build_identity_mine_icloud
128
- icloud_account = @extraction.meta[:my_icloud_account]
129
- raise(Chronicle::ETL::UntransformableRecordError, "Could not build record due to missing iCloud details. Please provide them through the extractor settings.") unless icloud_account
130
-
131
- record = ::Chronicle::ETL::Models::Entity.new({
132
- represents: 'identity',
133
- provider: 'icloud',
134
- provider_id: icloud_account[:dsid],
135
- title: icloud_account[:display_name],
136
- slug: icloud_account[:id]
137
- })
138
- record.dedupe_on << [:provider, :represents, :slug]
139
- record
84
+ def build_identity_mine_phone(phone_account)
85
+ unless phone_account
86
+ raise(Chronicle::ETL::UntransformableRecordError,
87
+ 'Could not build record due to missing phone details. Please provide them through the extractor settings.')
88
+ end
89
+
90
+ Chronicle::Models::Person.new do |r|
91
+ r.name = phone_account[:name]
92
+ r.source = 'phone'
93
+ r.slug = phone_account[:phone_number]
94
+ r.dedupe_on = [%i[type source slug]]
95
+ end
140
96
  end
141
97
 
142
- def build_identity_mine_phone
143
- phone_account = @extraction.meta[:my_phone_contact]
144
- raise(Chronicle::ETL::UntransformableRecordError, "Could not build record due to missing phone details. Please provide them through the extractor settings.") unless phone_account
145
-
146
- record = ::Chronicle::ETL::Models::Entity.new({
147
- represent: 'identity',
148
- provider: 'phone',
149
- title: phone_account[:name],
150
- slug: phone_account[:phone_number]
151
- })
152
- record.dedupe_on << [:provider, :represents, :slug]
153
- record
98
+ def build_identity(message, identity)
99
+ Chronicle::Models::Person.new do |r|
100
+ r.name = identity['full_name']
101
+ r.source = agent_source(message['service'])
102
+ r.slug = identity['id']
103
+ r.dedupe_on = [%i[type source slug]]
104
+ end
154
105
  end
155
106
 
156
- # in the wild, this is either null or sms
157
- def build_provider service
107
+ def entity_source(service)
158
108
  service ? service.downcase : 'imessage'
159
109
  end
160
110
 
161
- # FIXME: should probably try to preserve imessage ids instead of imessage
162
- def identity_provider service
111
+ def agent_source(service)
163
112
  case service
164
113
  # an SMS message is on the 'sms' provider but the provider of the identity used to send it is 'phone'
165
114
  when 'SMS'then 'phone'
@@ -170,12 +119,72 @@ module Chronicle
170
119
  end
171
120
 
172
121
  # FIXME: refactor to shared
173
- def ios_timestamp_to_unix ts
174
- ts + 978307200
122
+ def ios_timestamp_to_unix(ts)
123
+ ts + 978_307_200
175
124
  end
176
- def unix_to_ios_timestamp ts
177
- ts - 978307200
125
+
126
+ def unix_to_ios_timestamp(ts)
127
+ ts - 978_307_200
128
+ end
129
+
130
+ def normalized_body(message)
131
+ message['text'] || normalize_attributed_body(message['attributedBody'])
178
132
  end
133
+
134
+ # Based on https://github.com/kndonlee/meds/blob/224be297e8e709e6a52aca5fa05ec42d34af1aef/all_messages.rb#L31
135
+ def normalize_attributed_body(body)
136
+ return unless body
137
+
138
+ attributed_body = body.force_encoding('UTF-8').encode('UTF-8', invalid: :replace)
139
+
140
+ return unless attributed_body.include?('NSNumber')
141
+
142
+ attributed_body = attributed_body.split('NSNumber')[0]
143
+ return unless attributed_body.include?('NSString')
144
+
145
+ attributed_body = attributed_body.split('NSString')[1]
146
+ return unless attributed_body.include?('NSDictionary')
147
+
148
+ attributed_body = attributed_body.split('NSDictionary')[0]
149
+ attributed_body = attributed_body[6..-13]
150
+
151
+ if attributed_body =~ /^.[\u0000]/
152
+ attributed_body.gsub(/^.[\u0000]/, '')
153
+ else
154
+ attributed_body
155
+ end
156
+ end
157
+
158
+ # def build_attachment(attachment)
159
+ # return unless attachment['mime_type']
160
+
161
+ # type, subtype = attachment['mime_type'].split('/')
162
+ # return unless %w[image audio video].include?(type)
163
+ # return unless attachment['filename']
164
+
165
+ # attachment_filename = attachment['filename'].gsub('~', Dir.home)
166
+ # return unless File.exist?(attachment_filename)
167
+
168
+ # attachment_data = ::Chronicle::ETL::Utils::BinaryAttachments.filename_to_base64(filename: attachment_filename,
169
+ # mimetype: attachment['mime_type'])
170
+ # if type == 'image'
171
+ # recognized_text = ::Chronicle::ETL::Utils::TextRecognition.recognize_in_image(filename: attachment_filename)
172
+ # end
173
+
174
+ # record = ::Chronicle::ETL::Models::Entity.new
175
+ # record.provider = 'imessage'
176
+ # record.provider_id = attachment['guid']
177
+ # record.represents = type
178
+ # record.title = File.basename(attachment['filename'])
179
+ # record.metadata[:ocr_text] = recognized_text if recognized_text
180
+ # record.dedupe_on = [%i[provider provider_id represents]]
181
+
182
+ # attachment = ::Chronicle::ETL::Models::Attachment.new
183
+ # attachment.data = attachment_data
184
+ # record.attachments = [attachment]
185
+
186
+ # record
187
+ # end
179
188
  end
180
189
  end
181
190
  end
@@ -1,5 +1,5 @@
1
1
  module Chronicle
2
2
  module Imessage
3
- VERSION = "0.2.7"
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
- require "chronicle/imessage/version"
2
- require "chronicle/imessage/imessage_extractor"
3
- require "chronicle/imessage/imessage_transformer"
4
- require "chronicle/imessage/local_contacts"
1
+ require 'chronicle/imessage/version'
2
+ require 'chronicle/imessage/imessage_extractor'
3
+ require 'chronicle/imessage/imessage_transformer'
4
+ require 'chronicle/imessage/local_contacts'
5
5
 
6
6
  module Chronicle
7
7
  module Imessage
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronicle-imessage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Louis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-13 00:00:00.000000000 Z
11
+ date: 2024-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: chronicle-etl
14
+ name: chronicle-core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.5'
19
+ version: '0.3'
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: '0.5'
26
+ version: '0.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: sqlite3
28
+ name: chronicle-etl
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.4'
33
+ version: '0.6'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.4'
40
+ version: '0.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: phonelib
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +80,48 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '2.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.10'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.10'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: rake
71
99
  requirement: !ruby/object:Gem::Requirement
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: 13.0.6
103
+ version: '13.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.63'
76
118
  type: :development
77
119
  prerelease: false
78
120
  version_requirements: !ruby/object:Gem::Requirement
79
121
  requirements:
80
122
  - - "~>"
81
123
  - !ruby/object:Gem::Version
82
- version: 13.0.6
124
+ version: '1.63'
83
125
  description: Connectors for iMessage
84
126
  email:
85
127
  - andrew@hyfen.net
@@ -88,6 +130,7 @@ extensions: []
88
130
  extra_rdoc_files: []
89
131
  files:
90
132
  - ".gitignore"
133
+ - ".rubocop.yml"
91
134
  - CODE_OF_CONDUCT.md
92
135
  - Gemfile
93
136
  - README.md
@@ -107,6 +150,7 @@ metadata:
107
150
  homepage_uri: https://github.com/chronicle-app/chronicle-imessage
108
151
  source_code_uri: https://github.com/chronicle-app/chronicle-imessage
109
152
  changelog_uri: https://github.com/chronicle-app/chronicle-imessage
153
+ rubygems_mfa_required: 'true'
110
154
  post_install_message:
111
155
  rdoc_options: []
112
156
  require_paths:
@@ -115,14 +159,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
159
  requirements:
116
160
  - - ">="
117
161
  - !ruby/object:Gem::Version
118
- version: '0'
162
+ version: '3.1'
119
163
  required_rubygems_version: !ruby/object:Gem::Requirement
120
164
  requirements:
121
165
  - - ">="
122
166
  - !ruby/object:Gem::Version
123
167
  version: '0'
124
168
  requirements: []
125
- rubygems_version: 3.3.3
169
+ rubygems_version: 3.4.10
126
170
  signing_key:
127
171
  specification_version: 4
128
172
  summary: iMessage importer for Chronicle