基于Flutter实现短信验证码监控与转发
作者:代码轨迹
1. 前言
前段时间,我基于deepseek制作了一个基于小红书的自动推文生成发送工作流。然而,先前制作的windows端的工作流到小红书发布时显得异常繁琐,原先的思路是在手机接收到验证码后进入系统进行人为输入,这显然太麻烦了。同时,这一问题当部署到linux服务器上时显得尤为突出,这与自动化的理念显然有些背道而驰。因此,我决定基于flutter制作一个验证码提取转发应用,将手机短信验证码提取出来,通过http接口转发给工作流,从而实现自动化的工作流。
2.开发环境
- IDE:VSCode
- 语言:Dart 3.7.0
- 框架:Flutter 3.29.0
3. 实现思路
3.1 验证码提取
flutter中存在大量不错的第三方短信处理库,例如flutter_sms_inbox, sms_v2, sms_receiver等,但经过测试,许多库在当前开发环境下存在许多问题,因此我最终选择了sms_advanced库进行短信处理。
sms_advanced提供了querySms()这样一个方法,这个方法可以根据条件进行短信查询。以下是方法源码:
/// Query a list of SMS Future<List<SmsMessage>> querySms({ int? start, int? count, String? address, int? threadId, List<SmsQueryKind> kinds = const [SmsQueryKind.Inbox], bool sort = true}) async { List<SmsMessage> result = []; for (var kind in kinds) { result.addAll(await _querySmsWrapper( start: start, count: count, address: address, threadId: threadId, kind: kind, )); } if (sort == true) { result.sort((a, b) => a.compareTo(b)); } return (result); }
可以看到,querySms()方法可以接受多个参数,其中address参数可以指定短信发送者的手机号,kinds参数默认为SmsQueryKind.Inbox,即从收件箱获取短信,从而实现短信提取。然后使用正则表达式对短信内容进行匹配,提取出验证码。
if (messages.isNotEmpty) { // 获取第一条短信 SmsMessage firstMessage = messages.first; String? messageBody = firstMessage.body; // 使用正则表达式匹配验证码,假设验证码是 6 位数字 RegExp regex = RegExp(r'\d{6}'); Match? match = regex.firstMatch(messageBody!); if (match != null) { String smsCode = match.group(0)!; // 发送验证码到 API result = await _sendCodeToAPI(smsCode); } else { result = '未在短信中找到验证码'; } } else { result = '未找到短信'; }
但后面我发现小红书的验证码发送者手机号并非固定,因此我选择制作一个多条件筛选器。在条件筛选中,我选择先根据手机号做一次短信筛选,如果没有找到,则根据短信内容做一次筛选,如果还是没有找到,则返回未找到短信。这样用户就可以在仅知道验证码发送应用名称的情况下,不填写发送者手机号,获取到短信并提取到验证码。实现代码如下:
SmsQuery query = SmsQuery(); List<SmsMessage>? messages = await query.querySms( address: _phoneNumber, kinds: [SmsQueryKind.Inbox], ); // 获取收件箱中的短信 if (messages.isEmpty) { List<SmsMessage>? messages = await query.querySms( kinds: [SmsQueryKind.Inbox], ); // 根据条件二进行查询 for (SmsMessage message in messages) { if (message.body?.contains(_targetApp) ?? false) { final code = _extractCode(message.body); if (code != null) { return await _sendCodeToAPI(code); } } } }
3.2 验证码转发
验证码转发是将提取到的验证码通过http接口转发给工作流。这里我选择使用http库进行http请求,实现代码如下:
Future<String?> _sendCodeToAPI(String code) async { try { final response = await http.post( Uri.parse(_apiEndpoint), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'code': code}), ); if (response.statusCode != 200) { return ('Failed to send code: ${response.statusCode}'); } else { return 'Successfully Code sent '; } } catch (e) { return ('Error sending code: $e'); } }
3.3 线程通信
由于验证码提取是一个耗时操作,因此我选择将其放在一个子线程中执行,以避免阻塞主线程。这里我选择使用flutter的Isolate进行线程通信。同时,为了更新监控状态并控制监控开始和停止,设计了两个Port,分别是mainpPort和isolatePort。mainpPort用于向接收子线程的监控状态消息,实现监控状态的实时更新;isolatePort用于接收主线程发来的启停信息,当点击停止监控后,由父线程告知子线程停止作业。实现代码如下:
MainPort:
// 在主isolate的接收端口监听中添加状态更新 void _initReceivePort() { _receivePort = ReceivePort(); _receivePort.listen((message) { if (message is String) { if (message == 'isolate_stopped') { // 处理isolate退出通知 if (mounted) { setState(() { _isolate = null; isMonitoring = false; buttonText = '开始监控'; }); } } else { setState(() => response = message); if(message == 'Successfully Code sent') { _stopIsolate(); } } } else if (message is SendPort) { _isolateSendPort = message; } }); }
IsolatePort:
static void _monitorSmsInBackground(List<dynamic> args) async { final rootIsolateToken = args[4] as RootIsolateToken; BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken); final apiEndpoint = args[0] as String; final targetApp = args[1] as String; final phoneNumber = args[2] as String; final mainSendPort = args[3] as SendPort; final smsHandler = SMSHandler(apiEndpoint, targetApp, phoneNumber); final controlPort = ReceivePort(); mainSendPort.send(controlPort.sendPort); final stopCompleter = Completer<void>(); controlPort.listen((message) { if (message == 'stop') { stopCompleter.complete(); } }); try { while (!stopCompleter.isCompleted) { final value = await smsHandler.initSMSListener().timeout( const Duration(seconds: 1), onTimeout: () => null, ); print(value); mainSendPort.send(value); if (stopCompleter.isCompleted) break; } } finally { controlPort.close(); mainSendPort.send('isolate_stopped'); // 添加退出通知 } }
StopIsolate:
// 修改 _stopIsolate 方法,仅发送停止信号,不强制终止Isolate void _stopIsolate() { if (_isolate != null) { _isolateSendPort?.send('stop'); } }
4. 总结
总体来说,整体项目还是挺简单的。主要就是利用flutter的插件进行短信的监听,然后通过正则表达式提取验证码,最后通过http接口将验证码发送给工作流。但因为初次学习flutter,许多地方没有做详细的优化,仅仅实现了整体功能。工程代码放在github上,有兴趣的可以看看:verify_code_app
到此这篇关于基于Flutter实现短信验证码监控与转发的文章就介绍到这了,更多相关Flutter短信验证码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!