您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Create Duolingo accounts
// ==UserScript== // @name Duolingo Account Creator // @namespace http://tampermonkey.net/ // @version 1.2 // @description Create Duolingo accounts // @author You // @match https://www.duolingo.com/* // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @require https://code.jquery.com/jquery-3.6.0.min.js // @connect duolingo.com // @connect www.duolingo.com // @connect https://www.duolingo.com/2017-06-30/user // @discord https://discord.gg/kfct8FuHmW // @connect api.ipify.org // @license MIT // ==/UserScript== (function() { 'use strict'; const MAX_ACCOUNTS = 5000; const style = document.createElement('style'); style.textContent = ` :root { --bg-color: #2B2D31; --panel-color: #3E4043; --text-color: #E6E6E6; --border-color: #4D4F53; --primary-color: #58CC02; --primary-hover: #61E002; --primary-shadow: #46A302; --info-color: #1CB0F6; --warning-color: #FFC107; --error-color: #FF6B6B; --secondary-text: #B5B5B5; --button-bg: #3E4043; --button-text: #E6E6E6; --button-hover-text: #FFFFFF; --input-bg: #3E4043; } :root.light-mode { --bg-color: #FFFFFF; --panel-color: #F5F5F5; --text-color: #333333; --border-color: #E0E0E0; --primary-color: #58CC02; --primary-hover: #61E002; --primary-shadow: #46A302; --info-color: #1CB0F6; --warning-color: #FFC107; --error-color: #FF6B6B; --secondary-text: #666666; --button-bg: #E0E0E0; --button-text: #333333; --button-hover-text: #FFFFFF; --input-bg: #F5F5F5; } #duolingo-account-creator * { box-sizing: border-box; } .account-row:hover .delete-account-btn { display: inline-block; } .delete-account-btn { display: none; background-color: var(--error-color) !important; color: white !important; border: none; border-radius: 4px; padding: 2px 6px; font-size: 11px; cursor: pointer; margin-left: 6px; } .delete-account-btn:hover { background-color: #ff5252 !important; } `; document.head.appendChild(style); const state = { accounts: [], isCreating: false, notifications: [], activeMenu: null, userIP: null, settings: JSON.parse(localStorage.getItem('duolingoAccountCreatorSettings')) || { saveAccounts: true, randomEmail: true, randomPassword: true, creationSpeed: 'medium', theme: 'dark' }, createdCount: 0, totalCount: 0 }; const elements = { mainContainer: null, settingsPanel: null, logsPanel: null, notificationsPanel: null, accountProgress: null }; const utils = { async getUserIP() { try { const response = await fetch('https://api.ipify.org?format=json'); const data = await response.json(); return data.ip; } catch (error) { console.error('Error getting IP:', error); return 'unknown'; } }, applyTheme() { if (state.settings.theme === 'light') { document.documentElement.classList.add('light-mode'); } else { document.documentElement.classList.remove('light-mode'); } this.updateAllUI(); }, updateAllUI() { if (elements.mainContainer) { elements.mainContainer.style.backgroundColor = 'var(--bg-color)'; elements.mainContainer.style.borderColor = 'var(--border-color)'; elements.mainContainer.style.color = 'var(--text-color)'; } document.querySelectorAll('#duolingo-account-creator button, #duolingo-account-creator input, #duolingo-account-creator select').forEach(el => { if (el.tagName === 'BUTTON' && !el.id.includes('start-create')) { el.style.color = 'var(--button-text)'; el.style.backgroundColor = 'var(--button-bg)'; } }); }, closeAllMenus() { for (const panel in elements) { if (panel !== 'mainContainer' && elements[panel] && panel !== 'accountProgress') { elements[panel].style.display = 'none'; } } state.activeMenu = null; }, createIconButton(icon, normalColor, hoverColor, titleText = '') { const btn = document.createElement('button'); btn.innerHTML = icon; btn.title = titleText; Object.assign(btn.style, { width: '28px', height: '28px', borderRadius: '50%', border: 'none', backgroundColor: normalColor === 'transparent' ? 'transparent' : 'var(--button-bg)', color: 'var(--button-text)', display: 'flex', justifyContent: 'center', alignItems: 'center', cursor: 'pointer', transition: 'all 0.2s', fontSize: '14px', margin: '0 2px' }); btn.addEventListener('mouseover', () => { btn.style.backgroundColor = hoverColor; btn.style.color = 'var(--button-hover-text)'; }); btn.addEventListener('mouseout', () => { btn.style.backgroundColor = normalColor === 'transparent' ? 'transparent' : 'var(--button-bg)'; btn.style.color = 'var(--button-text)'; }); return btn; }, makeDraggable(element, handle) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; handle.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; element.style.top = (element.offsetTop - pos2) + "px"; element.style.left = (element.offsetLeft - pos1) + "px"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } }, generateRandomEmail() { const domainSelect = document.getElementById('email-domain'); const domain = domainSelect.value === 'custom' ? document.getElementById('custom-domain').value : domainSelect.value; const randomString = Math.random().toString(36).substring(2, 10); const randomNumber = Math.floor(Math.random() * 1000); return `${randomString}${randomNumber}@${domain}`; }, generateRandomPassword() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()'; let password = ''; for (let i = 0; i < 12; i++) { password += chars.charAt(Math.floor(Math.random() * chars.length)); } return password; }, generateRandomUsername() { const randomString = Math.random().toString(36).substring(2, 10); const randomNumber = Math.floor(Math.random() * 1000); return `user_${randomString}${randomNumber}`; }, saveSettings() { localStorage.setItem('duolingoAccountCreatorSettings', JSON.stringify(state.settings)); this.applyTheme(); }, addNotification(message, type = 'info') { const notification = { id: Date.now(), message, type, timestamp: new Date().toLocaleTimeString() }; state.notifications.unshift(notification); if (elements.notificationsPanel) { ui.updateNotificationsPanel(); } }, saveAccountsToStorage() { if (state.settings.saveAccounts) { const accountsToSave = state.accounts.map(account => ({ ...account, ip: state.userIP, createdAt: new Date().toISOString() })); localStorage.setItem('duolingoAccounts', JSON.stringify(accountsToSave)); } }, loadAccountsFromStorage() { const savedAccounts = localStorage.getItem('duolingoAccounts'); if (savedAccounts) { const accounts = JSON.parse(savedAccounts); state.accounts = accounts.filter(acc => acc.ip === state.userIP); } }, deleteAllAccounts() { state.accounts = []; localStorage.removeItem('duolingoAccounts'); ui.updateLogsTable(); utils.addNotification('All accounts deleted', 'success'); }, deleteAccount(index) { if (index >= 0 && index < state.accounts.length) { state.accounts.splice(index, 1); utils.saveAccountsToStorage(); ui.updateLogsTable(); utils.addNotification('Account deleted', 'success'); } }, updateProgressCounter() { if (elements.accountProgress) { elements.accountProgress.textContent = ` (${state.createdCount}/${state.totalCount})`; } } }; const api = { checkUsernameAvailability(username, retries = 3) { return new Promise((resolve, reject) => { const url = `https://www.duolingo.com/2017-06-30/user?username=${encodeURIComponent(username)}`; GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'Accept': 'application/json' }, onload: function(response) { if (response.status === 200) { try { const data = JSON.parse(response.responseText); resolve(data.available === true); } catch (error) { if (retries > 0) { setTimeout(() => { api.checkUsernameAvailability(username, retries - 1).then(resolve).catch(reject); }, 1000); } else { reject(new Error(`Invalid response format: ${error.message}`)); } } } else if (response.status === 429 && retries > 0) { setTimeout(() => { api.checkUsernameAvailability(username, retries - 1).then(resolve).catch(reject); }, 2000); } else { reject(new Error(`API Error: ${response.status} - ${response.statusText}`)); } }, onerror: function(error) { if (retries > 0) { setTimeout(() => { api.checkUsernameAvailability(username, retries - 1).then(resolve).catch(reject); }, 2000); } else { reject(new Error(`Network error: ${error.message || 'Unknown error'}`)); } } }); }); }, createAccount(email, username, password, retries = 3) { return new Promise((resolve, reject) => { const url = 'https://www.duolingo.com/2017-06-30/user'; const payload = { email: email, username: username, password: password, fromLanguage: 'en', learningLanguage: 'es' }; GM_xmlhttpRequest({ method: 'POST', url: url, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, data: JSON.stringify(payload), onload: function(response) { if (response.status >= 200 && response.status < 300) { resolve(response.responseText); } else if (response.status === 429 && retries > 0) { setTimeout(() => { api.createAccount(email, username, password, retries - 1).then(resolve).catch(reject); }, 2000); } else { reject(new Error(`API Error: ${response.status} - ${response.statusText}`)); } }, onerror: function(error) { if (retries > 0) { setTimeout(() => { api.createAccount(email, username, password, retries - 1).then(resolve).catch(reject); }, 2000); } else { reject(new Error(`Network error: ${error.message || 'Unknown error'}`)); } } }); }); } }; const ui = { createMainContainer() { const container = document.createElement('div'); container.id = 'duolingo-account-creator'; Object.assign(container.style, { position: 'fixed', top: '100px', left: '20px', zIndex: '9999', backgroundColor: 'var(--bg-color)', padding: '12px', borderRadius: '12px', boxShadow: '0 4px 12px rgba(0,0,0,0.3)', fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, sans-serif', width: '280px', cursor: 'move', userSelect: 'none', border: '1px solid var(--border-color)', color: 'var(--text-color)' }); const header = document.createElement('div'); header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.marginBottom = '12px'; const title = document.createElement('h3'); title.textContent = 'Account Creator'; Object.assign(title.style, { margin: '0', color: 'var(--primary-color)', fontSize: '15px', fontWeight: '600' }); const buttonGroup = document.createElement('div'); buttonGroup.style.display = 'flex'; buttonGroup.style.gap = '4px'; const settingsBtn = utils.createIconButton('⚙', 'transparent', 'var(--primary-color)', 'Settings'); settingsBtn.addEventListener('click', (e) => { e.stopPropagation(); this.showSettings(); }); const logsBtn = utils.createIconButton('📋', 'transparent', 'var(--info-color)', 'Show Logs'); logsBtn.addEventListener('click', (e) => { e.stopPropagation(); this.showLogs(); }); const notifyBtn = utils.createIconButton('🔔', 'transparent', 'var(--warning-color)', 'Notifications'); notifyBtn.addEventListener('click', (e) => { e.stopPropagation(); this.showNotifications(); }); const closeBtn = utils.createIconButton('×', 'transparent', 'var(--error-color)', 'Close'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); container.style.display = 'none'; utils.closeAllMenus(); }); buttonGroup.appendChild(settingsBtn); buttonGroup.appendChild(logsBtn); buttonGroup.appendChild(notifyBtn); buttonGroup.appendChild(closeBtn); header.appendChild(title); header.appendChild(buttonGroup); const content = document.createElement('div'); content.innerHTML = ` <div style="margin-bottom: 12px;"> <label for="account-count" style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500; color: var(--secondary-text);"> Number of accounts <span id="account-progress" style="color: var(--primary-color); font-size: 12px;"> (0/0)</span> </label> <input type="number" id="account-count" min="1" max="${MAX_ACCOUNTS}" value="1" style="width: 100%; padding: 8px 10px; box-sizing: border-box; background-color: var(--input-bg); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 8px; font-size: 13px; outline: none; transition: all 0.2s;" onkeypress="return (event.charCode !=8 && event.charCode == 0 || (event.charCode >= 48 && event.charCode <= 57))" oninput="if(this.value > ${MAX_ACCOUNTS}) {this.value = ${MAX_ACCOUNTS};}"> </div> <div style="margin-bottom: 12px;"> <label for="email-domain" style="display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500; color: var(--secondary-text);">Email Domain</label> <select id="email-domain" style="width: 100%; padding: 8px 10px; box-sizing: border-box; background-color: var(--input-bg); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 8px; font-size: 13px; outline: none;"> <option value="gmail.com">gmail.com</option> <option value="yahoo.com">yahoo.com</option> <option value="outlook.com">outlook.com</option> <option value="protonmail.com">protonmail.com</option> <option value="custom">Custom Domain</option> </select> <input type="text" id="custom-domain" placeholder="Enter custom domain" style="width: 100%; padding: 8px 10px; margin-top: 6px; box-sizing: border-box; background-color: var(--input-bg); color: var(--text-color); border: 1px solid var(--border-color); border-radius: 8px; font-size: 13px; outline: none; display: none;"> </div> `; const createBtn = document.createElement('button'); createBtn.id = 'start-create'; createBtn.textContent = 'Start Creating'; Object.assign(createBtn.style, { width: '100%', padding: '10px 0', borderRadius: '8px', border: 'none', backgroundColor: 'var(--primary-color)', color: 'white', fontSize: '13px', fontWeight: '600', cursor: 'pointer', transition: 'all 0.2s', boxShadow: '0 3px 0 0 var(--primary-shadow)', position: 'relative', overflow: 'hidden' }); createBtn.addEventListener('mouseover', () => { createBtn.style.backgroundColor = 'var(--primary-hover)'; createBtn.style.boxShadow = '0 3px 0 0 var(--primary-shadow)'; }); createBtn.addEventListener('mouseout', () => { createBtn.style.backgroundColor = 'var(--primary-color)'; createBtn.style.boxShadow = '0 3px 0 0 var(--primary-shadow)'; }); createBtn.addEventListener('mousedown', () => { createBtn.style.transform = 'translateY(2px)'; createBtn.style.boxShadow = '0 1px 0 0 var(--primary-shadow)'; }); createBtn.addEventListener('mouseup', () => { createBtn.style.transform = 'translateY(0)'; createBtn.style.boxShadow = '0 3px 0 0 var(--primary-shadow)'; }); createBtn.addEventListener('click', accountManager.startCreatingAccounts); content.appendChild(createBtn); container.appendChild(header); container.appendChild(content); document.body.appendChild(container); document.getElementById('email-domain').addEventListener('change', function() { const customDomainInput = document.getElementById('custom-domain'); customDomainInput.style.display = this.value === 'custom' ? 'block' : 'none'; }); utils.makeDraggable(container, header); elements.mainContainer = container; elements.accountProgress = document.getElementById('account-progress'); }, showSettings() { if (state.activeMenu === 'settings') { utils.closeAllMenus(); return; } utils.closeAllMenus(); state.activeMenu = 'settings'; if (elements.settingsPanel) { elements.settingsPanel.style.display = 'block'; return; } const panel = document.createElement('div'); panel.id = 'settings-panel'; Object.assign(panel.style, { position: 'fixed', right: '20px', top: '100px', backgroundColor: 'var(--panel-color)', borderRadius: '12px', padding: '12px', width: '220px', boxShadow: '0 4px 12px rgba(0,0,0,0.2)', zIndex: '10000', border: '1px solid var(--border-color)', display: 'block' }); const panelHeader = document.createElement('div'); panelHeader.style.display = 'flex'; panelHeader.style.justifyContent = 'space-between'; panelHeader.style.alignItems = 'center'; panelHeader.style.marginBottom = '10px'; const panelTitle = document.createElement('div'); panelTitle.textContent = 'Settings'; panelTitle.style.fontWeight = '600'; panelTitle.style.color = 'var(--primary-color)'; const closeBtn = utils.createIconButton('×', 'transparent', 'var(--error-color)', 'Close'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); panel.style.display = 'none'; state.activeMenu = null; }); panelHeader.appendChild(panelTitle); panelHeader.appendChild(closeBtn); panel.appendChild(panelHeader); panel.innerHTML += ` <div style="margin-bottom: 12px;"> <div style="font-size: 12px; font-weight: 500; margin-bottom: 6px; color: var(--secondary-text);">Account Options</div> <label style="display: flex; align-items: center; gap: 6px; font-size: 12px; margin-bottom: 4px; cursor: pointer; color: var(--text-color);"> <input type="checkbox" id="save-accounts" ${state.settings.saveAccounts ? 'checked' : ''}> Save accounts </label> <label style="display: flex; align-items: center; gap: 6px; font-size: 12px; margin-bottom: 4px; cursor: pointer; color: var(--text-color);"> <input type="checkbox" id="random-email" ${state.settings.randomEmail ? 'checked' : ''}> Random emails </label> <label style="display: flex; align-items: center; gap: 6px; font-size: 12px; margin-bottom: 4px; cursor: pointer; color: var(--text-color);"> <input type="checkbox" id="random-password" ${state.settings.randomPassword ? 'checked' : ''}> Random passwords </label> </div> <div style="margin-bottom: 12px;"> <div style="font-size: 12px; font-weight: 5 00; margin-bottom: 6px; color: var(--secondary-text);">Creation Speed</div> <select id="creation-speed" style="width: 100%; padding: 6px; border-radius: 6px; background-color: var(--input-bg); color: var(--text-color); border: 1px solid var(--border-color); font-size: 12px;"> <option value="fast" ${state.settings.creationSpeed === 'fast' ? 'selected' : ''}>Fast (1s)</option> <option value="medium" ${state.settings.creationSpeed === 'medium' ? 'selected' : ''}>Medium (3s)</option> <option value="slow" ${state.settings.creationSpeed === 'slow' ? 'selected' : ''}>Slow (5s)</option> </select> </div> <div style="margin-bottom: 12px;"> <div style="font-size: 12px; font-weight: 500; margin-bottom: 6px; color: var(--secondary-text);">Theme</div> <select id="theme-selector" style="width: 100%; padding: 6px; border-radius: 6px; background-color: var(--input-bg); color: var(--text-color); border: 1px solid var(--border-color); font-size: 12px;"> <option value="dark" ${state.settings.theme === 'dark' ? 'selected' : ''}>Dark Mode</option> <option value="light" ${state.settings.theme === 'light' ? 'selected' : ''}>Light Mode</option> </select> </div> <button id="save-settings" style="width: 100%; padding: 8px; background-color: var(--primary-color); color: white; border: none; border-radius: 6px; font-size: 12px; cursor: pointer; margin-top: 6px;">Save Settings</button> `; document.body.appendChild(panel); elements.settingsPanel = panel; document.getElementById('save-settings').addEventListener('click', () => { state.settings = { saveAccounts: document.getElementById('save-accounts').checked, randomEmail: document.getElementById('random-email').checked, randomPassword: document.getElementById('random-password').checked, creationSpeed: document.getElementById('creation-speed').value, theme: document.getElementById('theme-selector').value }; utils.saveSettings(); utils.addNotification('Settings saved successfully', 'success'); panel.style.display = 'none'; state.activeMenu = null; }); }, showLogs() { if (state.activeMenu === 'logs') { utils.closeAllMenus(); return; } utils.closeAllMenus(); state.activeMenu = 'logs'; if (elements.logsPanel) { elements.logsPanel.style.display = 'block'; this.updateLogsTable(); return; } const panel = document.createElement('div'); panel.id = 'logs-panel'; Object.assign(panel.style, { position: 'fixed', right: '20px', top: '100px', backgroundColor: 'var(--bg-color)', borderRadius: '12px', padding: '12px', width: '500px', maxHeight: '500px', overflowY: 'auto', boxShadow: '0 4px 12px rgba(0,0,0,0.3)', zIndex: '10000', border: '1px solid var(--border-color)', color: 'var(--text-color)', display: 'block' }); const header = document.createElement('div'); header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.marginBottom = '12px'; const title = document.createElement('h3'); title.textContent = 'Created Accounts'; Object.assign(title.style, { margin: '0', color: 'var(--info-color)', fontSize: '15px', fontWeight: '600' }); const buttonGroup = document.createElement('div'); buttonGroup.style.display = 'flex'; buttonGroup.style.gap = '4px'; const deleteAllBtn = utils.createIconButton('🗑️', 'transparent', 'var(--error-color)', 'Delete All'); deleteAllBtn.addEventListener('click', (e) => { e.stopPropagation(); if (confirm('Are you sure you want to delete ALL accounts?')) { utils.deleteAllAccounts(); } }); const closeBtn = utils.createIconButton('×', 'transparent', 'var(--error-color)', 'Close'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); panel.style.display = 'none'; state.activeMenu = null; }); buttonGroup.appendChild(deleteAllBtn); buttonGroup.appendChild(closeBtn); header.appendChild(title); header.appendChild(buttonGroup); panel.appendChild(header); const tableContainer = document.createElement('div'); tableContainer.style.overflowX = 'auto'; const table = document.createElement('table'); table.style.width = '100%'; table.style.borderCollapse = 'collapse'; table.style.marginTop = '8px'; table.style.fontSize = '12px'; const thead = document.createElement('thead'); thead.innerHTML = ` <tr style="background-color: var(--panel-color); color: var(--secondary-text);"> <th style="padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-color);">#</th> <th style="padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-color);">Email</th> <th style="padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-color);">Username</th> <th style="padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-color);">Password</th> <th style="padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-color);">Status</th> <th style="padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-color);">Actions</th> </tr> `; table.appendChild(thead); const tbody = document.createElement('tbody'); tbody.id = 'logs-table-body'; table.appendChild(tbody); tableContainer.appendChild(table); panel.appendChild(tableContainer); const exportBtn = document.createElement('button'); exportBtn.textContent = 'Export to CSV'; Object.assign(exportBtn.style, { marginTop: '12px', padding: '8px 12px', backgroundColor: 'var(--primary-color)', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '12px', width: '100%' }); exportBtn.addEventListener('click', this.exportToCSV); panel.appendChild(exportBtn); document.body.appendChild(panel); elements.logsPanel = panel; this.updateLogsTable(); }, showNotifications() { if (state.activeMenu === 'notifications') { utils.closeAllMenus(); return; } utils.closeAllMenus(); state.activeMenu = 'notifications'; if (elements.notificationsPanel) { elements.notificationsPanel.style.display = 'block'; this.updateNotificationsPanel(); return; } const panel = document.createElement('div'); panel.id = 'notifications-panel'; Object.assign(panel.style, { position: 'fixed', right: '20px', top: '100px', backgroundColor: 'var(--bg-color)', borderRadius: '12px', padding: '12px', width: '300px', maxHeight: '400px', overflowY: 'auto', boxShadow: '0 4px 12px rgba(0,0,0,0.3)', zIndex: '10000', border: '1px solid var(--border-color)', color: 'var(--text-color)', display: 'block' }); const header = document.createElement('div'); header.style.display = 'flex'; header.style.justifyContent = 'space-between'; header.style.alignItems = 'center'; header.style.marginBottom = '12px'; const title = document.createElement('h3'); title.textContent = 'Notifications'; Object.assign(title.style, { margin: '0', color: 'var(--warning-color)', fontSize: '15px', fontWeight: '600' }); const closeBtn = utils.createIconButton('×', 'transparent', 'var(--error-color)', 'Close'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); panel.style.display = 'none'; state.activeMenu = null; }); const clearBtn = utils.createIconButton('🗑️', 'transparent', 'var(--error-color)', 'Clear All'); clearBtn.addEventListener('click', (e) => { e.stopPropagation(); state.notifications = []; this.updateNotificationsPanel(); }); const btnGroup = document.createElement('div'); btnGroup.style.display = 'flex'; btnGroup.appendChild(clearBtn); btnGroup.appendChild(closeBtn); header.appendChild(title); header.appendChild(btnGroup); panel.appendChild(header); const notificationsContainer = document.createElement('div'); notificationsContainer.id = 'notifications-container'; panel.appendChild(notificationsContainer); document.body.appendChild(panel); elements.notificationsPanel = panel; this.updateNotificationsPanel(); }, updateNotificationsPanel() { const container = document.getElementById('notifications-container'); if (!container) return; container.innerHTML = ''; if (state.notifications.length === 0) { const emptyMsg = document.createElement('div'); emptyMsg.textContent = 'No notifications yet'; emptyMsg.style.color = 'var(--secondary-text)'; emptyMsg.style.textAlign = 'center'; emptyMsg.style.padding = '12px'; container.appendChild(emptyMsg); return; } state.notifications.forEach(notification => { const notifElement = document.createElement('div'); notifElement.style.padding = '8px'; notifElement.style.marginBottom = '6px'; notifElement.style.borderRadius = '6px'; notifElement.style.backgroundColor = 'var(--panel-color)'; notifElement.style.borderLeft = `3px solid ${ notification.type === 'success' ? 'var(--primary-color)' : notification.type === 'error' ? 'var(--error-color)' : 'var(--info-color)' }`; const timeElement = document.createElement('div'); timeElement.textContent = notification.timestamp; timeElement.style.fontSize = '10px'; timeElement.style.color = 'var(--secondary-text)'; timeElement.style.marginBottom = '4px'; const messageElement = document.createElement('div'); messageElement.textContent = notification.message; messageElement.style.fontSize = '12px'; notifElement.appendChild(timeElement); notifElement.appendChild(messageElement); container.appendChild(notifElement); }); }, updateLogsTable() { const tbody = document.getElementById('logs-table-body'); if (!tbody) return; tbody.innerHTML = ''; if (state.accounts.length === 0) { const row = document.createElement('tr'); row.innerHTML = ` <td colspan="6" style="padding: 12px; text-align: center; color: var(--secondary-text);">No accounts created yet</td> `; tbody.appendChild(row); return; } state.accounts.forEach((account, index) => { const row = document.createElement('tr'); row.className = 'account-row'; Object.assign(row.style, { borderBottom: '1px solid var(--border-color)', backgroundColor: index % 2 === 0 ? 'var(--bg-color)' : 'var(--panel-color)' }); const deleteBtn = document.createElement('button'); deleteBtn.className = 'delete-account-btn'; deleteBtn.textContent = 'Delete'; deleteBtn.addEventListener('click', (e) => { e.stopPropagation(); utils.deleteAccount(index); }); const actionCell = document.createElement('td'); actionCell.style.padding = '6px 8px'; actionCell.appendChild(deleteBtn); row.innerHTML = ` <td style="padding: 6px 8px;">${index + 1}</td> <td style="padding: 6px 8px;">${account.email || ''}</td> <td style="padding: 6px 8px;">${account.username || ''}</td> <td style="padding: 6px 8px;">${account.password || ''}</td> <td style="padding: 6px 8px; color: ${account.status === 'Success' ? 'var(--primary-color)' : 'var(--error-color)'}">${account.status || ''}</td> `; row.appendChild(actionCell); tbody.appendChild(row); }); }, exportToCSV() { if (state.accounts.length === 0) { utils.addNotification('No accounts to export', 'error'); return; } let csvContent = "STT,Email,Username,Password,Status\n"; state.accounts.forEach((account, index) => { csvContent += `${index + 1},${account.email || ''},${account.username || ''},${account.password || ''},${account.status || ''}\n`; }); const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('download', `duolingo_accounts_${new Date().toISOString().slice(0,10)}.csv`); link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); utils.addNotification('Accounts exported to CSV', 'success'); }, addToggleButton() { const toggleBtn = document.createElement('div'); toggleBtn.textContent = 'AC'; Object.assign(toggleBtn.style, { position: 'fixed', bottom: '20px', right: '20px', zIndex: '9998', backgroundColor: 'var(--primary-color)', color: 'white', width: '36px', height: '36px', borderRadius: '50%', display: 'flex', justifyContent: 'center', alignItems: 'center', cursor: 'pointer', boxShadow: '0 3px 6px rgba(0,0,0,0.2)', fontWeight: 'bold', fontSize: '13px', transition: 'all 0.2s' }); toggleBtn.addEventListener('mouseover', () => { toggleBtn.style.transform = 'scale(1.1)'; toggleBtn.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)'; }); toggleBtn.addEventListener('mouseout', () => { toggleBtn.style.transform = 'scale(1)'; toggleBtn.style.boxShadow = '0 3px 6px rgba(0,0,0,0.2)'; }); toggleBtn.addEventListener('click', () => { if (elements.mainContainer) { elements.mainContainer.style.display = elements.mainContainer.style.display === 'none' ? 'block' : 'none'; if (elements.mainContainer.style.display === 'none') { utils.closeAllMenus(); } } }); document.body.appendChild(toggleBtn); } }; const accountManager = { async startCreatingAccounts() { const count = parseInt(document.getElementById('account-count').value); const button = document.getElementById('start-create'); if (isNaN(count) || count <= 0) { utils.addNotification('Invalid account count', 'error'); return; } if (count > MAX_ACCOUNTS) { utils.addNotification(`Cannot create more than ${MAX_ACCOUNTS} accounts`, 'error'); return; } state.isCreating = true; state.createdCount = 0; state.totalCount = count; utils.updateProgressCounter(); button.disabled = true; button.style.backgroundColor = 'var(--button-bg)'; button.style.boxShadow = '0 3px 0 0 var(--border-color)'; button.textContent = 'Creating...'; utils.addNotification(`Starting creation of ${count} accounts`, 'info'); state.accounts = []; let created = 0; let successCount = 0; let errorCount = 0; const delay = { fast: 1000, medium: 3000, slow: 5000 }[state.settings.creationSpeed] || 3000; while (created < count && state.isCreating) { try { const email = state.settings.randomEmail ? utils.generateRandomEmail() : ''; let username = utils.generateRandomUsername(); const password = state.settings.randomPassword ? utils.generateRandomPassword() : 'defaultPassword123!'; let isAvailable = false; let attempts = 0; const maxAttempts = 5; while (!isAvailable && attempts < maxAttempts && state.isCreating) { try { isAvailable = await api.checkUsernameAvailability(username); if (!isAvailable) { attempts++; username = utils.generateRandomUsername(); await new Promise(resolve => setTimeout(resolve, 500)); } } catch (error) { console.error('Error checking username:', error); attempts++; await new Promise(resolve => setTimeout(resolve, 500)); } } if (!isAvailable) { state.accounts.push({ email, username, password, status: 'Failed: No available username', ip: state.userIP, createdAt: new Date().toISOString() }); utils.saveAccountsToStorage(); utils.addNotification('Failed to create account: No available username', 'error'); errorCount++; created++; state.createdCount++; utils.updateProgressCounter(); ui.updateLogsTable(); continue; } state.accounts.push({ email, username, password, status: 'Creating...', ip: state.userIP, createdAt: new Date().toISOString() }); utils.saveAccountsToStorage(); ui.updateLogsTable(); await api.createAccount(email, username, password) .then(() => { state.accounts[created].status = 'Success'; utils.saveAccountsToStorage(); utils.addNotification(`Account created: ${username}`, 'success'); successCount++; }) .catch(error => { state.accounts[created].status = 'Failed: ' + error.message; utils.saveAccountsToStorage(); utils.addNotification(`Failed to create account: ${error.message}`, 'error'); errorCount++; }); created++; state.createdCount++; utils.updateProgressCounter(); ui.updateLogsTable(); if (created < count) { await new Promise(resolve => setTimeout(resolve, delay)); } } catch (error) { console.error('Error in account creation loop:', error); created++; state.createdCount++; utils.updateProgressCounter(); errorCount++; utils.addNotification(`Error during account creation: ${error.message}`, 'error'); } } button.disabled = false; button.style.backgroundColor = 'var(--primary-color)'; button.style.boxShadow = '0 3px 0 0 var(--primary-shadow)'; button.textContent = 'Start Creating'; state.isCreating = false; utils.addNotification(`Finished creating ${count} accounts (${successCount} success)`, successCount > 0 ? 'success' : 'error'); } }; async function init() { state.userIP = await utils.getUserIP(); utils.loadAccountsFromStorage(); ui.createMainContainer(); ui.addToggleButton(); utils.applyTheme(); utils.addNotification('Account Creator initialized', 'info'); } if (document.readyState === 'complete') { init(); } else { window.addEventListener('load', init); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址