슬랙 무료 플랜의 경우, 90일 단위로 주기적으로 메시지 함을 비웁니다.
이에 따라 추후 백업 및 활용 용도로 메시지를 저장하는 프로젝트를 진행했었습니다.
※음슴체주의※
- 슬랙에서 데이터를 저장하는 방법은 크게 두 가지가 있음
- 하나는 설정 및 관리 → 워크 스페이스 설정 → 데이터 가져오기/내보내기 → 데이터 내보내기
- 다른 하나는 API를 이용하는 방법.
https://api.slack.com/methods/conversations.history
- API를 이용해 슬랙 데이터를 저장하는 방법에 대해 시리즈 글로 남길 예정.
(본 글은 성윤님께서 진행하신 genie 프로젝트를 기반으로 작성하였습니다.)
https://github.com/geultto/genie
슬랙 앱 생성
- API를 그냥 사용할 수는 없음. 많은 것들이 준비되어 있어야함. 우선 활성화된 커뮤니티... 커뮤니티... 커뮤니티..(글또..짱...⭐️⭐️)
- 활성화된 커뮤니티가 있다면, 다음으로는 앱을 생성해야함.
- 우선 앱 페이지에 들어가주자. → 앱 생성 → Features에서 OAuth&Permissions 클릭
1. Redirect URLs생성(필자는 아무 페이지나 입력했음)
2. 사용 Token종류
- Token에는 크게 두 가지 종류가 있음.
- User OAuth Token | Bot User OAuth Token 우리는 무슨 토큰을 써야 하는가? 일단. 우리의 목적은 슬랙 앱 데이터를 저장하는 것임
https://api.slack.com/authentication/token-types
- 이 내용은 token-types에 잘 정리되어 있는데, 해당 문서에 따르면
User tokens gain the "old world" resource-based OAuth scopes requested in the installation process (example: asking for channels:history grants a user token access to conversations.history for any public channel)
- history API를 사용하려면 User token을 이용하라고 가이드 되어 있다. 따라서, 메시지 데이터를 저장하는 용도로는 User OAuth Token을 사용하면 된다.
근데 잠깐!!!!! 여기서 그냥 Token을 사용하면 안됨.
3. 앱 사용 권한 설정하기
- Slack API문서를 살펴보면 Required scopes라는게 있음. "사용기능별로 신청권한"이 다르다는 의미.
- 따라서, Scopes에서 필요한 기능들을 먼저 요청해야 한다.
4. 워크스페이스에 앱 설치
- 여기까지 하고 나서 워크스페이스에 앱 설치하면됨.
- 근데, 중간 중간 권한 Scopes에서 기능들을 추가할때마다 앱도 다시 설치해준다고 생각하면됨.
파이프라인 구조
- 일별로 유저/채널 리스트가 다를 수 있음. 그렇기 때문에 유저/채널 리스트를 먼저 업데이트 한 후, 메시지를 저장.
- 유저/채널 리스트가 필요한 이유는 메시지 내의 채널, 유저는 모두 ID로 되어 있어 별도의 식별자가 필요함.
- 그리고, 채널은 메시지 API 요청 시 파라미터로 넣어줘야함.
- 그래서, 유저/채널 리스트를 먼저 업데이트 하고 메시지를 업데이트
유저/채널 리스트 업데이트
- history API를 사용하기 위해서는 두 가지 파라미터가 (필수적으로)필요함. 하나는 token, 하나는 channel ID
- Token은 아까 위에서 말했고.. 그럼 channel ID를 가져와야함. 해당 API를 사용하면 채널 리스트를 다 가져올 수 있음.
https://api.slack.com/methods/conversations.list
- 테스터에 User Token을 넣으면 아래와 같이 채널아이디와 이름을 확인할 수 있음
- 이 채널 리스트를 처리해서 Conversation_history의 파라미터로 사용.
import ssl
import certifi
from slack_sdk import WebClient
from slack_bolt import App
# ssl 인증 에러가 발생하는 경우가 있어 추가
ssl_context = ssl.create_default_context(cafile=certifi.where())
app = App(client=WebClient(token=token, ssl=ssl_context))
channels = app.client.conversations_list()["channels"]
channel_sample = {
"channel_id": channels[0]["id"],
"channel_name": channel[0]["name"],
"num_member": int(channel[0]["num_members"]),
}
- 그럼 위와 같이 채널id와 채널 이름 그리고, 채널에 속한 사람들의 숫자를 알 수 있음.
- 유저리스트는 users_list API를 이용하여 업데이트 가능
users = app.client.users_list()["members"]
user_sample = {
"user_id": users[0]["id"],
"real_name": users[0]["profile"]["real_name"],
"display_name": users[0]["profile"]["display_name"],
}
- 위와 같이 user_id그리고 real_name과 display_name을 알 수 있음.
- 참고로 real_name은 성명에 해당하고, display_name은 표시 이름에 해당함
메시지 업데이트
무엇을 수집할 것인가?
- 슬랙의 대화는 기본적으로 게시글/쓰레드(댓글)/이모지 세 가지 구조를 가지고 있음.
- 각각의 스키마에 대해 가볍게 살펴보자.
게시글의 스키마
text: 메시지 정보
user: 유저아이디
ts: 게시글 작성시간(유닉스타임)
reactions: 이모지와 이모지를 남긴 유저 ID 및 카운트 수가 남음
- 반면, 쓰레드의 스키마는 거의 비슷하지만 thread_ts와 parent_user_id가 추가적으로 남음.
text: 메시지 정보
user: 쓰레드 작성 유저아이디
ts: 쓰레드 작성시간(유닉스타임)
reactions: 이모지와 이모지를 남긴 유저 ID 및 카운트 수가 남음
thread_ts: 게시글 작성시간(유닉스타임)
parent_user_id: 게시글 작성 유저 id
- thread_ts(게시글 작성시간 유닉스타임)이 167477261.653539로 위 게시글의 ts와 동일한 것을 알 수 있음
- 그렇다면 여기서 질문...
게시글 + 쓰레드를 묶을 수 있는 키 값을 무엇으로 사용해야 하는가?
- 그것은 바로. 게시글 작성 시간과 작성 유저아이디를 묶어주는 것.
- 게시글에서는 user + ts, 쓰레드에서는 thread_ts + parent_user_id 이렇게 묶어주면됨.
- 나는 아래와 같이 사용함.
# 게시글에서 게시글 키
{
"post_id": post["user"]
+ "-"
+ datetime.fromtimestamp(float(post["ts"])).strftime(
"%Y-%m-%d-%H-%M-%S-%f"
),
}
# 쓰레드에서 게시글 키
{
"post_id": parent_user_id
+ "-"
+ datetime.fromtimestamp(float(thread["thread_ts"])).strftime(
"%Y-%m-%d-%H-%M-%S-%f"
),
}
- 여튼 이렇게 하면 user + ts = thread_ts + parent를 게시글의 키 값으로 활용하여. 댓글이 어떤 게시글의 댓글인지 확인할 수 있음.
- 근데, 여기서 끝이 아님.. 간혹가다 쓰레드에서 parent_user_id가 남지 않는 경우가 있음 ㅜ. 채널로 전송하기를 체크했을 때 이런 경우가 발생함.
- 위와 같은 방식으로 게시글에 댓글을 달았을 때는 parent_user_id가 남지 않음. 이 때는 아래와 같이 하면됨.
parent_user_id = (
thread["parent_user_id"]
if "parent_user_id" in thread.keys()
else thread["root"]["user"]
)
- root유저가 누구인지 남겨준다. 이 ['root']['user']가 parent_user_id와 동일함.
- 여기까지.. 기본적인 구조에 대해서 설명하였음. 상세 코드에서는 조금 정리 후 다음 글에서 다룰 예정
'딥상어동의 딥한 프로그래밍 > 엔지니어링' 카테고리의 다른 글
Airflow DAG 개념 톺아보기 (설치/실행 과정 포함) (4) | 2024.10.13 |
---|---|
우당탕탕 슬랙 메시지 저장기(2) - 게시글과 쓰레드 조회하기 (2) | 2023.05.21 |
데이터 마트에서는 뭘 파나요?(feat. OLTP, OLAP) (14) | 2023.03.26 |
[Bigquery] 지난 며칠 간 Python과 연동하여 사용한 소감 (0) | 2023.01.18 |
[도커] 컨테이너 삭제 (0) | 2022.12.08 |
제 블로그에 와주셔서 감사합니다! 다들 오늘 하루도 좋은 일 있으시길~~
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!