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;
};