chronicle-imessage 0.1.0 → 0.2.2

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: bcaf59959a65537025a3ec616fc7edfed08aebcd6a9aca59569e8102553ada41
4
- data.tar.gz: c78cc1e83b6fef4d41b710944484c935409e7833a30c5269792d59872579f4f4
3
+ metadata.gz: 76cac6e564a07e8e411e2378c43b15b812c4b4631dedcdee3863eedb0e3932e6
4
+ data.tar.gz: 7f7551136fae37aa832a98f9bd5ba386fceb7d4d1c0e3396eee4d64c0cbc03d0
5
5
  SHA512:
6
- metadata.gz: a71a3c50cad2861c304b687c291fdcab1ef418c20a384f20c0a71ea2563a75ffec6937630288723d91620c5148224cf13ff143250b07e44bbd62dd893358421f
7
- data.tar.gz: 6b4948f26b2e9d4aced7326c9f6f7bb5e031b6447c1885be8b85912209fc440a03047af01833e536fd8f31c9cf9205885fa0ea3edd5f69cf1da2f930065b2f0d
6
+ metadata.gz: fec05237d35c28b1ff9be65c5a60a622abdabec4c3726d7a91583c579891903e5ecd0860422c88bd35ef5b63d9f1c1fea807233dc6997ae6cd8ee3a0557dc4de
7
+ data.tar.gz: f1d909c69c41258d43d0175ad707dd1069f540527abc122460a587815ff0617fb7b452e2c53ac93de5bc366a6f0fae6ff89905d11edc7eca9b008096ab2f6c80
data/.gitignore CHANGED
@@ -6,3 +6,5 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+
10
+ Gemfile.lock
data/README.md CHANGED
@@ -13,7 +13,6 @@ IMessage importer for [chronicle-etl](https://github.com/chronicle-app/chronicle
13
13
 
14
14
  ```bash
15
15
  gem install chronicle-etl
16
- chronicle-etl connectors:install imessage
17
-
18
- chronicle-etl --extractor imessage --extractor-opts load_since:"2022-02-07" --transformer imessage --loader table
16
+ chronicle-etl plugins:install imessage
17
+ chronicle-etl --extractor imessage --since "2022-02-07" --transformer imessage
19
18
  ```
@@ -35,9 +35,9 @@ Gem::Specification.new do |spec|
35
35
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
36
  spec.require_paths = ["lib"]
37
37
 
38
- spec.add_dependency "chronicle-etl", "~> 0.3"
39
- spec.add_dependency "sqlite3", "~> 1.4.2"
40
- spec.add_dependency "phonelib", "~> 0.6.54"
38
+ spec.add_dependency "chronicle-etl", "~> 0.4.2"
39
+ spec.add_dependency "sqlite3", "~> 1.4"
40
+ spec.add_dependency "phonelib", "~> 0.6"
41
41
 
42
42
  spec.add_development_dependency "bundler", "~> 2.3"
43
43
  spec.add_development_dependency "rake", "~> 13.0.6"
@@ -10,14 +10,16 @@ module Chronicle
10
10
  r.description = 'a local imessage database'
11
11
  end
12
12
 
13
- DEFAULT_OPTIONS = {
14
- db: File.join(Dir.home, 'Library', 'Messages', 'chat.db'),
15
- load_attachments: false,
16
- load_since: Time.now - 5000000
17
- }.freeze
18
-
19
- def initialize(options = {})
20
- super(DEFAULT_OPTIONS.merge(options))
13
+ setting :db, default: File.join(Dir.home, 'Library', 'Messages', 'chat.db'), required: true
14
+ setting :load_attachments, default: false
15
+ setting :only_attachments, default: false
16
+ setting :my_phone_number
17
+ setting :my_name
18
+ setting :icloud_account_id
19
+ setting :icloud_account_dsid
20
+ setting :icloud_account_display_name
21
+
22
+ def prepare
21
23
  prepare_data
22
24
  end
23
25
 
@@ -26,6 +28,8 @@ module Chronicle
26
28
  meta = {}
27
29
  meta[:participants] = @chats[message['chat_id']]
28
30
  meta[:attachments] = @attachments[message['message_id']] if @attachments
31
+ meta[:my_phone_contact] = @my_phone_contact
32
+ meta[:my_icloud_account] = @my_icloud_account
29
33
 
30
34
  yield Chronicle::ETL::Extraction.new(data: message, meta: meta)
31
35
  end
@@ -38,34 +42,60 @@ module Chronicle
38
42
  private
39
43
 
40
44
  def prepare_data
41
- @db = SQLite3::Database.new(@options[:db], results_as_hash: true)
42
- @messages = load_messages(
43
- load_since: @options[:load_since],
44
- load_until: @options[:load_until],
45
- limit: @options[:limit]
46
- )
47
- @contacts = LocalContacts.new.contacts
45
+ @db = SQLite3::Database.new(@config.db, results_as_hash: true)
46
+ @local_contacts = LocalContacts.new
47
+ @contacts = @local_contacts.contacts
48
+
49
+ @messages = load_messages
48
50
  @chats = load_chats
49
51
 
50
- if @options[:load_attachments]
52
+ @my_phone_contact = load_my_phone_contact(@local_contacts)
53
+ @my_icloud_account = load_my_icloud_account(@local_contacts)
54
+
55
+ if @config.load_attachments
51
56
  @attachments = load_attachments(@messages.map{|m| m['message_id']})
52
57
  end
53
58
  end
54
59
 
55
- def load_messages(load_since: nil, load_until: nil, limit: nil)
56
- load_since_ios = unix_to_ios_timestamp(load_since.to_i) * 1000000000 if load_since
57
- load_until_ios = unix_to_ios_timestamp(load_until.to_i) * 1000000000 if load_until
60
+ def load_my_phone_contact(local_contacts)
61
+ {
62
+ phone_number: @config.my_phone_number || local_contacts.my_phone_contact.fetch(:phone_number),
63
+ name: @config.my_name || local_contacts.my_phone_contact.fetch(:full_name)
64
+ }
65
+ end
66
+
67
+ def load_my_icloud_account(local_contacts)
68
+ {
69
+ id: @config.icloud_account_id || local_contacts.my_icloud_account.fetch(:AccountID),
70
+ dsid: @config.icloud_account_dsid || local_contacts.my_icloud_account.fetch(:AccountDSID),
71
+ display_name: @config.icloud_account_display_name || @config.my_name || local_contacts.my_icloud_account.fetch(:DisplayName)
72
+ }
73
+ end
74
+
75
+ def load_messages
76
+ conditions = []
77
+
78
+ if @config.until
79
+ load_until_ios = unix_to_ios_timestamp(@config.until.to_i) * 1000000000
80
+ conditions << "date < #{load_until_ios}"
81
+ end
82
+
83
+ if @config.since
84
+ load_since_ios = unix_to_ios_timestamp(@config.since.to_i) * 1000000000
85
+ conditions << "date > #{load_since_ios}"
86
+ end
87
+
88
+ if @config.only_attachments
89
+ conditions << "cache_has_attachments = true"
90
+ end
58
91
 
59
92
  sql = "SELECT * from message as m
60
93
  LEFT OUTER JOIN handle as h ON m.handle_id=h.ROWID
61
94
  INNER JOIN chat_message_join as cm ON m.ROWID = cm.message_id"
62
95
 
63
- conditions = []
64
- conditions << "date < #{load_until_ios}" if load_until
65
- conditions << "date > #{load_since_ios}" if load_since
66
96
  sql += " WHERE #{conditions.join(" AND ")}" if conditions.any?
67
97
  sql += " ORDER BY date DESC"
68
- sql += " LIMIT #{limit}" if limit
98
+ sql += " LIMIT #{@config.limit}" if @config.limit
69
99
 
70
100
  messages = @db.execute(sql)
71
101
  end
@@ -8,14 +8,6 @@ module Chronicle
8
8
  r.description = 'a row from a local imessage database'
9
9
  end
10
10
 
11
- DEFAULT_OPTIONS = {
12
- }.freeze
13
-
14
- def initialize(*args)
15
- super(*args)
16
- @options = @options.reverse_merge(DEFAULT_OPTIONS)
17
- end
18
-
19
11
  def transform
20
12
  @message = @extraction.data
21
13
  @participants = @extraction.meta[:participants]
@@ -123,14 +115,40 @@ module Chronicle
123
115
  end
124
116
 
125
117
  def build_identity_mine
118
+ case identity_provider(@message['service'])
119
+ when 'icloud'
120
+ build_identity_mine_icloud
121
+ when 'phone'
122
+ build_identity_mine_phone
123
+ end
124
+ end
125
+
126
+ def build_identity_mine_icloud
127
+ icloud_account = @extraction.meta[:my_icloud_account]
128
+ raise(UntransformableRecordError, "Missing iCloud account information") unless icloud_account
129
+
126
130
  record = ::Chronicle::ETL::Models::Entity.new({
127
- represents: 'identity',
128
- slug: @options[:my_phone_slug],
129
- title: @options[:my_name],
130
- provider: identity_provider(@message['service']),
131
- provider_id: @message['account_guid']
131
+ represent: 'identity',
132
+ provider: 'icloud',
133
+ provider_id: icloud_account[:dsid],
134
+ title: icloud_account[:display_name],
135
+ slug: icloud_account[:id]
136
+ })
137
+ record.dedupe_on << [:provider, :represents, :slug]
138
+ record
139
+ end
140
+
141
+ def build_identity_mine_phone
142
+ phone_account = @extraction.meta[:my_phone_contact]
143
+ raise(UntransformableRecordError, "Missing own phone contact information") unless phone_account
144
+
145
+ record = ::Chronicle::ETL::Models::Entity.new({
146
+ represent: 'identity',
147
+ provider: 'phone',
148
+ title: phone_account[:name],
149
+ slug: phone_account[:phone_number]
132
150
  })
133
- record.dedupe_on = [[:represents, :slug, :provider], [:represents, :provider, :provider_id]]
151
+ record.dedupe_on << [:provider, :represents, :slug]
134
152
  record
135
153
  end
136
154
 
@@ -23,6 +23,14 @@ module Chronicle
23
23
  end
24
24
  end
25
25
 
26
+ def my_phone_contact
27
+ @my_phone_contact ||= load_my_phone_contact
28
+ end
29
+
30
+ def my_icloud_account
31
+ @my_icloud_account ||= load_my_icloud_account
32
+ end
33
+
26
34
  # The synced address book doesn't have a stable folder location so we
27
35
  # have to search for it
28
36
  def find_local_icloud_address_book
@@ -30,6 +38,38 @@ module Chronicle
30
38
  Dir.glob(pattern).first
31
39
  end
32
40
 
41
+ def load_my_icloud_account
42
+ @my_icloud_account || begin
43
+ output = `defaults read MobileMeAccounts Accounts | plutil -convert json -r -o - -- -`
44
+ JSON.parse(output, symbolize_names: true).first
45
+ end
46
+ end
47
+
48
+ def load_my_phone_contact
49
+ sql = <<-SQL
50
+ SELECT
51
+ ZABCDPHONENUMBER.ZFULLNUMBER AS identifier,
52
+ ZABCDRECORD.ZFIRSTNAME as first_name,
53
+ ZABCDRECORD.ZLASTNAME as last_name
54
+ FROM
55
+ ZABCDRECORD,
56
+ ZABCDPHONENUMBER
57
+ WHERE
58
+ ZABCDRECORD.Z_PK = ZABCDPHONENUMBER.ZOWNER
59
+ AND ZABCDRECORD.zcontainerwherecontactisme IS NOT NULL
60
+ SQL
61
+
62
+ results = @db.execute(sql)
63
+
64
+ guessed_number = results.first
65
+ return unless guessed_number
66
+
67
+ {
68
+ phone_number: Phonelib.parse(guessed_number['identifier'], "US").e164,
69
+ full_name: "#{guessed_number['first_name']} #{guessed_number['last_name']}"
70
+ }
71
+ end
72
+
33
73
  def load_phone_numbers
34
74
  sql = <<-SQL
35
75
  SELECT
@@ -1,5 +1,5 @@
1
1
  module Chronicle
2
2
  module Imessage
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.2"
4
4
  end
5
5
  end
@@ -5,7 +5,5 @@ require "chronicle/imessage/local_contacts"
5
5
 
6
6
  module Chronicle
7
7
  module Imessage
8
- class Error < StandardError; end
9
- # Your code goes here...
10
8
  end
11
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronicle-imessage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.2
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-02-07 00:00:00.000000000 Z
11
+ date: 2022-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronicle-etl
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.3'
19
+ version: 0.4.2
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.3'
26
+ version: 0.4.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sqlite3
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.4.2
33
+ version: '1.4'
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.2
40
+ version: '1.4'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: phonelib
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.6.54
47
+ version: '0.6'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.6.54
54
+ version: '0.6'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -90,7 +90,6 @@ files:
90
90
  - ".gitignore"
91
91
  - CODE_OF_CONDUCT.md
92
92
  - Gemfile
93
- - Gemfile.lock
94
93
  - README.md
95
94
  - Rakefile
96
95
  - bin/console
@@ -123,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
125
124
  requirements: []
126
- rubygems_version: 3.1.2
125
+ rubygems_version: 3.3.9
127
126
  signing_key:
128
127
  specification_version: 4
129
128
  summary: iMessage importer for Chronicle
data/Gemfile.lock DELETED
@@ -1,88 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- chronicle-imessage (0.1.0)
5
- chronicle-etl (~> 0.3)
6
- phonelib (~> 0.6.54)
7
- sqlite3 (~> 1.4.2)
8
-
9
- GEM
10
- remote: https://rubygems.org/
11
- specs:
12
- activesupport (7.0.1)
13
- concurrent-ruby (~> 1.0, >= 1.0.2)
14
- i18n (>= 1.6, < 2)
15
- minitest (>= 5.1)
16
- tzinfo (~> 2.0)
17
- chronic_duration (0.10.6)
18
- numerizer (~> 0.1.1)
19
- chronicle-etl (0.3.0)
20
- activesupport
21
- chronic_duration (~> 0.10.6)
22
- colorize (~> 0.8.1)
23
- marcel (~> 1.0.2)
24
- mini_exiftool (~> 2.10)
25
- nokogiri (~> 1.13)
26
- runcom (~> 6.2)
27
- sequel (~> 5.35)
28
- sqlite3 (~> 1.4)
29
- thor (~> 0.20)
30
- tty-progressbar (~> 0.17)
31
- tty-table (~> 0.11)
32
- colorize (0.8.1)
33
- concurrent-ruby (1.1.9)
34
- i18n (1.9.1)
35
- concurrent-ruby (~> 1.0)
36
- marcel (1.0.2)
37
- mini_exiftool (2.10.2)
38
- mini_portile2 (2.7.1)
39
- minitest (5.15.0)
40
- nokogiri (1.13.1)
41
- mini_portile2 (~> 2.7.0)
42
- racc (~> 1.4)
43
- numerizer (0.1.1)
44
- pastel (0.8.0)
45
- tty-color (~> 0.5)
46
- phonelib (0.6.55)
47
- racc (1.6.0)
48
- rake (13.0.6)
49
- refinements (7.18.0)
50
- runcom (6.6.0)
51
- refinements (~> 7.16)
52
- xdg (~> 4.4)
53
- sequel (5.53.0)
54
- sqlite3 (1.4.2)
55
- strings (0.2.1)
56
- strings-ansi (~> 0.2)
57
- unicode-display_width (>= 1.5, < 3.0)
58
- unicode_utils (~> 1.4)
59
- strings-ansi (0.2.0)
60
- thor (0.20.3)
61
- tty-color (0.6.0)
62
- tty-cursor (0.7.1)
63
- tty-progressbar (0.18.2)
64
- strings-ansi (~> 0.2)
65
- tty-cursor (~> 0.7)
66
- tty-screen (~> 0.8)
67
- unicode-display_width (>= 1.6, < 3.0)
68
- tty-screen (0.8.1)
69
- tty-table (0.12.0)
70
- pastel (~> 0.8)
71
- strings (~> 0.2.0)
72
- tty-screen (~> 0.8)
73
- tzinfo (2.0.4)
74
- concurrent-ruby (~> 1.0)
75
- unicode-display_width (2.1.0)
76
- unicode_utils (1.4.0)
77
- xdg (4.5.0)
78
-
79
- PLATFORMS
80
- ruby
81
-
82
- DEPENDENCIES
83
- bundler (~> 2.3)
84
- chronicle-imessage!
85
- rake (~> 13.0.6)
86
-
87
- BUNDLED WITH
88
- 2.3.6