Python远程登陆和控制实战

1.目的和流程

  目的:就是控制电脑来执行相应的需求,这个很容易理解,至于为什么,因人而异!
  流程:

  1. 使用itchat实现登陆
  2. 因为itchat本身只有打开图片扫描和在命令行显示登陆,显然这不符合远程登陆的目的,故自己写一个基于python的email的邮件发送。
  3. 把邮件发送的方法注册进itchat的登陆代码
  4. 远程扫码登陆过后,检测微信filehelper发来的信息,实现命令解析
  5. 根据相关命令完成命令实现类

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,无法进行对比。
  再这里推荐一个网站,写邮件可以参考一下 Pyhton电子邮件发送和接受


感谢大家支持!人生苦短,我用python!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值