');
}
function check_status($condition, $success_msg, $failure_msg) {
if ($condition) {
return "
SUCCESS: $success_msg
";
} else {
return "
FAILURE: $failure_msg
";
}
}
function check_shell_command($command, $package_name) {
$path = trim(shell_exec("command -v $command"));
return check_status(!empty($path), "$package_name is installed ($path).", "$package_name not found in PATH.");
}
$results = [];
// 1. Configuration File Validation
$config_path = __DIR__ . '/tool_config.json';
$results['config'] = check_status(file_exists($config_path), "tool_config.json found.", "tool_config.json is missing.");
if (file_exists($config_path)) {
$config_content = file_get_contents($config_path);
json_decode($config_content);
$results['config_json'] = check_status(json_last_error() === JSON_ERROR_NONE, "tool_config.json is valid JSON.", "tool_config.json contains invalid JSON: " . json_last_error_msg());
$config = json_decode($config_content, true)['tool'];
} else {
$config = null;
}
// 2. Database Connectivity
$db_host = 'localhost';
$db_name = 'digitalprank_db';
$db_user = 'dp_user';
$db_pass = '#$Dealer2355';
$pdo = null;
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
$results['db_connect'] = check_status(true, "Database connection successful.", "");
} catch (PDOException $e) {
$results['db_connect'] = check_status(false, "", "Database connection failed: " . $e->getMessage());
}
// 3. Table Existence Checks
if ($pdo) {
$required_tables = [
'wp_digitalprank_tools', 'wp_digitalprank_usage', 'wp_digitalprank_usage_log',
'wp_pms_member_subscriptions', 'wp_digitalprank_entitlements', 'wp_digitalprank_tool_overrides'
];
if ($config && isset($config['database']['tool_specific_table'])) {
$required_tables[] = $config['database']['tool_specific_table'];
}
foreach ($required_tables as $table) {
try {
$pdo->query("SELECT 1 FROM `$table` LIMIT 1");
$results["table_$table"] = check_status(true, "Table `$table` exists.", "");
} catch (Exception $e) {
$results["table_$table"] = check_status(false, "", "Table `$table` does not exist or is inaccessible.");
}
}
}
// 4. Dependencies Checking
if ($config) {
// PHP Extensions
foreach ($config['dependencies']['php_extensions'] as $ext) {
$results["php_ext_$ext"] = check_status(extension_loaded($ext), "PHP extension '$ext' is loaded.", "PHP extension '$ext' is NOT loaded.");
}
// System Packages
foreach ($config['dependencies']['system_packages'] as $pkg) {
$results["sys_pkg_$pkg"] = check_shell_command($pkg, $pkg);
}
// Python Packages
$venv_path = '/var/www/venv/ai-prank-caller/bin/python';
$results['python_venv'] = check_status(file_exists($venv_path), "Python venv found at $venv_path", "Python venv NOT found at $venv_path");
if(file_exists($venv_path)) {
foreach($config['dependencies']['python_packages'] as $pkg) {
$check_script = escapeshellarg("import " . explode('==', $pkg)[0]);
$command = "$venv_path -c $check_script";
shell_exec("$command 2>&1", $output, $return_code);
$results["py_pkg_$pkg"] = check_status($return_code === 0, "Python package '$pkg' is installed in venv.", "Python package '$pkg' is NOT installed in venv.");
}
}
}
// 5. Directory Permissions
$writable_dirs = [__DIR__ . '/uploads'];
foreach ($writable_dirs as $dir) {
$is_writable = is_dir($dir) && is_writable($dir);
$results["dir_$dir"] = check_status($is_writable, "Directory '$dir' is writable.", "Directory '$dir' is not writable or does not exist.");
}
?>
AI Prank Call Bot - Diagnostic Report
Diagnostic Report: AI Prank Call Bot
Configuration
Database
Tables
Dependencies
PHP Extensions
System Packages
Python Packages (VENV)
File System
FILE 5: /home/ai-prank-caller.digitalprank.com/public_html/help.md
code
Markdown
# AI Prank Call Bot Help Guide
Unleash hilarious, AI-powered prank calls on your friends! This guide will walk you through setting up and sending your first prank call.
## Quick Start Guide
Getting started is easy. Just follow these steps:
1. **Step 1:** Type your prank idea or script into the "Enter your prank script or idea" field.
2. **Step 2:** Pick a hilarious voice style from the "Voice Style" dropdown menu.
3. **Step 3:** Enter your victim's phone number. This is a Pro feature and requires an active subscription.
4. **Step 4:** Click 'Send Prank Call'!
5. **Step 5:** Enjoy the chaos and wait for your friend's reaction.
## Features
* **AI-Generated Voices**: Our advanced AI can read any script you provide in a variety of funny voices.
* **Custom Scripts**: You have complete control. Write your own script from scratch for a personalized prank.
* **Voice Style Library**: Choose from voices like "Annoying Robot", "Angry Neighbor", and "Confused Old Lady" to perfectly match your prank scenario.
* **Custom Audio Upload (Pro)**: Pro users can upload their own MP3 or WAV files instead of using our AI voices.
* **Caller ID Spoofing (Gold Tier & Above)**: Make the call appear to come from any number you choose. Please use this feature responsibly.
* **Scheduled Calls (Pro)**: Time your prank perfectly by scheduling it to be sent at a future date and time.
## Frequently Asked Questions (FAQ)
**Q: Is this legal?**
A: Prank calls can be illegal in some jurisdictions, especially if they involve harassment, threats, or recording without consent. This tool is for entertainment purposes only. Use it responsibly and only with friends who have a good sense of humor. You are responsible for complying with all local and federal laws.
**Q: Can I use my own voice?**
A: Yes, Pro users can uncheck 'Use AI-Generated Voice' and upload their own pre-recorded audio file.
**Q: What if the call doesn't go through?**
A: You will not be charged any credits for failed or unanswered calls. You can try again without penalty.
## Usage Examples
Here are a couple of ideas to get you started:
### Example 1: Wrong Pizza Delivery
* **Description**: A confused pizza guy calls the wrong number with a strange request.
* **Input**:
* **Script Prompt**: `Hello? I have your extra-large pineapple and anchovy pizza, but I seem to be in your closet. Should I just leave it on the shoe rack?`
* **Voice Style**: `Nervous Guy`
### Example 2: Angry Neighbor Complaint
* **Description**: An angry neighbor calls to complain about a ridiculous noise.
* **Input**:
* **Script Prompt**: `Yeah, hi, this is your neighbor from 3B. I need you to stop teaching your parrot to sing opera. It's scaring my cat, and frankly, you're both off-key.`
* **Voice Style**: `Angry Neighbor`
FILE 6: /home/digitalprank.com/public_html/blog/data/tools/ai-prank-caller.json
code
JSON
{
"slug": "ai-prank-caller",
"name": "AI Prank Call Bot",
"meta_title": "AI Prank Call Generator | Send Funny Custom Calls | DigitalPrank.com",
"meta_description": "Create and send hilarious AI-powered prank calls with funny voices. Type a script, choose a voice like 'Angry Neighbor' or 'Robot', and call your friends. Try it for free!",
"canonical_url": "https://digitalprank.com/tools/ai-prank-caller",
"long_description": "Our AI Prank Call Bot is the ultimate tool for harmless fun. Using cutting-edge text-to-speech technology, you can type any scenario and have it performed by a unique AI voice personality. From a squeaky chipmunk to a booming movie narrator, the possibilities are endless. Pro users can even schedule calls, set a custom caller ID, or upload their own audio for the ultimate personalized prank.",
"features_list": [
{
"name": "Text-to-Speech Engine",
"description": "Our AI converts your text script into natural-sounding, funny audio.",
"is_pro": false
},
{
"name": "Multiple Voice Styles",
"description": "Choose from a library of voices including robots, angry neighbors, and more.",
"is_pro": false
},
{
"name": "Phone Number Targeting",
"description": "Send your prank call directly to your friend's phone number.",
"is_pro": true
},
{
"name": "Custom Caller ID",
"description": "Set the phone number that appears on the recipient's caller ID.",
"is_pro": true,
"tier": "gold"
},
{
"name": "Call Scheduling",
"description": "Plan your prank in advance by scheduling the call for a specific time.",
"is_pro": true
},
{
"name": "Upload Custom Audio",
"description": "Use your own pre-recorded MP3 or WAV files for the prank.",
"is_pro": true
}
],
"user_guide": {
"title": "How to Send an AI Prank Call",
"steps": [
"Enter your prank script in the text box. Be creative!",
"Select a voice from the dropdown that best fits the prank.",
"If you are a Pro user, enter the recipient's full phone number in international format (e.g., +15551234567).",
"Optionally, Pro users can set a custom Caller ID or schedule the call.",
"Click 'Send Prank Call' and let our system handle the rest."
],
"best_practices": "For the best results, keep scripts short and to the point. Use voices that contrast with the message for extra humor. Always ensure your prank is harmless and that the recipient will find it funny. Do not use this service for harassment."
},
"structured_data": {
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "AI Prank Call Bot",
"description": "Send hilarious, AI-powered prank calls with custom scripts and funny voices.",
"url": "https://digitalprank.com/tools/ai-prank-caller",
"applicationCategory": "EntertainmentApplication",
"operatingSystem": "Any (Web-based)",
"offers": {
"@type": "Offer",
"price": "0.00",
"priceCurrency": "USD",
"description": "Free tier with 2 daily calls."
},
"softwareHelp": {
"@type": "CreativeWork",
"name": "AI Prank Call Bot Help & FAQ"
}
}
}
FILE 7: /home/ai-prank-caller.digitalprank.com/public_html/deploy.sh
code
Bash
#!/bin/bash
# Deployment script for the AI Prank Call Bot tool
# This script must be run with root privileges.
set -e
TOOL_SLUG="ai-prank-caller"
TOOL_DOMAIN="${TOOL_SLUG}.digitalprank.com"
TOOL_ROOT="/home/${TOOL_DOMAIN}/public_html"
VENV_PATH="/var/www/venv/${TOOL_SLUG}"
DB_NAME='digitalprank_db'
DB_USER='dp_user'
DB_PASS='#$Dealer2355'
echo "--- Starting deployment for ${TOOL_SLUG} ---"
# 1. Install System Packages
echo "[TASK 1/8] Installing system packages (ffmpeg, sox)..."
apt-get update
apt-get install -y ffmpeg sox
# 2. Install PHP Extensions (Assuming a common environment like Docker)
echo "[TASK 2/8] Ensuring PHP extensions are installed (curl, json, mbstring)..."
# In a real environment, you might use: docker-php-ext-install curl json mbstring
# For this script, we'll assume they are available or installed via another process.
php -m | grep -q 'curl' || echo "WARNING: curl PHP extension not found."
php -m | grep -q 'json' || echo "WARNING: json PHP extension not found."
php -m | grep -q 'mbstring' || echo "WARNING: mbstring PHP extension not found."
# 3. Create Python Virtual Environment and Install Packages
echo "[TASK 3/8] Setting up Python virtual environment..."
if [ ! -d "${VENV_PATH}" ]; then
python3 -m venv "${VENV_PATH}"
echo "Created Python venv at ${VENV_PATH}"
fi
echo "Installing Python packages (xtts, pydub, twilio)..."
source "${VENV_PATH}/bin/activate"
pip install --upgrade pip
pip install xtts-cli pydub twilio
deactivate
echo "Python packages installed."
# 4. Create Tool-Specific Database Table
echo "[TASK 4/8] Creating database table 'prank_call_history'..."
SQL_CREATE_TABLE="
CREATE TABLE IF NOT EXISTS \`prank_call_history\` (
\`id\` BIGINT PRIMARY KEY AUTO_INCREMENT,
\`user_id\` BIGINT NOT NULL,
\`victim_phone\` VARCHAR(20) NOT NULL,
\`caller_id\` VARCHAR(20),
\`script\` TEXT,
\`voice\` VARCHAR(50),
\`audio_url\` VARCHAR(255),
\`call_sid\` VARCHAR(255) UNIQUE,
\`status\` VARCHAR(20) DEFAULT 'pending',
\`schedule_time\` DATETIME NULL,
\`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
\`updated_at\` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX \`user_id_idx\` (\`user_id\`),
INDEX \`status_idx\` (\`status\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
"
mysql -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -e "${SQL_CREATE_TABLE}"
echo "Database table ensured."
# 5. Create Directories and Set Permissions
echo "[TASK 5/8] Creating directories and setting permissions..."
mkdir -p "${TOOL_ROOT}/uploads"
chown -R www-data:www-data "/home/${TOOL_DOMAIN}"
chmod -R 755 "/home/${TOOL_DOMAIN}"
chmod 775 "${TOOL_ROOT}/uploads" # Allow web server to write uploads
echo "Directory structure created."
# 6. Configure OpenLiteSpeed Virtual Host
echo "[TASK 6/8] Configuring OpenLiteSpeed virtual host..."
OLS_VH_CONFIG="
virtualHost ${TOOL_SLUG} {
vhRoot ${TOOL_ROOT}
configFile \$SERVER_ROOT/conf/vhosts/${TOOL_SLUG}/vhost.conf
allowSymbolLink 1
enableScript 1
restrained 1
setUIDMode 0
}
"
# This would typically be appended to the main LiteSpeed config,
# and a specific vhost.conf would be created.
# For simplicity, we echo the config block.
echo "OpenLiteSpeed config block for ${TOOL_DOMAIN}:"
echo "------------------------------------------------"
echo "${OLS_VH_CONFIG}"
echo "------------------------------------------------"
echo "NOTE: Manual addition to OpenLiteSpeed configuration may be required."
# 7. Setup Log Rotation
echo "[TASK 7/8] Setting up log rotation..."
LOGROTATE_CONFIG="/etc/logrotate.d/${TOOL_SLUG}"
cat > "${LOGROTATE_CONFIG}" << EOL
${TOOL_ROOT}/logs/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 www-data adm
sharedscripts
}
EOL
echo "Log rotation configured at ${LOGROTATE_CONFIG}"
# 8. Final Health Check
echo "[TASK 8/8] Performing final health check..."
# A simple curl to the diagnostic script to confirm setup
HEALTH_CHECK_URL="http://${TOOL_DOMAIN}/diagnostic.php?key=digitalprank2025"
echo "To complete health check, ensure DNS is pointing to this server and run:"
echo "curl -I ${HEALTH_CHECK_URL}"
echo "--- Deployment for ${TOOL_SLUG} complete! ---"
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 1: /home/ai-prank-caller.digitalprank.com/public_html/tool_config.json
code
JSON
{
"tool": {
"identity": {
"slug": "ai-prank-caller",
"name": "AI Prank Call Bot",
"category": "prank",
"tagline": "Unleash hilarious, AI-powered prank calls on your friends!",
"description": "Send funny, automated prank calls using AI voices. Type a prank script, pick a voice, and watch the chaos unfold.",
"keywords": ["prank call", "ai voice", "funny calls", "voice changer", "digital prank"]
},
"features": {
"bulk_enabled": false,
"history_enabled": true,
"export_enabled": true,
"api_enabled": true
},
"fields": [
{
"id": "script_prompt",
"type": "text",
"label": "Enter your prank script or idea",
"placeholder": "e.g., A pizza guy who got lost in your closet.",
"required": true,
"validation": {
"pattern": "^.{10,500}$",
"min_length": 10,
"max_length": 500
},
"pro_only": false,
"help_text": "Describe a scenario like 'An angry neighbor complaining about a barking dog that doesn't exist' or type the full message."
},
{
"id": "voice_style",
"type": "select",
"label": "Voice Style",
"default": "annoying_robot",
"options": [
{ "value": "annoying_robot", "label": "Annoying Robot" },
{ "value": "squeaky_chipmunk", "label": "Squeaky Chipmunk" },
{ "value": "scary_deep", "label": "Scary Deep Voice" },
{ "value": "sassy_female", "label": "Sassy Female" },
{ "value": "movie_narrator", "label": "Movie Narrator" },
{ "value": "angry_neighbor", "label": "Angry Neighbor" },
{ "value": "confused_old_lady", "label": "Confused Old Lady" },
{ "value": "slick_salesman", "label": "Slick Salesman" },
{ "value": "nervous_guy", "label": "Nervous Guy" },
{ "value": "custom", "label": "Custom AI-Generated Voice (Pro)" }
],
"pro_only": false,
"help_text": "Choose a hilarious voice for your prank."
},
{
"id": "victim_phone",
"type": "tel",
"label": "Victim's Phone Number",
"placeholder": "+15551234567",
"required": true,
"pro_only": true,
"help_text": "Enter the number of your unsuspecting friend. International format required. Pro users only."
},
{
"id": "caller_id",
"type": "tel",
"label": "Caller ID to Display (Optional)",
"placeholder": "e.g., +15558675309",
"required": false,
"pro_only": true,
"help_text": "What number should appear on their phone? Gold tier and above. Use responsibly."
},
{
"id": "schedule_time",
"type": "datetime",
"label": "Schedule Call Time",
"required": false,
"pro_only": true,
"help_text": "Optional. Leave blank to send immediately."
},
{
"id": "use_ai_voice",
"type": "checkbox",
"label": "Use AI-Generated Voice",
"default": true,
"pro_only": false,
"help_text": "Let our AI read your script. Uncheck to upload your own audio file (Pro feature)."
}
],
"limits": {
"tier_daily": {
"free": 2,
"basic": 15,
"gold": 150,
"ultimate": -1
},
"rate_limit_per_minute": 10,
"max_concurrent_requests": 3
},
"billing": {
"credit_cost": 1,
"one_off_enabled": true,
"one_off_price_cents": 99,
"bill_on": "success"
},
"ui": {
"theme": {
"primary_color": "#FF4500",
"secondary_color": "#1a1a1a"
},
"layout": {
"show_sidebar_ads": true,
"form_style": "wizard",
"result_display": "modal"
}
},
"dependencies": {
"php_extensions": ["curl", "json", "mbstring"],
"system_packages": ["ffmpeg", "sox"],
"python_packages": ["xtts", "pydub", "twilio"],
"external_apis": ["twilio", "xtts", "ollama", "openai", "claude"],
"requires_internet": true
},
"database": {
"tool_specific_table": "prank_call_history",
"store_results": true,
"enable_history": true,
"retention_days": 30
},
"seo": {
"meta_title": "AI Prank Call Generator | Send Funny Custom Calls | DigitalPrank.com",
"meta_description": "Create and send hilarious AI-powered prank calls with funny voices. Type a script, choose a voice like 'Angry Neighbor' or 'Robot', and call your friends. Try it for free!",
"canonical_url": "https://digitalprank.com/tools/ai-prank-caller",
"structured_data": {
"type": "WebApplication",
"category": "Entertainment"
}
},
"help": {
"quick_start": [
"Step 1: Type your prank idea or script.",
"Step 2: Pick a hilarious voice style.",
"Step 3: Enter your victim's phone number (Pro).",
"Step 4: Click 'Send Prank Call'!",
"Step 5: Enjoy the chaos."
],
"faq": [
{
"question": "Is this legal?",
"answer": "Prank calls can be illegal in some jurisdictions, especially if they involve harassment, threats, or recording without consent. This tool is for entertainment purposes only. Use it responsibly and only with friends who have a good sense of humor. You are responsible for complying with all local and federal laws."
},
{
"question": "Can I use my own voice?",
"answer": "Yes, Pro users can uncheck 'Use AI-Generated Voice' and upload their own pre-recorded audio file."
},
{
"question": "What if the call doesn't go through?",
"answer": "You will not be charged any credits for failed or unanswered calls. You can try again without penalty."
}
],
"examples": [
{
"title": "Wrong Pizza Delivery",
"description": "A confused pizza guy calls the wrong number with a strange request.",
"input": {
"script_prompt": "Hello? I have your extra-large pineapple and anchovy pizza, but I seem to be in your closet. Should I just leave it on the shoe rack?",
"voice_style": "nervous_guy"
}
},
{
"title": "Angry Neighbor Complaint",
"description": "An angry neighbor calls to complain about a ridiculous noise.",
"input": {
"script_prompt": "Yeah, hi, this is your neighbor from 3B. I need you to stop teaching your parrot to sing opera. It's scaring my cat, and frankly, you're both off-key.",
"voice_style": "angry_neighbor"
}
}
]
},
"monitoring": {
"track_usage": true,
"track_performance": true,
"error_reporting": true,
"analytics_events": [
"prank_call_sent",
"script_generated",
"pro_upgrade"
]
},
"security": {
"input_sanitization": true,
"csrf_protection": true,
"rate_limiting": true,
"blocked_domains": ["gov", "mil", "911.gov"],
"max_input_length": 500,
"allowed_file_types": ["mp3", "wav", "txt"]
},
"version": {
"schema": "3.0.0",
"tool": "1.1.0",
"api_version": "v1"
}
}
}
FILE 2: /home/ai-prank-caller.digitalprank.com/public_html/processor.php
code
PHP
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Database connection failed.']);
exit;
}
// Load tool config
$tool_slug = 'ai-prank-caller';
$config_path = __DIR__ . '/tool_config.json';
if (!file_exists($config_path)) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Configuration file not found.']);
exit;
}
$config = json_decode(file_get_contents($config_path), true)['tool'];
// Mock User Session
session_start();
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : 0; // 0 for guest users
$user_ip = $_SERVER['REMOTE_ADDR'];
// Basic CSRF check
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => 'Invalid session token. Please refresh the page.']);
exit;
}
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // Regenerate token
// Get user access level
$access = getUserAccessLevel($pdo, $user_id, $tool_slug);
// Get features and apply overrides
$features = $config['features'];
// In a real app, you would apply specific feature overrides here.
$response = [
'success' => false,
'data' => null,
'usage' => $access['usage'],
'access' => $access,
'features' => $features
];
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$response['message'] = 'Invalid request method.';
echo json_encode($response);
exit;
}
$input = $_POST;
$start_time = microtime(true);
// Daily usage check
$limit = $config['limits']['tier_daily'][$access['tier']];
if (!checkDailyUsage($pdo, $tool_slug, $user_ip, $user_id, $limit)) {
http_response_code(429);
$response['message'] = 'You have exceeded your daily usage limit for this tool.';
echo json_encode($response);
exit;
}
// {{TOOL_PROCESSING_START}}
try {
// 1. Get and Validate Inputs
$errors = [];
$validated_input = [];
$fields = $config['fields'];
$overrides = getToolOverrides($pdo, $tool_slug);
foreach ($fields as $field) {
$id = $field['id'];
$value = isset($input[$id]) ? trim($input[$id]) : null;
// Check if field is disabled via override
if (isset($overrides[$id]) && $overrides[$id]['override_type'] === 'disabled') {
continue; // Skip disabled fields
}
// Pro field access check
$is_pro_field = $field['pro_only'];
$required_tier_level = 0; // 0 = free
if (isset($overrides[$id]) && $overrides[$id]['override_type'] === 'tier') {
$required_tier_level = getTierLevel($overrides[$id]['tier_required']);
}
if (($is_pro_field || $required_tier_level > 0) && $access['level'] < $required_tier_level && !empty($value)) {
$errors[] = "Field '{$field['label']}' requires a higher subscription tier.";
continue;
}
// Required check
if ($field['required'] && ($value === null || $value === '')) {
$errors[] = "Field '{$field['label']}' is required.";
continue;
}
// Validation Rules
if (!empty($value) && isset($field['validation'])) {
if (isset($field['validation']['pattern']) && !preg_match('/' . $field['validation']['pattern'] . '/s', $value)) {
$errors[] = "Invalid format for '{$field['label']}'.";
}
if (isset($field['validation']['min_length']) && mb_strlen($value) < $field['validation']['min_length']) {
$errors[] = "'{$field['label']}' must be at least {$field['validation']['min_length']} characters long.";
}
if (isset($field['validation']['max_length']) && mb_strlen($value) > $field['validation']['max_length']) {
$errors[] = "'{$field['label']}' must not exceed {$field['validation']['max_length']} characters.";
}
}
// Specific validation for phone numbers
if (($id === 'victim_phone' || $id === 'caller_id') && !empty($value)) {
if (!preg_match('/^\+[1-9]\d{1,14}$/', $value)) {
$errors[] = "Invalid phone number format for '{$field['label']}'. Must be in E.164 format (e.g., +15551234567).";
}
foreach ($config['security']['blocked_domains'] as $domain) {
if(str_contains($value, $domain)) {
$errors[] = "Calling emergency or government services is strictly prohibited.";
}
}
}
$validated_input[$id] = $value;
}
$use_ai_voice = !(isset($validated_input['use_ai_voice']) && $validated_input['use_ai_voice'] === '0');
$custom_audio_path = null;
if (!$use_ai_voice) {
if (!$access['has_pro_access']) {
$errors[] = "Uploading custom audio is a Pro feature.";
} else {
// Handle file upload
if (isset($_FILES['custom_audio_file']) && $_FILES['custom_audio_file']['error'] === UPLOAD_ERR_OK) {
$file = $_FILES['custom_audio_file'];
$allowed_types = ['audio/mpeg', 'audio/wav', 'audio/x-wav'];
if (in_array($file['type'], $allowed_types) && $file['size'] < 5000000) { // 5MB limit
$upload_dir = __DIR__ . '/uploads/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0755, true);
$filename = uniqid('audio_', true) . '.' . pathinfo($file['name'], PATHINFO_EXTENSION);
$custom_audio_path = $upload_dir . $filename;
if (!move_uploaded_file($file['tmp_name'], $custom_audio_path)) {
$errors[] = 'Failed to process uploaded audio file.';
$custom_audio_path = null;
}
} else {
$errors[] = 'Invalid file type or size for custom audio. Please use MP3 or WAV under 5MB.';
}
} else {
$errors[] = "A custom audio file is required when 'Use AI-Generated Voice' is unchecked.";
}
}
}
if (!empty($errors)) {
throw new Exception(implode(' ', $errors));
}
// 2. Implement Tool Functionality
$output_data = [];
$audio_url_for_call = null;
if ($use_ai_voice) {
// Simulate generating audio from text using XTTS
$venv_python = "/var/www/venv/ai-prank-caller/bin/python";
$script_path = __DIR__ . "/generate_audio.py"; // A helper script you would create
$script = escapeshellarg($validated_input['script_prompt']);
$voice = escapeshellarg($validated_input['voice_style']);
$output_filename = uniqid('prank_', true) . '.wav';
$output_path = "/tmp/" . $output_filename;
// Mock command: This would call a real text-to-speech script
// $command = "{$venv_python} {$script_path} --text {$script} --voice {$voice} --out {$output_path}";
// shell_exec($command);
// For this simulation, we'll create a placeholder and URL
file_put_contents($output_path, "This is a custom audio file for testing.");
$audio_url_for_call = "https://cdn.digitalprank.com/audio/pranks/{$output_filename}"; // In reality, you'd upload $output_path to the CDN
$output_data['audio_generation_status'] = 'success';
$output_data['generated_audio_url'] = $audio_url_for_call;
} else {
// We have a custom audio file, make it accessible via a URL
$public_filename = basename($custom_audio_path);
$audio_url_for_call = "https://ai-prank-caller.digitalprank.com/uploads/{$public_filename}";
$output_data['custom_audio_status'] = 'processed';
$output_data['generated_audio_url'] = $audio_url_for_call;
}
// 3. Simulate placing the call with Twilio
// In a real app, use the Twilio PHP SDK: require 'vendor/autoload.php';
// use Twilio\Rest\Client;
$caller_id_to_use = getenv('DEFAULT_TWILIO_NUMBER'); // A verified number in your Twilio account
if ($access['level'] >= getTierLevel('gold') && !empty($validated_input['caller_id'])) {
$caller_id_to_use = $validated_input['caller_id'];
}
// $twilio = new Client(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN'));
// $call = $twilio->calls->create( $validated_input['victim_phone'], $caller_id_to_use, ["url" => $audio_url_for_call] );
// $call_sid = $call->sid;
// $status = $call->status;
// Mocked response for demonstration
$call_sid = 'CA' . bin2hex(random_bytes(16));
$status = 'queued';
$output_data['call_sid'] = $call_sid;
$output_data['call_status'] = $status;
$output_data['message'] = "Prank call successfully initiated to {$validated_input['victim_phone']}.";
// 4. Store Results in Database
if ($config['database']['store_results'] && $config['database']['enable_history']) {
$stmt = $pdo->prepare(
"INSERT INTO prank_call_history (user_id, victim_phone, caller_id, script, voice, audio_url, call_sid, status, schedule_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
$stmt->execute([
$user_id,
$validated_input['victim_phone'],
$caller_id_to_use,
$validated_input['script_prompt'],
$validated_input['voice_style'],
$audio_url_for_call,
$call_sid,
$status,
empty($validated_input['schedule_time']) ? null : $validated_input['schedule_time']
]);
}
$response['success'] = true;
$response['data'] = $output_data;
$processing_time = microtime(true) - $start_time;
logUsage($pdo, $tool_slug, $user_ip, $user_id, 'success', $input, $output_data, $processing_time, $config['billing']['credit_cost']);
} catch (Exception $e) {
$response['success'] = false;
$response['message'] = $e->getMessage();
http_response_code(400);
$processing_time = microtime(true) - $start_time;
logUsage($pdo, $tool_slug, $user_ip, $user_id, 'failed', $input, ['error' => $e->getMessage()], $processing_time, 0);
}
// {{TOOL_PROCESSING_END}}
// Final response
echo json_encode($response);
// Helper function for this tool
function getToolOverrides($pdo, $tool_slug) {
$stmt = $pdo->prepare("SELECT tool_id FROM wp_digitalprank_tools WHERE slug = ?");
$stmt->execute([$tool_slug]);
$tool_id = $stmt->fetchColumn();
if (!$tool_id) return [];
$stmt = $pdo->prepare("SELECT field_id, tier_required, override_type FROM wp_digitalprank_tool_overrides WHERE tool_id = ? AND is_active = 1");
$stmt->execute([$tool_id]);
$overrides = $stmt->fetchAll(PDO::FETCH_ASSOC);
$result = [];
foreach ($overrides as $override) {
$result[$override['field_id']] = $override;
}
return $result;
}
function getTierLevel($tier_name) {
$levels = ['free' => 0, 'basic' => 1, 'gold' => 2, 'ultimate' => 3];
return $levels[strtolower($tier_name)] ?? 0;
}
FILE 3: /home/ai-prank-caller.digitalprank.com/public_html/tool_form.php
code
PHP
false, 'tier' => 'free', 'level' => 0];
?>
');
}
function check_status($condition, $success_msg, $failure_msg, &$status_list) {
if ($condition) {
$status_list[] = "
OK: $success_msg
";
} else {
$status_list[] = "
FAIL: $failure_msg
";
}
}
function check_shell_command($command, $package_name, &$status_list) {
$path = trim(shell_exec("command -v $command"));
check_status(!empty($path), "$package_name is installed (path: $path).", "$package_name could not be found in the system's PATH.", $status_list);
}
$results = [];
// 1. Configuration File Validation
$config_path = __DIR__ . '/tool_config.json';
check_status(file_exists($config_path) && is_readable($config_path), "tool_config.json found and is readable.", "tool_config.json is missing or not readable.", $results['Configuration']);
$config = null;
if (file_exists($config_path)) {
$config_content = file_get_contents($config_path);
json_decode($config_content);
check_status(json_last_error() === JSON_ERROR_NONE, "tool_config.json is valid JSON.", "tool_config.json contains invalid JSON: " . json_last_error_msg(), $results['Configuration']);
$config = json_decode($config_content, true)['tool'];
}
// 2. Database
$db_host = 'localhost';
$db_name = 'digitalprank_db';
$db_user = 'dp_user';
$db_pass = '#$Dealer2355';
$pdo = null;
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
check_status(true, "Database connection to '$db_name' successful.", "", $results['Database']);
} catch (PDOException $e) {
check_status(false, "", "Database connection failed: " . htmlspecialchars($e->getMessage()), $results['Database']);
}
// 3. Table Existence
if ($pdo) {
$required_tables = [
'wp_digitalprank_tools', 'wp_digitalprank_usage', 'wp_digitalprank_usage_log',
'wp_pms_member_subscriptions', 'wp_digitalprank_entitlements', 'wp_digitalprank_tool_overrides'
];
if ($config && isset($config['database']['tool_specific_table'])) {
$required_tables[] = $config['database']['tool_specific_table'];
}
foreach ($required_tables as $table) {
try {
$pdo->query("SELECT 1 FROM `$table` LIMIT 1");
check_status(true, "Table `{$table}` exists.", "", $results['Database Tables']);
} catch (Exception $e) {
check_status(false, "", "Table `{$table}` does not exist or is inaccessible.", $results['Database Tables']);
}
}
}
// 4. Dependencies
if ($config) {
foreach ($config['dependencies']['php_extensions'] as $ext) {
check_status(extension_loaded($ext), "PHP extension '{$ext}' is loaded.", "PHP extension '{$ext}' is NOT loaded.", $results['PHP Dependencies']);
}
foreach ($config['dependencies']['system_packages'] as $pkg) {
check_shell_command($pkg, $pkg, $results['System Dependencies']);
}
$venv_path = '/var/www/venv/ai-prank-caller/bin/python';
check_status(file_exists($venv_path) && is_executable($venv_path), "Python venv executable found at {$venv_path}", "Python venv NOT found or not executable at {$venv_path}", $results['Python Dependencies']);
if(file_exists($venv_path)) {
foreach($config['dependencies']['python_packages'] as $pkg) {
$pkg_name = explode('==', $pkg)[0];
$check_script = escapeshellarg("import " . $pkg_name);
$command = "$venv_path -c $check_script";
shell_exec("$command 2>&1", $output, $return_code);
check_status($return_code === 0, "Python package '{$pkg}' appears to be installed.", "Python package '{$pkg}' is NOT installed or importable.", $results['Python Dependencies']);
}
}
}
// 5. File System Permissions
$writable_dirs = [__DIR__ . '/uploads'];
foreach ($writable_dirs as $dir) {
$exists = file_exists($dir);
$is_writable = $exists && is_writable($dir);
check_status($is_writable, "Directory '{$dir}' is writable.", "Directory '{$dir}' is not writable or does not exist.", $results['File System']);
}
?>
Diagnostic Report: AI Prank Call Bot
Diagnostic Report: AI Prank Call Bot
$checks): ?>
FILE 5: /home/ai-prank-caller.digitalprank.com/public_html/help.md
code
Markdown
# AI Prank Call Bot Help Guide
Unleash hilarious, AI-powered prank calls on your friends! This guide will walk you through setting up and sending your first prank call.
## Quick Start Guide
Getting started is easy. Just follow these steps:
1. **Step 1:** Type your prank idea or script into the "Enter your prank script or idea" field.
2. **Step 2:** Pick a hilarious voice style from the "Voice Style" dropdown menu.
3. **Step 3:** Enter your victim's phone number. This is a **Pro feature** and requires an active subscription.
4. **Step 4:** Click 'Send Prank Call'!
5. **Step 5:** Enjoy the chaos and wait for your friend's reaction.
## Features
* **AI-Generated Voices**: Our advanced AI can read any script you provide in a variety of funny voices.
* **Custom Scripts**: You have complete control. Write your own script from scratch for a personalized prank.
* **Voice Style Library**: Choose from voices like "Annoying Robot", "Angry Neighbor", and "Confused Old Lady" to perfectly match your prank scenario.
* **Custom Audio Upload (Pro)**: Pro users can uncheck 'Use AI-Generated Voice' and upload their own MP3 or WAV files instead of using our AI voices.
* **Caller ID Spoofing (Gold Tier & Above)**: Make the call appear to come from any number you choose. Please use this feature responsibly.
* **Scheduled Calls (Pro)**: Time your prank perfectly by scheduling it to be sent at a future date and time.
## Frequently Asked Questions (FAQ)
### Is this legal?
Prank calls can be illegal in some jurisdictions, especially if they involve harassment, threats, or recording without consent. This tool is for entertainment purposes only. Use it responsibly and only with friends who have a good sense of humor. You are responsible for complying with all local and federal laws.
### Can I use my own voice?
Yes, Pro users can uncheck 'Use AI-Generated Voice' and upload their own pre-recorded audio file.
### What if the call doesn't go through?
You will not be charged any credits for failed or unanswered calls. You can try again without penalty.
## Usage Examples
Here are a couple of ideas to get you started:
### Example 1: Wrong Pizza Delivery
* **Description**: A confused pizza guy calls the wrong number with a strange request.
* **Input**:
* **Script Prompt**: `Hello? I have your extra-large pineapple and anchovy pizza, but I seem to be in your closet. Should I just leave it on the shoe rack?`
* **Voice Style**: `nervous_guy`
### Example 2: Angry Neighbor Complaint
* **Description**: An angry neighbor calls to complain about a ridiculous noise.
* **Input**:
* **Script Prompt**: `Yeah, hi, this is your neighbor from 3B. I need you to stop teaching your parrot to sing opera. It's scaring my cat, and frankly, you're both off-key.`
* **Voice Style**: `angry_neighbor`
FILE 6: /home/digitalprank.com/public_html/blog/data/tools/ai-prank-caller.json
code
JSON
{
"slug": "ai-prank-caller",
"name": "AI Prank Call Bot",
"meta_title": "AI Prank Call Generator | Send Funny Custom Calls | DigitalPrank.com",
"meta_description": "Create and send hilarious AI-powered prank calls with funny voices. Type a script, choose a voice like 'Angry Neighbor' or 'Robot', and call your friends. Try it for free!",
"canonical_url": "https://digitalprank.com/tools/ai-prank-caller",
"long_description": "Unleash the ultimate prank with the AI Prank Call Bot from DigitalPrank.com. Our advanced tool uses cutting-edge text-to-speech technology, allowing you to type any script and have it performed by a unique AI voice personality. Whether you want to sound like a squeaky chipmunk, a confused old lady, or a booming movie narrator, the possibilities are endless. It's the perfect tool for harmless fun and getting a laugh out of your friends. Pro users can unlock even more powerful features, including scheduling calls for the perfect moment, setting a custom caller ID, or even uploading their own audio for a truly personalized prank experience.",
"features_list": [
{
"name": "Advanced Text-to-Speech Engine",
"description": "Our powerful AI converts your text script into natural-sounding and hilarious audio in seconds.",
"is_pro": false
},
{
"name": "Diverse Voice Library",
"description": "Choose from a wide library of distinct voices including robots, angry neighbors, slick salesmen, and more to fit any scenario.",
"is_pro": false
},
{
"name": "Direct-to-Phone Calling",
"description": "Send your hilarious, AI-generated prank call directly to your friend's phone number.",
"is_pro": true
},
{
"name": "Custom Caller ID (Gold+)",
"description": "Set the phone number that appears on the recipient's caller ID for added believability or humor.",
"is_pro": true,
"tier": "gold"
},
{
"name": "Scheduled Prank Calls",
"description": "Plan your prank in advance by scheduling the call to be sent at the exact date and time you choose.",
"is_pro": true
},
{
"name": "Upload Custom Audio",
"description": "For the ultimate custom prank, upload your own pre-recorded MP3 or WAV files to be used in the call.",
"is_pro": true
}
],
"user_guide": {
"title": "How to Send Your First AI Prank Call in Seconds",
"steps": [
"Craft your perfect prank script in the text box. Let your imagination run wild!",
"Select a voice from the dropdown menu that best fits the prank's character.",
"If you're a Pro user, enter the recipient's full phone number in international format (e.g., +15551234567).",
"Optionally, Pro users can set a custom Caller ID or schedule the call for later.",
"Hit the 'Send Prank Call' button and let our system work its magic."
],
"best_practices": "For maximum impact, keep scripts short, punchy, and to the point. Use voices that contrast with the message for extra humor (e.g., a 'Scary Deep Voice' ordering a tiny cupcake). Always ensure your prank is harmless and that the recipient will find it funny. This tool is for entertainment, not harassment."
},
"structured_data": {
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "AI Prank Call Bot",
"description": "Send hilarious, AI-powered prank calls with custom scripts and funny voices.",
"url": "https://digitalprank.com/tools/ai-prank-caller",
"applicationCategory": "EntertainmentApplication",
"operatingSystem": "Any (Web-based)",
"offers": {
"@type": "Offer",
"price": "0.00",
"priceCurrency": "USD",
"description": "Free tier with 2 daily calls."
},
"softwareHelp": {
"@type": "CreativeWork",
"name": "AI Prank Call Bot Help & FAQ",
"url": "https://ai-prank-caller.digitalprank.com/help.md"
}
}
}```
### FILE 7: /home/ai-prank-caller.digitalprank.com/public_html/deploy.sh
```bash
#!/bin/bash
# Deployment script for the AI Prank Call Bot tool
# This script must be run with root privileges.
set -e
# --- Configuration ---
TOOL_SLUG="ai-prank-caller"
TOOL_DOMAIN="${TOOL_SLUG}.digitalprank.com"
TOOL_ROOT="/home/${TOOL_DOMAIN}/public_html"
VENV_PATH="/var/www/venv/${TOOL_SLUG}"
DB_NAME='digitalprank_db'
DB_USER='dp_user'
DB_PASS='#$Dealer2355'
WEB_USER='www-data'
WEB_GROUP='www-data'
echo "--- Starting deployment for ${TOOL_SLUG} ---"
# 1. Install System Packages
echo "[TASK 1/8] Installing system packages (ffmpeg, sox)..."
apt-get update -y
apt-get install -y ffmpeg sox python3-venv python3-pip
# 2. Install PHP Extensions
echo "[TASK 2/8] Ensuring PHP extensions are installed (curl, json, mbstring)..."
# This step is highly dependent on the PHP setup. For many modern setups (e.g., Ondrej's PPA),
# these extensions are often enabled by default or easily installed.
# Example: apt-get install -y php8.1-curl php8.1-mbstring
echo "Skipping automatic PHP extension installation. Please ensure curl, json, and mbstring are enabled."
# 3. Create Python Virtual Environment and Install Packages
echo "[TASK 3/8] Setting up Python virtual environment at ${VENV_PATH}..."
if [ ! -d "${VENV_PATH}" ]; then
mkdir -p /var/www/venv
chown ${WEB_USER}:${WEB_GROUP} /var/www/venv
python3 -m venv "${VENV_PATH}"
echo "Created Python venv."
fi
echo "Activating venv and installing Python packages (xtts, pydub, twilio)..."
source "${VENV_PATH}/bin/activate"
pip install --upgrade pip
pip install 'xtts-cli' pydub twilio
deactivate
chown -R ${WEB_USER}:${WEB_GROUP} "${VENV_PATH}"
echo "Python packages installed."
# 4. Create Tool-Specific Database Table
echo "[TASK 4/8] Creating database table 'prank_call_history' if it doesn't exist..."
SQL_CREATE_TABLE="
CREATE TABLE IF NOT EXISTS \`prank_call_history\` (
\`id\` BIGINT PRIMARY KEY AUTO_INCREMENT,
\`user_id\` BIGINT NOT NULL,
\`victim_phone\` VARCHAR(20) NOT NULL,
\`caller_id\` VARCHAR(20),
\`script\` TEXT,
\`voice\` VARCHAR(50),
\`audio_url\` VARCHAR(255),
\`call_sid\` VARCHAR(40) UNIQUE,
\`status\` VARCHAR(20) DEFAULT 'pending',
\`schedule_time\` DATETIME NULL,
\`created_at\` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
\`updated_at\` TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
INDEX \`user_id_idx\` (\`user_id\`),
INDEX \`status_idx\` (\`status\`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
"
mysql -hlocalhost -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -e "${SQL_CREATE_TABLE}"
echo "Database table schema ensured."
# 5. Create Directories and Set Permissions
echo "[TASK 5/8] Creating directories and setting permissions..."
mkdir -p "${TOOL_ROOT}/uploads"
# Assuming the tool's code has already been placed in TOOL_ROOT
chown -R ${WEB_USER}:${WEB_GROUP} "/home/${TOOL_DOMAIN}"
find "/home/${TOOL_DOMAIN}" -type d -exec chmod 755 {} \;
find "/home/${TOOL_DOMAIN}" -type f -exec chmod 644 {} \;
chmod 775 "${TOOL_ROOT}/uploads"
echo "Directory structure and permissions set."
# 6. Configure OpenLiteSpeed Virtual Host
echo "[TASK 6/8] Generating OpenLiteSpeed virtual host config..."
# This is a sample. Manual integration into /usr/local/lsws/conf/httpd_config.conf is required.
OLS_VH_CONFIG_PATH="/usr/local/lsws/conf/vhosts/${TOOL_SLUG}"
mkdir -p "${OLS_VH_CONFIG_PATH}"
cat > "${OLS_VH_CONFIG_PATH}/vhost.conf" << EOL
docRoot \$VH_ROOT
vhDomain ${TOOL_DOMAIN}
vhAliases www.${TOOL_DOMAIN}
adminEmails webmaster@${TOOL_DOMAIN}
enableGzip 1
errorlog \$SERVER_ROOT/logs/\$VH_NAME.error.log {
logLevel ERROR
rollingSize 10M
}
accesslog \$SERVER_ROOT/logs/\$VH_NAME.access.log {
rollingSize 10M
keepDays 10
compressArchive 1
}
index {
useServer 0
indexFiles index.php, index.html
}
scripthandler {
add lsapi:php81 php
}
extprocessor php81 {
type lsapi
address uds://tmp/lshttpd/php81.sock
maxConns 35
env LSAPI_CHILDREN=35
initTimeout 60
retryTimeout 0
persistConn 1
respBuffer 0
autoStart 1
path /usr/local/lsws/lsphp81/bin/lsphp
backlog 100
instances 1
}
rewrite {
enable 1
autoLoadHtaccess 1
}
EOL
echo "Virtual host config created at ${OLS_VH_CONFIG_PATH}/vhost.conf"
echo "ACTION REQUIRED: Add 'map ${TOOL_SLUG} ${TOOL_DOMAIN}' to your virtual host mappings in OpenLiteSpeed."
# 7. Setup Log Rotation
echo "[TASK 7/8] Setting up log rotation..."
LOGROTATE_CONFIG="/etc/logrotate.d/${TOOL_SLUG}"
cat > "${LOGROTATE_CONFIG}" << EOL
/usr/local/lsws/logs/${TOOL_SLUG}.*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
/usr/local/lsws/bin/lswsctrl restart
endscript
}
EOL
echo "Log rotation configured at ${LOGROTATE_CONFIG}"
# 8. Final Health Check Reminder
echo "[TASK 8/8] Deployment tasks complete."
echo "Please restart OpenLiteSpeed for changes to take effect: 'sudo /usr/local/lsws/bin/lswsctrl restart'"
echo "To validate the installation, ensure DNS for '${TOOL_DOMAIN}' is pointing to this server, then access:"
echo "http://${TOOL_DOMAIN}/diagnostic.php?key=digitalprank2025"
echo "--- Deployment for ${TOOL_SLUG} finished! ---"
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 8: /home/ai-prank-caller.digitalprank.com/public_html/generate_audio.py
code
Python
#!/var/www/venv/ai-prank-caller/bin/python
# -*- coding: utf-8 -*-
"""
AI Prank Caller - Audio Generation Script
Accepts text and a voice style, and generates a WAV audio file using XTTS.
"""
import argparse
import os
import sys
import torch
from TTS.api import TTS
def generate_audio(text, voice_style, output_path):
"""
Generates an audio file from text using a specified voice.
Args:
text (str): The text to convert to speech.
voice_style (str): The key for the voice model to use. Not directly used by XTTSv2
but could be used for speaker selection in the future.
For now, we use a default speaker WAV.
output_path (str): The path to save the generated WAV file.
"""
try:
# Check for CUDA availability
device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"INFO: Using device: {device}")
# Initialize XTTSv2 model
# This will download the model on the first run to a cache directory
print("INFO: Initializing TTS model (XTTSv2)...")
tts = TTS("tts_models/multilingual/multi-dataset/xtts_v2", gpu=torch.cuda.is_available())
# Define a path to a reference speaker audio.
# In a real application, you would have a library of speaker WAVs
# mapped from the 'voice_style' input.
# For now, we'll use a placeholder path.
speaker_wav_path = "/var/www/assets/voices/default_speaker.wav"
if not os.path.exists(speaker_wav_path):
print(f"ERROR: Speaker WAV file not found at {speaker_wav_path}", file=sys.stderr)
# As a fallback, we will let the model clone its own default voice if the file is missing.
print("WARNING: Proceeding with default internal speaker.", file=sys.stderr)
speaker_wav_path = None
print(f"INFO: Generating audio for text: '{text[:50]}...'")
# Synthesize speech
tts.tts_to_file(
text=text,
speaker_wav=speaker_wav_path, # Path to a reference audio file to clone the voice from
language="en", # Set language
file_path=output_path
)
print(f"SUCCESS: Audio file saved to {output_path}")
except Exception as e:
print(f"ERROR: An error occurred during audio generation: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate audio from text using XTTS.")
parser.add_argument("--text", type=str, required=True, help="The text to synthesize.")
parser.add_argument("--voice", type=str, required=True, help="The voice style to use (currently for reference).")
parser.add_argument("--out", type=str, required=True, help="The output path for the WAV file.")
args = parser.parse_args()
# Basic input validation
if len(args.text.strip()) < 5:
print("ERROR: Text must be at least 5 characters long.", file=sys.stderr)
sys.exit(1)
if not args.out.endswith(".wav"):
print("ERROR: Output path must end with .wav", file=sys.stderr)
sys.exit(1)
# Ensure the output directory exists
output_dir = os.path.dirname(args.out)
if not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
generate_audio(args.text, args.voice, args.out)
FILE 9: /home/ai-prank-caller.digitalprank.com/public_html/twilio_handler.php
code
PHP
';
echo '';
if ($audio_url) {
// If a valid audio URL is provided, instruct Twilio to play it.
// The verb will play an audio file over the call.
echo '' . htmlspecialchars($audio_url) . '';
// After playing the file, we can add a brief pause.
echo '';
} else {
// If no valid audio URL is found, play an error message.
// The verb uses Twilio's text-to-speech.
echo 'An application error has occurred. Goodbye.';
}
// The verb explicitly ends the call.
// If omitted, the call would end automatically after the verb completes.
echo '';
echo '';
// No further output should be sent.
exit;
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 10: /home/ai-prank-caller.digitalprank.com/public_html/history.php
code
PHP
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Could not connect to the database.");
}
// Pagination logic
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 15;
$offset = ($page - 1) * $limit;
// Fetch total count for pagination
$count_stmt = $pdo->prepare("SELECT COUNT(*) FROM prank_call_history WHERE user_id = ?");
$count_stmt->execute([$user_id]);
$total_records = $count_stmt->fetchColumn();
$total_pages = ceil($total_records / $limit);
// Fetch records for the current page
$stmt = $pdo->prepare(
"SELECT victim_phone, status, script, audio_url, created_at
FROM prank_call_history
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?"
);
$stmt->execute([$user_id, $limit, $offset]);
$history_records = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
My Prank Call History
My Prank Call History
You haven't sent any prank calls yet.
Date
Sent To
Script
Status
Recording
50 ? '...' : ''); ?>
N/A
FILE 11: /home/ai-prank-caller.digitalprank.com/public_html/twilio_status_webhook.php
code
PHP
validate($twilioSignature, $url, $postVars)) {
header('HTTP/1.1 403 Forbidden');
error_log("Twilio Webhook: Invalid signature received.");
exit("Invalid signature.");
}
// --- Process the Webhook Data ---
header('Content-Type: text/plain');
$callSid = isset($_POST['CallSid']) ? $_POST['CallSid'] : null;
$callStatus = isset($_POST['CallStatus']) ? $_POST['CallStatus'] : null;
if (!$callSid || !$callStatus) {
header("HTTP/1.1 400 Bad Request");
error_log("Twilio Webhook: Missing CallSid or CallStatus.");
exit("Missing required parameters.");
}
// Log the incoming request for debugging
$log_file = __DIR__ . '/webhook_log.txt';
$log_entry = "[" . date('Y-m-d H:i:s') . "] SID: $callSid, Status: $callStatus\n";
file_put_contents($log_file, $log_entry, FILE_APPEND);
// Connect to the database
$db_host = 'localhost';
$db_name = 'digitalprank_db';
$db_user = 'dp_user';
$db_pass = '#$Dealer2355';
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Update the record in the database
$stmt = $pdo->prepare("UPDATE prank_call_history SET status = ? WHERE call_sid = ?");
$stmt->execute([$callStatus, $callSid]);
if ($stmt->rowCount() > 0) {
echo "Status for $callSid updated to $callStatus.";
} else {
// This might happen if Twilio sends a webhook for a SID not in our system.
header("HTTP/1.1 404 Not Found");
error_log("Twilio Webhook: CallSid $callSid not found in the database.");
echo "CallSid not found.";
}
} catch (PDOException $e) {
header("HTTP/1.1 500 Internal Server Error");
error_log("Twilio Webhook: Database error - " . $e->getMessage());
echo "Database error.";
}
FILE 12: /home/ai-prank-caller.digitalprank.com/public_html/cron_scheduler.php
code
PHP
#!/usr/bin/php
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Find scheduled calls that are due
$stmt = $pdo->prepare(
"SELECT id, victim_phone, caller_id, audio_url FROM prank_call_history
WHERE status = 'scheduled' AND schedule_time <= NOW()"
);
$stmt->execute();
$due_calls = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($due_calls)) {
echo "No scheduled calls are due.\n";
} else {
echo "Found " . count($due_calls) . " due calls to process.\n";
// In a real app, load Twilio SDK and credentials securely
// require_once 'vendor/autoload.php';
// use Twilio\Rest\Client;
// $twilio_client = new Client(getenv('TWILIO_SID'), getenv('TWILIO_TOKEN'));
foreach ($due_calls as $call) {
echo "Processing call ID: {$call['id']} to {$call['victim_phone']}...\n";
try {
// Prepare TwiML URL for the call handler
$handler_url = "https://ai-prank-caller.digitalprank.com/twilio_handler.php?audio_url=" . urlencode($call['audio_url']);
// MOCK INITIATING TWILIO CALL
// $twilio_call = $twilio_client->calls->create(
// $call['victim_phone'],
// $call['caller_id'], // The 'from' number
// ['url' => $handler_url]
// );
// $new_call_sid = $twilio_call->sid;
// For this mock, generate a new SID
$new_call_sid = 'CA_CRON_' . bin2hex(random_bytes(14));
$new_status = 'queued';
// Update the database with the new SID and status
$update_stmt = $pdo->prepare(
"UPDATE prank_call_history SET status = ?, call_sid = ? WHERE id = ?"
);
$update_stmt->execute([$new_status, $new_call_sid, $call['id']]);
echo " -> Successfully initiated call. New SID: {$new_call_sid}\n";
} catch (Exception $e) {
// If initiating the call failed, update status to 'failed'
$fail_stmt = $pdo->prepare(
"UPDATE prank_call_history SET status = 'failed' WHERE id = ?"
);
$fail_stmt->execute([$call['id']]);
error_log("Scheduler Error: Failed to initiate call for ID {$call['id']}: " . $e->getMessage());
echo " -> ERROR: Failed to initiate call. See error log.\n";
}
}
}
} catch (PDOException $e) {
error_log("Scheduler Error: Database connection failed - " . $e->getMessage());
echo "ERROR: Database connection failed.\n";
} finally {
// --- Release Lock ---
flock($lock_handle, LOCK_UN);
fclose($lock_handle);
echo "Scheduler finished at " . date('Y-m-d H:i:s') . "\n";
}
FILE 13: /home/ai-prank-caller.digitalprank.com/public_html/api.php
code
PHP
false, 'message' => 'API key is missing.']);
exit;
}
// Database Connection
$db_host = 'localhost';
$db_name = 'digitalprank_db';
$db_user = 'dp_user';
$db_pass = '#$Dealer2355';
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'API service unavailable.']);
exit;
}
// --- Validate API Key and Get User Info ---
// In a real system, you'd have an `api_keys` table linked to `users`.
// We will mock this by assuming a key maps to a user_id with Ultimate tier access.
$stmt = $pdo->prepare("SELECT user_id FROM wp_user_api_keys WHERE api_key = ? AND is_active = 1");
// $stmt->execute([$api_key]);
// $user_id = $stmt->fetchColumn();
$user_id = ($api_key === 'dp-ultimate-user-secret-key') ? 101 : false; // Mock validation
if (!$user_id) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => 'Invalid or inactive API key.']);
exit;
}
// For API users, we assume they have 'ultimate' tier access.
$access = ['tier' => 'ultimate', 'level' => 3];
// --- Process API Request ---
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed. Use POST.']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid JSON body.']);
exit;
}
// --- Input Validation (Simplified for API) ---
$script = $input['script_prompt'] ?? null;
$voice = $input['voice_style'] ?? 'annoying_robot';
$victim_phone = $input['victim_phone'] ?? null;
$caller_id = $input['caller_id'] ?? null; // Optional
$errors = [];
if (empty($script) || strlen($script) < 10 || strlen($script) > 500) {
$errors[] = 'script_prompt must be between 10 and 500 characters.';
}
if (empty($victim_phone) || !preg_match('/^\+[1-9]\d{1,14}$/', $victim_phone)) {
$errors[] = 'victim_phone is required and must be in E.164 format.';
}
if (!empty($caller_id) && !preg_match('/^\+[1-9]\d{1,14}$/', $caller_id)) {
$errors[] = 'caller_id must be in E.164 format if provided.';
}
if (!empty($errors)) {
http_response_code(422); // Unprocessable Entity
echo json_encode(['success' => false, 'errors' => $errors]);
exit;
}
// --- Core Logic (similar to processor.php) ---
try {
// 1. Generate Audio (mocked)
$output_filename = 'api_prank_' . uniqid() . '.wav';
$audio_url = "https://cdn.digitalprank.com/audio/pranks/" . $output_filename;
// 2. Initiate Call (mocked)
$call_sid = 'CA_API_' . bin2hex(random_bytes(15));
$status = 'queued';
// 3. Store in Database
$stmt = $pdo->prepare(
"INSERT INTO prank_call_history (user_id, victim_phone, caller_id, script, voice, audio_url, call_sid, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
);
$stmt->execute([$user_id, $victim_phone, $caller_id, $script, $voice, $audio_url, $call_sid, $status]);
// --- Success Response ---
http_response_code(202); // Accepted
echo json_encode([
'success' => true,
'message' => 'Prank call accepted and is being processed.',
'call_sid' => $call_sid,
'status' => $status
]);
} catch (Exception $e) {
http_response_code(500);
error_log("API Error: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'An internal server error occurred while processing the request.']);
}
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 14: /home/ai-prank-caller.digitalprank.com/public_html/admin/index.php
code
PHP
Admin Login
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Database connection failed.");
}
// Get Tool ID
$tool_slug = 'ai-prank-caller';
$stmt = $pdo->prepare("SELECT tool_id FROM wp_digitalprank_tools WHERE slug = ?");
$stmt->execute([$tool_slug]);
$tool_id = $stmt->fetchColumn();
// Handle form submissions for overrides
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['field_id'])) {
$field_id = $_POST['field_id'];
$override_type = $_POST['override_type'];
$tier_required = $_POST['tier_required'] ?? 'basic';
$is_active = isset($_POST['is_active']) ? 1 : 0;
$stmt = $pdo->prepare(
"INSERT INTO wp_digitalprank_tool_overrides (tool_id, field_id, tier_required, override_type, is_active)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE tier_required = VALUES(tier_required), override_type = VALUES(override_type), is_active = VALUES(is_active)"
);
$stmt->execute([$tool_id, $field_id, $tier_required, $override_type, $is_active]);
header("Location: " . $_SERVER['PHP_SELF']); // Refresh page
exit;
}
// Fetch current overrides
$stmt = $pdo->prepare("SELECT field_id, tier_required, override_type, is_active FROM wp_digitalprank_tool_overrides WHERE tool_id = ?");
$stmt->execute([$tool_id]);
$overrides = $stmt->fetchAll(PDO::FETCH_KEY_PAIR | PDO::FETCH_GROUP);
// Fetch recent usage logs
$usage_logs = $pdo->query("SELECT timestamp, user_id, ip_address, status, input_data FROM wp_digitalprank_usage_log WHERE tool_slug = '{$tool_slug}' ORDER BY timestamp DESC LIMIT 20")->fetchAll(PDO::FETCH_ASSOC);
// Load tool config to get field list
$config = json_decode(file_get_contents(__DIR__ . '/../tool_config.json'), true)['tool'];
?>
Admin - AI Prank Caller
Admin Panel: AI Prank Caller
Field Overrides
Control feature access for different subscription tiers.
FILE 16: /home/ai-prank-caller.digitalprank.com/public_html/README.md```markdown
AI Prank Caller Tool - Developer README
This document provides technical setup and configuration instructions for the AI Prank Caller tool on the DigitalPrank platform.
1. Overview
This tool allows users to send AI-voiced prank calls. It consists of a frontend form (tool_form.php), a backend processor (processor.php), an audio generation script (generate_audio.py), and several supporting scripts for handling webhooks, scheduling, and administration.
2. Dependencies
Ensure all dependencies listed in tool_config.json are installed. The deploy.sh script automates most of this.
System Packages: ffmpeg, sox, python3-venv
PHP Extensions: curl, json, mbstring, pdo_mysql
Python Venv (/var/www/venv/ai-prank-caller): xtts-cli, pydub, twilio
PHP Composer: twilio/sdk (Install via composer require twilio/sdk in the public_html directory).
3. Configuration
3.1. Environment Variables
The application relies on environment variables for security. These should be set in your web server's configuration (e.g., OpenLiteSpeed vHost config or a .env file loaded by your framework).
TWILIO_ACCOUNT_SID: Your Twilio Account SID.
TWILIO_AUTH_TOKEN: Your Twilio Auth Token.
DEFAULT_TWILIO_NUMBER: A verified Twilio phone number to use as the default caller ID.
3.2. Web Server Configuration (OpenLiteSpeed)
The deploy.sh script generates a sample vHost configuration. This must be manually added to your OpenLiteSpeed server configuration. The key parts are:
Defining the virtualHost with the correct vhRoot.
Mapping the domain ai-prank-caller.digitalprank.com to this virtual host.
Ensuring the lsapi:php81 script handler is correctly configured.
3.3. Speaker Voice WAV
The generate_audio.py script uses a reference audio file to clone a voice. Place a high-quality, 10-15 second WAV file of your desired default speaker at:
/var/www/assets/voices/default_speaker.wav
4. Cron Jobs
Two cron jobs are required for the tool's operation. Add the following lines to your system's crontab (crontab -e):
code
Crontab
# Run the prank call scheduler every minute to process due calls
* * * * * /usr/bin/php /home/ai-prank-caller.digitalprank.com/public_html/cron_scheduler.php >> /var/log/digitalprank/scheduler.log 2>&1
# Run the data cleanup job once per day at 3:00 AM
0 3 * * * /usr/bin/php /home/ai-prank-caller.digitalprank.com/public_html/cleanup.php >> /var/log/digitalprank/cleanup.log 2>&1
Ensure the log directory /var/log/digitalprank/ exists and is writable by the cron user.
5. Twilio Webhook Configuration
Twilio needs to send real-time updates about the call's status. In your Twilio phone number's configuration:
A CALL COMES IN: Set to Webhook.
URL: https://ai-prank-caller.digitalprank.com/twilio_status_webhook.php
HTTP Method: HTTP POST
Under the main "Voice" settings for your account, find the "Status Callbacks" section and set the URL to the same webhook to receive final call status updates.
6. API Usage
The API provides programmatic access to the tool.
Endpoint: https://ai-prank-caller.digitalprank.com/api.php
Method: POST
Authentication: Provide your API key in the X-API-KEY header.
Body: JSON payload.
Example Request:
code
Bash
curl -X POST \
https://ai-prank-caller.digitalprank.com/api.php \
-H 'Content-Type: application/json' \
-H 'X-API-KEY: dp-ultimate-user-secret-key' \
-d '{
"script_prompt": "Hello, we have been trying to reach you about your car'\''s extended warranty.",
"voice_style": "slick_salesman",
"victim_phone": "+15551234567",
"caller_id": "+15558675309"
}'
Success Response (202 Accepted):
code
JSON
{
"success": true,
"message": "Prank call accepted and is being processed.",
"call_sid": "CA_API_...",
"status": "queued"
}
code
Code
### FILE 17: /home/ai-prank-caller.digitalprank.com/public_html/rate_limiter.php
```php
pdo = $pdo;
$this->tool_slug = $config['identity']['slug'];
$this->rate_limit_per_minute = $config['limits']['rate_limit_per_minute'];
$this->max_concurrent_requests = $config['limits']['max_concurrent_requests'];
}
/**
* Checks if the user is allowed to make a request based on per-minute limits.
*
* @param string $user_identifier (IP address or User ID)
* @return bool True if allowed, false if rate-limited.
*/
public function isAllowed(string $user_identifier): bool {
// We use the usage log table for rate limiting.
// This is simple but might be slow under heavy load. Redis would be better.
$stmt = $this->pdo->prepare(
"SELECT COUNT(*) FROM wp_digitalprank_usage_log
WHERE (ip_address = ? OR user_id = ?)
AND tool_slug = ?
AND timestamp > (NOW() - INTERVAL 1 MINUTE)"
);
$stmt->execute([$user_identifier, (int)$user_identifier, $this->tool_slug]);
$request_count = $stmt->fetchColumn();
return $request_count < $this->rate_limit_per_minute;
}
/**
* Checks for concurrent requests.
* This is a simplified check and may not be perfectly accurate without a proper locking mechanism.
*
* @param int $user_id
* @return bool True if under the concurrency limit, false otherwise.
*/
public function checkConcurrency(int $user_id): bool {
if ($user_id === 0) {
// Concurrency checks are for logged-in users only
return true;
}
$stmt = $this->pdo->prepare(
"SELECT COUNT(*) FROM wp_digitalprank_usage_log
WHERE user_id = ?
AND tool_slug = ?
AND status = 'processing' -- A custom status you'd have to manage
AND timestamp > (NOW() - INTERVAL 5 MINUTE)"
);
$stmt->execute([$user_id, $this->tool_slug]);
$concurrent_requests = $stmt->fetchColumn();
return $concurrent_requests < $this->max_concurrent_requests;
}
}
?>
FILE 18: /home/ai-prank-caller.digitalprank.com/public_html/cleanup.php
code
PHP
#!/usr/bin/php
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
error_log("Cleanup Cron: Database connection failed: " . $e->getMessage());
die("Database connection failed.\n");
}
// --- Load all tool configs to find their retention policies ---
$tools_dir = '/home/digitalprank.com/public_html/tools/'; // A hypothetical directory
$tool_slugs = ['ai-prank-caller']; // In a real system, you'd scan for all tools
foreach ($tool_slugs as $slug) {
$config_path = "/home/{$slug}.digitalprank.com/public_html/tool_config.json";
if (!file_exists($config_path)) {
echo "Config for '{$slug}' not found. Skipping.\n";
continue;
}
$config = json_decode(file_get_contents($config_path), true)['tool'];
$db_config = $config['database'] ?? null;
if ($db_config && $db_config['store_results'] && !empty($db_config['tool_specific_table']) && !empty($db_config['retention_days'])) {
$table_name = $db_config['tool_specific_table'];
$retention_days = (int)$db_config['retention_days'];
if ($retention_days > 0) {
echo "Processing table '{$table_name}' with a {$retention_days}-day retention policy...\n";
try {
$sql = "DELETE FROM `{$table_name}` WHERE `created_at` < (NOW() - INTERVAL {$retention_days} DAY)";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$deleted_count = $stmt->rowCount();
echo " -> Deleted {$deleted_count} old records from `{$table_name}`.\n";
} catch (Exception $e) {
error_log("Cleanup Cron: Error cleaning table '{$table_name}': " . $e->getMessage());
echo " -> ERROR processing table '{$table_name}'. See logs.\n";
}
}
}
}
// --- Clean up the main usage log table (e.g., keep 90 days of logs) ---
$general_log_retention_days = 90;
echo "Processing platform-wide 'wp_digitalprank_usage_log' with a {$general_log_retention_days}-day policy...\n";
try {
$sql = "DELETE FROM `wp_digitalprank_usage_log` WHERE `timestamp` < (NOW() - INTERVAL {$general_log_retention_days} DAY)";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$deleted_count = $stmt->rowCount();
echo " -> Deleted {$deleted_count} old records from `wp_digitalprank_usage_log`.\n";
} catch (Exception $e) {
error_log("Cleanup Cron: Error cleaning 'wp_digitalprank_usage_log': " . $e->getMessage());
echo " -> ERROR processing 'wp_digitalprank_usage_log'. See logs.\n";
}
echo "Data cleanup job finished at " . date('Y-m-d H:i:s') . "\n";
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 19: /home/ai-prank-caller.digitalprank.com/public_html/assets/js/main.js
code
JavaScript
/**
* Main JavaScript file for the AI Prank Caller Tool
* Handles form submissions, UI updates, and conditional logic.
*/
document.addEventListener('DOMContentLoaded', function() {
const toolForm = document.getElementById('toolForm');
if (!toolForm) {
console.error("Tool form not found. Aborting script.");
return;
}
const loadingOverlay = document.getElementById('loadingOverlay');
const resultModal = document.getElementById('resultModal');
const resultTitle = document.getElementById('resultTitle');
const resultBody = document.getElementById('resultBody');
const closeModalButton = document.getElementById('closeModal');
const useAiVoiceCheckbox = document.getElementById('use_ai_voice');
const customAudioGroup = document.getElementById('group_custom_audio_file');
const scriptPromptGroup = document.getElementById('group_script_prompt');
const victimPhoneField = document.getElementById('victim_phone');
// This value should be set by PHP based on the user's logged-in status and tier.
const userAccess = {
hasPro: document.body.dataset.hasProAccess === 'true' || false
};
/**
* Toggles the visibility of the script prompt and the custom audio upload field
* based on the state of the "Use AI Voice" checkbox.
*/
function toggleInputFields() {
if (!useAiVoiceCheckbox) return;
if (useAiVoiceCheckbox.checked) {
if (scriptPromptGroup) scriptPromptGroup.style.display = 'block';
if (customAudioGroup) customAudioGroup.style.display = 'none';
} else {
if (scriptPromptGroup) scriptPromptGroup.style.display = 'none';
// Only show the custom audio field if the user is a Pro member
if (customAudioGroup && userAccess.hasPro) {
customAudioGroup.style.display = 'block';
}
}
}
/**
* Handles the form submission via AJAX.
* @param {Event} e The form submission event.
*/
function handleFormSubmit(e) {
e.preventDefault();
// Perform client-side validation
if (!toolForm.checkValidity()) {
toolForm.reportValidity();
return;
}
// Additional validation for Pro features
if (!userAccess.hasPro && victimPhoneField && victimPhoneField.value) {
displayModal('Error', "Sending calls is a Pro feature. Please upgrade your account to continue.", true);
return;
}
if (loadingOverlay) loadingOverlay.style.display = 'flex';
const formData = new FormData(toolForm);
// Ensure checkbox value is correctly represented when unchecked
if (useAiVoiceCheckbox && !useAiVoiceCheckbox.checked) {
formData.set('use_ai_voice', '0');
}
fetch('processor.php', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
// Handle non-2xx responses by trying to parse the JSON error message
return response.json().then(err => Promise.reject(err));
}
return response.json();
})
.then(data => {
if (data.success) {
displayModal('Success!', data.data.message || 'Your prank call has been queued successfully.');
toolForm.reset(); // Clear the form on success
} else {
displayModal('Error', data.message || 'An unknown error occurred.', true);
}
})
.catch(error => {
console.error('Submission Error:', error);
const errorMessage = error.message || 'A network error occurred. Please check your connection and try again.';
displayModal('Submission Failed', errorMessage, true);
})
.finally(() => {
if (loadingOverlay) loadingOverlay.style.display = 'none';
});
}
/**
* Displays the result modal with a given title and body.
* @param {string} title The title to display.
* @param {string} bodyHTML The HTML content for the modal body.
* @param {boolean} isError If true, styles the title as an error.
*/
function displayModal(title, bodyHTML, isError = false) {
if (!resultModal) return;
if (resultTitle) {
resultTitle.textContent = title;
resultTitle.className = isError ? 'error-message' : 'success-message';
}
if (resultBody) {
resultBody.innerHTML = bodyHTML;
}
resultModal.style.display = 'flex';
}
/**
* Hides the result modal.
*/
function hideModal() {
if (resultModal) resultModal.style.display = 'none';
}
// --- Event Listeners ---
if (useAiVoiceCheckbox) {
useAiVoiceCheckbox.addEventListener('change', toggleInputFields);
}
toolForm.addEventListener('submit', handleFormSubmit);
if (closeModalButton) {
closeModalButton.addEventListener('click', hideModal);
}
if (resultModal) {
// Close modal if user clicks outside the content area
resultModal.addEventListener('click', (event) => {
if (event.target === resultModal) {
hideModal();
}
});
}
// --- Initial State Setup ---
toggleInputFields();
});
FILE 20: /home/ai-prank-caller.digitalprank.com/public_html/assets/css/style.css
code
CSS
/**
* Main Stylesheet for the AI Prank Caller Tool
*/
/* --- General & Variables --- */
:root {
--primary-color: #FF4500; /* OrangeRed */
--secondary-color: #1a1a1a;
--background-color: #f4f7f6;
--form-background: #ffffff;
--border-color: #d1d1d1;
--success-color: #28a745;
--error-color: #dc3545;
--text-color: #333;
--label-color: #555;
--help-text-color: #777;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
background-color: var(--background-color);
color: var(--text-color);
margin: 0;
padding: 20px;
}
/* --- Form Container --- */
#toolForm {
max-width: 650px;
margin: 2rem auto;
padding: 2rem;
background: var(--form-background);
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
border: 1px solid var(--border-color);
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: var(--label-color);
font-size: 0.95rem;
}
.form-control {
width: 100%;
padding: 0.8rem 1rem;
border: 1px solid var(--border-color);
border-radius: 8px;
box-sizing: border-box;
transition: border-color 0.2s, box-shadow 0.2s;
font-size: 1rem;
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(255, 69, 0, 0.2);
outline: none;
}
textarea.form-control {
min-height: 140px;
resize: vertical;
}
/* --- Pro & Disabled States --- */
.pro-badge {
background-color: #ffd700;
color: #333;
font-size: 0.65rem;
padding: 3px 7px;
border-radius: 10px;
margin-left: 8px;
font-weight: bold;
vertical-align: middle;
letter-spacing: 0.5px;
}
.field-disabled {
opacity: 0.65;
cursor: not-allowed;
}
.field-disabled label,
.field-disabled .form-control {
cursor: not-allowed;
}
.field-disabled .form-control {
background-color: #f9f9f9;
}
.help-text {
font-size: 0.8rem;
color: var(--help-text-color);
margin-top: 6px;
}
/* --- Buttons --- */
.submit-btn {
background: var(--primary-color);
background: linear-gradient(145deg, var(--primary-color), #f97316);
color: white;
padding: 14px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1.1rem;
font-weight: 600;
width: 100%;
transition: transform 0.1s, box-shadow 0.2s;
}
.submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(255, 69, 0, 0.3);
}
/* --- Checkbox Styling --- */
.checkbox-label {
display: flex;
align-items: center;
font-weight: normal;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
margin-right: 12px;
height: 1.2em;
width: 1.2em;
accent-color: var(--primary-color);
}
/* --- Overlays and Modals --- */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(4px);
z-index: 10000;
display: none;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 1rem;
}
.spinner {
border: 5px solid #f3f3f3;
border-top: 5px solid var(--primary-color);
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.modal {
position: fixed;
z-index: 10001;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(5px);
display: none;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: var(--form-background);
margin: auto;
padding: 2rem 2.5rem;
border: none;
width: 90%;
max-width: 500px;
border-radius: 12px;
text-align: center;
position: relative;
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.close-button {
color: #aaa;
position: absolute;
top: 10px;
right: 20px;
font-size: 32px;
font-weight: bold;
cursor: pointer;
transition: color 0.2s;
}
.close-button:hover {
color: #333;
}
.error-message {
color: var(--error-color);
}
.success-message {
color: var(--success-color);
}
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 21: /home/ai-prank-caller.digitalprank.com/public_html/index.php
code
PHP
FILE 22: /home/ai-prank-caller.digitalprank.com/public_html/composer.json
code
JSON
{
"name": "digitalprank/ai-prank-caller",
"description": "The backend services for the AI Prank Caller tool.",
"type": "project",
"require": {
"php": ">=8.1",
"twilio/sdk": "^7.0"
},
"authors": [
{
"name": "DigitalPrank Dev Team",
"email": "dev@digitalprank.com"
}
],
"config": {
"vendor-dir": "vendor"
},
"minimum-stability": "stable"
}
FILE 23: /home/ai-prank-caller.digitalprank.com/public_html/processor.php
code
PHP
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Database connection failed.']);
exit;
}
// --- Configuration & Session ---
$config = json_decode(file_get_contents(__DIR__ . '/tool_config.json'), true)['tool'];
session_start();
$user_id = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 0;
$user_ip = $_SERVER['REMOTE_ADDR'];
// --- CSRF Check ---
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($_POST['csrf_token']) || empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => 'Invalid session token. Please refresh and try again.']);
exit;
}
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // Regenerate for next request
// --- Rate Limiting ---
$rate_limiter = new RateLimiter($pdo, $config);
$user_identifier = $user_id > 0 ? (string)$user_id : $user_ip;
if (!$rate_limiter->isAllowed($user_identifier)) {
http_response_code(429); // Too Many Requests
echo json_encode(['success' => false, 'message' => 'You are making requests too quickly. Please wait a moment.']);
exit;
}
// --- User Access & Usage Check ---
$access = getUserAccessLevel($pdo, $user_id, $config['identity']['slug']);
$limit = $config['limits']['tier_daily'][$access['tier']];
if (!checkDailyUsage($pdo, $config['identity']['slug'], $user_ip, $user_id, $limit)) {
http_response_code(429);
echo json_encode(['success' => false, 'message' => 'You have exceeded your daily usage limit.']);
exit;
}
// --- Main Processing Logic ---
$input = $_POST;
$start_time = microtime(true);
$response = ['success' => false, 'data' => null];
try {
// [The validation logic from the previous version of processor.php remains the same]
$errors = [];
$validated_input = [];
$fields = $config['fields'];
foreach ($fields as $field) { /* ... validation logic ... */ }
if (!empty($errors)) { throw new Exception(implode(' ', $errors)); }
// --- Tool Functionality ---
$output_data = [];
$audio_url_for_call = null;
$use_ai_voice = !(isset($input['use_ai_voice']) && $input['use_ai_voice'] === '0');
if ($use_ai_voice) {
// Mocked XTTS call
$script_to_run = escapeshellarg($validated_input['script_prompt']);
$output_filename = 'prank_' . uniqid() . '.wav';
$output_path = '/tmp/' . $output_filename;
// shell_exec("/var/www/venv/ai-prank-caller/bin/python generate_audio.py --text {$script_to_run} --voice ... --out {$output_path}");
$audio_url_for_call = "https://cdn.digitalprank.com/audio/pranks/{$output_filename}";
$output_data['audio_generation_status'] = 'success';
} else {
// Custom audio upload logic
// [Logic for handling $_FILES['custom_audio_file'] remains the same]
$public_filename = basename($custom_audio_path);
$audio_url_for_call = "https://ai-prank-caller.digitalprank.com/uploads/{$public_filename}";
}
// --- Twilio Call Initiation ---
$twilio_client = new Client(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN'));
$caller_id_to_use = getenv('DEFAULT_TWILIO_NUMBER');
if ($access['level'] >= getTierLevel('gold') && !empty($validated_input['caller_id'])) {
$caller_id_to_use = $validated_input['caller_id'];
}
$handler_url = "https://ai-prank-caller.digitalprank.com/twilio_handler.php?audio_url=" . urlencode($audio_url_for_call);
$call_options = [
'from' => $caller_id_to_use,
'url' => $handler_url,
'statusCallback' => 'https://ai-prank-caller.digitalprank.com/twilio_status_webhook.php',
'statusCallbackMethod' => 'POST',
'statusCallbackEvent' => ['initiated', 'ringing', 'answered', 'completed'],
];
$call = $twilio_client->calls->create($validated_input['victim_phone'], $caller_id_to_use, $call_options);
$output_data['call_sid'] = $call->sid;
$output_data['call_status'] = $call->status;
$output_data['message'] = "Prank call successfully initiated to {$validated_input['victim_phone']}.";
// --- Database Logging ---
if ($config['database']['store_results']) {
$stmt = $pdo->prepare(
"INSERT INTO prank_call_history (user_id, victim_phone, caller_id, script, voice, audio_url, call_sid, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
);
$stmt->execute([ $user_id, $validated_input['victim_phone'], $caller_id_to_use, $validated_input['script_prompt'], $validated_input['voice_style'], $audio_url_for_call, $call->sid, $call->status ]);
}
$response['success'] = true;
$response['data'] = $output_data;
$processing_time = microtime(true) - $start_time;
logUsage($pdo, $config['identity']['slug'], $user_ip, $user_id, 'success', $input, $output_data, $processing_time, $config['billing']['credit_cost']);
} catch (Exception $e) {
http_response_code(400);
$response['message'] = $e->getMessage();
$processing_time = microtime(true) - $start_time;
logUsage($pdo, $config['identity']['slug'], $user_ip, $user_id, 'failed', $input, ['error' => $e->getMessage()], $processing_time, 0);
}
// --- Final Response ---
echo json_encode($response);
// Helper functions (getToolOverrides, getTierLevel) remain the same
function getTierLevel($tier_name) {
$levels = ['free' => 0, 'basic' => 1, 'gold' => 2, 'ultimate' => 3];
return $levels[strtolower($tier_name)] ?? 0;
}
FILE 24: /home/ai-prank-caller.digitalprank.com/public_html/api.php
code
PHP
false, 'message' => 'API key is missing.']);
exit;
}
// --- Database Connection ---
$db_host = 'localhost';
$db_name = 'digitalprank_db';
$db_user = 'dp_user';
$db_pass = '#$Dealer2355';
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'API service unavailable.']);
exit;
}
// --- Validate API Key and User ---
// In a real system, you would join tables to get user tier.
$stmt = $pdo->prepare("SELECT user_id FROM wp_user_api_keys WHERE api_key = ? AND is_active = 1");
$stmt->execute([$api_key]);
$user_id = $stmt->fetchColumn();
if (!$user_id) {
http_response_code(403);
echo json_encode(['success' => false, 'message' => 'Invalid or inactive API key.']);
exit;
}
$config = json_decode(file_get_contents(__DIR__ . '/tool_config.json'), true)['tool'];
// --- Rate Limiting for the API key/user ---
$rate_limiter = new RateLimiter($pdo, $config);
if (!$rate_limiter->isAllowed((string)$user_id)) {
http_response_code(429);
echo json_encode(['success' => false, 'message' => 'API rate limit exceeded.']);
exit;
}
// --- Process API Request ---
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed. Use POST.']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid JSON body.']);
exit;
}
// --- Input Validation (Same as before) ---
$script = $input['script_prompt'] ?? null;
$victim_phone = $input['victim_phone'] ?? null;
$errors = [];
if (empty($script) || strlen($script) < 10 || strlen($script) > 500) $errors[] = 'script_prompt is required (10-500 chars).';
if (empty($victim_phone) || !preg_match('/^\+[1-9]\d{1,14}$/', $victim_phone)) $errors[] = 'victim_phone is required and must be in E.164 format.';
if (!empty($errors)) {
http_response_code(422);
echo json_encode(['success' => false, 'errors' => $errors]);
exit;
}
// --- Core Logic ---
try {
// 1. Generate Audio (mocked)
$audio_url = "https://cdn.digitalprank.com/audio/pranks/api_" . uniqid() . ".wav";
// 2. Initiate Twilio Call
$twilio_client = new Client(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN'));
$caller_id = $input['caller_id'] ?? getenv('DEFAULT_TWILIO_NUMBER');
$handler_url = "https://ai-prank-caller.digitalprank.com/twilio_handler.php?audio_url=" . urlencode($audio_url);
$call = $twilio_client->calls->create($victim_phone, $caller_id, [
'url' => $handler_url,
'statusCallback' => 'https://ai-prank-caller.digitalprank.com/twilio_status_webhook.php',
'statusCallbackMethod' => 'POST'
]);
// 3. Log to Database
$stmt = $pdo->prepare("INSERT INTO prank_call_history (user_id, victim_phone, caller_id, script, voice, audio_url, call_sid, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $victim_phone, $caller_id, $script, $input['voice_style'] ?? 'annoying_robot', $audio_url, $call->sid, $call->status]);
// --- Success Response ---
http_response_code(202);
echo json_encode(['success' => true, 'message' => 'Prank call accepted.', 'call_sid' => $call->sid, 'status' => $call->status]);
} catch (Exception $e) {
http_response_code(500);
error_log("API Error for user {$user_id}: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'An internal server error occurred.']);
}
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 25: /home/ai-prank-caller.digitalprank.com/public_html/bootstrap.php
code
PHP
get();
} catch (Exception $e) {
// In a real app, you'd show a user-friendly error page.
die("Fatal Error: Could not load or parse tool configuration: " . $e->getMessage());
}
// --- Database Connection ---
$db_host = 'localhost';
$db_name = 'digitalprank_db';
$db_user = 'dp_user';
$db_pass = '#$Dealer2355';
$pdo = null; // Make it globally accessible for scripts that include this bootstrap
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
} catch (PDOException $e) {
// In a real API, you'd just log this and return a generic 500.
die("Fatal Error: Database connection failed. Please check the service status.");
}
// --- Session Initialization ---
session_start([
'cookie_httponly' => true,
'cookie_secure' => isset($_SERVER['HTTPS']),
'cookie_samesite' => 'Lax'
]);
// --- Service Initialization ---
// Initialize services that might be needed by the including script.
$twilioService = null;
try {
$twilioService = new TwilioService(
getenv('TWILIO_ACCOUNT_SID'),
getenv('TWILIO_AUTH_TOKEN'),
getenv('DEFAULT_TWILIO_NUMBER')
);
} catch (Exception $e) {
// Log the error but don't die, as not all pages might need Twilio.
error_log('Twilio Service Initialization Failed: ' . $e->getMessage());
}
$rateLimiter = new RateLimiter($pdo, $config);
// --- User Context ---
$user_id = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 0;
$user_ip = $_SERVER['REMOTE_ADDR'];
// $access = getUserAccessLevel($pdo, $user_id, TOOL_SLUG); // This function would be defined in the platform includes
FILE 26: /home/ai-prank-caller.digitalprank.com/public_html/services/TwilioService.php
code
PHP
client = new Client($accountSid, $authToken);
$this->authToken = $authToken;
$this->defaultFromNumber = $defaultFromNumber;
}
/**
* Initiates a call via Twilio.
*
* @param string $to The recipient's phone number in E.164 format.
* @param string $audioUrl The publicly accessible URL of the audio file to play.
* @param string|null $from The caller ID to use. If null, uses the default.
* @return \Twilio\Rest\Api\V2010\Account\CallInstance
* @throws \Twilio\Exceptions\TwilioException
*/
public function makeCall(string $to, string $audioUrl, ?string $from = null): \Twilio\Rest\Api\V2010\Account\CallInstance {
$fromNumber = $from ?: $this->defaultFromNumber;
// Construct the URL to our TwiML handler script
$handlerUrl = "https://ai-prank-caller.digitalprank.com/twilio_handler.php?audio_url=" . urlencode($audioUrl);
// Configure webhook for status updates
$statusCallbackUrl = "https://ai-prank-caller.digitalprank.com/twilio_status_webhook.php";
return $this->client->calls->create(
$to,
$fromNumber,
[
'url' => $handlerUrl,
'statusCallback' => $statusCallbackUrl,
'statusCallbackMethod' => 'POST',
'statusCallbackEvent' => ['ringing', 'answered', 'completed', 'busy', 'failed', 'no-answer'],
]
);
}
/**
* Validates an incoming webhook request to ensure it came from Twilio.
*
* @param string $signature The value of the X-Twilio-Signature header.
* @param string $url The full URL of the request.
* @param array $postVars The POST variables of the request.
* @return bool True if the request is valid, false otherwise.
*/
public function validateRequest(string $signature, string $url, array $postVars): bool {
$validator = new RequestValidator($this->authToken);
return $validator->validate($signature, $url, $postVars);
}
}
FILE 27: /home/ai-prank-caller.digitalprank.com/public_html/lib/Config.php
code
PHP
settings = json_decode($jsonContent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("Error parsing configuration JSON: " . json_last_error_msg());
}
}
/**
* Gets the single instance of the Config class.
* @param string|null $configFilePath The path to the config file on first call.
* @return Config
* @throws Exception if the instance is requested without a file path for the first time.
*/
public static function getInstance(?string $configFilePath = null): Config {
if (self::$instance === null) {
if ($configFilePath === null) {
throw new Exception("Configuration file path must be provided on first call to getInstance.");
}
self::$instance = new Config($configFilePath);
}
return self::$instance;
}
/**
* Returns the entire configuration array.
* @return array
*/
public function get(): array {
return $this->settings['tool'] ?? [];
}
/**
* Gets a specific configuration value using dot notation.
* Example: 'identity.slug'
*
* @param string $key
* @param mixed|null $default
* @return mixed
*/
public function getVal(string $key, mixed $default = null): mixed {
$config = $this->get();
$keys = explode('.', $key);
$value = $config;
foreach ($keys as $k) {
if (!isset($value[$k])) {
return $default;
}
$value = $value[$k];
}
return $value;
}
}
FILE 28: /home/ai-prank-caller.digitalprank.com/public_html/.htaccess
code
Apache
# AI Prank Caller .htaccess File
# Provides URL rewriting, security, and performance enhancements.
RewriteEngine On
# Redirect www to non-www (and https)
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Block access to sensitive files
Require all denied
# Block access to specific directories
RewriteRule ^(vendor|database|services|lib)/ - [F,L]
# Basic Front Controller: Route all non-existent file/dir requests to index.php
# This is less critical for this simple tool but good practice.
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule . index.php [L]
# Caching for static assets
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
# Security Headers
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
# Header set Content-Security-Policy "default-src 'self';"
# Disable directory browsing
Options -Indexes
# PHP settings
php_flag log_errors On
php_value error_log /home/ai-prank-caller.digitalprank.com/logs/php_errors.log
FILE 29: /home/ai-prank-caller.digitalprank.com/public_html/database/schema.sql
code
SQL
-- DigitalPrank Platform Schema for AI Prank Caller Tool
-- This file documents the structure of the tool-specific table.
-- The core platform tables (wp_digitalprank_*) are assumed to exist.
-- Table structure for `prank_call_history`
-- Stores a record for every prank call initiated by the tool.
CREATE TABLE IF NOT EXISTS `prank_call_history` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` BIGINT UNSIGNED NOT NULL COMMENT 'Corresponds to the user ID in the main platform.',
`victim_phone` VARCHAR(20) NOT NULL COMMENT 'The recipient''s phone number in E.164 format.',
`caller_id` VARCHAR(20) DEFAULT NULL COMMENT 'The number displayed as the caller ID (From number).',
`script` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'The text script used for AI voice generation.',
`voice` VARCHAR(50) DEFAULT NULL COMMENT 'The selected voice style, e.g., ''annoying_robot''.',
`audio_url` VARCHAR(255) DEFAULT NULL COMMENT 'The public URL of the audio file played during the call.',
`call_sid` VARCHAR(40) CHARACTER SET 'ascii' COLLATE 'ascii_general_ci' DEFAULT NULL COMMENT 'The unique session identifier from Twilio.',
`status` VARCHAR(20) DEFAULT 'pending' COMMENT 'The status of the call, updated via Twilio webhook (e.g., queued, ringing, completed, failed).',
`schedule_time` DATETIME DEFAULT NULL COMMENT 'If the call was scheduled, this is the time it was set for.',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `call_sid` (`call_sid`),
KEY `user_id_idx` (`user_id`),
KEY `status_idx` (`status`),
KEY `schedule_time_status_idx` (`schedule_time`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='History of all prank calls made with the AI tool.';
-- Table structure for `wp_user_api_keys` (Assumed to exist for API)
-- CREATE TABLE IF NOT EXISTS `wp_user_api_keys` (
-- `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
-- `user_id` BIGINT UNSIGNED NOT NULL,
-- `api_key` VARCHAR(64) NOT NULL,
-- `is_active` TINYINT(1) NOT NULL DEFAULT '1',
-- `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
-- PRIMARY KEY (`id`),
-- UNIQUE KEY `api_key` (`api_key`),
-- KEY `user_id` (`user_id`)
-- ) ENGINE=InnoDB;
FILE 30: /home/ai-prank-caller.digitalprank.com/public_html/api_docs.md
code
Markdown
# AI Prank Caller API Documentation (v1)
Welcome to the AI Prank Caller API! This document provides all the information you need to integrate our prank calling functionality into your own applications. Access to the API is available to **Ultimate tier** subscribers.
## 1. Authentication
Authentication is handled via an API key. You must include your key in the `X-API-KEY` header with every request.
- **Header:** `X-API-KEY`
- **Value:** `Your-Unique-API-Key`
Requests without a valid API key will receive a `401 Unauthorized` or `403 Forbidden` response.
---
## 2. Endpoints
### 2.1. Send a Prank Call
This is the primary endpoint for initiating a new prank call. The request is processed asynchronously. A successful response indicates that the call has been accepted for processing, not that it has been completed.
- **URL:** `https://ai-prank-caller.digitalprank.com/api.php`
- **Method:** `POST`
- **Content-Type:** `application/json`
#### Request Body
| Parameter | Type | Required | Description |
| ---------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------- |
| `script_prompt` | String | Yes | The text you want the AI to speak. Must be between 10 and 500 characters. |
| `victim_phone` | String | Yes | The recipient's phone number in E.164 format (e.g., `+15551234567`). |
| `voice_style` | String | No | The voice to use. Defaults to `annoying_robot`. See below for available voices. |
| `caller_id` | String | No | The number to display as the Caller ID, in E.164 format. If omitted, a default platform number is used. |
#### Available `voice_style` Values
- `annoying_robot`
- `squeaky_chipmunk`
- `scary_deep`
- `sassy_female`
- `movie_narrator`
- `angry_neighbor`
- `confused_old_lady`
- `slick_salesman`
- `nervous_guy`
#### Example Request
```bash
curl -X POST \
https://ai-prank-caller.digitalprank.com/api.php \
-H 'Content-Type: application/json' \
-H 'X-API-KEY: YOUR_API_KEY_HERE' \
-d '{
"script_prompt": "Hello? I am calling from the International Association of Lost Socks. We have located a missing argyle sock of yours. Please describe its partner for verification.",
"victim_phone": "+15551234567",
"voice_style": "movie_narrator",
"caller_id": "+15558675309"
}'
Responses
202 Accepted (Success): The request was valid and the call has been queued.
code
JSON
{
"success": true,
"message": "Prank call accepted.",
"call_sid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "queued"
}
400 Bad Request: The request body is not valid JSON.
401/403 Unauthorized/Forbidden: The API key is missing or invalid.
422 Unprocessable Entity: The JSON is valid, but the data fails validation (e.g., missing required fields, invalid phone number format).
code
JSON
{
"success": false,
"errors": [
"victim_phone is required and must be in E.164 format."
]
}
429 Too Many Requests: You have exceeded your allowed API request rate.
500 Internal Server Error: A problem occurred on our end.
3. Rate Limiting
The API is rate-limited to prevent abuse. The current limit is 10 requests per minute. If you exceed this limit, you will receive a 429 Too Many Requests response. Please design your application to handle this gracefully, for example, by using an exponential backoff strategy.
code
Code
Model
ThinkingThoughts
(experimental)
Auto
Expand to view model thoughts
chevron_right
FILE 31: /home/ai-prank-caller.digitalprank.com/public_html/database/prank_scripts_library.json
code
JSON
{
"categories": [
{
"name": "Service Calls",
"description": "Confusing calls from fictional services.",
"scripts": [
{
"title": "The Refrigerator Runner",
"prompt": "Hello, this is Kevin from 'Running Refrigerators'. Is your refrigerator running? ... Well, you'd better go catch it! We'll be sending our team to help you track it down in the neighborhood shortly.",
"suggested_voice": "slick_salesman"
},
{
"title": "Pigeon Rental Service",
"prompt": "Hi, I'm calling to confirm your order for the deluxe pigeon rental package for this Friday. Our homing pigeons are very excited. Just a reminder, please do not feed them glitter. It was a problem last time.",
"suggested_voice": "nervous_guy"
},
{
"title": "Emotional Support Ostrich",
"prompt": "Good afternoon, this is a courtesy call from Animal Companions Inc. regarding your application for an emotional support ostrich. We need to schedule a home inspection to ensure your ceilings are high enough. He's a bit clumsy.",
"suggested_voice": "sassy_female"
}
]
},
{
"name": "Mistaken Identity",
"description": "Pretending to be someone they're definitely not.",
"scripts": [
{
"title": "Space Mission Control",
"prompt": "This is Star Command to Captain Lightyear. Do you copy? We've lost your signal since you entered the K-pax nebula. We need a status report on the alien squirrels. Over.",
"suggested_voice": "movie_narrator"
},
{
"title": "Your Future Self",
"prompt": "Listen carefully, it's you, from the future. Do NOT, I repeat, do NOT eat the spicy burrito for lunch tomorrow. It's a trap. Also, buy more toilet paper. You'll thank me later.",
"suggested_voice": "scary_deep"
},
{
"title": "Spy Handler",
"prompt": "Agent 7, the package has been delivered. The eagle has landed. I repeat, the eagle has landed. Your next mission, should you choose to accept it, is to retrieve the milk from the corner store. This message will self-destruct.",
"suggested_voice": "annoying_robot"
}
]
},
{
"name": "Custom Complaints",
"description": "Hilarious complaints about non-existent problems.",
"scripts": [
{
"title": "Lawn Gnomes Union",
"prompt": "Yes, hello. I represent the Local 42 Garden Gnomes Union. It has come to our attention that the gnome in your front yard is being forced to work overtime without adequate sun protection. This is a violation of our collective bargaining agreement.",
"suggested_voice": "angry_neighbor"
},
{
"title": "Cat Interpreter",
"prompt": "Hi, I'm a professional cat interpreter, and I'm here with your cat, Mittens. She's asked me to call you and express her deep disappointment with the quality of the sunbeams in the living room this morning. She expects better.",
"suggested_voice": "sassy_female"
},
{
"title": "Confused Grandma",
"prompt": "Hello dearie, is that you? I tried to send you a picture on The Google, but it ended up in my toaster. The bread came out with your face on it! Can you come over and fix the internet machine?",
"suggested_voice": "confused_old_lady"
}
]
}
]
}
FILE 32: /home/ai-prank-caller.digitalprank.com/public_html/ajax_get_scripts.php
code
PHP
false, 'message' => 'Script library is currently unavailable.']);
exit;
}
try {
$content = file_get_contents($library_file);
$library_data = json_decode($content, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Error parsing script library data.');
}
echo json_encode(['success' => true, 'data' => $library_data]);
} catch (Exception $e) {
http_response_code(500);
error_log("Script Library Error: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Could not retrieve script library.']);
}
FILE 33: /home/ai-prank-caller.digitalprank.com/public_html/tool_form.php
code
PHP
×
Prank Script Library
FILE 34: /home/ai-prank-caller.digitalprank.com/public_html/assets/js/main.js
code
JavaScript
/**
* Main JavaScript file (VERSION 1.2 - WITH SCRIPT LIBRARY)
*/
document.addEventListener('DOMContentLoaded', function() {
// ... (Existing variables for form, modals, etc.)
const openScriptLibraryButton = document.getElementById('openScriptLibrary');
const scriptLibraryModal = document.getElementById('scriptLibraryModal');
const closeScriptLibraryModal = document.getElementById('closeScriptLibraryModal');
const scriptLibraryContent = document.getElementById('scriptLibraryContent');
const scriptPromptTextarea = document.getElementById('script_prompt');
const voiceStyleSelect = document.getElementById('voice_style');
/**
* Fetches scripts from the server and populates the library modal.
*/
async function loadScriptLibrary() {
try {
const response = await fetch('ajax_get_scripts.php');
if (!response.ok) {
throw new Error('Network response was not ok.');
}
const result = await response.json();
if (result.success && result.data.categories) {
scriptLibraryContent.innerHTML = ''; // Clear spinner
result.data.categories.forEach(category => {
const categoryDiv = document.createElement('div');
categoryDiv.className = 'category';
const categoryTitle = document.createElement('h4');
categoryTitle.textContent = category.name;
categoryDiv.appendChild(categoryTitle);
category.scripts.forEach(script => {
const scriptItem = document.createElement('div');
scriptItem.className = 'script-item';
scriptItem.innerHTML = `${script.title}${script.prompt}`;
// Add event listener to populate the form when a script is clicked
scriptItem.addEventListener('click', () => {
if (scriptPromptTextarea) {
scriptPromptTextarea.value = script.prompt;
}
if (voiceStyleSelect && script.suggested_voice) {
voiceStyleSelect.value = script.suggested_voice;
}
hideScriptLibraryModal();
});
categoryDiv.appendChild(scriptItem);
});
scriptLibraryContent.appendChild(categoryDiv);
});
} else {
scriptLibraryContent.innerHTML = `