Size
23.2 KB
Version
1.1.5
Created
Mar 25, 2026
Updated
23 days ago
1// ==UserScript==
2// @name Progrentis Points Modifier
3// @description A new extension
4// @version 1.1.5
5// @match https://*.prod.progrentis.com/*
6// @icon https://prod.progrentis.com/Portals/6/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Progrentis Auto-Complete Extension iniciado');
12
13 // ============================================
14 // CONFIGURACIÓN
15 // ============================================
16 const AUTO_COMPLETE_CONFIG = {
17 pointsPerExercise: 10,
18 progressPerExercise: 10, // 10% por ejercicio
19 autoClickDelay: 2000, // 2 segundos entre acciones
20 maxScore: 100,
21 maxProgress: 100
22 };
23
24 // ============================================
25 // FUNCIONES DE ALMACENAMIENTO
26 // ============================================
27 async function getSavedPoints() {
28 const saved = await GM.getValue('progrentis_points', null);
29 console.log('Puntos guardados recuperados:', saved);
30 return saved;
31 }
32
33 async function savePoints(points) {
34 await GM.setValue('progrentis_points', points);
35 console.log('Puntos guardados:', points);
36 }
37
38 async function getSavedUnit() {
39 const saved = await GM.getValue('progrentis_unit', null);
40 console.log('Unidad guardada recuperada:', saved);
41 return saved;
42 }
43
44 async function saveUnit(unit) {
45 await GM.setValue('progrentis_unit', unit);
46 console.log('Unidad guardada:', unit);
47 }
48
49 async function getSavedUnlockState() {
50 const saved = await GM.getValue('progrentis_unlocked', false);
51 console.log('Estado de desbloqueo recuperado:', saved);
52 return saved;
53 }
54
55 async function saveUnlockState(unlocked) {
56 await GM.setValue('progrentis_unlocked', unlocked);
57 console.log('Estado de desbloqueo guardado:', unlocked);
58 }
59
60 async function getAutoCompleteEnabled() {
61 const enabled = await GM.getValue('progrentis_autocomplete', false);
62 console.log('Auto-completar habilitado:', enabled);
63 return enabled;
64 }
65
66 async function setAutoCompleteEnabled(enabled) {
67 await GM.setValue('progrentis_autocomplete', enabled);
68 console.log('Auto-completar configurado a:', enabled);
69 }
70
71 // ============================================
72 // FUNCIONES DE MODIFICACIÓN DE PROGRESO
73 // ============================================
74 async function modifyScore(newScore) {
75 const scoreElements = document.querySelectorAll('div[data-v-548dd043].progress-bar-x.progress span');
76
77 for (const element of scoreElements) {
78 const text = element.textContent.trim();
79 // Buscar el elemento que muestra la nota (número sin %)
80 if (!text.includes('%') && !isNaN(parseInt(text))) {
81 element.textContent = newScore.toString();
82 console.log('Nota modificada a:', newScore);
83 await savePoints(newScore);
84 return true;
85 }
86 }
87 return false;
88 }
89
90 async function modifyProgress(newProgress) {
91 const progressElements = document.querySelectorAll('div[data-v-548dd043].progress-bar-x.progress span');
92
93 for (const element of progressElements) {
94 const text = element.textContent.trim();
95 // Buscar el elemento que muestra el porcentaje
96 if (text.includes('%')) {
97 element.textContent = `${newProgress} %`;
98 console.log('Progreso modificado a:', newProgress + '%');
99
100 // También modificar la barra visual
101 const progressBar = element.previousElementSibling;
102 if (progressBar && progressBar.classList.contains('inside')) {
103 progressBar.style.width = `${newProgress}%`;
104 }
105 return true;
106 }
107 }
108 return false;
109 }
110
111 async function incrementScoreAndProgress() {
112 const scoreElements = document.querySelectorAll('div[data-v-548dd043].progress-bar-x.progress span');
113 let currentScore = 0;
114 let currentProgress = 0;
115
116 // Obtener valores actuales
117 for (const element of scoreElements) {
118 const text = element.textContent.trim();
119 if (text.includes('%')) {
120 currentProgress = parseFloat(text.replace('%', '').trim());
121 } else if (!isNaN(parseInt(text))) {
122 currentScore = parseInt(text);
123 }
124 }
125
126 // Calcular nuevos valores
127 const newScore = Math.min(currentScore + AUTO_COMPLETE_CONFIG.pointsPerExercise, AUTO_COMPLETE_CONFIG.maxScore);
128 const newProgress = Math.min(currentProgress + AUTO_COMPLETE_CONFIG.progressPerExercise, AUTO_COMPLETE_CONFIG.maxProgress);
129
130 console.log(`Incrementando - Nota: ${currentScore} → ${newScore}, Progreso: ${currentProgress}% → ${newProgress}%`);
131
132 // Aplicar cambios
133 await modifyScore(newScore);
134 await modifyProgress(newProgress);
135
136 showSuccessMessage(`✅ +${AUTO_COMPLETE_CONFIG.pointsPerExercise} puntos | +${AUTO_COMPLETE_CONFIG.progressPerExercise}% progreso`);
137
138 return { newScore, newProgress };
139 }
140
141 // ============================================
142 // AUTO-COMPLETAR EJERCICIOS
143 // ============================================
144 async function autoCompleteExercise() {
145 const isEnabled = await getAutoCompleteEnabled();
146 if (!isEnabled) {
147 console.log('Auto-completar deshabilitado');
148 return;
149 }
150
151 console.log('Buscando botones de ejercicio...');
152
153 // Buscar botón "Empezar" o "Continuar"
154 const startButtons = document.querySelectorAll('button[data-v-123be8a0].btn-panel-gamificacion.btn-start-active');
155
156 if (startButtons.length > 0) {
157 console.log('Botón de inicio encontrado, haciendo clic...');
158 startButtons[0].click();
159
160 // Esperar y luego incrementar progreso
161 setTimeout(async () => {
162 await incrementScoreAndProgress();
163
164 // Buscar siguiente ejercicio
165 setTimeout(() => autoCompleteExercise(), AUTO_COMPLETE_CONFIG.autoClickDelay);
166 }, AUTO_COMPLETE_CONFIG.autoClickDelay);
167
168 return;
169 }
170
171 // Buscar botones de respuesta o siguiente
172 const actionButtons = document.querySelectorAll('button[type="button"]');
173 for (const button of actionButtons) {
174 const buttonText = button.textContent.trim().toLowerCase();
175 if (buttonText.includes('siguiente') || buttonText.includes('continuar') || buttonText.includes('finalizar')) {
176 console.log('Botón de acción encontrado:', buttonText);
177 button.click();
178
179 setTimeout(async () => {
180 await incrementScoreAndProgress();
181 setTimeout(() => autoCompleteExercise(), AUTO_COMPLETE_CONFIG.autoClickDelay);
182 }, AUTO_COMPLETE_CONFIG.autoClickDelay);
183
184 return;
185 }
186 }
187
188 console.log('No se encontraron más ejercicios para completar');
189 }
190
191 // ============================================
192 // FUNCIONES DE APLICACIÓN DE VALORES GUARDADOS
193 // ============================================
194 async function applyModifiedPoints() {
195 const savedPoints = await getSavedPoints();
196 if (savedPoints !== null) {
197 await modifyScore(savedPoints);
198 }
199 }
200
201 async function applyModifiedUnit() {
202 const savedUnit = await getSavedUnit();
203 if (savedUnit !== null) {
204 const unitDisplay = document.querySelector('div[data-v-28b36b89].unidad span');
205 if (unitDisplay) {
206 unitDisplay.textContent = `Unidad ${savedUnit}`;
207 console.log('Unidad aplicada desde almacenamiento:', savedUnit);
208 }
209 }
210 }
211
212 async function applyUnlockedObjectives() {
213 const isUnlocked = await getSavedUnlockState();
214 if (isUnlocked) {
215 const objectives = document.querySelectorAll('div[data-un-premio][data-disabled="true"]');
216 objectives.forEach((objective) => {
217 objective.removeAttribute('data-disabled');
218 objective.setAttribute('data-disabled', 'false');
219 objective.style.opacity = '1';
220 objective.style.filter = 'none';
221 objective.style.pointerEvents = 'auto';
222 objective.style.cursor = 'pointer';
223 });
224 console.log('Objetivos desbloqueados aplicados desde almacenamiento');
225 }
226 }
227
228 // ============================================
229 // DESBLOQUEAR OBJETIVOS
230 // ============================================
231 async function unlockAllObjectives() {
232 const objectives = document.querySelectorAll('div[data-un-premio][data-disabled="true"]');
233 console.log('Objetivos encontrados:', objectives.length);
234
235 objectives.forEach((objective) => {
236 objective.removeAttribute('data-disabled');
237 objective.setAttribute('data-disabled', 'false');
238 objective.style.opacity = '1';
239 objective.style.filter = 'none';
240 objective.style.pointerEvents = 'auto';
241 objective.style.cursor = 'pointer';
242 });
243
244 if (objectives.length > 0) {
245 await saveUnlockState(true);
246 showSuccessMessage(`🎉 ${objectives.length} objetivos desbloqueados`);
247 } else {
248 const allObjectives = document.querySelectorAll('div[data-un-premio]');
249 if (allObjectives.length > 0) {
250 await saveUnlockState(true);
251 showSuccessMessage('✅ Todos los objetivos ya están desbloqueados');
252 } else {
253 alert('No se encontraron objetivos');
254 }
255 }
256 }
257
258 // ============================================
259 // OBSERVADORES
260 // ============================================
261 function observeScoreChanges() {
262 const progressSection = document.querySelector('div[data-v-9d32d75b].progresslogros');
263
264 if (!progressSection) {
265 return;
266 }
267
268 const observer = new MutationObserver(async () => {
269 const savedPoints = await getSavedPoints();
270 if (savedPoints !== null) {
271 await modifyScore(savedPoints);
272 }
273 });
274
275 observer.observe(progressSection, {
276 childList: true,
277 characterData: true,
278 subtree: true
279 });
280
281 console.log('Observer de puntos activado');
282 }
283
284 function observeObjectivesChanges() {
285 const awardsSection = document.querySelector('div[data-seccion-retos-x]');
286
287 if (!awardsSection) {
288 return;
289 }
290
291 const observer = new MutationObserver(async () => {
292 const isUnlocked = await getSavedUnlockState();
293 if (isUnlocked) {
294 const objectives = document.querySelectorAll('div[data-un-premio][data-disabled="true"]');
295 if (objectives.length > 0) {
296 console.log('Detectados objetivos bloqueados, desbloqueando...');
297 objectives.forEach((objective) => {
298 objective.removeAttribute('data-disabled');
299 objective.setAttribute('data-disabled', 'false');
300 objective.style.opacity = '1';
301 objective.style.filter = 'none';
302 objective.style.pointerEvents = 'auto';
303 objective.style.cursor = 'pointer';
304 });
305 }
306 }
307 });
308
309 observer.observe(awardsSection, {
310 childList: true,
311 subtree: true,
312 attributes: true,
313 attributeFilter: ['data-disabled']
314 });
315
316 console.log('Observer de objetivos activado');
317 }
318
319 // ============================================
320 // INTERFAZ DE USUARIO
321 // ============================================
322 function createControlPanel() {
323 const banner = document.querySelector('div[data-v-f8d4be6e].container_banner');
324
325 if (!banner) {
326 console.log('No se encontró el banner, reintentando...');
327 return false;
328 }
329
330 if (document.getElementById('progrentis-control-panel')) {
331 console.log('El panel de control ya existe');
332 return true;
333 }
334
335 console.log('Creando panel de control');
336
337 const panel = document.createElement('div');
338 panel.id = 'progrentis-control-panel';
339 panel.style.cssText = `
340 position: fixed;
341 top: 20px;
342 right: 20px;
343 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
344 padding: 20px;
345 border-radius: 15px;
346 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
347 z-index: 10000;
348 min-width: 300px;
349 color: white;
350 font-family: Arial, sans-serif;
351 `;
352
353 const title = document.createElement('div');
354 title.style.cssText = `
355 font-size: 18px;
356 font-weight: bold;
357 margin-bottom: 15px;
358 text-align: center;
359 border-bottom: 2px solid rgba(255, 255, 255, 0.3);
360 padding-bottom: 10px;
361 `;
362 title.textContent = '⚡ Progrentis Turbo';
363
364 const buttonsContainer = document.createElement('div');
365 buttonsContainer.style.cssText = `
366 display: flex;
367 flex-direction: column;
368 gap: 10px;
369 `;
370
371 // Botón Auto-Completar
372 const autoCompleteBtn = document.createElement('button');
373 autoCompleteBtn.id = 'auto-complete-toggle';
374 autoCompleteBtn.textContent = '🚀 Iniciar Auto-Completar';
375 autoCompleteBtn.style.cssText = `
376 background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
377 color: white;
378 border: none;
379 padding: 12px 20px;
380 border-radius: 8px;
381 font-size: 14px;
382 font-weight: bold;
383 cursor: pointer;
384 transition: all 0.3s ease;
385 `;
386
387 autoCompleteBtn.addEventListener('mouseenter', () => {
388 autoCompleteBtn.style.transform = 'scale(1.05)';
389 });
390
391 autoCompleteBtn.addEventListener('mouseleave', () => {
392 autoCompleteBtn.style.transform = 'scale(1)';
393 });
394
395 autoCompleteBtn.addEventListener('click', async () => {
396 const isEnabled = await getAutoCompleteEnabled();
397 await setAutoCompleteEnabled(!isEnabled);
398
399 if (!isEnabled) {
400 autoCompleteBtn.textContent = '⏸️ Detener Auto-Completar';
401 autoCompleteBtn.style.background = 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)';
402 showSuccessMessage('🚀 Auto-completar ACTIVADO');
403 autoCompleteExercise();
404 } else {
405 autoCompleteBtn.textContent = '🚀 Iniciar Auto-Completar';
406 autoCompleteBtn.style.background = 'linear-gradient(135deg, #11998e 0%, #38ef7d 100%)';
407 showSuccessMessage('⏸️ Auto-completar DESACTIVADO');
408 }
409 });
410
411 // Botón +10 Puntos Manual
412 const addPointsBtn = document.createElement('button');
413 addPointsBtn.textContent = '➕ +10 Puntos';
414 addPointsBtn.style.cssText = `
415 background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
416 color: white;
417 border: none;
418 padding: 12px 20px;
419 border-radius: 8px;
420 font-size: 14px;
421 font-weight: bold;
422 cursor: pointer;
423 transition: all 0.3s ease;
424 `;
425
426 addPointsBtn.addEventListener('mouseenter', () => {
427 addPointsBtn.style.transform = 'scale(1.05)';
428 });
429
430 addPointsBtn.addEventListener('mouseleave', () => {
431 addPointsBtn.style.transform = 'scale(1)';
432 });
433
434 addPointsBtn.addEventListener('click', async () => {
435 await incrementScoreAndProgress();
436 });
437
438 // Botón Modificar Valores
439 const modifyBtn = document.createElement('button');
440 modifyBtn.textContent = '✏️ Modificar Valores';
441 modifyBtn.style.cssText = `
442 background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
443 color: white;
444 border: none;
445 padding: 12px 20px;
446 border-radius: 8px;
447 font-size: 14px;
448 font-weight: bold;
449 cursor: pointer;
450 transition: all 0.3s ease;
451 `;
452
453 modifyBtn.addEventListener('mouseenter', () => {
454 modifyBtn.style.transform = 'scale(1.05)';
455 });
456
457 modifyBtn.addEventListener('mouseleave', () => {
458 modifyBtn.style.transform = 'scale(1)';
459 });
460
461 modifyBtn.addEventListener('click', async () => {
462 const newScore = prompt('¿Qué nota quieres tener? (0-100)', '100');
463 if (newScore !== null) {
464 const score = parseInt(newScore);
465 if (!isNaN(score) && score >= 0 && score <= 100) {
466 await modifyScore(score);
467
468 const newProgress = prompt('¿Qué progreso quieres tener? (0-100)', '100');
469 if (newProgress !== null) {
470 const progress = parseInt(newProgress);
471 if (!isNaN(progress) && progress >= 0 && progress <= 100) {
472 await modifyProgress(progress);
473 showSuccessMessage(`✅ Nota: ${score} | Progreso: ${progress}%`);
474 }
475 }
476 } else {
477 alert('Por favor ingresa un número válido entre 0 y 100');
478 }
479 }
480 });
481
482 // Botón Desbloquear Todo
483 const unlockBtn = document.createElement('button');
484 unlockBtn.textContent = '🏆 Desbloquear Todo';
485 unlockBtn.style.cssText = `
486 background: linear-gradient(135deg, #ffa751 0%, #ffe259 100%);
487 color: white;
488 border: none;
489 padding: 12px 20px;
490 border-radius: 8px;
491 font-size: 14px;
492 font-weight: bold;
493 cursor: pointer;
494 transition: all 0.3s ease;
495 `;
496
497 unlockBtn.addEventListener('mouseenter', () => {
498 unlockBtn.style.transform = 'scale(1.05)';
499 });
500
501 unlockBtn.addEventListener('mouseleave', () => {
502 unlockBtn.style.transform = 'scale(1)';
503 });
504
505 unlockBtn.addEventListener('click', () => {
506 unlockAllObjectives();
507 });
508
509 // Botón Resetear
510 const resetBtn = document.createElement('button');
511 resetBtn.textContent = '🔄 Resetear Todo';
512 resetBtn.style.cssText = `
513 background: linear-gradient(135deg, #868f96 0%, #596164 100%);
514 color: white;
515 border: none;
516 padding: 12px 20px;
517 border-radius: 8px;
518 font-size: 14px;
519 font-weight: bold;
520 cursor: pointer;
521 transition: all 0.3s ease;
522 `;
523
524 resetBtn.addEventListener('mouseenter', () => {
525 resetBtn.style.transform = 'scale(1.05)';
526 });
527
528 resetBtn.addEventListener('mouseleave', () => {
529 resetBtn.style.transform = 'scale(1)';
530 });
531
532 resetBtn.addEventListener('click', async () => {
533 if (confirm('¿Estás seguro de que quieres resetear TODO?')) {
534 await GM.deleteValue('progrentis_points');
535 await GM.deleteValue('progrentis_unit');
536 await GM.deleteValue('progrentis_unlocked');
537 await GM.deleteValue('progrentis_autocomplete');
538 location.reload();
539 }
540 });
541
542 buttonsContainer.appendChild(autoCompleteBtn);
543 buttonsContainer.appendChild(addPointsBtn);
544 buttonsContainer.appendChild(modifyBtn);
545 buttonsContainer.appendChild(unlockBtn);
546 buttonsContainer.appendChild(resetBtn);
547
548 panel.appendChild(title);
549 panel.appendChild(buttonsContainer);
550 document.body.appendChild(panel);
551
552 console.log('Panel de control creado exitosamente');
553 return true;
554 }
555
556 function showSuccessMessage(text) {
557 const message = document.createElement('div');
558 message.style.cssText = `
559 position: fixed;
560 top: 20px;
561 left: 50%;
562 transform: translateX(-50%);
563 background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
564 color: white;
565 padding: 15px 25px;
566 border-radius: 10px;
567 font-weight: bold;
568 box-shadow: 0 4px 15px rgba(17, 153, 142, 0.4);
569 z-index: 10001;
570 animation: slideDown 0.3s ease;
571 font-size: 16px;
572 `;
573 message.textContent = text;
574
575 document.body.appendChild(message);
576
577 setTimeout(() => {
578 message.style.animation = 'slideUp 0.3s ease';
579 setTimeout(() => message.remove(), 300);
580 }, 3000);
581 }
582
583 const style = document.createElement('style');
584 style.textContent = `
585 @keyframes slideDown {
586 from {
587 transform: translateX(-50%) translateY(-100px);
588 opacity: 0;
589 }
590 to {
591 transform: translateX(-50%) translateY(0);
592 opacity: 1;
593 }
594 }
595 @keyframes slideUp {
596 from {
597 transform: translateX(-50%) translateY(0);
598 opacity: 1;
599 }
600 to {
601 transform: translateX(-50%) translateY(-100px);
602 opacity: 0;
603 }
604 }
605 `;
606 document.head.appendChild(style);
607
608 // ============================================
609 // INICIALIZACIÓN
610 // ============================================
611 async function init() {
612 console.log('Iniciando extensión Progrentis Turbo...');
613
614 await applyModifiedPoints();
615 await applyModifiedUnit();
616 await applyUnlockedObjectives();
617
618 if (createControlPanel()) {
619 observeScoreChanges();
620 observeObjectivesChanges();
621 return;
622 }
623
624 const observer = new MutationObserver(async (mutations, obs) => {
625 if (createControlPanel()) {
626 await applyModifiedPoints();
627 await applyModifiedUnit();
628 await applyUnlockedObjectives();
629 observeScoreChanges();
630 observeObjectivesChanges();
631 obs.disconnect();
632 }
633 });
634
635 observer.observe(document.body, {
636 childList: true,
637 subtree: true
638 });
639
640 setTimeout(() => {
641 observer.disconnect();
642 createControlPanel();
643 applyModifiedPoints();
644 applyModifiedUnit();
645 applyUnlockedObjectives();
646 observeScoreChanges();
647 observeObjectivesChanges();
648 }, 10000);
649 }
650
651 if (document.readyState === 'loading') {
652 document.addEventListener('DOMContentLoaded', init);
653 } else {
654 init();
655 }
656
657})();