Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

빅챗 스프레드 시트 생성 후 GOGO 이모지를 누른 사람들을 일괄 등록하도록 기능 구현 (#89) #90

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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()
Loading