import os import sys import argparse import stripe from collections import defaultdict, deque import re import logging from dataclasses import dataclass, field, asdict import os from dotenv import load_dotenv import json import asyncio from tqdm import tqdm from datetime import datetime, timedelta import time import json from urllib.parse import urlparse from urllib.parse import quote_plus load_dotenv(dotenv_path=".env") # idk if required working_dir = os.getcwd() project_root = os.path.abspath( os.path.join(os.path.dirname(__file__), "../../studio_api") ) os.chdir(project_root) sys.path.append(project_root) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "studio_api.settings") os.environ[ "DJANGO_ALLOW_ASYNC_UNSAFE" ] = "true" # https://docs.djangoproject.com/en/4.1/topics/async/#async-safety database_url = f"postgresql://{os.getenv('DATABASE_USER')}:{quote_plus(os.getenv('DATABASE_PASSWORD'))}@{os.getenv('DATABASE_HOST')}/{os.getenv('DATABASE_NAME')}" replica_database_url = database_url print("Using database:") print(f"- host: {os.getenv('DATABASE_HOST')}") print(f"- username: {os.getenv('DATABASE_USER')}") print( f"- password: {'*' * len(os.getenv('DATABASE_PASSWORD')) if os.getenv('DATABASE_PASSWORD') else 'None'}" ) print(f"- database: {os.getenv('DATABASE_NAME')}") if not database_url or not replica_database_url: raise ValueError( "DATABASE_URL and REPLICA_DATABASE_URL must be set in environment variables" ) os.environ["DATABASE_URL"] = database_url os.environ["REPLICA_DATABASE_URL"] = replica_database_url import django django.setup() from django.contrib.auth.models import Group, User from studio_api.bots import models from django.utils import timezone from django.db.models import F def load_file(file_path): with open(file_path, "r") as file: return [s.strip() for s in file.readlines()] if __name__ == "__main__": parser = argparse.ArgumentParser(description="Unjail users from botjail, maybe") parser.add_argument( "--user-id-file", type=str, required=True, help="Path to file containing user IDs (one per line)", ) # parser.add_argument( # "--copycat-name", # type=str, # required=True, # help="Name of the copycat app/bot to store in the DB for reference.", # ) parser.add_argument( "--execute", action="store_true", help="Actually execute changes. Without this flag, runs in dry-run mode", ) args = parser.parse_args() should_execute = args.execute # combine working directory with user id file path using os.path.join user_id_file_path = os.path.join(working_dir, args.user_id_file) # load user ids user_ids = load_file(user_id_file_path) user_ids = [int(user_id) for user_id in user_ids] print(f"Loaded {len(user_ids)} user ids from {user_id_file_path}") BATCH_SIZE = 200 total_batches = len(user_ids) // BATCH_SIZE + 1 total_updated = 0 total_new = 0 # prompt for user confirmation if we are actually executing if should_execute: user_input = input( f"Are you sure you want to unjail up to {len(user_ids)} users from botjail? (y/n): " ) if user_input.lower() != "y": print("Aborting.") exit(0) total_unjailed = 0 for i in range(0, len(user_ids), BATCH_SIZE): batch_user_ids = user_ids[i : i + BATCH_SIZE] batch_number = i // BATCH_SIZE + 1 print(f"Processing batch {batch_number} of {total_batches}") existing_botjailed_users = models.BotJail.objects.filter( user_id__in=batch_user_ids ) if len(existing_botjailed_users) > BATCH_SIZE: raise Exception( f"Found {len(existing_botjailed_users)} botjailed users, more than expected. This should not happen." ) """ logic: - look at existing user - if they have are jailed for reason copycat or for reason api, leave them - latest_api_violation_at is not null - - if they are outlook or hotmail, leave them - otherwise if they are jailed only for reason hcaptcha... """ for user in existing_botjailed_users: if ( user.latest_jailed_reason == models.BotJail.Reason.COPYCAT or user.latest_jailed_reason == models.BotJail.Reason.API ): continue if user.user.email.lower().endswith( "@outlook.com" ) or user.user.email.lower().endswith("@hotmail.com"): continue if user.latest_api_violation_at is not None: continue if user.latest_copycat_violation_at is not None: continue total_unjailed += 1 # otherwise, unjail them if should_execute: # now = timezone.now() user.status = models.BotJail.Status.SUSPICIOUS user.latest_released_at = timezone.now() user.save() print(f"Unjailed user {user.user_id} - {user.user.username}") time.sleep(0.1) else: print(f"Would have unjailed user {user.user_id} - {user.user.username}") print(f"πŸŽΆπŸŽΆπŸŽΆπŸŽΆπŸŽΆπŸ“£πŸ€–πŸ€–πŸ€– Unjailed {total_unjailed} users. πŸŽΆπŸŽΆπŸŽΆπŸŽΆπŸŽΆπŸ“£πŸ€–πŸ€–πŸ€–")