背景:这段时间某位同事需要频繁登录我的账号,并且要验证手机验证码,每次都需要我手动将验证码转发给他,觉得非常的麻烦,便想看一下有没有什么能够解放双手的办法,然后发现了
AppleScript
这么一个非常好用的东西 :)
所需配置
- Mac 电脑:既然是使用
AppleScript
,那么一台MAC电脑自然是不可少的(macOS Catalina 10.15.5) - iPhone:如果消息类型是iMessage的话,则只需要MAC上登录苹果账号就可以,如果消息类型是SMS的,则需要利用苹果的生态,iphone 上的消息能够自动转发到MAC上(IOS 8 以后支持)。
AppleScript
什么是AppleScript?
AppleScript是用来编写运行于mac的脚本的,并且能够操作应用程序。苹果官方也要求开发mac上的软件需要留出能够共AppleScript能够操作的方法。利用AppleScript我们能非常方便实现一些平常工作中重复工作的脚本化,提升工作效率,避免重复劳动。
基础语法
AppleScript 的语法非常接近自然语言,几乎没有标点符号,语法不像其他语言那样严格
变量定义
-- 将百度网址赋值给变量url_str
set url_str to "https://www.baidu.com"
通知
-- 将百度网址赋值给变量url_str
set url_str to "https://www.baidu.com"
-- 通知
display notification "url set success"
操作应用
-- 将百度网址赋值给变量url_str
set url_str to "https://www.baidu.com"
-- 将bing网址赋值给变量url_str2
set url_str2 to "https://www.bing.com"
-- 通知
display notification "url set success"
-- 操作应用
tell application "Google Chrome"
-- 创建新窗口
set newWin to make new window
-- 获取新窗口tab
set theTab to get active tab of newWin
-- 操作tab
tell theTab
-- 设置tabURL
set URL of theTab to url_str
-- 访问
go forward
-- 延迟3秒
delay 3
--重新设置tabURL
set URL of theTab to url_str2
go forward
end tell
end tell
字典
苹果官方会要求mac软件的开发提供能够操作应用软件的方法,可以通过字典查询能否由AppleScript操控软件
字典打开方式
Script Editer -> 打开词典 -> 选择想要操作的软件
chrome dictionary.
AppleScript Language Guide
了解 AppleScript 的基础语法是掌握 AppleScript 自动化的第一步,在对 AppleScript 有了一定的了解后,在日后发现自己有重复操作时,就可以先想想可以如何通过 AppleScript 解决它
问题解决
回到正题,因为对AppleScript
有过一定了解,并且也做过简单的测试,所以第一时间想到的就是能不能用自动话脚本来处理重复的短信转发工作(在这之前还从没用AppleScript解决过现实问题)
发送短信
首先需要的就是打开字典,因为Message本身就是苹果开发的,所以肯定会有丰富的API供我们使用的
通过翻找,我们能够基本定位我们发送短信需要用到的send方法,send方法中还需要我们明确一个buddy,继续查看buddy
查看我们的buddy,他是包含在一个service里面的,继续service ...
service 上层为application包含,因此我们可以书写我们的脚本了
tell application "Messages"
send "test" to buddy "+86199xxxx" of service "SMS"
end tell
非常简单的一段代码,可以测试发送了。在测试的过程中发现需要有和该号码发过消息的记录(即需要打开有一个窗口)
事件监听
在能够成功发送短信后,我们距离解放双手已经很近了,接下来我们只需要在接收到验证码信息的时候,调用我们的发送脚本将我们收到的消息转发出去就大功告成了:)
继续查看我们的字典,有一个message received
事件,看字面上的意思就能满足我们的需要,就是他了
很简单,我们只需要获取到我们需要的消息,以及发送给我们的人用来判断(毕竟不能把所有的消息都转发出去)
using terms from application "Messages"
on message received theMessage from theBuddy
-- 通知接收到消息
display notification "message"
-- 将Message App可见
set visible of window "Messages" to true
-- 将Message App置顶
set frontmost of window "Message" to true
end message received
end using terms from
事情到这里就结束了嘛?哪有那么简单,正所谓“天之道,没那么容易让你成功~”
经过不断的测试,发现这个脚本根本就没达到我的预期,没有通知也没有置顶,是我哪里写的有问题嘛?好嘛,知错要改,要虚心学习。百度~,嗯,查不到,害,谷歌~(个人习惯,先百度,当发现查不到想要的信息或者百度上全是同一篇文章抄来抄去的时候才谷歌,毕竟谷歌是不被允许,我可是乖孩子)
不断的变换关键词及描述,用我的散装英语终于找到了一些解决办法
发现我的脚本写的是没有问题的,只是还少一些操作步骤。例如
copy the script into a new AppleScript Editor document and save it in ~/Library/Application Scripts/com.apple.iChat/
Then activate it by selecting it in Messages -> Preferences -> General -> AppleScript Handler:
以及
AppleScript file copied to ~/Library/Scripts/Messages by Messages, it started to work.
不同的路径,但是好像都需要拷贝,于是分别考到目录下,但是一直没有找到Message -> Preference —> General(发现我的MAC好像和广大网友不太一样啊,买到了假货了吧)总之还是不成功,继续google...
终于,原来Message.app Message Received Event Handler
在macOS High Sierra 10.13.4
版本被移除,并且开通了Business Chat
emmmmmmm.....,害~
分析数据库
事件的方式走不通了,只能换一种方式了。还可以用什么方式呢,于是我想到了一种可能,我的这些消息,哪怕时间再久远都能查到,他是怎么存放的呢。
OK,有思路就有方向,最后不断的搜索发现是存在目录/Users/herbert/Library/Messages
下 的chat.db
文件中
用sqlite打开看一下
sqlite3 /Users/herbert/Library/Messages/chat.db
查看表结构及表内部分数据
.schema message
select * from message order by ROWID desc limit 5;
最终根据梳理的表结构关系,及传入我需要查询的号码整理SQL
-- 查询一分钟内来自指定号码xxxxxx_number的短信
SELECT text, handle_id,date,datetime(date/1000000000 + strftime('%s','2001-01-01'), 'unixepoch', 'localtime') as date_utc FROM message T1 INNER JOIN chat_message_join T2 ON T1.ROWID=T2.message_id INNER JOIN chat T3 ON T2.chat_id=T3.ROWID AND T3.chat_identifier = 'xxxxxx_number' where datetime(date/1000000000 + strftime('%s','2001-01-01'), 'unixepoch', 'localtime') > datetime('now','localtime','-1 minute') ORDER BY T1.date;
在查询数据的时候还发现了一个不常见的日期存储(我之前没有见过)一个18位的时间数字
处理方式:datetime(date/1000000000 + 978307200,'unixepoch','localtime')
.datetime(date/1000000000+ strftime('%s','2001-01-01'),'unixepoch','localtime')
编写脚本
我们已经拿到我们需要的数据了,接下来就只需要每个一分钟取一次数据,如果能够去到数据,那么调用我们发送短信的脚本就OK了
编写脚本auto_forward.sh
#/bin/sh
phone="+86xxxx"
call_apple()
{
while read line
do
osascript /Users/herbert/Documents/AppleScript/ForwardMessage/SendMessage.scpt $line $phone
done
}
export -f call_apple
sqlite3 /Users/herbert/Library/Messages/chat.db '.read /Users/herbert/Documents/AppleScript/ForwardMessage/select.txt' | call_apple
将我们的sql存在单独的文件里,方便修改select.txt
SELECT text FROM message T1 INNER JOIN chat_message_join T2 ON T1.ROWID=T2.message_id INNER JOIN chat T3 ON T2.chat_id=T3.ROWID AND T3.chat_identifier = 'from_number' where datetime(date/1000000000 + strftime('%s','2001-01-01'), 'unixepoch', 'localtime') > datetime('now','localtime','-1 minute') ORDER BY T1.date;
SendMessage.scpt
on run argv
set msgContent to item 1 of argv
set phone to item 2 of argv
sendMsg(msgContent, phone)
end run
on sendMsg(msgContent, phone)
tell application "Messages"
send msgContent to buddy phone of service "SMS"
end tell
end sendMsg
万事具备,只需要加入crontab中就行了
crontab -e
*/1 * * * * sh /Users/herbert/Documents/AppleScript/ForwardMessage/auto_forward.sh
此处在测试的时候还是没有达到预期,但是单独执行脚本的时候能够成功,推测是cron的问题,于是查看日志
vim /var/mail/herbert
果然发现问题sh: /Users/herbert/Documents/test/demo.sh: Operation not permitted
此问题会在Mojave 10.14, Catalina 10.15 以及后续版本出现
解决方法:系统偏好设置 -> 安全性与隐私 -> 隐私 -> 完全磁盘访问权限 -> 添加cron