diff --git a/.gitignore b/.gitignore
index 134e751..fc5d31a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -134,3 +134,7 @@ GitHub.sublime-settings
!.vscode/launch.json
!.vscode/extensions.json
.history
+
+# ai
+.claude
+CLAUDE.local.md
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 9b3221b..e0b3bb2 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/.idea/poc.iml b/.idea/poc.iml
index cdc9ae2..5b5795b 100644
--- a/.idea/poc.iml
+++ b/.idea/poc.iml
@@ -17,7 +17,7 @@
-
+
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..9ead3df
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,100 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+Oscarr is a Discord bot that integrates with Plex and Ombi to provide a Discord interface for managing media requests. The project consists of a Django backend that serves both as an admin interface and the bot's data layer.
+
+## Development Commands
+
+All commands should be run from `/packages/django-app/` directory using the `just` task runner:
+
+### Setup & Initialization
+- `just init-dcp` - Initialize Docker volumes and build images
+- `just dcp-generate-secret-key` - Generate Django secret key for .env
+- `just generate-pg-secret-key` - Generate PostgreSQL encryption key
+- `just create-superuser` - Create Django admin user
+- `just dcp-load-dev-data` - Load fixture data (admin:password login)
+- `just setup-local-python-venv` - Create local Python virtual environment and install dependencies
+
+### Development Workflow
+- `just dcp-up-all` - Start all containers (web + db)
+- `just dcp-migrate` - Run database migrations
+- `just start-discord-bot` - Launch Discord bot in container
+- `just sync-with-plex` - Sync movie data from Plex server
+
+### Testing & Code Quality
+- `just dcp-run-tests` - Run pytest test suite (excludes integration tests)
+- `just dcp-format` - Format Python code with autopep8
+
+### Backup & Data Management
+- `just dcp-dumpdata` - Export database to JSON
+- `just create-json-backup` - Create timestamped JSON backup
+- `just create-pgdump` - Create PostgreSQL dump backup
+
+### Utility Commands
+- `just update-oscarr` - Pull latest code, build, migrate, and restart containers
+- `just dcp-cleanup` - Stop and remove all containers and volumes
+- `just dcp-build-images` - Build both web and database Docker images
+
+## Architecture
+
+### Django Apps Structure
+- `core/` - User management with custom User model, Discord/Ombi integration
+- `plex/` - Plex server data models and sync functionality
+- `movie_requests/` - Movie request handling commands
+- `watchbot/` - Additional bot functionality
+- `discordbot/` - Discord bot implementation with commands
+- `services/` - External API integrations (Ombi, Radarr, TMDB)
+
+### Key Patterns
+- **Repository Pattern**: Base repository classes in `common/repositories/`
+- **Management Commands**: Custom Django commands for bot operations and data sync
+- **Signal Integration**: Django signals for automated workflows (e.g., Ombi user creation)
+- **Encrypted Fields**: Uses django-pgcrypto-fields for sensitive data
+
+### Discord Bot Commands
+- `/search_tmdb` - Search TMDB for movies with request buttons
+- `/request ` - Request movie by TMDB ID
+- `/search_plex` - Search Plex library (by title, actor, director, producer)
+- `/get_random` - Get random movie from Plex
+- `/bacon from: to: ` - Show actor connections through movies
+
+## Environment Setup
+
+The project requires a `.env` file in `/packages/django-app/` with configuration for:
+- Django secret keys
+- PostgreSQL credentials
+- Plex server connection
+- Ombi API credentials
+- Discord bot token
+
+See `.env.template` for required environment variables.
+
+## Technology Stack
+
+- **Backend**: Django 4.2.5, PostgreSQL 15.4
+- **Bot**: discord.py
+- **Media APIs**: PlexAPI, TMDB Simple
+- **External Services**: Ombi, Radarr
+- **Testing**: pytest with Django integration
+- **Containerization**: Docker Compose
+- **Task Runner**: Just (justfile)
+- **Code Quality**: autopep8, pylint, flake8
+
+## Development Notes
+
+- Uses Python 3.11.4 with Pipenv for dependency management
+- PostgreSQL with pgcrypto extension for encrypted fields
+- All Django management commands should be run via `./bin/dcp-django-admin.sh` for Docker
+- Tests exclude integration tests by default (use `-m "not integration"`)
+- The bot runs as a separate management command (`start_discord_bot`)
+- Plex sync should be run periodically to keep movie data current
+
+## Local Development Setup
+
+1. **Python Environment**: Use pyenv to install Python 3.11.4, then run `just setup-local-python-venv`
+2. **Docker Setup**: Run `just init-dcp` to initialize Docker containers and volumes
+3. **Database**: Run `just dcp-migrate` to apply database migrations
+4. **Development Data**: Run `just dcp-load-dev-data` to load test data (admin:password)
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..6d4d2e6
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,6 @@
+# TODOs 5/23/2025
+
+- [ ] create ombi user on user create
+- [ ] add bot command that only admin can use to create user
+ - [ ] would be extra nice to be able to select discord user from discord
+ interface to get discord user id for the new user when sending command
diff --git a/packages/django-app/app/core/apps.py b/packages/django-app/app/core/apps.py
index 8115ae6..3bcfc64 100644
--- a/packages/django-app/app/core/apps.py
+++ b/packages/django-app/app/core/apps.py
@@ -4,3 +4,7 @@
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core'
+
+ def ready(self):
+ """Import signal handlers when the app is ready."""
+ import core.signals
diff --git a/packages/django-app/app/core/signals.py b/packages/django-app/app/core/signals.py
new file mode 100644
index 0000000..5ea8cff
--- /dev/null
+++ b/packages/django-app/app/core/signals.py
@@ -0,0 +1,35 @@
+import logging
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
+from core.models import User
+from services.ombi import Ombi
+
+logger = logging.getLogger(__name__)
+
+
+@receiver(post_save, sender=User)
+def create_ombi_user(sender, instance, created, **kwargs):
+ """
+ Signal handler to create an Ombi user when a Django user is created,
+ and set the ombi_uid field on the Django user.
+ """
+ if created and not instance.ombi_uid:
+ try:
+ logger.info(f"Creating Ombi user for Django user {instance.nickname}")
+
+ # Create user in Ombi
+ response = Ombi.create_user(username=instance.nickname)
+
+ # Extract user ID from response and update Django user
+ if response and 'id' in response:
+ ombi_uid = response['id']
+ logger.info(f"Updating Django user {instance.nickname} with Ombi UID {ombi_uid}")
+
+ # Update the user without triggering the signal again
+ User.objects.filter(id=instance.id).update(ombi_uid=ombi_uid)
+ else:
+ logger.error(f"Failed to get Ombi UID from response: {response}")
+
+ except Exception as e:
+ logger.error(f"Error creating Ombi user for {instance.nickname}: {str(e)}")
diff --git a/packages/django-app/app/services/ombi.py b/packages/django-app/app/services/ombi.py
index 873570b..5754eee 100644
--- a/packages/django-app/app/services/ombi.py
+++ b/packages/django-app/app/services/ombi.py
@@ -44,3 +44,62 @@ def create_request(cls, data: dict) -> dict:
data = res.json()
return data
+
+ @classmethod
+ def create_user(cls, username: str, email: str = None, password: str = None) -> dict:
+ """
+ Creates a user in Ombi and returns the created user data including the user ID.
+
+ Args:
+ username: The username for the new Ombi user
+ email: Optional email for the user
+ password: Optional password for the user
+
+ Returns:
+ dict: The created user data containing the Ombi user ID
+ """
+ endpoint = f'{cls.base_url}/Identity'
+ logger.info(f"Ombi create user endpoint: {endpoint}")
+ headers = {
+ 'content-type': 'application/json',
+ 'ApiKey': settings.OMBI_API_KEY,
+ }
+
+ # Create user data
+ user_data = {
+ "userName": username,
+ "userType": 1,
+ "movieRequestLimit": 0,
+ "episodeRequestLimit": 0,
+ "musicRequestLimit": 0,
+ "streamingCountry": "us",
+ "movieRequestLimitType": 0,
+ "musicRequestLimitType": 0,
+ "episodeRequestLimitType": 0,
+ "hasLoggedIn": False,
+ "source": "local"
+ }
+
+ # Add optional fields if provided
+ if email:
+ user_data["emailAddress"] = email
+ if password:
+ user_data["password"] = password
+
+ logger.info(f"Creating Ombi user: {username}")
+ logger.info(f"Request data: {json.dumps(user_data, indent=2)}")
+ logger.info(f"Request headers: {headers}")
+
+ res = requests.post(
+ endpoint,
+ auth=HTTPBasicAuth(settings.SEEDBOX_UN, settings.SEEDBOX_PW),
+ data=json.dumps(user_data),
+ headers=headers)
+
+ if not res.ok:
+ logger.error(f"Failed to create Ombi user: {res.status_code} - {res.text}")
+ raise Exception(f"Failed to create Ombi user: {res.status_code} - {res.text}")
+
+ data = res.json()
+ logger.info(f"Successfully created Ombi user: {data}")
+ return data