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 -1Showing whitespace changes
public/scripts/slash-commands.js+112 -1
@@ -1,5 +1,5 @@
1import { Fuse, DOMPurify } from '../lib.js';1import { Fuse, DOMPurify } from '../lib.js';
2import { canUseNegativeLookbehind, copyText, flashHighlight } from './utils.js';2import { canUseNegativeLookbehind, copyText, findPersona, flashHighlight } from './utils.js';
33
4import {4import {
5 Generate,5 Generate,
@@ -7,6 +7,7 @@ import {
7 addOneMessage,7 addOneMessage,
8 characters,8 characters,
9 chat,9 chat,
10 chatElement,
10 chat_metadata,11 chat_metadata,
11 comment_avatar,12 comment_avatar,
12 deactivateSendButtons,13 deactivateSendButtons,
@@ -765,6 +766,50 @@ export function initDefaultSlashCommands() {
765 `,766 `,
766 }));767 }));
767 SlashCommandParser.addCommandObject(SlashCommand.fromProps({768 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({
768 name: 'sendas',813 name: 'sendas',
769 rawQuotes: true,814 rawQuotes: true,
770 callback: sendMessageAs,815 callback: sendMessageAs,
@@ -4510,6 +4555,72 @@ export function getNameAndAvatarForMessage(character, name = null) {
4510 };4555 };
4511}4556}
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 */
4565export 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
4513export async function sendMessageAs(args, text) {4624export async function sendMessageAs(args, text) {
4514 let name = args.name?.trim();4625 let name = args.name?.trim();
45154626