mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into small-bookmark-updates
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
||||
main_api,
|
||||
name1,
|
||||
name2,
|
||||
neutralCharacterName,
|
||||
reloadCurrentChat,
|
||||
removeMacros,
|
||||
renameCharacter,
|
||||
@@ -427,6 +428,7 @@ export function initDefaultSlashCommands() {
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'ask',
|
||||
callback: askCharacter,
|
||||
returns: 'the generated text',
|
||||
namedArgumentList: [
|
||||
SlashCommandNamedArgument.fromProps({
|
||||
name: 'name',
|
||||
@@ -438,7 +440,7 @@ export function initDefaultSlashCommands() {
|
||||
],
|
||||
unnamedArgumentList: [
|
||||
new SlashCommandArgument(
|
||||
'prompt', [ARGUMENT_TYPE.STRING], true, false,
|
||||
'prompt', [ARGUMENT_TYPE.STRING], false, false,
|
||||
),
|
||||
],
|
||||
helpString: 'Asks a specified character card a prompt. Character name must be provided in a named argument.',
|
||||
@@ -2467,44 +2469,40 @@ async function askCharacter(args, text) {
|
||||
// Not supported in group chats
|
||||
// TODO: Maybe support group chats?
|
||||
if (selected_group) {
|
||||
toastr.error('Cannot run this command in a group chat!');
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!text) {
|
||||
console.warn('WARN: No text provided for /ask command');
|
||||
toastr.warning('No text provided for /ask command');
|
||||
toastr.error('Cannot run /ask command in a group chat!');
|
||||
return '';
|
||||
}
|
||||
|
||||
let name = '';
|
||||
let mesText = '';
|
||||
|
||||
if (args?.name) {
|
||||
name = args.name.trim();
|
||||
mesText = text.trim();
|
||||
|
||||
if (!name && !mesText) {
|
||||
toastr.warning('You must specify a name and text to ask.');
|
||||
if (!name) {
|
||||
toastr.warning('You must specify a name of the character to ask.');
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND);
|
||||
|
||||
const prevChId = this_chid;
|
||||
|
||||
// Find the character
|
||||
const chId = characters.findIndex((e) => e.name === name);
|
||||
const chId = characters.findIndex((e) => e.name === name || e.avatar === name);
|
||||
if (!characters[chId] || chId === -1) {
|
||||
toastr.error('Character not found.');
|
||||
return '';
|
||||
}
|
||||
|
||||
if (text) {
|
||||
const mesText = getRegexedString(text.trim(), regex_placement.SLASH_COMMAND);
|
||||
// Sending a message implicitly saves the chat, so this needs to be done before changing the character
|
||||
// Otherwise, a corruption will occur
|
||||
await sendMessageAsUser(mesText, '');
|
||||
}
|
||||
|
||||
// Override character and send a user message
|
||||
setCharacterId(String(chId));
|
||||
|
||||
// TODO: Maybe look up by filename instead of name
|
||||
const character = characters[chId];
|
||||
let force_avatar, original_avatar;
|
||||
|
||||
@@ -2519,9 +2517,11 @@ async function askCharacter(args, text) {
|
||||
|
||||
setCharacterName(character.name);
|
||||
|
||||
await sendMessageAsUser(mesText, '');
|
||||
|
||||
const restoreCharacter = () => {
|
||||
if (String(this_chid) !== String(chId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCharacterId(prevChId);
|
||||
setCharacterName(characters[prevChId].name);
|
||||
|
||||
@@ -2532,23 +2532,27 @@ async function askCharacter(args, text) {
|
||||
lastMessage.force_avatar = force_avatar;
|
||||
lastMessage.original_avatar = original_avatar;
|
||||
}
|
||||
|
||||
// Kill this callback once the event fires
|
||||
eventSource.removeListener(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
|
||||
};
|
||||
|
||||
// Run generate and restore previous character on error
|
||||
let askResult = '';
|
||||
|
||||
// Run generate and restore previous character
|
||||
try {
|
||||
eventSource.once(event_types.MESSAGE_RECEIVED, restoreCharacter);
|
||||
toastr.info(`Asking ${character.name} something...`);
|
||||
await Generate('ask_command');
|
||||
} catch {
|
||||
askResult = await Generate('ask_command');
|
||||
} catch (error) {
|
||||
restoreCharacter();
|
||||
console.error('Error running /ask command', error);
|
||||
} finally {
|
||||
if (String(this_chid) === String(prevChId)) {
|
||||
await saveChatConditional();
|
||||
} else {
|
||||
toastr.error('It is strongly recommended to reload the page.', 'Something went wrong');
|
||||
}
|
||||
}
|
||||
|
||||
// Restore previous character once message renders
|
||||
// Hack for generate
|
||||
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
|
||||
return '';
|
||||
return askResult;
|
||||
}
|
||||
|
||||
async function hideMessageCallback(_, arg) {
|
||||
@@ -3129,7 +3133,13 @@ export async function sendMessageAs(args, text) {
|
||||
const character = characters.find(x => x.avatar === name) ?? characters.find(x => x.name === name);
|
||||
let force_avatar, original_avatar;
|
||||
|
||||
if (character && character.avatar !== 'none') {
|
||||
const chatCharacter = this_chid !== undefined ? characters[this_chid] : null;
|
||||
const isNeutralCharacter = !chatCharacter && name2 === neutralCharacterName && name === neutralCharacterName;
|
||||
|
||||
if (chatCharacter === character || isNeutralCharacter) {
|
||||
// If the targeted character is the currently selected one in a solo chat, we don't need to force any avatars
|
||||
}
|
||||
else if (character && character.avatar !== 'none') {
|
||||
force_avatar = getThumbnailUrl('avatar', character.avatar);
|
||||
original_avatar = character.avatar;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from '../../script.js';
|
||||
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types, name2, neutralCharacterName } from '../../script.js';
|
||||
import { extension_settings } from '../extensions.js';
|
||||
import { getGroupMembers, groups } from '../group-chats.js';
|
||||
import { power_user } from '../power-user.js';
|
||||
@@ -6,7 +6,9 @@ import { searchCharByName, getTagsList, tags } from '../tags.js';
|
||||
import { world_names } from '../world-info.js';
|
||||
import { SlashCommandClosure } from './SlashCommandClosure.js';
|
||||
import { SlashCommandEnumValue, enumTypes } from './SlashCommandEnumValue.js';
|
||||
import { SlashCommandScope } from "./SlashCommandScope.js";
|
||||
|
||||
/** @typedef {import('./SlashCommandExecutor.js').SlashCommandExecutor} SlashCommandExecutor */
|
||||
/** @typedef {import('./SlashCommandScope.js').SlashCommandScope} SlashCommandScope */
|
||||
|
||||
/**
|
||||
* A collection of regularly used enum icons
|
||||
@@ -140,7 +142,7 @@ export const commonEnumProviders = {
|
||||
* @param {...('global'|'local'|'scope'|'all')} type - The type of variables to include in the array. Can be 'all', 'global', or 'local'.
|
||||
* @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
|
||||
*/
|
||||
variables: (...type) => (executor, scope) => {
|
||||
variables: (...type) => (_, scope) => {
|
||||
const types = type.flat();
|
||||
const isAll = types.includes('all');
|
||||
return [
|
||||
@@ -160,6 +162,7 @@ export const commonEnumProviders = {
|
||||
return [
|
||||
...['all', 'character'].includes(mode) ? characters.map(char => new SlashCommandEnumValue(char.name, null, enumTypes.name, enumIcons.character)) : [],
|
||||
...['all', 'group'].includes(mode) ? groups.map(group => new SlashCommandEnumValue(group.name, null, enumTypes.qr, enumIcons.group)) : [],
|
||||
...(name2 === neutralCharacterName) ? [new SlashCommandEnumValue(neutralCharacterName, null, enumTypes.name, '🥸')] : [],
|
||||
];
|
||||
},
|
||||
|
||||
@@ -184,7 +187,7 @@ export const commonEnumProviders = {
|
||||
* @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
|
||||
* @returns {() => SlashCommandEnumValue[]}
|
||||
*/
|
||||
tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
|
||||
tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {
|
||||
// Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
|
||||
const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
|
||||
if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
|
||||
@@ -202,13 +205,13 @@ export const commonEnumProviders = {
|
||||
* @param {object} [options={}] - Optional arguments
|
||||
* @param {boolean} [options.allowIdAfter=false] - Whether to add an enum option for the new message id after the last message
|
||||
* @param {boolean} [options.allowVars=false] - Whether to add enum option for variable names
|
||||
* @returns {() => SlashCommandEnumValue[]}
|
||||
* @returns {(executor:SlashCommandExecutor, scope:SlashCommandScope) => SlashCommandEnumValue[]}
|
||||
*/
|
||||
messages: ({ allowIdAfter = false, allowVars = false } = {}) => () => {
|
||||
messages: ({ allowIdAfter = false, allowVars = false } = {}) => (_, scope) => {
|
||||
return [
|
||||
...chat.map((message, index) => new SlashCommandEnumValue(String(index), `${message.name}: ${message.mes}`, enumTypes.number, message.is_user ? enumIcons.user : message.is_system ? enumIcons.system : enumIcons.assistant)),
|
||||
...allowIdAfter ? [new SlashCommandEnumValue(String(chat.length), '>> After Last Message >>', enumTypes.enum, '➕')] : [],
|
||||
...allowVars ? commonEnumProviders.variables('all')() : [],
|
||||
...allowVars ? commonEnumProviders.variables('all')(_, scope) : [],
|
||||
];
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user