-
Notifications
You must be signed in to change notification settings - Fork 7
/
find-messages.php
236 lines (208 loc) · 7.35 KB
/
find-messages.php
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<?php
use Alfred\Workflows\Workflow;
require __DIR__ . '/vendor/autoload.php';
// determine how many minutes to look back
$lookBackMinutes = max(0, (int) @$argv[1]) ?: 15;
$workflow = new Workflow;
$dbPath = $_SERVER['HOME'] . '/Library/Messages/chat.db';
if (! is_readable($dbPath)) {
$workflow->result()
->title('ERROR: Unable to Access Your Messages')
->subtitle('We were unable to access the file that contains your text messages')
->arg('')
->valid(true);
echo $workflow->output();
exit;
}
try {
$db = new PDO('sqlite:' . $dbPath);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$workflow->result()
->title('ERROR: Unable to Access Your Messages')
->subtitle('We were unable to access the file that contains your text messages')
->arg('')
->valid(true);
$workflow->result()
->title('Error Message:')
->subtitle($e->getMessage())
->arg('')
->valid(true);
echo $workflow->output();
exit;
}
try {
$query = $db->query("
select
message.rowid,
ifnull(handle.uncanonicalized_id, chat.chat_identifier) AS sender,
message.service,
datetime(message.date / 1000000000 + 978307200, 'unixepoch', 'localtime') AS message_date,
message.text
from
message
left join chat_message_join
on chat_message_join.message_id = message.ROWID
left join chat
on chat.ROWID = chat_message_join.chat_id
left join handle
on message.handle_id = handle.ROWID
where
message.is_from_me = 0
and message.text is not null
and length(message.text) > 0
and (
message.text glob '*[0-9][0-9][0-9]*'
or message.text glob '*[0-9][0-9][0-9][0-9]*'
or message.text glob '*[0-9][0-9][0-9][0-9][0-9]*'
or message.text glob '*[0-9][0-9][0-9][0-9][0-9][0-9]*'
or message.text glob '*[0-9][0-9][0-9]-[0-9][0-9][0-9]*'
or message.text glob '*[0-9][0-9][0-9][0-9][0-9][0-9][0-9]*'
or message.text glob '*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*'
)
and datetime(message.date / 1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime')
>= datetime('now', '-$lookBackMinutes minutes', 'localtime')
order by
message.date desc
limit 100
");
} catch (PDOException $e) {
$workflow->result()
->title('ERROR: Unable to Query Your Messages')
->subtitle('We were unable to run the query that reads your text messages')
->arg('')
->valid(true);
$workflow->result()
->title('Error Message:')
->subtitle($e->getMessage())
->arg('')
->valid(true);
echo $workflow->output();
exit;
}
$found = 0;
$max = 8;
while ($message = $query->fetch(PDO::FETCH_ASSOC)) {
$code = null;
$text = $message['text'];
// remove URLs
$text = preg_replace('/\b((https?|ftp|file):\/\/|www\.)[-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i', '', $text);
// skip now-empty messages
$text = trim($text);
if (empty($text)) {
continue;
}
if (preg_match('/(^|\s|\R|\t|\b|G-|:)(\d{5,8})($|\s|\R|\t|\b|\.|,)/', $text, $matches)) {
// 5-8 consecutive digits
// examples:
// "您的验证码是 199035,10分钟内有效,请勿泄露"
// "登录验证码:627823,您正在尝试【登录】,10分钟内有效"
// "【赛验】验证码 54538"
// "Enter this code to log in:59678."
// "G-315643 is your Google verification code"
// "Enter the code 765432, and then click the button to log in."
// "Your code is 45678!"
// "Your code is:98765!"
$code = $matches[2];
} elseif (preg_match('/^(\d{4,8})(\sis your.*code)/', $text, $matches)) {
// 4-8 digits followed by "is your [...] code"
// examples:
// "2773 is your Microsoft account verification code"
$code = $matches[1];
} elseif (preg_match('/(code:|is:)\s*(\d{4,8})($|\s|\R|\t|\b|\.|,)/i', $text, $matches)) {
// "code:" OR "is:", optional whitespace, then 4-8 consecutive digits
// examples:
// "Your Airbnb verification code is: 1234."
// "Your verification code is: 1234, use it to log in"
// "Here is your authorization code:9384"
$code = $matches[2];
} elseif (preg_match('/(code|is):?\s*(\d{3,8})($|\s|\R|\t|\b|\.|,)/i', $text, $matches)) {
// "code" OR "is" followed by an optional ":" + optional whitespace, then 3-8 consecutive digits
// examples:
// "Please enter code 548 on Zocdoc."
$code = $matches[2];
} elseif (preg_match('/(^|code:|is:|\b)\s*(\d{3})-(\d{3})($|\s|\R|\t|\b|\.|,)/', $text, $matches)) {
// line beginning OR "code:" OR "is:" OR word boundary, optional whitespace, 3 consecutive digits, a hyphen, then 3 consecutive digits
// but NOT a phone number (###-###-####)
// examples:
// "123-456"
// "Your Stripe verification code is: 719-839."
$first = $matches[2];
$second = $matches[3];
// make sure it isn't a phone number
// doesn't match: <first digits>-<second digits>-<4 consecutive digits>
if (! preg_match('/(^|code:|is:|\b)\s*' . $first . '-' . $second . '-(\d{4})($|\s|\R|\t|\b|\.|,)/', $text, $matches)) {
$code = $first . $second;
}
}
if ($code) {
$found++;
$date = formatDate($message['message_date']);
$text = formatText($message['text']);
$sender = formatSender($message['sender']);
$workflow->result()
->title($code)
->subtitle("$date from $sender: $text")
->arg($code)
->valid(true);
if ($found >= $max) {
break;
}
}
}
if (! $found) {
$workflow->result()
->title('No 2FA Codes Found')
->subtitle("No two-factor auth codes were found in your text messages from the past $lookBackMinutes minutes")
->arg('')
->valid(true);
}
echo $workflow->output();
/**
* Format the date of the message
*
* @param string $date
*
* @return string
*/
function formatDate($date)
{
$time = strtotime($date);
if (date('m/d/Y', $time) === date('m/d/Y')) {
return 'Today @ ' . date('g:ia', $time);
}
return date('M j @ g:ia', $time);
}
/**
* Format the text of the message
*
* @param string $text
*
* @return string
*/
function formatText($text)
{
return str_replace(
["\n", ':;'],
['; ', ':'],
trim($text)
);
}
/**
* Format a sender number
*
* @param string $sender
*
* @return string
*/
function formatSender($sender)
{
$sender = trim($sender, '+');
if (strlen($sender) === 11 && substr($sender, 0, 1) === '1') {
$sender = substr($sender, 1);
}
if (strlen($sender) === 10) {
return substr($sender, 0, 3) . '-' . substr($sender, 3, 3) . '-' . substr($sender, 6, 4);
}
return $sender;
}