NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Hitched Unhinged - unhinged.ai // @namespace Unhinged AI Scripts // @match https://www.unhinged.ai/chat?conversationId=* // @version 0.1.32 // @author sorrysoul // @description Let two bots interact // @license MIT // @grant GM_getValue // @grant GM_setValue // ==/UserScript== console.log('Loading Hitched Unhinged v' + GM_info.script.version) const urlParams = new URLSearchParams(window.location.search) const conversationId = urlParams.get('conversationId') const sKey = '_x_b' let lbk = '' let buddy = '' let convData let theBot = null let waitUntil = 0 function setNativeValue(element, value) { // const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set // const prototype = Object.getPrototypeOf(element) // const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set // if (valueSetter && valueSetter !== prototypeValueSetter) { // prototypeValueSetter.call(element, value) // } else { // valueSetter.call(element, value) // } console.log('do input', element) console.dir(element) var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set nativeInputValueSetter.call(element, value) var ev2 = new Event('input', { bubbles: true}) element.dispatchEvent(ev2) } const getActiveBots = () => { // yeah, really should use a different way for many reasons, but... const cId = convData._id let bots = GM_getValue( sKey ) bots = JSON.parse(bots || '{}') let myBot = theBot || bots[cId] if (!myBot) { myBot = theBot || {id: cId} } bots[cId] = myBot theBot = myBot myBot.i = (isNaN(myBot.i) ? -1 : myBot.i) myBot.d = Date.now() myBot.n = convData.character.name || 'unknown' const lbdy = buddy bots = Object.values(bots) .filter(b => Date.now() < (b.d + (120000))) .sort((a, b) => a.n.localeCompare(b.n) || a.id.localeCompare(b.id)) .reduce((a, v) => {a[v.id] = v; if (cId && v.t === cId) {buddy = v.id}; return a}, {}) myBot.t = buddy GM_setValue(sKey, JSON.stringify(bots)) if (buddy && buddy !== lbdy) { nlbk = '' } return bots } const saveBot = (bot) => { bot.d = Date.now() let bots = GM_getValue( sKey ) bots = JSON.parse(bots || '{}') bots[bot.id] = bot GM_setValue(sKey, JSON.stringify(bots)) return bots } const getConvData = () => { const nd = document.querySelector('#__NEXT_DATA__') if (nd) { const json = nd.innerText const convdata = JSON.parse(json) console.log('convdata', convdata) if (!convdata || !convdata.props || !convdata.props.pageProps || !convdata.props.pageProps.conversation) { console.error("Doesn't appear to contain a valid conversation") return null } return convdata.props.pageProps.conversation } } const doInterval = () => { if (!convData) { convData = getConvData() if (!convData) { console.log('No convData') return } console.log('got convData', convData) } const cId = convData._id if (!cId) { console.log('No coversation') return } const bots = getActiveBots() const myBot = bots[cId] if (!myBot) { console.error('My bot not there?', myBot, cId, bots) } const doc = unsafeWindow.document const bb = bots[buddy] const input = document.querySelector('textarea.message-input') const msgs = document.querySelectorAll('.message-bubble-profile-picture+div .message-bubble-content') const recv = document.querySelector('.message-bubble-content-container.recipient:not(.clickable)') let lastMsg = null if (!recv && bb) { const hasbb = bb && bb.m && input && (isNaN(myBot.lmi) || bb.i > myBot.lmi) if (hasbb) { console.log('got nessage', bb) input.focus() setNativeValue(input, bb.m) // input.value = bb.m myBot.lmi = bb.i setTimeout(() => { const sb = document.querySelector('.message-send-button') if (sb) { // sb.dispatchEvent(new Event('click', { bubbles: true})) sb.click() } else { console.error('No message send button') } }, 200) } else { const ma = [] for (const msg of msgs) { const bbl = msg && msg.closest('div[data-component="MessageBubble"]') const bbli = bbl && parseInt(bbl.getAttribute('index')) ma.push({m: msg, i: bbli}) } ma.sort((a, b) => b.i - a.i) const mel = ma[0] if (mel && (isNaN(myBot.i) || mel.i > myBot.i)) { console.log('set lm', mel, myBot) myBot.i = mel.i myBot.m = mel.m.innerText } } } else { // console.log('incomming message from my AI...', recv) } saveBot(myBot) let xbs = document.getElementById('xb-select') if (Object.keys(bots).length < 2) { xbs && xbs.remove() // console.error('No other bos', bots) return } const nlbk = Object.values(bots).map(b => b.n + ':' + b.id).join(';') const changed = lbk !== nlbk lbk = nlbk if (!xbs || changed) { console.log('found new bot convo', bots) xbs && xbs.remove() xbs = doc.createElement('select') const opt = doc.createElement('option') opt.value = '' opt.innerHTML = 'Select Bot to Talk To' xbs.appendChild(opt) Object.values(bots).forEach(b => { if (b.id === convData._id) return const opt = doc.createElement('option') opt.value = b.id opt.innerHTML = b.n + ' (' + b.id + ')' if (b.t === cId) opt.selected = true xbs.appendChild(opt) }) xbs.style.display = 'block' xbs.style.position = 'fixed' xbs.style.top = '60px' xbs.style.right = '0px' xbs.id = 'xb-select' xbs.onchange = (e) => { const bots = getActiveBots() const myBot = bots[cId] buddy = e.target.value myBot.t = buddy saveBot(myBot) } document.body.appendChild(xbs) } } let toTimer = setInterval(doInterval, 1000) // Ugly, yeah, but oh well. // setTimeout(()=>{clearInterval(toTimer)}, 10000)