----------------------------------------------------- lib_ver = "2026-05-03" ----------------------------------------------------- LuaPath = "/var/mobile/Media/1ferver/lua/scripts/" --脚本存放目录 ResPath = "/var/mobile/Media/1ferver/res/" --资源存放目录 LogPath = "/var/mobile/Media/1ferver/log/" --日志存放目录 LibPath = "/var/mobile/Media/1ferver/lib/" --插件存放目录 CfgPath = "/var/mobile/Media/1ferver/uicfg/" --配置目录 DataPath = "/var/mobile/Media/1ferver/tessdata/" --字库存放目录 -----------------------------------------------------基本函数 function _exit() --结束脚本 os.exit() end function _restart() --重启脚本 os.restart() end function _tabletostring(_table) --table转换成文本 return table.deep_print(_table) end -----------------------------------------------------系统模块 (sys) --_text:文字内容 --_ms:时间(毫秒/秒) --_log:日志内容 --_buttonTextA:按钮1文字内容 --_buttonTextB:按钮2文字内容 --_buttonTextB:按钮3文字内容 --_isEnter:输入完成是否按回车(true/false) --_numA:开始数值 --_numB:结尾数值 function _nlog(_log) --输出标准系统日志 if islog == true then sys.log(_log) end end function _toast(_text) --显示提示文字 sys.toast(_text, device.front_orien()) end function _alert(_text, _ms, _buttonTextA, _buttonTextB, _buttonTextC) --弹出系统提示 local choice = sys.alert(_text, _ms, '你的选择', _buttonTextA, _buttonTextB, _buttonTextC) return choice end function _inputText(_text, _isEnter, _ms, _log) --输入文字 sys.input_text(_text, _isEnter) _msleep(_ms) _nlog(_log) end function _msleep(_ms) --! 毫秒级延迟 sys.msleep(_ms) end function _random(_numA, _numB) --产生一个随机数 math.randomseed(sys.rnd()) --初始化随机因子为一个真随机数 local r = math.random(_numA, _numB) --产生一个范围的随机数 return r end -----------------------------------------------------屏幕模块 (screen) --_array:坐标/颜色(单/数组) --_dim:相似度 --_color:颜色 --_posandcolors:判断数组 function _getColor(_x, _y) --获取屏幕上某点颜色 screen.keep() local color = screen.get_color(_x, _y) screen.unkeep() return color end function _getColorRGB(_x, _y) --获取屏幕上某点颜色 RGB screen.keep() local r, g, b = screen.get_color_rgb(_x, _y) screen.unkeep() return r, g, b end function _multiColor(_array, _dim) --屏幕多点颜色匹配 screen.keep() local bool = screen.is_colors(_array, _dim) screen.unkeep() return bool end function _findColor(_color, _dim, _x1, _y1, _x2, _y2) --多点相似度模式找色 匹配成功:返回坐标(x,y),失败:返回 -1,-1 screen.keep() local x, y = screen.find_color(_color, _dim, _x1, _y1, _x2, _y2) screen.unkeep() return x, y end function _findMultiColorInRegionFuzzy(_color, _posandcolors, _dim, _x1, _y1, _x2, _y2) --触动函数 匹配成功:返回坐标(x,y),失败:返回 -1,-1 screen.keep() local x, y = findMultiColorInRegionFuzzy(_color, _posandcolors, _dim, _x1, _y1, _x2, _y2) screen.unkeep() return x, y end function _findColorArray(_posandcolors, _dim, _x1, _y1, _x2, _y2) --多点相似度模式找色 匹配成功返回数组{{x1, y1}, {x2, y2}, ...} screen.keep() local results = screen.find_color(_posandcolors, _dim, _x1, _y1, _x2, _y2) screen.unkeep() return results end function _multiColTap(_array, _dim, _x, _y, _ms, _log) --屏幕多点颜色匹配+点击 if _multiColor(_array, _dim) then _tap(_x, _y, _ms, _log) end end function _snapshot(_path, _x1, _y1, _x2, _y2, _kind, _quality) --截图保存 screen.keep() if string.lower(_kind) == ".png" then screen.image(_x1, _y1, _x2, _y2):save_to_png_file(_path) end if string.lower(_kind) == ".jpg" then screen.image(_x1, _y1, _x2, _y2):save_to_jpeg_file(_path, _quality) end screen.unkeep() _nlog("截图保存") end function _imageOperMerge(_pictb, _path, _kind, _quality) --图像合并 类型,0 - 横向合并;1 - 竖向合并; 返回 0 - 成功;1 - 失败;2 - 失败;3 - 失败 image.oper_merge(_pictb, _path, _kind, _quality) end function _ocrText(x1, y1, x2, y2, whitelist) -- 使用混合引擎 if whitelist == "1234567890" then -- 纯数字识别,使用混合引擎 return _ocrTextHybrid(x1, y1, x2, y2, {expectedLength = nil}) else -- 其他情况使用 Tesseract screen.keep() local text = string.atrim(screen.ocr_text(x1, y1, x2, y2, { lang = "eng", white_list = whitelist or "" })) screen.unkeep() return text end end -- 混合识别引擎(推荐) function _ocrTextHybrid(x1, y1, x2, y2, options) options = options or {} local expectedLength = options.expectedLength local requireConsensus = options.requireConsensus ~= false -- 默认需要一致 screen.keep() local originalImg = screen.image(x1, y1, x2, y2) -- 1. 准备不同处理的图片副本 local images = { original = originalImg:copy(), binary = originalImg:copy():binaryzation({ csim_mode = true, csim_algorithm = 2, white_background = true, {0x000000, 85} }), enhanced = originalImg:copy():cv_binaryzation(120) -- OpenCV二值化 } -- 2. 多个识别结果 local results = {} local confidenceMap = {} -- Apple Vision 识别(使用不同预处理) local appleResults = {} for name, img in pairs(images) do local text, details = img:ocr_text({ engine = "apple", lang = "en-US" }) if text then -- 字母转数字 local converted = convertLettersToDigits(text) table.insert(appleResults, converted) results[#results + 1] = converted -- 记录置信度 if details and #details > 0 then local avgConf = 0 for _, d in ipairs(details) do avgConf = avgConf + (d.confidence or 0) end avgConf = avgConf / #details confidenceMap[converted] = (confidenceMap[converted] or 0) + avgConf end end end -- Tesseract 识别(严格白名单) for name, img in pairs(images) do local text = img:ocr_text({ engine = "tesseract", lang = "eng", white_list = "0123456789" }) if text then local digits = string.gsub(text, "[^%d]", "") if #digits > 0 then table.insert(results, digits) end end end -- 3. 清理图片对象 for _, img in pairs(images) do img:destroy() end originalImg:destroy() screen.unkeep() -- 4. 投票机制 local voteCount = {} local voteDetails = {} for _, result in ipairs(results) do -- 如果指定了长度,只考虑长度匹配的结果 if expectedLength then if #result == expectedLength then voteCount[result] = (voteCount[result] or 0) + 1 if not voteDetails[result] then voteDetails[result] = {} end table.insert(voteDetails[result], result) end else voteCount[result] = (voteCount[result] or 0) + 1 end end -- 找出得票最高的结果 local bestResult, maxVotes = "", 0 for result, count in pairs(voteCount) do if count > maxVotes then bestResult, maxVotes = result, count end end -- 5. 如果需要一致但未达成一致,尝试智能选择 if requireConsensus and maxVotes < #results / 2 then _nlog("识别结果不一致,尝试智能选择") -- 优先选择有高置信度的结果 local bestByConf, maxConf = "", 0 for result, conf in pairs(confidenceMap) do if conf > maxConf then bestByConf, maxConf = result, conf end end if bestByConf ~= "" then bestResult = bestByConf end end -- 6. 后处理修正 if bestResult ~= "" then bestResult = postProcessDigits(bestResult, expectedLength) end _nlog(string.format("混合识别结果: %s (投票数: %d/%d)", bestResult, maxVotes, #results)) return bestResult end -- 字母转数字辅助函数 function convertLettersToDigits(text) local mapping = { s = "5", S = "5", ["$"] = "5", l = "1", L = "1", I = "1", i = "1", O = "0", o = "0", D = "0", d = "0", Z = "2", z = "2", B = "8", b = "8", g = "9", G = "9", q = "9", Q = "9", } local result = "" for char in text:gmatch(".") do if char:match("%d") then result = result .. char elseif mapping[char] then result = result .. mapping[char] end end return result end -- 后处理修正 function postProcessDigits(digits, expectedLength) if not digits or digits == "" then return "" end -- 修正常见数字混淆 local corrections = { {pattern = "88", replacement = "00", condition = function(s) return #s == expectedLength end}, {pattern = "33", replacement = "88", condition = function(s) return #s == expectedLength end}, {pattern = "66", replacement = "55", condition = function(s) return #s == expectedLength end}, } local result = digits for _, corr in ipairs(corrections) do if (not corr.condition or corr.condition(result)) and result:find(corr.pattern) then result = result:gsub(corr.pattern, corr.replacement) _nlog(string.format("后处理修正: %s -> %s", corr.pattern, corr.replacement)) end end return result end -----------------------------------------------------模拟触摸模块 (touch) function _tap(_x1, _y1, _ms, _log) touch.tap(_x1, _y1, 50, _ms) _nlog(_log) end function _touchlong(_x, _y, _msa, _msb, _log) --长按 touch.on(_x, _y):msleep(_msa):off() _msleep(_msb) _nlog(_log) end function _move(_x1, _y1, _x2, _y2, _len, _delay1, _delay2, _ms, _log) --匀速滑动 _len:移动步长 _delaya和_delayb:移动延迟 _ms:滑动完成后等待时间 touch.on(_x1, _y1) :step_len(_len) :step_delay(_delay1) :move(_x2, _y2) :delay(_delay2) :off() _msleep(_ms) _nlog(_log) end function _moveTo(_x1, _y1, _x2, _y2, _len1, _Value1, _Value2, _len2, _delay, _ms, _log) --精准滑动 _lena和_lena:移动步长 _delay:移动延迟 _ms:滑动完成后等待时间 touch.on(_x1, _y1) :step_len(_len1) :step_delay(0.01) :move(_x2 + _Value1, _y2 + _Value2) :step_len(_len2) :move(_x2, _y2) :delay(_delay) :off() _msleep(_ms) _nlog(_log) end -----------------------------------------------------剪贴板模块 (pasteboard) function _writePasteboard(_text) --写内容进剪贴板 pasteboard.write(_text) end function _readPasteboard() --获取剪贴板中的数据 return pasteboard.read() end function _clearPasteboard() --清空剪贴板 return pasteboard.clear() end -----------------------------------------------------设备相关模块 (device) --true:打开 false:关闭 function _unlock(_ms) --解锁屏幕 if device.is_screen_locked() then device.unlock_screen() _msleep(_ms) end end function _setWifiEnable(_boolean, _ms) --设置wifi if _boolean then device.turn_on_wifi() else device.turn_off_wifi() end _msleep(_ms) end function _setAirplaneMode(_boolean, _ms) --设置飞行模式 if _boolean then device.turn_on_airplane() else device.turn_off_airplane() end _msleep(_ms) end function _setBTEnable(_boolean) --设置蓝牙 if _boolean then device.turn_on_bluetooth() else device.turn_off_bluetooth() end end function _setLockOrien(_boolean) --设置旋转锁 if _boolean then device.lock_orien() else device.unlock_orien() end end function _setAssistiveTouch(_boolean) --设置AssistiveTouch if _boolean then device.assistive_touch_on() else device.assistive_touch_off() end end function _setAutoLockTime(_ms) --设置自动锁屏分钟数 0:永不锁屏 1~5分钟 device.set_autolock_time(_ms) end function _setVolumeLevel(_vol) --设置设备音量 范围:0.0 ~ 1 device.set_volume(_vol) end function _setBacklightLevel(_light) --设置亮度 范围:0.0 ~ 1 device.set_brightness(_light) end function _getDeviceName() --获取设备名 return device.name() end function _setDeviceName(_name) --设置设备名 device.set_name(_name) end function _wifiaddr() --检测本地网络地址 local lj_wifi, wifi_cs = 0, 0 --1.检测wifi是否连接成功 2.检测wifi次数 repeat local wifi_sz = device.ifaddrs() --获取设备所有的接口IP if #wifi_sz >= 2 then lj_wifi = 1 else if wifi_cs >= 11 then lj_wifi = 2 else wifi_cs = wifi_cs + 1 _setWifiEnable(false, 5000) --关闭 WiFi _setWifiEnable(true, 20000) --打开 WiFi end end until lj_wifi >= 1 return lj_wifi end -----------------------------------------------------清理模块 (clear) function _clearAppdata(_bid) --清理应用存档数据 clear.app_data(_bid) end function _clearAllPhotos() --清空相册 clear.all_photos() end function _clearCache() --清理系统缓存 clear.caches() end function _clearCookies() --清理浏览器Cookies clear.cookies() end -----------------------------------------------------应用程序模块 (app) function _closeApp(_bid, _ms, _log) --关闭指定应用 if type(_bid) == "table" then for _,bid in ipairs(_bid) do app.close(bid) end else app.close(_bid) end _msleep(_ms) _nlog(_log) end function _closeAllApp(_ms, _log) --关闭所有应用 for _,bid in ipairs(app.bundles()) do app.close(bid) end _msleep(_ms) _nlog(_log) end function _runApp(_bid, _ms, _log) --打开指定应用 app.run(_bid) _msleep(_ms); _nlog(_log) end function _ipaUninstall(_bid , _ms) --卸载应用 成功返回 true,卸载失败返回 false local flag = app.uninstall(_bid) _msleep(_ms) return flag end function _appBundlePath(_bid) --获取应用包路径 返回 文本型 或 nil return app.bundle_path(_bid) end function _appDataPath(_bid) --获取应用存档路径 返回 文本型 或 nil return app.data_path(_bid) end function _frontAppBid() --获取前台程序 bid 前台没有应用返回 0 return app.front_bid() end function _openURL(_url, _ms) --前台打开一个 URL app.open_url(_url) _msleep(_ms) end -----------------------------------------------------VPN模块 function _crevpn(_dispName, _VPNType, _server, _authorization, _password, _secret, _ms, _log) --创建 VPN:成功 - 返回 true,失败 - 返回 false local success = vpnconf.create{dispName = _dispName, VPNType = _VPNType, server = _server, authorization = _authorization, password = _password, secret = _secret, } _msleep(_ms) _nlog(_log) return success end function _selvpn(_dispName, _ms, _log) --选择指定vpn:成功 - 返回 true,失败返回 false local success = vpnconf.select(_dispName) _msleep(_ms) _nlog(_log) return success end function _getVPNList() --获取vpn列表:成功 - 返回vpn列表,失败 - 返回 nil local list = vpnconf.list() if list then return list else return {} end end function _delvpn(_ms, _log) --删除全部vpn local list = _getVPNList() if list then for _,v in ipairs(list) do vpnconf.delete(v.VPNID) end end _msleep(_ms) _nlog(_log) end function _setVPNEnable(_boolean, _ms, _log) --设置vpn:true - 打开vpn,false - 关闭vpn if _boolean == true then vpnconf.connect() else vpnconf.disconnect() end _msleep(_ms) _nlog(_log) end function _getVPNStatus() --获取当前vpn状态:成功 - 返回状态描述表,没有vpn - 返回 nil local flag = vpnconf.status() if flag == nil then return nil else if flag.text == "Connected" and flag.connected == true and flag.disconnected == false then return true else if flag.text == "Not Connected" and flag.connected == false and flag.disconnected == true then return false else return nil end end end end -----------------------------------------------------文件操作模块 (file) --_path:路径 --_from:源路径 --_to:目的路径 --模式(_mode) --文本型,可选参数,复制模式。取值: --空字符串或省略:默认模式(no-clobber),当目标已存在时返回失败 --"overwrite" 或 "o":覆盖模式,直接替换已存在的目标(已存在的目标会被删除) --"merge" 或 "m":合并模式(仅用于目录),保留目标中已存在的文件 --"overwrite-merge" 或 "om"/"mo":覆盖合并模式,合并目录但覆盖同名文件 function _isFileExist(_path) --判断一个文件或目录是否存在 if file.exists(_path) then return true else return false end end function _getFileList(_path) --获取目录所有文件名列表 return file.list(_path) end function _readFileString(_path) --读取一个文件中的所有数据 return string.atrim(string.strip_utf8_bom(file.reads(_path))) end function _readFileNubmer(_path) --读取一个文件中的数字文本并转换成数值(记录数) if _isFileExist(_path) == false then return 0 else local nubmer = tonumber(_readFileString(_path)) if nubmer == nil then return 0 else return nubmer end end end function _writeFileString(_path, _text, _mode, _log) --将数据写入到文件 if _mode == "o" then file.writes(_path, _text) else file.appends(_path, _text.."\r\n") end _nlog(_log) end function _delfile(_path, _ms, _log) --删除文件或目录 local ok, err = file.remove(_path) _msleep(_ms) _nlog(_log) end function _movefile(_from, _to, _mode, _ms, _log) --移动文件或目录 local ok, err = file.move(_from, _to, _mode) _msleep(_ms) _nlog(_log) end function _copyfile(_from, _to, _mode, _ms, _log) --拷贝文件或目录 local ok, err = file.copy(_from, _to, _mode) _msleep(_ms) _nlog(_log) end function _newfile(_path, _ms, _log) --递归创建目录 local success, err = file.mkdir_p(_path) _msleep(_ms) _nlog(_log) end function _readFile(_path, _log) --读文件返回表 local table = file.get_lines(_path) if table and #table > 0 and #table[1] > 0 then for k = 1, #table, 1 do if #table[k] > 0 then if k == 1 then table[k] = string.atrim(string.strip_utf8_bom(table[k])) --处理可能存在的 UTF8-BOM else table[k] = string.atrim(table[k]) end -- _nlog(#table[k]) end end _nlog(_log) return table else _nlog("获取文件列表为空!") return {} end end function _clearFiles(_path, _key, _ms, _log) --批量清理文件列表 local list = _getFileList(_path) if list then for i,v in pairs(list) do if _key == nil then _delfile(_path..v, 0); else local _a, _b = string.find(v, _key) if _b ~= nil then _delfile(_path..v, 0); end end end end _msleep(_ms) _nlog(_log) end function _moveFiles(_from, _to, _mode, _key, _ms, _log) --批量清理文件列表 local list = _getFileList(_from) if list then for i,v in pairs(list) do if _key == nil then _movefile(_from..v, _to..v, _mode, _ms, _log) else local _a, _b = string.find(v, _key) if _b ~= nil then _movefile(_from..v, _to..v, _mode, _ms, _log) end end end end end -----------------------------------------------------! HTTP 模块 (http) function _httpGet(_url) --_code 200:获取成功 404:文件不存在; _body:返回文件内容 local code, headers, body = http.get(_url) if code == -1 then return "timeout" else if code == 200 then return string.atrim(string.strip_utf8_bom(body)) else if code == 404 then return "nofile" end end end end function _httpDown(_webpath, _savepath) --返回 true,操作失败返回 false local done, info = http.download(_webpath, _savepath) if (done) then return true else return false end end function _httpDel(_serverip, _webpath) --HTTP状态码 200:删除成功 返回主体:NOfile或DELok或DELlow local code, headers, body = http.get(("http://".._serverip.."/del.php?action=web/".._webpath)) if code == 200 then if string.strip_utf8_bom(body) == "NOfile" or string.strip_utf8_bom(body) == "DELok" then return true else return false end else return false end end function _httpUpload(_serverip, _webpath, _text, _mode) --提交方式 w;覆盖 a:追加 local code, headers, body = -1, nil, nil if _mode == "w" then code, headers, body = http.get("http://".._serverip.."/upw.php?file=web/".._webpath.."&upxx=".._text) else code, headers, body = http.get("http://".._serverip.."/upa.php?file=web/".._webpath.."&upxx=".._text) end if code == 200 and string.strip_utf8_bom(body) == "UPok" then return true else return false end end