100行程式碼 實現 釘釘實現自動打卡
阿新 • • 發佈:2018-12-20
越有錢的公司越不會在意這些打卡的字面東西,也不會讓你天天寫個日報啥的。不寫還罰錢 遲到還扣錢。
emmm… 當一個公司開始計較這些東西的時候 ,他就開始走下坡路了。廢話不多說。開始走起我們程式設計師的翻盤之路
你如果需要使用的:
- 安裝上邊的手機 軟體安裝包
- 下載 程式碼 檔案
- 修改你需要上傳的 ftp(非必要,你需要有一個ftp賬號) ,或修改 郵件賬號 密碼(非必要,你需要有一個郵箱以及安裝python,不過你可以用 bat 實現傳送郵箱)
- 一個手機 ,一條資料線 ,連線 電腦 adb
- 開啟工作管理員 定時開啟 continue.bat 和 start.bat
- 嘿 ,其實不是 100行,得有個 500行吧
說一下實現的效果吧
- 長連線
- 打卡操作
最後 實現是 ftp 上傳的截圖 ,每天去看一下圖片URL就可以了
我想過 最後能達到的效果就是 能通過 網站檢視 自己有沒有打卡,並 傳送郵件 通知打卡成功。
你們get 到技能後 如果再做得細一點就是 非同步回撥 簡訊通知,等等
首先說一下在網上搜索到的各種實現方式
- 無障礙服務 AccessibilityService + ROOT 手機使用 ADB 命令 (適用於 root手機)
- 無障礙服務 AccessibilityService 模式 + 7.0手機的無障礙手勢 dispatchGesture()(適用於7.0以後的手機)這是我反編譯一個成型軟體看到的方法,屌屌的
- 無障礙服務 AccessibilityService + 電腦 ADB 連線後 傳送shell 指令(適用一切手機,缺陷得連電腦,不過電腦開機讓他黑屏就好)
- 編譯Android 原始碼 拿到 系統簽名 使用 Instrumentation 類實現 (太麻煩)
- IOS手機修改系統地圖就好
- 採用向日葵遠端連線電腦,電腦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();
}
}