python wechat chat robot is no longer afraid of being praised for its high Eq

Writing this chat robot is purely fun, it is not practical!!!

Realization idea

In the past, many wechat robots relied on the itchat in python to write, because at that time, the wechat web page was still developed for most users, and the itchat module operated on the wechat web page (it felt like a crawler). But now many people's wechat can't log on to the wechat web page, which makes the wechat robot relying on itchat unusable. I want to be a wechat robot. After looking for a method that does not rely on itchat for a long time, I see two more appropriate methods. One is Reverse Hook analysis of PC wechat client (there is source code on github), the other is the method used in this article( 👉 Method source)
The realization idea is as follows:

Complete code

import pyautogui 
pyautogui.FAILSAFE = False #Prevent failSafeCheck() from reporting errors
from ctypes import windll
import win32api,win32con,pyperclip
import win32gui
import win32ui
import ocr
from PIL import Image
import time


'''Get window handle'''
def gethandle(Class,Caption):
    return win32gui.FindWindow(Class,Caption)

'''Get the coordinates of the upper left and lower right corners of the window'''
def getposition(handle):
    if handle==0:
        return None
    else:
        return win32gui.GetWindowRect(handle)

'''Take a screenshot of the window and save it'''
def Screenshot(hWnd,file_path):
    #Gets the size information of the handle window
    left, top, right, bot = win32gui.GetWindowRect(hWnd)
    width = right - left
    height = bot - top
    #Returns the device environment of the handle window, covering the whole window, including non client area, title bar, menu and border
    hWndDC = win32gui.GetWindowDC(hWnd)
    #Create device description table
    mfcDC = win32ui.CreateDCFromHandle(hWndDC)
    #Create memory device description table
    saveDC = mfcDC.CreateCompatibleDC()
    #Create a bitmap object and prepare to save the picture
    saveBitMap = win32ui.CreateBitmap()
    #Open up storage space for bitmap
    saveBitMap.CreateCompatibleBitmap(mfcDC,width,height)
    #Save the screenshot to saveBitMap
    saveDC.SelectObject(saveBitMap)
    #Save bitmap to memory device description table
    saveDC.BitBlt((0,0), (width,height), mfcDC, (0, 0), win32con.SRCCOPY)
    #If you want to take a screenshot to a printing device:
    ###The last int parameter: 0 - save the whole window, 1 - save only the client area. If PrintWindow succeeds, the return value of the function is 1
    result = windll.user32.PrintWindow(hWnd,saveDC.GetSafeHdc(),0)
    #Save image
    ##Method 1: windows api save
    ###Save bitmap to file
    saveBitMap.SaveBitmapFile(saveDC,file_path)
    #print(result) #If PrintWindow succeeds, output 1
    return result
    '''
    ##Method 2 (Part I): PIL preservation
    ###Get bitmap information
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    ###Generate image
    im_PIL = Image.frombuffer('RGB',(bmpinfo['bmWidth'],bmpinfo['bmHeight']),bmpstr,'raw','BGRX',0,1)
    ##Method II (follow up to part II)
    
    ##Method 3 (Part 1): opencv+numpy save
    ###Get bitmap information
    signedIntsArray = saveBitMap.GetBitmapBits(True)
    ##Method III (follow up to part II)
    
    #Memory release
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(hWnd,hWndDC)
    
    ##Method 2 (Part II): PIL preservation
    ###PrintWindow is successfully saved to a file and displayed on the screen
    im_PIL.save("im_PIL.png") #preservation
    im_PIL.show() #display
    
    ##Method 3 (Part 2): opencv+numpy save
    ###PrintWindow is successfully saved to a file and displayed on the screen
    im_opencv = numpy.frombuffer(signedIntsArray, dtype = 'uint8')
    im_opencv.shape = (height, width, 4)
    cv2.cvtColor(im_opencv, cv2.COLOR_BGRA2RGB)
    cv2.imwrite("im_opencv.jpg",im_opencv,[int(cv2.IMWRITE_JPEG_QUALITY), 100]) #preservation
    cv2.namedWindow('im_opencv') #name window
    cv2.imshow("im_opencv",im_opencv) #display
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    '''


'''Process (crop) pictures'''
def cropimg(inimg,outimg):
    #Open picture
    im = Image.open(inimg)
    # The width and height of the picture
    img_size = im.size
    print("The width and height of the picture are{}".format(img_size))
    '''
    Clipping: pass in a tuple as a parameter
    The elements in the tuple are: (distance from the left boundary of the picture) x, Distance from the upper boundary of the picture y,Distance from left edge of picture+Crop box width x+w,Distance from the upper boundary of the picture+Clipping box Height y+h)
    '''
    x = 465
    y = 535
    w = 270
    h = 70
    region = im.crop((x, y, x + w, y + h))

    #Save picture
    region.save(outimg)

'''Call Baidu ocr Recognition text'''
def Getmsg(file_path):
    # Get access token
    token = ocr.fetch_token()

    # Splicing universal character recognition high precision url
    image_url = ocr.OCR_URL + "?access_token=" + token

    text = ""

    # Read test picture
    file_content = ocr.read_file(file_path)

    # Call character recognition service
    result = ocr.request(image_url, ocr.urlencode({'image': ocr.base64.b64encode(file_content)}))
    #print(result)

    # Parsing returned results
    result_json = ocr.json.loads(result)
    for words_result in result_json["words_result"]:
        text = text + words_result["words"]

    # Print text
    print('Character recognition results:')
    print(text)
    return text

'''Compare pictures, similar return True,Otherwise return False'''
def Compare(img1,img2):
    #Open two pictures
    image1=Image.open(img1)
    image3=Image.open(img2)

    #The image object is converted into histogram data and stored in lists H1 and h2
    h1=image1.histogram()
    h2=image3.histogram()
    L1=len(h1)
    L2=len(h2)
    if L1>=L2:
        n=L2
    else:
        n=L1
    sum=0
    for i in range(n):
        sum=sum+abs(h1[i]-h2[i])
    print(sum)
    result=sum/(len(h1)+len(h2)/2)
    print(result)
    if result<=1:
        return True
    else:
        return False

'''Used to access the smart answer website for answers'''
import requests #Import the requests package and use the crawler

def get_remsg(text):
    #Disguised as a browser
    User_Agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47'
    #Online robot
    Furl="https://api.ownthink.com/bot?appid=9ffcb5785ad9617bf4e64178ac64f7b1&spoken="
    url=Furl+text  
    req=requests.get(url,User_Agent) #Ask questions to online robots
    req.encoding='utf-8' #Define the encoding format as utf-8
    re=eval(req.text) #Convert the returned content to dictionary type

    return re['data']['info']['text'] #Return answer

'''send message'''
def send(msg_to_sent,handle,rightx,topy):
    #Position the mouse over the input box
    win32api.SetCursorPos([rightx-400,topy+720])
    # Forced display interface
    win32gui.ShowWindow(handle, win32con.SW_RESTORE)
    # Bring the window to the front
    win32gui.SetForegroundWindow(handle)
    time.sleep(0.5)
    pyperclip.copy(msg_to_sent) # Content to send
    time.sleep(0.5)
    pyautogui.hotkey('ctrl', 'v') # Press ctrl + v to paste the content
    time.sleep(0.5)
    pyautogui.hotkey('enter')
    return 1


if __name__=='__main__':
    #Get window handle
    handle=gethandle('WeChatMainWndForPC','WeChat')

    #Get window size information
    left, top, right, bot=getposition(handle)

    #Take a screenshot of the screen 
    Screenshot(handle,'my.bmp')

    #Process pictures
    cropimg('my.bmp','my.bmp')
    
    #Character recognition of pictures
    text=Getmsg('my.bmp')
    
    #According to the identified content, the reply content is obtained from the online chat robot by using the crawler method
    r=get_remsg(text)
    print('Answer of online robot:')
    print(r)
    
    #send message
    send(r,handle,right,top)
    

code analysis

Import the module to use

import pyautogui 
pyautogui.FAILSAFE = False #Prevent failSafeCheck() from reporting errors
from ctypes import windll
import win32api,win32con,pyperclip
import win32gui
import win32ui
import ocr
from PIL import Image
import time

If you don't have the above library, you can download it yourself. Download method:
Open cmd or PowerShell and enter the pip install library name
For example, import win32gui

pip install win32gui

The import of win32gui, win32con and win32ui is likely to fail (the import of high version is very easy to fail. Don't ask, just try). It is recommended to find a solution by yourself.
In addition, ocr is a local file and cannot be imported through pip. Next, call Baidu ocr to explain the text recognition part.

Get window handle

'''Get window handle'''
def gethandle(Class,Caption):
    return win32gui.FindWindow(Class,Caption)

Use the function FindWindow() in win32gui to obtain the window handle. Class is the class name of the window and Caption is the title of the window. I don't know how to obtain the window class name and title (usually the word in the upper left corner of the window) with code. I view it through spy + + tools in Visual Studio (wechat class name is WeChatMainWndForPC and the title is wechat).

Get the coordinates of the upper left and lower right corners of the window

'''Get the coordinates of the upper left and lower right corners of the window'''
def getposition(handle):
    if handle==0:
        return None
    else:
        return win32gui.GetWindowRect(handle)

Simply call GetWindowRect() in win32gui. This function returns the coordinates of the upper left corner and lower right corner of the window (in the form of tuples)

Take a screenshot of the window and save it

'''Take a screenshot of the window and save it'''
def Screenshot(hWnd,file_path):
    #Gets the size information of the handle window
    left, top, right, bot = win32gui.GetWindowRect(hWnd)
    width = right - left
    height = bot - top
    #Returns the device environment of the handle window, covering the whole window, including non client area, title bar, menu and border
    hWndDC = win32gui.GetWindowDC(hWnd)
    #Create device description table
    mfcDC = win32ui.CreateDCFromHandle(hWndDC)
    #Create memory device description table
    saveDC = mfcDC.CreateCompatibleDC()
    #Create a bitmap object and prepare to save the picture
    saveBitMap = win32ui.CreateBitmap()
    #Open up storage space for bitmap
    saveBitMap.CreateCompatibleBitmap(mfcDC,width,height)
    #Save the screenshot to saveBitMap
    saveDC.SelectObject(saveBitMap)
    #Save bitmap to memory device description table
    saveDC.BitBlt((0,0), (width,height), mfcDC, (0, 0), win32con.SRCCOPY)
    #If you want to take a screenshot to a printing device:
    ###The last int parameter: 0 - save the whole window, 1 - save only the client area. If PrintWindow succeeds, the return value of the function is 1
    result = windll.user32.PrintWindow(hWnd,saveDC.GetSafeHdc(),0)
    #Save image
    ##Method 1: windows api save
    ###Save bitmap to file
    saveBitMap.SaveBitmapFile(saveDC,file_path)
    #print(result) #If PrintWindow succeeds, output 1
    return result
    '''
    ##Method 2 (Part I): PIL preservation
    ###Get bitmap information
    bmpinfo = saveBitMap.GetInfo()
    bmpstr = saveBitMap.GetBitmapBits(True)
    ###Generate image
    im_PIL = Image.frombuffer('RGB',(bmpinfo['bmWidth'],bmpinfo['bmHeight']),bmpstr,'raw','BGRX',0,1)
    ##Method II (follow up to part II)
    
    ##Method 3 (Part 1): opencv+numpy save
    ###Get bitmap information
    signedIntsArray = saveBitMap.GetBitmapBits(True)
    ##Method III (follow up to part II)
    
    #Memory release
    win32gui.DeleteObject(saveBitMap.GetHandle())
    saveDC.DeleteDC()
    mfcDC.DeleteDC()
    win32gui.ReleaseDC(hWnd,hWndDC)
    
    ##Method 2 (Part II): PIL preservation
    ###PrintWindow is successfully saved to a file and displayed on the screen
    im_PIL.save("im_PIL.png") #preservation
    im_PIL.show() #display
    
    ##Method 3 (Part 2): opencv+numpy save
    ###PrintWindow is successfully saved to a file and displayed on the screen
    im_opencv = numpy.frombuffer(signedIntsArray, dtype = 'uint8')
    im_opencv.shape = (height, width, 4)
    cv2.cvtColor(im_opencv, cv2.COLOR_BGRA2RGB)
    cv2.imwrite("im_opencv.jpg",im_opencv,[int(cv2.IMWRITE_JPEG_QUALITY), 100]) #preservation
    cv2.namedWindow('im_opencv') #name window
    cv2.imshow("im_opencv",im_opencv) #display
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    '''

I'm a code Porter, and I didn't look at the content specifically. In a word, it can be used (I'm too lazy to write it, but it's available online Ready made)

Process (crop) pictures

'''Process (crop) pictures'''
def cropimg(inimg,outimg):
    #Open picture
    im = Image.open(inimg)
    # The width and height of the picture
    img_size = im.size
    print("The width and height of the picture are{}".format(img_size))
    '''
    Clipping: pass in a tuple as a parameter
    The elements in the tuple are: (distance from the left boundary of the picture) x, Distance from the upper boundary of the picture y,Distance from left edge of picture+Crop box width x+w,Distance from the upper boundary of the picture+Clipping box Height y+h)
    '''
    x = 465
    y = 535
    w = 270
    h = 70
    region = im.crop((x, y, x + w, y + h))

    #Save picture
    region.save(outimg)

This function cuts the latest screenshot message from the complete screenshot of the chat interface. The effect is similar:

The x, y, w and h parameters in the function should be adjusted according to their own wechat window, and the size can be measured through the wechat screenshot tool. This method is not intelligent, but there is no way. My original idea was to use the wechat window handle as the parent handle to obtain the child handles of the child windows and controls, but I didn't find it when I used the relevant functions in win32gui. I searched the Internet for a long time. Are there any other methods until I saw:


I don't know whether it's right or not, but I changed my mind at that time. If I can get the sub handle, I hope someone can give me some advice

Call Baidu ocr to recognize text

def Getmsg(file_path):
    # Get access token
    token = ocr.fetch_token()

    # Splicing universal character recognition high precision url
    image_url = ocr.OCR_URL + "?access_token=" + token

    text = ""

    # Read test picture
    file_content = ocr.read_file(file_path)

    # Call character recognition service
    result = ocr.request(image_url, ocr.urlencode({'image': ocr.base64.b64encode(file_content)}))
    #print(result)

    # Parsing returned results
    result_json = ocr.json.loads(result)
    for words_result in result_json["words_result"]:
        text = text + words_result["words"]

    # Print text
    print('Character recognition results:')
    print(text)
    return text

This is to call the ocr recognition text on Baidu online. You should open the service on Baidu AI open platform (you can whore for nothing, and there are tutorials in station B). The technical documents have standard interface Codes:

You can copy it directly and modify it based on it.

Used to access the smart answer website for answers

'''Used to access the smart answer website for answers'''
import requests #Import the requests package and use the crawler

def get_remsg(text):
    #Disguised as a browser
    User_Agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47'
    #Online robot
    Furl="https://api.ownthink.com/bot?appid=9ffcb5785ad9617bf4e64178ac64f7b1&spoken="
    url=Furl+text  
    req=requests.get(url,User_Agent) #Ask questions to online robots
    req.encoding='utf-8' #Define the encoding format as utf-8
    re=eval(req.text) #Convert the returned content to dictionary type

    return re['data']['info']['text'] #Return answer

Visit an online chat robot through a crawler and get a reasonable answer 😏

send message

'''send message'''
def send(msg_to_sent,handle,rightx,topy):
    #Position the mouse over the input box
    win32api.SetCursorPos([rightx-400,topy+720])
    # Forced display interface
    win32gui.ShowWindow(handle, win32con.SW_RESTORE)
    # Bring the window to the front
    win32gui.SetForegroundWindow(handle)
    time.sleep(0.5)
    pyperclip.copy(msg_to_sent) # Content to send
    time.sleep(0.5)
    pyautogui.hotkey('ctrl', 'v') # Press ctrl + v to paste the content
    time.sleep(0.5)
    pyautogui.hotkey('enter')
    return 1

This code first uses the right edge coordinate rightx and the top edge coordinate topy of the window to position the mouse to the position of the input box. The offset may be different in different computers. Measure by yourself. The measurement method refers to picture processing (you can't get the sub handle, so you can only use this method). The code then wakes up the minimized window and simulates keyboard and mouse operations through functions in pyperclip and pyautogui. (it seems that win32gui.SendMessage() can also be used, but I didn't achieve the desired effect during operation, so I changed to this method)

Main function

if __name__=='__main__':
    #Get window handle
    handle=gethandle('WeChatMainWndForPC','WeChat')

    #Get window size information
    print(getposition(handle))
    left, top, right, bot=getposition(handle)

    #Take a screenshot of the screen 
    Screenshot(handle,'my.bmp')

    #Process pictures
    cropimg('my.bmp','my.bmp')
    
    #Character recognition of pictures
    text=Getmsg('my.bmp')
    
    #According to the identified content, the reply content is obtained from the online chat robot by using the crawler method
    r=get_remsg(text)
    print('Answer of online robot:')
    print(r)
    
    #send message
    send(r,handle,right,top)
    

Additional code (comparison picture)

'''Compare pictures, similar return True,Otherwise return False'''
def Compare(img1,img2):
    #Open two pictures
    image1=Image.open(img1)
    image3=Image.open(img2)

    #The image object is converted into histogram data and stored in lists H1 and h2
    h1=image1.histogram()
    h2=image3.histogram()
    L1=len(h1)
    L2=len(h2)
    if L1>=L2:
        n=L2
    else:
        n=L1
    sum=0
    for i in range(n):
        sum=sum+abs(h1[i]-h2[i])
    print(sum)
    result=sum/(len(h1)+len(h2)/2)
    print(result)
    if result<=1:
        return True
    else:
        return False

In my complete code, I also have the above function to judge whether the two images are the same, but it is not called in my main function, because the program runs continuously when the actual automatic reply message is positive, and replies only when a new message is detected. When I check the idea of new messages: when I take a screenshot of the chat interface, keep the last screenshot, and use the above function to compare the last and current chat screenshots. If there is a difference, it is judged that there is a message.

summary

I learned a lot from Baidu OCR interface to win32 automatic operation, and then to python crawler and image processing. I read a lot of articles and videos, and there were many bug s during this period.
My implementation idea must be relatively primary, and I can make better in the future!

Keywords: Python AI wechat http Visual Studio Code

Added by ~J~R~R on Mon, 20 Sep 2021 17:48:37 +0300