diff --git a/pets.html b/pets.html index c2b1e23..8bf5a18 100644 --- a/pets.html +++ b/pets.html @@ -1784,9 +1784,10 @@ async function handleChatMessage(authorDid, record) { // Temporarily replace the handle pill with the redacted chat message pet.showMessage(record.text || ''); - // Pet-type pick: 'cat', 'dog', 'bunny', etc. swap the chatter's pet in place. - // 'random' clears the override and falls back to the hash-derived default. - // Use word boundaries so casual mentions (`my cat`, `dancing dog`) still match. + // Pet-type pick: must be the *whole* message (optionally with a leading !). + // Casual mentions ("did you turn me into a duck") used to trigger this and + // accidentally morph the speaker — that was a footgun, hence the strict match. + // 'random' (also whole-message-only) clears the override. const TYPE_WORDS = { cat: 'cat', kitty: 'cat', kitten: 'cat', katze: 'cat', kat: 'cat', dog: 'dog', doggo: 'dog', puppy: 'dog', hund: 'dog', hond: 'dog', @@ -1795,13 +1796,15 @@ async function handleChatMessage(authorDid, record) { hamster: 'hamster', goose: 'goose', gans: 'goose', }; + const typeMatch = text.match(/^\s*!?([a-zäöü]+)\s*$/i); let chosenType = null; - for (const [word, type] of Object.entries(TYPE_WORDS)) { - if (new RegExp(`\\b${word}\\b`, 'i').test(text)) { chosenType = type; break; } - } - if (/\brandom\b/i.test(text) && !chosenType) { - petTypeOverrides.delete(authorDid); - chosenType = '__rebuild__'; + if (typeMatch) { + const w = typeMatch[1].toLowerCase(); + if (TYPE_WORDS[w]) chosenType = TYPE_WORDS[w]; + else if (w === 'random') { + petTypeOverrides.delete(authorDid); + chosenType = '__rebuild__'; + } } if (chosenType) { if (chosenType !== '__rebuild__') petTypeOverrides.set(authorDid, chosenType);