Macos 使用AppleScript读取iMessages

Macos 使用AppleScript读取iMessages,macos,applescript,imessage,Macos,Applescript,Imessage,我正在尝试编写一个脚本,它将遍历Messages应用程序中的所有聊天记录——我的目标是找到所有我没有回复的消息,并向我发送提醒 但我陷入了困境——我可以看到我有多少信息: tell application "Messages" to log (count of chats) 但我甚至不能从聊天中获得简单的属性;例如: tell application "Messages" to set x to started of first chat 给出错误:无法开始聊天1。“聊天1开始时的编号-17

我正在尝试编写一个脚本,它将遍历Messages应用程序中的所有聊天记录——我的目标是找到所有我没有回复的消息,并向我发送提醒

但我陷入了困境——我可以看到我有多少信息:

tell application "Messages" to log (count of chats)
但我甚至不能从聊天中获得简单的属性;例如:

tell application "Messages" to set x to started of first chat
给出错误:无法开始聊天1。“聊天1开始时的编号-1728”


你知道我哪里出错了吗?

我使用了你的
sqlite3
代码,并做了一些小的调整,然后创建了这个AppleScript,它会生成一个提醒列表(在提醒应用程序中)。唯一的缺点是发件人显示为电话号码或iMessage地址,并且不会被“转换”“使用您的通讯簿发送姓名。这可能是通过对联系人发出一些
tell
命令和搜索电话号码来实现的,但这超出了目前这项特殊工作的范围

    -- Run an SQL query on the messages database via shell script
    do shell script (["sqlite3 -line ~/Library/Messages/chat.db ", ¬
        "'SELECT MAX(date) lastMessageDate, h.id, text ", ¬
        "FROM message m INNER JOIN handle h ON h.ROWID=m.handle_id ", ¬
        "WHERE is_from_me = 0 GROUP BY h.ROWID' | egrep -io -e '\\w+ = .+'"] as text)
    
    set query_response to result
        --> lastMessageDate = %timestamp%
        --> id = %phone number or iMessage address%
        --> text = %message content%
        --> ...
    
    -- Groups all messages into a single item of an array
    -- Each occurence of  "lastMessageDate = " indicates 
    -- a new message and, hence, a new array item.
    set AppleScript's text item delimiters to "lastMessageDate = "
    set query_response to rest of text items of query_response
    
    -- Loop through the array organise each message
    repeat with message in query_response
        
        -- Split the array item up into its components
        set [receivedOn, sentFrom, textContent] ¬
            to [paragraph 1, ¬
            text 6 thru -1 of paragraph 2, ¬
            text 8 thru -1 of paragraph 3] of message
        
        -- Convert ReceivedOn to a value representing
        -- the number of days since 01/01/2001
        set receivedOn to receivedOn / (1.0E+9 * 86400)
        
        -- Convert ReceivedOn again to the date
        -- corresponding to the number of days since
        -- 01/01/2001, i.e. the date and time the message
        -- was sent
        set receivedOn to (date "Monday, 1 January 2001 at 00:00:00") + receivedOn * days
        
        -- Make a reminder
        tell application "Reminders"
            if not (exists (list named "Awaiting My Reply")) then ¬
                make new list with properties {name:"Awaiting My Reply"}
            
            tell list named "Awaiting My Reply"
                -- This creates a reminder with a past due date
                -- meaning you can sort the reminder list by due
                -- date to see who has been waiting longest
                -- for you to reply
                make new reminder with properties ¬
                    {name:"Reply To " & sentFrom, body:¬
                        textContent, due date:¬
                        receivedOn} ¬
                        
            end tell
        end tell
    end repeat
当然,如果您的任何文本消息碰巧包含短语“lastMessagedate=”,则上述脚本将无法正常工作。但是,这方面有一些变通方法,我尝试了由
sqlite3
返回的各种格式,包括列表、列和csv,所有这些格式都有其优缺点

附录:从匹配的电话号码中检索联系人详细信息 经过多次试验,我们成功地将上述脚本与通讯簿搜索相结合,以联系人姓名替换电话号码:

    -- Replace dbID with your address book's serial number
    -- (look in ~/Library/Application Support/AddressBook/Sources)
    property dbID : "377FDA5C-013A-430A-A964-9943C0B40137"
    property db : {Messages:"/Users/CK/Library/Messages/chat.db", Addresses:"/Users/CK/Library/Application Support/AddressBook/Sources/" & dbID & "/AddressBook-v22.abcddb"}

    -- Run an SQL query on the messages database via shell script
    set Messages to do shell script (["sqlite3 -line ", ¬
        quoted form of (Messages in db) as text, ¬
        " 'SELECT MAX(date) lastMessageDate, h.id, text", ¬
        " FROM message m INNER JOIN handle h ON h.ROWID=m.handle_id", ¬
        " WHERE is_from_me = 0 GROUP BY h.ROWID'", ¬
        " | egrep -io -e '\\w+ = .+'"] as text)
    --> lastMessageDate = %timestamp%
    --> id = %phone number or iMessage address%
    --> text = %message content%
    --> ...
    
    set PhoneNumbers to do shell script (["sqlite3 -list -separator : ", ¬
        quoted form of (Addresses in db) as text, ¬
        " 'select r.ZFIRSTNAME, r.ZLASTNAME, p.ZFULLNUMBER", ¬
        " from ZABCDRECORD as r, ZABCDPHONENUMBER as p", ¬
        " WHERE p.ZOWNER=r.Z_PK'", ¬
        " | tr -c -d '[:alnum:]:[:cntrl:]:[:space:]:+#*'"] as text)
    --> %first name%:%last name:%phone number%
    --> ...
    
    set NamesAndNumbers to paragraphs of PhoneNumbers
    set AppleScript's text item delimiters to ":"
    tell NamesAndNumbers to ¬
        repeat with n from 1 to count it
            set its item n to ¬
                {firstname:text item 1, lastname:text item 2, phone:text item 3} ¬
                    of its item n
        end repeat
    
    -- Groups all messages into a single item of an array
    -- Each occurence of  "lastMessageDate = " indicates 
    -- a new message and, hence, a new array item.
    set AppleScript's text item delimiters to "lastMessageDate = "
    set Messages to rest of text items of Messages
    
    -- Loop through the array organise each message
    repeat with message in Messages
        
        -- Split the array item up into its components
        set [receivedOn, sentFrom, textContent] ¬
            to [paragraph 1, ¬
            text 6 thru -1 of paragraph 2, ¬
            text 8 thru -1 of paragraph 3] of message
        
        -- Convert ReceivedOn to a value representing
        -- the number of days since 01/01/2001
        set receivedOn to receivedOn / (1.0E+9 * 86400)
        
        -- Convert ReceivedOn again to the date
        -- corresponding to the number of days since
        -- 01/01/2001, i.e. the date and time the message
        -- was sent
        set receivedOn to (date "Monday, 1 January 2001 at 00:00:00") + receivedOn * days
        
        -- Keep the "+" delimiter but replace the "+44" with
        -- your own country's intl dialling code, e.g. {"+1", "+"}
        -- as this may or may not prefix a phone number
        set AppleScript's text item delimiters to {"+44", "+"}
        set sentFrom to last text item of sentFrom
        
        -- Find the name to whom the phone number belongs
        ignoring white space
            set match to ""
            repeat with person in NamesAndNumbers
                if the person's phone contains sentFrom then
                    set match to contents of the person
                    exit repeat
                end if
            end repeat
        end ignoring
        
        set AppleScript's text item delimiters to space
        if match is not "" then set sentFrom to the contents of ¬
            {firstname, lastname} of match as text
        
        -- Make a reminder
        tell application "Reminders"
            if not (exists (list named "Awaiting My Reply")) then ¬
                make new list with properties {name:"Awaiting My Reply"}
            
            tell list named "Awaiting My Reply"
                -- This creates a reminder with a past due date
                -- meaning you can sort the reminder list by due
                -- date to see who has been waiting longest
                -- for you to reply
                make new reminder with properties ¬
                    {name:"Reply To " & sentFrom, body:¬
                        textContent, due date:¬
                        receivedOn} ¬
                        
            end tell
        end tell
    end repeat

决定放弃这个,直接用shell脚本访问数据库。如果它对任何人都有帮助的话,我想这可以完成任务:
sqlite3-line~/Library/Messages/chat.db“选择m.ROWID,text,MAX(date)lastMessageDate,is_from_me,h.id from message m INNER JOIN handle h ON h.ROWID=m.handle_id GROUP BY h.ROWID”
实际上,AppleScript目前对消息没有太多的作用。给定从SQL查询返回的数据,您具体希望如何处理它?你是想要一个包含每个电话号码/I消息地址和他们最后回复的列表,还是想对这个列表做些什么?这非常感谢-链接到联系人是下一个大挑战:)我想类似于
告诉应用程序“Contacts”这样的事情,以获得phone 1值包含“%phone number%”的每个人的姓名
可能有效,但它在匹配方式上非常挑剔。我可以让它匹配个人,如果我已经知道了号码的格式,等等。否则,我可以使用一个非常广泛的号码匹配,只有3位左右。我已经使用另一个SQL查询访问联系人数据库,并从那里检索电话号码。我必须找到我的相关联系人数据库的存储位置,并在
~/Library/Application\Support/AddressBook/Sources/%SERIALNUMBER%/AddressBook-v22.abcddb
找到它。检索姓名和电话号码的相关
sqlite3
查询是
从ZabcRecord中选择r.ZFIRSTNAME、r.ZLASTNAME、p.ZFULLNUMBER作为r,ZABCDPHONENUMBER作为p,其中p.ZOWNER=r.Z_PK
,我发现将其作为
列表返回最有用。我将制作一个AppleScript,将其与上面的内容集成,并在完成后更新我的答案。完成!正在为我的上述答案添加附录。将receivedOn设置为(日期“2001年1月1日星期一00:00:00”)+receivedOn*天时出错。
:“无效日期和时间日期2001年1月1日星期一00:00:00。”