민프

[DevOps][NCP] Slack말고 네이버 웍스(naver works) API를 이용한 푸시 알림을 해보자 본문

DevOps/[NCP]

[DevOps][NCP] Slack말고 네이버 웍스(naver works) API를 이용한 푸시 알림을 해보자

민프야 2025. 6. 11. 20:40

원래 항상 Slack을 이용하여 서부 내부적인 알림을 보냈었는데

회사에서 works를 이용하자는 제안에 이번엔 works를 통하여 알림을 보내려고 한다.


앱추가

https://dev.worksmobile.com/kr/console/openapi/v2/app/list/view

 

https://auth.worksmobile.com/login/login?accessUrl=http%3A%2F%2Fdev.worksmobile.com%3A80%2Fconsole%2Fopenapi%2Fv2%2Fapp%2Flist%2Fview

 

auth.worksmobile.com


Bot 추가

 

단체 채팅방에 봇을 사용하려면 Bot 정책 - 조직/그룹, 1:N 메세지방 초대 기능을 체크해야합니다.

 


Bot 연동

https://admin.worksmobile.com/service/bot

 

Admin

 

admin.worksmobile.com


1.  서비스 > 위에서 생성한 Bot 체크 후 Bot 추가 클릭


2.  서비스 > 위에서 추가한 Bot 클릭 후 > 수정 > 공개 설정 활성화 후 저장


3. OAuth Scopes > 관리 > 아래 3개 항목 선택 후 저장


4. Bot No 확인 및 채팅방에 Bot추가

API를 사용해 메세지를 보내려면 아래의 Bot No가 필요합니다.


3. 코드 (Python)

아래 APi문서를 참고하여 보내면 됩니다.

https://developers.worksmobile.com/kr/docs/bot-api

 

Developers

 

developers.worksmobile.com

 

 

 

디렉토리 구조

├── main.py
├── works/
│   ├── __init__.py
│   ├── config.py          # Works 인증 정보 저장
│   └── notifier.py        # 메시지 전송 기능 구현
config.py


WORKS_BOT = {
    "client_id": "----", # 아래 이미지 부분에서 확인 가능
    "client_secret": "----", # 아래 이미지 부분에서 확인 가능
    "bot_no": "----",  # 봇 등록 시 발급받은 봇 번호
    "account_id": "----",  # 메시지 받을 사용자 or 그룹의 ID,works 채팅방에서 확인 가능
    "service_account": "----", # 서비스 계정 인증 정보에서 확인 가능
    "iss": "-=----" # client_id와 같음
    "private_key": Path("---.key").read_text() # 서비스 계정 인증 정보에서 확인 가능
}

https://dev.worksmobile.com/kr/console/openapi/v2/app/detail/view/Enivvd1PYYhlsiLkoXvwIQ?manageDomainId=300208028

 

 

notifier.py



import jwt
from datetime import datetime, timedelta
import requests
import json
from works.config import WORKS_BOT


def get_server_token():
    headers = {
        "alg": "RS256",
        "typ": "JWT"
    }

    iat = datetime.utcnow()
    exp = iat + timedelta(minutes=10)

    payload = {
        "iss": WORKS_BOT["iss"],
        "sub": WORKS_BOT["service_account"],
        "iat": int(iat.timestamp()),
        "exp": int(exp.timestamp())
    }

    print("JWT Payload:", json.dumps(payload, indent=2))
    try:
        assertion = jwt.encode(
            payload,
            WORKS_BOT["private_key"],
            algorithm="RS256",
            headers=headers
        )
        print("JWT Assertion 생성 완료")
    except Exception as e:
        print("JWT 인코딩 실패:", e)
        raise

    return assertion


def get_access_token():
    try:
        assertion = get_server_token()
        print("JWT Assertion 길이:", len(assertion))
    except Exception as e:
        print("Assertion 생성 실패:", e)
        raise

    token_data = {
        "assertion": assertion,
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "client_id": WORKS_BOT["client_id"],
        "client_secret": WORKS_BOT["client_secret"],
        "scope": "bot"
    }

    print("AccessToken 요청 중...")

    try:
        response = requests.post("https://auth.worksmobile.com/oauth2/v2.0/token", data=token_data)
        print("응답 코드:", response.status_code)
        print("응답 내용:", response.text)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print("AccessToken 요청 실패:", e)
        raise

    token_json = response.json()
    print("🔑 Access Token 발급 완료:", token_json.get("access_token"))
    return token_json["access_token"]




def send_channel_message(text: str):
    try:
        token = get_access_token()
    except Exception as e:
        print("토큰 획득 실패:", e)
        return

    url = f"https://www.worksapis.com/v1.0/bots/{WORKS_BOT['bot_no']}/channels/{WORKS_BOT['account_id']}/messages"

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {token}"
    }

    body = {
        "content": {
            "type": "text",
            "text": text
        }
    }

    print("채널 메시지 전송 중...")
    print("요청 바디:", json.dumps(body, indent=2))

    try:
        res = requests.post(url, headers=headers, json=body)
        print("응답 코드:", res.status_code)
        print("응답 내용:", res.text)
        res.raise_for_status()
        print("메시지 전송 성공")
    except requests.exceptions.RequestException as e:
        print("메시지 전송 실패:", e)
        raise

 

 


결과

Comments