본문 바로가기

Minding's Programming/Knowledge

[Python] OpenCV를 이용해 자동 카톡 메시지 보내기

728x90
반응형

살다보면 한번 쯤 여러 사람, 여러 그룹에 같은 메시지를 보내야 할 때가 있다(청첩장이라던가... 마케팅 일을 한다던가..). 그러나 그 양이 만만치 않다면 메시지를 복사 붙여넣기 하는 것마저 부담스러운 일이 된다. 이런 일을 대비해 같은 내용의 메시지를 여러 채팅방에 자동으로 보내주는 프로그램을 만들어보기로 했다. 업무자동화의 대표주자인 Python과 화면 인식에 능한 OpenCV 등을 이용하면 어렵지 않게 만들 수 있다.

 

 

필요한 라이브러리

opencv-python == 4.5.3.56 (다른 버전을 써도 무관하다.)
pyperclip
pyautogui

 

모두 pip install 명령어를 통해 설치하면 된다.

pip install opencv-python
pip install pyperclip
pip install pyautogui

 

 

구현 계획

여러 채팅방에 같은 메시지를 하나씩 보내는 자동 프로그램을 만든다.

 

[계획 포인트]

1. 괜히 매크로 프로그램 사용했다고 신고당하고 계정 정지 당하지 않도록 설계한다.

2. 필터 기능을 사용할 수 있게 설계한다.

3. 시작 위치, 반복 횟수 등을 설정할 수 있도록 한다.

4. 긴 메시지를 보낼 수 있도록 한다.

 

 

코드

import sys
import pyautogui
import time
import pyperclip
import os
import random


def send_msg(my_msg, repeat_number):
    for i in range(int(repeat_number)):
        time_wait = random.uniform(3, 5)
        print('Repeat Number : ', i + 1, end='')
        print(' // Time wait : ', round(time_wait,2))
        time.sleep(time_wait)
        pyautogui.keyDown('enter')
        pyperclip.copy(my_msg)
        pyautogui.hotkey('ctrl', 'v')
        pyautogui.keyDown('enter')
        pyautogui.keyDown('esc')
        for j in range(i+2):
            pyautogui.keyDown('down')


def filter_chat(filter_keyword, init_number):
    # 사람 아이콘 클릭
    try:
        click_img(img_path + 'chat.png')
    except:
        try:
            click_img(img_path + 'chat_2.png')
        except:
            quit()

    # X 버튼이 존재한다면 클릭하여 내용 삭제
    try:
        click_img(img_path + 'x.png')
    except:
        pass
    time.sleep(1)

    # 검색창 열기
    if filter_keyword == '':
        pyautogui.keyDown('down')
        pass
    else:
        pyautogui.hotkey('ctrl', 'f')
        pyperclip.copy(filter_keyword)
        pyautogui.hotkey('ctrl', 'v')


def click_img(imagePath):
    location = pyautogui.locateCenterOnScreen(imagePath, confidence = conf)
    x, y = location
    pyautogui.click(x, y)


def check():
    play_check = input("프로그램을 실행하시겠습니까?(y/n): ")
    if play_check == 'y':
        print("프로그램을 실행합니다.")
    else:
        bye_msg()


def bye_msg():
    input('프로그램이 종료되었습니다.')


def set_import_msg():
    with open("send_for_text.txt", "r", encoding='UTF-8') as f:
        text = f.read()
        print('======== 아래는 전송할 텍스트입니다. ========\n', text, '\n')
        return text


def initialize():
    print('Monitor size : ', end='')
    print(pyautogui.size())
    print(pyautogui.position())
    filter_keyword = input("필터링할 채팅방 문구 (빈칸 입력 시 기존 채팅방 목록사용): ")
    init_number = input('목록의 몇 번째 채팅방부터 시작할까요?: ')
    repeat_number = input('몇 번 반복할까요?: ')
    return (filter_keyword, init_number, repeat_number)


# config
img_path = os.path.dirname(os.path.realpath(__file__)) + '/img/'
conf = 0.70
pyautogui.PAUSE = 0.5

if __name__ == "__main__":
    (filter_keyword, init_number, repeat_number) = initialize()
    long_msg = set_import_msg()
    check()
    filter_chat(filter_keyword, init_number)
    send_msg(long_msg, repeat_number)
    bye_msg()

 

코드 해석 (함수 별)

main

# config
img_path = os.path.dirname(os.path.realpath(__file__)) + '/img/'
conf = 0.70
pyautogui.PAUSE = 0.5

if __name__ == "__main__":
    (filter_keyword, init_number, repeat_number) = initialize()
    long_msg = set_import_msg()
    check()
    filter_chat(filter_keyword, init_number)
    send_msg(long_msg, repeat_number)
    bye_msg()

우선 main부터 살펴보자. 우선 OpenCV로 탐색할 이미지의 주소 경로를 설정해준다. Line 3의 conf는 confidence를 나타내며, 탐색한 이미지 신뢰도가 nn% 이상일때만 해당 기능을 사용한다는 뜻이다. OpenCV를 설치했을 때만 사용 가능하다.

 

main함수가 시작되는 위치에는 채팅방 검색 필터의 키워드, 시작 위치, 반복 횟수 등이 initialize() 함수를 통해 적용된다.  set_import_msg() 함수를 통해 보낼 메시지를 불러오고, check()함수를 통해 실행여부를 다시 확인한다. 그 다음 filter_chat() 함수로 채팅방 리스트에 진입 및 키워드를 통해 검색한다. 마지막으로 send_msg() 함수로 메시지를 보낸 다음 bye_msg()함수로 종료를 알린다.

 

initialize()

def initialize():
    print('Monitor size : ', end='')
    print(pyautogui.size())
    print(pyautogui.position())
    filter_keyword = input("필터링할 채팅방 문구 (빈칸 입력 시 기존 채팅방 목록사용): ")
    init_number = input('목록의 몇 번째 채팅방부터 시작할까요?: ')
    repeat_number = input('몇 번 반복할까요?: ')
    return (filter_keyword, init_number, repeat_number)

initialize() 함수를 통해 프로그램을 실행하는데 필요한 변수들을 입력받는다. 모니터 사이즈 등은 큰 의미는 없고, 키워드와 시작 위치, 반복 횟수 변수값을 받고 return해준다.

 

set_import_msg()

def set_import_msg():
    with open("send_for_text.txt", "r", encoding='UTF-8') as f:
        text = f.read()
        print('======== 아래는 전송할 텍스트입니다. ========\n', text, '\n')
        return text

미리 메모장에 적어놓은 메시지(send_for_text.txt)를 불러와 저장하고, 카카오톡으로 보낼 메시지를 프롬프트 화면에 노출시킨다. 마지막 확인같은 느낌이다.

 

check()

def check():
    play_check = input("프로그램을 실행하시겠습니까?(y/n): ")
    if play_check == 'y':
        print("프로그램을 실행합니다.")
    else:
        print("프로그램을 재실행합니다.")
        (filter_keyword, init_number, repeat_number) = initialize()
    	long_msg = set_import_msg()
    	check()
    	filter_chat(filter_keyword, init_number)
    	send_msg(long_msg, repeat_number)
    	bye_msg()

check() 함수는 보낼 메시지 내용까지 최종 확인하고 나서 프로그램 실행 여부를 묻는다. 만약 사용자가 실행하라는 뜻의 'y'를 입력하지 않았다면 프로그램을 처음부터 다시 시작하도록 했다.

 

filter_chat()

def filter_chat(filter_keyword, init_number):
    # 사람 아이콘 클릭
    try:
        click_img(img_path + 'chat.png')
    except:
        try:
            click_img(img_path + 'chat_2.png')
        except:
            quit()

    # X 버튼이 존재한다면 클릭하여 내용 삭제
    try:
        click_img(img_path + 'x.png')
    except:
        pass
    time.sleep(1)

    # 검색창 열기
    if filter_keyword == '':
        pyautogui.keyDown('down')
        pass
    else:
        pyautogui.hotkey('ctrl', 'f')
        pyperclip.copy(filter_keyword)
        pyautogui.hotkey('ctrl', 'v')

filter_chat() 함수에서는 이제 본격적으로 메시지를 보내기 전 세팅 작업을 한다. OpenCV가 미리 찍어놓은 사진을 통해 현재 화면에서 해당 사진과 70% 이상 비슷한 곳의 위치를 찾는다. 그 다음 click_img를 통해 해당 부분을 클릭한다. 카카오톡의 경우 초기 부팅화면이 친구리스트로 나와있기 때문에, 내 의도인 채팅방으로 넘어가기 위해서는 채팅방 버튼을 자동으로 누르는 기능이 필요했다.

 

채팅방 리스트까지 들어왔다면, 채팅방을 검색창을 통해 필터화해주는 기능을 실행한다. pyautogui.hotkey()로 두 개의 키를 동시에 누를 수 있는데, Ctrl + F는 카카오톡 검색창이 열리는 단축키라 유용하게 쓸 수 있다. 물론 붙여넣기도 말이다.

 

def click_img(imagePath):
    location = pyautogui.locateCenterOnScreen(imagePath, confidence = conf)
    x, y = location
    pyautogui.click(x, y)

click_img()함수는 화면을 분석한 뒤 사진과 같다고 판단되는 부분의 가운데 좌표를 돌려주므로, 해당 버튼이 가운데로 오도록해서 스크린샷을 찍어준 뒤 위에서 설정한 경로(img폴더)에 저장해주면 된다. 사진이 너무 작으면 인식이 어려울 수 있으니, 최대한 넓은 범위의 스크린샷을 찍길 권장한다.

 

send_msg()

def send_msg(my_msg, repeat_number):
    for i in range(int(repeat_number)):
        time_wait = random.uniform(3, 5)
        print('Repeat Number : ', i + 1, end='')
        print(' // Time wait : ', round(time_wait,2))
        time.sleep(time_wait)
        pyautogui.keyDown('enter')
        pyperclip.copy(my_msg)
        pyautogui.hotkey('ctrl', 'v')
        pyautogui.keyDown('enter')
        pyautogui.keyDown('esc')
        for j in range(i+2):
            pyautogui.keyDown('down')

그리고 이제 메시지를 보내는 함수다. for문 바로 아래에 있는 random.uniform값의 대기시간은 카카오톡의 매크로 방지 시스템을 피하기 위한 하나의 작전이다. 일정하지 않은 시간으로 급하지 않게 카카오톡 채팅방에 메시지를 남겨 의심을 피할 수 있다. 또한, 현재 반복 횟수와 랜덤 대기시간을 동시에 노출시킨다.

 

이 다음은 KeyDown() 기능을 통해 enter로 채팅방을 열고, 메시지를 복사 및 붙여넣기 한 다음 전송시킨다. esc를 눌러 채팅방을 닫고, 아래에 있는 for문을 통해 아래 채팅방으로 이동한다.(range(i+2)인 이유는 채팅방에서 Enter를 눌러 메시지를 보낼 때마다 맨 위에 있는 채팅방으로 초기화되어 다시 방향키를 눌러 내려가줘야 하기 때문이다.)

 

 

전체적인 참고는 아래 영상(웨커 TV님)을 참고했다. 영상을 함께 보면 더 이해가 쉬울 것이다.

https://www.youtube.com/watch?v=oNjRH1Cz9k4&list=TLPQMTkwODIwMjNK79apzF05lQ&index=1&pp=gAQBiAQB 

 

728x90