这是一个 2.0 版的免补签工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
from playwright.sync_api import sync_playwright
import json
import time
from bs4 import BeautifulSoup
from email.mime.text import MIMEText
from email.header import Header
from smtplib import SMTP_SSL
import traceback


class kq():
def __init__(self, username, password, receiver=None):
self.username = username
self.password = password
self.x_value = 0
if receiver:
self.receiver = receiver

def send_email(self, msg, receiver):
host_server = '' # 邮箱smtp服务器
user = '' # user 为发件人的邮箱号码
pwd = '' #pwd 为邮箱的授权码
sender_mail = '' #发件人的邮箱
#邮件的正文内容
mail_content = msg

#添加机器人签名
mail_sign = \
'''
\n
---

No need to reply!
Contact me: w-gx@qq.com
'''

#邮件标题
mail_title = '【打卡消息推送】'
#ssl登录
smtp = SMTP_SSL(host_server)
#set_debuglevel()是用来调试的。参数值为1表示开启调试模式,参数值为0关闭调试模式
smtp.set_debuglevel(0)
smtp.ehlo(host_server)
smtp.login(user, pwd)

msg = MIMEText(mail_content + mail_sign, "plain", 'utf-8')
msg["Subject"] = Header(mail_title, 'utf-8')
msg["From"] = sender_mail
msg["To"] = receiver
smtp.sendmail(sender_mail, receiver, msg.as_string())
smtp.quit()

def run(self, playwright):
retry = 0
browser = playwright.chromium.launch(headless=True)
context = browser.new_context()

# Open new page
page = context.new_page()

# Add response handler
page.on('response', lambda response: self.handle_response(response))

page.goto("")

user_input_box = page.locator("xpath=/html/body/div/form/div[1]/div/div/div/div[2]/div[2]/input")
pwd_input_box = page.locator("xpath=/html/body/div/form/div[1]/div/div/div/div[2]/div[3]/input")
user_input_box.fill(self.username)
pwd_input_box.fill(self.password)

login_button = page.locator("xpath=/html/body/div/form/div[1]/div/div/div/div[2]/div[4]/input")
login_button.click()

# Wait for navigation or action to complete
page.wait_for_timeout(5000)
while True:
x_percent = (360 - self.x_value) / 360
print(self.x_value)
# Wait for the slider and track elements to appear and calculate the move distance based on the track width and x_value percentage
slider_handle = page.wait_for_selector('.captcha-control-button')
slider_track = page.wait_for_selector('.captcha-control-wrap') # Assuming this is the slider track

# Get bounding box of slider handle and track
slider_handle_box = slider_handle.bounding_box()
print(slider_handle_box['width'])
slider_track_box = slider_track.bounding_box()
print(slider_track_box['width'])

# Calculating the move distance based on the track width and the percentage obtained from x_value
move_distance = slider_track_box['width'] * x_percent

# Starting point for the movement
start_x = slider_handle_box['x'] + slider_handle_box['width'] / 2
start_y = slider_handle_box['y'] + slider_handle_box['height'] / 2

# Ending X coordinate based on the calculated distance
end_x = start_x + move_distance - slider_handle_box['width'] / 2

# Simulate the drag and drop action
page.mouse.move(start_x, start_y)
page.mouse.down()
page.mouse.move(end_x, start_y, steps=20) # Use steps to simulate a more realistic drag action
page.mouse.up()

time.sleep(3)

try:
daka_button = page.locator("xpath=/html/body/div/body/div[2]/div[2]/div[2]/div[2]/a[1]")
daka_button.click()
break
except:
retry += 1
if retry >= 20:
print("Failed to click the daka button")
break
print("Verification failed, try again.")
continue

page.wait_for_timeout(5000)
msg = '【打卡成功】\n'
msg += self.parse_daka(page.content())
print(msg)

if self.receiver:
self.send_email(msg, self.receiver)
context.close()
browser.close()

def parse_daka(self, html_content):
# 使用BeautifulSoup解析HTML
soup = BeautifulSoup(html_content, 'html.parser')

# 初始化结果列表
result = []

# 找到表格
table = soup.find('table', {'class': 'kq-message-table'})

# 获取表头中的所有th标签,用于列名称
headers = []
for th in table.find('thead').find_all('th'):
# 每个th标签,获取文本并消除空格和换行符
headers.append(' '.join(th.stripped_strings))

# 遍历表格的每一行
for row in table.find('tbody').find_all('tr'):
# 获取行中的所有td标签
cells = row.find_all('td')
# 每一行的数据都以字典形式存在,字典的键来自标题,值来自单元格数据
data = {}
for index, cell in enumerate(cells):
key = headers[index]
# 提取每个单元格的文本作为数据
data[key] = cell.get_text(strip=True)
# 将每行数据添加至结果列表
result.append(data)

# 将结果列表转换为JSON格式
json_data = json.dumps(result, ensure_ascii=False, indent=4)
return json_data

def handle_response(self, response):
if response.url == 'https://xxxx/attendance_api/services/login/imageRoute':
json_body = response.json()
self.x_value = int(json_body["data"]["imageVerificationVo"]["x"])
if response.url == 'https://xxxx/attendance_api/services/login/verifyRoute':
json_body = response.json()
if json_body["code"] == 0:
self.verification = 1
print("Verification successful")
else:
self.verification = 0
print("Verification failed")


with sync_playwright() as playwright:
username = ""
password = ""
receiver = ""
kq = kq(username, password, receiver)
retry = 0
while retry < 5:
try:
kq.run(playwright)
break
except Exception as e:
retry += 1
msg = "【打卡失败】\n" + traceback.format_exc()
kq.send_email(msg,receiver)
continue