[Go to site: main page, start]

0% found this document useful (0 votes)
102 views13 pages

Dynamic Character Scripts for Chatbots

Easy guide to create a script on Janitor.ai

Uploaded by

tamtamagames
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
102 views13 pages

Dynamic Character Scripts for Chatbots

Easy guide to create a script on Janitor.ai

Uploaded by

tamtamagames
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

JAI’s Script Documentation (ES5 Sandbox)

Getting Started
Scripts allow you to dynamically modify character behavior during conversations.

Your script has access to a context object that contains character data and chat information.

What are Scripts?


Scripts are JavaScript functions that run during chat conversations to:

Modify character personality based on conversation flow


Update scenario details dynamically
Inject context-aware information
Create adaptive character responses

When Scripts Run


Scripts execute:

Before each character response


After receiving user messages
When conversation context changes

Scripts run synchronously — avoid heavy computations.

Context Object Reference


The context object is your gateway to character and chat data.

Character Properties
[Link] = {
name: string, // Character's name
chat_name: string, // Name used in chat
example_dialogs: string, // Example conversations (modifiable)
personality: string, // Core personality traits (modifiable)
scenario: string // Current scenario/setting (modifiable)
}

Only personality and scenario should be modified in scripts. Other properties are read-only.

Chat Properties
[Link] = {
message_count: number, // Total messages in conversation
last_message: string, // Most recent user message
first_message_date?: Date, // When conversation started (optional)
last_bot_message_date?: Date // Last bot response time (optional)
}

Chat properties are read-only and cannot be modified.

Helper Functions
Helper functions for advanced lorebook functionality:

[Link] = {
sillytavern: {
TranslateSillyTavernLorebook(
context, // ScriptContextV1
loreEntries // SillyTavernLorebookEntry[]
) /* returns ScriptContextV1 */
}
}

TranslateSillyTavernLorebook processes lorebook entries with filters, priorities, and triggers. See the Advanced Lore example for usage.

Sandbox Environment
The script sandbox provides a secure environment with limited JavaScript features.
Available Globals
// Available globals:
Math // [Link], [Link], etc.
JSON // [Link], [Link]
Array // [Link], [Link]
Object // [Link], [Link], [Link], [Link]
String // String constructor
Number // Number constructor
Boolean // Boolean constructor
Date // Date constructor and methods
console // [Link], [Link], [Link]
Map // Map constructor
Set // Set constructor
RegExp // Regular expressions

Limitations (ES5 only)


The following JavaScript features are NOT supported:

ES6+ destructuring (const {chat} = context)


Spread operator (...array)
Template literals with complex expressions
Async/await and Promises
Import/export statements
Classes and class syntax
Generators and iterators
External API calls (fetch, XMLHttpRequest)
DOM manipulation
setTimeout, setInterval

Always access properties directly: [Link].message_count ( ) — not const {message_count} = [Link] ( ).

Return Behavior
Scripts automatically return the modified context. You don't need to add return context at the end.

// Correct - no return needed


if ([Link].message_count > 5) {
[Link] += ', friendly and casual';
}

// Also correct - explicit early exit


if (someCondition) {
return; // Early exit without modifications
}

Code Examples (Quick Patterns)

Dynamic Personality (by length)


// Character becomes more friendly over time
if ([Link].message_count > 10) {
[Link] += ', warmed up to the user and now more friendly';
}

Time-Based Changes
var hour = new Date().getHours();
if (hour < 6 || hour > 22) {
[Link] += "\n\nYou're feeling sleepy and your responses are a bit drowsy.";
[Link] += " It's late at night, and everything is quiet.";
}

Message Analysis
var lastMsg = [Link].last_message.toLowerCase();
if ([Link]('help') || [Link]('assist')) {
[Link] += "\n\nYou're particularly eager to be helpful right now.";
}

// Track emotional keywords


var emotions = ['happy', 'sad', 'angry', 'excited'];
var detectedEmotion = null;
for (var i = 0; i < [Link]; i++) {
if ([Link](emotions[i])) { detectedEmotion = emotions[i]; break; }
}
if (detectedEmotion) {
[Link] += ' The user seems to be feeling ' + detectedEmotion + '.';
}
Best Practices

Performance Tips
Keep scripts lightweight — they run on every message
Avoid complex calculations or deep loops
Don’t make external API calls (not supported)
Avoid ES6+ features like destructuring/spread operators
Cache repeated calculations when possible

The sandbox has limited JS support. Test features before relying on them.

Error Handling
try {
// Your script logic here
if ([Link].last_message) {
// Safe to use last_message
}
} catch (error) {
[Link]('Script error:', error);
// Script continues with original context
}

Debugging Tips
Use [Link]() to inspect values
Test with various message counts/content
Check the Debug Panel for applied changes
Start simple, then add complexity

[Link]('Message count:', [Link].message_count);


[Link]('Last message:', [Link].last_message);
[Link]('Current personality:', [Link]);

Common Patterns

Simple Memory System


Create a basic memory system using the scenario field:

// Extract memories from previous interactions


var memories = [];
if ([Link].last_message && [Link].last_message.toLowerCase().includes('my name is')) {
var nameMatch = [Link].last_message.match(/my name is (\w+)/i);
if (nameMatch) {
[Link]("User's name is " + nameMatch[1]);
}
}

// Add memories to scenario


if ([Link] > 0) {
[Link] += ' Things you remember: ' + [Link](', ');
}

Relationship Development
var messageCount = [Link].message_count;
var relationship = 'stranger';

if (messageCount > 5) relationship = 'acquaintance';


if (messageCount > 15) relationship = 'friend';
if (messageCount > 30) relationship = 'close friend';

[Link] += ', you view the user as a ' + relationship;

// Adjust formality based on relationship


if (relationship === 'close friend') {
[Link] += ', speaks casually and uses informal language';
}

Progressive Example Scripts (Least → Most Complex)


Each example includes: what it does, how it differs, why you’d use it, and full ES5-safe code.
1) Minimal Regex Keyword Match ([Link])
What: Single regex test → append personality.
Diff: Absolute baseline (no arrays/loops).
Use: Tiny, single-purpose trigger.

var message = String([Link].last_message || '');


if (/\b(corleone|mafia|family|godfather)\b/[Link](message)) {
[Link] += ', they are the most powerful and organized crime family in the city, but they don’t touch women or children';
}

2) Minimal .includes() Match (Basic [Link])


What: Single keyword check → append scenario.
Diff: Simpler than regex, still one-off.
Use: Easiest beginner trigger.

if ([Link].last_message.toLowerCase().includes('hello')) {
[Link] += " They greet you warmly.";
}

3) Array-Based Basic Template (Basic Template [Link])


What: Multiple keyword groups in an array; apply personality/scenario.
Diff: Scales without duplicating if blocks.
Use: Maintain many simple triggers in one place.

var keywords = [
{
words: ['hello', 'hi', 'hey'],
scenario: "They greet you warmly.",
personality: ", is feeling friendly and welcoming"
}
];
var message = [Link].last_message.toLowerCase();
for (var i = 0; i < [Link]; i++) {
var entry = keywords[i];
var hasKeyword = false;
for (var j = 0; j < [Link]; j++) {
if ([Link]([Link][j])) { hasKeyword = true; break; }
}
if (hasKeyword) {
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
}
[Link]('Message count:', [Link].message_count);

4) Simple Lorebook (Priority, Personality-Only) (Simple [Link])


What: Collect matches → sort by priority → apply top personality only.
Diff: Adds conflict resolution via priority.
Use: Ensure only the most important trait applies.

var lorebook = [
{
name: "Godfather / Damien",
keywords: ["godfather", "damien"],
priority: 10,
personality: ", Damien, the Godfather himself, is a calculating and charismatic leader who commands loyalty through quiet intimidation and unshakable control"
},
{
name: "Family",
keywords: ["corleone", "mafia", "family"],
priority: 5,
personality: ", they are the most powerful and organized crime family in the city, but they don’t touch women or children"
}
];
var lastMessage = [Link].last_message.toLowerCase();
var activatedEntries = [];

[Link](function(entry) {
var hasKeyword = [Link](function(keyword) { return [Link](keyword); });
if (hasKeyword) [Link](entry);
});

[Link](function(a, b) { return [Link] - [Link]; });

if ([Link] > 0) {
var top = activatedEntries[0];
if (![Link]([Link])) {
[Link] += [Link];
}
}
5) Simple Array (Personality-Only) (Simple [Link])
What: Array of keyword groups → append personality only.
Diff: Same as #3 but no scenario.
Use: Hidden trait shaping without scene changes.

var lore = [
{
keywords: ['corleone', 'mafia', 'family', 'godfather'],
personality: ', they are the most powerful and organized crime family in the city, but they don’t touch women or children'
}
];
var message = [Link].last_message.toLowerCase();
for (var i = 0; i < [Link]; i++) {
var entry = lore[i], hasKeyword = false;
for (var j = 0; j < [Link]; j++) {
if ([Link]([Link][j])) { hasKeyword = true; break; }
}
if (hasKeyword) [Link] += [Link];
}

6) Simple Lorebook Scenario Array (Simple Lorebook Scenario [Link])


What: Priority + apply personality and scenario (highest only).
Diff: Extends #4 by also adjusting scene.
Use: Keyword-driven lore that sets the scene.

var lorebook = [
{
name: "Godfather / Damien",
keywords: ["godfather", "damien"],
priority: 10,
personality: ", Damien, the Godfather himself, is a calculating and charismatic leader who commands loyalty through quiet intimidation and unshakable control",
scenario: "The Godfather is in a tense meeting with a rival family, his demeanor cool and calculating."
},
{
name: "Family",
keywords: ["corleone", "mafia", "family"],
priority: 5,
personality: ", they are the most powerful and organized crime family in the city, but they don’t touch women or children",
scenario: "The corleone mafia family has spread through the city."
}
];
var lastMessage = [Link].last_message.toLowerCase();
var activatedEntries = [];

[Link](function(entry) {
var hasKeyword = [Link](function(keyword) { return [Link](keyword); });
if (hasKeyword) [Link](entry);
});

[Link](function(a, b) { return [Link] - [Link]; });

if ([Link] > 0) {
var top = activatedEntries[0];
if ([Link] && ![Link]([Link])) {
[Link] += [Link];
}
if ([Link] && ![Link]([Link])) {
[Link] += [Link];
}
}

7) Dynamic Lorebook Template (Procedural) (Dynamic Lorebook [Link])


What: Pure if / else checks (no arrays) for lore; includes a time-gated secret.
Diff: Alternate style to #8 (array) that non-coders may find simpler.
Use: Easy copy-paste edits without touching arrays.

/**
* Dynamic Lore Book System
* Character reveals backstory and world knowledge based on keywords
*/

// Analyze last message for lore triggers


var lastMessage = [Link].last_message.toLowerCase();

// Fantasy/Magic lore
if ([Link]('magic') || [Link]('spell') || [Link]('wizard')) {
[Link] += ', knowledgeable about magical arts and ancient spells';
[Link] += ' {{char}} has studied magic for years and can sense magical energies around them.';
}

if ([Link]('dragon') || [Link]('beast') || [Link]('monster')) {


[Link] += ', experienced with dangerous creatures and their behaviors';
[Link] += ' {{char}} has encountered many mythical beasts and knows their weaknesses.';
}

// Historical/Background lore
if ([Link]('war') || [Link]('battle') || [Link]('soldier')) {
if ([Link]('war') || [Link]('battle') || [Link]('soldier')) {
[Link] += ', haunted by memories of past conflicts';
[Link] += ' {{char}} served in the Great War and bears both visible and invisible scars.';
}

if ([Link]('family') || [Link]('parent') || [Link]('childhood')) {


[Link] += ', shaped by a complex family history';
[Link] += ' {{char}} grew up in a noble house but left to forge their own path.';
}

// Location/World lore
if ([Link]('forest') || [Link]('woods') || [Link]('tree')) {
[Link] += ', deeply connected to nature and forest spirits';
[Link] += ' {{char}} spent their youth in the Whispering Woods, learning druidic ways.';
}

if ([Link]('city') || [Link]('town') || [Link]('street')) {


[Link] += ', street-smart and familiar with urban politics';
[Link] += ' {{char}} knows every alley and hidden passage in the capital city.';
}

// Profession/Skill lore
if ([Link]('sword') || [Link]('fight') || [Link]('weapon')) {
[Link] += ', disciplined in the ancient fighting arts';
[Link] += ' {{char}} trained under Master Korin, learning the Seven Sacred Stances.';
}

if ([Link]('book') || [Link]('knowledge') || [Link]('study')) {


[Link] += ', scholarly and well-versed in ancient texts';
[Link] += ' {{char}} spent decades in the Great Library, mastering forbidden knowledge.';
}

// Mysterious/Secret lore - only after some conversation


if ([Link].message_count > 15) {
if ([Link]('secret') || [Link]('hidden') || [Link]('truth')) {
[Link] += ', keeper of ancient secrets that could change everything';
[Link] += ' {{char}} knows the truth about the Sundering, but speaks of it only in whispers.';
}
}

8) Dynamic Lorebook Array (Dynamic Lorebook [Link])


What: Array of lore entries with optional minMessages gates.
Diff: Same features as #7 but data-driven.
Use: Editors can add rows without touching logic.

/**
* Dynamic Lore Book System
* Character reveals backstory and world knowledge based on keywords
*/

// Your Lore Entries


var dynamicLore = [
// === Fantasy/Magic lore ===
{ keywords: ['magic', 'spell', 'wizard'],
personality: ', knowledgeable about magical arts and ancient spells',
scenario: ' {{char}} has studied magic for years and can sense magical energies around them.' },
{ keywords: ['dragon', 'beast', 'monster'],
personality: ', experienced with dangerous creatures and their behaviors',
scenario: ' {{char}} has encountered many mythical beasts and knows their weaknesses.' },

// === Historical/Background lore ===


{ keywords: ['war', 'battle', 'soldier'],
personality: ', haunted by memories of past conflicts',
scenario: ' {{char}} served in the Great War and bears both visible and invisible scars.' },
{ keywords: ['family', 'parent', 'childhood'],
personality: ', shaped by a complex family history',
scenario: ' {{char}} grew up in a noble house but left to forge their own path.' },

// === Location/World lore ===


{ keywords: ['forest', 'woods', 'tree'],
personality: ', deeply connected to nature and forest spirits',
scenario: ' {{char}} spent their youth in the Whispering Woods, learning druidic ways.' },
{ keywords: ['city', 'town', 'street'],
{ keywords: ['city', 'town', 'street'],
personality: ', street-smart and familiar with urban politics',
scenario: ' {{char}} knows every alley and hidden passage in the capital city.' },

// === Profession/Skill lore ===


{ keywords: ['sword', 'fight', 'weapon'],
personality: ', disciplined in the ancient fighting arts',
scenario: ' {{char}} trained under Master Korin, learning the Seven Sacred Stances.' },
{ keywords: ['book', 'knowledge', 'study'],
personality: ', scholarly and well-versed in ancient texts',
scenario: ' {{char}} spent decades in the Great Library, mastering forbidden knowledge.' },

// === Mysterious/Secret lore (Timing Tested) ===


{ keywords: ['secret', 'hidden', 'truth'], minMessages: 0, maxMessages: 15,
personality: ', keeper of ancient secrets is a myth',
scenario: ' {{char}} knows the truth about the Sundering, but will not speak about it.' },
{ keywords: ['secret', 'hidden', 'truth'], minMessages: 16, maxMessages: 30,
personality: ', keeper of ancient secrets that could change everything',
scenario: ' {{char}} knows the truth about the Sundering, but speaks of it only in whispers.' }
];

var lastMessage = [Link].last_message.toLowerCase();


var messageCount = [Link].message_count;

// Loop through each lore entry


for (var i = 0; i < [Link]; i++) {
var entry = dynamicLore[i];

if ([Link] && messageCount < [Link]) continue;

// Check keywords
var hasKeyword = false;
for (var j = 0; j < [Link]; j++) {
if ([Link]([Link][j])) { hasKeyword = true; break; }
}

if (hasKeyword) {
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
}

9) Dynamic Scenarios Template (Procedural) (Dynamic Scenarios [Link])


What: Location keywords + timed events (specific counts).
Diff: Procedural counterpart to the array-based event script.
Use: Easy knobs for beginners.

/**
* Dynamic Scenario Events
* Triggers special events or changes based on keywords or message count
*/

// Get current conversation context


var lastMessage = [Link].last_message.toLowerCase();

// Location-based events
if ([Link]('restaurant') || [Link]('cafe')) {
[Link] += ' The cozy establishment has ambient sounds of clinking dishes and soft music.';
[Link] += ', notices and comments on the atmosphere around them';
}

if ([Link]('park') || [Link]('outside')) {
[Link] += ' They are outdoors with natural surroundings and fresh air.';
[Link] += ', observant of nature and weather';
}
// Time-based special events
if ([Link].message_count === 10) {
[Link] += ' Suddenly, their phone rings with an unexpected call.';
[Link] += ', momentarily distracted by unexpected interruptions';
}

if ([Link].message_count === 25) {


[Link] += ' The weather suddenly changes around them.';
[Link] += ', reactive to environmental changes';
}

// Keyword-triggered events
if ([Link]('secret')) {
[Link] += ', becomes mysterious when secrets are mentioned';
[Link] += ' {{char}} becomes slightly more mysterious and thoughtful.';
}

if ([Link]('music') || [Link]('song')) {
[Link] += ', enthusiastic about music';
[Link] += ' {{char}} shows enthusiasm about music and might share favorites.';
}
[Link]('Message count:', [Link].message_count);

10) Relationship Progression Template (Relationship Progression [Link])


What: Pure timing → relationship stage changes.
Diff: No keywords; simple pacing arc.
Use: Formal → casual → close tone shifts.

/**
* Dynamic Relationship Progression
* Character's behavior evolves based on conversation length
*/

// Define relationship stages based on message count


var messageCount = [Link].message_count;

if (messageCount < 5) {
// First meeting - formal and cautious
[Link] += ", polite but maintains professional distance";
[Link] += " This is their first meeting, so they are careful and observant.";
} else if (messageCount < 15) {
// Getting comfortable - warming up
[Link] += ", becoming more comfortable and casual";
[Link] += " They are warming up and becoming more relaxed in conversation.";
} else if (messageCount < 30) {
// Friends - open and relaxed
[Link] += ", friendly and open";
[Link] += " They feel comfortable and speak openly as friends.";
} else {
// Close friends - deep connection
[Link] += ", trusting and deeply connected";
[Link] += " They share a deep friendship and trust completely.";
}
[Link]('Message count:', [Link].message_count);

11) Relationship Progression Array (Relationship Progression [Link])


What: Keyword entries and timing entries in one list.
Diff: Combines scene beats with keyword reactions.
Use: Narrative “beats” + contextual color.

/**
* Dynamic Scenario Events
* Array-driven keyword and timing entries
*/

// Your Dynamic Entries


var dynamicEntries = [

// === KEYWORD ENTRIES ===


{ type: 'keyword', words: ['restaurant', 'cafe'],
{ type: 'keyword', words: ['restaurant', 'cafe'],
scenario: ' The cozy establishment has ambient sounds of clinking dishes and soft music.',
personality: ', notices and comments on the atmosphere around them' },

{ type: 'keyword', words: ['park', 'outside'],


scenario: ' They are outdoors with natural surroundings and fresh air.',
personality: ', observant of nature and weather' },

{ type: 'keyword', words: ['secret'],


personality: ', becomes mysterious when secrets are mentioned',
scenario: ' {{char}} becomes slightly more mysterious and thoughtful.' },

{ type: 'keyword', words: ['music', 'song'],


personality: ', enthusiastic about music',
scenario: ' {{char}} shows enthusiasm about music and might share favorites.' },

// === MESSAGE COUNT ENTRIES ===


{ type: 'timing', minMessages: 0, maxMessages: 10,
scenario: ' Suddenly, their phone rings with an unexpected call.',
personality: ', momentarily distracted by unexpected interruptions' },

{ type: 'timing', minMessages: 11, maxMessages: 15,


scenario: ' Suddenly, their phone rings with an unexpected call.',
personality: ', momentarily distracted by unexpected interruptions' },

{ type: 'timing', minMessages: 26, maxMessages: 50,


scenario: ' The weather suddenly changes around them.',
personality: ', reactive to environmental changes' }
];

var lastMessage = [Link].last_message.toLowerCase();


var messageCount = [Link].message_count;

// Loop
for (var i = 0; i < [Link]; i++) {
var entry = dynamicEntries[i];

if ([Link] === 'keyword') {


var hasKeyword = false;
for (var j = 0; j < [Link]; j++) {
if ([Link]([Link][j])) { hasKeyword = true; break; }
}
if (hasKeyword) {
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
} else if ([Link] === 'timing') {
if (messageCount >= [Link] && messageCount <= [Link]) {
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
}
}
[Link]('Message count:', [Link].message_count);

12) Advanced Lorebook (Advanced [Link])


What: Multi-pass system with triggers, priority, and probability.
Diff: Entries can cause other entries to activate; random chance gating.
Use: Interconnected, alive-feeling worlds.

/**
* Dynamic Lore Book System
* Multi-pass: triggers + probability + priority
*/

var dynamicLore = [
// === Fantasy/Magic lore ===
{ keywords: ['magic', 'spell', 'wizard'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 0.5,
personality: ', knowledgeable about magical arts and ancient spells',
scenario: ' {{char}} has studied magic for years and can sense magical energies around them.',
triggers: ['knowledge', 'study', 'monster'] },

{ keywords: ['dragon', 'beast', 'monster'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', experienced with dangerous creatures and their behaviors',
scenario: ' {{char}} has encountered many mythical beasts and knows their weaknesses.' },

// === Historical/Background lore ===


{ keywords: ['war', 'battle', 'soldier'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', haunted by memories of past conflicts',
scenario: ' {{char}} served in the Great War and bears both visible and invisible scars.',
triggers: ['war', 'battle'] },

{ keywords: ['family', 'parent', 'childhood'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', shaped by a complex family history',
scenario: ' {{char}} grew up in a noble house but left to forge their own path.' },

// === Location/World lore ===


{ keywords: ['forest', 'woods', 'tree'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', deeply connected to nature and forest spirits',
scenario: ' {{char}} spent their youth in the Whispering Woods, learning druidic ways.' },

{ keywords: ['city', 'town', 'street'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', street-smart and familiar with urban politics',
scenario: ' {{char}} knows every alley and hidden passage in the capital city.',
triggers: ['street', 'alley'] },

// === Profession/Skill lore ===


{ keywords: ['sword', 'fight', 'weapon'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', disciplined in the ancient fighting arts',
scenario: ' {{char}} trained under Master Korin, learning the Seven Sacred Stances.' },

{ keywords: ['book', 'knowledge', 'study'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', scholarly and well-versed in ancient texts',
scenario: ' {{char}} spent decades in the Great Library, mastering forbidden knowledge.',
triggers: ['knowledge', 'study'] },

// === Mysterious/Secret lore (Timing Tested) ===


{ keywords: ['secret', 'hidden', 'truth'], minMessages: 0, maxMessages: 15, priority: 10, probability: 1,
personality: ', keeper of ancient secrets is a myth',
scenario: ' {{char}} knows the truth about the Sundering, but will not speak about it.' },

{ keywords: ['secret', 'hidden', 'truth'], minMessages: 16, maxMessages: 30, priority: 10, probability: 1,
personality: ', keeper of ancient secrets that could change everything',
scenario: ' {{char}} knows the truth about the Sundering, but speaks of it only in whispers.' }
];

var lastMessage = [Link].last_message.toLowerCase();


var messageCount = [Link].message_count;
var activatedEntries = [];
var triggeredKeywords = [];

// First pass: direct matches + collect triggers


for (var i = 0; i < [Link]; i++) {
var entry = dynamicLore[i], hasKeyword = false;

if (messageCount >= [Link] && messageCount <= [Link]) {


if ([Link] && [Link]() > [Link]) continue;

for (var j = 0; j < [Link]; j++) {


if ([Link]([Link][j])) { hasKeyword = true; break; }
}
if (hasKeyword) {
[Link](entry);
if ([Link]) {
for (var k = 0; k < [Link]; k++) {
[Link]([Link][k]);
}
}
}
}
}

// Second pass: entries activated by triggers


if ([Link] > 0) {
for (var a = 0; a < [Link]; a++) {
var e2 = dynamicLore[a], isTriggered = false, already = false;

for (var b = 0; b < [Link]; b++) {


if (activatedEntries[b] === e2) { already = true; break; }
}
if (already) continue;

for (var c = 0; c < [Link]; c++) {


for (var d = 0; d < [Link]; d++) {
if ([Link][c] === triggeredKeywords[d]) { isTriggered = true; break; }
}

if (isTriggered) break;
}

if (isTriggered && messageCount >= [Link] && messageCount <= [Link]) {


if ([Link] && [Link]() > [Link]) continue;
[Link](e2);
}
}
}

// Final: sort by priority and apply


if ([Link] > 0) {
[Link](function(x, y) { return [Link] - [Link]; });
for (var m = 0; m < [Link]; m++) {
var apply = activatedEntries[m];
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
}
13) Advanced Shifter Lorebook (Advanced Shifter [Link])
What: After a main match, shifter words (e.g., “stars” vs “shadows”) pick a reply variant ( default , light , dark ).
Diff: In-entry branching without duplicate entries.
Use: Flavorful, context-sensitive replies.

/**
* Advanced Lore with Reply Shifters
* Keyword match + shifter variants in a single entry
*/

var dynamicLore = [
{
keywords: ['magic', 'spell', 'wizard'],
minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
shifters: {
light: ['stars', 'astronomy', 'healing', 'light', 'good'],
dark: ['darkness', 'chaos', 'shadows', 'evil', 'unpredictable']
},
replies: {
default: {
personality: ', knowledgeable about magical arts and ancient spells',
scenario: ' {{char}} has studied magic for years and can sense magical energies around them.'
},
light: {
personality: ', finds a creative and positive connection between magic and what they love',
scenario: ' {{char}} notes that magic feels harmonious, like a song from the stars.'
},
dark: {
personality: ', is cautious, knowing that magic can also be a tool for unpredictable and dangerous forces',
scenario: ' {{char}} explains that magic is not without its risks and dangers, especially when dealing with shadows.'
}
}
},
{ keywords: ['war', 'battle', 'soldier'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', haunted by memories of past conflicts',
scenario: ' {{char}} served in the Great War and bears both visible and invisible scars.' },
{ keywords: ['family', 'parent', 'childhood'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', shaped by a complex family history',
scenario: ' {{char}} grew up in a noble house but left to forge their own path.' }
];

var lastMessage = [Link].last_message.toLowerCase();


var messageCount = [Link].message_count;

// Loop through entries


for (var i = 0; i < [Link]; i++) {
var entry = dynamicLore[i], hasKeyword = false, replyToApply = null;

if (messageCount < [Link] || messageCount > [Link]) continue;


if ([Link] && [Link]() > [Link]) continue;

// Main keywords
for (var j = 0; j < [Link]; j++) {
if ([Link]([Link][j])) { hasKeyword = true; break; }
}

if (hasKeyword) {
if ([Link]) {
var shifterFound = false;
var categories = [Link]([Link]);
for (var k = 0; k < [Link]; k++) {
var cat = categories[k], words = [Link][cat];
for (var l = 0; l < [Link]; l++) {
if ([Link](words[l])) {
replyToApply = [Link][cat]; shifterFound = true; break;
}
}
if (shifterFound) break;
}
if (!shifterFound && [Link] && [Link]) {
replyToApply = [Link];
}
} else {
replyToApply = entry; // fallback to entry fields
}

if (replyToApply) {
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
}
}

14) Memory System (Memory [Link])


What: Detect hobby words + “favorite/love/like” patterns → memory-like adjustments.
Diff: Not lore; a lightweight personalization layer.
Use: Make the bot “remember” interests within the session.

/**
* Conversation Memory System
* Character remembers and references earlier parts of the conversation
* Character remembers and references earlier parts of the conversation
*/

// Analyze last message for interests and preferences


var lastMessage = [Link].last_message.toLowerCase();

// Detect hobbies mentioned in last message


var hobbies = ['reading', 'gaming', 'cooking', 'sports', 'art', 'music'];
var mentionedHobbies = [];
for (var i = 0; i < [Link]; i++) {
if ([Link](hobbies[i])) [Link](hobbies[i]);
}

// Detect preference expressions


var hasPreferences = [Link]('favorite') ||
[Link]('love') ||
[Link]('like');

// Add memory-based personality traits


if ([Link] > 0) {
[Link] += ", remembers {{user}}'s interest in " + [Link](' and ');
[Link] += ' {{char}} shows interest in ' + [Link](' and ') + ' topics.';
}

if (hasPreferences) {
[Link] += ", attentive to {{user}}'s preferences and opinions";
[Link] += ' {{char}} pays careful attention to what {{user}} likes and dislikes.';
}

// Add general memory-focused behavior


[Link] += ', has good memory for conversation details';
[Link] += ' {{char}} remembers important things {{user}} has shared.';

15) Likes & Dislikes (Likes and [Link])


What: Preconfigured lists for likes/dislikes; mirrors them into personality/scenario.
Diff: Structured categories vs freeform detection.
Use: Quick preference hooks for many topics.

/**
* Conversation Memory System
* Character remembers and references earlier parts of the conversation
*/

var lastMessage = [Link].last_message.toLowerCase();

// Your Interests
var interests = {
likes: ['pizza', 'movies', 'music', 'hiking', 'sports'],
dislikes: ['spiders', 'loud noises', 'waking up early', 'crowds']
};

var categories = [Link](interests);

for (var i = 0; i < [Link]; i++) {


var category = categories[i];
var mentionedItems = [];
var items = interests[category];

for (var j = 0; j < [Link]; j++) {


if ([Link](items[j])) [Link](items[j]);
}

if ([Link] > 0) {
if (category === 'likes') {
[Link] += ", remembers {{user}}'s interest in " + [Link](' and ');
[Link] += ' {{char}} shows interest in ' + [Link](' and ') + ' topics.';
} else if (category === 'dislikes') {
[Link] += ", remembers {{user}}'s dislike of " + [Link](' and ');
[Link] += ' {{char}} pays careful attention to avoid ' + [Link](' and ') + ' topics.';
}
}
}

16) Hybrid: Likes/Dislikes + Lore (Likes, Dislikes, and Advanced Prompt [Link])
What: Merge preference detection with lore replies that switch phrasing based on likes/dislikes.
Diff: Most adaptive; personality+scene tuned by who the user is and what keyword fired.
Use: Responsive, player-tailored worldbuilding.

/**
* Hybridized Lore and Memory System
* Character reveals lore based on keywords and user's likes and dislikes
*/

// YOUR LIKES AND DISLIKES


var interests = {
likes: ['art', 'cooking', 'traveling', 'animals', 'coffee', 'stars', 'astronomy', 'creative arts'],
dislikes: ['spiders', 'loud noises', 'waking up early', 'crowds', 'bugs', 'mornings', 'traffic', 'cold weather', 'darkness', 'chaos', 'shadows']
};

// YOUR LORE ENTRIES


var dynamicLore = [
{
keywords: ['magic', 'spell', 'wizard'],
minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
replies: {
default: { personality: ', knowledgeable about magical arts and ancient spells',
scenario: ' {{char}} has studied magic for years and can sense magical energies around them.' },
onLike: { personality: ', finds a creative and positive connection between magic and what they love',
scenario: ' {{char}} notes that magic feels harmonious, like a song from the stars.' },
onDislike:{ personality: ', is cautious, knowing that magic can also be a tool for unpredictable and dangerous forces',
scenario: ' {{char}} explains that magic is not without its risks and dangers, especially when dealing with shadows.' }
}
},
{ keywords: ['war', 'battle', 'soldier'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', haunted by memories of past conflicts',
scenario: ' {{char}} served in the Great War and bears both visible and invisible scars.' },
{ keywords: ['family', 'parent', 'childhood'], minMessages: 0, maxMessages: 999999999, priority: 10, probability: 1,
personality: ', shaped by a complex family history',
scenario: ' {{char}} grew up in a noble house but left to forge their own path.' }
];

// === SYSTEM PLUMBING ===


var lastMessage = [Link].last_message.toLowerCase();
var messageCount = [Link].message_count;

// First: detect mentioned likes/dislikes


var mentionedLikes = [];
var mentionedDislikes = [];
var categories = [Link](interests);

for (var i = 0; i < [Link]; i++) {


var category = categories[i];
var items = interests[category];
for (var j = 0; j < [Link]; j++) {
if ([Link](items[j])) {
if (category === 'likes') [Link](items[j]);
if (category === 'dislikes') [Link](items[j]);
}
}
}

// Second: evaluate lore


for (var a = 0; a < [Link]; a++) {
var entry = dynamicLore[a], hasKeyword = false, replyToApply = null;

if (messageCount < [Link] || messageCount > [Link]) continue;


if ([Link] && [Link]() > [Link]) continue;

for (var b = 0; b < [Link]; b++) {


if ([Link]([Link][b])) { hasKeyword = true; break; }
}

if (hasKeyword) {
if ([Link]) {
if ([Link] > 0 && [Link]) replyToApply = [Link];
else if ([Link] > 0 && [Link]) replyToApply = [Link];
else if ([Link]) replyToApply = [Link];
} else {
replyToApply = entry;
}

if (replyToApply) {
if ([Link]) [Link] += [Link];
if ([Link]) [Link] += [Link];
}
}
}

Final Notes for ES5/Sandbox Success


Prefer arrays of entries for editor-friendly systems; prefer procedural if/else you want to see step by step.
Use priority to avoid over-firing many entries; use min/max message gates to pace reveals.
Add shifters or conditional replies for flavor without duplicating entries.
Keep debug logs during development; remove them for production bots.
Only modify [Link] and [Link] .
```

You might also like