Add Getting Started sections and correct usage commands to all category READMEs (TypeScript, Python, Bash). Update top-level README.md and CLAUDE.md to reflect the Bash language, correct project structure, and accurate make run instructions. Remove completed DOCS_TODO.md.
22 KiB
Cohort Scripts
Scripts for managing the NHCarrigan spring cohort programme. Covers the full lifecycle: applicant evaluation, Discord server setup, team assignment, member onboarding, activity tracking, and member removal.
Most scripts interact with the Discord API and require a DISCORD_BOT_TOKEN. Several also use the gh CLI for GitHub operations.
Getting Started
Run scripts via the interactive runner from the project root:
make run
# Select: Python → cohort → <script>
Or run directly:
cd python && op run --env-file=../prod.env -- uv run python cohort/<script>.py
Prerequisites:
- Run
make install-pyto set up the Python virtual environment. - Most scripts require
DISCORD_BOT_TOKENset inprod.env. - Scripts that manage GitHub teams require
gh auth loginto be run first.
Table of Contents
Applicant Evaluation
- verify_discord.py
- evaluate_technical_proficiency.py
- analyse_availability.py
- generate_member_files.py
- generate_timeslots.py
Server Setup
- create_team_voice_channels.py
- fix_channel_permissions.py
- update_cohort_leads_permissions.py
- list_all_guild_roles.py
- list_discord_roles.py
- check_channel_permissions.py
Member Onboarding
Ongoing Management
- get_cohort_members.py
- check_member_status.py
- fetch_roster.py
- update_roster_messages.py
- send_checkin.py
- send_team_checkin.py
- send_team_messages.py
Activity Tracking
Member Removal
verify_discord.py
Reads Discord user IDs from a markdown table and verifies each one against the Discord API, checking whether the user is a member of the freeCodeCamp guild. Handles rate limits and retries automatically.
Usage
make run
# Select: Python → cohort → verify_discord.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
table.md |
Markdown table | Applicant data; the script reads Discord IDs from the table rows |
Output (written to data/):
| File | Format | Description |
|---|---|---|
discord_verification.json |
JSON object | Results grouped as verified (with username), missing, and errors lists |
evaluate_technical_proficiency.py
Evaluates each applicant's technical proficiency by analysing their GitHub profile and self-described expertise. Scores applicants as Beginner, Intermediate, or Advanced based on public repository count, follower count, language variety, and keywords in their self-assessment text.
Usage
make run
# Select: Python → cohort → evaluate_technical_proficiency.py
Environment Variables
None. Uses the public GitHub API (unauthenticated, subject to rate limiting).
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
applicants_to_evaluate.json |
JSON array | Applicant records, each with a GitHub profile URL and a self-description field |
Output (written to data/):
| File | Format | Description |
|---|---|---|
proficiency_evaluations.json |
JSON array | Proficiency scores (Beginner/Intermediate/Advanced) and detected tech stacks per applicant |
Notes
- Unauthenticated GitHub API requests are rate-limited to 60 per hour. For large batches of applicants, consider adding a GitHub token or running the script in smaller chunks.
analyse_availability.py
Parses a markdown table of applicant availability responses with timezone information, converts local timeslot selections to UTC, and produces a JSON summary of UTC coverage across morning, afternoon, evening, and night blocks per applicant.
Usage
make run
# Select: Python → cohort → analyse_availability.py
Environment Variables
None.
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
table.md |
Markdown table | Availability responses with timezone column |
discord_verification.json |
JSON object | Output of verify_discord.py — used to link applicants to Discord usernames |
Output (written to data/):
| File | Format | Description |
|---|---|---|
availability_analysis.json |
JSON object | UTC block distribution (morning/afternoon/evening/night) per applicant |
generate_member_files.py
Consolidates all evaluation data into two markdown files: one for participants and one for team leaders. Each entry includes the member's tech stack, availability, proficiency level, and (for leaders) their leadership assessment.
Usage
make run
# Select: Python → cohort → generate_member_files.py
Environment Variables
None.
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
discord_verification.json |
JSON object | Verified applicant Discord info |
proficiency_evaluations.json |
JSON array | Technical proficiency scores |
availability_analysis.json |
JSON object | UTC availability blocks |
leadership_candidates.json |
JSON array | Applicants being considered for leadership roles |
leadership_evaluations.json |
JSON array | Leadership assessment results |
Output (written to data/):
| File | Format | Description |
|---|---|---|
participants.md |
Markdown | Profile summary for each participant |
leaders.md |
Markdown | Profile summary for each team leader candidate |
generate_timeslots.py
Generates a list of hourly timeslots for use with the Crabfit scheduling tool, covering a date range at Los Angeles timezone.
Usage
make run
# Select: Python → cohort → generate_timeslots.py
Environment Variables
None.
Data Files
Output (written to data/):
| File | Format | Description |
|---|---|---|
crabfit_timeslots.json |
JSON array of ISO-format strings | One entry per hour across the configured date range |
Notes
- The date range and timezone are hardcoded in the script. Update the start/end date constants before running.
create_team_voice_channels.py
Creates private voice channels for each cohort team in a specified Discord category. Each channel is visible and joinable only by members who have that team's role.
Usage
make run
# Select: Python → cohort → create_team_voice_channels.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Team role IDs and the target category ID are hardcoded in the script.
Notes
- Update the team role IDs, channel names, and category ID constants in the script before running.
fix_channel_permissions.py
Denies Send Messages and Send Messages in Threads permissions for the @everyone and @cohort roles on a specific Discord channel. Used to lock down a channel so only designated roles can post.
Usage
make run
# Select: Python → cohort → fix_channel_permissions.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. The target channel ID is hardcoded in the script.
Notes
- Update the
CHANNEL_IDconstant before running.
update_cohort_leads_permissions.py
Grants the Cohort Leads role MENTION_EVERYONE and PIN_MESSAGES permissions across all 14 team channels.
Usage
make run
# Select: Python → cohort → update_cohort_leads_permissions.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Team channel IDs are hardcoded in the script.
list_all_guild_roles.py
Lists all roles in the freeCodeCamp Discord guild, highlighting team and cohort-related roles. Useful for finding role IDs to use in other scripts.
Usage
make run
# Select: Python → cohort → list_all_guild_roles.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Output is printed to stdout.
list_discord_roles.py
Lists Discord roles in the freeCodeCamp server, filtering for cohort-, leader-, and 2026-related roles.
Usage
make run
# Select: Python → cohort → list_discord_roles.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Output is printed to stdout.
check_channel_permissions.py
Audits cohort-team-* Discord channels to identify incorrect Send Messages or Send Messages in Threads permissions on the @everyone or @cohort roles.
Usage
make run
# Select: Python → cohort → check_channel_permissions.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Results are printed to stdout.
send_team_messages.py
Sends initial welcome and roster messages to all 14 team Discord channels, pins them, and saves the resulting message IDs, channel IDs, and role IDs to data/team_message_ids.json. This file is required by several other scripts.
Usage
make run
# Select: Python → cohort → send_team_messages.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Team rosters (leaders and participants per team) |
applicants_to_evaluate.json |
JSON array | Applicant data used to populate roster messages |
Output (written to data/):
| File | Format | Description |
|---|---|---|
team_message_ids.json |
JSON object | Channel ID, pinned message ID, and role ID per team name |
Notes
- Run this once at the start of the cohort. Other scripts (
send_checkin.py,update_roster_messages.py, etc.) depend onteam_message_ids.json.
assign_cohort_role.py
Assigns the Cohort Discord role to all cohort participants. Reads the full list of unique member Discord IDs from team_assignments.json and assigns the role to each one, with exponential backoff to handle rate limits.
Usage
make run
# Select: Python → cohort → assign_cohort_role.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Team rosters; all unique user IDs are extracted from here |
assign_team_roles.py
Assigns team-specific Discord roles to all cohort participants based on their team membership in team_assignments.json. Uses exponential backoff for rate limit handling.
Usage
make run
# Select: Python → cohort → assign_team_roles.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Team rosters with Discord IDs and corresponding role IDs |
add_github_team_members.py
Adds cohort members to their corresponding GitHub teams in the nhcarrigan-spring-2026-cohort organisation. Leaders are added to both the main team and the corresponding -leaders sub-team; participants are added to the main team only.
Usage
make run
# Select: Python → cohort → add_github_team_members.py
Environment Variables
None. Uses the gh CLI for authentication (run gh auth login first).
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Team rosters with leader/participant distinction |
discord_to_github.json |
JSON object | Maps Discord user IDs to GitHub usernames |
get_cohort_members.py
Fetches all Discord guild members with the Cohort role and writes their IDs, usernames, and display names to a JSON file. Also prints a formatted list to stdout.
Usage
make run
# Select: Python → cohort → get_cohort_members.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Output (written to data/):
| File | Format | Description |
|---|---|---|
active_cohort_members.json |
JSON array | Objects with id, username, and display_name for each Cohort role member |
check_member_status.py
Verifies whether specific Discord member IDs are still in the freeCodeCamp guild by querying the Discord API. Used to confirm that removed members have been successfully ejected.
Usage
make run
# Select: Python → cohort → check_member_status.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. The member IDs to check are hardcoded in the script. Update the list before running.
fetch_roster.py
Fetches pinned messages from a specific team channel to retrieve the current roster.
Usage
make run
# Select: Python → cohort → fetch_roster.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Output is printed to stdout as JSON.
Notes
- The target channel ID is hardcoded. Update it before running.
update_roster_messages.py
Edits the pinned team roster messages in each Discord channel with the latest data from team_assignments.json and discord_to_github.json. Run this after any membership changes to keep the pinned rosters up to date.
Usage
make run
# Select: Python → cohort → update_roster_messages.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_message_ids.json |
JSON object | Channel and message IDs per team (output of send_team_messages.py) |
team_assignments.json |
JSON object | Current team rosters |
discord_to_github.json |
JSON object | Discord ID → GitHub username mapping |
send_checkin.py
Sends biweekly check-in prompts to all team Discord channels (except Jade Jasmine), automatically creating threads for responses. Members who do not respond face removal for inactivity.
Usage
make run
# Select: Python → cohort → send_checkin.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_message_ids.json |
JSON object | Channel IDs per team |
send_team_checkin.py
Sends a capacity check-in message to each team channel asking whether the team feels able to complete their project with their current member count, and inviting them to request support if needed.
Usage
make run
# Select: Python → cohort → send_team_checkin.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_message_ids.json |
JSON object | Channel IDs per team |
team_assignments.json |
JSON object | Current team rosters (used to mention team members) |
discord_activity_checker.py
Scans each team's Discord channel and threads to identify members who have not sent a message within the last 36 hours. Can optionally send notification messages directly to inactive members via the --send CLI flag.
Usage
# Check only (no messages sent)
make run
# Select: Python → cohort → discord_activity_checker.py
# Check and notify inactive members
cd python && op run --env-file=../prod.env -- uv run python cohort/discord_activity_checker.py --send
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Member lists per team |
Output (written to data/):
| File | Format | Description |
|---|---|---|
discord_activity_report.json |
JSON object | Inactive members per team with their last message timestamp |
catch_up_report.py
Generates a detailed markdown activity report covering Discord messages (in team channels and their threads) and GitHub activity (PRs, issues, comments, reviews, commits) since a configured start date. Uses async API calls for efficiency.
Usage
make run
# Select: Python → cohort → catch_up_report.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Team rosters |
discord_to_github.json |
JSON object | Discord ID → GitHub username mapping |
Output (written to data/):
| File | Format | Description |
|---|---|---|
catch_up_report.md |
Markdown table | Activity counts per member (Discord messages + GitHub contributions) per team |
Notes
- The report start date is hardcoded in the script. Update it before each use.
- GitHub activity is fetched via the unauthenticated public API; rate limiting may apply for large cohorts.
check_lengths.py
Dry-run validation that parses catch_up_report.md and formats each team's data into Discord monospace table strings, checking whether any would exceed Discord's 2,000-character message limit before actually sending them.
Usage
make run
# Select: Python → cohort → check_lengths.py
Environment Variables
None.
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
catch_up_report.md |
Markdown table | Output of catch_up_report.py |
Notes
- Run this after
catch_up_report.pyand beforesend_activity_report.pyto catch any messages that would be rejected by Discord.
send_activity_report.py
Parses catch_up_report.md and sends the formatted activity table for each team to its Discord channel as a monospace code block.
Usage
make run
# Select: Python → cohort → send_activity_report.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input (expected in data/):
| File | Format | Description |
|---|---|---|
catch_up_report.md |
Markdown table | Output of catch_up_report.py |
Notes
- Run
check_lengths.pyfirst to verify that no messages exceed Discord's character limit.
remove_member.py
Comprehensive member removal script. Given a Discord ID as a CLI argument, it:
- Removes the member from
team_assignments.json. - Removes the member from
discord_to_github.json. - Prints GitHub removal instructions.
- Removes the member's Discord Cohort and team roles.
- Sends an announcement message to the team's Discord channel.
- Outputs markdown notes suitable for pasting into
COHORT_NOTES.md.
Usage
cd python && op run --env-file=../prod.env -- uv run python cohort/remove_member.py <discord_id>
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
Input/Output (read and updated in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Updated: member removed from their team |
discord_to_github.json |
JSON object | Updated: Discord→GitHub mapping removed |
team_message_ids.json |
JSON object | Read: used to find the team's Discord channel ID |
remove_resigned_members.py
Removes a list of resigned member Discord IDs from team_assignments.json, updating team rosters and reporting which teams were affected. Does not interact with Discord or GitHub.
Usage
make run
# Select: Python → cohort → remove_resigned_members.py
Environment Variables
None.
Data Files
Input/Output (read and updated in data/):
| File | Format | Description |
|---|---|---|
team_assignments.json |
JSON object | Updated in place; affected teams are reported to stdout |
Notes
- The list of Discord IDs to remove is hardcoded in the script. Update it before running.
- This script only updates the local JSON file. Run
update_roster_messages.pyand handle GitHub/Discord role removal separately.
remove_discord_roles.py
Removes the Cohort and team-specific Discord roles from a hardcoded list of inactive members.
Usage
make run
# Select: Python → cohort → remove_discord_roles.py
Environment Variables
| Variable | Description |
|---|---|
DISCORD_BOT_TOKEN |
Discord bot token |
Data Files
None. Member IDs and their team-to-role mappings are hardcoded in the script. Update them before running.