Windows版iMessage:永远见不到曙光的爱的劳动(2018)

2020-06-04 10:25:41

一年半前,我注意到了越来越多的警告信号,这些迹象表明MacOS(née OS X)作为开发人员和真正的计算机爱好者的平台即将消亡,于是开始尝试寻找一个新的生态系统。幸运的是,大约在同一时间,Windows10的更新与WSL的持续出色结合在一起,1使其成为一个可行的重新考虑的选择。

虽然由于我的工作性质,Windows一直是我首选的主要开发平台,但我已经习惯了使用各种应用程序,不得不寻找替代方案,其中最主要的是iMessage。作为一个从来没有接受过手机热潮的人,我非常怀念在PC上发短信(或“iMessaging”)的能力,并开始绝对鄙视不得不从口袋里拖出手机,从狭小的显示屏上发短信,不断地与自动错误作斗争,用打字和拼写错误来让自己难堪。我试着切换到其他平台,包括WhatsApp、Skype和FB Messenger;但没有什么能与iMessage的“短信向后兼容”方法的纯粹简单性和绝对天才相提并论,这种方法将iPhone到iPhone的通信升级到iMessage,同时透明地退回到iMessage或MMS,而iMessage不是iMessage的选项。

在某一时刻,我对没有iMessage的生活感到沮丧,以至于我正在考虑用我的Precision 5520换一台Emoji MacBook“Pro”的危险想法,这时我决定自己解决问题,开始对iMessage进行反向工程,想知道用我的旧15英寸rMBP作为iMessage网关会有多难。我当然不是第一个有这种想法的人,自从iMessage首次作为iPhone独家发布以来的近10年里,在线搜索结果中充斥着这种失败或放弃的尝试的垃圾和垃圾。

iMessage并不是一个完全开放的平台,苹果已经在相当程度上模糊了其内部工作原理的细节。虽然随着时间的推移,协议的某些部分已经被反向工程和文档记录,但实际实现仍在继续增长和变化,以包含更多功能并增强隐私和安全性。绝对没有批准的API可以通过编程方式与iMessage接口(这有助于遏制平台上垃圾邮件发送者的扩张),而且苹果实际上已经在某些未经记录的程度上偷偷地阻止了iMessage的使用,即使是在看似真正的OS X/MacOS安装上也是如此。2个。

与WhatsApp和Facebook Messenger等基于网络的平台不同,这些平台在各种不同的硬件和软件组合上运行相当标准的RESTful服务,使它们更易于复制和更容易进行反向工程,而iMessage具有在非常小的设备子集上运行的独特战略优势,并且是以本地代码实现的,这使得拦截变得更加困难。3苹果对安全的重新关注也使得搭乘iMessage网络变得更加困难,很明显,试图在Windows上创建独立的iMessage客户端,而不通过iPhone或Mac作为iMessage代理,充其量也是极其困难和非常脆弱的-但主要是浪费时间。

一开始,最好的选择似乎是专注于向AppleScript公开的iMessage功能的(极其有限的)外围区域,这可以用来至少实现一些基本的消息转发功能-尽管有些笨拙和大量的猜测。在Google/InternetArchive缓存的很久以前的网页副本中进行了大量的试错和挖掘,在将这种方法扩展到支持对现有聊天的有限传出消息方面取得了一些成功,但很快就变得明显起来,苹果已经不遗余力地相当故意和非常秘密地实现了AppleScript方法提供的有限功能的收支平衡。

对依赖AppleScript发送和接收iMessage所提供的残缺功能感到失望,我决定尝试直接与相同的库iChat/Messages.app接口,这是在我的rMBP上安装OS X Mountain Lion(第一个以iMessage支持为特色的OS X,在故意通过AppleScript残废iMessage之前),并在可预见的未来接受运行iMessage之前的最后一次尝试。虽然我从来没有写过超过几行Objective C的代码(当时主要是与Carbon接口的C代码),但我知道iOS开发者多年来一直通过使用专供第三方苹果应用程序使用的私有框架来规避App Store的某些限制,并假设像iMessage这样复杂的东西可以在移动和桌面平台上使用,并在MacOS和iOS中大量集成,肯定会有一个暴露其功能的框架。当有一天

例如,几乎没有花任何时间就在Messages.app使用的一个私有框架中找到了一个基本API,这似乎正是我要寻找的:一个函数,它可以将NSString发送到另一个NSString指定的地址-听起来很完美!兴奋得头晕目眩,几乎不敢相信事情会这么简单,我试了试,决定发短信给我的VoIP工作电话号码。几秒钟后,我的通知应用程序响起,让我知道一条传入的SMS已经从我那里传给了我,它是用XCode以编程方式生成的!然而,当我试图给自己发送一条iMessage,从我的电话号码到与iMessage相关的电子邮件地址时,我可能有的任何关于这件事实际上已经结束并很快就结束的想法都崩溃了。

事实证明,我第一次很幸运,因为我选择了一个短信收件人,没有iMessage支持。无论我在MacBook上通过这个API发送的地址是什么,都会导致向相关地址/号码发出一条短信,不管是有效的还是无效的。这也包括电子邮件地址,因为我在通过电子邮件“成功”接收到来自自己的消息时发现,MacOS对我的API调用的响应是生成一条以我的iCloud电子邮件为目的地地址的文本消息,而Verizon的回应是使用他们的SMS到电子邮件网关生成一封电子邮件,并慷慨地将其发送给我。

其他的尝试导致了完全相反的体验。我发现,MacOS内部通过SMS://ID或iMessage://目的地前缀来确定在哪个网络上发送消息(奇怪的是,即使在特定API调用中指定onService:@";iMessage&34;),但尽我所能地尝试,我无法想出一种方法来事先确定给定的电话号码是否在iMessage网络上-至少,不是使用我通过转储MessagesKit和其他API发现的那些API来确定是否在iMessage网络上-至少,不是使用我通过转储MessagesKit和其他API发现的那些API来确定是否在iMessage网络上发送消息(奇怪的是,即使在特定的API调用中指定了onService:@";iMessage";)。

有一次,我以为我被打败了。我能够使用私有消息传递API在Messages.app中创建(但重要的是不发送)新的对话;然后我可以从另一个API利用它来确定目的地地址的性质,这取决于iMessage识别(通过远程ping)发送到目标地址的消息是SMS还是iMessage这一事实,只要您键入地址,在编写或发送任何消息之前(这就是它决定联系人姓名是以绿色还是蓝色显示的方式,取决于是以绿色还是蓝色显示,取决于是以绿色还是蓝色显示联系人的姓名,取决于是以绿色还是蓝色显示联系人的姓名,取决于是以绿色还是蓝色显示联系人的姓名,取决于是以绿色还是蓝色显示该联系人的姓名,具体取决于您在编写或发送任何消息之前,只要键入地址,iMessage就会以SMS或iMessage的形式发送出去。

我匆忙发了几条信息,重复了我记住的、后来被鄙视的例行公事,测试了以下每一种单一收件人的情景:

通过iMessage发送给我自己的消息(从与我的iMessage帐户相关联的一个电子邮件地址到与同一帐户相关联的另一个电子邮件地址,这是给自己发送便条的一个诀窍),

将消息发送给我已知但iMessage 5不知道的号码,以成为仅限SMS的目标,

我屏住呼吸,等待响亮的ping声让我知道每台目标设备都收到了这条消息,不敢相信它会这样-然而,在这一点上,“太好了以至于不可能是真的”这句话不再有任何意义。毕竟,自从我第一次踏上这段旅程以来,已经有很多很多个月了。

2015年秋天,我的第一次努力是保留iMessage,同时将MacOS从我的生活中剔除-这是一项足够简单的工作-我从未想过最终会有这样的生活,在六个多月的断断续续的尝试中,跨越了许多代完全不同的机制,在这期间,我几乎以一种两极的方式摇摆,在认为我终于开始弄清楚事情的满足感带来的极端兴奋和当不可避免的意识破灭时强烈的绝望和绝望之间摇摆。对抗库比蒂诺巨人无穷无尽的资源和力量。

第一次尝试依赖于通过MacOS上的Messages.app首选项(在MacOS更新中反复中断)中公开的基本“接收到消息时执行AppleScript”和“发送消息时执行AppleScript”挂钩触发的AppleScript,我将这些钩子编码为以发送方、接收方和正文作为命令行参数来执行一个简单的实用程序。该实用程序反过来会给我自己写一封电子邮件,几毫秒后,我的Windows PC会发出来自[email protected]的通知气泡,让我知道我的妻子已经在下雪天安全到达目的地,或者某某迟到了,我甚至忘了正在开会。

虽然它解决了我最紧迫的问题(即,我如何快速浏览收到的信息或参考一条过去的信息,而不必处理将上下文切换到移动设备的繁琐任务),更不用说这意味着我不再需要处理可怕的屏幕键盘、狭窄的显示屏,或者使用iPhone带来的可怜的搜索借口,但这只是不够的。

我觉得,如果我要在智力上诚实地对待自己,只读传入消息通知api虽然不错,但实际上只是一个拙劣的模仿,应该被迅速而仁慈地扼杀,而不是继续炫耀自己是[email protected],而它实际上应该被命名为a.pathetic.attempt.at.ditching.osx.by.scraping.imessage@neosmart.net.。这不是骄傲,也不是真正的成就,只是提醒我,苹果是多么成功地将我与他们的平台捆绑在一起--也是对2010年的一次倒退,当时我终于不情愿地同意与我的黑莓Curve 8900分道扬镳,黑莓Curve 8900拥有漂亮、精确的键盘和可靠的BBM,用于iPhone4上华丽的、死气沉沉的视网膜显示屏。

就在那时,我开始了第一次真正取代iMessage的“真正”尝试。一旦我确定有可能让某种形式的iMessage代理同时用于传入和传出消息-即使后者会非常有限和受限-我就开始同时开发一个比我的电子邮件收件箱更好的用户体验。第一步是将现有的基于SMTP的消息转发代码移植到能够更好地反映对话性质、允许双向通信并提供多重呈现的接口,以便我可以在工作和家里的许多PC、笔记本电脑和工作站上使用它。

我很羞愧地说,我的第一次尝试是用电子邮件换成了Slake。为了保护自己,我部署了几个开源XMPP服务器,并尝试了各种可用的多呈现扩展(实际上,XMPP服务器的选择取决于可用的多呈现插件/集成的质量),但最终对XMPP服务器提供的功能(消息历史记录、多呈现等)和客户端提供的功能(高dpi支持、多呈现和消息历史兼容性、附件支持等)感到非常失望。Slake提供的功能非常诱人:简单易用的API、搜索、带有阅读消息同步功能的多在线状态,以及在Windows10上运行的客户端(尽管丑陋、臃肿和缓慢),并至少利用了它的一些功能(高dpi支持、通知祝酒词等)。

那个实验没有持续太久。它开始于从一开始就折磨着我的不适,随着我考虑到这样一个事实,我刚刚使用了或多或少相当安全的平台,并-故意和知情的-与Slake分享了我的联系人、我的对话和我的私人事务,一家我对其一无所知、不可能信任的公司,这种不适只会随着每一条收到的消息而增长。随着我与客户互动的增加,我对继续依赖Slake作为我未来的“iMessage for Windows”前端的保留意见只会变得更加强烈。与Slake-the-Electron-app带来的臃肿和独特的“神秘谷”截然不同的是,Slake-the-Electron-app作为捆绑在资源密集型独立Chrome实例中的单页Web应用程序,以在硅谷和全球范围内降低较小的PC和工作站而闻名

如果我不能解决iMessage API的剩余问题,那么我的Slake问题就无关紧要了。Windows10的原生iMessage客户端如果只能发送给我以前发过信息的人--而且它只能接收iMessage,而不是短信!9对我来说,它又回到了绘图板上。

我们放弃了服务器端的事情,尝试了一种似乎有效的方法,即使是未知的联系人也能正确区分iMessage和SMS,它们可以正确地发送到号码和电子邮件地址,而不需要用户手动干预。嗯,长话短说,那个特别的测试没有起作用。我想是的,Messages.app告诉我是的,看着我的苹果手机似乎证实了它的存在--所有的消息都是以各种深浅不一的绿色和蓝色…发出的。但他们从未真正接触过。不知何故,我欺骗了我所有的iDevices,以为新的对话已经开始,信息已经发送,但这都是史蒂夫·乔布斯(Steve Jobs)的鬼魂对我玩的一个残酷的恶作剧,他兴高采烈地窃笑着,很高兴在他的方向上设置的路障成功地阻碍了我抛弃MacOS但带走iMessage的努力。

它花了更多的工作和更多的不眠之夜,但实际上我终于想出了如何让传出的消息发挥作用。实际上,这纯粹是运气不佳。我草率地复制并粘贴了涉及私有框架公开的函数的一个测试,然后运行make test。我刚按下Enter键就意识到我忘了改正其中一个参数。只是我的手机在震动,固定在桌面一角的原本空白的终端窗口突然不可能地显示了我发送的信息的内容。这没有意义,但话又说回来,它显然不是有意的。它起作用了。

在这一点上,我受到成功的鼓舞,并决定不再陷入另一个兔子洞或浪费我的时间实现一个成熟的iMessage客户端,但后来发现iMessage代理服务器中缺少一个关键功能,我决定完全充实该API。将我在过去六个月中发现或发明的各种组件拼凑在一起,我缓慢但可靠地构建了iMessage代理API。当然,除了一个关键组件之外,所有的组件都是如此。

我不确定是否仅仅是幸运之神特别慷慨地扼杀了开发人员与iMessage接口的羽翼未丰的尝试,或者是特别有创意的白胡子们躲在他们的小房间里,无视世界,一想到一个无辜的开发人员只是为了满足智力和好奇心而试图挠痒痒,特别需要实现功能对等的完美,就大声自言自语,但有一项iOS消息传递功能,无论我追逐了多久,或者是哪种方法,我都没有做到。群发消息,尽管有与向单个人发送消息的API完美并行的API,但这是一项特别繁重的任务,我就是不能让它工作。

在这一点上,我们遇到了之前暗示过的最糟糕的死胡同和留在原地的诱杀装置。在此之前,正如我所提到的,我遇到过许多iMessaging行为的“边缘案例”:没有发送的消息,声称失败但却导致混乱回复的消息:“你为什么给我发了40次这条消息?”因为我的脚本响应了特定的(False!)。通过重试传输而产生的错误代码,以及仅在试图强迫和诱骗非第三方使用的API执行您的命令时才会出现的其他那种性质的错误。但这是一件丑陋的小玩意儿,而且不是很好的形式--要是有人能让我投诉就好了。

你看,除了我碰到的一个案子。

..