NeerajCodz commited on
Commit
db25c73
·
verified ·
1 Parent(s): ee7cf13
Files changed (1) hide show
  1. app.py +46 -14
app.py CHANGED
@@ -17,6 +17,7 @@ from slack_bolt.oauth.async_oauth_settings import AsyncOAuthSettings
17
  from slack_sdk.oauth.installation_store.async_installation_store import AsyncInstallationStore
18
  from slack_sdk.oauth import AuthorizeUrlGenerator
19
  from slack_sdk.oauth.installation_store.models import Installation
 
20
 
21
  # RAG/ML Libraries
22
  from sentence_transformers import SentenceTransformer
@@ -51,6 +52,10 @@ if not all(required_vars):
51
  missing = [var for var in required_vars if not var]
52
  raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
53
 
 
 
 
 
54
  # Set HF_TOKEN if provided (helps with authentication for Hub access)
55
  if HF_TOKEN:
56
  try:
@@ -62,7 +67,7 @@ if HF_TOKEN:
62
  # Initialize Supabase client
63
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
64
 
65
- # --- Supabase Async Installation Store ---
66
  class SupabaseAsyncInstallationStore(AsyncInstallationStore):
67
  def __init__(self, supabase_client):
68
  self.supabase = supabase_client
@@ -85,14 +90,14 @@ class SupabaseAsyncInstallationStore(AsyncInstallationStore):
85
  raise
86
 
87
  async def fetch_installation(self, team_id: str, *, enterprise_id: str | None = None, user_id: str | None = None) -> Installation | None:
88
- logger.info(f"Fetching installation for team_id: {team_id}, enterprise_id: {enterprise_id}, user_id: {user_id}") # DEBUG LOG
89
  try:
90
  result = await asyncio.to_thread(
91
  lambda: self.supabase.table("installations").select("*").eq("team_id", team_id).execute()
92
  )
93
- logger.info(f"Fetch result for {team_id}: {len(result.data)} rows") # DEBUG LOG
94
  if not result.data:
95
- logger.warning(f"No installation found for team {team_id}") # Instead of error here
96
  return None
97
 
98
  data = result.data[0]
@@ -120,20 +125,44 @@ class SupabaseAsyncInstallationStore(AsyncInstallationStore):
120
  logger.error(f"Failed to delete installation for team {team_id}: {e}")
121
  raise
122
 
123
- # Initialize installation store
124
- installation_store = SupabaseAsyncInstallationStore(supabase)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  # Initialize Bolt Async App
127
  app = AsyncApp(
128
  signing_secret=SLACK_SIGNING_SECRET,
 
129
  installation_store=installation_store,
 
130
  oauth_settings=AsyncOAuthSettings(
131
  client_id=SLACK_CLIENT_ID,
132
  client_secret=SLACK_CLIENT_SECRET,
133
  scopes=["app_mentions:read", "files:read", "chat:write", "im:read", "im:write", "channels:read"],
134
  redirect_uri_path="/slack/oauth/callback",
135
  ),
136
- # Removed token for multi-workspace support; add back if single-workspace only
137
  )
138
 
139
  api = FastAPI()
@@ -364,6 +393,7 @@ handler = AsyncSlackRequestHandler(app)
364
  async def slack_events(request: Request):
365
  """Endpoint for all Slack event subscriptions."""
366
  try:
 
367
  return await handler.handle(request)
368
  except Exception as e:
369
  logger.error(f"Error handling Slack events: {e}")
@@ -397,9 +427,8 @@ async def install_url():
397
  client_id=SLACK_CLIENT_ID,
398
  scopes=["app_mentions:read", "files:read", "chat:write", "im:read", "im:write", "channels:read"]
399
  )
400
- # NOTE: The redirect_uri should match your Slack App config (e.g., https://yourspace.hf.space/slack/oauth/callback)
401
- # If needed, add: redirect_uri=os.environ.get("SLACK_REDIRECT_URI")
402
  url = generator.generate(state="state")
 
403
  return {"install_url": url}
404
  except Exception as e:
405
  logger.error(f"Error generating install URL: {e}")
@@ -409,19 +438,22 @@ async def install_url():
409
  async def oauth_callback(request: Request):
410
  """Handles the OAuth callback from Slack to complete installation."""
411
  try:
 
412
  response = await handler.handle(request)
413
- logger.info(f"OAuth callback response: {response.status_code if hasattr(response, 'status_code') else 'No status'}") # DEBUG
414
- # For successful OAuth, return a simple HTML response
415
  if hasattr(response, 'status_code') and response.status_code == 200:
 
 
 
416
  return HTMLResponse(
417
- content="<html><body><h1>Installation successful!</h1><p>You can now use the bot in your Slack workspace.</p><p>Check Supabase for new installation data.</p></body></html>",
418
  status_code=200
419
  )
420
  return response
421
  except Exception as e:
422
  logger.error(f"OAuth Callback Error: {e}")
423
  return HTMLResponse(
424
- content=f"<html><body><h1>Installation Failed!</h1><p>Error: {str(e)}</p><p>Check logs and Supabase.</p></body></html>",
425
  status_code=500
426
  )
427
 
@@ -433,7 +465,7 @@ async def debug_installations():
433
  result = await asyncio.to_thread(
434
  lambda: supabase.table("installations").select("*").execute()
435
  )
436
- return {"installations": result.data}
437
  except Exception as e:
438
  return {"error": str(e)}
439
 
 
17
  from slack_sdk.oauth.installation_store.async_installation_store import AsyncInstallationStore
18
  from slack_sdk.oauth import AuthorizeUrlGenerator
19
  from slack_sdk.oauth.installation_store.models import Installation
20
+ from slack_bolt.authorization import AsyncAuthorizeResult # Added for custom authorize
21
 
22
  # RAG/ML Libraries
23
  from sentence_transformers import SentenceTransformer
 
52
  missing = [var for var in required_vars if not var]
53
  raise ValueError(f"Missing required environment variables: {', '.join(missing)}")
54
 
55
+ # For single-team mode, require SLACK_BOT_TOKEN
56
+ if not SLACK_BOT_TOKEN:
57
+ raise ValueError("SLACK_BOT_TOKEN is required for single-team mode.")
58
+
59
  # Set HF_TOKEN if provided (helps with authentication for Hub access)
60
  if HF_TOKEN:
61
  try:
 
67
  # Initialize Supabase client
68
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
69
 
70
+ # --- Supabase Async Installation Store (for multi-team; optional) ---
71
  class SupabaseAsyncInstallationStore(AsyncInstallationStore):
72
  def __init__(self, supabase_client):
73
  self.supabase = supabase_client
 
90
  raise
91
 
92
  async def fetch_installation(self, team_id: str, *, enterprise_id: str | None = None, user_id: str | None = None) -> Installation | None:
93
+ logger.info(f"Fetching installation for team_id: {team_id}, enterprise_id: {enterprise_id}, user_id: {user_id}")
94
  try:
95
  result = await asyncio.to_thread(
96
  lambda: self.supabase.table("installations").select("*").eq("team_id", team_id).execute()
97
  )
98
+ logger.info(f"Fetch result for {team_id}: {len(result.data)} rows")
99
  if not result.data:
100
+ logger.warning(f"No installation found for team {team_id}")
101
  return None
102
 
103
  data = result.data[0]
 
125
  logger.error(f"Failed to delete installation for team {team_id}: {e}")
126
  raise
127
 
128
+ # Initialize installation store (only if multi-team)
129
+ installation_store = None
130
+ USE_MULTI_TEAM = os.environ.get("USE_MULTI_TEAM", "false").lower() == "true" # Set env to 'true' for multi-team
131
+ if USE_MULTI_TEAM:
132
+ installation_store = SupabaseAsyncInstallationStore(supabase)
133
+ logger.info("Using multi-team mode with Supabase store.")
134
+ else:
135
+ logger.info("Using single-team mode with SLACK_BOT_TOKEN.")
136
+
137
+ # Custom authorize for multi-team (falls back to store)
138
+ async def async_authorize(enterprise_id, team_id, user_id):
139
+ logger.info(f"Custom authorize called for enterprise: {enterprise_id}, team: {team_id}, user: {user_id}")
140
+ if not USE_MULTI_TEAM:
141
+ return None # Single-team doesn't need this
142
+ installation = await installation_store.fetch_installation(team_id=team_id, enterprise_id=enterprise_id)
143
+ if installation:
144
+ return AsyncAuthorizeResult(
145
+ enterprise_id=enterprise_id or installation.enterprise_id,
146
+ team_id=team_id,
147
+ user_id=user_id or installation.user_id,
148
+ bot_token=installation.bot_token,
149
+ bot_user_id=installation.bot_user_id,
150
+ )
151
+ logger.error(f"No authorization found for team {team_id}")
152
+ return None
153
 
154
  # Initialize Bolt Async App
155
  app = AsyncApp(
156
  signing_secret=SLACK_SIGNING_SECRET,
157
+ token=SLACK_BOT_TOKEN if not USE_MULTI_TEAM else None, # Single-team: use token
158
  installation_store=installation_store,
159
+ authorize=async_authorize if USE_MULTI_TEAM else None, # Multi-team: custom authorize
160
  oauth_settings=AsyncOAuthSettings(
161
  client_id=SLACK_CLIENT_ID,
162
  client_secret=SLACK_CLIENT_SECRET,
163
  scopes=["app_mentions:read", "files:read", "chat:write", "im:read", "im:write", "channels:read"],
164
  redirect_uri_path="/slack/oauth/callback",
165
  ),
 
166
  )
167
 
168
  api = FastAPI()
 
393
  async def slack_events(request: Request):
394
  """Endpoint for all Slack event subscriptions."""
395
  try:
396
+ logger.info("Received Slack event request")
397
  return await handler.handle(request)
398
  except Exception as e:
399
  logger.error(f"Error handling Slack events: {e}")
 
427
  client_id=SLACK_CLIENT_ID,
428
  scopes=["app_mentions:read", "files:read", "chat:write", "im:read", "im:write", "channels:read"]
429
  )
 
 
430
  url = generator.generate(state="state")
431
+ logger.info(f"Generated install URL: {url}")
432
  return {"install_url": url}
433
  except Exception as e:
434
  logger.error(f"Error generating install URL: {e}")
 
438
  async def oauth_callback(request: Request):
439
  """Handles the OAuth callback from Slack to complete installation."""
440
  try:
441
+ logger.info("OAuth callback received")
442
  response = await handler.handle(request)
443
+ logger.info(f"OAuth callback response: {response.status_code if hasattr(response, 'status_code') else 'No status'}")
 
444
  if hasattr(response, 'status_code') and response.status_code == 200:
445
+ # In single-team, no need to save; for multi, it should auto-save via store
446
+ if USE_MULTI_TEAM:
447
+ logger.info("Multi-team install: Check Supabase for new data.")
448
  return HTMLResponse(
449
+ content=f"<html><body><h1>Installation successful!</h1><p>You can now use the bot in your Slack workspace.</p><p>Mode: {'Single-team (no store needed)' if not USE_MULTI_TEAM else 'Multi-team (check Supabase)'}</p><p><a href='/debug/installations'>Debug Installations</a></p></body></html>",
450
  status_code=200
451
  )
452
  return response
453
  except Exception as e:
454
  logger.error(f"OAuth Callback Error: {e}")
455
  return HTMLResponse(
456
+ content=f"<html><body><h1>Installation Failed!</h1><p>Error: {str(e)}</p><p>Check logs. If multi-team, ensure Supabase table exists.</p></body></html>",
457
  status_code=500
458
  )
459
 
 
465
  result = await asyncio.to_thread(
466
  lambda: supabase.table("installations").select("*").execute()
467
  )
468
+ return {"installations": result.data, "mode": "multi-team" if USE_MULTI_TEAM else "single-team"}
469
  except Exception as e:
470
  return {"error": str(e)}
471