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-2faimport "@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:18sender: string; // e.g. amazon.de or +49123456789service: string; // e.g. SMStext: 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 */`selectmessage.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.textfrom messageleft join chat_message_join on chat_message_join.message_id = message.ROWIDleft join chat on chat.ROWID = chat_message_join.chat_idleft join handle on message.handle_id = handle.ROWIDwhere message.is_from_me = 0and message.text is not nulland length(message.text) > 0anddatetime(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 codebaseQuery = /* 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 textbaseQuery = /* sql */`${baseQuery} and message.text like '%${qs}%'`;}return `${baseQuery} \norder by message.date desc limit 100`.trim();}export function extractCode(original: string) {// remove URLsconst 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 firstif ((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 regexconst 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;}