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>
Signed| @@ -1,5 +1,5 @@ | |||
| 1 | import { Fuse, DOMPurify } from '../lib.js'; | 1 | import { Fuse, DOMPurify } from '../lib.js'; |
| 2 | import { canUseNegativeLookbehind, copyText, flashHighlight } from './utils.js'; | 2 | import { canUseNegativeLookbehind, copyText, findPersona, flashHighlight } from './utils.js'; |
| 3 | 3 | ||
| 4 | import { | 4 | import { |
| 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 | } |
| 4512 | 4557 | ||
| 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 | |||
| 4513 | export async function sendMessageAs(args, text) { | 4624 | export async function sendMessageAs(args, text) { |
| 4514 | let name = args.name?.trim(); | 4625 | let name = args.name?.trim(); |
| 4515 | 4626 | ||