流程一览
- 1.目的和流程
- 2.代码实现
- 3.要点讲解
- 3.1 注册发送邮件的代码
- 3.2 itchat的信息留存问题
- 3.3 发送邮件的问题
1.目的和流程
目的:就是控制电脑来执行相应的需求,这个很容易理解,至于为什么,因人而异!
流程:
- 使用itchat实现登陆
- 因为itchat本身只有打开图片扫描和在命令行显示登陆,显然这不符合远程登陆的目的,故自己写一个基于python的email的邮件发送。
- 把邮件发送的方法注册进itchat的登陆代码
- 远程扫码登陆过后,检测微信filehelper发来的信息,实现命令解析
- 根据相关命令完成命令实现类
2.代码实现
- 邮件发送代码(send_email.py)
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.header import Header
from smtplib import SMTP_SSL
from email.utils import parseaddr, formataddr
from email import encoders
def _format_addr(s):
"""
处理邮件头的包装
:param s: 发送的信息 雪梅长青<*********@qq.com>
:return: 包装后的信息 =?utf-8?b?6Zuq5qKF6ZW/6Z2S?= <*********@qq.com>
"""
name, addr = parseaddr(s)
return formataddr((Header(name, 'utf-8').encode(), addr))
def send_mail(picAddr):
"""
发送邮件,携带附件。填写自己的邮箱,自己发给自己就可以了。
:param picAddr: 附件地址
:return: None
"""
host_server = 'smtp.qq.com'
sender_qq = '*********@qq.com'
# pwd为126邮箱的授权码
pwd = '******************'
# 发件人的邮箱
sender_qq_mail = '雪梅长青<*********@qq.com>'
# 收件人邮箱
receiver = '<*********@qq.com>'
# 邮件的正文内容
mail_content = '<html><body><h1>Please scan the QR code to log in.</h1><p><img src="cid:0"></p></body></html>'
# 邮件标题
mail_title = 'QR-Login'
# ssl登录
smtp = SMTP_SSL(host_server)
# set_debuglevel()是用来调试的。参数值为1表示开启调试模式,参数值为0关闭调试模式
smtp.ehlo(host_server) # 向Gamil发送SMTP 'ehlo' 命令
smtp.login(sender_qq, pwd)
msg = MIMEMultipart()
msg['Subject'] = Header(mail_title, 'utf-8').encode()
msg['From'] = _format_addr(sender_qq_mail)
msg['To'] = _format_addr(receiver)
msg.attach(MIMEText(mail_content, 'html', 'utf-8'))
# 添加附件就是加上一个MIMEBase,从本地读取一个图片:
with open(picAddr, 'rb') as f:
# 设置附件的MIME和文件名,这里是png类型:
mime = MIMEBase('image', 'png', filename='QR.png')
# 加上必要的头信息:
mime.add_header('Content-Disposition', 'attachment', filename='QR.png')
mime.add_header('Content-ID', '<0>')
mime.add_header('X-Attachment-Id', '0')
# 把附件的内容读进来:
mime.set_payload(f.read())
# 用Base64编码:
encoders.encode_base64(mime)
# 添加到MIMEMultipart:
msg.attach(mime)
smtp.sendmail(sender_qq_mail, receiver, msg.as_string())
smtp.quit()
- itchat登陆和接收信息代码(main.py)
from send_email import send_mail
import itchat
from itchat.content import *
import cv2
from threading import Thread, Timer
import os
import time
TEMP_IMAGE = 'temp_photo.jpg'
USER_NAME = 'filehelper'
class CMD(object):
"""
命令的集合
"""
def __init__(self):
self.ex_list = []
def shutdown(self, seconds):
"""
关机
:param seconds: 执行时间
:return: None
"""
s = Thread(target=lambda x: os.system(f'shutdown -s -t {x}'), args=(seconds,))
s.start()
def restart(self, seconds):
"""
重启
:param seconds: 执行时间
:return: None
"""
r = Thread(target=lambda x: os.system(f'shutdown -r -t {x}'), args=(seconds,))
r.start()
def stop(self, seconds):
"""
停止关机,重启命令
:param seconds: 执行时间
:return: None
"""
time.sleep(seconds)
os.system('shutdown -a')
def exits(self, seconds):
"""
退出程序
:param seconds: 执行时间
:return: None
"""
time.sleep(seconds)
os._exit(0)
def over(self):
"""
命令开始执行
:return: None
"""
pass
def null(self, seconds):
"""
当命令不存在时
:param seconds: 不执行
:return: None
"""
itchat.send_msg('命令不存在!', toUserName=USER_NAME)
def kill360(self, seconds):
"""
kill360浏览器
:param seconds: 执行时间
:return: None
"""
ex360 = Timer(seconds, function=self.__killEntity, args=['360se'])
self.ex_list.append(ex360)
ex360.start()
def killchrome(self, seconds):
"""
killchrome浏览器
:param seconds: 执行时间
:return: None
"""
exchrome = Timer(seconds, function=self.__killEntity, args=['chrome'])
self.ex_list.append(exchrome)
exchrome.start()
def __killEntity(self, obj):
"""
杀死浏览器线程的实体
:param obj: 浏览器对象
:return: None
"""
os.system(f'taskkill /F /IM {obj}.exe')
itchat.send_msg(f'{obj}浏览器已kill!', toUserName=USER_NAME)
def killstop(self, seconds):
"""
停止线程的执行
:param seconds: 执行时间
:return: None
"""
time.sleep(seconds)
for ex in self.ex_list:
if ex.isAlive():
ex.cancel()
itchat.send_msg('浏览器的kill行为已停止!', toUserName=USER_NAME)
def takephoto(self, seconds):
"""
拍照保存
:param seconds: 执行时间
:return: 拍照是否成功
"""
time.sleep(seconds)
cap = cv2.VideoCapture(0)
cap.set(3, 1920) # 设置分辨率 高度
cap.set(4, 1080) # 设置分辨率 宽度
while True:
ret, frame = cap.read()
if ret:
cv2.imwrite(TEMP_IMAGE, frame)
break
cap.release()
itchat.send_image(TEMP_IMAGE, toUserName=USER_NAME)
if os.path.exists(TEMP_IMAGE):
os.remove(TEMP_IMAGE)
cmd = CMD()
choose_value = {}
@itchat.msg_register(TEXT)
def cmd_accept(msg):
"""
微信接收发送消息
:param msg: 消息对象
:return: None
"""
try:
if msg.user['UserName'] == USER_NAME:
msg2cmd, times = msg['Content'].split(' ')
if not times.isdigit():
msg.user.send(u'参数2为数字!')
else:
times = int(times)
if msg2cmd == 'over':
if hasattr(cmd, choose_value.get('cmd', 'null')):
time.sleep(times)
msg.user.send(u'命令开始执行!! exits不返回!')
getattr(cmd, choose_value.get('cmd', 'null'))(choose_value.get('time', 10))
msg.user.send(u'执行成功!')
time.sleep(1)
else:
choose_value['cmd'] = msg2cmd
choose_value['time'] = times
except ValueError:
msg.user.send(u'命令有错误!')
except Exception:
msg.user.send(u'程序BUG!')
finally:
time.sleep(0.5)
def login_wechat():
"""
登陆设置:使用自定义登陆,邮件发送
:return: None
"""
itchat.utils.print_qr = send_mail
itchat.auto_login(hotReload=True)
def run():
"""
主运行逻辑
:return: None
"""
login_wechat()
itchat.run()
if __name__ == "__main__":
"""
main
"""
run()
3.要点讲解
3.1 注册发送邮件的代码
根据itchat的源代码可知:
def get_QR(self, uuid=None, enableCmdQR=False, picDir=None, qrCallback=None):
uuid = uuid or self.uuid
picDir = picDir or config.DEFAULT_QR
qrStorage = io.BytesIO()
qrCode = QRCode('https://login.weixin.qq.com/l/' + uuid)
qrCode.png(qrStorage, scale=10)
if hasattr(qrCallback, '__call__'):
qrCallback(uuid=uuid, status='0', qrcode=qrStorage.getvalue())
else:
if enableCmdQR:
utils.print_cmd_qr(qrCode.text(1), enableCmdQR=enableCmdQR)
else:
with open(picDir, 'wb') as f:
f.write(qrStorage.getvalue())
utils.print_qr(picDir) # 会在这里打开图片,所以重写这个方法就好
return qrStorage
def print_qr(fileDir): # 原本的源代码
if config.OS == 'Darwin':
subprocess.call(['open', fileDir])
elif config.OS == 'Linux':
subprocess.call(['xdg-open', fileDir])
else:
os.startfile(fileDir)
# 我们在auto_login()执行之前重新对它赋值就可以了。因为它在导入的时候已经有了,你再重新赋值,后续的代码就会使用你的函数方法。
itchat.utils.print_qr = send_mail
itchat.auto_login(hotReload=True)
原本我想重写它的 Core 类的,发现怎么都注册不进去,可能是它的内部调用太复杂了,或者是还没有找对方法或位置。不过对于 Core.get_QR 函数的重写倒是并没有问题。当然最简单的就是直接重写 print_qr 方法
3.2 itchat的信息留存问题
为什么我要使用一个字典choose_value,因为网页微信可能有信息留存问题,比如上一次exits我退出之后,在重新运行会再次出现exits就直接退出了,所以为了避免,我就使用两层命令来判断,第一次命令保存到字典里,第二次发送over就执行命令,不过每次命令后面都要带一个整数,表示执行时间。
3.3 发送邮件的问题
之前因为一些需求写了126邮箱发送邮件,但是他会一直报一个错,大概意思就是你发送的邮件不符合标准邮件
smtplib.SMTPDataError: (554, b'DT:SPM 163 smtp10,DsCowAA3h9_QbgZXI9_fCQ--.713S2 1460039376,please see http://mail.163.com/help/help_spam_16.htm?ip=117.114.147.187&hostid=smtp10&time=1460039376')
后来发现只是邮件头的构造不够要求,需要用以下函数进行构造
def _format_addr(s):
"""
处理邮件头的包装
:param s: 发送的信息 雪梅长青<*********@qq.com>
:return: 包装后的信息 =?utf-8?b?6Zuq5qKF6ZW/6Z2S?= <*********@qq.com>
"""
name, addr = parseaddr(s)
return formataddr((Header(name, 'utf-8').encode(), addr))
后来还发现一个问题,就是无法短时间大量给同一个用户发送邮件,但是看别人的vbs就可以实现,不会被检测,故猜测邮箱的接受都是同一个api接口,有问题应该还是在于整封邮件的构造上有区别。不会vbs,无法进行对比。
感谢大家支持!人生苦短,我用python!