您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Export song results to Google Spreadsheet!
当前为
// ==UserScript== // @name AMQ Result Exporter // @namespace https://tampermonkey.net/ // @version 0.2.0 // @description Export song results to Google Spreadsheet! // @author SlashNephy <[email protected]> // @match https://animemusicquiz.com/ // @license MIT license // @grant GM_xmlhttpRequest // @icon https://animemusicquiz.com/favicon-32x32.png // @connect script.google.com // @connect raw.githubusercontent.com // ==/UserScript== const executeXhr = async (request) => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ ...request, onload: (response) => { resolve(response); }, onerror: (error) => { reject(error); }, }); }); }; const fetchArmEntries = async () => { const response = await executeXhr({ method: 'GET', url: 'https://raw.githubusercontent.com/kawaiioverflow/arm/master/arm.json', }); return JSON.parse(response.responseText); }; class AmqAnswerTimesUtility { songStartTime = 0; playerTimes = []; constructor() { if (typeof Listener === 'undefined') { return; } new Listener('play next song', () => { this.songStartTime = Date.now(); this.playerTimes = []; }).bindListener(); new Listener('player answered', (data) => { const time = Date.now() - this.songStartTime; data.forEach((gamePlayerId) => { this.playerTimes[gamePlayerId] = time; }); }).bindListener(); new Listener('Join Game', (data) => { const quizState = data.quizState; if (quizState) { this.songStartTime = Date.now() - quizState.songTimer * 1000; } }).bindListener(); } } const amqAnswerTimesUtility = new AmqAnswerTimesUtility(); const AMQ_createInstalledWindow = () => { if (!window.setupDocumentDone) return; if ($('#installedModal').length === 0) { $('#gameContainer').append($(` <div class="modal fade" id="installedModal" tabindex="-1" role="dialog"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h2 class="modal-title">Installed Userscripts</h2> </div> <div class="modal-body" style="overflow-y: auto;max-height: calc(100vh - 150px);"> <div id="installedContainer"> You have the following scripts installed (click on each of them to learn more)<br> This window can also be opened by going to AMQ settings (the gear icon on bottom right) and clicking "Installed Userscripts" <div id="installedListContainer"></div> </div> </div> </div> </div> </div> `)); $('#mainMenu') .prepend($(` <div class="button floatingContainer mainMenuButton" id="mpInstalled" data-toggle="modal" data-target="#installedModal"> <h1>Installed Userscripts</h1> </div> `)) .css('margin-top', '20vh'); $('#optionsContainer > ul').prepend($(` <li class="clickAble" data-toggle="modal" data-target="#installedModal">Installed Userscripts</li> `)); AMQ_addStyle(` .descriptionContainer { width: 95%; margin: auto; } .descriptionContainer img { width: 80%; margin: 10px 10%; } `); } }; const AMQ_addScriptData = (metadata) => { AMQ_createInstalledWindow(); $('#installedListContainer').append($('<div></div>') .append($('<h4></h4>') .html(`<i class="fa fa-caret-right"></i> ${metadata.name !== undefined ? metadata.name : 'Unknown'} by ${metadata.author !== undefined ? metadata.author : 'Unknown'}`) .css('font-weight', 'bold') .css('cursor', 'pointer') .click(function () { const selector = $(this).next(); if (selector.is(':visible')) { selector.slideUp(); $(this).find('.fa-caret-down').addClass('fa-caret-right').removeClass('fa-caret-down'); } else { selector.slideDown(); $(this).find('.fa-caret-right').addClass('fa-caret-down').removeClass('fa-caret-right'); } })) .append($('<div></div>') .addClass('descriptionContainer') .html(metadata.description !== undefined ? metadata.description : 'No description provided') .hide())); }; const AMQ_addStyle = (css) => { const head = document.head; const style = document.createElement('style'); head.appendChild(style); style.appendChild(document.createTextNode(css)); }; const GAS_URL = 'https://script.google.com/macros/s/xxx/exec'; const armEntries = []; fetchArmEntries() .then((entries) => armEntries.push(...entries)) .catch(console.error); const executeGas = async (row) => { await executeXhr({ url: GAS_URL, method: 'POST', data: JSON.stringify(row), }); }; const handle = (payload) => { const self = Object.values(quiz.players).find((p) => p.isSelf && p._inGame); if (!self) { return; } const result = { time: Date.now(), number: parseInt($('#qpCurrentSongCount').text()), game_mode: quiz.gameMode, song: { name: payload.songInfo.songName, anime: { answer: { english: payload.songInfo.animeNames.english, romaji: payload.songInfo.animeNames.romaji, alt_answers: [...new Set(payload.songInfo.altAnimeNames.concat(payload.songInfo.altAnimeNamesAnswers))], }, vintage: payload.songInfo.vintage, tags: payload.songInfo.animeTags, genre: payload.songInfo.animeGenre, mal_id: payload.songInfo.siteIds.malId, annict_id: armEntries.find((e) => e.mal_id === payload.songInfo.siteIds.malId)?.annict_id, type: payload.songInfo.animeType, score: payload.songInfo.animeScore, }, artist: payload.songInfo.artist, difficulty: payload.songInfo.animeDifficulty.toFixed(1), type: payload.songInfo.type === 3 ? 'Insert Song' : payload.songInfo.type === 2 ? `Ending ${payload.songInfo.typeNumber}` : `Opening ${payload.songInfo.typeNumber}`, file: { sample_point: quizVideoController.moePlayers[quizVideoController.currentMoePlayerId].startPoint, video_length: parseFloat(quizVideoController.moePlayers[quizVideoController.currentMoePlayerId].$player .find('video')[0] .duration.toFixed(2)), video_url: payload.songInfo.urlMap.catbox ? payload.songInfo.urlMap.catbox['720'] || payload.songInfo.urlMap.catbox['480'] : payload.songInfo.urlMap.openingsmoe ? payload.songInfo.urlMap.openingsmoe['720'] || payload.songInfo.urlMap.openingsmoe['480'] : null, audio_url: payload.songInfo.urlMap.catbox ? payload.songInfo.urlMap.catbox['0'] : payload.songInfo.urlMap.openingsmoe ? payload.songInfo.urlMap.openingsmoe['0'] : null, }, }, players: { count: Object.values(quiz.players).length, active_count: Object.values(quiz.players).filter((player) => !player.avatarSlot._disabled).length, correct_count: payload.players.filter((player) => player.correct).length, items: Object.values(payload.players) .sort((a, b) => { if (a.answerNumber !== undefined && b.answerNumber !== undefined) { return a.answerNumber - b.answerNumber; } const p1name = quiz.players[a.gamePlayerId]._name; const p2name = quiz.players[b.gamePlayerId]._name; return p1name.localeCompare(p2name); }) .map((p) => ({ status: p.listStatus, id: p.gamePlayerId, name: quiz.players[p.gamePlayerId]._name, score: p.score, correctGuesses: quiz.gameMode !== 'Standard' && quiz.gameMode !== 'Ranked' ? p.correctGuesses : p.score, correct: p.correct, answer: quiz.players[p.gamePlayerId].avatarSlot.$answerContainerText.text(), guessTime: amqAnswerTimesUtility.playerTimes[p.gamePlayerId], active: !quiz.players[p.gamePlayerId].avatarSlot._disabled, position: p.position, positionSlot: p.positionSlot, })), }, }; const selfResult = result.players.items.find((p) => p.id === self.gamePlayerId); const selfAnswer = selfResult?.answer.replace('...', '').replace(/ \(\d+ms\)$/, '') || ''; const row = [ result.time, result.number, result.game_mode, selfResult?.correct ?? false, selfAnswer, selfResult?.guessTime ?? 0, result.song.anime.answer.romaji, result.song.anime.answer.english, result.song.anime.answer.alt_answers.join('\n'), result.song.difficulty, result.song.type, result.song.anime.vintage, result.song.anime.type, result.song.anime.score, result.song.anime.mal_id, result.song.anime.annict_id ?? '', result.song.name, result.song.artist, result.song.anime.genre.join('\n'), result.song.anime.tags.join('\n'), result.song.file.video_url ?? '', result.song.file.audio_url ?? '', result.song.file.video_length, result.song.file.sample_point, result.players.correct_count, result.players.active_count, result.players.items .filter((p) => p.correct) .map((p) => p.name) .join('\n'), result.players.items.map((p) => p.name).join('\n'), selfResult?.status ?? 0, result.players.items .filter((p) => p.status) .map((p) => p.name) .join('\n'), ]; executeGas(row).catch(console.error); }; const listener = new Listener('answer results', handle); listener.bindListener(); AMQ_addScriptData({ name: 'Result Exporter', author: 'SlashNephy <[email protected]>', description: '<p>Export song results to Google Spreadsheet!</p>', });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址