https://github.com/user-attachments/assets/c10e418e-dce9-43b4-9c8e-e899ec8e4e7b

Open messages-2fa in Script Kit

// Name: Messages 2FA codes
// Description: Search for 2FA codes in your Messages, within the last 30 minutes
// Ackowledgements:
// - https://github.com/squatto/alfred-imessage-2fa/
// - https://github.com/raycast/extensions/tree/main/extensions/imessage-2fa
import "@johnlindquist/kit"
import Database from 'better-sqlite3';
let preferences = {
lookBackMinutes: 30,
ignoreRead: false,
}
export type TMessage = {
guid: string;
message_date: string; // 2024-11-26 06:11:18
sender: string; // e.g. amazon.de or +49123456789
service: string; // e.g. SMS
text: string;
}
const db = new Database(home("Library/Messages/chat.db"));
let output = await arg({
placeholder: "Select a message or start typing to search",
choices: async (input) => {
let stmt = db.prepare(dbQuery(input));
let messages = stmt.all() as TMessage[];
return messages.map((m) => ({
name: m.text,
tag: extractCode(m.text) ?? "no code",
description: `${m.message_date}${m.sender}${m.service}`,
value: m.text,
preview: `<div class="p-2 text-sm">${m.text}</div>`,
}));
},
actions: [
{
name: "Copy Whole Message",
flag: "copyWholeMessage",
visible: true,
shortcut: `${cmd}+c`,
},
]
})
if (flag.copyWholeMessage) {
clipboard.writeText(output)
notify("Whole message copied to clipboard")
} else {
let code = extractCode(output)
if (code) {
clipboard.writeText(code)
notify(`Code: ${code} copied to clipboard`)
} else {
clipboard.writeText(output)
notify("No code found. Copied whole message to clipboard instead")
}
}
// Helpers
// ===
function dbQuery(qs: string = "") {
let baseQuery = /* sql */`
select
message.guid,
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
datetime(message.date / 1000000000 + strftime('%s', '2001-01-01'), 'unixepoch', 'localtime')
>=
datetime('now', '-${preferences.lookBackMinutes} minutes', 'localtime')
`;
if (preferences.ignoreRead) baseQuery += " and message.is_read = 0";
if (!qs) { // search for code
baseQuery = /* sql */`${baseQuery} and (
-- Matches 3 alphanumeric (e.g., 'ABC')
message.text glob '*[0-9A-Z][0-9A-Z][0-9A-Z]*'
-- Matches 4 alphanumeric (e.g., 'ABCD')
or message.text glob '*[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]*'
-- Matches 5 alphanumeric (e.g., 'ABCDE')
or message.text glob '*[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]*'
-- Matches 6 alphanumeric (e.g., 'ABCDEF')
or message.text glob '*[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]*'
-- Matches format '123-456'
or message.text glob '*[0-9][0-9][0-9]-[0-9][0-9][0-9]*'
-- Matches 7 alphanumeric (e.g., 'ABCDEFG')
or message.text glob '*[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]*'
-- Matches 8 alphanumeric (e.g., 'ABCDEFGH')
or message.text glob '*[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]*'
)`;
} else { // Search for text
baseQuery = /* sql */`${baseQuery} and message.text like '%${qs}%'`;
}
return `${baseQuery} \norder by message.date desc limit 100`.trim();
}
export function extractCode(original: string) {
// remove URLs
const urlRegex = new RegExp(
"\\b((https?|ftp|file):\\/\\/|www\\.)[-A-Z0-9+&@#\\/%?=~_|$!:,.;]*[A-Z0-9+&@#\\/%=~_|$]",
"ig"
);
let message = original.replaceAll(urlRegex, "");
if (message.trim() === "") return "";
let m;
let code;
// Look for specific patterns first
if ((m = /^(\d{4,8})(\sis your.*code)/.exec(message)) !== null) {
// 4-8 digits followed by "is your [...] code"
// examples:
// "2773 is your Microsoft account verification code"
code = m[1];
} else if (
(m = /(code\s*:|is\s*:|码|use code|autoriza(?:ca|çã)o\s*:|c(?:o|ó)digo\s*:)\s*(\w{4,8})($|\s|\\R|\t|\b|\.|,)/i.exec(
message
)) !== null
) {
// "code:" OR "is:" OR "use code", optional whitespace, then 4-8 consecutive alphanumeric characters
// examples:
// "Your Airbnb verification code is: 1234."
// "Your verification code is: 1234, use it to log in"
// "Here is your authorization code:9384"
// "【抖音】验证码9316,用于手机验证"
// "Your healow verification code is : 7579."
// "TRUSTED LOCATION PASSCODE: mifsuc"
// "Código de Autorização: 12345678"
code = m[2];
} else {
// more generic, brute force patterns
// remove phone numbers
// we couldn't do this before, because some auth codes resemble text shortcodes, which would be filtered by this regex
const phoneRegex = new RegExp(
// https://stackoverflow.com/a/123666
/(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?/,
"ig"
);
const originalMessage = message;
message = message.replaceAll(phoneRegex, "");
if ((m = /(^|\s|\\R|\t|\b|G-|:)(\d{5,8})($|\s|\\R|\t|\b|\.|,)/.exec(message)) !== null) {
// 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 = m[2];
} else if ((m = /\b(?=[A-Z]*[0-9])(?=[0-9]*[A-Z])[0-9A-Z]{3,8}\b/.exec(message)) !== null) {
// 3-8 character uppercase alphanumeric string, containing at least one letter and one number
// examples:
// "5WGU8G"
// "Your code is: 5WGU8G"
// "CWGUG8"
// "CWGUG8 is your code"
// "7645W453"
code = m[0];
} else if ((m = /(^|code:|is:|\b)\s*(\d{3})-(\d{3})($|\s|\\R|\t|\b|\.|,)/i.exec(message)) !== null) {
// 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."
// and make sure it isn't a phone number
// doesn't match: <first digits>-<second digits>-<4 consecutive digits>
const first = m[2];
const second = m[3];
code = `${first}${second}`;
} else if ((m = /(code|is):?\s*(\d{3,8})($|\s|\\R|\t|\b|\.|,)/i.exec(originalMessage)) !== null) {
// "code" OR "is" followed by an optional ":" + optional whitespace, then 3-8 consecutive digits
// examples:
// "Please enter code 548 on Zocdoc."
code = m[2];
} else {
// console.log("no code found in message");
}
}
return code;
}