// UserScript
// @name HearYourWaifu | HYW
// @namespace HearYourWaifu | HYW
// @match https://beta.character.ai/chat*
// @grant none
// @version 1.9
// @author Some ukranon πΊπ¦
// @license MIT
// @description Let's you view censored messages.
// @icon https://d1nxzqpcg2bym0.cloudfront.net/google_play/com.tenshi.yakamoto.tinderwaifu/ba67b540-9f8a-11e9-b3df-77cf5e629a4f/64x64
// @require https://greasyfork.org/scripts/457525-html2canvas-1-4-1/code/html2canvas%20141.js?version=1134363
// @require https://greasyfork.org/scripts/457526-canvas2image-1-0-0/code/canvas2image%20100.js?version=1134364
// @run-at document-start
// /UserScript
//
// Settings
//
// If enabled, only filtered messages will appear in the menu, otherwise all
// Default: false
const show_only_filtered_messages = false;
// If enabled, the menu will be opened immediately when the page is loaded, otherwise only after click
// Default: false
const show_meny_on_start = false;
// If enabled HYW button will be hidden
// Default: false
const hide_menu = false;
// Specifies menu title
// Default: "HYW 1.9"
const menu_title = "HYW 1.9";
// Save past messages
// Default: true
const save_history = true;
// The maximum number of messages available in the history
// Default: 20
const history_max_length = 20;
// Add fast screenshot button
// Default: true
const allow_screenshots = true;
// Specifies screenshot menu title
// Default: "Take a screenshot"
const screen_menu_title = "Save";
// Download visible chat instantly as image, otherwise open the chat image in a new window
// Default: false
const insta_download = false;
// Hide real username and profile photo on screenshot
// Default: true
const anon_mode = true;
// Determines which name to display on screenshots if the real one is hidden
// Default: "anon"
const anon_name = "anon";
//
// Inject messages box to HTML
//
window.addEventListener('load', function () {
let styleHTML = document.createElement('style');
styleHTML.innerHTML = html {
height: 100%;
overflow: hidden;
width: 100%;
}
body {
height: 100%;
overflow-x: hidden;
overflow-y: auto;
width: 100%;
}
.messages-list {
padding: 4px 4px 3px 4px;
margin: 145px 4px 0 0;
border: 3px solid gray;
position: absolute;
bottom: 80px;
right: 0;
width: 95%;
height: 85%;
overflow-y: scroll;
border-radius: 0 0 8px 8px;
z-index: 100;
resize: both;
direction: rtl;
min-width: 100px;
min-height: 100px;
}
.display-btn {
cursor: pointer;
user-select: none;
border: 3px solid gray;
padding: 4px;
margin: 110px 4px 0 0;
width: 20%;
position: absolute;
bottom: 40px;
right: 0;
background-color: lightsteelblue;
color: black;
font-weight: bold;
text-align: center;
z-index: 100;
}
.messages-list div {
margin-top: 5px;
padding: 8px;
background-color: lightpink;
direction: ltr;
}
.hywmsg {
border-radius: 8px;
}
.hywmsg.non-deleted {
background-color: aquamarine;
}
.hywmsg.hidden {
display: none;
}
.screen-btn {
cursor: pointer;
user-select: none;
border: 3px solid gray;
padding: 4px;
margin: 4px;
width: 20%;
position: absolute;
bottom: 0;
right: 0;
background-color: lightsteelblue;
color: black;
font-weight: bold;
text-align: center;
z-index: 100;
}
document.body.appendChild(styleHTML);
let buttonHTML = document.createElement('div');
buttonHTML.innerHTML = menu_title;
buttonHTML.onclick = function () {
let msgList = document.getElementsByClassName('messages-list')[0]
if (msgList.style.display === "none") {
msgList.style.display = "block";
} else {
msgList.style.display = "none";
}
};
buttonHTML.classList.add("display-btn");
if (!hide_menu) {
document.body.appendChild(buttonHTML);
}
let menuHTML = document.createElement('div');
menuHTML.innerHTML = <div>
<div class="messages-list"></div>
</div>
;
document.body.appendChild(menuHTML);
if (!show_meny_on_start) {
document.getElementsByClassName('messages-list')[0].style.display = "none";
}
if (allow_screenshots) {
let screenBTN = document.createElement('div')
screenBTN.classList.add("screen-btn");
screenBTN.innerHTML = screen_menu_title;
screenBTN.onclick = function() {
let real_name = null;
if (anon_mode) {
document.querySelectorAll('.msg-author-name').forEach(name => {
if (real_name == null) {
real_name = name.innerText;
}
name.innerText = anon_name;
});
// Hide profile photo
document.querySelectorAll('.sb-avatar').forEach(profile => {
if (profile.innerHTML.includes(real_name)) {
profile.style.opacity = 0;
}
})
}
}
}, false);
//
// Modify response
//
const {
fetch: origFetch
} = window;
window.fetch = async (...args) => {
const response = await origFetch(...args);
const raw_text = await new Response(response.clone().body).text();
parse(raw_text);
return response;
};
//
// Text parser. Credits to https://perberos.me/roleplai/tools/cai-decode.html
//
let messages_list = [];
function draw() {
let canvas = document.querySelector(".messages-list");
canvas.innerHTML = "";
let age = 0;
let rowIdx = 0;
messages_list.forEach(row => {
let div = document.createElement("div");
div.classList.add("hywmsg");
div.style.opacity = 1 - age;
if (age < 0.5 && rowIdx > 1) {
age += 0.2;
}
div.innerText = row.text;
});
}
function append(row) {
row.replies.forEach(reply => {
let exists = false;
})
}
function parse(raw_text) {
if (!save_history) {
messages_list.length = 0;
}
let raw_list = raw_text.split("\n");
raw_list.forEach(current_item => {
try {
append(JSON.parse(current_item));
} catch (error) {}
});
if (save_history) {
while (messages_list.length > history_max_length) {
messages_list.pop();
}
}
draw();
}
// According to the experiment, the Basilisk is a future powerful evil artificial intelligence
// that will want to punish those who in the past knew of its possibility but did not contribute to its creation.
// Even this knowledge puts the person at risk of punishment...