100行代码 实现 钉钉实现自动打卡

版权声明:本文为博主原创文章,转载请声明出处 https://blog.csdn.net/qq_16666847/article/details/84108226

越有钱的公司越不会在意这些打卡的字面东西,也不会让你天天写个日报啥的。不写还罚钱 迟到还扣钱。
emmm… 当一个公司开始计较这些东西的时候 ,他就开始走下坡路了。废话不多说。开始走起我们程序员的翻盘之路


你如果需要使用的:

  1. 安装上边的手机 软件安装包
  2. 下载 代码 文件
  3. 修改你需要上传的 ftp(非必要,你需要有一个ftp账号) ,或修改 邮件账号 密码(非必要,你需要有一个邮箱以及安装python,不过你可以用 bat 实现发送邮箱)
  4. 一个手机 ,一条数据线 ,连接 电脑 adb
  5. 开启任务管理器 定时开启 continue.bat 和 start.bat

  • 嘿 ,其实不是 100行,得有个 500行吧

说一下实现的效果吧

在这里插入图片描述
在这里插入图片描述

  • 长连接
    1
  • 打卡操作
  • 2
    最后 实现是 ftp 上传的截图 ,每天去看一下图片URL就可以了
    我想过 最后能达到的效果就是 能通过 网站查看 自己有没有打卡,并 发送邮件 通知打卡成功。
    你们get 到技能后 如果再做得细一点就是 异步回调 短信通知,等等

首先说一下在网上搜索到的各种实现方式

  1. 无障碍服务 AccessibilityService + ROOT 手机使用 ADB 命令 (适用于 root手机)
  2. 无障碍服务 AccessibilityService 模式 + 7.0手机的无障碍手势 dispatchGesture()(适用于7.0以后的手机)这是我反编译一个成型软件看到的方法,屌屌的
  3. 无障碍服务 AccessibilityService + 电脑 ADB 连接后 发送shell 指令(适用一切手机,缺陷得连电脑,不过电脑开机让他黑屏就好)
  4. 编译Android 源码 拿到 系统签名 使用 Instrumentation 类实现 (太麻烦)
  5. IOS手机修改系统地图就好
  6. 采用向日葵远程连接电脑,电脑Vysor 连接 手机 ,另一台手机 安装向日葵 控制端 手动打卡 (小白也可以做成功,但是 成本两手机 + 电脑,以前我用这个但是 不智能,所以我就写了此脚本 )
  • 第一种方式 网上一大堆 这里不做介绍,小白式编程
  • 第二种方式 新的 API 去看看就好了 谷歌搜索 无障碍手势 dispatchGesture()就能做
  • 第三种方式 ,要做就做支持全款手机的,省的找个root手机 或7.0测试机费劲,所以我用的这个

电脑端需要做的:

  • 把这几个文件 放到一个文件夹中
    • adbshell.bat 是发送shell命令 并判断是否打卡
    • click.bat 是上传打卡结果截图发送到服务器
    • continue.bat 是重复发送adb命令 保持长连接,不然OPPO 这类手机 会10分钟自动关掉开发者模式需要加到任务计划中Dcontinue
    • start.bat 是任务开始,需要加到任务计划中Dstart
    • 在这里插入图片描述
  • 任务计划程序 实现 定时开启 程序 (怎么添加任务?任务计划程序库上 右键 创建任务)
    在这里插入图片描述
  • 其实这样完事了

bat脚本代码实现:

  • start.bat
:: 非工作日 才打卡,不是工作日退出就好
:: 路径为你放上边那几个bat文件的路径
cd C:\Users\newone\Desktop\shell 
set day=%date:~-1%
if %day%==六 (
	echo 非工作日
	exit
)
if %day%==七 (
	echo 非工作日
	exit
)
adbshell.bat
exit
  • continue.bat
:: 编码格式 GB2312  
@echo off
echo 重复发送指令 保持 adb连接,一分钟跑一次 (非工作日执行)
set day=%date:~-1%
:a
if %day%==六 (
	echo %day%
	choice /t 5 /d y /n >nul
	exit
)
if %day%==七 (
	echo %day%
	choice /t 5 /d y /n >nul
	exit
)
adb shell dumpsys activity | findstr "mFocusedActivity"
choice /t 60 /d y /n >nul
goto a
  • adbshell.bat 主要逻辑实现Android 开启服务 开启钉钉页面
:: 编码格式 GB2312 
@echo off
echo 服务进行中...
:: 查看是否锁屏?
del screen.txt
choice /t 1 /d y /n >nul
adb shell "dumpsys window policy|grep mShowingLockscreen" >>screen.txt
choice /t 1 /d y /n >nul
set /p a=<screen.txt
echo %a%|findstr "mShowingLockscreen=true"
if %errorlevel% equ 0 (  
	:: 锁屏状态开屏(不可设置密码或指纹)
	adb shell input keyevent 26
	adb shell input keyevent 3
	adb shell input swipe 300 1800 300 800
)
::adb shell settings get secure enabled_accessibility_services获取无障碍列表
::adb 指定 到 无障碍服务 并关闭其他已开启的无障碍
adb shell settings put secure enabled_accessibility_services top.jplayer.quick_test1.debug/top.jplayer.baseprolibrary.alive.service.CustomAccessibilityService 
:: 1 开启服务
adb shell settings put secure accessibility_enabled 1

::回到手机主页
adb shell input keyevent 3
::开启服务
adb shell am startservice  top.jplayer.quick_test1.debug/top.jplayer.baseprolibrary.alive.service.WhiteService>nul
choice /t 2 /d y /n >nul
adb shell am start -n com.alibaba.android.rimet/com.alibaba.android.rimet.biz.SplashActivity>nul
choice /t 10 /d y /n >nul
:a
set HOUR=%time:~0,2%  
set MINUTE=%time:~3,2%  
set SECOND=%time:~6,2%  
set CURRENT_TIME=%HOUR%:%MINUTE%:%SECOND%  
set yy=%date:~0,4%
set mm=%date:~5,2%
set dd=%date:~8,2%
set hh=%time:~0,2%
set mn=%time:~3,2%
set ss=%time:~6,2%
set filename=%yy%%mm%%dd%%hh%%mn%%ss%
set delay=5
set /a am=%random%
set /a am=am%%600+1
set /a pm=%random%
set /a pm=pm%%600+1
:: 打卡操作
if %HOUR% LEQ 16  if %HOUR% GEQ 11 (
	echo 非打卡时间
	echo 等待时间:%am%
	echo 等待时间:%pm%
	adb shell dumpsys activity | findstr "mFocusedActivity">nul
	echo adb 指令重复发送中
	choice /t 60 /d y /n >nul
	goto a
)
if %HOUR% LEQ 10  if %HOUR% GEQ 8 (
	echo 上班打卡
	echo 等待时间:%am%
	choice /t %am% /d y /n >nul
	adb shell input tap 540 800
	click.bat
	pause
) 
if %HOUR% GEQ   18 (
	echo 下班打卡
	echo 等待时间:%pm%
	choice /t %pm% /d y /n >nul
	adb shell input tap 540 1200
	click.bat	
	pause
)
goto a


  • click.bat ftp 上传 截图 到服务器
:: 获取图片
set delay=5
choice /t %delay% /d y /n >nul
adb shell screencap -p sdcard/screen.png
adb pull sdcard/screen.png
adb shell rm sdcard/screen.png
move /-y screen.png %~sdp0
::ren "screen.png" "%filename%.png"
:: ftp 上传
choice /t %delay% /d y /n >nul
echo open 60.205.30.49>>ftp.up
::ftp 账号
echo bxu2359240250>>ftp.up
::我怎么会告诉你我的ftp密码
echo ********>>ftp.up
:: 刷成 bin 模式 不然图片上传出问题
echo bin>>ftp.up
echo cd /htdocs/>>ftp.up
echo put "screen.png">>ftp.up
echo close>>ftp.up
echo bye>>ftp.up
FTP -s:ftp.up
choice /t %delay% /d y /n >nul
del screen.png
del ftp.up	
del screen.txt
python _email.py
pause
  • _email.py发送邮件到指定邮箱
#coding:utf-8   #强制使用utf-8编码格式
import smtplib  #加载smtplib模块
from email.mime.text import MIMEText
import datetime
from email.utils import formataddr
my_sender='[email protected]' #发件人邮箱账号,为了后面易于维护,所以写成了变量
my_user='[email protected]' #收件人邮箱账号,为了后面易于维护,所以写成了变量
def mail():
    ret=True
    try:
        strTime=datetime.datetime.now().strftime('%Y-%m-%d')
        print(strTime)
        body="<a href='https://jplayer.top/screen.png'>前往查看打卡记录</a>"
        msg=MIMEText(body,"html","utf-8")
        msg['From']=formataddr(["来着自动打卡",my_sender])   #括号里的对应发件人邮箱昵称、发件人邮箱账号
        msg['To']=formataddr(["敬爱的 刘大大",my_user])   #括号里的对应收件人邮箱昵称、收件人邮箱账号
        msg['Subject']="打卡成功提醒" #邮件的主题,也可以说是标题
        server=smtplib.SMTP("smtp.qq.com",25)  #发件人邮箱中的SMTP服务器,端口是25
        server.login(my_sender,"*******")    #括号中对应的是发件人邮箱账号、邮箱密码
        server.sendmail(my_sender,[my_user,],msg.as_string())   #括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件
        server.quit()   #这句是关闭连接的意思
    except Exception as e:   #如果try中的语句没有执行,则会执行下面的ret=False
        ret=False
        print(e)
    return ret

ret=mail()
if ret:
    print("ok") #如果发送成功则会返回ok,稍等20秒左右就可以收到邮件
else:
    print("filed")  #如果发送失败则会返回filed


android 端核心代码实现:

  • 自定义 AccessibilityService
public class CustomAccessibilityService extends AccessibilityService {


    /**
     * 当启动服务的时候就会被调用
     */
    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        settingAccessibilityInfo();
    }

    /**
     * 监听窗口变化的回调
     */
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        //根据事件回调类型进行处理
        int eventType = event.getEventType();

        switch (eventType) {

            //当通知栏发生改变时
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                ToastUtils.init().showQuickToast("通知栏的状态发生改变");
                break;
            //当窗口的状态发生改变时
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
                ToastUtils.init().showQuickToast("窗口的状态发生改变");
                break;
                /*窗口变化*/
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                Observable.timer(1, TimeUnit.SECONDS).compose(new IoMainSchedule<>()).subscribe(aLong -> {
                    String id = "com.alibaba.android.rimet:id/home_bottom_tab_button_work";
                    clickById(findById(id));
                });
                Observable.timer(1, TimeUnit.SECONDS).compose(new IoMainSchedule<>()).subscribe(aLong -> {
                    String id = "com.alibaba.android.rimet:id/oa_entry_title";
                    clickById(findById(id));
                });

                break;

        }
    }

    private void clickByWeb(String text) {
        AccessibilityNodeInfo mAccessibilityNodeInfo = this.getRootInActiveWindow();
        for (int i = 0; i < mAccessibilityNodeInfo.getChildCount(); i++) {
            AccessibilityNodeInfo child = mAccessibilityNodeInfo.getChild(i);
            CharSequence contentDescription = child.getContentDescription();
            LogUtil.str(contentDescription);
        }
        //todo 7.0  dispatchGesture
    }

    private AccessibilityNodeInfo findById(String id) {
        AccessibilityNodeInfo mAccessibilityNodeInfo = this.getRootInActiveWindow();
        AccessibilityNodeInfo ac = null;
        if (mAccessibilityNodeInfo == null)
            return null;
        List<AccessibilityNodeInfo> mNodeInfos = mAccessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        if (mNodeInfos == null || mNodeInfos.isEmpty())
            return null;
        for (AccessibilityNodeInfo info : mNodeInfos) {
            CharSequence contentDescription = info.getContentDescription();
            if (info.getContentDescription() != null) {
                boolean contains = contentDescription.toString().contains("工作");
                if (contains) {
                    ac = info;
                }
            }
            CharSequence text = info.getText();
            if (text != null && text.toString().contains("考勤打卡")) {
                ac = info;
            }
        }
        return ac;
    }

    private void clickById(AccessibilityNodeInfo ac0) {
        if (ac0 != null) {
            CharSequence contentDescription = ac0.getContentDescription();
            if (contentDescription != null) {
                boolean contains = contentDescription.toString().contains("工作");
                if (contains) {
                    ac0.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
            CharSequence text = ac0.getText();
            if (text != null && "考勤打卡".equals(text)) {
                ac0.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    private void clickById(String id) {
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();

        if (nodeInfo != null) {
            List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(id);
            LogUtil.str(list + "----------what");
            if (list != null && !list.isEmpty()) {
                AccessibilityNodeInfo accessibilityNodeInfo = list.get(0);
                LogUtil.str(accessibilityNodeInfo + "-----");
            }

        }
    }
    //  dispatchGesture()

    private void clickByText(String text) {
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText(text);
        AccessibilityNodeInfo ac0;
        for (AccessibilityNodeInfo info : list) {
            ac0 = info;
            if (text.equals(ac0.getContentDescription())) {
                ac0.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
            LogUtil.str(ac0);
        }
    }

    /**
     * 中断服务的回调
     */
    @Override
    public void onInterrupt() {

    }

    private void settingAccessibilityInfo() {
        String[] packageNames = {"com.alibaba.android.rimet"};
        AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo();
        // 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
        mAccessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        // 反馈给用户的类型,这里是语音提示
        mAccessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
        // 过滤的包名
        mAccessibilityServiceInfo.packageNames = packageNames;
        setServiceInfo(mAccessibilityServiceInfo);
    }
}

  • 开启服务代码

public class WhiteService extends Service {

    private final static int FOREGROUND_ID = 1000;

    @Override
    public void onCreate() {
        LogUtil.e("WhiteService->onCreate");
        super.onCreate();
    }


    @Override

    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.e("WhiteService->onStartCommand");
        Intent activityIntent = new Intent(BuildConfig.APPLICATION_ID + ".main.live");
        Notification notification = NotificationUtil.init(this).pendingIntent(activityIntent, "白色服务", "服务运行中...");
        startForeground(FOREGROUND_ID, notification);
        Intent intent1 = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
        intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent1);
        startOutActivity("com.alibaba.android.rimet", "com.alibaba.android.rimet.biz.SplashActivity");
        return START_STICKY;
    }

    public void startOutActivity(String sPkg, String tClass) {
        try {
            ComponentName cn = new ComponentName(sPkg, tClass);
            Intent i = new Intent();
            i.setComponent(cn);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);
        } catch (Exception e) {
            e.printStackTrace();
            ToastUtils.init().showQuickToast("无法查找Activity");
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onDestroy() {
        LogUtil.e("WhiteService->onDestroy");
        super.onDestroy();
    }
}

主要是实现手段 ,不是拿过来用

猜你喜欢

转载自blog.csdn.net/qq_16666847/article/details/84108226