Gimkit Enhanced Assistant

Enhanced learning assistant for Gimkit

// ==UserScript==
// @name        Gimkit Enhanced Assistant
// @namespace   Violentmonkey Scripts
// @match       *://*.gimkit.com/*
// @grant       none
// @version     1.0
// @author      CMH
// @description Enhanced learning assistant for Gimkit
// ==/UserScript==

(function() {
  'use strict';

  // Configuration
  const config = {
    highlightCorrectAnswers: true,
    autoAnswerDelay: 0, // Set to 0 to disable auto-answer
    showAnswerStats: true,
    enableKeyboardShortcuts: true
  };

  // Add custom CSS
  const style = document.createElement('style');
  style.textContent = `
    .enhanced-assistant-panel {
      position: fixed;
      top: 10px;
      right: 10px;
      background: rgba(30, 30, 30, 0.9);
      color: white;
      border: 2px solid #6c5ce7;
      border-radius: 8px;
      padding: 10px;
      z-index: 9999;
      max-width: 300px;
      box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
      font-family: Arial, sans-serif;
      transition: all 0.3s ease;
    }
    .enhanced-assistant-panel h3 {
      margin-top: 0;
      color: #6c5ce7;
      font-size: 16px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .enhanced-assistant-button {
      background: #6c5ce7;
      color: white;
      border: none;
      padding: 5px 10px;
      border-radius: 4px;
      margin: 5px;
      cursor: pointer;
      transition: background 0.2s;
    }
    .enhanced-assistant-button:hover {
      background: #5b4bc4;
    }
    .enhanced-assistant-button.active {
      background: #4a3cb3;
      box-shadow: inset 0 0 5px rgba(0,0,0,0.3);
    }
    .study-notes {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
    }
    .study-notes textarea {
      width: 100%;
      height: 100px;
      margin-top: 5px;
      border-radius: 4px;
      border: 1px solid #444;
      padding: 5px;
      background: #222;
      color: #eee;
    }
    .timer-display {
      font-size: 1.2em;
      font-weight: bold;
      margin: 5px 0;
      text-align: center;
    }
    .answer-stats {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
    }
    .answer-stats-item {
      display: flex;
      justify-content: space-between;
      margin-bottom: 4px;
    }
    .answer-stats-item .question {
      flex: 1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      margin-right: 10px;
    }
    .answer-stats-item .answer {
      color: #6c5ce7;
      font-weight: bold;
    }
    .hidden {
      display: none;
    }
    .correct-answer {
      box-shadow: 0 0 0 2px #4CAF50 !important;
      position: relative;
    }
    .correct-answer::after {
      content: "✓";
      position: absolute;
      top: 5px;
      right: 5px;
      background: #4CAF50;
      color: white;
      width: 20px;
      height: 20px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 12px;
    }
    .settings-section {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
    }
    .settings-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 8px;
    }
    .toggle-switch {
      position: relative;
      display: inline-block;
      width: 40px;
      height: 20px;
    }
    .toggle-switch input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    .toggle-slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #444;
      transition: .4s;
      border-radius: 20px;
    }
    .toggle-slider:before {
      position: absolute;
      content: "";
      height: 16px;
      width: 16px;
      left: 2px;
      bottom: 2px;
      background-color: white;
      transition: .4s;
      border-radius: 50%;
    }
    input:checked + .toggle-slider {
      background-color: #6c5ce7;
    }
    input:checked + .toggle-slider:before {
      transform: translateX(20px);
    }
    .delay-input {
      width: 50px;
      background: #222;
      color: white;
      border: 1px solid #444;
      border-radius: 4px;
      padding: 2px 5px;
    }
    .keyboard-shortcuts {
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 10px;
      font-size: 12px;
    }
    .keyboard-shortcut-item {
      display: flex;
      justify-content: space-between;
      margin-bottom: 4px;
    }
    .keyboard-shortcut-item .key {
      background: #333;
      padding: 2px 6px;
      border-radius: 3px;
      border: 1px solid #555;
    }
    .made-by {
      font-size: 10px;
      opacity: 0.7;
      text-align: center;
      margin-top: 10px;
      border-top: 1px solid #444;
      padding-top: 5px;
    }
  `;
  document.head.appendChild(style);

  // Create the assistant panel
  const panel = document.createElement('div');
  panel.className = 'enhanced-assistant-panel';
  panel.innerHTML = `
    <h3>
      <span>Enhanced Assistant</span>
      <span class="version" style="font-size: 10px; opacity: 0.7;">v1.0</span>
    </h3>
    <div class="button-container">
      <button class="enhanced-assistant-button toggle-notes">Notes</button>
      <button class="enhanced-assistant-button toggle-timer">Timer</button>
      <button class="enhanced-assistant-button toggle-stats">Stats</button>
      <button class="enhanced-assistant-button toggle-settings">Settings</button>
      <button class="enhanced-assistant-button toggle-panel">Hide</button>
    </div>

    <div class="study-notes hidden">
      <p>Quick Notes:</p>
      <textarea placeholder="Take notes here..."></textarea>
      <button class="enhanced-assistant-button save-notes">Save</button>
    </div>

    <div class="timer-container hidden">
      <div class="timer-display">00:00</div>
      <div style="display: flex; justify-content: center;">
        <button class="enhanced-assistant-button start-timer">Start</button>
        <button class="enhanced-assistant-button reset-timer">Reset</button>
      </div>
    </div>

    <div class="answer-stats hidden">
      <p>Recent Answers:</p>
      <div class="answer-stats-list"></div>
    </div>

    <div class="settings-section hidden">
      <div class="settings-item">
        <span>Highlight Answers</span>
        <label class="toggle-switch">
          <input type="checkbox" id="highlight-toggle" ${config.highlightCorrectAnswers ? 'checked' : ''}>
          <span class="toggle-slider"></span>
        </label>
      </div>

      <div class="settings-item">
        <span>Auto-Answer Delay (ms)</span>
        <input type="number" id="auto-answer-delay" class="delay-input" value="${config.autoAnswerDelay}" min="0" max="10000" step="100">
      </div>

      <div class="settings-item">
        <span>Show Answer Stats</span>
        <label class="toggle-switch">
          <input type="checkbox" id="stats-toggle" ${config.showAnswerStats ? 'checked' : ''}>
          <span class="toggle-slider"></span>
        </label>
      </div>

      <div class="settings-item">
        <span>Keyboard Shortcuts</span>
        <label class="toggle-switch">
          <input type="checkbox" id="shortcuts-toggle" ${config.enableKeyboardShortcuts ? 'checked' : ''}>
          <span class="toggle-slider"></span>
        </label>
      </div>
    </div>

    <div class="keyboard-shortcuts ${config.enableKeyboardShortcuts ? '' : 'hidden'}">
      <p>Shortcuts:</p>
      <div class="keyboard-shortcut-item">
        <span>Toggle Panel</span>
        <span class="key">Alt+P</span>
      </div>
      <div class="keyboard-shortcut-item">
        <span>Quick Note</span>
        <span class="key">Alt+N</span>
      </div>
      <div class="keyboard-shortcut-item">
        <span>Timer Start/Pause</span>
        <span class="key">Alt+T</span>
      </div>
    </div>

    <div class="made-by">Made by CMH</div>
  `;
  document.body.appendChild(panel);

  // Timer functionality
  let timerInterval;
  let seconds = 0;
  const timerDisplay = panel.querySelector('.timer-display');
  const startTimerBtn = panel.querySelector('.start-timer');
  const resetTimerBtn = panel.querySelector('.reset-timer');

  function updateTimerDisplay() {
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = seconds % 60;
    timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
  }

  startTimerBtn.addEventListener('click', function() {
    if (this.textContent === 'Start') {
      timerInterval = setInterval(function() {
        seconds++;
        updateTimerDisplay();
      }, 1000);
      this.textContent = 'Pause';
    } else {
      clearInterval(timerInterval);
      this.textContent = 'Start';
    }
  });

  resetTimerBtn.addEventListener('click', function() {
    clearInterval(timerInterval);
    seconds = 0;
    updateTimerDisplay();
    startTimerBtn.textContent = 'Start';
  });

  // Toggle functionality
  const toggleNotesBtn = panel.querySelector('.toggle-notes');
  const toggleTimerBtn = panel.querySelector('.toggle-timer');
  const toggleStatsBtn = panel.querySelector('.toggle-stats');
  const toggleSettingsBtn = panel.querySelector('.toggle-settings');
  const togglePanelBtn = panel.querySelector('.toggle-panel');

  const notesSection = panel.querySelector('.study-notes');
  const timerSection = panel.querySelector('.timer-container');
  const statsSection = panel.querySelector('.answer-stats');
  const settingsSection = panel.querySelector('.settings-section');

  function hideAllSections() {
    notesSection.classList.add('hidden');
    timerSection.classList.add('hidden');
    statsSection.classList.add('hidden');
    settingsSection.classList.add('hidden');

    toggleNotesBtn.classList.remove('active');
    toggleTimerBtn.classList.remove('active');
    toggleStatsBtn.classList.remove('active');
    toggleSettingsBtn.classList.remove('active');
  }

  toggleNotesBtn.addEventListener('click', function() {
    if (notesSection.classList.contains('hidden')) {
      hideAllSections();
      notesSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      notesSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  toggleTimerBtn.addEventListener('click', function() {
    if (timerSection.classList.contains('hidden')) {
      hideAllSections();
      timerSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      timerSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  toggleStatsBtn.addEventListener('click', function() {
    if (statsSection.classList.contains('hidden')) {
      hideAllSections();
      statsSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      statsSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  toggleSettingsBtn.addEventListener('click', function() {
    if (settingsSection.classList.contains('hidden')) {
      hideAllSections();
      settingsSection.classList.remove('hidden');
      this.classList.add('active');
    } else {
      settingsSection.classList.add('hidden');
      this.classList.remove('active');
    }
  });

  togglePanelBtn.addEventListener('click', function() {
    if (this.textContent === 'Hide') {
      hideAllSections();
      panel.style.width = 'auto';
      panel.style.height = 'auto';
      panel.style.overflow = 'hidden';
      panel.style.padding = '5px';
      Array.from(panel.children).forEach(child => {
        if (child.tagName !== 'H3' && !child.contains(togglePanelBtn)) {
          child.style.display = 'none';
        }
      });
      this.textContent = 'Show';
    } else {
      panel.style.width = '';
      panel.style.height = '';
      panel.style.overflow = '';
      panel.style.padding = '10px';
      Array.from(panel.children).forEach(child => {
        if (child.tagName !== 'H3') {
          child.style.display = '';
        }
      });
      this.textContent = 'Hide';
    }
  });

  // Save notes functionality
  const saveNotesBtn = panel.querySelector('.save-notes');
  const notesTextarea = panel.querySelector('textarea');

  saveNotesBtn.addEventListener('click', function() {
    const notes = notesTextarea.value;
    localStorage.setItem('gimkitEnhancedNotes', notes);
    alert('Notes saved!');
  });

  // Load saved notes
  const savedNotes = localStorage.getItem('gimkitEnhancedNotes');
  if (savedNotes) {
    notesTextarea.value = savedNotes;
  }

  // Settings functionality
  const highlightToggle = document.getElementById('highlight-toggle');
  const autoAnswerDelayInput = document.getElementById('auto-answer-delay');
  const statsToggle = document.getElementById('stats-toggle');
  const shortcutsToggle = document.getElementById('shortcuts-toggle');
  const keyboardShortcutsSection = panel.querySelector('.keyboard-shortcuts');

  highlightToggle.addEventListener('change', function() {
    config.highlightCorrectAnswers = this.checked;
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
  });

  autoAnswerDelayInput.addEventListener('change', function() {
    config.autoAnswerDelay = parseInt(this.value, 10);
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
  });

  statsToggle.addEventListener('change', function() {
    config.showAnswerStats = this.checked;
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
  });

  shortcutsToggle.addEventListener('change', function() {
    config.enableKeyboardShortcuts = this.checked;
    localStorage.setItem('gimkitConfig', JSON.stringify(config));
    if (this.checked) {
      keyboardShortcutsSection.classList.remove('hidden');
    } else {
      keyboardShortcutsSection.classList.add('hidden');
    }
  });

  // Load saved config
  const savedConfig = localStorage.getItem('gimkitConfig');
  if (savedConfig) {
    try {
      const parsedConfig = JSON.parse(savedConfig);
      Object.assign(config, parsedConfig);

      // Update UI to match loaded config
      highlightToggle.checked = config.highlightCorrectAnswers;
      autoAnswerDelayInput.value = config.autoAnswerDelay;
      statsToggle.checked = config.showAnswerStats;
      shortcutsToggle.checked = config.enableKeyboardShortcuts;

      if (config.enableKeyboardShortcuts) {
        keyboardShortcutsSection.classList.remove('hidden');
      } else {
        keyboardShortcutsSection.classList.add('hidden');
      }
    } catch (e) {
      console.error('Error loading saved config:', e);
    }
  }

  // Answer tracking
  const answerMap = new Map();
  const answerStatsList = panel.querySelector('.answer-stats-list');

  function updateAnswerStats() {
    if (!config.showAnswerStats) return;

    answerStatsList.innerHTML = '';

    // Get the last 5 entries
    const entries = Array.from(answerMap.entries()).slice(-5);

    entries.forEach(([question, answer]) => {
      const item = document.createElement('div');
      item.className = 'answer-stats-item';
      item.innerHTML = `
        <span class="question" title="${question}">${question}</span>
        <span class="answer">${answer}</span>
      `;
      answerStatsList.appendChild(item);
    });
  }

  // Keyboard shortcuts
  if (config.enableKeyboardShortcuts) {
    document.addEventListener('keydown', function(e) {
      // Alt+P to toggle panel
      if (e.altKey && e.key === 'p') {
        togglePanelBtn.click();
      }

      // Alt+N to toggle notes
      if (e.altKey && e.key === 'n') {
        toggleNotesBtn.click();
      }

      // Alt+T to toggle timer
      if (e.altKey && e.key === 't') {
        toggleTimerBtn.click();
      }
    });
  }

  // Detect quiz questions and provide assistance
  const observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.addedNodes && mutation.addedNodes.length > 0) {
        for (let i = 0; i < mutation.addedNodes.length; i++) {
          const node = mutation.addedNodes[i];
          if (node.nodeType === 1) {
            // Check if this is a question container
            const questionElement = node.querySelector && node.querySelector('[data-testid="question-text"]');
            if (questionElement) {
              const questionText = questionElement.textContent.trim();

              // Look for answer options
              const answerOptions = Array.from(document.querySelectorAll('[role="button"]')).filter(el =>
                el.textContent && el.textContent.length > 0 && !el.textContent.includes('Skip')
              );

              // If we have a stored answer for this question
              if (answerMap.has(questionText) && config.highlightCorrectAnswers) {
                const correctAnswer = answerMap.get(questionText);

                // Find and highlight the correct answer
                answerOptions.forEach(option => {
                  if (option.textContent.trim() === correctAnswer) {
                    option.classList.add('correct-answer');

                    // Auto-answer if enabled
                    if (config.autoAnswerDelay > 0) {
                      setTimeout(() => {
                        option.click();
                      }, config.autoAnswerDelay);
                    }
                  }
                });
              }

              // Add click listeners to capture correct answers
              answerOptions.forEach(option => {
                option.addEventListener('click', function() {
                  // We'll check after a short delay if the answer was correct
                  setTimeout(() => {
                    // If we're still on the same question, the answer was wrong
                    // If we moved to a new question, the answer was correct
                    const currentQuestion = document.querySelector('[data-testid="question-text"]');
                    if (currentQuestion && currentQuestion.textContent.trim() !== questionText) {
                      // The answer was correct, store it
                      answerMap.set(questionText, option.textContent.trim());
                      updateAnswerStats();
                    }
                  }, 500);
                }, { once: true });
              });
            }
          }
        }
      }
    });
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true
  });

  console.log('Gimkit Enhanced Assistant loaded successfully!');
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址