aardio 文档
aardio 范例: 动态口令(TOTP)工具
import win.clip;
import win.clip.bitmap;
import inet.url;
import crypt.otp;
import zbar;
import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var winform = win.form(text="动态口令(TOTP)工具";right=468;bottom=323;border="dialog frame";max=false)
winform.add(
btnQr={cls="button";text="识别屏幕或剪贴板二维码";left=131;top=261;right=365;bottom=291;z=11};
btnRefresh={cls="plus";text='\uF021';left=344;top=75;right=374;bottom=105;border={radius=-1};font=LOGFONT(h=-16;name='FontAwesome');z=8};
comboAlgo={cls="combobox";left=131;top=132;right=253;bottom=158;edge=1;items={"sha1","sha256","sha512"};mode="dropdownlist";z=2};
editDigits={cls="edit";text="6";left=131;top=173;right=253;bottom=197;edge=1;z=4};
lbSlider={cls="static";text="刷新周期(30 秒):";left=-4;top=216;right=114;bottom=241;align="right";transparent=1;z=7};
plusCountdown={cls="plus";left=314;top=123;right=397;bottom=206;notify=1;z=6};
plusOtp={cls="plus";left=131;top=57;right=299;bottom=104;align="right";border={bottom=1;color=0xFF808080};db=1;dl=1;editable=1;font=LOGFONT(h=-24);hideSel=false;notify=1;textPadding={top=7;right=45};z=13};
plusSecret={cls="plus";left=131;top=20;right=413;bottom=46;align="right";border={bottom=1;color=0xFF808080};db=1;dl=1;editable=1;notify=1;paddingTop=5;password=1;textPadding={right=24;bottom=1};z=12};
plusSlider={cls="plus";left=131;top=220;right=411;bottom=235;bgcolor=0xD9AB23;border={radius=-1};foreRight=15;forecolor=0x1C77FF;paddingBottom=5;paddingTop=5;z=5};
static={cls="static";text="哈希算法:";left=42;top=136;right=114;bottom=156;align="right";transparent=1;z=1};
static2={cls="static";text="密码位数:";left=42;top=178;right=114;bottom=198;align="right";transparent=1;z=3};
static4={cls="static";text="密钥(Base32):";left=16;top=27;right=114;bottom=47;align="right";transparent=1;z=9};
static5={cls="static";text="动态密码:";left=16;top=88;right=114;bottom=108;align="right";transparent=1;z=10}
)
/*}}*/
// 1.初始化界面控件
//----------------------------------
// 可显示/隐藏密码的密钥输入框
var revealIcon = winform.plusSecret.addCtrl(
cls="plus";
marginRight=0;marginBottom=2;width=24;
iconText = '\uF023'/*锁*/; iconStyle={align="right";font=LOGFONT(h=-15;name='FontAwesome');padding={top=3}}
)
revealIcon.skin({
color = {default = 0xC0000000;hover = 0xFFFF0000;active = 0xFF00FF00;};
checked = {iconText = '\uF06E'/*眼睛*/;}
})
revealIcon.onMouseClick = function(){
winform.plusSecret.passwordChar = !owner.checked ? "*" : null;
}
// 带复制按钮的 OTP 显示框
var copyIcon = winform.plusOtp.addCtrl(
cls="plus";
marginRight=8;marginTop=8;width=32; height=32;
iconText = '\uF0C5'/*复制*/; iconStyle={align="center";font=LOGFONT(h=-18;name='FontAwesome');padding={top=2}}
)
copyIcon.skin({
color = {default = 0xD0999999;hover = 0xFF0078D7;active = 0xFF005A9E;};
checked = {iconText = '\uF00C'/*对勾*/; color={default=0xFF008000}}
})
copyIcon.onMouseClick = function(){
win.clip.write(winform.plusOtp.text);
winform.plusOtp.editBox.selectAll(); // 全选
owner.checked = true;
owner.setTimeout(function(){
copyIcon.checked = false;
winform.plusOtp.editBox.setSel(); // 取消全选
}, 1000)
}
// 刷新周期滑块
winform.plusSlider.setTrackbarRange(5, 60);
winform.plusSlider.progressPos = 30;
// 倒计时圆形进度条
winform.plusCountdown.setPieRange(0, 100);
winform.plusCountdown.foreground = 0x99008000;
winform.plusCountdown.background = 0x30808080;
winform.plusCountdown.text = "30s";
// 算法下拉框
winform.comboAlgo.selIndex = 1;
// 2.核心逻辑
//----------------------------------
var mainTimer, countdownTimer;
// 更新 OTP 的核心函数
var updateOtp = function(){
var secret = winform.plusSecret.text;
if(!#secret){
winform.plusOtp.text = string.repeat(tonumber(winform.editDigits.text) : 6,"0");
return;
}
var period = winform.plusSlider.progressPos;
var digits = tonumber(winform.editDigits.text);
var algo = winform.comboAlgo.text;
var code, expiresIn, err = crypt.otp.totp(secret, period, digits, algo);
if(!code){
winform.msgboxErr(err,"错误");
winform.plusOtp.text = string.repeat(digits : 6,"!");
return;
}
winform.plusOtp.text = code;
return expiresIn, period;
}
// 重启主定时器
var restartTimer
restartTimer = function(){
// 停止旧的定时器
if(mainTimer) winform.clearInterval(mainTimer);
if(countdownTimer) winform.clearInterval(countdownTimer);
// 立即更新一次
var expiresIn = updateOtp();
if(!expiresIn) return;
// 启动倒计时动画
var startTick = time.tick();
countdownTimer = winform.setInterval(function(){
var elapsed = (time.tick() - startTick)/1000;
var progress = ( (expiresIn - elapsed) / expiresIn ) * 100;
if(progress < 0) progress = 0;
winform.plusCountdown.progressPos = progress;
winform.plusCountdown.text = math.ceil(expiresIn - elapsed) ++ "s";
}, 100);
// 启动下一个周期的主定时器
mainTimer = winform.setTimeout(restartTimer, expiresIn * 1000);
}
// 3. 事件绑定
//----------------------------------
// 滑块、输入框、下拉框变动时重启定时器
winform.plusSecret.onTextChange = restartTimer;
winform.editDigits.onTextChange = restartTimer;
winform.comboAlgo.oncommand = restartTimer;
winform.plusSlider.onPosChanged = function( pos,triggeredByUser ){
winform.lbSlider.text = "刷新周期("+pos+" 秒):"
restartTimer();
}
// 手动刷新按钮
winform.btnRefresh.skin({
background = {hover=0xFFE1E1E1; active=0xFFCCCCCC}
})
winform.btnRefresh.oncommand = restartTimer;
var scanner = zbar.scanner();
scanner.config('qrcode.enable');
winform.btnQr.oncommand = function(id,event){
var bmp = win.clip.bitmap.read()
var text = bmp && scanner.scanBitmap(bmp)
if(!(text && string.startsWith(text,"otpauth://totp/"))) text = scanner.scanBitmap(com.picture.snap());
if(text && string.startsWith(text,"otpauth://totp/")){
var params = inet.url.getParams(text);
if(params){
if(params.secret){
// OTP 密钥是 Base32 编码的
winform.plusSecret.text = params.secret;
}
if(params.algorithm){
winform.comboAlgo.text = string.lower(params.algorithm);
}
if(params.digits){
winform.editDigits.text = params.digits;
}
if(params.period){
winform.plusSlider.progressPos = tonumber(params.period);
}
restartTimer(); // 立即刷新
return; // 找到一个就退出
}
}
}
// 4. 保存配置文件
//----------------------------------
import fsys.config;
var config = fsys.config(io.appData("aardio/std/tools/otp"));
winform.bindConfig( config.winform,{
editDigits = "text";
plusSecret = "text";
plusSlider = "progressPos";
combobox = "selIndex";
lbSlider = "text";
} );
restartTimer();
winform.show();
win.loopMessage();
Markdown 格式