Skip to content
30 changes: 30 additions & 0 deletions src/handler/bigchat/create_bigchat_sheet.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from implementation.slack_client import Reaction
from implementation.member_finder import MemberManager, MemberNotFound, MemberLackInfo


class CreateBigchatSheet:
def __init__(self, event, slack_client, gs_client):
self.text = event["text"]
Expand All @@ -9,15 +13,41 @@ def run(self):
if "새로운 빅챗" not in self.text:
return False

# TODO: REGEX로 더 깔끔하게 따올 수 있지 않을까?
sheet_name = self.text.split("새로운 빅챗", maxsplit=1)[1].split("\n")[0].strip()
if not sheet_name:
self.slack_client.send_message(msg="시트 이름이 입력되지 않았어. 다시 입력해줘!", ts=self.ts)
return False

worksheet_id = self.gs_client.create_bigchat_sheet(sheet_name)
sheet_url = self.gs_client.get_url(worksheet_id)

self.slack_client.send_message(
msg=f"새로운 빅챗, 등록 완료! <{sheet_url}|{sheet_name}> :google_spreadsheets:",
ts=self.ts,
)

# 빅챗 시트가 생성되기 이전에 등록을 시도한(GOGO 이모지를 누른)
# 인원들이 누락된 것에 대한 사후처리
channel = self.slack_client.get_channel()
assert channel is not None
reaction = self.slack_client.get_emoji(
channel=channel,
timestamp=self.ts,
)
Comment on lines +34 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 라인을 지나간 직후에 생성되는 이모지는 어떻게 되는걸까요? 여전히 하나의 처리되지 못한 이모지로 남게 될까요?

if reaction is not None:
reaction: Reaction
for user in reaction.users:
error_message = None
try:
member = MemberManager.get_instance().find(user)
except MemberNotFound:
error_message = f"<@{user}>, 네 정보를 찾지 못했어. 운영진에게 연락해줘!"
except MemberLackInfo:
error_message = f"<@{user}>, 네 정보에 누락된 값이 있어. 운영진에게 연락해줘!"
else:
self.gs_client.append_row(worksheet_id, member.transform_for_spreadsheet())
finally:
if error_message:
self.slack_client.send_message(msg=error_message, ts=self.ts)
Comment on lines +49 to +52
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용자가 시트에 추가 된 뒤, 슬렉으로 사용자 정보를 보내주는 부분은 가져오지 못했네요.
해당 부분에 대한 수정이 추가로 필요할 것 같습니다.

return True
8 changes: 5 additions & 3 deletions src/handler/bigchat/join_bigchat.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import List
import re
import textwrap

from implementation.member_finder import MemberNotFound, MemberLackInfo
from implementation.slack_client import Message
from util.utils import strip_multiline


SPREADSHEET_PAT = re.compile(
r"https://docs.google.com/spreadsheets/d/.*/edit#gid=(\d*)"
Expand Down Expand Up @@ -60,7 +61,7 @@ def run(self):

self.slack_client.send_message(msg=f"<@{self.user}>, 등록 완료!", ts=self.ts)
self.slack_client.send_message_only_visible_to_user(
msg=strip_multiline(
msg=textwrap.dedent(
f"""
<@{self.user}> 네 신청 정보를 아래와 같이 등록했어. 바뀐 부분이 있다면 운영진에게 DM으로 알려줘!
```
Expand All @@ -69,7 +70,8 @@ def run(self):
이메일: {member.email}
학교/회사: {member.school_name_or_company_name}
```
(참고로 이 메시지는 너만 볼 수 있어!)"""
(참고로 이 메시지는 너만 볼 수 있어!)
"""
),
channel=self.channel,
ts=self.ts,
Expand Down
13 changes: 2 additions & 11 deletions src/handler/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@
from implementation.member_finder import MemberManager
from implementation.slack_client import SlackClient

MEMBER_MANAGER = None


def _get_member_manager(): # TODO(seonghyeok): we need better singleton
global MEMBER_MANAGER
if not MEMBER_MANAGER:
MEMBER_MANAGER = MemberManager(GoogleSpreadsheetClient())
return MEMBER_MANAGER


# reaction_added event sample:
# {
Expand All @@ -40,7 +31,7 @@ def join_bigchat(event, say, client):
envs.JOIN_BIGCHAT_EMOJI,
SlackClient(say, client),
GoogleSpreadsheetClient(),
_get_member_manager(),
MemberManager.get_instance(),
).run()


Expand All @@ -52,7 +43,7 @@ def abandon_bigchat(event, say, client):
envs.ANNA_ID,
envs.JOIN_BIGCHAT_EMOJI,
SlackClient(say, client),
_get_member_manager(),
MemberManager.get_instance(),
GoogleSpreadsheetClient(),
).run()

Expand Down
8 changes: 8 additions & 0 deletions src/implementation/member_finder.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import logging
from typing import Dict, List

Expand Down Expand Up @@ -35,6 +37,12 @@ class MemberLackInfo(Exception):


class MemberManager:
@classmethod
def get_instance(cls) -> MemberManager:
if not hasattr(cls, "_instance"):
cls._instance = cls(GoogleSpreadsheetClient())
return cls._instance

def __init__(self, gs_client: GoogleSpreadsheetClient):
self.gs_client = gs_client
self.members_worksheet_id = int(envs.MEMBERS_INFO_WORKSHEET_ID)
Expand Down
31 changes: 31 additions & 0 deletions src/implementation/slack_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class Emoji(BaseModel):
name: str


class Reaction(BaseModel):
name: str
users: List[str]
count: int


class SlackClient:
def __init__(self, say: Say, web_client: WebClient):
self.say = say
Expand Down Expand Up @@ -50,6 +56,10 @@ def _messages_to_members(messages, channel):
for msg in messages
]

def get_channel(self) -> Optional[str]:
"""현재 메시지가 발송된 채널을 반환한다."""
return self.say.channel

def get_replies(
self, channel: str, thread_ts: str = None, ts: str = None
) -> List[Message]:
Expand Down Expand Up @@ -88,6 +98,27 @@ def add_emoji(self, channel, ts, emoji_name):
return
raise ex

def get_emoji(self, channel: str, ts: str, emoji_name: str) -> Optional[Reaction]:
"""channel에 있는 ts 시간에 발송된 메시지에 사용자들이 남긴 반응 목록을 가져온다.
Comment on lines +101 to +102
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 하면 정확히 "ts 시간에" 남긴 반응만 가져오지 않나요? 밀리세컨드 단위로 찍히는 것 같아서요


해당 반응이 존재하지 않는다면 None을 반환한다."""
response = self.web_client.reactions_get(
channel=channel,
full=True,
timestamp=ts,
)
assert response["ok"]
assert response["type"] == "message"
for reaction in response["message"]["reactions"]:
if reaction["name"] == emoji_name:
return Reaction(
name=reaction["name"],
users=reaction["users"],
count=reaction["count"],
)
else:
return None

def remove_emoji(self, channel, ts, emoji_name):
try:
self.web_client.reactions_remove(
Expand Down
32 changes: 32 additions & 0 deletions test/handler/bigchat/test_create_bigchat_sheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from unittest.mock import MagicMock

from handler.bigchat.create_bigchat_sheet import CreateBigchatSheet
from implementation.slack_client import Reaction
from implementation.member_finder import Member, MemberManager
from test.handler.bigchat.sample_data import create_sample_app_mention_event


Expand Down Expand Up @@ -44,3 +46,33 @@ def test_not_run_by_sheet_name_notfound(self):
mock_slack_client.send_message.assert_called_once()
assert result is False
assert "시트 이름이 입력되지 않았어. 다시 입력해줘!" in mock_slack_client.send_message.call_args.kwargs["msg"]

def test_gogo_pressed_while_building_spreadsheet(self):
"""빅챗 시트 생성이 완료되기 이전에 등록을 시도한(GOGO 이모지를 누른)
인원들이 누락되지 않고 빅챗에 등록되었는지 확인합니다.
"""
event = create_sample_app_mention_event("<@U01BN035Y6L> 새로운 빅챗 빅챗 24-08-01")
mock_slack_client = MagicMock()
mock_slack_client.get_emoji.return_value = Reaction(
name="gogo",
users=["U01BN035Y6L"],
count=1,
)
mock_gs_client = MagicMock()
mock_member_manager = MagicMock()
mock_member_manager.find.return_value = Member(
kor_name="김동주",
eng_name="Kim Dongjoo",
email="email",
phone="phone",
school_name_or_company_name="school_name_or_company_name",
)
MemberManager.get_instance = MagicMock(return_value=mock_member_manager)

sut = CreateBigchatSheet(event, mock_slack_client, mock_gs_client)

assert sut.run()

mock_member_manager.find.assert_called_once()
mock_slack_client.get_emoji.assert_called_once()
mock_gs_client.append_row.assert_called_once()