跳到主要内容

11 篇博文 含有标签「lua」

查看所有标签

· 阅读需 1 分钟
Lessica
a-simple-testcase.lua
local function tapWord(word)
local txts, details =
screen.ocr_text {
languages = {"en-US"},
words = {word},
confidence = 0.8
}
local tapped = false
for i, v in ipairs(txts) do
if v == word then
touch.tap(details[i].center[1], details[i].center[2])
tapped = true
end
end
return tapped
end

while true do
nLog("Exit all applications…")
touch.show_pose(true)
app.quit("*")
sys.sleep(2)

nLog("Open “Settings”…")
app.run("com.apple.Preferences")
sys.sleep(2)

nLog("Test gestures…")
for _ = 1, 5 do
touch.on(375, 1300):move(375, 275):step_delay(20):step_len(1):move(375, 255):off()
sys.sleep(1)
end
sys.sleep(2)

nLog("Exit all applications…")
app.quit("*")
sys.sleep(2)

nLog("Open “Photos”…")
app.run("com.apple.mobileslideshow")
sys.sleep(2)

nLog("Test multi-touch gestures…")
assert(app.front_bid() == "com.apple.mobileslideshow")
require("rec_20221011184701")
sys.sleep(2)

nLog("Exit all applications…")
app.quit("*")

nLog("Open “Notes”…")
app.run("com.apple.mobilenotes")
sys.sleep(2)

nLog("Test hardware keyboard simulation…")
assert(app.front_bid() == "com.apple.mobilenotes")
x, y =
screen.find_color(
{
{713, 1497, 0xe3ae09, 90.00}, -- 1
{688, 1522, 0xe3ae09, 90.00}, -- 2
{698, 1501, 0xe3ae09, 90.00}, -- 3
{674, 1518, 0xe3ae09, 90.00}, -- 4
{692, 1536, 0xe3ae09, 90.00}, -- 5
{709, 1512, 0xe3ae09, 90.00}, -- 6
{698, 1506, 0xffffff, 90.00}, -- 7
{704, 1513, 0xffffff, 90.00}, -- 8
{678, 1506, 0xffffff, 90.00}, -- 9
{678, 1532, 0xffffff, 90.00}, -- 10
{704, 1532, 0xffffff, 90.00} -- 11
}
)
if x > -1 then
touch.tap(x, y)
sys.sleep(2)

key.send_text("Hello, World!")
sys.sleep(2)

assert(tapWord("Done"))
end
sys.sleep(2)

nLog("Exit all applications…")
app.quit("*")

sys.sleep(5)
end

· 阅读需 2 分钟
Lessica
imap4-example.lua
local imap4 = require 'imap4'

-- If in doubt, see RFC 3501:
-- https://tools.ietf.org/html/rfc3501#section-6

-- Create new imap4 connection.
-- Port is optional and defaults to 143.
-- If you want to use TLS, set port to 993.
-- local connection = imap4('localhost', 993)

-- If you are connecting to gmail, yahoo or any other server that needs a SSL
-- connection before accepting commands, uncomment this line:
-- connection:enabletls { protocol = 'tlsv1_2' }

-- You can skip this step by creating the connection using
local connection = imap4('imap-mail.outlook.com', 993, {protocol = 'tlsv1_2'})

-- Print the servers capabilities.
print(table.concat(connection:capability(), ', '))

-- Make sure we can do what we came for.
assert(connection:isCapable('IMAP4rev1'))

-- Login. Warning: The credentials are sent in plaintext unless you
-- tunnel the connection over ssh, or use SSL (either via the method shown
-- above or calling connection:starttls(params) before logging in).
user = "tesmasenrogeos@outlook.com"
pass = "mC36WP01"
connection:login(user, pass)

-- connection:lsub() lists all subscribed mailboxes.
for mb, info in pairs(connection:lsub()) do
-- connection:status(mailbox, items) queries status of a mailbox.
-- Note: The mailbox name may contain unescaped whitespace. You are
-- responsible to escape it properly - try ("%q"):format(mb).
local stat = connection:status(mb, {'MESSAGES', 'RECENT', 'UNSEEN'})
print(mb, stat.MESSAGES, stat.RECENT, stat.UNSEEN)
end

-- Select INBOX with read only permissions.
local info = connection:examine('INBOX')
print(info.exist, info.recent)

-- List info on the 10 most recent mails.
-- See https://tools.ietf.org/html/rfc3501#section-6.4.5
local msgs = connection:fetch('BODY.PEEK[HEADER.FIELDS (From Date Subject)]', (math.max(info.exist-10, 1))..':*')
for _,v in pairs(msgs) do
-- `v' contains the response as mixed (possibly nested) table.
-- Keys are stored in the list part. In this example:
--
-- v[1] = "UID", v[2] = BODY
--
-- `v[key]' holds the value of that part, e.g.
--
-- v.UID = 10
--
-- `v.BODY' is the only exception and returns a table of the format
--
-- {parts = part-table, value = response}
--
-- For example:
--
-- v.BODY = {
-- parts = {"HEADER.FIELDS", {"From", "Date", "Subject"}},
-- value = "From: Foo <foo@bar.baz>\r\nDate:..."
-- }
print(v.id, v.UID, v.BODY.value)
end

-- close connection
connection:logout()

· 阅读需 1 分钟
Lessica

获取路径中所有文件名

local lfs = require("lfs")
for filename in lfs.dir("/var/mobile") do
if filename ~= ".." and filename ~= "." then
print(filename)
end
end
sys.alert(print.out())

获取文件属性

local lfs = require("lfs")
--
local attr = lfs.attributes("/var/mobile")
--
print("类型", attr.mode)
print("最后访问时间", os.date("%Y-%m-%d %H:%M:%S", attr.access))
print("最后修改时间", os.date("%Y-%m-%d %H:%M:%S", attr.modification))
print("最后状态变更时间", os.date("%Y-%m-%d %H:%M:%S", attr.change))
--
sys.alert(print.out())

获取及切换脚本进程当前目录

local lfs = require 'lfs'
--
sys.alert(lfs.currentdir()) -- 输出 "/private/var/root"
--
lfs.chdir('/var/mobile/Media/1ferver/lua/scripts')
--
sys.alert(lfs.currentdir()) -- 输出 "/var/mobile/Media/1ferver/lua/scripts"

· 阅读需 3 分钟
Lessica

自 XXTouch 3.0.1 开始,我们修改并预编译了 libexifluaexif 库,可以用来读取或写入 JPEG 图片的 EXIF 元数据。从 iPhone 拍摄的一张照片中读取出的 EXIF 元数据 YAML 格式如下:

luaexif-example.yaml
---
0:
DateTime: 2023:02:03 21:55:09
Make: Apple
Model: iPhone SE (2nd generation)
Orientation: 6
ResolutionUnit: 2
Software: '14.8'
XResolution:
- 72
- 1
YCbCrPositioning: 1
YResolution:
- 72
- 1
1:
Compression: 6
ResolutionUnit: 2
XResolution:
- 72
- 1
YResolution:
- 72
- 1
EXIF:
ApertureValue:
- 54823
- 32325
BrightnessValue:
- 22033
- 12445
ColorSpace: 65535
ComponentsConfiguration: "\x01\x02\x03\0"
CompositeImage: 2
DateTimeDigitized: 2023:02:03 21:55:09
DateTimeOriginal: 2023:02:03 21:55:09
ExifVersion: '0232'
ExposureBiasValue:
- 0
- 1
ExposureMode: 0
ExposureProgram: 2
ExposureTime:
- 1
- 59
Flash: 24
FlashpixVersion: '0100'
FNumber:
- 9
- 5
FocalLength:
- 399
- 100
FocalLengthIn35mmFilm: 28
ISOSpeedRatings: 250
LensMake: Apple
LensModel: iPhone SE (2nd generation) back camera 3.99mm f/1.8
LensSpecification:
- - 399
- 100
- - 399
- 100
- - 9
- 5
- - 9
- 5
MakerNote: '{length = 1244, bytes = 0x4170706c 6520694f 53000001 4d4d0020 ... 41424138
36410000 }'
MeteringMode: 5
OffsetTime: +08:00
OffsetTimeDigitized: +08:00
OffsetTimeOriginal: +08:00
PixelXDimension: 4032
PixelYDimension: 3024
SceneCaptureType: 0
SceneType: "\x01"
SensingMethod: 2
ShutterSpeedValue:
- 41912
- 7129
SubjectArea:
- 2013
- 1511
- 2217
- 1330
SubSecTimeDigitized: '439'
SubSecTimeOriginal: '439'
WhiteBalance: 0
GPS:
GPSAltitude:
- 87103
- 6210
GPSAltitudeRef: 0
GPSDateStamp: 2023:02:03
GPSDestBearing:
- 550625
- 3374
GPSDestBearingRef: T
GPSHPositioningError:
- 65
- 1
GPSImgDirection:
- 550625
- 3374
GPSImgDirectionRef: T
GPSLatitude:
- - 32
- 1
- - 1
- 1
- - 3551
- 100
GPSLatitudeRef: 'N'
GPSLongitude:
- - 118
- 1
- - 53
- 1
- - 4916
- 100
GPSLongitudeRef: E
GPSSpeed:
- 0
- 1
GPSSpeedRef: K
...

由于 EXIF 元数据的结构复杂,且没有标准化,所以在此不作详细介绍。你可以参考 EXIF Tags下述例程对其进行修改。

luaexif-example.lua
-- 引入模块
exif = require 'image.exif'

-- 读取 JPEG 图片的 EXIF 元数据
exif_data = exif.loadfile('IMG_0001.jpeg')

-- 将 EXIF 元数据转换为可修改的 Lua 表(此表可以进一步转换为 plist/json/yaml 并保存,以供重复使用)
exif_table = exif_data:as_table()

-- 修改表中的一些字段
exif_table['0'].Model = 'iPhone 11 Pro'
exif_table['0'].Software = '16.2'

-- 重新构造 EXIF 元数据
new_exif_data = exif.from_table(exif_table)

-- 写入 EXIF 元数据到 JPEG 文件(可写回原来文件,也可写入新文件)
ok, err = new_exif_data:save('IMG_0002.jpeg')

· 阅读需 1 分钟
Lessica

读取短信数据库

lsqlite3-example.lua
local sqlite3 = require('sqlite3')
--
local db = sqlite3.open('/private/var/mobile/Library/SMS/sms.db')
--
local handle_map = {}
local messages = {}
--
db:exec('select handle_id, text, date from message', function (ud, ncols, values, names)
messages[#messages + 1] = {
handle_id = values[1],
text = values[2],
date = os.date("%Y-%m-%d %H:%M:%S", os.time({year = 2001, month = 1, day = 1}) + tonumber(values[3]))
}
return sqlite3.OK
end)
--
db:exec('select ROWID, id from handle', function (ud, ncols, values, names)
handle_map[values[1]] = values[2]
return sqlite3.OK
end)
--
for _,v in ipairs(messages) do
v.id = handle_map[v.handle_id]
v.handle_id = nil
end
--
local results = {}
for _,v in ipairs(messages) do
results[#results + 1] = string.format("[%s](%s):%s", v.date, v.id, v.text)
end
--
sys.toast(table.concat(results, '\n'))

· 阅读需 1 分钟
Lessica

GBK 转 UTF-8

local iconv = require("iconv")
local cd = iconv.new("utf-8", "gbk") -- 新建一个 GBK 编码到 UTF8 编码的转换器
local f = io.open("/var/mobile/1.txt", "rb")
local s = f:read("*a")
f:close()
sys.alert(cd:iconv(s))

UTF-16LE 转 UTF-8

local iconv = require("iconv")
local cd = iconv.new("utf-8", "utf-16le") -- 新建一个 UTF-16LE 编码到 UTF8 编码的转换器
local f = io.open("/var/mobile/1.txt", "rb")
local s = f:read("*a")
f:close()
sys.alert(cd:iconv(s))

UTF-16BE 转 UTF-8

local iconv = require("iconv")
local cd = iconv.new("utf-8", "utf-16be") -- 新建一个 UTF-16BE 编码到 UTF8 编码的转换器
local f = io.open("/var/mobile/1.txt", "rb")
local s = f:read("*a")
f:close()
sys.alert(cd:iconv(s))

· 阅读需 1 分钟
Lessica

连接超时

luasocket-test.lua
local socket = require("socket")
local sock = socket.tcp()
sock:settimeout(0.2) -- 设置连接超时秒数
if (sock:connect("220.181.57.217", 80)) then
sock:close() -- 关闭连接
sys.alert("能连上")
else
sys.alert("超时了")
end

请求 Bing 首页

luasocket-example.lua
local socket = require('socket')

local sock = socket.tcp()
local ip = assert(socket.dns.toip('www.bing.com'), '域名解析失败')
sock:settimeout(10)
assert(sock:connect(ip, 80) == 1, '连接失败或超时')

assert(
sock:send(
'GET / HTTP/1.1\r\n'..
'Host: www.bing.com\r\n'..
'Accept: */*\r\n'..
'Connection: close\r\n'..
'\r\n'
),
'发送数据超时'
)

local buf = {}
repeat
local chunk, status, partial = sock:receive(4096)
if (chunk) then
buf[#buf + 1] = chunk
else
if (partial) then
buf[#buf + 1] = partial
end
end
until status == "closed"
sock:close()

sys.alert(table.concat(buf))

· 阅读需 3 分钟
Lessica
  • 此方案支持的验证码类型:
    • 所有 reCAPTCHA 类型,包括 v2、v3 和 invisible
    • Arkose Labs FunCaptcha
    • hCaptcha
  • 此方案支持的人工打码平台:

你需要先在上述平台注册账号并充值。随后,从相应的开发者中心获取 API 密钥,以备接下来的配置。

配置步骤

  1. 下载 captcha-resolver.js 并保存到本地
  2. 修改 CAPTCHA_SOLVER_KEY 为你的 API 密钥
  3. 修改 CAPTCHA_SOLVER_PROVIDER 为你的打码平台,可选值为 2captchaanticaptcha
  4. 修改 CAPTCHA_SOLVER_DELAY 为开始打码前的延迟时间,单位为秒。如果你不希望自动开始打码,可以将此值先设置为 -1
  5. 如果你的打码平台需要代理,请修改 CAPTCHA_SOLVER_PROVIDER_PROXY 为你的代理信息,否则请删除或设置为 null
  6. 将修改后的 captcha-resolver.js 放置在你的项目中
captcha-resolver.js
const $$payloads = {
options: {
CAPTCHA_SOLVER_DELAY: 3,
CAPTCHA_SOLVER_KEY: "138e80********************97faab",
CAPTCHA_SOLVER_PROVIDER: "2captcha", // 2captcha 或 anticaptcha
CAPTCHA_SOLVER_PROVIDER_PROXY: {
'proxyType': 'socks5', // socks4, socks5, http, https
'proxyAddress': '27.***.***.***',
'proxyPort': 60000,
'proxyLogin': 's***************', // 可选
'proxyPassword': '*************', // 可选
'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
},
},
};

使用方法

自动打码

将以下代码添加到你的项目中,即可在所有网页中引入 captcha-resolver.js 并启用自动打码。

local resolver_fp = io.open("captcha-resolver.js", "r")
local resolver_payload = resolver_fp:read("*a")
monkey.clear_userscripts() -- 移除所有预设用户脚本
monkey.add_userscript({ scheme = "https" }, resolver_payload)
resolver_fp:close()

触发打码

如果你已经将 CAPTCHA_SOLVER_DELAY 设置为 -1,则打码不会自主开始,你可以执行以下代码,选择一个恰当的时机开始打码。

monkey.eval({ scheme = "https" }, "window.$_solveReCaptcha()")

· 阅读需 2 分钟
Lessica

此方案是对 Google reCAPTCHA 人工打码方案 的一次重要升级,它可以在不使用打码工人的情况下,利用 AI 自动识别并解决各类验证码。

配置步骤

警告
  • 此方案仅支持 iOS 14 及以上版本、XXTouchNG 3.0.3 及以上版本。
  • 此方案仅供内部使用,暂未开放给外部用户,你可能无法访问其发布页面。
  1. NopeCHA 注册账号,获取 API Key。
  2. nopecha-extension-monkey 发布页面下载最新的 ch.xxtou.nopecha-extension_0.3.13_iphoneos-arm.deb 文件,安装到装有 XXTouchNG 的 iOS 设备上。
  3. 要激活此插件,在业务脚本中添加以下代码(将代码中的 {YOUR_API_KEY} 替换为你第一步中获取的 API Key):
NopeCHA = require("NopeCHA")("{YOUR_API_KEY}")
NopeCHA.inject() -- 安装 NopeCHA 钩子到 Web 自动化模块
  1. 要解除此插件,可参考以下代码:
monkey.clear_userscripts()  -- 清除所有用户脚本

· 阅读需 1 分钟
Lessica
pop3-example.lua
local pop3 = require "pop3"

local some_mail = {
host = os.getenv("LUA_MAIL_HOST") or '127.0.0.1';
username = os.getenv("LUA_MAIL_USER") or 'me@host.local';
password = os.getenv("LUA_MAIL_PASS") or 'mypassword';
}

local function print_msg(msg, indent)
indent = indent or ''
print(indent .. "----------------------------------------------")
print(indent .. "ID: ", msg:id())
print(indent .. "subject: ", msg:subject())
print(indent .. "to: ", msg:to())
print(indent .. "from: ", msg:from())
print(indent .. "from addr: ", msg:from_address())
print(indent .. "reply: ", msg:reply_to())
print(indent .. "reply addr: ", msg:reply_address())
print(indent .. "trunc: ", msg:is_truncated())
for i,v in ipairs(msg:full_content()) do
if v.text then print(indent .. " ", i , "TEXT : ", v.type, #v.text)
elseif v.data then print(indent .. " ", i , "FILE : ", v.type, v.disposition, v.file_name or v.name, #v.data)
elseif v.message then print(indent .. " ", i , "RFC822: ", v.type, v.disposition, v.file_name or v.name)
print_msg(v.message, indent .. '\t\t\t')
end
end
end

local mbox = pop3.new()

mbox:open(some_mail.host, some_mail.port or '110')
print('open :', mbox:is_open())

mbox:auth(some_mail.username, some_mail.password)
print('auth :', mbox:is_auth())

for k, msg in mbox:messages() do
print(string.format(" *** MESSAGE NO %d ***", k))
print_msg(msg)
end