본문 바로가기
Computer/Etc

chatgpt와 함께 telegram-bot 개발하기(가계부 봇) 3

by hexists 2023. 7. 5.

 

chatgpt와 함께 telegram-bot 개발하기(가계부 봇) 1

chatgpt와 함께 telegram-bot 개발하기(가계부 봇) 2

chatgpt와 함께 telegram-bot 개발하기(가계부 봇) 3 (현재글)


 

1, 2편에 이어 가계부 봇을 계속 작성해본다.

 

기본 기능이 작성되고 나니 이런 저런 아이디어가 생각난다.

 

- [완] 명령어가 좀 단순했으면 좋겠음
- [완] 잘못된 포맷이면 안내해줬으면 좋겠음
- [완] 항목을 조회하는 기능도 있었으면 좋겠음
- [완] 기록할 때 날짜가 남으면 좋겠음
- [완] 헬프 메세지를 더 디테일하게 적어야겠음
- [완] 카드 명세서를 자동으로 파싱하는 기능은 꼭 필요
- [완] 예산 초기화 등록
- [완] logger 달기
- [완] 텔레그램 봇은 아무나 접근 가능한가?, Filter 추가
- [완] nohup으로 실행되도록 설정하기

 

이제 아이디어를 하나씩 구현해본다.

 

명령어가 좀 단순했으면 좋겠음

기존 명령어에 알파벳 하나로 된 명령어들을 추가했다.

help에는 h, budget에는 b

def main():
    """메인 함수"""
    updater = Updater(TOKEN, use_context=True)
    dispatcher = updater.dispatcher

    dispatcher.add_handler(CommandHandler('help', start, user_filter))
    dispatcher.add_handler(CommandHandler('h', start, user_filter))
    dispatcher.add_handler(CommandHandler('budget', budget, user_filter))
    dispatcher.add_handler(CommandHandler('bg', budget, user_filter))
    dispatcher.add_handler(CommandHandler('spend', spend, user_filter))
    dispatcher.add_handler(CommandHandler('s', spend, user_filter))
    dispatcher.add_handler(CommandHandler('recipe', recipe, user_filter))
    dispatcher.add_handler(CommandHandler('r', recipe, user_filter))
    dispatcher.add_handler(CommandHandler('balance', balance, user_filter))
    dispatcher.add_handler(CommandHandler('b', balance, user_filter))
    dispatcher.add_handler(CommandHandler('list', list_balance, user_filter))
    dispatcher.add_handler(CommandHandler('l', list_balance, user_filter))

    # 오류 처리기 등록
    dispatcher.add_error_handler(error)

    # 봇 시작
    updater.start_polling()
    updater.idle()


잘못된 포맷이면 안내해줬으면 좋겠음

사용시 잘못된 입력에 대해 exception이 발생하고 콘솔에만 메세지가 출력되고 있었다.

try, except을 사용해서 예외 발생시 메세지를 출력하도록 수정했다.

def spend(update, context):
    """지출 기록 명령어를 처리합니다."""
    logging.info('call spend()')
    try:
        amount = int(context.args[0])
        description = ' '.join(context.args[1:])
    except Exception as e:
        msg = '''
입력 형식을 맞춰주세요.

/spend(/s): 지출 등록
ex) /s 500 과자
'''
        context.bot.send_message(chat_id=update.effective_chat.id, text=msg)
        logging.warning(f'Exception in spend: {e}')
        return


항목을 조회하는 기능도 있었으면 좋겠음

어떤 지출이 있었는지 확인하는 기능이 없어 추가했다.

/list_balance, /l 명령어를 추가하고 db table을 조회해서 출력하도록 했다.

def list_balance(update, context):
    """잔액 리스트 확인 명령어를 처리합니다."""
    logging.info('call list_balance()')
    cursor.execute("SELECT * FROM expenses WHERE category = 'spend' and lastweek = 0")
    rows = cursor.fetchall()
    logging.info(f'{rows}')
    list_b = []
    for row in rows:
        b_id, amount, cate, desc, date, lastweek = row
        list_b.append(f'{date} {amount:>6} {desc}')

    remaining_budget = get_balance()

    list_b_str = '\n'.join(sorted(list_b))
    msg = f'''
{list_b_str}
-----
잔액: {remaining_budget}'''
    context.bot.send_message(chat_id=update.effective_chat.id, text=msg)

 

기록할 때 날짜가 남으면 좋겠음

지출 발생시 날짜 기록이 필요하다.

db table 생성시 날짜 필드를 추가하고 기본값이 입력되도록 했다.

sqllite3를 처음 사용해봐서 어떻게 테이블에 값을 입력할지는 검색을 통해 익혔다.

# expenses 테이블 생성
cursor.execute('''CREATE TABLE IF NOT EXISTS expenses
                  (id INTEGER PRIMARY KEY AUTOINCREMENT,
                   amount INTEGER,
                   category TEXT,
                   description TEXT,
                   date DATETIME DEFAULT (strftime('%m/%d', DATETIME('now', 'localtime'))),
                   lastweek INTEGER DEFAULT 0)''')

 

헬프 메세지를 더 디테일하게 적어야겠음

사용성 향상을 위해 헬프 메세지를 다듬었다.

 


카드 명세서를 자동으로 파싱하는 기능은 꼭 필요

카드 명세서는 포맷이 다양하다.

어차피 외부로 공개할 봇이 아니기 때문에, 집에서 사용하는 카드사 몇개만 파싱하는 코드를 짰다.

메세지에서 카드 정보를 먼저 확인하고, 카드 정보에 따라 사용할 필드를 선택했다.

amount, description, date 정보를 파싱해서 저장하고 이를 입력되도록 했다.

/r 이비인후과의원
3,700원 승인
*** 코웨이(***)
일시불, 07/03 10:09
누적금액 1,000,000원

3700, 이비인후과, 07/03이 입력된다.

 

예산 초기화 등록

예산 입력 기능을 다듬었다.

한 주 동안 입력한 예산을 last_week = 1로 변경해서 백업하고, budget을 초기화했다.

def budget(update, context):
    """예산 초기화 명령어를 처리합니다."""
    logging.info('call budget()')
    try:
        budget_amount = int(context.args[0])
    except Exception as e:
        msg = '''
입력 형식을 맞춰주세요.

/budget(/bg): 예산 초기화
/bg 숫자
ex) /bg 400000
'''
        context.bot.send_message(chat_id=update.effective_chat.id, text=msg)
        logging.warning(f'Exception in budget: {e}')
        return

    '''
    예산 초기화 로직

    1) lastweek = 1인 결과를 삭제
    2) lastweek = 0인 결과를 1로 업데이트
    3) budget을 등록
    '''

    cursor.execute("DELETE FROM expenses WHERE lastweek = 1")
    cursor.execute("UPDATE expenses SET lastweek = 1 where lastweek = 0")
    cursor.execute("INSERT INTO expenses (amount, category, description) VALUES (?, 'budget', '예산 등록')", (budget_amount,))
    conn.commit()
    logging.info("executed budget()")
    context.bot.send_message(chat_id=update.effective_chat.id, text=f"예산이 {budget_amount}으로 초기화 되었습니다.")

 

logger 달기

집에서 쓰는 것이라 logger가 꼭 필요하진 않지만, debugging을 위해서 logger를 추가했다.

아주 간단한 형태의 logger이다.

좋은 점인지 모르겠는데, logging을 기본 클래스로 설정하니 telegram의 로그도 함께 기록된다.

logging.basicConfig(
    filename = 'expenses.log',
    format = '%(asctime)s:%(levelname)s:%(message)s',
    datefmt = '%m/%d/%Y %I:%M:%S %p',
    level = logging.DEBUG
)


텔레그램 봇은 아무나 접근 가능한가?, Filter 추가

이제 다른 사람이 해당 봇에 접근할 수 없도록 보안? 기능을 추가했다.

검색을 통해 Filter에 user_id를 입력해서 필터링 할 수 있다는 것을 배우고 적용했다.

    user_filter = Filters.user(user_id=[id1, id2])


automator(nohup)으로 실행되도록 설정하기

외부 호스팅을 알아보다가 일단 개인pc를 이용하기로 했다.

맥을 사용하고 있어서 automator를 통해 프로그램이 실행되도록 설정했다.

부팅 후 실행되도록 설정했다.

 

아래 블로그를 참고했다.

https://bekusib.tistory.com/41

 

여기까지 실제 구현을 했다. 전체 코드는 개인 정보 정리가 필요해서 당장 공개는 어렵지만, 조만간 정리해서 공개하겠다. 끝