-
Notifications
You must be signed in to change notification settings - Fork 152
/
Copy pathichat-convert
executable file
·175 lines (142 loc) · 4.14 KB
/
ichat-convert
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#!/usr/bin/env macruby -wKU
framework 'Foundation'
$VERBOSE = nil
framework 'AppKit' # for decoding classes such as NSMutableParagraphStyle
$VERBOSE = true
framework 'AddressBook'
# based on Logorrhea from Spiny Software
class InstantMessage
attr_reader :sender, :time, :text, :flags
def initWithCoder(coder)
@coder = coder
if @coder.allowsKeyedCoding
@sender = @coder.decodeObjectForKey "Sender"
@time = @coder.decodeObjectForKey "Time"
@text = @coder.decodeObjectForKey "MessageText"
@flags = @coder.decodeInt32ForKey "Flags"
else
@sender = @coder.decodeObject
@time = @coder.decodeObject
@text = @coder.decodeObject
# [decoder decodeValueOfObjCType:@encode(unsigned int) at:&flags];
end
self
end
end
class Presentity
attr_reader :service, :sender_id
def initWithCoder(coder)
@coder = coder
if @coder.allowsKeyedCoding
@service = @coder.decodeObjectForKey "ServiceName"
@sender_id = @coder.decodeObjectForKey "ID"
else
@service = @coder.decodeObject
@sender_id = @coder.decodeObject
end
self
end
def self.person_mapper(&block)
if block_given?
@person_mapper = block
else
if @person_mapper.respond_to? :call
mapper = @person_mapper.call
@person_mapper = Hash.new { |h, id| h[id] = mapper[id] }
end
@person_mapper
end
end
def person
self.class.person_mapper[sender_id]
end
def ==(other)
raise ArgumentError unless self.class === other
self.service == other.service and self.sender_id == other.sender_id
end
end
class IChat
attr_reader :messages
include Enumerable
def initialize(filename)
log = NSData.alloc.initWithContentsOfMappedFile filename
data = NSKeyedUnarchiver.unarchiveObjectWithData log
@messages = data.detect { |obj| Array === obj }
end
def each(&block)
messages.each(&block)
end
end
class ABAddressBook
def search_properties(props, for:value)
searches = props.map do |property|
prop_const = Object.const_get :"KAB#{property}Property"
if property.to_s == 'URLs' and value =~ %r{^-(\d+)@chat\.facebook\.com$}
facebook_id = "fb://profile/#{$1}"
ABPerson.searchElementForProperty(prop_const, label:nil, key:nil, value:facebook_id, comparison:KABEqual)
else
ABPerson.searchElementForProperty(prop_const, label:nil, key:nil, value:value, comparison:KABEqualCaseInsensitive)
end
end
multi = ABSearchElement.searchElementForConjunction(KABSearchOr, children:searches)
self.recordsMatchingSearchElement multi
end
def find_chat_buddy(id)
search_properties(%w[Email AIMInstant JabberInstant URLs], for:id).first
end
def find_by_email(email)
search_properties(%w[Email], for:email).first
end
end
# adding a method to String for some reason doesn't work for all strings
class NSString
def presence
empty? ? nil : self
end
end unless ''.respond_to? :presence
class NilClass
def presence() self end
end unless nil.respond_to? :presence
class ABPerson
def first_name
valueForProperty(KABFirstNameProperty).presence
end
def last_name
valueForProperty(KABLastNameProperty).presence
end
def nickname
valueForProperty(KABNicknameProperty).presence
end
def to_s
[first_name, last_name].compact.join(' ')
end
end
if $0 == __FILE__
# takes in a file, dumps chat in plaintext format
chat = IChat.new ARGV.first
Presentity.person_mapper do
ab = ABAddressBook.sharedAddressBook
lambda { |id|
if id =~ /^e:(.+)/ and $1.include? '@'
buddy = ab.find_by_email($1)
else
buddy = ab.find_chat_buddy(id)
end
buddy ? (buddy.nickname || buddy.first_name) : id.split('@').first
}
end
previous_msg = nil
chat.each do |msg|
next unless msg.sender
sender = msg.sender.person
text = msg.text.string
if text.index('/me ') == 0
puts "(#{text.sub(/\/me\s+/, sender + ' ')})"
else
print "[#{sender}] " if previous_msg.nil? or previous_msg.sender != msg.sender
puts text
end
puts
previous_msg = msg
end
end