Skip to content

Commit 7fca1a7

Browse files
author
Irving Popovetsky
authored
Merge pull request #420 from OperationCode/irving/fix_join_team
Fix join_team workflow, add a manage.py script for debugging said workflow
2 parents 2286e71 + 8d32182 commit 7fca1a7

28 files changed

+4037
-55
lines changed

docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ WORKDIR /app
2222
RUN pip install --no-cache-dir poetry
2323

2424
# Copy dependency files
25-
COPY pyproject.toml poetry.lock README.md logging.yml ./
25+
COPY pyproject.toml poetry.lock README.md logging.yml manage.py ./
2626
COPY pybot ./pybot
2727

2828
# Install dependencies into .venv in project

logging.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ loggers:
2525
slack:
2626
level: DEBUG
2727
propagate: true
28+
urllib3:
29+
level: WARNING
30+
propagate: true
2831
root:
2932
level: DEBUG
3033
handlers:

manage.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/usr/bin/env python
2+
"""
3+
Management commands for Pybot.
4+
5+
Usage:
6+
python manage.py replay-team-join <slack_user_id>
7+
python manage.py replay-team-join <slack_user_id> --skip-messages
8+
python manage.py replay-team-join <slack_user_id> --skip-backend
9+
"""
10+
11+
import argparse
12+
import asyncio
13+
import logging
14+
import os
15+
import sys
16+
17+
import aiohttp
18+
from dotenv import load_dotenv
19+
20+
# Load environment variables from .env file if present
21+
load_dotenv()
22+
23+
logging.basicConfig(
24+
level=logging.INFO,
25+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
26+
)
27+
logger = logging.getLogger(__name__)
28+
29+
30+
async def replay_team_join(
31+
user_id: str,
32+
skip_messages: bool = False,
33+
skip_backend: bool = False,
34+
skip_sleep: bool = True,
35+
) -> None:
36+
"""
37+
Replay the team_join workflow for a specific Slack user.
38+
39+
This is useful for:
40+
- Testing the team_join flow after code changes
41+
- Re-linking a user to the backend after API changes
42+
- Debugging issues with the onboarding flow
43+
"""
44+
from pybot._vendor.slack import methods
45+
from pybot._vendor.slack.io.aiohttp import SlackAPI
46+
from pybot.endpoints.slack.utils import (
47+
BACKEND_URL,
48+
COMMUNITY_CHANNEL,
49+
slack_configs,
50+
)
51+
from pybot.endpoints.slack.utils.event_utils import (
52+
build_messages,
53+
get_backend_auth_headers,
54+
link_backend_user,
55+
send_community_notification,
56+
send_user_greetings,
57+
)
58+
59+
token = slack_configs.get("token")
60+
if not token:
61+
logger.error(
62+
"No Slack token configured. Set one of: "
63+
"BOT_USER_OAUTH_ACCESS_TOKEN, BOT_OAUTH_TOKEN, or SLACK_TOKEN"
64+
)
65+
sys.exit(1)
66+
67+
logger.info(f"Starting team_join replay for user: {user_id}")
68+
logger.info(f" Skip messages: {skip_messages}")
69+
logger.info(f" Skip backend linking: {skip_backend}")
70+
logger.info(f" Backend URL: {BACKEND_URL}")
71+
logger.info(f" Community channel: {COMMUNITY_CHANNEL}")
72+
73+
async with aiohttp.ClientSession() as session:
74+
slack_api = SlackAPI(session=session, token=token)
75+
76+
# Verify the user exists
77+
try:
78+
user_info = await slack_api.query(methods.USERS_INFO, {"user": user_id})
79+
if not user_info.get("ok"):
80+
logger.error(f"Failed to fetch user info: {user_info.get('error')}")
81+
sys.exit(1)
82+
user_name = user_info["user"].get("real_name", user_info["user"].get("name"))
83+
user_email = user_info["user"]["profile"].get("email", "N/A")
84+
logger.info(f" User found: {user_name} ({user_email})")
85+
except Exception as e:
86+
logger.error(f"Failed to fetch user info: {e}")
87+
sys.exit(1)
88+
89+
# Build the welcome messages
90+
*user_messages, community_message, outreach_team_message = build_messages(user_id)
91+
92+
if not skip_sleep:
93+
logger.info("Waiting 30 seconds (use --skip-sleep to bypass)...")
94+
await asyncio.sleep(30)
95+
96+
# Send Slack messages
97+
if not skip_messages:
98+
logger.info("Sending welcome messages to user...")
99+
try:
100+
await send_user_greetings(user_messages, slack_api)
101+
logger.info(" User messages sent successfully")
102+
except Exception as e:
103+
logger.error(f" Failed to send user messages: {e}")
104+
105+
logger.info("Sending community notifications...")
106+
try:
107+
await send_community_notification(community_message, slack_api)
108+
await send_community_notification(outreach_team_message, slack_api)
109+
logger.info(" Community notifications sent successfully")
110+
except Exception as e:
111+
logger.error(f" Failed to send community notifications: {e}")
112+
else:
113+
logger.info("Skipping Slack messages (--skip-messages)")
114+
115+
# Link user to backend
116+
if not skip_backend:
117+
logger.info("Authenticating with backend API...")
118+
headers = await get_backend_auth_headers(session)
119+
if headers:
120+
logger.info(" Backend authentication successful")
121+
logger.info("Linking user to backend profile...")
122+
try:
123+
await link_backend_user(user_id, headers, slack_api, session)
124+
logger.info(" Backend user linking completed")
125+
except Exception as e:
126+
logger.error(f" Failed to link backend user: {e}")
127+
else:
128+
logger.error(" Backend authentication failed - check BACKEND_USERNAME/BACKEND_PASS")
129+
else:
130+
logger.info("Skipping backend linking (--skip-backend)")
131+
132+
logger.info("Team join replay completed")
133+
134+
135+
def main():
136+
parser = argparse.ArgumentParser(
137+
description="Pybot management commands",
138+
formatter_class=argparse.RawDescriptionHelpFormatter,
139+
epilog="""
140+
Examples:
141+
# Replay full team_join workflow for a user
142+
python manage.py replay-team-join U0A9K62QTL4
143+
144+
# Only re-link user to backend (skip Slack messages)
145+
python manage.py replay-team-join U0A9K62QTL4 --skip-messages
146+
147+
# Only send Slack messages (skip backend linking)
148+
python manage.py replay-team-join U0A9K62QTL4 --skip-backend
149+
150+
# Include the 30-second delay (for realistic testing)
151+
python manage.py replay-team-join U0A9K62QTL4 --with-sleep
152+
""",
153+
)
154+
155+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
156+
157+
# replay-team-join command
158+
replay_parser = subparsers.add_parser(
159+
"replay-team-join",
160+
help="Replay the team_join workflow for a specific user",
161+
)
162+
replay_parser.add_argument(
163+
"user_id",
164+
help="Slack user ID (e.g., U0A9K62QTL4)",
165+
)
166+
replay_parser.add_argument(
167+
"--skip-messages",
168+
action="store_true",
169+
help="Skip sending Slack messages (useful for just re-linking backend)",
170+
)
171+
replay_parser.add_argument(
172+
"--skip-backend",
173+
action="store_true",
174+
help="Skip backend user linking (useful for just re-sending messages)",
175+
)
176+
replay_parser.add_argument(
177+
"--with-sleep",
178+
action="store_true",
179+
help="Include the 30-second delay before sending messages",
180+
)
181+
replay_parser.add_argument(
182+
"-v",
183+
"--verbose",
184+
action="store_true",
185+
help="Enable debug logging",
186+
)
187+
188+
args = parser.parse_args()
189+
190+
if args.command is None:
191+
parser.print_help()
192+
sys.exit(1)
193+
194+
if hasattr(args, "verbose") and args.verbose:
195+
logging.getLogger().setLevel(logging.DEBUG)
196+
197+
if args.command == "replay-team-join":
198+
asyncio.run(
199+
replay_team_join(
200+
user_id=args.user_id,
201+
skip_messages=args.skip_messages,
202+
skip_backend=args.skip_backend,
203+
skip_sleep=not args.with_sleep,
204+
)
205+
)
206+
207+
208+
if __name__ == "__main__":
209+
main()

0 commit comments

Comments
 (0)