Commision

[Python3] Win32 API를 이용한 PC 카카오톡 자동화

서주녁 2023. 1. 18. 19:26

이번 클라이언트는 교내 에브리타임에서 컨택해 만났다. 토목공학과에 재학 중인 후배였는데,

 

카카오톡에서 일용직 일자리 구인/구직에 관련한 대형 오픈채팅방을 운영하고 있었다.

 

네이버 밴드에 올라오는 일용직 구인 광고를 수집하고 조건에 맞는 일자리들을

 

정해진 시간대에 카카오톡 채팅방에 전달해주는 프로그램이 필요하다고 했다.

 

모바일에서 구현을 해보려고 했는데, 각종 API 활용이나 구현의 한계가 있어 PC 버전에서 구현했다.


우선 네이버 밴드에서 게시글을 수집하는건 네이버에서 제공하는 API를 활용하면 쉽게 해결될 문제니

 

프로그램에서 카카오톡 메시지 전송 기능만 구현해보자.

 

기본적으로 Window OS 기준 우리가 사용하는 응용 프로그램들은 윈도우 속성을 갖는다.

 

윈도우 속성에는 해당 프로그램의 위치, 크기, 권한등의 정보가 들어있고

 

Python3에서는 이를 win32 API를 활용하여 제어할 수 있다.

Spy++로 본 PC 카카오톡 구조 (출처 : https://airfox1.tistory.com/3)

Spy++ 이라는 툴을 활용하면 프로그램의 구조를 뜯어볼 수 있는데, 그 구조는 위 사진과 같다.

 

0 : 카카오톡 프로그램

1 : 카카오톡 프로그램 초기 화면
2-1 : 친구 목록

2-2 : 채팅방 검색 목록

3 : 채팅방

 

만약 내가 원하는 카카오톡 채팅방을 열려면, 0 -> 1 -> 2-2 -> 3의 순서로 부모 윈도우부터 자식 윈도우까지 접근해야한다.

 

그 예시로, 내가 원하는 카카오톡 채팅방을 검색하여 여는 코드는 다음과 같다.

# Send Return Value to Handled Window
def sendReturn(hwnd):
    win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_RETURN, 0)
    time.sleep(0.01)
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_RETURN, 0)

# Open Room
def openRoom(room):
    hwndKakao = win32gui.FindWindow(None, "카카오톡")
    hwndKakao_Edit1 = win32gui.FindWindowEx(hwndKakao, None, "EVA_ChildWindow", None)
    hwndKakao_Edit2_1 = win32gui.FindWindowEx(hwndKakao_Edit1, None, "EVA_Window", None)
    hwndKakao_Edit2_2 = win32gui.FindWindowEx(hwndKakao_Edit1, hwndKakao_Edit2_1, "EVA_Window", None)
    hwndKakao_Edit3 = win32gui.FindWindowEx(hwndKakao_Edit2_2, None, "Edit", None)
    win32api.SendMessage(hwndKakao_Edit3, win32con.WM_SETTEXT, 0, room)
    time.sleep(1)
    sendReturn(hwndKakao_Edit3)
    time.sleep(3)

우선, FindWindow 함수를 이용해 "카카오톡" 이라는 이름을 가진 프로그램의 윈도우의 핸들을 가져온다.

 

그리고 FindWindowEx 함수로 자식 윈도우의 핸들을 하나씩 타고 내려가

 

최종적으로 검색창(Edit)의 핸들을 가져온다.

 

여기서 문제가 EVA_Window가 2개 있어 2-1에서 이름이 같으므로 멈출 수 있는데,

hwndKakao_Edit2_2 = win32gui.FindWindowEx(hwndKakao_Edit1, hwndKakao_Edit2_1, "EVA_Window", None)

이 코드를 보면 인자로 hwndKakao_Edit2_1이라는 인자를 넘겨줌으로써 자식 윈도우의

 

핸들을 찾는 시작지점을 지정해줄 수 있다. None 값을 주면 처음부터 찾기 때문에 2-1에서 멈추게 되지만

 

2-1의 핸들을 인자로 주면 그 다음 윈도우인 2-2 윈도우의 핸들을 가져올 수 있게 되는 것이다.

 

그렇게 찾은 검색창 핸들에 room 변수에 들어있는 채팅방 이름을 SendMessage 함수로 전달하고

 

sendReturn 함수를 호출하여 엔터키를 입력함으로써 해당 채팅방을 열면서 종료된다.


원하는 채팅방은 열었으니 이번엔 메시지를 전송해보자.

 

카카오톡 메시지를 입력하는 윈도우는 "RichEdit50W"라는 이름으로 프로그램 구조에 나타나있다.

 

그럼 위에서 했던 방식대로 다음과 같은 코드로 구현이 된다.

# Send Text Message to the Room
def sendText(room, text):
    hwndMain = win32gui.FindWindow(None, room)
    hwndEdit = win32gui.FindWindowEx(hwndMain, None, "RichEdit50W", None)
    win32api.SendMessage(hwndEdit, win32con.WM_SETTEXT, 0, text)
    sendReturn(hwndEdit)

현재 열어놓은 채팅방 윈도우를 부모 윈도우로 하여 자식 윈도우 핸들을 잡아주고,

 

SendMessage 함수를 이용해 채팅 입력란에 원하는 텍스트를 입력한다.

 

그 후 똑같이 sendReturn 함수를 호출하면 엔터키가 입력되므로 메시지를 전송하게 되는 것이다.


네이버 밴드 API를 활용하고 전달받은 응답값을 가공하여 채팅방에 전송하는건

 

앞서 구현한 내용으로 마무리 지을 수 있는 내용이라 별도의 설명은 하지 않겠다.

 

Thread를 활용하여 실시간 감지와 메시지 전송을 멀티 스레드로 구현했는데

 

자세한 내용 및 전체 코드는 Github에서 확인해볼 수 있으니 참고하면 좋을 듯 하다.

 

https://github.com/ssam2s/AutomaticKakaotalkSender/blob/main/Test.py

 

GitHub - ssam2s/AutomaticKakaotalkSender: Python 기반 멀티 쓰레딩과 시스템 윈도우 접근을 통한 카카오톡

Python 기반 멀티 쓰레딩과 시스템 윈도우 접근을 통한 카카오톡 자동 전송 프로그램 (Comission) - GitHub - ssam2s/AutomaticKakaotalkSender: Python 기반 멀티 쓰레딩과 시스템 윈도우 접근을 통한 카카오톡 자

github.com