-
카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 4편python/매크로 2020. 5. 1. 18:54반응형
3줄 선 요약
- 플러스친구 아님
- 모바일 어플을 활용한 방법 아님
- pc카톡, python 으로 pc 환경에서 봇을 제작함
2020/04/21 - [python/매크로] - 카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 1편
2020/04/24 - [python/매크로] - 카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 2편
2020/04/27 - [python/매크로] - 카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 3편
1편 에서는 카톡 봇의 기능설명과 메시지 전송,
2편 에서는 친구 목록 or 채팅방 목록을 선택해서 입장하는 방법,
3편 에서는 1~2편의 전송 기능을 활용해 간단한 네이버 실시간 검색어 크롤링 알림봇 을 만들어봤다.
이번 4편에서는 채팅방의 채팅 내용을 가져와 지정한 키워드가 나왔는지 체크한 후 기능을 실행하는
키워드 명령어로 동작하는 카톡 봇을 구현해보려 한다.
이번 글을 좀 자세히 쓸까 하다가 그냥 코드 위주로 설명하려고 합니다.
이유는 글 마지막에 씀
동작 방법
- 채팅 목록에서 채팅창을 선택해 열고
- 채팅 내용을 클립보드에 저장
- 지정한 키워드가 나와있는지 체크
- 새로운 채팅인지 확인 방법은, 마지막 채팅 인덱스와 채팅 내용으로 구분
- (4)를 위해 초기 저장 필요
채팅 내용을 클립보드로 가져오는 코드
import time, win32con, win32api, win32gui, ctypes from pywinauto import clipboard # 채팅창내용 가져오기 위해 # # 카톡창 이름, (활성화 상태의 열려있는 창) kakao_opentalk_name = '메모장' chat_command = '실검 알려줘' # 테스트용.. PBYTE256 = ctypes.c_ubyte * 256 _user32 = ctypes.WinDLL("user32") GetKeyboardState = _user32.GetKeyboardState SetKeyboardState = _user32.SetKeyboardState PostMessage = win32api.PostMessage SendMessage = win32gui.SendMessage FindWindow = win32gui.FindWindow IsWindow = win32gui.IsWindow GetCurrentThreadId = win32api.GetCurrentThreadId GetWindowThreadProcessId = _user32.GetWindowThreadProcessId AttachThreadInput = _user32.AttachThreadInput MapVirtualKeyA = _user32.MapVirtualKeyA MapVirtualKeyW = _user32.MapVirtualKeyW MakeLong = win32api.MAKELONG w = win32con # 조합키 쓰기 위해 def PostKeyEx(hwnd, key, shift, specialkey): if IsWindow(hwnd): ThreadId = GetWindowThreadProcessId(hwnd, None) lparam = MakeLong(0, MapVirtualKeyA(key, 0)) msg_down = w.WM_KEYDOWN msg_up = w.WM_KEYUP if specialkey: lparam = lparam | 0x1000000 if len(shift) > 0: # Если есть модификаторы - используем PostMessage и AttachThreadInput pKeyBuffers = PBYTE256() pKeyBuffers_old = PBYTE256() SendMessage(hwnd, w.WM_ACTIVATE, w.WA_ACTIVE, 0) AttachThreadInput(GetCurrentThreadId(), ThreadId, True) GetKeyboardState(ctypes.byref(pKeyBuffers_old)) for modkey in shift: if modkey == w.VK_MENU: lparam = lparam | 0x20000000 msg_down = w.WM_SYSKEYDOWN msg_up = w.WM_SYSKEYUP pKeyBuffers[modkey] |= 128 SetKeyboardState(ctypes.byref(pKeyBuffers)) time.sleep(0.01) PostMessage(hwnd, msg_down, key, lparam) time.sleep(0.01) PostMessage(hwnd, msg_up, key, lparam | 0xC0000000) time.sleep(0.01) SetKeyboardState(ctypes.byref(pKeyBuffers_old)) time.sleep(0.01) AttachThreadInput(GetCurrentThreadId(), ThreadId, False) else: # Если нету модификаторов - используем SendMessage SendMessage(hwnd, msg_down, key, lparam) SendMessage(hwnd, msg_up, key, lparam | 0xC0000000) def main(): # # 핸들 _ 채팅방 hwndMain = win32gui.FindWindow( None, kakao_opentalk_name) hwndListControl = win32gui.FindWindowEx(hwndMain, None, "EVA_VH_ListControl_Dblclk", None) # #조합키, 본문을 클립보드에 복사 ( ctl + A , C ) PostKeyEx(hwndListControl, ord('A'), [w.VK_CONTROL], False) time.sleep(1) PostKeyEx(hwndListControl, ord('C'), [w.VK_CONTROL], False) ctext = clipboard.GetData() print(ctext) # 내용 확인 if __name__ == '__main__': main()
EVA_VH_ListControl_Dblclk
이게 채팅내용 적히는 곳의 클래스 이름이고, 이걸로 핸들을 가져온다
이후 PostKeyEx 함수로 ctr + a , c 를 비활성으로 클립보드에 가져온다
..가져온 내용 확인
1~3편 코드 + 키워드(명령어) 로 동작하는 봇 코드 ( 예시 )
import time, win32con, win32api, win32gui, ctypes import requests from bs4 import BeautifulSoup from apscheduler.schedulers.background import BackgroundScheduler from pywinauto import clipboard # 채팅창내용 가져오기 위해 import pandas as pd # 가져온 채팅내용 DF로 쓸거라서 # # 카톡창 이름, (활성화 상태의 열려있는 창) kakao_opentalk_name = '메모장' chat_command = '실검 알려줘' # 테스트용.. PBYTE256 = ctypes.c_ubyte * 256 _user32 = ctypes.WinDLL("user32") GetKeyboardState = _user32.GetKeyboardState SetKeyboardState = _user32.SetKeyboardState PostMessage = win32api.PostMessage SendMessage = win32gui.SendMessage FindWindow = win32gui.FindWindow IsWindow = win32gui.IsWindow GetCurrentThreadId = win32api.GetCurrentThreadId GetWindowThreadProcessId = _user32.GetWindowThreadProcessId AttachThreadInput = _user32.AttachThreadInput MapVirtualKeyA = _user32.MapVirtualKeyA MapVirtualKeyW = _user32.MapVirtualKeyW MakeLong = win32api.MAKELONG w = win32con # # 채팅방에 메시지 전송 def kakao_sendtext(chatroom_name, text): # # 핸들 _ 채팅방 hwndMain = win32gui.FindWindow( None, chatroom_name) hwndEdit = win32gui.FindWindowEx( hwndMain, None, "RichEdit20W", None) win32api.SendMessage(hwndEdit, win32con.WM_SETTEXT, 0, text) SendReturn(hwndEdit) # # 채팅내용 가져오기 def copy_chatroom(chatroom_name): # # 핸들 _ 채팅방 hwndMain = win32gui.FindWindow( None, chatroom_name) hwndListControl = win32gui.FindWindowEx(hwndMain, None, "EVA_VH_ListControl_Dblclk", None) # #조합키, 본문을 클립보드에 복사 ( ctl + c , v ) PostKeyEx(hwndListControl, ord('A'), [w.VK_CONTROL], False) time.sleep(1) PostKeyEx(hwndListControl, ord('C'), [w.VK_CONTROL], False) ctext = clipboard.GetData() # print(ctext) return ctext # 조합키 쓰기 위해 def PostKeyEx(hwnd, key, shift, specialkey): if IsWindow(hwnd): ThreadId = GetWindowThreadProcessId(hwnd, None) lparam = MakeLong(0, MapVirtualKeyA(key, 0)) msg_down = w.WM_KEYDOWN msg_up = w.WM_KEYUP if specialkey: lparam = lparam | 0x1000000 if len(shift) > 0: pKeyBuffers = PBYTE256() pKeyBuffers_old = PBYTE256() SendMessage(hwnd, w.WM_ACTIVATE, w.WA_ACTIVE, 0) AttachThreadInput(GetCurrentThreadId(), ThreadId, True) GetKeyboardState(ctypes.byref(pKeyBuffers_old)) for modkey in shift: if modkey == w.VK_MENU: lparam = lparam | 0x20000000 msg_down = w.WM_SYSKEYDOWN msg_up = w.WM_SYSKEYUP pKeyBuffers[modkey] |= 128 SetKeyboardState(ctypes.byref(pKeyBuffers)) time.sleep(0.01) PostMessage(hwnd, msg_down, key, lparam) time.sleep(0.01) PostMessage(hwnd, msg_up, key, lparam | 0xC0000000) time.sleep(0.01) SetKeyboardState(ctypes.byref(pKeyBuffers_old)) time.sleep(0.01) AttachThreadInput(GetCurrentThreadId(), ThreadId, False) else: SendMessage(hwnd, msg_down, key, lparam) SendMessage(hwnd, msg_up, key, lparam | 0xC0000000) # # 엔터 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) # # 채팅방 열기 def open_chatroom(chatroom_name): # # # 채팅방 목록 검색하는 Edit (채팅방이 열려있지 않아도 전송 가능하기 위하여) 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_edit2_1) hwndkakao_edit3 = win32gui.FindWindowEx( hwndkakao_edit2_2, None, "Edit", None) # # Edit에 검색 _ 입력되어있는 텍스트가 있어도 덮어쓰기됨 win32api.SendMessage(hwndkakao_edit3, win32con.WM_SETTEXT, 0, chatroom_name) time.sleep(1) # 안정성 위해 필요 SendReturn(hwndkakao_edit3) time.sleep(1) # # 채팅내용 초기 저장 _ 마지막 채팅 def chat_last_save(): open_chatroom(kakao_opentalk_name) # 채팅방 열기 ttext = copy_chatroom(kakao_opentalk_name) # 채팅내용 가져오기 a = ttext.split('\r\n') # \r\n 으로 스플릿 __ 대화내용 인용의 경우 \r 때문에 해당안됨 df = pd.DataFrame(a) # DF 으로 바꾸기 df[0] = df[0].str.replace('\[([\S\s]+)\] \[(오전|오후)([0-9:\s]+)\] ', '') # 정규식으로 채팅내용만 남기기 return df.index[-2], df.iloc[-2, 0] # # 채팅방 커멘드 체크 def chat_chek_command(cls, clst): open_chatroom(kakao_opentalk_name) # 채팅방 열기 ttext = copy_chatroom(kakao_opentalk_name) # 채팅내용 가져오기 a = ttext.split('\r\n') # \r\n 으로 스플릿 __ 대화내용 인용의 경우 \r 때문에 해당안됨 df = pd.DataFrame(a) # DF 으로 바꾸기 df[0] = df[0].str.replace('\[([\S\s]+)\] \[(오전|오후)([0-9:\s]+)\] ', '') # 정규식으로 채팅내용만 남기기 if df.iloc[-2, 0] == clst: print("채팅 없었음..") return df.index[-2], df.iloc[-2, 0] else: print("채팅 있었음") df1 = df.iloc[cls+1 : , 0] # 최근 채팅내용만 남김 found = df1[ df1.str.contains(chat_command) ] # 챗 카운트 if 1 <= int(found.count()): print("-------커멘드 확인!") p_time_ymd_hms = \ f"{time.localtime().tm_year}-{time.localtime().tm_mon}-{time.localtime().tm_mday} / " \ f"{time.localtime().tm_hour}:{time.localtime().tm_min}:{time.localtime().tm_sec}" realtimeList = naver_realtimeList() # 네이버 실시간 검색어 상위 20개 kakao_sendtext(kakao_opentalk_name, f"{p_time_ymd_hms}\n{realtimeList}") # 메시지 전송, time/실검 # 명령어 여러개 쓸경우 리턴값으로 각각 빼서 쓰면 될듯. 일단 테스트용으로 위에 하드코딩 해둠 return df.index[-2], df.iloc[-2, 0] else: print("커멘드 미확인") return df.index[-2], df.iloc[-2, 0] # # 네이버 실검 상위 20개, 리턴 def naver_realtimeList(): headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'} url = 'https://datalab.naver.com/keyword/realtimeList.naver?where=main' res = requests.get(url, headers = headers) soup = BeautifulSoup(res.content, 'html.parser') data = soup.findAll('span','item_title') a = [] for item in data: a.append(item.get_text()) s = "\n".join(a) return s # # 스케줄러 job_1 def job_1(): p_time_ymd_hms = \ f"{time.localtime().tm_year}-{time.localtime().tm_mon}-{time.localtime().tm_mday} / " \ f"{time.localtime().tm_hour}:{time.localtime().tm_min}:{time.localtime().tm_sec}" open_chatroom(kakao_opentalk_name) # 채팅방 열기 realtimeList = naver_realtimeList() # 네이버 실시간 검색어 상위 20개 kakao_sendtext(kakao_opentalk_name, f"{p_time_ymd_hms}\n{realtimeList}") # 메시지 전송, time/실검 def main(): # sched = BackgroundScheduler() # sched.start() cls, clst = chat_last_save() # 초기설정 _ 마지막채팅 저장 # # 매 분 5초마다 job_1 실행 # sched.add_job(job_1, 'cron', second='*/5', id="test_1") while True: print("실행중.................") cls, clst = chat_chek_command(cls, clst) # 커멘드 체크 time.sleep(5) if __name__ == '__main__': main()
사용법은
kakao_opentalk_name = '메모장'
에서 '메모장' 부분을 원하는 채팅방 이름으로 바꾸고
chat_command = '실검 알려줘''실검 알려줘' 를 기능에 맞게 원하는 걸로 바꾸면 됨
위에 쓴 코드들은 테스트 용도로 하드코딩해둔 거라서
여러 명령어를 쓸 경우엔 손좀 봐야 함
지금은 3편에서 만들었던 네이버 실시간 검색어 크롤링 기능을 그대로 씀
잘 짠 것도 아니라서 "가능은 하구나".. 정도만 이해하시면 되고,
더 좋게 짜실 분은 참고하는 느낌으로 보시면 됩니다.
일단 카카오톡, pc 카톡 봇 만들기 포스팅을 마무리 짓긴 해야 하기 때문에
클립보드도 사용하는 등,,, 억지로 4편을 만들긴 했지만
명령어 인식 같은 건 이왕이면 API 제대로 지원해주는 텔레그램, 디스코드, 슬랙
이런 거 쓰세요 그냥..단순 알림 봇은 '파이썬으로 가능한 것들은 다 된다' 라는 장점이 있어 그래도 쓸 만 하지만,
위에 억지로 만든 명령어 인식 카톡 봇은 문제점이 여러 가지가 있는데
무한 요청으로 계속 확인 작업을 해야 하는 것도 있고
채팅방의 내용이 쌓일수록 처리에 부담이 가고 속도도 떨어지는 등
꽤 많은 문제점이 있어서 장시간 사용 및 채팅이 많은 곳에서 사용하기에 다소 힘들 겁니다.
꼼수를 굳이 부리자면 채팅창을 닫았다가 다시 초기 설정(함수) 해주면 좀 나아지는 정도..
반응형'python > 매크로' 카테고리의 다른 글
카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 3편 (1) 2020.04.27 카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 2편 (4) 2020.04.24 카카오톡 봇 만들기 ( python, pc 카톡, 비활성 ) - 1편 (30) 2020.04.21