mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Change type of timed events metadata
This commit is contained in:
@ -89,6 +89,7 @@ const MAX_SCAN_DEPTH = 1000;
|
|||||||
* @property {boolean} [matchWholeWords] If the scan should match whole words
|
* @property {boolean} [matchWholeWords] If the scan should match whole words
|
||||||
* @property {boolean} [useGroupScoring] If the scan should use group scoring
|
* @property {boolean} [useGroupScoring] If the scan should use group scoring
|
||||||
* @property {number} [uid] The UID of the entry that triggered the scan
|
* @property {number} [uid] The UID of the entry that triggered the scan
|
||||||
|
* @property {string} [world] The world info book of origin of the entry
|
||||||
* @property {string[]} [key] The primary keys to scan for
|
* @property {string[]} [key] The primary keys to scan for
|
||||||
* @property {string[]} [keysecondary] The secondary keys to scan for
|
* @property {string[]} [keysecondary] The secondary keys to scan for
|
||||||
* @property {number} [selectiveLogic] The logic to use for selective activation
|
* @property {number} [selectiveLogic] The logic to use for selective activation
|
||||||
@ -333,7 +334,7 @@ class WorldInfoBuffer {
|
|||||||
|
|
||||||
class WorldInfoTimedEvents {
|
class WorldInfoTimedEvents {
|
||||||
/**
|
/**
|
||||||
* @type {Map<object, number>} Cache for entry hashes
|
* @type {Map<WIScanEntry, number>} Cache for entry hashes
|
||||||
*/
|
*/
|
||||||
static #entryHashCache = new Map();
|
static #entryHashCache = new Map();
|
||||||
|
|
||||||
@ -373,24 +374,27 @@ class WorldInfoTimedEvents {
|
|||||||
*/
|
*/
|
||||||
#ensureChatMetadata() {
|
#ensureChatMetadata() {
|
||||||
if (!chat_metadata.timedWorldInfo) {
|
if (!chat_metadata.timedWorldInfo) {
|
||||||
chat_metadata.timedWorldInfo = {
|
chat_metadata.timedWorldInfo = {};
|
||||||
sticky: {},
|
|
||||||
cooldown: {},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!chat_metadata.timedWorldInfo.sticky || typeof chat_metadata.timedWorldInfo.sticky !== 'object') {
|
['sticky', 'cooldown'].forEach(type => {
|
||||||
chat_metadata.timedWorldInfo.sticky = {};
|
// Ensure the property exists and is an object
|
||||||
}
|
if (!chat_metadata.timedWorldInfo[type] || typeof chat_metadata.timedWorldInfo[type] !== 'object') {
|
||||||
|
chat_metadata.timedWorldInfo[type] = {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!chat_metadata.timedWorldInfo.cooldown || typeof chat_metadata.timedWorldInfo.cooldown !== 'object') {
|
// Clean up invalid entries
|
||||||
chat_metadata.timedWorldInfo.cooldown = {};
|
Object.entries(chat_metadata.timedWorldInfo[type]).forEach(([key, value]) => {
|
||||||
}
|
if (!value || typeof value !== 'object') {
|
||||||
|
delete chat_metadata.timedWorldInfo[type][key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a hash for a WI entry.
|
* Gets a hash for a WI entry.
|
||||||
* @param {object} entry WI entry
|
* @param {WIScanEntry} entry WI entry
|
||||||
* @returns {number} String hash
|
* @returns {number} String hash
|
||||||
*/
|
*/
|
||||||
#getEntryHash(entry) {
|
#getEntryHash(entry) {
|
||||||
@ -404,48 +408,26 @@ class WorldInfoTimedEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes entries for a given type of timed event.
|
* Gets a unique-ish key for a WI entry.
|
||||||
* @param {string} type Identifier for the type of timed event
|
* @param {WIScanEntry} entry WI entry
|
||||||
* @param {WIScanEntry[]} buffer Buffer to store the entries
|
* @returns {string} String key for the entry
|
||||||
* @param {(entry: WIScanEntry) => void} onEnded Callback for when a timed event ends
|
|
||||||
*/
|
*/
|
||||||
#processEntries(type, buffer, onEnded) {
|
#getEntryKey(entry) {
|
||||||
for (const [hash, value] of Object.entries(chat_metadata.timedWorldInfo[type])) {
|
return `${entry.world}.${entry.uid}`;
|
||||||
console.log(`Processing ${type} entry ${hash} with value ${value}`);
|
}
|
||||||
const entry = this.#entries.find(x => String(this.#getEntryHash(x)) === String(hash));
|
|
||||||
|
|
||||||
if (this.#chat.length <= Number(value)) {
|
/**
|
||||||
console.log(`Removing ${type} entry from timedWorldInfo: chat not advanced`, entry);
|
* Gets a timed event for a WI entry.
|
||||||
delete chat_metadata.timedWorldInfo[type][hash];
|
* @param {WIScanEntry} entry WI entry
|
||||||
continue;
|
* @param {'sticky'|'cooldown'} type Type of timed event
|
||||||
}
|
* @returns {WITimedEvent} Timed event for the entry
|
||||||
|
*/
|
||||||
// Ignore missing entries (they could be from another character's lorebook)
|
#getEntryTimedEvent(entry, type) {
|
||||||
if (!entry) {
|
return {
|
||||||
continue;
|
hash: this.#getEntryHash(entry),
|
||||||
}
|
start: this.#chat.length,
|
||||||
|
end: this.#chat.length + Number(entry[type]),
|
||||||
// Ignore invalid entries (not configured for timed effects)
|
};
|
||||||
if (!entry[type]) {
|
|
||||||
console.log(`Removing ${type} entry from timedWorldInfo: entry not ${type}`, entry);
|
|
||||||
delete chat_metadata.timedWorldInfo[type][hash];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetRelease = Number(value) + Number(entry[type]);
|
|
||||||
|
|
||||||
if (this.#chat.length > targetRelease) {
|
|
||||||
console.log(`Removing ${type} entry from timedWorldInfo: ${type} interval passed`, entry);
|
|
||||||
delete chat_metadata.timedWorldInfo[type][hash];
|
|
||||||
if (typeof onEnded === 'function') {
|
|
||||||
onEnded(entry);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.push(entry);
|
|
||||||
console.log(`Timed effect "${type}" applied to entry`, entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -458,10 +440,10 @@ class WorldInfoTimedEvents {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hash = this.#getEntryHash(entry);
|
const key = this.#getEntryKey(entry);
|
||||||
const targetRelease = this.#chat.length + entry.cooldown;
|
const event = this.#getEntryTimedEvent(entry, 'cooldown');
|
||||||
chat_metadata.timedWorldInfo.cooldown[hash] = this.#chat.length;
|
chat_metadata.timedWorldInfo.cooldown[key] = event;
|
||||||
console.log(`Adding cooldown entry ${hash} on ended sticky: target release @ message ID ${targetRelease}`);
|
console.log(`Adding cooldown entry ${key} on ended sticky: target release @ message ID ${event.end}`);
|
||||||
// Set the cooldown immediately for this evaluation
|
// Set the cooldown immediately for this evaluation
|
||||||
this.#cooldownSuppressions.push(entry);
|
this.#cooldownSuppressions.push(entry);
|
||||||
}
|
}
|
||||||
@ -475,12 +457,82 @@ class WorldInfoTimedEvents {
|
|||||||
console.debug('Cooldown ended for entry', entry.uid);
|
console.debug('Cooldown ended for entry', entry.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes entries for a given type of timed event.
|
||||||
|
* @param {'sticky'|'cooldown'} type Identifier for the type of timed event
|
||||||
|
* @param {WIScanEntry[]} buffer Buffer to store the entries
|
||||||
|
* @param {(entry: WIScanEntry) => void} onEnded Callback for when a timed event ends
|
||||||
|
*/
|
||||||
|
#checkTimedEventOfType(type, buffer, onEnded) {
|
||||||
|
/** @type {[string, WITimedEvent][]} */
|
||||||
|
const events = Object.entries(chat_metadata.timedWorldInfo[type]);
|
||||||
|
for (const [key, value] of events) {
|
||||||
|
console.log(`Processing ${type} entry ${key}`, value);
|
||||||
|
const entry = this.#entries.find(x => String(this.#getEntryHash(x)) === String(value.hash));
|
||||||
|
|
||||||
|
if (this.#chat.length <= Number(value.start)) {
|
||||||
|
console.log(`Removing ${type} entry ${key} from timedWorldInfo: chat not advanced`, value);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Missing entries (they could be from another character's lorebook)
|
||||||
|
if (!entry) {
|
||||||
|
if (this.#chat.length > Number(value.end)) {
|
||||||
|
console.log(`Removing ${type} entry from timedWorldInfo: entry not found and interval passed`, entry);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][key];
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore invalid entries (not configured for timed effects)
|
||||||
|
if (!entry[type]) {
|
||||||
|
console.log(`Removing ${type} entry from timedWorldInfo: entry not ${type}`, entry);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#chat.length > Number(value.end)) {
|
||||||
|
console.log(`Removing ${type} entry from timedWorldInfo: ${type} interval passed`, entry);
|
||||||
|
delete chat_metadata.timedWorldInfo[type][key];
|
||||||
|
if (typeof onEnded === 'function') {
|
||||||
|
onEnded(entry);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.push(entry);
|
||||||
|
console.log(`Timed effect "${type}" applied to entry`, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for timed effects on chat messages.
|
* Checks for timed effects on chat messages.
|
||||||
*/
|
*/
|
||||||
checkTimedEvents() {
|
checkTimedEvents() {
|
||||||
this.#processEntries('sticky', this.#stickyActivations, this.#onStickyEndedCallback.bind(this));
|
this.#checkTimedEventOfType('sticky', this.#stickyActivations, this.#onStickyEndedCallback.bind(this));
|
||||||
this.#processEntries('cooldown', this.#cooldownSuppressions, this.#onCooldownEndedCallback.bind(this));
|
this.#checkTimedEventOfType('cooldown', this.#cooldownSuppressions, this.#onCooldownEndedCallback.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a timed event for a WI entry.
|
||||||
|
* @param {'sticky'|'cooldown'} type Type of timed event
|
||||||
|
* @param {WIScanEntry} entry WI entry to check
|
||||||
|
*/
|
||||||
|
#setTimedEventOfType(type, entry) {
|
||||||
|
// Skip if entry does not have the type (sticky or cooldown)
|
||||||
|
if (!entry[type]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = this.#getEntryKey(entry);
|
||||||
|
|
||||||
|
if (!chat_metadata.timedWorldInfo[type][key]) {
|
||||||
|
const event = this.#getEntryTimedEvent(entry, type);
|
||||||
|
chat_metadata.timedWorldInfo[type][key] = event;
|
||||||
|
|
||||||
|
console.log(`Adding ${type} entry ${key}: target release @ message ID ${event.end}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -489,23 +541,8 @@ class WorldInfoTimedEvents {
|
|||||||
*/
|
*/
|
||||||
setTimedEvents(activatedEntries) {
|
setTimedEvents(activatedEntries) {
|
||||||
for (const entry of activatedEntries) {
|
for (const entry of activatedEntries) {
|
||||||
if (!entry.sticky && !entry.cooldown) {
|
this.#setTimedEventOfType('sticky', entry);
|
||||||
continue;
|
this.#setTimedEventOfType('cooldown', entry);
|
||||||
}
|
|
||||||
|
|
||||||
const hash = this.#getEntryHash(entry);
|
|
||||||
|
|
||||||
if (entry.sticky && !chat_metadata.timedWorldInfo.sticky[hash]) {
|
|
||||||
const targetRelease = this.#chat.length + entry.sticky;
|
|
||||||
chat_metadata.timedWorldInfo.sticky[hash] = this.#chat.length;
|
|
||||||
console.log(`Adding sticky entry ${hash}: target release @ message ID ${targetRelease}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.cooldown && !chat_metadata.timedWorldInfo.cooldown[hash]) {
|
|
||||||
const targetRelease = this.#chat.length + entry.cooldown;
|
|
||||||
chat_metadata.timedWorldInfo.cooldown[hash] = this.#chat.length;
|
|
||||||
console.log(`Adding cooldown entry ${hash}: target release @ message ID ${targetRelease}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,7 +552,7 @@ class WorldInfoTimedEvents {
|
|||||||
* @returns {boolean} True if the entry is sticky activated
|
* @returns {boolean} True if the entry is sticky activated
|
||||||
*/
|
*/
|
||||||
isStickyActivated(entry) {
|
isStickyActivated(entry) {
|
||||||
return this.#stickyActivations.some(x => JSON.stringify(x) === JSON.stringify(entry));
|
return this.#stickyActivations.some(x => this.#getEntryHash(x) === this.#getEntryHash(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -524,7 +561,7 @@ class WorldInfoTimedEvents {
|
|||||||
* @returns {boolean} True if the entry is suppressed by cooldown
|
* @returns {boolean} True if the entry is suppressed by cooldown
|
||||||
*/
|
*/
|
||||||
isOnCooldown(entry) {
|
isOnCooldown(entry) {
|
||||||
return this.#cooldownSuppressions.some(x => JSON.stringify(x) === JSON.stringify(entry));
|
return this.#cooldownSuppressions.some(x => this.#getEntryHash(x) === this.#getEntryHash(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user