自动加入腾讯会议的摸鱼实践

背景

学校要在假期上网课并进行考勤,为了防止赶不上时间(也为了摸鱼),尝试摸索下自动加入腾讯会议的解决方案。

唤起腾讯会议

网课开始的流程是这样的:教师端通过腾讯会议企业版的接口申请会议,企业版自动创建会议号和带有企业内部身份认证的加入链接,并将加入链接分发至个人的账户。学生点击加入链接,自动以企业内身份进入会议,同时腾讯会议会记录加入时间作为考勤依据。
加入链接是每人独立动态生成的,我们学校的生成方式是在URL参数中夹带每个人的鉴权JWT和会议号等信息,当链接被访问后,腾讯会议的服务器根据JWT参数请求学校的鉴权网关兑换得到企业内部身份信息,生成一个每用户独立标识符UserCode并作为URL参数传递到最后的落地页,落地页尝试拉起腾讯会议App或小程序并传递参数。很幸运的是,整个加入链接就包含了完整的鉴权信息,不论是否登录态,只需要访问加入链接就可以进入会议(不需要再折腾学校的登录网关了),而我们只需要拿到UserCode和其他会议信息传递给腾讯会议客户端就可以了。
落地页是个SPA应用,接收一个邀请链接的字符串,并通过这个字符串兑换得到会议号发起人等详细信息(这个过程属实有些折腾,考虑到学校的平台本身提供了会议号,我就懒得分析邀请链接字符串是怎么调用API兑换的了)。在获得会议号之后,在桌面端尝试通过自定义URL-Scheme(wemeet:)拉起腾讯会议App并传递会议号参数,当然对于企业版来讲,还要额外传递一个企业内部身份的UserCode.整个URL格式如下:

wemeet://page/inmeeting?meeting_code={会议号}&token=&user_code={UserCode}

在macOS上,我们在终端中输入open wemeet://...即可,而在Windows上,我们只需要在CMD中输入 start wemeet://....就可以唤醒腾讯会议App并传参。腾讯会议会自动加入会议,我们也就自动完成了考勤。
::好耶!::
我们学校的平台说明中还提到,如果某些同学无法使用企业用户身份加入会议,则必须手动填写会议号和会议密码来加入会议。但是在上课过程中,可能是由于教师申请会议的时候配置失误,导致部分企业用户也必须填写密码。而这个密码在一般流程中不会出现,必须教师手动下发…..所以,好几次我虽然成功唤醒了腾讯会议,但是却卡在如下界面:


坏耶!

自动填写密码

自动填写密码最好的方式当然是用URL参数直接传入,但是我尝试了下,并没有找到相关的参数,因此只能尝试使用UI自动化控制的方式来完成密码填写。
目前我没有成功在Windows上实现这个过程,而在macOS上使用AppleScript成功实现。
在Windows上实现UI自动化主要主要使用Accessibility特性实现,通过尝试模拟一个残障人士读屏器的方式来获取桌面窗体的控件结构,具体的API包含在UI Automation相关的功能中。但是在Windows平台尝试操作腾讯会议的时候,我遇到了以下问题:

  • Accessibility真的很卡。MS认为这个功能实现代价比较昂贵(需要遍历每一个窗体的所有元素),因此将这个API设置为了运行时初始化。只有当有程序调用了相关API时,系统才会准备相关功能,适配的相关应用程序才会上报辅助功能事件(如Chrome在Windows中会向系统上报网页的详细结构和内容,以及用户的点击行为,供读屏器使用)。而实际上当我开启这个功能之后,我的电脑几乎完全卡住了…..
  • 腾讯会议的入会密码窗体做的非常垃圾,这个窗体似乎是完全自绘的,它在窗体树中并不是腾讯会议应用的子窗体,同时自定义的密码输入框没有任何Label,甚至上浮在父窗体之上
    总之,我放弃了Windows平台上实现这个功能的想法,而在macOS平台,得益于设计优秀的辅助功能API,我大概完成了整个过程。

    macOS自动化框架

    很遗憾,在macOS平台,我没有找到像Pyauto这么知名的自动化框架,因此只能尝试使用AppleScript脚本来完成控制功能。使用Xcode自带的Accessibility Inspector,我们可以获取到如下窗体结构。但是在AppleScript中,我们很难获得这么详尽的信息,更多只能通过text field 1button 2这种类型+序号的形式来定位元素。


在AppleScript自动化应用中,我们必须先调用“System Events”,所有的运行进程窗口都是它的子对象。此外还要注意在macOS 10.15以上的系统中,必须申请辅助功能权限才能调用以上API。
成功获取权限后,我们可以使用 tell +Type +Name的形式来展开调用,对于腾讯会议来讲,进程名称为”TencentMeeting”,主窗体名称为“腾讯会议”,密码输入窗格类型为表单,没有Label和名称,我们用Sheet 1来指代.然后我们会写出这样一个VB风格的代码.

tell application "System Events"
    tell process "TencentMeeting"
        repeat until window "腾讯会议" exists
        end repeat
        tell window "腾讯会议"
            delay 10
            if exists (sheet 1) then
                tell sheet 1
                    set value of text field 1 to "12341"
                    click button 3
                    delay 3
                    
                end tell
            end if
        end tell
    end tell
end tell

至于sheet 1中的控件是怎么猜到的,我们可以使用entire contents列出窗口的全部控件,但是这个功能通常用处不大,因为大部分控件还是以序号来标明的,控件的编号顺序一般是从上而下从左至右,我们可以结合有Label的控件来推断目标控件的序号。


最后,我们在文本框中填入密码,点击确定按钮,等待几秒,若表单未消失,则判断密码错误,并发出提醒。这里我使用了通知提醒和Say合成语音播放提醒,最终代码如下:

tell application "System Events"
    tell process "TencentMeeting"
        repeat until window "腾讯会议" exists
        end repeat
        tell window "腾讯会议"
            delay 10
            if exists (sheet 1) then
                tell sheet 1
                    set value of text field 1 to "12341"
                    click button 3
                    delay 3
                end tell
            end if
            if exists (sheet 1) then
                tell sheet 1
                    display notification "密码错误" with title "会议加入失败"
                    say "密码错误"
                end tell
            else
                display notification "加入成功" with title "会议加入"
                
            end if
        end tell
    end tell
end tell

我们也可以通过Python等其他脚本来调用AppleScript,实现自动唤醒腾讯会议并输入密码,如Pypi上的 applescript库,但是在使用时要注意AppleScript的权限来自于父进程,因此必须为父进程赋予辅助功能等权限。

Edit with markdown