chchar slash cmd (#4916) * Add an additional slash command to change a message's character and avatar. * Refactor message character change command to update message sender's name * Fix default value * Allow setting unbound message names --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

af495cefbd62e35e51489b6e20102a77415c4821

AphidGit <Aphid.mmonly@gmail.com>

Signed
1 files changed, +112 -1Ignore whitespace
public/scripts/slash-commands.js+112 -1
@@ -1,5 +1,5 @@
11import { Fuse, DOMPurify } from '../lib.js';
22import { canUseNegativeLookbehind, copyText, findPersona, flashHighlight } from './utils.js';
33
44import {
55 Generate,
@@ -7,6 +7,7 @@ import {
77 addOneMessage,
88 characters,
99 chat,
10+ chatElement,
1011 chat_metadata,
1112 comment_avatar,
1213 deactivateSendButtons,
@@ -765,6 +766,50 @@ export function initDefaultSlashCommands() {
765766 `,
766767 }));
767768 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
769+ name: 'message-name',
770+ callback: changeMessageName,
771+ returns: 'The updated name of the message sender',
772+ namedArgumentList: [
773+ SlashCommandNamedArgument.fromProps({
774+ name: 'at',
775+ description: 'the ID of the message to modify (index-based, corresponding to message id). If omitted, the last message is chosen.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will modify the message right before the last message in chat. At must be nonzero.',
776+ typeList: [ARGUMENT_TYPE.NUMBER],
777+ defaultValue: '',
778+ enumProvider: commonEnumProviders.messages({ allowIdAfter: true }),
779+ }),
780+ ],
781+ unnamedArgumentList: [
782+ SlashCommandArgument.fromProps({
783+ description: 'Persona name, character name, or unique character identifier (avatar key)',
784+ typeList: [ARGUMENT_TYPE.STRING],
785+ isRequired: true,
786+ enumProvider: (executor) => {
787+ let modifyAt = Number(executor.namedArgumentList.find(arg => arg.name === 'at')?.value ?? (chat.length - 1));
788+ if (!isNaN(modifyAt) && (modifyAt < 0 || Object.is(modifyAt, -0))) {
789+ modifyAt = chat.length + modifyAt;
790+ }
791+ return chat[modifyAt]?.is_user
792+ ? commonEnumProviders.personas()
793+ : commonEnumProviders.characters('character')();
794+ },
795+ }),
796+ ],
797+ helpString: `
798+ <div>
799+ Changes the name of a message sender to one of your choice.
800+ </div>
801+ <div>
802+ <strong>Example:</strong>
803+ <ul>
804+ <li>
805+ <pre><code>/message-name at=-2 "Chloe"</code></pre>
806+ Will change the third message from the bottom to be sent by "Chloe".
807+ </li>
808+ </ul>
809+ </div>
810+ `,
811+ }));
812+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
768813 name: 'sendas',
769814 rawQuotes: true,
770815 callback: sendMessageAs,
@@ -4510,6 +4555,72 @@ export function getNameAndAvatarForMessage(character, name = null) {
45104555 };
45114556}
45124557
4558+/**
4559+ * Changes the character name on a message at a given index.
4560+ * @param {object?} args - Named arguments
4561+ * @param {string} name - Name to change to.
4562+ *
4563+ * @returns {Promise<string>} The updated message name.
4564+ */
4565+export async function changeMessageName(args, name) {
4566+ name = String(name ?? '').trim();
4567+ if (!name) {
4568+ toastr.warning(t`You must provide a name to change the message to.`);
4569+ return '';
4570+ }
4571+
4572+ let modifyAt = Number(args?.at ?? (chat.length - 1));
4573+ // Convert possible depth parameter to index
4574+ if (!isNaN(modifyAt) && (modifyAt < 0 || Object.is(modifyAt, -0))) {
4575+ // Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
4576+ modifyAt = chat.length + modifyAt;
4577+ }
4578+
4579+ const message = chat[modifyAt];
4580+ if (!message) {
4581+ toastr.warning(t`No message found at the specified index.`);
4582+ return '';
4583+ }
4584+
4585+ let newName = '';
4586+
4587+ if (message.is_user) {
4588+ const persona = findPersona({ name: name });
4589+ if (persona) {
4590+ message.name = newName = persona.name;
4591+ message.force_avatar = getThumbnailUrl('persona', persona.avatar);
4592+ message.original_avatar = persona.avatar;
4593+ } else {
4594+ message.name = newName = name;
4595+ message.force_avatar = default_avatar;
4596+ message.original_avatar = default_avatar;
4597+ }
4598+ } else {
4599+ const character = findChar({ name: name });
4600+ if (character) {
4601+ const characterInfo = getNameAndAvatarForMessage(character, name);
4602+ message.name = newName = characterInfo.name;
4603+ message.force_avatar = characterInfo.force_avatar;
4604+ message.original_avatar = characterInfo.original_avatar;
4605+ } else {
4606+ message.name = newName = name;
4607+ message.force_avatar = default_avatar;
4608+ message.original_avatar = default_avatar;
4609+ }
4610+ }
4611+
4612+ await eventSource.emit(event_types.MESSAGE_EDITED, modifyAt);
4613+ const existingMessage = chatElement.find(`.mes[mesid="${modifyAt}"]`);
4614+ if (existingMessage.length) {
4615+ addOneMessage(message, { forceId: modifyAt, insertAfter: modifyAt, scroll: false });
4616+ existingMessage.remove();
4617+ }
4618+ await eventSource.emit(event_types.MESSAGE_UPDATED, modifyAt);
4619+ await saveChatConditional();
4620+
4621+ return newName;
4622+}
4623+
45134624export async function sendMessageAs(args, text) {
45144625 let name = args.name?.trim();
45154626