chronicle-imessage 0.2.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +2 -3
- data/chronicle-imessage.gemspec +2 -2
- data/lib/chronicle/imessage/imessage_extractor.rb +26 -1
- data/lib/chronicle/imessage/imessage_transformer.rb +32 -9
- data/lib/chronicle/imessage/local_contacts.rb +40 -0
- data/lib/chronicle/imessage/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 708f7221b523605befa9a52e8fd375d79fc2a82109cf55c04fe145d53a1465b7
|
4
|
+
data.tar.gz: 570cbd92f6c52f8149fbebc18f5f961f314a5e3908983a2ce702ce324d2b88ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3d7c77e1fe7ee50ab51a4d11566c5c0ab5279b347f1b946c42cb376b6382ed2687af7d0431c5d30ef87bf8bc2c8f58ccf185d2ee5ebd91a99b47af7756ef31d
|
7
|
+
data.tar.gz: 51ed17c712516215615f964859ba08cb59deeb6a9d91811865e3b8e226bff1e357fafe04a52fd19a0e545dfe05fc4e309bdcc47853be6c177b80a1e319001643
|
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
|
17
|
-
|
18
|
-
chronicle-etl --extractor imessage --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
|
```
|
data/chronicle-imessage.gemspec
CHANGED
@@ -36,8 +36,8 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.require_paths = ["lib"]
|
37
37
|
|
38
38
|
spec.add_dependency "chronicle-etl", "~> 0.4"
|
39
|
-
spec.add_dependency "sqlite3", "~> 1.4
|
40
|
-
spec.add_dependency "phonelib", "~> 0.6
|
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"
|
@@ -13,6 +13,11 @@ module Chronicle
|
|
13
13
|
setting :db, default: File.join(Dir.home, 'Library', 'Messages', 'chat.db'), required: true
|
14
14
|
setting :load_attachments, default: false
|
15
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
|
16
21
|
|
17
22
|
def prepare
|
18
23
|
prepare_data
|
@@ -23,6 +28,8 @@ module Chronicle
|
|
23
28
|
meta = {}
|
24
29
|
meta[:participants] = @chats[message['chat_id']]
|
25
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
|
26
33
|
|
27
34
|
yield Chronicle::ETL::Extraction.new(data: message, meta: meta)
|
28
35
|
end
|
@@ -37,14 +44,32 @@ module Chronicle
|
|
37
44
|
def prepare_data
|
38
45
|
@db = SQLite3::Database.new(@config.db, results_as_hash: true)
|
39
46
|
@messages = load_messages
|
40
|
-
@contacts = LocalContacts.new.contacts
|
41
47
|
@chats = load_chats
|
42
48
|
|
49
|
+
@local_contacts = LocalContacts.new
|
50
|
+
@my_phone_contact = load_my_phone_contact(@local_contacts)
|
51
|
+
@my_icloud_account = load_my_icloud_account(@local_contacts)
|
52
|
+
|
43
53
|
if @config.load_attachments
|
44
54
|
@attachments = load_attachments(@messages.map{|m| m['message_id']})
|
45
55
|
end
|
46
56
|
end
|
47
57
|
|
58
|
+
def load_my_phone_contact(local_contacts)
|
59
|
+
{
|
60
|
+
phone_number: @config.my_phone_number || local_contacts.my_phone_contact.fetch(:phone_number),
|
61
|
+
name: @config.my_name || local_contacts.my_phone_contact.fetch(:full_name)
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_my_icloud_account(local_contacts)
|
66
|
+
{
|
67
|
+
id: @config.icloud_account_id || local_contacts.my_icloud_account.fetch(:AccountID),
|
68
|
+
dsid: @config.icloud_account_dsid || local_contacts.my_icloud_account.fetch(:AccountDSID),
|
69
|
+
display_name: @config.icloud_account_display_name || @config.my_name || local_contacts.my_icloud_account.fetch(:DisplayName)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
48
73
|
def load_messages
|
49
74
|
conditions = []
|
50
75
|
|
@@ -8,9 +8,6 @@ module Chronicle
|
|
8
8
|
r.description = 'a row from a local imessage database'
|
9
9
|
end
|
10
10
|
|
11
|
-
setting :my_phone_slug, required: true
|
12
|
-
setting :my_name, required: true
|
13
|
-
|
14
11
|
def transform
|
15
12
|
@message = @extraction.data
|
16
13
|
@participants = @extraction.meta[:participants]
|
@@ -118,14 +115,40 @@ module Chronicle
|
|
118
115
|
end
|
119
116
|
|
120
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
|
+
|
121
130
|
record = ::Chronicle::ETL::Models::Entity.new({
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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]
|
127
150
|
})
|
128
|
-
record.dedupe_on
|
151
|
+
record.dedupe_on << [:provider, :represents, :slug]
|
129
152
|
record
|
130
153
|
end
|
131
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
|
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.2.
|
4
|
+
version: 0.2.1
|
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-
|
11
|
+
date: 2022-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chronicle-etl
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.4
|
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
|
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
|
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
|
+
version: '0.6'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
122
|
- !ruby/object:Gem::Version
|
123
123
|
version: '0'
|
124
124
|
requirements: []
|
125
|
-
rubygems_version: 3.
|
125
|
+
rubygems_version: 3.3.9
|
126
126
|
signing_key:
|
127
127
|
specification_version: 4
|
128
128
|
summary: iMessage importer for Chronicle
|