Skip to content

Commit 54d357a

Browse files
author
Irving Popovetsky
committed
add a simple manage command
1 parent 8ef2407 commit 54d357a

File tree

4 files changed

+248
-30
lines changed

4 files changed

+248
-30
lines changed

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: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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("No Slack token configured. Set BOT_USER_OAUTH_ACCESS_TOKEN.")
62+
sys.exit(1)
63+
64+
logger.info(f"Starting team_join replay for user: {user_id}")
65+
logger.info(f" Skip messages: {skip_messages}")
66+
logger.info(f" Skip backend linking: {skip_backend}")
67+
logger.info(f" Backend URL: {BACKEND_URL}")
68+
logger.info(f" Community channel: {COMMUNITY_CHANNEL}")
69+
70+
async with aiohttp.ClientSession() as session:
71+
slack_api = SlackAPI(session=session, token=token)
72+
73+
# Verify the user exists
74+
try:
75+
user_info = await slack_api.query(methods.USERS_INFO, {"user": user_id})
76+
if not user_info.get("ok"):
77+
logger.error(f"Failed to fetch user info: {user_info.get('error')}")
78+
sys.exit(1)
79+
user_name = user_info["user"].get("real_name", user_info["user"].get("name"))
80+
user_email = user_info["user"]["profile"].get("email", "N/A")
81+
logger.info(f" User found: {user_name} ({user_email})")
82+
except Exception as e:
83+
logger.error(f"Failed to fetch user info: {e}")
84+
sys.exit(1)
85+
86+
# Build the welcome messages
87+
*user_messages, community_message, outreach_team_message = build_messages(user_id)
88+
89+
if not skip_sleep:
90+
logger.info("Waiting 30 seconds (use --skip-sleep to bypass)...")
91+
await asyncio.sleep(30)
92+
93+
# Send Slack messages
94+
if not skip_messages:
95+
logger.info("Sending welcome messages to user...")
96+
try:
97+
await send_user_greetings(user_messages, slack_api)
98+
logger.info(" User messages sent successfully")
99+
except Exception as e:
100+
logger.error(f" Failed to send user messages: {e}")
101+
102+
logger.info("Sending community notifications...")
103+
try:
104+
await send_community_notification(community_message, slack_api)
105+
await send_community_notification(outreach_team_message, slack_api)
106+
logger.info(" Community notifications sent successfully")
107+
except Exception as e:
108+
logger.error(f" Failed to send community notifications: {e}")
109+
else:
110+
logger.info("Skipping Slack messages (--skip-messages)")
111+
112+
# Link user to backend
113+
if not skip_backend:
114+
logger.info("Authenticating with backend API...")
115+
headers = await get_backend_auth_headers(session)
116+
if headers:
117+
logger.info(" Backend authentication successful")
118+
logger.info("Linking user to backend profile...")
119+
try:
120+
await link_backend_user(user_id, headers, slack_api, session)
121+
logger.info(" Backend user linking completed")
122+
except Exception as e:
123+
logger.error(f" Failed to link backend user: {e}")
124+
else:
125+
logger.error(" Backend authentication failed - check BACKEND_USERNAME/BACKEND_PASS")
126+
else:
127+
logger.info("Skipping backend linking (--skip-backend)")
128+
129+
logger.info("Team join replay completed")
130+
131+
132+
def main():
133+
parser = argparse.ArgumentParser(
134+
description="Pybot management commands",
135+
formatter_class=argparse.RawDescriptionHelpFormatter,
136+
epilog="""
137+
Examples:
138+
# Replay full team_join workflow for a user
139+
python manage.py replay-team-join U0A9K62QTL4
140+
141+
# Only re-link user to backend (skip Slack messages)
142+
python manage.py replay-team-join U0A9K62QTL4 --skip-messages
143+
144+
# Only send Slack messages (skip backend linking)
145+
python manage.py replay-team-join U0A9K62QTL4 --skip-backend
146+
147+
# Include the 30-second delay (for realistic testing)
148+
python manage.py replay-team-join U0A9K62QTL4 --with-sleep
149+
""",
150+
)
151+
152+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
153+
154+
# replay-team-join command
155+
replay_parser = subparsers.add_parser(
156+
"replay-team-join",
157+
help="Replay the team_join workflow for a specific user",
158+
)
159+
replay_parser.add_argument(
160+
"user_id",
161+
help="Slack user ID (e.g., U0A9K62QTL4)",
162+
)
163+
replay_parser.add_argument(
164+
"--skip-messages",
165+
action="store_true",
166+
help="Skip sending Slack messages (useful for just re-linking backend)",
167+
)
168+
replay_parser.add_argument(
169+
"--skip-backend",
170+
action="store_true",
171+
help="Skip backend user linking (useful for just re-sending messages)",
172+
)
173+
replay_parser.add_argument(
174+
"--with-sleep",
175+
action="store_true",
176+
help="Include the 30-second delay before sending messages",
177+
)
178+
replay_parser.add_argument(
179+
"-v",
180+
"--verbose",
181+
action="store_true",
182+
help="Enable debug logging",
183+
)
184+
185+
args = parser.parse_args()
186+
187+
if args.command is None:
188+
parser.print_help()
189+
sys.exit(1)
190+
191+
if hasattr(args, "verbose") and args.verbose:
192+
logging.getLogger().setLevel(logging.DEBUG)
193+
194+
if args.command == "replay-team-join":
195+
asyncio.run(
196+
replay_team_join(
197+
user_id=args.user_id,
198+
skip_messages=args.skip_messages,
199+
skip_backend=args.skip_backend,
200+
skip_sleep=not args.with_sleep,
201+
)
202+
)
203+
204+
205+
if __name__ == "__main__":
206+
main()

poetry.lock

Lines changed: 36 additions & 30 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ black = "^25.0"
2424
ruff = "^0.14"
2525
requests = "^2.31"
2626

27+
[tool.poetry.scripts]
28+
pybot-manage = "manage:main"
29+
2730
[build-system]
2831
requires = ["poetry-core>=1.0.0"]
2932
build-backend = "poetry.core.masonry.api"

0 commit comments

Comments
 (0)