├── config.json ├── README.md └── 钉钉打卡.js /config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "[非必填] 公司名", 4 | "name": "companyName", 5 | "placeholder": "如果你的钉钉加入了多个公司,请填写你打卡的公司名" 6 | }, 7 | { 8 | "label": "[非必选] 打卡规则,跳过节假日/周末", 9 | "name": "jumpRules", 10 | "type": "select", 11 | "options": { 12 | "rule_0": "请选择", 13 | "rule_1": "跳过法定节假日", 14 | "rule_2": "跳过周末", 15 | "rule_3": "跳过法定节假日及周末" 16 | } 17 | }, 18 | { 19 | "label": "上班打卡时间范围", 20 | "name": "morTime", 21 | "placeholder": "07:00-09:00", 22 | "validation": "required" 23 | }, 24 | { 25 | "label": "下班打卡时间范围", 26 | "name": "nightTime", 27 | "placeholder": "17:30-24:00", 28 | "validation": "required" 29 | }, 30 | { 31 | "label": "打卡随机时间(秒)", 32 | "name": "maxTime", 33 | "placeholder": "最小时间固定为10s,此处输入最大时间(秒)", 34 | "validation": "required|number|min:10" 35 | }, 36 | { 37 | "label": "[非必填] 手机解锁密码,仅支持数字密码解锁(不填则表示上滑即解锁)", 38 | "name": "pwd", 39 | "placeholder": "例:123456", 40 | "validation": "number" 41 | }, 42 | { 43 | "label": "[非必填] 钉钉登录账号", 44 | "name": "account" 45 | }, 46 | { 47 | "label": "[非必填] 钉钉登录密码", 48 | "name": "accountPwd" 49 | }, 50 | { 51 | "label": "[非必填] 开启早退打卡功能,请谨慎开启!!", 52 | "name": "leaveEarly", 53 | "placeholder": "若填写,则会执行早退打卡!不填写则不会执行早退打卡" 54 | }, 55 | { 56 | "label": "[非必填] 开启迟到打卡功能,请谨慎开启!!", 57 | "name": "punchLater", 58 | "placeholder": "若填写,则会执行迟到打卡!不填写则不会执行迟到打卡" 59 | }, 60 | { 61 | "label": "停顿时间:若进入打卡页面失败,请调大该数字(秒)", 62 | "name": "waitTime", 63 | "validation": "required|number" 64 | }, 65 | { 66 | "label": "[非必填] 百度OCR Token的get方式 Url", 67 | "name": "tokenUrl", 68 | "placeholder": "若填写,则通过OCR识别打卡结果中是否包含打卡成功字样" 69 | }, 70 | { 71 | "label": "[非必填]server酱的SCKEY,注意区分旧版和Turbo版", 72 | "name": "serverUrl" 73 | }, 74 | { 75 | "label": "server酱通知方式", 76 | "name": "sendImgRules", 77 | "type": "select", 78 | "options": { 79 | "notSend": "默认不通知", 80 | "sc": "旧版Server酱", 81 | "sctapi": "Turbo版Server酱" 82 | } 83 | } 84 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 【一个理论上能正常使用的钉钉打卡脚本】 2 | 3 | 【解锁屏幕】【打卡结果反馈】【多时段打卡】【随机时间】【钉钉自动登录】【跳过节假日/周末】 4 | 5 | ------ 6 | 7 | 开源地址:https://github.com/fangyk-rel/DingDing-SayNo 8 | 9 | 该脚本用于Hamibot,你可以修改后用于Auto.js 10 | 11 | ### 更新日志 12 | ------ 13 | 由于时间原因,目前没办法维护该脚本了~ 14 | ------ 15 | 16 | V3.6: 17 | 18 | - 修复 当打卡规则为”跳过节假日和周末“时,脚本无法运行(脚本报错)的问题 19 | 20 | - 修复 当手机未设置锁屏密码时,无法解锁的问题 21 | 22 | - 另外: 目前发现有网友存在设置了锁屏密码,但脚本提示暂时无法解锁的问题。 23 | 24 | ​ 由于我手上没有多的手机,无法测试及保证每台安卓手机都能解锁。 25 | 26 | ​ 见谅~ 27 | 28 | V3.5: 29 | 30 | - 新增 可选择同时跳过法定节假日和周末 31 | - 新增 Server酱的Turbo版推送方式 32 | 33 | V3.4: 34 | 35 | - 新增 兼容安卓7.0以下,5.0以上 36 | 37 | V3.3: 38 | 39 | - 新增 可选跳过法定节假日/周末 40 | 41 | V3.2: 42 | 43 | - 新增 解锁方式,解锁成功率更高 44 | - 新增 申请截图权限时自动确定 45 | 46 | #### 安装脚本后你需要干什么? 47 | 48 | ------ 49 | 50 | #### 开启必要权限 51 | 52 | - 无障碍权限 53 | 54 | - 后台弹出界面,用于打开钉钉 55 | 56 | - 后台锁定及电池优化白名单,保证Hamibot常驻后台 57 | 58 | - 如果在配置中输入了百度OCR url或者勾选了上传截图,会申请截图权限 59 | 60 | Tips:注意在权限弹出框中有一个隐藏的勾选框,瞎点几下勾选上,以后就不用重复弹出了 61 | 62 | - 可能还有其他的....... 63 | 64 | #### 脚本配置详解 65 | 66 | - **【非必填】【公司名】:** 67 | 68 | 如果钉钉加入了多个公司,请输入公司名称,可以填写部分(例:改革春风吹满地,可以填改革、春风等等),否则请置空 69 | 70 | - **[非必选] 打卡规则,跳过节假日/周末** 71 | 72 | 可选跳过法定节假日、周末 73 | 74 | - **【必填】【上班打卡时间范围】:** 75 | 76 | 格式(08:00-09:00)支持多个打卡时间,以英文分号分隔(08:00-09:00;11:00-12:00) 77 | 78 | - **【必填】【下班打卡时间范围】:** 79 | 80 | 同上 81 | 82 | - **【非必填】【钉钉登录账号】** 83 | 84 | 用于登录 85 | 86 | - **【非必填】【钉钉登录密码】** 87 | 88 | 同上 89 | 90 | - ** [非必填] 开启早退打卡功能:** 91 | 若填写此项,则会在遇到早退打卡时执行早退打卡!不会填写备注和拍照!!! 92 | 93 | - **[非必填] 开启迟到打卡功能!:** 94 | 若填写此项,则会在遇到迟到打卡时执行早退打卡!不会填写备注和拍照!!! 95 | 96 | - **【必填】【打卡随机时间】:** 97 | 98 | 进入打卡页面后,会从10s~输入的时间内,随机选择一个时间并等待后,开始打卡(避免每天打卡时间相同) 99 | 100 | - **【非必填】【解锁密码】:** 101 | 102 | 请填写数字密码,目前支持这个,不填表示上滑即解锁(没测试过所有机型,小米手机解锁失败的可以更换"MIUI11"这个主题试试) 103 | 104 | - **【必填】【停顿时间】:** 105 | 106 | 默认3s,打不开钉钉的话,请调大该数字 107 | 108 | - **【非必填】【百度OCR】:** 109 | 110 | 填写百度OCR获取token的url,一般不用管这个 111 | 112 | - **【非必填】【server酱Url】:** 113 | 114 | 旧版前往http://sc.ftqq.com/?c=wechat&a=bind 绑定, 115 | 116 | Turbo版前往https://sct.ftqq.com/sendkey绑定,注意设置消息通道 117 | 118 | 然后将SCKEY复制到此处输入框。用于推送打卡结果到微信 119 | 120 | - **【Server酱通知方式】**:** 121 | 122 | 可选不通知、旧版和Turbo版 123 | -------------------------------------------------------------------------------- /钉钉打卡.js: -------------------------------------------------------------------------------- 1 | importClass(android.provider.Settings); 2 | importClass(android.content.Context); 3 | /* --------------------------------------预配置开始----------------------------------- */ 4 | const { serverUrl, companyName, morTime, nightTime, tokenUrl, maxTime, waitTime, pwd, sendImgRules, account, accountPwd, jumpRules, leaveEarly, punchLater} = hamibot.env; 5 | var myLog = ""; 6 | var myStr = ""; 7 | const w = device.width; 8 | const h = device.height; 9 | const maxSwipeNum = 50; 10 | const holidayUrl = "http://timor.tech/api/holiday/year?week=Y"; 11 | const holidayCfgName = "HOLIDAY_ARRAY_" + new Date().getFullYear() + "_"; 12 | // 息屏时间/2 13 | const loopTime = getLoopTime(); 14 | var myCfg = storages.create("DingDing-SayNo"); 15 | 16 | /** 17 | * 防止息屏 18 | */ 19 | threads.start(function () { 20 | setInterval(() => { 21 | toast("防止锁屏"); 22 | }, loopTime); 23 | }); 24 | 25 | if (!morTime) { 26 | toastLog("请设置上班打卡时间范围"); 27 | exitShell(); 28 | } 29 | 30 | if (!nightTime) { 31 | toastLog("请设置下班打卡时间范围"); 32 | exitShell(); 33 | } 34 | 35 | if (!maxTime) { 36 | toastLog("请设置打卡随机时间"); 37 | exitShell(); 38 | } 39 | 40 | if (!waitTime) { 41 | toastLog("请设置等待时间"); 42 | exitShell(); 43 | } 44 | //上班打卡时间段 45 | var goToWorkTime = morTime.split(';'); 46 | 47 | //下班打卡时间段 48 | var afterWorkTime = nightTime.split(';'); 49 | 50 | // 设置当年的节假日 51 | if (("rule_1" == jumpRules || "rule_3" == jumpRules) && !myCfg.contains(holidayCfgName)) { 52 | setholiday(); 53 | } 54 | /* --------------------------------------预配置结束----------------------------------- */ 55 | 56 | startProgram(); 57 | 58 | /** 59 | * 脚本流程 60 | */ 61 | function startProgram() { 62 | unlockIfNeed(); 63 | sleep(waitTime * 1000); 64 | // 1.检查权限 65 | checkMyPermission(); 66 | // 2.进入页面 67 | goToPage(); 68 | handleOrgDialog(); 69 | // 3.获取操作并执行 70 | var randTime = random(10, maxTime); 71 | toast(randTime + "s后开始打卡"); 72 | setLog(randTime + "s后开始打卡"); 73 | sleep(randTime * 1000); 74 | punchTheClock(); 75 | // 4.获取结果 76 | checkPunch(); 77 | getReslt(); 78 | // 5.返回给用户 79 | exitShell(); 80 | } 81 | 82 | /** 83 | * 手机是否锁屏 84 | */ 85 | function isLocked() { 86 | var km = context.getSystemService(Context.KEYGUARD_SERVICE); 87 | return km.isKeyguardLocked() && km.isKeyguardSecure(); 88 | } 89 | 90 | /** 91 | * 根据当前自动息屏时间获取循环时间 92 | */ 93 | function getLoopTime() { 94 | let lockTime = Settings.System.getInt(context.getContentResolver(), Settings.System.SCREEN_OFF_TIMEOUT); 95 | if (null == lockTime || "" == lockTime || "undefined" == lockTime) { 96 | return 8000; 97 | } 98 | return lockTime / 2; 99 | } 100 | 101 | /** 102 | * 获取今年的所有节假日 103 | */ 104 | function setholiday() { 105 | setLog("获取当年节假日数据"); 106 | let res = http.get(holidayUrl, {}); 107 | let jsonObj = JSON.parse(res.body.string()); 108 | if (jsonObj.code == -1) { 109 | setLog("获取节假日数据失败"); 110 | exitShell(); 111 | } 112 | 113 | let holiday = jsonObj.holiday; 114 | let holidayArray = []; 115 | if (holiday) { 116 | for (let key in holiday) { 117 | if (holiday[key].holiday) { 118 | holidayArray.push(holiday[key].date); 119 | } 120 | } 121 | myCfg.put(holidayCfgName, holidayArray); 122 | } else { 123 | setLog("节假日数据接口变更,请联系开发者,并设置节假日规则为请选择或跳过周末"); 124 | exitShell(); 125 | } 126 | } 127 | 128 | /** 129 | * 解锁屏幕 130 | */ 131 | function unlockIfNeed() { 132 | device.wakeUpIfNeeded(); 133 | if (!isLocked()) { 134 | setLog("无需解锁"); 135 | swipeUp(); 136 | return; 137 | } 138 | swipeUp(); 139 | sleep(1000); 140 | if (pwd) { 141 | enterPwd(); 142 | } else { 143 | setLog("请配置手机解锁密码"); 144 | exitShell(); 145 | } 146 | setLog("解锁完毕"); 147 | } 148 | 149 | 150 | 151 | /** 152 | * 上滑至输入密码界面 153 | */ 154 | function swipeUp() { 155 | if (myCfg.contains("CFG_SWIPE_TIME_")) { 156 | const CFG_SWIPE_TIME_ = myCfg.get("CFG_SWIPE_TIME_"); 157 | gesture(CFG_SWIPE_TIME_, [w / 2, h * 0.9], [w / 2, h * 0.1]); 158 | sleep(1000); 159 | if (swipeUpSuc()) { 160 | return; 161 | } 162 | } 163 | 164 | if (swipeUpMethodOne()) { 165 | log("方式一上滑成功"); 166 | } else if (swipeUpMethodTwo()) { 167 | log("方式二上滑成功"); 168 | } else { 169 | setLog("当前程序无法上滑至桌面或密码输入界面"); 170 | exitShell(); 171 | } 172 | } 173 | 174 | /** 175 | * 上滑方式一 176 | */ 177 | function swipeUpMethodOne() { 178 | var xyArr = [220]; 179 | var x0 = w / 2; 180 | var y0 = h / 4 * 3; 181 | var angle = 0; 182 | var x = 0; 183 | var y = 0; 184 | for (let i = 0; i < 30; i++) { 185 | y = x * tan(angle); 186 | if ((y0 - y) < 0) { 187 | break; 188 | } 189 | var xy = [x0 + x, y0 - y]; 190 | xyArr.push(xy); 191 | x += 5; 192 | angle += 3; 193 | } 194 | gesture.apply(null, xyArr); 195 | function tan(angle) { 196 | return Math.tan(angle * Math.PI / 180); 197 | } 198 | return swipeUpSuc(); 199 | } 200 | 201 | /** 202 | * 上滑方式二 203 | */ 204 | function swipeUpMethodTwo() { 205 | let swipeTime = 0; 206 | let addTime = 20; 207 | for (let i = 0; i < maxSwipeNum; i++) { 208 | swipeTime += addTime; 209 | gesture(swipeTime, [w / 2, h * 0.9], [w / 2, h * 0.1]); 210 | sleep(1000); 211 | if (swipeUpSuc()) { 212 | myCfg.put("CFG_SWIPE_TIME_", swipeTime); 213 | return true; 214 | } 215 | } 216 | return false; 217 | } 218 | 219 | /** 220 | * 判断上滑结果 221 | */ 222 | function swipeUpSuc() { 223 | let km = context.getSystemService(Context.KEYGUARD_SERVICE); 224 | // 判断是否在锁屏界面 225 | if (!km.inKeyguardRestrictedInputMode()) { 226 | return true; 227 | } 228 | for (let i = 0; i < 10; i++) { 229 | if (!text(i).clickable(true).exists() && !desc(i).clickable(true).exists()) { 230 | return false; 231 | } 232 | } 233 | return true; 234 | } 235 | 236 | /** 237 | * 输入手机解锁密码 238 | */ 239 | function enterPwd() { 240 | //点击 241 | if (text(0).clickable(true).exists()) { 242 | for (var i = 0; i < pwd.length; i++) { 243 | a = pwd.charAt(i) 244 | sleep(200); 245 | text(a).clickable(true).findOne().click() 246 | } 247 | } else { 248 | for (var i = 0; i < pwd.length; i++) { 249 | a = pwd.charAt(i) 250 | sleep(200); 251 | desc(a).clickable(true).findOne().click() 252 | } 253 | } 254 | } 255 | 256 | /** 257 | * 是否需要登录 258 | */ 259 | function loginIfNeed() { 260 | if (text("密码登录").clickable(true).exists()) { 261 | text("密码登录").clickable(true).findOne().click(); 262 | } else if (desc("密码登录").clickable(true).exists()) { 263 | desc("密码登录").clickable(true).findOne().click(); 264 | } 265 | 266 | if (text("忘记密码").clickable(true).exists() || desc("忘记密码").clickable(true).exists()) { 267 | if (!account || !accountPwd) { 268 | setLog("当前未登录,请输入钉钉登录账号及密码"); 269 | exitShell(); 270 | } 271 | 272 | if (id("et_phone_input").exists() && id("et_pwd_login").exists()) { 273 | id("et_phone_input").findOne().setText(account); 274 | sleep(1000); 275 | id("et_pwd_login").findOne().setText(accountPwd); 276 | log("使用ID选择输入"); 277 | } else { 278 | setText(0, account); 279 | sleep(1000); 280 | setText(1, accountPwd); 281 | log("使用setText输入"); 282 | } 283 | // Android版本低于7.0 284 | if (device.sdkInt < 24) { 285 | let pageUIObj = []; 286 | if (id("btn_next").clickable(true).exists()) { 287 | id("btn_next").clickable(true).findOne().click(); 288 | } else { 289 | if (text("忘记密码").exists()) { 290 | pageUIObj = text("忘记密码").findOne().parent().parent().children(); 291 | } else { 292 | pageUIObj = desc("忘记密码").findOne().parent().parent().children(); 293 | } 294 | if (pageUIObj.length == 5) { 295 | let loginBtn = pageUIObj[3].children()[0]; 296 | loginBtn.click(); 297 | } else { 298 | setLog("找不到登录按钮,请联系脚本作者!"); 299 | } 300 | } 301 | 302 | } else { 303 | //获取登录按钮坐标 304 | if (text("忘记密码").clickable(true).exists()) { 305 | var loginBtnY = text("忘记密码").clickable(true).findOne().bounds().top - 10; 306 | } else { 307 | var loginBtnY = desc("忘记密码").clickable(true).findOne().bounds().top - 10; 308 | } 309 | click(w / 2, loginBtnY); 310 | } 311 | 312 | setLog("登录成功"); 313 | } else { 314 | setLog("已登录"); 315 | } 316 | } 317 | 318 | /** 319 | * 上传截图至SMMS 320 | */ 321 | function uploadImg() { 322 | toastLog("上传打卡截图..."); 323 | const url = "https://sm.ms/api/v2/upload"; 324 | const fileName = "/sdcard/" + new Date().getTime() + ".png"; 325 | captureScreen(fileName); 326 | 327 | let res = http.postMultipart(url, { 328 | smfile: open(fileName) 329 | }, { 330 | headers: { 331 | 'User-Agent': 332 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36', 333 | } 334 | }); 335 | 336 | let jsonObj = JSON.parse(res.body.string()); 337 | let isSuc = jsonObj.success; 338 | let imgUrl = jsonObj.data.url; 339 | let delUrl = jsonObj.data.delete; 340 | if (isSuc) { 341 | setLog("手机截图删除结果:" + ((files.remove(fileName) ? "成功" : "失败"))); 342 | setLog("图床图片删除链接:"); 343 | setLog(delUrl); 344 | setLog("打卡结果截图"); 345 | myLog += '![logo](' + imgUrl + ')'; 346 | } else { 347 | setLog("图片上传失败~"); 348 | } 349 | } 350 | 351 | /** 352 | * 1.切换不常用公司会出现是否继续打卡提示,默认继续打卡 353 | * 2.判断是否早退打卡,迟到打卡 354 | * */ 355 | function checkPunch() { 356 | toastLog("等待10s,确保操作完毕"); 357 | sleep(10000); 358 | toastLog("检查打卡"); 359 | if (text("继续打卡").clickable(true).exists()) { 360 | text("继续打卡").clickable(true).findOne().click(); 361 | } else if (desc("继续打卡").clickable(true).exists()) { 362 | desc("继续打卡").clickable(true).findOne().click(); 363 | } 364 | try { 365 | if (leaveEarly) { 366 | if (textContains("早退打卡").exists() || descContains("早退打卡").exists()) { 367 | setLog("早退打卡,执行早退打卡"); 368 | if (text("早退打卡").clickable(true).exists()) { 369 | text("早退打卡").clickable(true).findOne().click(); 370 | }else if (desc("早退打卡").clickable(true).exists()) { 371 | desc("早退打卡").clickable(true).findOne().click(); 372 | } 373 | } 374 | } 375 | if (punchLater) { 376 | if (textContains("迟到打卡").exists() || descContains("迟到打卡").exists()) { 377 | setLog("迟到打卡,执行迟到打卡"); 378 | if (text("迟到打卡").clickable(true).exists()) { 379 | text("迟到打卡").clickable(true).findOne().click(); 380 | } else if (desc("迟到打卡").clickable(true).exists()) { 381 | desc("迟到打卡").clickable(true).findOne().click(); 382 | } 383 | } 384 | } 385 | 386 | } catch (error) { 387 | setLog("检查打卡出错:" + '\n\n' + error.message); 388 | 389 | } 390 | 391 | } 392 | 393 | /** 394 | * 获取打卡结果 395 | */ 396 | function getReslt() { 397 | toastLog("等待10s,确保打卡操作完毕"); 398 | sleep(10000); 399 | toastLog("识别打卡结果"); 400 | 401 | try { 402 | if (textContains("打卡成功").exists() || descContains("打卡成功").exists()) { 403 | setLog("普通识别结果:" + myStr + "成功!"); 404 | } else if (textContains("已打卡").exists() || descContains("已打卡").exists()) { 405 | setLog("普通识别结果:" + myStr + ",重复打卡,请查看图片结果!"); 406 | } else { 407 | setLog("普通识别结果:" + myStr + "失败!,扣你丫工资~"); 408 | } 409 | if (tokenUrl) { 410 | let str = getContentByOcr(); 411 | setLog("OCR识别内容:" + str); 412 | if (str.indexOf("打卡成功") !== -1) { 413 | setLog("OCR识别结果:" + myStr + "成功!"); 414 | } else if (str.indexOf("已打卡") !== -1) { 415 | setLog("OCR识别结果:" + myStr + ",重复打卡,请查看图片结果!"); 416 | } else { 417 | setLog("OCR识别结果:" + myStr + "失败!,扣你丫工资~"); 418 | } 419 | } 420 | if (sendImgRules != "notSend") { 421 | uploadImg(); 422 | } 423 | 424 | } catch (error) { 425 | setLog("识别打卡结果出错:" + '\n\n' + error.message); 426 | } 427 | back(); 428 | back(); 429 | } 430 | 431 | /** 432 | * 调用百度文字识别ocr得到当前手机截屏文字 433 | */ 434 | function getContentByOcr() { 435 | let img = captureScreen(); 436 | access_token = http.get(tokenUrl).body.json().access_token; 437 | let url = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic" + "?access_token=" + access_token; 438 | let imag64 = images.toBase64(img); 439 | let res = http.post(url, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, image: imag64, image_type: "BASE64" }); 440 | str = JSON.parse(res.body.string()).words_result.map(val => val.words).join(); 441 | return str; 442 | } 443 | 444 | /** 445 | * 打卡 446 | */ 447 | function punchTheClock() { 448 | setLog("当前操作:" + myStr); 449 | waitBtnShow(); 450 | if (text(myStr).clickable(true).exists()) { 451 | text(myStr).clickable(true).findOne().click(); 452 | } 453 | if (desc(myStr).clickable(true).exists()) { 454 | desc(myStr).clickable(true).findOne().click(); 455 | } 456 | } 457 | 458 | /** 459 | * 等待进入钉钉登录界面或者主界面 460 | */ 461 | function waitStart() { 462 | let sTime = new Date().getTime(); 463 | let delay = 30000; 464 | 465 | while ((new Date().getTime() - sTime) < delay) { 466 | if (text("忘记密码").exists() || desc("忘记密码").exists() || 467 | text("工作台").exists() || desc("工作台").exists() || 468 | text("密码登录").exists() || desc("密码登录").exists()) { 469 | break; 470 | } 471 | sleep(1000); 472 | } 473 | } 474 | 475 | /** 476 | * 等待打卡按钮出现 477 | */ 478 | function waitBtnShow() { 479 | let sTime = new Date().getTime(); 480 | let delay = 60000; 481 | 482 | while ((new Date().getTime() - sTime) < delay) { 483 | if (textContains("已进入").exists() || descContains("已进入").exists()) { 484 | break; 485 | } 486 | sleep(1000); 487 | } 488 | } 489 | 490 | /** 491 | * 获取当前时间,格式:2019/11/26 15:32:27 492 | */ 493 | function getDateTime(e) { 494 | var date = new Date(); 495 | let year = date.getFullYear(); 496 | let month = date.getMonth() + 1; 497 | let day = date.getDate(); 498 | let hour = date.getHours(); 499 | let minute = date.getMinutes(); 500 | let second = date.getSeconds(); 501 | if (month < 10) { 502 | month = "0" + month; 503 | } 504 | if (day < 10) { 505 | day = "0" + month; 506 | } 507 | if (e) { 508 | return year + '年' + month + '月' + day + '日' + hour + ':' + minute + ':' + second; 509 | } 510 | return year + '-' + month + '-' + day; 511 | } 512 | 513 | /** 514 | * 发送日志并退出脚本 515 | */ 516 | function exitShell() { 517 | if (serverUrl && sendImgRules != "notSend") { 518 | sendMsg(getDateTime(true) + " 打卡结果", myLog); 519 | } 520 | home(); 521 | exit(); 522 | } 523 | 524 | /** 525 | * 通过server酱推送消息 526 | * @param {*} title 标题 527 | * @param {*} msg 内容 528 | */ 529 | function sendMsg(title, msg) { 530 | let url = "https://" + sendImgRules + ".ftqq.com/" + serverUrl + ".send"; 531 | var res = http.post(url, { 532 | "text": title, 533 | "desp": msg 534 | }); 535 | } 536 | 537 | /** 538 | * 保存日志 539 | * @param {*} msg 540 | */ 541 | function setLog(msg) { 542 | log(msg); 543 | msg += '\n\n'; 544 | myLog += msg; 545 | } 546 | 547 | /** 548 | * 根据当前时间返回是上班打卡,还是下班打卡 549 | */ 550 | function getOptByTime() { 551 | let now = new Date(); 552 | let yearStr = (now.getFullYear()) + "/" + (now.getMonth() + 1) + "/" + (now.getDate()) + ' '; 553 | 554 | for (i = 0; i < goToWorkTime.length; i++) { 555 | let e = goToWorkTime[i]; 556 | let morStartTime = e.split('-')[0]; 557 | let morEndTime = e.split('-')[1]; 558 | //上班打卡时间段->时间类型 559 | let morStart = new Date(yearStr + morStartTime); 560 | let morEnd = new Date(yearStr + morEndTime); 561 | //判断当前时间是否可以进行上班打卡 562 | if (now > morStart && now < morEnd) { 563 | return "上班打卡"; 564 | } 565 | } 566 | 567 | for (j = 0; j < afterWorkTime.length; j++) { 568 | let e = afterWorkTime[j]; 569 | let nightStartTime = e.split('-')[0]; 570 | let nightEndTime = e.split('-')[1]; 571 | //下班打卡时间段->时间类型 572 | let nightStart = new Date(yearStr + nightStartTime); 573 | let nightEnd = new Date(yearStr + nightEndTime); 574 | //判断当前时间是否可以进行下班打卡 575 | if (now > nightStart && now < nightEnd) { 576 | return "下班打卡"; 577 | } 578 | } 579 | 580 | return -1; 581 | } 582 | 583 | /** 584 | * 钉钉可能加入了多个公司,通过意图进入打卡页面会提示选择 585 | */ 586 | function handleOrgDialog() { 587 | if ("" == companyName || null == companyName) { 588 | return; 589 | } 590 | let delay = 30000; 591 | const flagStr = "请选择你要进入的考勤组织"; 592 | let sTime = new Date().getTime(); 593 | while ((new Date().getTime() - sTime) < delay) { 594 | if (text(flagStr).exists() || desc(flagStr).exists()) { 595 | if (textContains(companyName).clickable(true).exists()) { 596 | textContains(companyName).findOne().click(); 597 | setLog("选择公司:" + companyName); 598 | return; 599 | } 600 | if (descContains(companyName).clickable(true).exists()) { 601 | descContains(companyName).findOne().click(); 602 | setLog("选择公司:" + companyName); 603 | return; 604 | } 605 | } else { 606 | sleep(1000); 607 | } 608 | } 609 | } 610 | 611 | /** 612 | * 打开打卡页面 613 | */ 614 | function goToPage() { 615 | toastLog("打开钉钉中..."); 616 | launch("com.alibaba.android.rimet"); 617 | waitStart(); 618 | log("启动完成"); 619 | loginIfNeed(); 620 | sleep(waitTime * 1000); 621 | setLog("进入打卡页面"); 622 | var a = app.intent({ 623 | action: "VIEW", 624 | data: "dingtalk://dingtalkclient/page/link?url=https://attend.dingtalk.com/attend/index.html" 625 | }); 626 | app.startActivity(a); 627 | } 628 | 629 | /** 630 | * 检查权限 631 | */ 632 | function checkMyPermission() { 633 | // 1.检查当前是否是打卡时间段 634 | myStr = getOptByTime(); 635 | if (-1 === myStr) { 636 | setLog("当前时间不在设置的考勤范围内!!!"); 637 | exitShell(); 638 | } 639 | 640 | // 2.根据配置跳过节假日或周末 641 | if ("rule_1" == jumpRules) { 642 | let holidayArray = myCfg.get(holidayCfgName); 643 | if (holidayArray.indexOf(getDateTime(false)) != -1) { 644 | setLog("今天是节假日, 不会打卡哦~"); 645 | exitShell(); 646 | } 647 | } else if ("rule_2" == jumpRules) { 648 | let week = new Date().getDay(); 649 | if (week == 6 || week == 0) { 650 | setLog("今天是周末, 不会打卡哦~"); 651 | exitShell(); 652 | } 653 | } else if ("rule_3" == jumpRules) { 654 | let week = new Date().getDay(); 655 | let holidayArray = myCfg.get(holidayCfgName); 656 | if (holidayArray.indexOf(getDateTime(false)) != -1 || week == 6 || week == 0) { 657 | setLog("今天是节假日, 不会打卡哦~"); 658 | exitShell(); 659 | } 660 | } 661 | 662 | // 3.检查无障碍权限 663 | if (auto.service == null) { 664 | setLog("请打开无障碍服务,脚本退出!!!"); 665 | sleep(3000); 666 | app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" }); 667 | exitShell(); 668 | } 669 | 670 | // 4.请求截图权限 671 | if (tokenUrl || sendImgRules != "rule_0") { 672 | // 自动点击申请截图权限时的按钮 673 | threads.start(function () { 674 | let timer = setInterval(function () { 675 | if (text("立即开始").clickable(true).exists()) { 676 | text("立即开始").clickable(true).findOne().click(); 677 | clearInterval(timer); 678 | } else if (desc("立即开始").clickable(true).exists()) { 679 | desc("立即开始").clickable(true).findOne().click(); 680 | clearInterval(timer); 681 | } 682 | }, 500); 683 | }); 684 | 685 | if (!requestScreenCapture()) { 686 | setLog("申请截图权限失败"); 687 | exitShell(); 688 | } 689 | } 690 | 691 | toastLog("权限检查完毕"); 692 | } 693 | --------------------------------------------------------------------------------