test / index.html
txtorg's picture
Update index.html
e05c087 verified
Raw
History Blame Contribute Delete
74.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Baileys Command Generator</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--primary: #6c5ce7;
--primary-dark: #5649c0;
--secondary: #00cec9;
--dark: #2d3436;
--light: #f5f6fa;
--danger: #d63031;
--warning: #fdcb6e;
--success: #00b894;
--gray: #636e72;
--gray-light: #dfe6e9;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
background-color: var(--light);
color: var(--dark);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--gray-light);
margin-bottom: 30px;
}
.logo {
font-size: 24px;
font-weight: 700;
color: var(--primary);
display: flex;
align-items: center;
}
.logo-icon {
margin-right: 10px;
font-size: 28px;
}
.btn {
padding: 10px 20px;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
border: none;
font-family: 'Poppins', sans-serif;
display: inline-flex;
align-items: center;
justify-content: center;
}
.btn-primary {
background-color: var(--primary);
color: white;
}
.btn-primary:hover {
background-color: var(--primary-dark);
}
.btn-outline {
background-color: transparent;
border: 1px solid var(--primary);
color: var(--primary);
}
.btn-outline:hover {
background-color: var(--primary);
color: white;
}
.btn-danger {
background-color: var(--danger);
color: white;
}
.btn-danger:hover {
background-color: #c0392b;
}
.btn-sm {
padding: 6px 12px;
font-size: 14px;
}
.main {
display: grid;
grid-template-columns: 300px 1fr;
gap: 30px;
}
.sidebar {
background-color: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.sidebar-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.command-list {
list-style: none;
}
.command-item {
padding: 10px;
border-radius: 6px;
margin-bottom: 8px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: space-between;
}
.command-item:hover {
background-color: var(--gray-light);
}
.command-item.active {
background-color: var(--primary);
color: white;
}
.command-item .badge {
background-color: var(--secondary);
color: var(--dark);
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.command-builder {
background-color: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.builder-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--gray-light);
}
.builder-title {
font-size: 20px;
font-weight: 600;
}
.flow-container {
min-height: 500px;
border: 2px dashed var(--gray-light);
border-radius: 8px;
padding: 20px;
position: relative;
}
.flow-start {
background-color: var(--success);
color: white;
padding: 15px;
border-radius: 8px;
width: 200px;
text-align: center;
margin: 0 auto 30px;
position: relative;
}
.flow-start:after {
content: '';
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
width: 2px;
height: 15px;
background-color: var(--gray-light);
}
.flow-node {
background-color: white;
border: 2px solid var(--primary);
border-radius: 8px;
padding: 15px;
width: 250px;
margin: 30px auto;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
position: relative;
}
.flow-node:before {
content: '';
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
width: 2px;
height: 15px;
background-color: var(--gray-light);
}
.flow-node-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid var(--gray-light);
}
.flow-node-title {
font-weight: 600;
color: var(--primary);
}
.flow-node-actions {
display: flex;
gap: 5px;
}
.flow-node-content {
margin-top: 10px;
}
.form-group {
margin-bottom: 15px;
}
.form-label {
display: block;
margin-bottom: 5px;
font-size: 14px;
font-weight: 500;
}
.form-control {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--gray-light);
border-radius: 4px;
font-family: 'Poppins', sans-serif;
}
.form-control:focus {
outline: none;
border-color: var(--primary);
}
.select-control {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--gray-light);
border-radius: 4px;
font-family: 'Poppins', sans-serif;
background-color: white;
}
.btn-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
.options-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 10px;
margin-top: 15px;
}
.option-card {
border: 1px solid var(--gray-light);
border-radius: 6px;
padding: 12px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
}
.option-card:hover {
border-color: var(--primary);
background-color: rgba(108, 92, 231, 0.05);
}
.option-card.selected {
border-color: var(--primary);
background-color: rgba(108, 92, 231, 0.1);
}
.option-icon {
font-size: 24px;
margin-bottom: 5px;
color: var(--primary);
}
.option-title {
font-size: 14px;
font-weight: 500;
}
.preview-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: all 0.3s;
}
.preview-modal.active {
opacity: 1;
pointer-events: all;
}
.preview-content {
background-color: white;
border-radius: 10px;
width: 80%;
max-width: 900px;
max-height: 80vh;
overflow: auto;
padding: 30px;
position: relative;
}
.preview-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.preview-title {
font-size: 22px;
font-weight: 600;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: var(--gray);
}
.code-block {
background-color: #f8f9fa;
border-radius: 6px;
padding: 15px;
font-family: 'Roboto Mono', monospace;
font-size: 14px;
overflow-x: auto;
margin-bottom: 20px;
}
.download-btn {
display: block;
width: 100%;
text-align: center;
padding: 12px;
background-color: var(--success);
color: white;
border-radius: 6px;
text-decoration: none;
font-weight: 500;
margin-top: 20px;
}
.download-btn:hover {
background-color: #00a884;
}
.badge {
padding: 3px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
display: inline-block;
}
.badge-primary {
background-color: var(--primary);
color: white;
}
.badge-secondary {
background-color: var(--secondary);
color: var(--dark);
}
.badge-danger {
background-color: var(--danger);
color: white;
}
.badge-warning {
background-color: var(--warning);
color: var(--dark);
}
.tabs {
display: flex;
border-bottom: 1px solid var(--gray-light);
margin-bottom: 20px;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
font-weight: 500;
}
.tab.active {
border-bottom-color: var(--primary);
color: var(--primary);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.node-options {
position: absolute;
background: white;
border: 1px solid var(--gray-light);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
z-index: 100;
width: 200px;
display: none;
}
.node-option {
padding: 10px;
cursor: pointer;
transition: background 0.2s;
}
.node-option:hover {
background: var(--gray-light);
}
.node-option i {
margin-right: 8px;
color: var(--primary);
}
.condition-branch {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.condition-node {
background-color: var(--warning);
color: var(--dark);
padding: 15px;
border-radius: 8px;
width: 250px;
margin: 0 auto;
position: relative;
}
.condition-branches {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 20px;
position: relative;
}
.condition-branch-label {
position: absolute;
top: -20px;
background: white;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
border: 1px solid var(--gray-light);
}
.branch {
position: relative;
width: 45%;
}
.branch:before {
content: '';
position: absolute;
top: -20px;
left: 50%;
width: 2px;
height: 20px;
background-color: var(--gray-light);
}
@media (max-width: 768px) {
.main {
grid-template-columns: 1fr;
}
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<div class="container">
<header>
<div class="logo">
<i class="fas fa-robot logo-icon"></i>
<span>Baileys Command Generator</span>
</div>
<button class="btn btn-primary" id="newCommandBtn">
<i class="fas fa-plus"></i> New Command
</button>
</header>
<div class="main">
<div class="sidebar">
<div class="sidebar-title">
<span>Your Commands</span>
<i class="fas fa-sync-alt" id="refreshCommands" style="cursor: pointer;"></i>
</div>
<ul class="command-list" id="commandList">
<!-- Commands will be loaded here dynamically -->
</ul>
</div>
<div class="command-builder">
<div class="builder-header">
<div class="builder-title" id="commandTitle">New Command</div>
<div>
<button class="btn btn-outline btn-sm" id="saveCommandBtn">
<i class="fas fa-save"></i> Save
</button>
<button class="btn btn-primary btn-sm" id="previewBtn">
<i class="fas fa-eye"></i> Preview
</button>
</div>
</div>
<div class="tabs">
<div class="tab active" data-tab="flow">Flow</div>
<div class="tab" data-tab="settings">Settings</div>
<div class="tab" data-tab="packages">Packages</div>
</div>
<div class="tab-content active" id="flowTab">
<div class="flow-container" id="flowContainer">
<div class="flow-start">
<i class="fas fa-play"></i> Command Start
</div>
<!-- Nodes will be added here dynamically -->
</div>
<div class="btn-group">
<button class="btn btn-primary" id="addStepBtn">
<i class="fas fa-plus"></i> Add Step
</button>
<button class="btn btn-outline" id="addConditionBtn">
<i class="fas fa-share-alt"></i> Add Condition
</button>
</div>
<div class="node-options" id="nodeOptions">
<div class="node-option" data-type="trigger">
<i class="fas fa-bolt"></i> Trigger
</div>
<div class="node-option" data-type="response">
<i class="fas fa-reply"></i> Response
</div>
<div class="node-option" data-type="api">
<i class="fas fa-plug"></i> API Call
</div>
<div class="node-option" data-type="media">
<i class="fas fa-image"></i> Media Upload
</div>
<div class="node-option" data-type="database">
<i class="fas fa-database"></i> Database Operation
</div>
<div class="node-option" data-type="delay">
<i class="fas fa-clock"></i> Delay
</div>
</div>
</div>
<div class="tab-content" id="settingsTab">
<div class="form-group">
<label class="form-label">Command Name:</label>
<input type="text" class="form-control" id="commandName" placeholder="myCommand">
</div>
<div class="form-group">
<label class="form-label">Command Description:</label>
<textarea class="form-control" id="commandDescription" rows="3" placeholder="What this command does"></textarea>
</div>
<div class="form-group">
<label class="form-label">Required Permissions:</label>
<div class="options-grid" id="permissionOptions">
<div class="option-card selected" data-value="everyone">
<div class="option-icon">
<i class="fas fa-user"></i>
</div>
<div class="option-title">Everyone</div>
</div>
<div class="option-card" data-value="admin">
<div class="option-icon">
<i class="fas fa-user-shield"></i>
</div>
<div class="option-title">Group Admin</div>
</div>
<div class="option-card" data-value="owner">
<div class="option-icon">
<i class="fas fa-crown"></i>
</div>
<div class="option-title">Bot Owner</div>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">Works in:</label>
<div class="options-grid" id="contextOptions">
<div class="option-card selected" data-value="groups">
<div class="option-icon">
<i class="fas fa-comments"></i>
</div>
<div class="option-title">Groups</div>
</div>
<div class="option-card selected" data-value="private">
<div class="option-icon">
<i class="fas fa-user"></i>
</div>
<div class="option-title">Private</div>
</div>
</div>
</div>
</div>
<div class="tab-content" id="packagesTab">
<div class="form-group">
<label class="form-label">Required Packages:</label>
<div style="margin-top: 10px;" id="packageOptions">
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<input type="checkbox" id="axios" checked style="margin-right: 10px;">
<label for="axios">axios - For HTTP requests</label>
</div>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<input type="checkbox" id="formData" style="margin-right: 10px;">
<label for="formData">form-data - For multipart form data</label>
</div>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<input type="checkbox" id="fs" checked style="margin-right: 10px;">
<label for="fs">fs - File system operations</label>
</div>
<div style="display: flex; align-items: center; margin-bottom: 10px;">
<input type="checkbox" id="path" checked style="margin-right: 10px;">
<label for="path">path - Path utilities</label>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">Custom Package (npm):</label>
<div style="display: flex; gap: 10px; margin-top: 10px;">
<input type="text" class="form-control" id="customPackage" placeholder="package-name">
<button class="btn btn-primary btn-sm" id="addPackageBtn">Add</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="preview-modal" id="previewModal">
<div class="preview-content">
<div class="preview-header">
<div class="preview-title">Command Preview</div>
<button class="close-btn" id="closePreview">&times;</button>
</div>
<div class="tabs">
<div class="tab active" data-preview-tab="code">Code</div>
<div class="tab" data-preview-tab="info">Information</div>
<div class="tab" data-preview-tab="test">Test Command</div>
</div>
<div class="tab-content active" id="previewCodeTab">
<div class="code-block" id="generatedCode">
<!-- Code will be generated here -->
</div>
<a href="#" class="download-btn" id="downloadBtn">
<i class="fas fa-download"></i> Download Command
</a>
</div>
<div class="tab-content" id="previewInfoTab">
<div style="margin-bottom: 20px;">
<h3 style="margin-bottom: 10px;">Command Information</h3>
<p><strong>Name:</strong> <span id="infoName"></span></p>
<p><strong>Description:</strong> <span id="infoDescription"></span></p>
<p><strong>Trigger:</strong> <span id="infoTrigger"></span></p>
<p><strong>Response:</strong> <span id="infoResponse"></span></p>
</div>
<div>
<h3 style="margin-bottom: 10px;">Required Packages</h3>
<ul style="list-style-type: none;" id="infoPackages">
<!-- Packages will be listed here -->
</ul>
</div>
</div>
<div class="tab-content" id="previewTestTab">
<div class="form-group">
<label class="form-label">Test Command:</label>
<input type="text" class="form-control" id="testCommandInput" placeholder="Type your command here">
</div>
<button class="btn btn-primary" id="runTestBtn">Run Test</button>
<div class="form-group" style="margin-top: 20px;">
<label class="form-label">Bot Response:</label>
<div class="code-block" id="testOutput" style="min-height: 100px;">
<!-- Test output will appear here -->
</div>
</div>
</div>
</div>
</div>
<script>
// Command storage
let commands = JSON.parse(localStorage.getItem('baileysCommands')) || [];
let currentCommand = null;
let selectedNode = null;
// DOM elements
const commandList = document.getElementById('commandList');
const commandTitle = document.getElementById('commandTitle');
const flowContainer = document.getElementById('flowContainer');
const nodeOptions = document.getElementById('nodeOptions');
const addStepBtn = document.getElementById('addStepBtn');
const addConditionBtn = document.getElementById('addConditionBtn');
const newCommandBtn = document.getElementById('newCommandBtn');
const saveCommandBtn = document.getElementById('saveCommandBtn');
const previewBtn = document.getElementById('previewBtn');
const closePreview = document.getElementById('closePreview');
const previewModal = document.getElementById('previewModal');
const generatedCode = document.getElementById('generatedCode');
const downloadBtn = document.getElementById('downloadBtn');
const refreshCommands = document.getElementById('refreshCommands');
const commandName = document.getElementById('commandName');
const commandDescription = document.getElementById('commandDescription');
const permissionOptions = document.getElementById('permissionOptions');
const contextOptions = document.getElementById('contextOptions');
const packageOptions = document.getElementById('packageOptions');
const customPackage = document.getElementById('customPackage');
const addPackageBtn = document.getElementById('addPackageBtn');
const infoName = document.getElementById('infoName');
const infoDescription = document.getElementById('infoDescription');
const infoTrigger = document.getElementById('infoTrigger');
const infoResponse = document.getElementById('infoResponse');
const infoPackages = document.getElementById('infoPackages');
const testCommandInput = document.getElementById('testCommandInput');
const runTestBtn = document.getElementById('runTestBtn');
const testOutput = document.getElementById('testOutput');
// Initialize the app
function init() {
loadCommands();
setupEventListeners();
// Create a default command if none exist
if (commands.length === 0) {
createNewCommand();
} else {
loadCommand(commands[0].id);
}
}
// Load all commands into the sidebar
function loadCommands() {
commandList.innerHTML = '';
commands.forEach(cmd => {
const li = document.createElement('li');
li.className = 'command-item';
li.dataset.id = cmd.id;
li.innerHTML = `
<span>${cmd.name || 'Unnamed Command'}</span>
<span class="badge">${cmd.type || 'Basic'}</span>
`;
li.addEventListener('click', () => loadCommand(cmd.id));
commandList.appendChild(li);
});
// Highlight the current command if it exists
if (currentCommand) {
document.querySelectorAll('.command-item').forEach(item => {
item.classList.toggle('active', item.dataset.id === currentCommand.id);
});
}
}
// Load a specific command
function loadCommand(commandId) {
const command = commands.find(c => c.id === commandId);
if (!command) return;
currentCommand = command;
commandTitle.textContent = command.name || 'New Command';
commandName.value = command.name || '';
commandDescription.value = command.description || '';
// Update UI to reflect the loaded command
updateCommandUI();
// Highlight the selected command in the sidebar
document.querySelectorAll('.command-item').forEach(item => {
item.classList.toggle('active', item.dataset.id === commandId);
});
}
// Update the UI based on the current command
function updateCommandUI() {
if (!currentCommand) return;
// Clear the flow container except for the start node
flowContainer.innerHTML = `
<div class="flow-start">
<i class="fas fa-play"></i> Command Start
</div>
`;
// Rebuild the flow from the command data
if (currentCommand.flow && currentCommand.flow.length > 0) {
currentCommand.flow.forEach((node, index) => {
addNodeToFlow(node, index);
});
}
}
// Create a new command
function createNewCommand() {
const newCommand = {
id: generateId(),
name: 'New Command',
description: '',
type: 'Basic',
permissions: ['everyone'],
contexts: ['groups', 'private'],
packages: ['axios', 'fs', 'path'],
flow: []
};
commands.push(newCommand);
currentCommand = newCommand;
saveCommands();
loadCommands();
loadCommand(newCommand.id);
}
// Save all commands to localStorage
function saveCommands() {
localStorage.setItem('baileysCommands', JSON.stringify(commands));
}
// Add a node to the flow
function addNodeToFlow(nodeData, index) {
const nodeTypes = {
trigger: {
title: 'Trigger',
icon: 'bolt',
template: `
<div class="form-group">
<label class="form-label">When user sends:</label>
<select class="select-control trigger-type">
<option value="prefix" ${nodeData.triggerType === 'prefix' ? 'selected' : ''}>Prefix command (!ping)</option>
<option value="exact" ${nodeData.triggerType === 'exact' ? 'selected' : ''}>Exact match (ping)</option>
<option value="regex" ${nodeData.triggerType === 'regex' ? 'selected' : ''}>Regex pattern</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Command:</label>
<input type="text" class="form-control trigger-command" value="${nodeData.command || ''}">
</div>
${nodeData.triggerType === 'regex' ? `
<div class="form-group">
<label class="form-label">Regex Pattern:</label>
<input type="text" class="form-control trigger-regex" value="${nodeData.regex || ''}">
</div>
` : ''}
`
},
response: {
title: 'Response',
icon: 'reply',
template: `
<div class="form-group">
<label class="form-label">Response Type:</label>
<select class="select-control response-type">
<option value="text" ${nodeData.responseType === 'text' ? 'selected' : ''}>Text</option>
<option value="image" ${nodeData.responseType === 'image' ? 'selected' : ''}>Image</option>
<option value="video" ${nodeData.responseType === 'video' ? 'selected' : ''}>Video</option>
<option value="buttons" ${nodeData.responseType === 'buttons' ? 'selected' : ''}>Buttons</option>
<option value="list" ${nodeData.responseType === 'list' ? 'selected' : ''}>List</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Response Content:</label>
<textarea class="form-control response-content" rows="3">${nodeData.content || ''}</textarea>
</div>
${nodeData.responseType === 'image' || nodeData.responseType === 'video' ? `
<div class="form-group">
<label class="form-label">Media URL:</label>
<input type="text" class="form-control media-url" value="${nodeData.mediaUrl || ''}">
</div>
` : ''}
`
},
api: {
title: 'API Call',
icon: 'plug',
template: `
<div class="form-group">
<label class="form-label">API URL:</label>
<input type="text" class="form-control api-url" value="${nodeData.apiUrl || ''}">
</div>
<div class="form-group">
<label class="form-label">Method:</label>
<select class="select-control api-method">
<option value="GET" ${nodeData.method === 'GET' ? 'selected' : ''}>GET</option>
<option value="POST" ${nodeData.method === 'POST' ? 'selected' : ''}>POST</option>
<option value="PUT" ${nodeData.method === 'PUT' ? 'selected' : ''}>PUT</option>
<option value="DELETE" ${nodeData.method === 'DELETE' ? 'selected' : ''}>DELETE</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Request Data (JSON):</label>
<textarea class="form-control api-data" rows="3">${nodeData.data ? JSON.stringify(nodeData.data, null, 2) : ''}</textarea>
</div>
<div class="form-group">
<label class="form-label">Response Variable:</label>
<input type="text" class="form-control api-var" value="${nodeData.varName || 'apiResponse'}">
</div>
`
},
condition: {
title: 'Condition',
icon: 'code-branch',
template: `
<div class="form-group">
<label class="form-label">Condition Type:</label>
<select class="select-control condition-type">
<option value="text" ${nodeData.conditionType === 'text' ? 'selected' : ''}>Text Comparison</option>
<option value="var" ${nodeData.conditionType === 'var' ? 'selected' : ''}>Variable Comparison</option>
<option value="group" ${nodeData.conditionType === 'group' ? 'selected' : ''}>Group Check</option>
<option value="admin" ${nodeData.conditionType === 'admin' ? 'selected' : ''}>Admin Check</option>
</select>
</div>
${nodeData.conditionType === 'text' ? `
<div class="form-group">
<label class="form-label">Comparison:</label>
<select class="select-control condition-compare">
<option value="equals" ${nodeData.compare === 'equals' ? 'selected' : ''}>Equals</option>
<option value="contains" ${nodeData.compare === 'contains' ? 'selected' : ''}>Contains</option>
<option value="startsWith" ${nodeData.compare === 'startsWith' ? 'selected' : ''}>Starts With</option>
<option value="endsWith" ${nodeData.compare === 'endsWith' ? 'selected' : ''}>Ends With</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Value to Compare:</label>
<input type="text" class="form-control condition-value" value="${nodeData.value || ''}">
</div>
` : ''}
${nodeData.conditionType === 'var' ? `
<div class="form-group">
<label class="form-label">Variable Name:</label>
<input type="text" class="form-control condition-var" value="${nodeData.varName || ''}">
</div>
<div class="form-group">
<label class="form-label">Comparison:</label>
<select class="select-control condition-compare">
<option value="equals" ${nodeData.compare === 'equals' ? 'selected' : ''}>Equals</option>
<option value="contains" ${nodeData.compare === 'contains' ? 'selected' : ''}>Contains</option>
<option value="greater" ${nodeData.compare === 'greater' ? 'selected' : ''}>Greater Than</option>
<option value="less" ${nodeData.compare === 'less' ? 'selected' : ''}>Less Than</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Value to Compare:</label>
<input type="text" class="form-control condition-value" value="${nodeData.value || ''}">
</div>
` : ''}
`
}
};
if (nodeData.type === 'condition') {
// Create condition node with branches
const conditionDiv = document.createElement('div');
conditionDiv.className = 'condition-branch';
conditionDiv.innerHTML = `
<div class="condition-node">
<div class="flow-node-header">
<div class="flow-node-title">
<i class="fas fa-code-branch"></i> Condition
</div>
<div class="flow-node-actions">
<button class="btn btn-sm config-btn" style="padding: 0 5px;">
<i class="fas fa-cog"></i>
</button>
<button class="btn btn-sm delete-btn" style="padding: 0 5px;">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="flow-node-content">
${nodeTypes.condition.template}
</div>
</div>
<div class="condition-branches">
<div class="branch">
<div class="condition-branch-label">If True</div>
${nodeData.trueBranch && nodeData.trueBranch.length > 0 ?
nodeData.trueBranch.map(n => addNodeToFlow(n)).join('') :
'<div style="height: 50px;"></div>'}
</div>
<div class="branch">
<div class="condition-branch-label">If False</div>
${nodeData.falseBranch && nodeData.falseBranch.length > 0 ?
nodeData.falseBranch.map(n => addNodeToFlow(n)).join('') :
'<div style="height: 50px;"></div>'}
</div>
</div>
`;
flowContainer.appendChild(conditionDiv);
} else {
// Create regular node
const nodeDiv = document.createElement('div');
nodeDiv.className = 'flow-node';
nodeDiv.dataset.index = index;
nodeDiv.dataset.type = nodeData.type;
nodeDiv.innerHTML = `
<div class="flow-node-header">
<div class="flow-node-title">
<i class="fas fa-${nodeTypes[nodeData.type]?.icon || 'code'}"></i> ${nodeTypes[nodeData.type]?.title || nodeData.type}
</div>
<div class="flow-node-actions">
<button class="btn btn-sm config-btn" style="padding: 0 5px;">
<i class="fas fa-cog"></i>
</button>
<button class="btn btn-sm delete-btn" style="padding: 0 5px;">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="flow-node-content">
${nodeTypes[nodeData.type]?.template || ''}
</div>
`;
flowContainer.appendChild(nodeDiv);
}
// Add event listeners to the new node
addNodeEventListeners();
}
// Add event listeners to nodes
function addNodeEventListeners() {
document.querySelectorAll('.config-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
// Handle node configuration
});
});
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const node = btn.closest('.flow-node');
if (node) {
const index = node.dataset.index;
if (currentCommand.flow[index]) {
currentCommand.flow.splice(index, 1);
saveCurrentCommand();
updateCommandUI();
}
}
});
});
// Add change listeners for form elements
document.querySelectorAll('.flow-node-content select, .flow-node-content input, .flow-node-content textarea').forEach(el => {
el.addEventListener('change', updateNodeData);
el.addEventListener('input', updateNodeData);
});
}
// Update node data when form elements change
function updateNodeData(e) {
const node = e.target.closest('.flow-node');
if (!node) return;
const index = node.dataset.index;
const type = node.dataset.type;
if (!currentCommand.flow[index]) {
currentCommand.flow[index] = { type };
}
// Update node data based on type
switch (type) {
case 'trigger':
currentCommand.flow[index].triggerType = node.querySelector('.trigger-type').value;
currentCommand.flow[index].command = node.querySelector('.trigger-command').value;
if (currentCommand.flow[index].triggerType === 'regex') {
currentCommand.flow[index].regex = node.querySelector('.trigger-regex')?.value || '';
}
break;
case 'response':
currentCommand.flow[index].responseType = node.querySelector('.response-type').value;
currentCommand.flow[index].content = node.querySelector('.response-content').value;
if (['image', 'video'].includes(currentCommand.flow[index].responseType)) {
currentCommand.flow[index].mediaUrl = node.querySelector('.media-url')?.value || '';
}
break;
case 'api':
currentCommand.flow[index].apiUrl = node.querySelector('.api-url').value;
currentCommand.flow[index].method = node.querySelector('.api-method').value;
try {
currentCommand.flow[index].data = JSON.parse(node.querySelector('.api-data').value);
} catch (e) {
currentCommand.flow[index].data = {};
}
currentCommand.flow[index].varName = node.querySelector('.api-var').value;
break;
case 'condition':
currentCommand.flow[index].conditionType = node.querySelector('.condition-type').value;
currentCommand.flow[index].compare = node.querySelector('.condition-compare')?.value || '';
currentCommand.flow[index].value = node.querySelector('.condition-value')?.value || '';
currentCommand.flow[index].varName = node.querySelector('.condition-var')?.value || '';
break;
}
saveCurrentCommand();
}
// Save the current command
function saveCurrentCommand() {
if (!currentCommand) return;
// Update command metadata
currentCommand.name = commandName.value;
currentCommand.description = commandDescription.value;
currentCommand.permissions = Array.from(permissionOptions.querySelectorAll('.option-card.selected')).map(card => card.dataset.value);
currentCommand.contexts = Array.from(contextOptions.querySelectorAll('.option-card.selected')).map(card => card.dataset.value);
// Update packages
currentCommand.packages = ['fs', 'path']; // Always include these
if (packageOptions.querySelector('#axios').checked) currentCommand.packages.push('axios');
if (packageOptions.querySelector('#formData').checked) currentCommand.packages.push('form-data');
// Update command title
commandTitle.textContent = currentCommand.name || 'New Command';
// Save to storage
const index = commands.findIndex(c => c.id === currentCommand.id);
if (index !== -1) {
commands[index] = currentCommand;
saveCommands();
loadCommands();
}
}
// Generate a unique ID
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// Setup event listeners
function setupEventListeners() {
// Tab switching
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
const tabId = tab.getAttribute('data-tab');
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// Deactivate all tabs
document.querySelectorAll('.tab').forEach(t => {
t.classList.remove('active');
});
// Activate selected tab
tab.classList.add('active');
document.getElementById(tabId + 'Tab').classList.add('active');
});
});
// Preview tab switching
document.querySelectorAll('[data-preview-tab]').forEach(tab => {
tab.addEventListener('click', () => {
const tabId = tab.getAttribute('data-preview-tab');
// Hide all preview tab contents
document.querySelectorAll('.preview-content .tab-content').forEach(content => {
content.classList.remove('active');
});
// Deactivate all preview tabs
document.querySelectorAll('[data-preview-tab]').forEach(t => {
t.classList.remove('active');
});
// Activate selected preview tab
tab.classList.add('active');
document.getElementById('preview' + tabId.charAt(0).toUpperCase() + tabId.slice(1) + 'Tab').classList.add('active');
});
});
// Option card selection
document.querySelectorAll('.option-card').forEach(card => {
card.addEventListener('click', () => {
const parent = card.parentElement;
if (parent.classList.contains('options-grid')) {
// For single selection groups
if (!card.classList.contains('selected')) {
parent.querySelectorAll('.option-card').forEach(c => {
c.classList.remove('selected');
});
card.classList.add('selected');
saveCurrentCommand();
}
} else {
// For multiple selection
card.classList.toggle('selected');
saveCurrentCommand();
}
});
});
// Add step button
addStepBtn.addEventListener('click', (e) => {
const rect = addStepBtn.getBoundingClientRect();
nodeOptions.style.display = 'block';
nodeOptions.style.top = `${rect.bottom + 5}px`;
nodeOptions.style.left = `${rect.left}px`;
selectedNode = null;
});
// Add condition button
addConditionBtn.addEventListener('click', () => {
if (!currentCommand) return;
const newCondition = {
type: 'condition',
conditionType: 'text',
compare: 'equals',
value: '',
trueBranch: [],
falseBranch: []
};
currentCommand.flow.push(newCondition);
saveCurrentCommand();
updateCommandUI();
});
// Node options
document.querySelectorAll('.node-option').forEach(option => {
option.addEventListener('click', () => {
const type = option.dataset.type;
nodeOptions.style.display = 'none';
if (!currentCommand) return;
const newNode = { type };
// Set defaults based on type
switch (type) {
case 'trigger':
newNode.triggerType = 'prefix';
newNode.command = '';
break;
case 'response':
newNode.responseType = 'text';
newNode.content = '';
break;
case 'api':
newNode.apiUrl = '';
newNode.method = 'GET';
newNode.data = {};
newNode.varName = 'apiResponse';
break;
}
if (selectedNode !== null && currentCommand.flow[selectedNode]) {
// Add after selected node
currentCommand.flow.splice(selectedNode + 1, 0, newNode);
} else {
// Add to end
currentCommand.flow.push(newNode);
}
saveCurrentCommand();
updateCommandUI();
});
});
// Click outside node options to close
document.addEventListener('click', (e) => {
if (!nodeOptions.contains(e.target) && e.target !== addStepBtn) {
nodeOptions.style.display = 'none';
}
});
// Select node on click
flowContainer.addEventListener('click', (e) => {
const node = e.target.closest('.flow-node');
if (node) {
document.querySelectorAll('.flow-node').forEach(n => {
n.style.borderColor = 'var(--primary)';
});
node.style.borderColor = 'var(--secondary)';
selectedNode = parseInt(node.dataset.index);
}
});
// New command button
newCommandBtn.addEventListener('click', createNewCommand);
// Save command button
saveCommandBtn.addEventListener('click', saveCurrentCommand);
// Preview button
previewBtn.addEventListener('click', showPreview);
// Close preview
closePreview.addEventListener('click', () => {
previewModal.classList.remove('active');
});
// Refresh commands
refreshCommands.addEventListener('click', loadCommands);
// Add package button
addPackageBtn.addEventListener('click', () => {
const pkg = customPackage.value.trim();
if (pkg) {
const div = document.createElement('div');
div.style.display = 'flex';
div.style.alignItems = 'center';
div.style.marginBottom = '10px';
div.innerHTML = `
<input type="checkbox" id="${pkg}" checked style="margin-right: 10px;">
<label for="${pkg}">${pkg} - Custom package</label>
`;
packageOptions.appendChild(div);
customPackage.value = '';
saveCurrentCommand();
}
});
// Download button
downloadBtn.addEventListener('click', (e) => {
e.preventDefault();
downloadCommand();
});
// Run test button
runTestBtn.addEventListener('click', runTest);
}
// Show command preview
function showPreview() {
if (!currentCommand) return;
// Generate code
generateCode();
// Update info tab
infoName.textContent = currentCommand.name || 'Unnamed Command';
infoDescription.textContent = currentCommand.description || 'No description';
// Find trigger command
const triggerNode = currentCommand.flow.find(node => node.type === 'trigger');
if (triggerNode) {
let triggerText = '';
if (triggerNode.triggerType === 'prefix') {
triggerText = `!${triggerNode.command}`;
} else if (triggerNode.triggerType === 'exact') {
triggerText = triggerNode.command;
} else if (triggerNode.triggerType === 'regex') {
triggerText = `Regex: ${triggerNode.regex}`;
}
infoTrigger.textContent = triggerText;
} else {
infoTrigger.textContent = 'No trigger defined';
}
// Find response
const responseNode = currentCommand.flow.find(node => node.type === 'response');
if (responseNode) {
infoResponse.textContent = responseNode.content || 'No content';
} else {
infoResponse.textContent = 'No response defined';
}
// Update packages list
infoPackages.innerHTML = '';
currentCommand.packages.forEach(pkg => {
const li = document.createElement('li');
li.innerHTML = `<span class="badge badge-primary">${pkg}</span> - ${getPackageDescription(pkg)}`;
infoPackages.appendChild(li);
});
// Show modal
previewModal.classList.add('active');
}
// Get package description
function getPackageDescription(pkg) {
const descriptions = {
'axios': 'For HTTP requests',
'form-data': 'For multipart form data',
'fs': 'File system operations',
'path': 'Path utilities'
};
return descriptions[pkg] || 'Custom package';
}
// Generate JavaScript code from the command
function generateCode() {
if (!currentCommand) return '';
let code = `const config = require('../config');\n`;
// Add required packages
currentCommand.packages.forEach(pkg => {
if (pkg === 'fs' || pkg === 'path') {
code += `const ${pkg} = require('${pkg}');\n`;
} else {
code += `const ${pkg} = require('${pkg}');\n`;
}
});
code += `\nconst ${currentCommand.name || 'myCommand'} = async (m, sock) => {\n`;
code += ` try {\n`;
// Add permission checks
if (currentCommand.permissions.includes('admin')) {
code += ` // Check if user is admin\n`;
code += ` if (!m.isGroup) return;\n`;
code += ` const participants = await sock.groupMetadata(m.key.remoteJid);\n`;
code += ` const isAdmin = participants.participants.find(p => p.id === m.key.participant)?.admin === 'admin';\n`;
code += ` if (!isAdmin) {\n`;
code += ` await sock.sendMessage(m.key.remoteJid, { text: 'You need to be an admin to use this command' }, { quoted: m });\n`;
code += ` return;\n`;
code += ` }\n\n`;
} else if (currentCommand.permissions.includes('owner')) {
code += ` // Check if user is owner\n`;
code += ` const isOwner = m.key.participant === config.ownerNumber;\n`;
code += ` if (!isOwner) {\n`;
code += ` await sock.sendMessage(m.key.remoteJid, { text: 'Only the bot owner can use this command' }, { quoted: m });\n`;
code += ` return;\n`;
code += ` }\n\n`;
}
// Add context checks
if (currentCommand.contexts.length === 1) {
if (currentCommand.contexts[0] === 'groups') {
code += ` // Command only works in groups\n`;
code += ` if (!m.isGroup) {\n`;
code += ` await sock.sendMessage(m.key.remoteJid, { text: 'This command only works in groups' }, { quoted: m });\n`;
code += ` return;\n`;
code += ` }\n\n`;
} else if (currentCommand.contexts[0] === 'private') {
code += ` // Command only works in private chats\n`;
code += ` if (m.isGroup) {\n`;
code += ` await sock.sendMessage(m.key.remoteJid, { text: 'This command only works in private chats' }, { quoted: m });\n`;
code += ` return;\n`;
code += ` }\n\n`;
}
}
// Process flow nodes
currentCommand.flow.forEach(node => {
switch (node.type) {
case 'trigger':
if (node.triggerType === 'prefix') {
code += ` const prefix = config.prefix || '!';\n`;
code += ` const text = m.message?.conversation || m.message?.extendedTextMessage?.text || '';\n`;
code += ` \n`;
code += ` if (!text.toLowerCase().startsWith(\`\${prefix}${node.command.toLowerCase()}\`)) return;\n`;
code += ` const args = text.slice(prefix.length + '${node.command}'.length).trim().split(/\\s+/);\n\n`;
} else if (node.triggerType === 'exact') {
code += ` const text = m.message?.conversation || m.message?.extendedTextMessage?.text || '';\n`;
code += ` \n`;
code += ` if (text.toLowerCase() !== '${node.command.toLowerCase()}') return;\n\n`;
} else if (node.triggerType === 'regex') {
code += ` const text = m.message?.conversation || m.message?.extendedTextMessage?.text || '';\n`;
code += ` \n`;
code += ` if (!new RegExp(\`${node.regex}\`, 'i').test(text)) return;\n\n`;
}
break;
case 'response':
if (node.responseType === 'text') {
code += ` await sock.sendMessage(m.key.remoteJid, {\n`;
code += ` text: \`${node.content.replace(/`/g, '\\`')}\`\n`;
code += ` }, { quoted: m });\n\n`;
} else if (node.responseType === 'image' || node.responseType === 'video') {
code += ` // Send ${node.responseType}\n`;
code += ` const media = await sock.downloadMediaMessage(m);\n`;
code += ` await sock.sendMessage(m.key.remoteJid, {\n`;
code += ` ${node.responseType}: media,\n`;
code += ` caption: \`${node.content.replace(/`/g, '\\`')}\`\n`;
code += ` }, { quoted: m });\n\n`;
} else if (node.responseType === 'buttons') {
code += ` // Send buttons\n`;
code += ` const buttons = [\n`;
code += ` { buttonId: 'id1', buttonText: { displayText: 'Button 1' }, type: 1 },\n`;
code += ` { buttonId: 'id2', buttonText: { displayText: 'Button 2' }, type: 1 }\n`;
code += ` ];\n\n`;
code += ` await sock.sendMessage(m.key.remoteJid, {\n`;
code += ` text: \`${node.content.replace(/`/g, '\\`')}\`,\n`;
code += ` footer: 'Select an option',\n`;
code += ` buttons: buttons,\n`;
code += ` headerType: 1\n`;
code += ` }, { quoted: m });\n\n`;
} else if (node.responseType === 'list') {
code += ` // Send list\n`;
code += ` const sections = [\n`;
code += ` {\n`;
code += ` title: "Section 1",\n`;
code += ` rows: [\n`;
code += ` { title: "Option 1", rowId: "option1" },\n`;
code += ` { title: "Option 2", rowId: "option2" }\n`;
code += ` ]\n`;
code += ` }\n`;
code += ` ];\n\n`;
code += ` await sock.sendMessage(m.key.remoteJid, {\n`;
code += ` text: \`${node.content.replace(/`/g, '\\`')}\`,\n`;
code += ` footer: 'Select an option',\n`;
code += ` title: 'Menu',\n`;
code += ` buttonText: 'View Options',\n`;
code += ` sections: sections\n`;
code += ` }, { quoted: m });\n\n`;
}
break;
case 'api':
code += ` // API call\n`;
code += ` const ${node.varName} = await axios({\n`;
code += ` method: '${node.method}',\n`;
code += ` url: '${node.apiUrl}',\n`;
if (node.method !== 'GET' && node.data) {
code += ` data: ${JSON.stringify(node.data, null, 4).split('\n').join('\n ')},\n`;
}
code += ` });\n\n`;
break;
case 'condition':
code += ` // Condition check\n`;
if (node.conditionType === 'text') {
code += ` const text = m.message?.conversation || m.message?.extendedTextMessage?.text || '';\n`;
if (node.compare === 'equals') {
code += ` if (text === '${node.value}') {\n`;
} else if (node.compare === 'contains') {
code += ` if (text.includes('${node.value}')) {\n`;
} else if (node.compare === 'startsWith') {
code += ` if (text.startsWith('${node.value}')) {\n`;
} else if (node.compare === 'endsWith') {
code += ` if (text.endsWith('${node.value}')) {\n`;
}
} else if (node.conditionType === 'var') {
code += ` if (${node.varName} ${getComparisonOperator(node.compare)} '${node.value}') {\n`;
} else if (node.conditionType === 'group') {
code += ` if (m.isGroup) {\n`;
} else if (node.conditionType === 'admin') {
code += ` const participants = await sock.groupMetadata(m.key.remoteJid);\n`;
code += ` const isAdmin = participants.participants.find(p => p.id === m.key.participant)?.admin === 'admin';\n`;
code += ` if (isAdmin) {\n`;
}
// True branch
if (node.trueBranch && node.trueBranch.length > 0) {
// Would generate code for true branch here
code += ` // True branch actions\n`;
} else {
code += ` // No actions for true branch\n`;
}
code += ` } else {\n`;
// False branch
if (node.falseBranch && node.falseBranch.length > 0) {
// Would generate code for false branch here
code += ` // False branch actions\n`;
} else {
code += ` // No actions for false branch\n`;
}
code += ` }\n\n`;
break;
}
});
code += ` } catch (error) {\n`;
code += ` console.error('Error in ${currentCommand.name || 'command'}:', error);\n`;
code += ` await sock.sendMessage(m.key.remoteJid, { text: 'An error occurred while processing your command' }, { quoted: m });\n`;
code += ` }\n`;
code += `};\n\n`;
code += `module.exports = ${currentCommand.name || 'myCommand'};\n`;
generatedCode.innerHTML = `<pre>${escapeHtml(code)}</pre>`;
return code;
}
// Get comparison operator for condition
function getComparisonOperator(compare) {
switch (compare) {
case 'equals': return '===';
case 'contains': return '.includes';
case 'greater': return '>';
case 'less': return '<';
default: return '===';
}
}
// Escape HTML for code display
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
// Download command as file
function downloadCommand() {
if (!currentCommand) return;
const code = generateCode();
const blob = new Blob([code], { type: 'text/javascript' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${currentCommand.name || 'command'}.js`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Run test command
function runTest() {
const command = testCommandInput.value.trim();
if (!command) return;
// Simple test simulation
testOutput.innerHTML = 'Testing command...\n\n';
// Find trigger node
const triggerNode = currentCommand.flow.find(node => node.type === 'trigger');
if (!triggerNode) {
testOutput.innerHTML += 'Error: No trigger defined in command';
return;
}
// Check if command matches trigger
let matches = false;
if (triggerNode.triggerType === 'prefix') {
matches = command.toLowerCase().startsWith(`!${triggerNode.command.toLowerCase()}`);
} else if (triggerNode.triggerType === 'exact') {
matches = command.toLowerCase() === triggerNode.command.toLowerCase();
} else if (triggerNode.triggerType === 'regex') {
try {
matches = new RegExp(triggerNode.regex, 'i').test(command);
} catch (e) {
testOutput.innerHTML += `Regex error: ${e.message}`;
return;
}
}
if (!matches) {
testOutput.innerHTML += 'Command did not match trigger pattern';
return;
}
// Find response node
const responseNode = currentCommand.flow.find(node => node.type === 'response');
if (responseNode) {
if (responseNode.responseType === 'text') {
testOutput.innerHTML += `Bot would reply with:\n\n${responseNode.content}`;
} else {
testOutput.innerHTML += `Bot would send ${responseNode.responseType} response`;
}
} else {
testOutput.innerHTML += 'Command matched but no response defined';
}
}
// Initialize the application
init();
</script>
</body>
</html>