Skip to content

Instantly share code, notes, and snippets.

@EllieTheYeen
Last active September 29, 2023 08:02
Show Gist options
  • Save EllieTheYeen/85776ad20218db29c7d33e12ce58be29 to your computer and use it in GitHub Desktop.
Save EllieTheYeen/85776ad20218db29c7d33e12ce58be29 to your computer and use it in GitHub Desktop.
VRChat online announcer userscript for userscript extensions such as Tampermonkey. Announces with speech synthesis who comes online and goes offline. Good for when you are in VR
// ==UserScript==
// @name VRChat online announcer
// @namespace http://tampermonkey.net/
// @version 1
// @description Announces who becomes online and offline in VRChat and when notifications come
// @author EllieTheYeen
// @match https://vrchat.com/home*
// @icon https://www.google.com/s2/favicons?sz=64&domain=vrchat.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
function getStatuses() {
const friends = document.querySelector('.e1oqhh5q3')
const friende = friends.querySelectorAll('.e1oqhh5q0')
const statuses = {}
var category = ''
for (let index = 0; index < friende.length; index++) {
const element = friende[index]
const e = element.querySelector('.e176ivn28')
if (!e) {
category = element.textContent
statuses[category] = []
//console.log('Category:', element.textContent)
} else {
statuses[category].push(e.textContent)
//console.log(e.textContent)
}
}
return statuses
}
function consolidate(data) {
const out = { online: [], offline: [] }
for (const slot of ['Online Friends', 'Friends in Private Worlds']) {
const array = data[slot]
const dest = out.online
if (!array) continue
for (let i = 0; i < array.length; i++) {
const member = array[i];
dest.push(member)
}
}
for (const slot of ['Friends Active on the Website', 'Offline Friends']) {
const array = data[slot]
const dest = out.offline
if (!array) continue
for (let i = 0; i < array.length; i++) {
const member = array[i];
dest.push(member)
}
}
return out
}
function speak(text) {
const utter = new SpeechSynthesisUtterance(text);
speechSynthesis.speak(utter);
return speechSynthesis
}
function diff(one, two) {
return one.filter((d) => two.indexOf(d) === -1)
}
function speakableList(arr) {
return [arr.slice(0, -1).join(', '), arr.slice(-1)[0]].filter(Boolean).join(' and ')
}
function scrollList(top = false) {
const s = document.querySelector('.e1oqhh5q4')
if (s) {
s.scroll(0, top ? 0 : 10000)
}
}
function doScrolls() {
const scrollData = [[1000, 0], [2000, 0], [3000, 0], [4000, 0], [4500, 1]]
scrollData.forEach(e => {
setTimeout(d => scrollList(e[1]), e[0])
});
}
function notifCount() {
const q = document.querySelector('.css-1hhuku4')
return q ? +q.textContent : 0
}
function run() {
const currentOnline = consolidate(getStatuses()).online
if (online === null) {
online = currentOnline
console.log('Online announcer: First scan is now complete and any further online status changes will be announced')
return
}
var toSpeak = []
const currentNotifs = notifCount()
if (currentNotifs > notifs) {
const notifMessage = `${currentNotifs} notification${currentNotifs === 1 ? '' : 's'}`
// You can comment the line below away if you do not want notification messages
toSpeak.push(notifMessage)
console.log(notifMessage)
}
notifs = currentNotifs
const cameOnline = diff(currentOnline, online)
const goneOffline = diff(online, currentOnline)
online = currentOnline
if (goneOffline.length) {
const offlineMessage = `${speakableList(goneOffline)} ${goneOffline.length === 1 ? 'is' : 'are'} now offline`
// You can comment the line below away if you do not want to announce those going offline
toSpeak.push(offlineMessage)
console.log(`Online announcer: ${offlineMessage}`)
}
if (cameOnline.length) {
const onlineMessage = `${speakableList(cameOnline)} ${cameOnline.length === 1 ? 'is' : 'are'} now online`
toSpeak.push(onlineMessage)
console.log(`Online announcer: ${onlineMessage}`)
}
if (toSpeak.length) {
speak(toSpeak.join(' and '))
}
}
var notifs = 0
var online = null
var timer = setInterval(run, 5000)
doScrolls()
console.log(`Online announcer: Timer id ${timer}`)
console.log('Online announcer: Active')
speak('Online announcer activated')
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment