Ashok Kumar Bhati commited on
Commit
4c98e94
·
1 Parent(s): cc7f27e

Implement user onboarding flow

Browse files
src/controllers/_bot_controller.py CHANGED
@@ -1,8 +1,10 @@
1
  import os
2
  from fastapi import APIRouter, HTTPException
 
3
  from slack_sdk.web.async_client import AsyncWebClient
4
  from fastapi import Request
5
  from src.utils import SlackClient
 
6
 
7
  from src.utils import logger
8
 
@@ -14,6 +16,7 @@ class BotController:
14
  self.router.add_api_route("/event", self.receiveEvent, methods=["POST"])
15
  self.router.add_api_route("/auth", self.authenticate, methods=["GET"])
16
  self.router.add_api_route("/frequency", self.updateFrequency, methods=["POST"])
 
17
 
18
  async def receiveEvent(self, event: dict):
19
  logger.info(f"Received event: {event}")
@@ -44,16 +47,41 @@ class BotController:
44
  logger.error(e)
45
  raise HTTPException(status_code=500, detail=str(e))
46
 
47
- async def authenticate(self, code: str):
48
  try:
49
- response = await self.client.oauth_v2_access(
50
- client_id=os.environ.get("SLACK_CLIENT_ID"),
51
- client_secret=os.environ.get("SLACK_CLIENT_SECRET"),
52
- code=code,
53
- redirect_uri=os.environ.get("SLACK_REDIRECT_URI"),
54
- )
55
- logger.info(f"Authenticated successfully: {response}")
56
- return {"message": "Token authenticated successfully"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  except HTTPException as e:
58
  logger.warning(e)
59
  raise e
@@ -76,3 +104,15 @@ class BotController:
76
  except Exception as e:
77
  logger.error(e)
78
  raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  from fastapi import APIRouter, HTTPException
3
+ from fastapi.responses import RedirectResponse
4
  from slack_sdk.web.async_client import AsyncWebClient
5
  from fastapi import Request
6
  from src.utils import SlackClient
7
+ from src.services import SlackBotService, SlackTeamService, UserService
8
 
9
  from src.utils import logger
10
 
 
16
  self.router.add_api_route("/event", self.receiveEvent, methods=["POST"])
17
  self.router.add_api_route("/auth", self.authenticate, methods=["GET"])
18
  self.router.add_api_route("/frequency", self.updateFrequency, methods=["POST"])
19
+ self.router.add_api_route("/install", self.install, methods=["GET"])
20
 
21
  async def receiveEvent(self, event: dict):
22
  logger.info(f"Received event: {event}")
 
47
  logger.error(e)
48
  raise HTTPException(status_code=500, detail=str(e))
49
 
50
+ async def authenticate(self, code: str, state: str):
51
  try:
52
+ async with SlackBotService() as slack_bot_service:
53
+ auth_response = await slack_bot_service.handle_oauth_callback(
54
+ code, state
55
+ )
56
+ if "access_token" in auth_response:
57
+ async with SlackTeamService() as slack_team_service:
58
+ team = await slack_team_service.get_team_by_slack_team_id(
59
+ auth_response["team"]["id"]
60
+ )
61
+ if not team:
62
+ team = await slack_team_service.register_slackTeam(
63
+ {
64
+ "name": auth_response["team"]["name"],
65
+ "slack_team_id": auth_response["team"]["id"],
66
+ "token": auth_response["access_token"],
67
+ }
68
+ )
69
+ logger.info(f"Registered team: {team}")
70
+ team_users = await slack_bot_service.get_users_list(team.token)
71
+ for slack_user in team_users:
72
+ async with UserService() as user_service:
73
+ user = await user_service.get_user_info_by_email(
74
+ slack_user["profile"]["email"]
75
+ )
76
+ if user and hasattr(user, "id"):
77
+ await user_service.update_user_slack_info(
78
+ user.id,
79
+ {
80
+ "slack_team_id": team.id,
81
+ "slack_id": slack_user["id"],
82
+ },
83
+ )
84
+ return {"message": "installed successfully"}
85
  except HTTPException as e:
86
  logger.warning(e)
87
  raise e
 
104
  except Exception as e:
105
  logger.error(e)
106
  raise HTTPException(status_code=500, detail=str(e))
107
+
108
+ async def install(self):
109
+ try:
110
+ async with SlackBotService() as slack_bot_service:
111
+ auth_url = await slack_bot_service.generate_auth_url()
112
+ return RedirectResponse(url=auth_url)
113
+ except HTTPException as e:
114
+ logger.warning(e)
115
+ raise e
116
+ except Exception as e:
117
+ logger.error(e)
118
+ raise HTTPException(status_code=500, detail=str(e))
src/repositories/_base_repository.py CHANGED
@@ -60,7 +60,7 @@ class BaseRepository:
60
  return result
61
  raise Exception(f"{self.__model.__name__} not found")
62
 
63
- async def patch(self, id, **kwargs: dict):
64
  async with self.get_session() as session:
65
  instance = await session.get(self.__model, id)
66
  if instance:
 
60
  return result
61
  raise Exception(f"{self.__model.__name__} not found")
62
 
63
+ async def patch(self, id, kwargs: dict):
64
  async with self.get_session() as session:
65
  instance = await session.get(self.__model, id)
66
  if instance:
src/repositories/_slack_team_repository.py CHANGED
@@ -6,3 +6,9 @@ from ._base_repository import BaseRepository
6
  class SlackTeamRepository(BaseRepository):
7
  def __init__(self):
8
  super().__init__(model=SlackTeam)
 
 
 
 
 
 
 
6
  class SlackTeamRepository(BaseRepository):
7
  def __init__(self):
8
  super().__init__(model=SlackTeam)
9
+
10
+ async def get_by_slack_team_id(self, slack_team_id: str):
11
+ query = self.model.select().where(self.model.slack_team_id == slack_team_id)
12
+ result = await self.execute(query)
13
+ result = result.scalar_one_or_none()
14
+ return result
src/repositories/_user_repository.py CHANGED
@@ -10,7 +10,7 @@ class UserRepository(BaseRepository):
10
  super().__init__(model=User)
11
 
12
  async def get_user_by_email(self, email: str):
13
- query = select(User).where(User.email == email)
14
  result = await self.execute(query)
15
  result = result.scalar_one_or_none()
16
  return result
 
10
  super().__init__(model=User)
11
 
12
  async def get_user_by_email(self, email: str):
13
+ query = select(User).where(User.email_id == email)
14
  result = await self.execute(query)
15
  result = result.scalar_one_or_none()
16
  return result
src/services/__init__.py CHANGED
@@ -1,6 +1,8 @@
1
  from ._content_delivery_service import ContentDeliveryService
2
  from ._user_service import UserService
 
 
3
 
4
- __all__ = ["ContentDeliveryService", "UserService"]
5
  __version__ = "0.1.0"
6
  __author__ = "Ashok Bhati"
 
1
  from ._content_delivery_service import ContentDeliveryService
2
  from ._user_service import UserService
3
+ from ._slack_bot_service import SlackBotService
4
+ from ._slack_team_service import SlackTeamService
5
 
6
+ __all__ = ["ContentDeliveryService", "UserService", "SlackBotService"]
7
  __version__ = "0.1.0"
8
  __author__ = "Ashok Bhati"
src/services/_slack_bot_service.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import os
3
+
4
+ from fastapi import HTTPException
5
+ from src.utils import logger, SlackClient
6
+ from slack_sdk.oauth import AuthorizeUrlGenerator
7
+ from slack_sdk.oauth.installation_store import FileInstallationStore
8
+ from slack_sdk.oauth.state_store import FileOAuthStateStore
9
+
10
+ SCOPES = [
11
+ "chat:write",
12
+ "chat:write.customize",
13
+ "commands",
14
+ "files:read",
15
+ "im:write",
16
+ "users:read",
17
+ "users:read.email",
18
+ ]
19
+
20
+
21
+ class SlackBotService:
22
+
23
+ def __init__(self):
24
+ self.state_store = FileOAuthStateStore(
25
+ expiration_seconds=300,
26
+ base_dir="./slack_state",
27
+ )
28
+
29
+ async def __aenter__(self):
30
+ return self
31
+
32
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
33
+ pass
34
+
35
+ async def generate_auth_url(self):
36
+ state = self.state_store.issue()
37
+
38
+ url_generator = AuthorizeUrlGenerator(
39
+ client_id=os.environ.get("SLACK_CLIENT_ID"),
40
+ redirect_uri=os.getenv("SLACK_REDIRECT_URI"),
41
+ scopes=SCOPES,
42
+ )
43
+
44
+ auth_url = url_generator.generate(state=state)
45
+ return auth_url
46
+
47
+ async def handle_oauth_callback(self, code, state):
48
+ if not code:
49
+ raise HTTPException(status_code=400, detail="Missing authorization code")
50
+
51
+ if not state:
52
+ raise HTTPException(status_code=400, detail="Missing state parameter")
53
+
54
+ if not self.state_store.consume(state):
55
+ raise HTTPException(status_code=400, detail="Invalid state parameter")
56
+ async with SlackClient() as slack_client:
57
+ response = await slack_client.handle_oauth_callback(code=code)
58
+ return response
59
+
60
+ async def get_users_list(self, token):
61
+ logger.info(f"Getting users list with token: {token}")
62
+ async with SlackClient(token) as slack_client:
63
+ response = await slack_client.get_users_list()
64
+ return response
src/services/_slack_team_service.py CHANGED
@@ -1,7 +1,7 @@
1
  from src.repositories import SlackTeamRepository
2
 
3
  from src.models import ContentDeliveryFrequency
4
- import datetime
5
 
6
 
7
  class SlackTeamService:
@@ -14,10 +14,10 @@ class SlackTeamService:
14
  async def __aexit__(self, exc_type, exc_val, exc_tb):
15
  pass
16
 
17
- async def register_slackTeam(self, email: str):
18
- return await self.slackTeam_repository.create(email=email)
 
19
 
20
  async def get_slackTeam_token(self, slackTeam_id):
21
- return await self.slackTeam_repository.get(
22
- slackTeam_id, filter_by={"token": True}
23
- )
 
1
  from src.repositories import SlackTeamRepository
2
 
3
  from src.models import ContentDeliveryFrequency
4
+ from src.utils import logger
5
 
6
 
7
  class SlackTeamService:
 
14
  async def __aexit__(self, exc_type, exc_val, exc_tb):
15
  pass
16
 
17
+ async def register_slackTeam(self, team: dict):
18
+ logger.info(f"Registering team: {team}")
19
+ return await self.slackTeam_repository.create(team)
20
 
21
  async def get_slackTeam_token(self, slackTeam_id):
22
+ team = await self.slackTeam_repository.get_by_slack_team_id(slackTeam_id)
23
+ return team["token"] if team else None
 
src/utils/_slack_client.py CHANGED
@@ -7,8 +7,8 @@ from src.utils import logger
7
 
8
  class SlackClient:
9
 
10
- def __init__(self):
11
- self.client = AsyncWebClient(token=os.environ.get("SLACK_BOT_TOKEN"))
12
 
13
  async def __aenter__(self):
14
  return self
@@ -35,8 +35,20 @@ class SlackClient:
35
  return file_info
36
 
37
  async def get_users_list(self):
 
38
  users_list = await self.client.users_list()
 
39
  filtered_users = [
40
- user for user in users_list["members"] if user["is_bot"] == True
 
 
41
  ]
42
  return filtered_users
 
 
 
 
 
 
 
 
 
7
 
8
  class SlackClient:
9
 
10
+ def __init__(self, token: str = None):
11
+ self.client = AsyncWebClient(token=token)
12
 
13
  async def __aenter__(self):
14
  return self
 
35
  return file_info
36
 
37
  async def get_users_list(self):
38
+ logger.info("Getting users list...")
39
  users_list = await self.client.users_list()
40
+ logger.info(f"Users list: {users_list}")
41
  filtered_users = [
42
+ user
43
+ for user in users_list["members"]
44
+ if user["is_bot"] == False and "email" in user["profile"]
45
  ]
46
  return filtered_users
47
+
48
+ async def handle_oauth_callback(self, code):
49
+ return await self.client.oauth_v2_access(
50
+ client_id=os.environ.get("SLACK_CLIENT_ID"),
51
+ client_secret=os.environ.get("SLACK_CLIENT_SECRET"),
52
+ code=code,
53
+ redirect_uri=os.environ.get("SLACK_REDIRECT_URI"),
54
+ )