c.ai Neo Panel Edit Message

Lets you edit the last message

ของเมื่อวันที่ 29-09-2023 ดู เวอร์ชันล่าสุด

// ==UserScript==
// @name          c.ai Neo Panel Edit Message
// @namespace     c.ai Neo Panel Edit Message
// @match         https://*.character.ai/chat*
// @version       1.1
// @description   Lets you edit the last message
// @author        vishanka and jenpai
// @license       MIT
// @icon          https://i.imgur.com/iH2r80g.png
// @grant         none
// ==/UserScript==


(function() {
    'use strict';

    // Open a WebSocket connection at the start of the script
    const socket = new WebSocket('wss://neo.character.ai/ws/');

    // Listen for the WebSocket connection to open
    socket.addEventListener('open', () => {
        console.log('[CUSTOM] WebSocket connection opened.');
    });

    // Listen for errors
    socket.addEventListener('error', (error) => {
        console.error('[CUSTOM] WebSocket error:', error);
    });

    // Listen for the WebSocket connection to close
    socket.addEventListener('close', () => {
        console.log('[CUSTOM] WebSocket connection closed.');
    });

    // Create a button element
const button = document.createElement('button');
button.id = 'editButton';
button.style.position = 'absolute';
button.style.top = '18px';
button.style.width = 'auto';
button.style.height = '18px';
button.style.padding = '0';
button.style.zIndex = '9999';
button.style.border = 'none';
button.style.display = 'flex';
button.style.justifyContent = 'center';
button.style.alignItems = 'center';
button.style.backgroundColor = 'transparent';
button.style.right = '140px';

  // Adjust 'left' style to 65% for phones
function updateLeftStyle() {
  if (window.innerWidth <= 600) {
    // For phones (screen width <= 600px)
    button.style.right = '20%';
  } else {
    // For other devices
    button.style.right = '140px';
  }
}
// Update the 'left' style initially
updateLeftStyle();

// Update the 'left' style when the window is resized
window.addEventListener('resize', updateLeftStyle);



const iconContainer = document.createElement('div');
iconContainer.style.display = 'flex';
iconContainer.style.justifyContent = 'center';
iconContainer.style.alignItems = 'center';

// Add the text before the icon in the iconContainer
iconContainer.innerHTML = '<span style="margin-right: 5px; position: relative; top: -3px; color: #C1BBAF; font-weight: bold;">Save</span><svg stroke="currentColor" fill="#C1BBAF" stroke-width="0" viewBox="0 4 24 24" height="20" width="20" xmlns="http://www.w3.org/2000/svg" style="--darkreader-inline-fill: currentColor; --darkreader-inline-stroke: currentColor;" data-darkreader-inline-fill="" data-darkreader-inline-stroke=""><path fill="none" d="M0 0h24v24H0z"></path><path d="M19 12v7H5v-7H3v7c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-7h-2zm-6 .67l2.59-2.58L17 11.5l-5 5-5-5 1.41-1.41L11 12.67V3h2z"></path></svg>';

// Append the iconContainer to the button
button.appendChild(iconContainer);
// Add mouseover event listener to change background color
button.addEventListener('mouseover', () => {
  button.style.backgroundColor = '#3B362D';
});

// Add mouseout event listener to reset background color
button.addEventListener('mouseout', () => {
  button.style.backgroundColor = 'transparent';
});

// Set the SVG as the innerHTML of the button




const textBox = document.createElement('textarea');
textBox.placeholder = 'Enter new reply';
textBox.style.position = 'absolute';
textBox.style.top = '45px';
textBox.style.left = '60px';
textBox.style.zIndex = '9999';
textBox.style.width = '85%';
textBox.style.resize = 'none';
textBox.style.overflowY = 'auto';
textBox.style.height = '60%';

textBox.addEventListener('input', () => {
  const maxLength = 1000;
  const currentText = textBox.value;

  if (currentText.length > maxLength) {
    textBox.value = currentText.slice(0, maxLength);
  }
});

function updatetextBoxWidth() {
  if (window.innerWidth <= 600) {
    // For phones (screen width <= 600px)
textBox.style.width = '73%';
  } else {
    // For other devices
textBox.style.width = '85%';
  }
}
// Update the 'left' style initially
updatetextBoxWidth();

// Update the 'left' style when the window is resized
window.addEventListener('resize', updatetextBoxWidth);
// Create a button to toggle the visibility of the textBox
const toggleButton = document.createElement('button');
toggleButton.id = 'toggleButton';
toggleButton.style.position = 'absolute';
toggleButton.style.top = '18px';
toggleButton.style.left = '86.5%';
toggleButton.style.zIndex = '9999';
toggleButton.style.border = 'none';
toggleButton.style.backgroundColor = 'transparent';
toggleButton.style.width = '18px';
toggleButton.style.height = '18px';
toggleButton.style.padding = '0';
toggleButton.style.display = 'flex';
toggleButton.style.justifyContent = 'center';
toggleButton.style.alignItems = 'center';

const svgHTMLtoggle = '<svg stroke="currentColor" fill="#C8C5BE" stroke-width="0" viewBox="0 0 24 24" height="24" width="24" xmlns="http://www.w3.org/2000/svg" style="--darkreader-inline-fill: currentColor; --darkreader-inline-stroke: currentColor;" data-darkreader-inline-fill="" data-darkreader-inline-stroke=""><path fill="none" d="M0 0h24v24H0z"></path><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75zM20.71 5.63l-2.34-2.34a.996.996 0 00-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83a.996.996 0 000-1.41z"></path></svg>';
toggleButton.innerHTML = svgHTMLtoggle;
// Event listener to toggle the visibility of the textBox


toggleButton.addEventListener('click', () => {

const parentDiv = document.querySelector('.swiper');
//const parentDivbutton = document.querySelector('div.swiper-slide:nth-child(1) > div:nth-child(1)');
// Append the text box to the parent div
parentDiv.appendChild(textBox);
parentDiv.appendChild(button);

  if (textBox.style.display === 'none' || textBox.style.display === '') {
    textBox.style.display = 'block';
    button.style.display = 'block';
    toggleButton.style.backgroundColor = '#3B362D';
  } else {
    textBox.style.display = 'none';
    button.style.display = 'none';
    toggleButton.style.backgroundColor = 'transparent';
  }
});

const apiKey = JSON.parse(localStorage.getItem('char_token')).value;
const urlPattern = /^https:\/\/neo\.character\.ai\/turns\/[a-zA-Z0-9-]+\/$/;
let storedURL = null;
let chatId = null;
let turnId = null;

const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;

XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
    if (urlPattern.test(url)) {
        this._url = url;
        storedURL = url;
    }
    originalOpen.apply(this, arguments);
};

XMLHttpRequest.prototype.send = function() {
    const self = this;
    const originalOnLoad = this.onload;

    this.onload = function() {
        if (self._url && urlPattern.test(self._url)) {
            try {
                if (self.status === 200) {
                    const responseData = JSON.parse(self.responseText);
                    chatId = responseData.turns[0].turn_key.chat_id;
                    turnId = responseData.turns[0].turn_key.turn_id;
                    console.log('Chat ID:', chatId);
                    console.log('Turn ID:', turnId);
                }
            } catch (error) {
                console.error("Error parsing response:", error);
            }
        }

        if (originalOnLoad) {
            originalOnLoad.apply(this, arguments);
        }
    };

    originalSend.apply(this, arguments);
};



button.addEventListener('click', () => {
    try {
        const newContent = textBox.value;

        fetch(storedURL, {
            headers: {
                'Authorization': 'Token ' + apiKey
            }
        })
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.json();
        })
        .then(data => {
            if (data.turns && data.turns[0] && data.turns[0].turn_key) {
                chatId = data.turns[0].turn_key.chat_id;
                turnId = data.turns[0].turn_key.turn_id;
                console.log('Chat ID:', chatId);
                console.log('Turn ID:', turnId);

                const commandJson_edit = JSON.stringify({
                    command: "edit_turn_candidate",
                    request_id: "",
                    payload: {
                        turn_key: {
                            chat_id: chatId,
                            turn_id: turnId
                        },
                        new_candidate_raw_content: newContent,
                        origin_id: ""
                    }
                });

                console.log('[CUSTOM] Sending socket message:', JSON.parse(commandJson_edit));


              socket.send(commandJson_edit);
//              window.location.reload();
            } else {
                console.error('Unable to retrieve chat_id and turn_id from the response.');
            }
        })
        .catch(error => {
            console.error('Error retrieving data:', error);
        });

    } catch (error) {
        console.error('[CUSTOM] Error sending command over WebSocket:', error);
    }
});





    // Handle WebSocket messages
    socket.addEventListener('message', (event) => {
        try {
            const response = JSON.parse(event.data);
            console.log('[CUSTOM] Received message:', response);

            if (response.command === 'update_turn') {
                //console.log('[CUSTOM] Processing update_turn command');
            window.location.reload();

            }
        } catch (error) {
            console.error('[CUSTOM] Error handling WebSocket message:', error);
        }
    });


// Function to append the toggleButton to the .swiper element
function appendToggleButtonToSwiper() {
  const parentDiv = document.querySelector('.swiper');
  if (parentDiv) {
    if (!parentDiv.contains(toggleButton)) {
      parentDiv.appendChild(toggleButton);
    }
  }
}

// Mutation observer to watch for changes in the DOM
const observer = new MutationObserver((mutationsList) => {
  for (const mutation of mutationsList) {
    if (mutation.type === 'childList') {
      // Check if .swiper is added to the DOM
      const swiperElement = document.querySelector('.swiper');
      if (swiperElement) {
        // .swiper is present, append the toggleButton
        appendToggleButtonToSwiper();
      }
    }
  }
});

// Start observing changes in the DOM
observer.observe(document.body, { childList: true, subtree: true });


})();