PoonBrosAreValid
kiwifarms.net
- Joined
- Sep 15, 2023
The annoying thing about chat is you can't easily view the profiles of the people you are chatting with.
All the data needed to build the link is there, it just isn't used. It doesn't use any special features so should work on all userscript extensions.
All the data needed to build the link is there, it just isn't used. It doesn't use any special features so should work on all userscript extensions.
JavaScript:
// ==UserScript==
// @name Kiwifarms Profile Links Chat
// @namespace http://tampermonkey.net/
// @version 2024-10-08
// @description Adds a link to users profiles on the Kiwifarms chat.
// @author PoonBrosAreValid
// @match https://kiwifarms.st/chat/*
// @match https://kiwifarms.st/
// @icon https://kiwifarms.st/styles/custom/logos/kiwi_square.og.png
// @grant none
// ==/UserScript==
const chat = {
get frame() {
return document.querySelector("#rust-shim")
},
get document() {
return this.frame.contentWindow.document
},
buildUserUrl(id) {
const origin = new URL(location.href).origin
return `${origin}/members/${id}`
},
/** Formatted like "chat-activity-{{USER_ID}}" */
urlFromActivityId(activityId) {
const userId = activityId.replace('chat-activity-', '')
return this.buildUserUrl(userId)
},
/** Formatted like "/blah/blah/blah/{{USER_ID}}.jpg" */
urlFromMessageAvatarSrc(src) {
const path = new URL(location.origin + src).pathname
const pieces = path.split('/')
const userId = pieces[pieces.length - 1].replace('.jpg', '')
return this.buildUserUrl(userId)
},
/** Create and click a fake link to avoid popup warnings */
openProfile(url) {
const fakeLink = document.createElement('a')
fakeLink.href = url
fakeLink.target = '_blank'
fakeLink.click()
},
addAvatarClick(avatar, url) {
if (avatar.dataset.hasListener) return
avatar.style.cursor = 'pointer'
avatar.dataset.hasListener = 'true'
avatar.classList.add('clickable-avatar')
avatar.addEventListener('click', (_) => {
chat.openProfile(url)
})
},
observe(selector, callback) {
const observerOptions = {
childList: true,
subtree: true,
}
const elem = chat.document.querySelector(selector)
const observer = new MutationObserver(callback, observerOptions)
observer.observe(elem, observerOptions)
},
}
function onMembersChange(event) {
const unfilledLinks = chat.document.querySelectorAll('.chat .activity a:not([href])')
for (const link of unfilledLinks) {
const activityId = link.parentElement.id
const profileUrl = chat.urlFromActivityId(activityId)
link.href = profileUrl
link.style.textDecoration = 'none'
link.style.color = "white"
link.target = '_blank'
const username = link.innerText
link.title = `Go to ${username}'s profile`
const avatar = link.parentElement.querySelector('.avatar')
if (avatar) {
chat.addAvatarClick(avatar, profileUrl)
}
}
}
function onMessagesChange(event) {
const avatars = chat.document.querySelectorAll('.chat-message .avatar:not(.clickable-avatar)')
for (const avatar of avatars) {
const url = chat.urlFromMessageAvatarSrc(avatar.src)
chat.addAvatarClick(avatar, url)
}
}
function init() {
// Try to grab the frame and observe the chat
let timerInit = setInterval(() => {
if (!chat.frame) return
if (chat.document.readyState !== 'complete') return
chat.observe('#chat-activity', onMembersChange)
chat.observe('#chat-messages', onMessagesChange)
clearInterval(timerInit)
}, 500)
}
init()
Last edited: