您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A utility to grab and compile chat histories, for parsing, archiving or viewing in an accompanying WIP chat history viewer.
当前为
// ==UserScript== // @name Amino Chat Grabber // @namespace http://tampermonkey.net/ // @version 1.6 // @description A utility to grab and compile chat histories, for parsing, archiving or viewing in an accompanying WIP chat history viewer. // @author Rasutei // @match https://aminoapps.com // @icon https://www.google.com/s2/favicons?sz=64&domain=aminoapps.com // @grant none // @license GNU GPLv3 // ==/UserScript== /* eslint-disable curly */ const logcss = ` font-family: Hack, monospace; text-shadow: 0 0 10px black, 0 0 10px black; background: linear-gradient(to right, #4d94ff 0%, #4d94ff 8px, rgb(77 148 255 / 30%) 8px, transparent 50px); color: #4d94ff; padding: 2px 0 2px 30px; ` const warncss = ` font-family: Hack, monospace; text-shadow: 0 0 10px black, 0 0 10px black; background: linear-gradient(to right, #ffa621 0%, #ffa621 0% 8px, rgb(255 166 33 / 30%) 8px, transparent 50px); color: #ffa621; padding: 2px 0 2px 30px; ` const log = (e) => console.log('%c'+e, logcss) const warn = (e) => console.log('%c'+e, warncss) const warn_element = (e) => console.log('%c%o', warncss, e) function Community(name){ this.name = name this.link = '' this.icon = '' this.chats = [] } function Entry(username){ this.username = username this.link = '' this.avatar = '' this.cover = '' this.oldest_timestamp = '' this.history = [] } function Message(){ this.type = '' this.user = '' this.content = '' } let chat let msglist let rb = 0 window.addEventListener('load', function(){ log('Chat Grabber: Loading...') let hackfont = document.createElement('link') hackfont.setAttribute('rel','stylesheet') hackfont.setAttribute('type','text/css') hackfont.setAttribute('href','//cdn.jsdelivr.net/npm/[email protected]/build/web/hack-subset.css') document.head.appendChild(hackfont); let btnCSS = ` box-sizing: border-box; width: 260px; background: rgb(10 10 10); border: solid 2px hsl(var(--rainbow), 100%, 50%); color: hsl(var(--rainbow), 100%, 90%); font-family: Hack; font-size: 14px; margin-top: -2px; cursor: pointer; opacity: 0; display: none;` let wrapper = document.createElement("wrapper") wrapper.className = "master-wrapper" wrapper.style.cssText = ` position:fixed; top:0; left:0; z-index:99999999; display:flex; flex-direction:column;` wrapper.style.setProperty('--rainbow', '0deg') let alwayson = document.createElement("div") alwayson.style.order = 0 wrapper.appendChild(alwayson) let toggle = document.createElement("button") toggle.className = "ras_button toggle nohide" toggle.innerText = '▣' toggle.style.cssText = btnCSS toggle.style.fontSize = '16px' toggle.style.display = 'block' toggle.style.float = 'left' toggle.style.opacity = 1 // toggle.style.order = 0 toggle.style.width = '21px' toggle.style.height = '21px' toggle.style.padding = 0 toggle.style.margin = 0 toggle.onclick = function(){ wrapper.querySelectorAll(".ras_button").forEach(m => { if (!m.classList.contains('nohide')){ m.style.opacity = +!+(m.style.opacity) if (m.style.display != 'none') m.style.display = 'none' else m.style.display = 'inline-block' } }) } alwayson.appendChild(toggle) let title = document.createElement("button") title.className = "ras_button title nohide" title.innerText = 'Chat Grabber 1.6, by Rasutei' title.style.cssText = btnCSS title.style.display = 'block' title.style.opacity = 1 // title.style.order = 1 title.style.height = '21px' title.style.width = '241px' title.style.float = 'left' title.style.padding = 0 title.style.margin = '0 0 0 -2px' alwayson.appendChild(title) let genBase = document.createElement("button") genBase.className = "ras_button gen-base" genBase.innerText = 'Generate base JSON structure' genBase.onclick = GenBase genBase.style.cssText = btnCSS genBase.style.order = 2 genBase.style.opacity = 0 wrapper.appendChild(genBase) let cmpCommunity = document.createElement("button") cmpCommunity.className = "ras_button gen-community" cmpCommunity.innerText = 'GenJSON cur. comm' cmpCommunity.onclick = GrabCommunity cmpCommunity.style.cssText = btnCSS cmpCommunity.style.order = 3 cmpCommunity.style.opacity = 0 wrapper.appendChild(cmpCommunity) let cmpCurrent = document.createElement("button") cmpCurrent.className = "ras_button gen-chat" cmpCurrent.innerText = 'GenJSON cur. chat' cmpCurrent.onclick = GrabCurrent cmpCurrent.style.cssText = btnCSS cmpCurrent.style.order = 4 cmpCurrent.style.opacity = 0 wrapper.appendChild(cmpCurrent) document.body.insertBefore(wrapper, document.body.firstChild) setInterval(function(){ wrapper.style.setProperty('--rainbow', rb+++'deg') if (rb == 360) rb = 0 }, 20) log('Chat Grabber: Loaded.') }) function Update(){ chat = document.querySelector("iframe").contentDocument msglist = chat.querySelector(".message-list") } function GenBase(){ let gen = `{ "gen_version": 1.6, "cur_version": 1.6, "communities":[ // Add JSON structures of communities // here, separated by commas. // Example structure: // { // "name": "", // "link": "", // "chats": [ // { // "username": "", // "link": "", // "avatar": "", // "cover": "", // "oldest_timestamp": "", // "history": [ // { // "type": "", "user": "", // "content": "" // }, // [...] // { // "type": "", "user": "", // "content": "" // } // ] // }, // ] // }, // [...] // ### IMPORTANT! ### // DELETE ALL COMMENTS BEFORE USE // (Comments are lines starting in "//") ] } ` console.log(gen) if(confirm('Done. Would you like to have the resulting JSON string copied to the clipboard?')) setTimeout(() => navigator.clipboard.writeText(gen), 200) else alert('JSON string not copied to the clipboard.\nAccess the browser\'s console to view or copy it.') } async function GrabCommunity(){ Update() let entry = new Community(JSONSafe(chat.querySelector(".community-title").textContent.trim())) /* Grab community name, link and icon */ entry.link = chat.querySelector(".community-title :first-child").href entry.icon = chat.querySelector(".community-title img.logo").src /* Post resulting entry */ let gen = JSON.stringify(entry, null, "\t") console.log(gen) /* Copying resulting entry to clipboard */ if(confirm('Done. Would you like to have the resulting JSON string copied to the clipboard?')) setTimeout(() => navigator.clipboard.writeText(gen), 200) else alert('JSON string not copied to the clipboard.\nAccess the browser\'s console to view or copy it.') } async function GrabCurrent(){ Update() /* Create entry for current chat */ let user = JSONSafe(chat.querySelector(".thread-title").textContent.trim()) let entry = await new Entry(user) /* Grab images */ chat.querySelector(".user-message:not(.from-me)").querySelector(".message-author.cover-img").click() await new Promise(r => setTimeout(r, 500)); let profile = chat.querySelector(".user-profile") if (profile.querySelector(".user-cover .img-cover")) entry.cover = profile.querySelector(".user-cover .img-cover").src if (profile.querySelector(".user-link")) entry.link = profile.querySelector(".user-link").href if (profile.querySelector(".avatar")) entry.avatar = profile.querySelector(".avatar").src /* Scroll chat history up as far as possible */ let message_count = 0; let prv_msg_count; while (message_count != prv_msg_count){ prv_msg_count = message_count // console.log("Scrolling...") msglist.scrollTo(top) await new Promise(r => setTimeout(r, 300)) message_count = msglist.childElementCount } /* Grab oldest timestamp */ if (chat.querySelector(".timestamp")) entry.oldest_timestamp = chat.querySelector(".timestamp").textContent.trim() /* Compile messages */ let messages = Array.from(msglist.children); messages.forEach(m => { if (m.classList.contains("user-message")){ /* Create message object */ let msg = new Message() /* Set message author */ if (m.classList.contains("from-me")) msg.user = "Me" else msg.user = user /* Set message content */ /* If message is a sticker */ if (m.querySelector(".sticker-message")){ msg.type = "sticker" msg.content = m.querySelector(".sticker-message").firstChild.src } /* If message is an image */ else if (m.querySelector(".img-msg")){ msg.type = "image" msg.content = m.querySelector(".img-msg").firstChild.src } /* If message is a voice message */ else if (m.querySelector(".voice-message-container")){ msg.type = "audio" msg.content = m.querySelector(".voice-message-container audio").src } /* If message is text */ else if (m.querySelector(".text-msg")){ msg.type = "text" msg.content = JSONSafe(m.querySelector(".text-msg").innerHTML) /* Replace tags and restore formatting information */ msg.content = msg.content.replaceAll('<p>','') msg.content = msg.content.replaceAll('</p>','\n') msg.content = msg.content.replaceAll('<p class="', '[') msg.content = msg.content.replaceAll('">', ']') msg.content = msg.content.replaceAll('center', 'C') msg.content = msg.content.replaceAll('italic', 'I') msg.content = msg.content.replaceAll('bolder', 'B') msg.content = msg.content.replaceAll('strike', 'S') msg.content = msg.content.replaceAll('underline', 'U') let toreplace = msg.content.substring(msg.content.indexOf('[')+1,msg.content.indexOf(']')) msg.content = msg.content.replace(toreplace, toreplace.replaceAll(' ', '')) /* Remove trailing line break */ msg.content = msg.content.substring(0, msg.content.length-1) /* Try to check if message is meant to be a comment in a scene, or out-of-character */ let rawmsg = msg.content.substring(((msg.content.indexOf(']') == -1)? 0 : msg.content.indexOf(']')+2)) let cmt = rawmsg.startsWith('||') || rawmsg.startsWith('((') || rawmsg.endsWith('||') || rawmsg.endsWith('))') if (cmt) msg.type += " comment" } /* If message is unknown type */ else{ warn("Uncaught message type: "+m.querySelector(".message-content :first-child").className) console.group("Message") warn_element(m) console.groupEnd() } entry.history.push(msg) } }) /* Post resulting entry */ let gen = JSON.stringify(entry, null, "\t").replaceAll(',\n\t\t\t"user',', "user').replaceAll('},\n\t\t{','},{').replaceAll('\n','\n\t\t\t\t') console.log(gen) /* Copying resulting entry to clipboard */ if(confirm('Done. Would you like to have the resulting JSON string copied to the clipboard?')) setTimeout(() => navigator.clipboard.writeText(gen), 200) else alert('JSON string not copied to the clipboard. Access the browser\'s console to view or copy it.') } function JSONSafe(s){ return s.replaceAll('"', '\"') }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址