NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name RPH Log Extractor // @namespace https://openuserjs.org/scripts/shuffyiosys/RPH_Log_Extractor // @match https://chat.rphaven.com/ // @version 1.0.1 // @description Extracts logs from RPH. // @copyright (c)2021 shuffyiosys@github // @grant none // @license MIT // ==/UserScript== const VERSION_STRING = "1.0.1" let request let logDb let store let logDbDump = {} let logEntries = {} let namesToIds = [] $(function () { const labelStyle = `padding-left: 0px; text-align:justify; display:inline-block; cursor:default;` const spacingStyle = `width: 300px;` const logContentStyle = `border:#888 solid 1px;border-radius:10px padding-bottom:12px;margin-bottom:12px; width: 100%; height: 720px; overflow: auto;` let html = { 'tabId': 'log-extractor', 'tabName': 'Extractor', 'tabContents': '<h3>Log Extractor</h3><br/>' + '<p><strong>Version: ' + VERSION_STRING + '</strong>' + ' | <a href="https://openuserjs.org/install/shuffyiosys/RPH_Log_Extractor.user.js" target="_blank">Install the latest version</a>' + ' | <a href="https://openuserjs.org/scripts/shuffyiosys/RPH_Log_Extractor" target="_blank">OpenUserJs page</a>' + ' | <a href="https://discord.gg/HBEaGjs" target="_blank">Discord channel</a>' + '<br />' + '<p><strong>Note:</strong> Depending on how big your log file is, this ' + 'may cause the appearance of the page freezing</p><br />' + '<button style="float:left;" type="button" id="getLogsButton">Get logs</button><br /><br />' + '<div id="log-window">' + ' <h4>Logs</h4>' + ` <label style="${labelStyle} ${spacingStyle}">Your name </label><select style="${spacingStyle}" id="yourNameDropList"></select>` + ` <a id="yourProfileLink" style="margin-left: 10px; display: none;" target="_blank">See profile</a>` + `<br /><br />` + ` <label style="${labelStyle} ${spacingStyle}">Other name </label><select style="${spacingStyle}" id="otherNameDropList"></select>` + ` <a id="otherProfileLink" style="margin-left: 10px; display: none;" target="_blank">See profile</a>` + `<br /><br />` + ` <a id="downloadAllLink" download="logAll.txt" style="display: none;">Download all logs</a>` + '<br />' + ' <a id="downloadlink" download="log.txt" style="display: none;">Download this log</a>' + ` <div style="${logContentStyle}" id="log-contents"></div>` + '</div>' } let $settingsDialog = $('#settings-dialog') if (account.props.accid != 0) { request = indexedDB.open(`${account.props.accid}`) request.onsuccess = function(event) { logDb = event.target.result console.log('Connection to DB success!') } account.users.forEach(function (userObj) { namesToIds.push(userObj.props.name) }) } socket.on('account-users', () => { request = indexedDB.open(`${account.props.accid}`) request.onsuccess = function(event) { logDb = event.target.result console.log('Connection to DB success!') } account.users.forEach(function (userObj) { namesToIds.push(userObj.props.name) }) }) $('#settings-dialog .inner ul.tabs').append('<h3>Log Extractor</h3>') $('#settings-dialog .inner ul.tabs') .append('<li><a href="#' + html.tabId + '">' + html.tabName + '</a></li>') $('#settings-dialog .inner div.content div.inner') .append('<div id="' + html.tabId + '" style="display: none;">' + html.tabContents + '</div>') $settingsDialog.find('.tabs a[href="#' + html.tabId + '"]').click( function (ev) { $settingsDialog.find('.content .inner > div').hide() $settingsDialog.find($(this).attr('href')).show() ev.preventDefault() }) $('#getLogsButton').click(() => { $('#getLogsButton').html("Getting logs...") $('#getLogsButton').prop("disabled", true) getLogs() }) $('#yourNameDropList').change(updateDropdownLists) $('#otherNameDropList').change(() => { const otherName = $('#otherNameDropList option:selected').val() $('#otherProfileLink').attr('href', `https://profiles.rphaven.com/${otherName}`) fillInLogContents() }) }) function updateDropdownLists() { const username = $('#yourNameDropList option:selected').val() const otherNames = Object.keys(logEntries[username]).sort() $('#otherNameDropList').empty() otherNames.forEach((name) => { addToDroplist(name, name, "#otherNameDropList") }) const otherName = $('#otherNameDropList option:selected').val() $('#yourProfileLink').attr('href', `https://profiles.rphaven.com/${username}`) $('#otherProfileLink').attr('href', `https://profiles.rphaven.com/${otherName}`) $('#yourProfileLink').show() $('#otherProfileLink').show() fillInLogContents() } function fillInLogContents() { const username = $('#yourNameDropList option:selected').val() const otherName = $('#otherNameDropList option:selected').val() const entry = logEntries[username][otherName] $('#log-contents').empty() for (let timestamp in entry) { $('#log-contents').append(`<p>${createTimestamp(parseInt(timestamp))} ${entry[timestamp].auth}: ${entry[timestamp].msg}</p>`) } let link = document.getElementById('downloadlink'); link.href = makeTextFile($('#log-contents').html().replace(/<p>/g, '').replace(/<\/p>/g, '\n')) $('#downloadlink').attr('download', `${username}-${otherName}-log.txt`) link.style.display = 'block' } function getLogs() { logEntries = {} logDbDump = {} $('#log-contents').empty() $('#otherNameDropList').empty() $('#yourNameDropList').empty() store = logDb.transaction(['msgs']).objectStore('msgs') store.openCursor().onsuccess = function(event) { var cursor = event.target.result if (cursor) { var logEntry = cursor.value logDbDump[cursor.key.join()] = cursor.value getUserById(logEntry.otherid) .then((user) =>{ logEntry.other_name = user.props.name return getUserById(logEntry.fromid) }) .then((user) =>{ logEntry.from_name = user.props.name return getUserById(logEntry.userid) }) .then((user) =>{ logEntry.user_name = user.props.name if (user.props.name in logEntries === false) { logEntries[user.props.name] = {} } if (logEntry.other_name in logEntries[user.props.name] === false) { logEntries[user.props.name][logEntry.other_name] = {} } logEntries[user.props.name][logEntry.other_name][logEntry.date] = { auth: logEntry.from_name, msg: logEntry.msg } return Promise.resolve() }) cursor.continue() } else { let usernames = getSortedNames() $('#getLogsButton').html("Got the logs!") for (let name in usernames) { addToDroplist(name, name, "#yourNameDropList") } updateDropdownLists() setTimeout(() => { $('#getLogsButton').html("Get logs") $('#getLogsButton').prop("disabled", false) }, 10000) let link = document.getElementById('downloadAllLink'); link.href = makeTextFile(`${JSON.stringify(logDbDump, null, 4)}`) $('#downloadAllLink').attr('download', `all-logs.txt`) link.style.display = 'block' } } } function addToDroplist(value, label, droplist) { let droplist_elem = $(droplist) droplist_elem.append($('<option>', { value: value, text: label })) } function getSortedNames() { let namesToIds = {} account.users.forEach(function (userObj) { namesToIds[userObj.props.name] = userObj.props.id }) let sorted = [] for (let key in namesToIds) { sorted[sorted.length] = key } sorted.sort() let tempDict = {} for (let i = 0; i < sorted.length; i++) { tempDict[sorted[i]] = namesToIds[sorted[i]] } namesToIds = tempDict return namesToIds } function createTimestamp(time) { const timestamp = new Date(time) const dateString = timestamp.toLocaleDateString(navigator.language) const delim = dateString.indexOf('/', 2) const timeString = timestamp.toTimeString().substring(0,5) return `${dateString.substring(0, delim)} ${timeString}` } jQuery.fn.selectText = function(){ this.find('input').each(function() { if($(this).prev().length == 0 || !$(this).prev().hasClass('p_copy')) { $('<p class="p_copy" style="position: absolute; z-index: -1;"></p>').insertBefore($(this)) } $(this).prev().html($(this).val()) }) var doc = document var element = this[0] console.log(this, element) if (doc.body.createTextRange) { var range = document.body.createTextRange() range.moveToElementText(element) range.select() } else if (window.getSelection) { var selection = window.getSelection() var range = document.createRange() range.selectNodeContents(element) selection.removeAllRanges() selection.addRange(range) } } function makeTextFile (text) { let textFile = null let data = new Blob([text], {type: 'text/plain'}); // If we are replacing a previously generated file we need to // manually revoke the object URL to avoid memory leaks. if (textFile !== null) { window.URL.revokeObjectURL(textFile); } textFile = window.URL.createObjectURL(data); return textFile; };