Generate Expression Images after Every Chat

This custom code will generate expression images after every chat, it will first get the description of the character, and the next messages should generate an expression for the AI.

Version 3

With /change-desc to change the saved description used for the expressions:

oc.thread.on("MessageAdded", async function({message}) {
  if (message.author != "ai") {
    if (message.content.startsWith('/change-desc')) {
      oc.thread.messages.pop();
      oc.thread.messages.push({
        author: "user",
        content: `${oc.character.name}, describe your current appearance in detail. Reply with a comma-separated list of keywords and keyphrases which *visually* capture your apprearance only. Imagine you're giving a list of keywords to an artist who will use them to draw you. Describe the your appearance with keywords/keyphrases, including your race, class, age, etc. Respond with only the comma-separated keyphrases - nothing more, nothing less.

Start with "Here is my current appearance:"`,
      });
    }
    if (oc.character.customData.description != "") return
    oc.thread.messages.push({
      author: "user",
      content: `${oc.character.name}, describe your current appearance in detail. Reply with a comma-separated list of keywords and keyphrases which *visually* capture your apprearance only. Imagine you're giving a list of keywords to an artist who will use them to draw you. Describe the your appearance with keywords/keyphrases, including your race, class, age, etc. Respond with only the comma-separated keyphrases - nothing more, nothing less.

Start with "Here is my current appearance:"`,
    });
  }
})

window.alreadyGenerating = false;
oc.thread.on("MessageAdded", async function () {
  oc.character.customData.description = oc.character.customData.description
    ? oc.character.customData.description
    : "";
  let lastMessage = oc.thread.messages.at(-1);

  if (lastMessage.author == "ai") {
    if (alreadyGenerating) return;
    alreadyGenerating = true;
    console.log("DESC: ", oc.character.customData.description);
    if (/^Here is my current appearance\:/.test(lastMessage.content)) {
      if (oc.character.customData.description == "") {
        oc.character.customData.description = lastMessage.content.replace(
          "Here is my current appearance:",
          ""
        );
        // console.log("NEW DESC: ", oc.character.customData.description);

        oc.character.customData.PUBLIC = {
          description: oc.character.customData.description,
        };
        // console.log("PUBLIC: ", oc.character.customData.PUBLIC);
      }
      oc.thread.messages.pop();
      oc.thread.messages.pop();
    } else {
      // oc.thread.messages.pop()
      let response = await oc.getInstructCompletion({
        instruction: `Given the message below, infer the emotions that can be sensed on the message:
---
${lastMessage.content}
---
Be sure to only give the words that describes the emotion e.g. joy, sad, happy, cheeky, etc. 
Respond with only the comma-separated keyphrases - nothing more, nothing less.`,
        stopSequences: ["\n\n"],
      });

      let prompt = `${oc.character.imagePromptPrefix == '' ? '' : oc.character.imagePromptPrefix + ', '}${oc.character.customData.description}, (in ${response.text} expression:1.1)${oc.character.imagePromptSuffix == '' ? '' : ', '+ oc.character.imagePromptSuffix}`;
      (async function () {
        // let prevDataUrl = oc.character.avatar.url;
        let { dataUrl } = await oc.textToImage({
          prompt: prompt,
          seed: `104`,
          resolution: `512x768`,
        });
        oc.thread.messages.push({
          author: "ai",
          content: `<br><img src="${await resizeDataURLWidth(
            dataUrl,
            200
          )}" title="${prompt}">`,
          hiddenFrom: ["ai"],
        });
      })();
    }
  }
  alreadyGenerating = false;
});

async function resizeDataURLWidth(dataURL, newWidth) {
  const blob = await fetch(dataURL).then((res) => res.blob());
  const bitmap = await createImageBitmap(blob);
  const canvas = Object.assign(document.createElement("canvas"), {
    width: newWidth,
    height: (bitmap.height / bitmap.width) * newWidth,
  });
  const ctx = canvas.getContext("2d");
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
  return canvas.toDataURL("image/jpeg");
}

Version 2

Now uses the imagePromptPrefix and imagePromptSuffix! Also removed the negativePrompt since we can specify them on the imagePromptSuffix

oc.thread.on("MessageAdded", async function () {
  let lastMessage = oc.thread.messages.at(-1);
  // console.log(lastMessage);
  if (lastMessage.author !== "ai") {
    if (oc.character.customData.description != "") return
    oc.thread.messages.push({
      author: "user",
      content: `${oc.character.name}, describe your current appearance in detail. Reply with a comma-separated list of keywords and keyphrases which *visually* capture your apprearance only. Imagine you're giving a list of keywords to an artist who will use them to draw you. Describe the your appearance with keywords/keyphrases, including your race, class, age, etc. Respond with only the comma-separated keyphrases - nothing more, nothing less.

Start with "Here is my current appearance:"`,
    });
  }
});
window.alreadyGenerating = false;
oc.thread.on("MessageAdded", async function () {
  oc.character.customData.description = oc.character.customData.description
    ? oc.character.customData.description
    : "";
  let lastMessage = oc.thread.messages.at(-1);
  if (alreadyGenerating) return;
  alreadyGenerating = true;

  if (lastMessage.author === "ai") {
    console.log("DESC: ", oc.character.customData.description);
    if (/^Here is my current appearance\:/.test(lastMessage.content)) {
      if (oc.character.customData.description == "") {
        oc.character.customData.description = lastMessage.content.replace(
          "Here is my current appearance:",
          ""
        );
        // console.log("NEW DESC: ", oc.character.customData.description);

        oc.character.customData.PUBLIC = {
          description: oc.character.customData.description,
        };
        // console.log("PUBLIC: ", oc.character.customData.PUBLIC);
      }
      oc.thread.messages.pop();
      oc.thread.messages.pop();
    } else {
      // oc.thread.messages.pop()
      let response = await oc.getInstructCompletion({
        instruction: `Given the message below, infer the emotions that can be sensed on the message:
---
${lastMessage.content}
---
Be sure to only give the words that describes the emotion e.g. joy, sad, happy, cheeky, etc. 
Respond with only the comma-separated keyphrases - nothing more, nothing less.`,
        stopSequences: ["\n\n"],
      });

      let prompt = `${oc.character.imagePromptPrefix == '' ? '' : oc.character.imagePromptPrefix + ', '}${oc.character.customData.description}, (in ${response.text} expression:1.1)${oc.character.imagePromptSuffix == '' ? '' : ', '+ oc.character.imagePromptSuffix}`;
      (async function () {
        // let prevDataUrl = oc.character.avatar.url;
        let { dataUrl } = await oc.textToImage({
          prompt: prompt,
          seed: `104`,
          resolution: `512x768`,
        });
        oc.thread.messages.push({
          author: "ai",
          content: `<br><img src="${await resizeDataURLWidth(
            dataUrl,
            200
          )}" title="${prompt}">`,
          hiddenFrom: ["ai"],
        });
      })();
    }
  }
  alreadyGenerating = false;
});

async function resizeDataURLWidth(dataURL, newWidth) {
  const blob = await fetch(dataURL).then((res) => res.blob());
  const bitmap = await createImageBitmap(blob);
  const canvas = Object.assign(document.createElement("canvas"), {
    width: newWidth,
    height: (bitmap.height / bitmap.width) * newWidth,
  });
  const ctx = canvas.getContext("2d");
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
  return canvas.toDataURL("image/jpeg");
}

Version 1

oc.thread.on("MessageAdded", async function () {
  let lastMessage = oc.thread.messages.at(-1);
  // console.log(lastMessage);
  if (lastMessage.author !== "ai") {
    if (oc.character.customData.description != "") return
    oc.thread.messages.push({
      author: "user",
      content: `${oc.character.name}, describe your current appearance in detail. Reply with a comma-separated list of keywords and keyphrases which *visually* capture your apprearance only. Imagine you're giving a list of keywords to an artist who will use them to draw you. Describe the your appearance with keywords/keyphrases, including your race, class, age, etc. Respond with only the comma-separated keyphrases - nothing more, nothing less.

Start with "Here is my current appearance:"`,
    });
  }
});
window.alreadyGenerating = false;
oc.thread.on("MessageAdded", async function () {
  oc.character.customData.description = oc.character.customData.description
    ? oc.character.customData.description
    : "";
  let lastMessage = oc.thread.messages.at(-1);
  if (alreadyGenerating) return;
  alreadyGenerating = true;

  if (lastMessage.author === "ai") {
    console.log("DESC: ", oc.character.customData.description);
    if (/^Here is my current appearance\:/.test(lastMessage.content)) {
      if (oc.character.customData.description == "") {
        oc.character.customData.description = lastMessage.content.replace(
          "Here is my current appearance:",
          ""
        );
        // console.log("NEW DESC: ", oc.character.customData.description);

        oc.character.customData.PUBLIC = {
          description: oc.character.customData.description,
        };
        // console.log("PUBLIC: ", oc.character.customData.PUBLIC);
      }
      oc.thread.messages.pop();
      oc.thread.messages.pop();
    } else {
      // oc.thread.messages.pop()
      let response = await oc.getInstructCompletion({
        instruction: `Given the message below, infer the emotions that can be sensed on the message:
---
${lastMessage.content}
---
Be sure to only give the words that describes the emotion e.g. joy, sad, happy, cheeky, etc. 
Do not reply with a sentence, only keywords of the emotion.`,
        stopSequences: ["\n\n"],
      });

      let prompt = `${oc.character.customData.description}, (in ${response.text} expression:1.1), portrait, digital art, detailed, concept portrait art, uhd, 8k`;
      (async function () {
        // let prevDataUrl = oc.character.avatar.url;
        let { dataUrl } = await oc.textToImage({
          prompt: prompt,
          negativePrompt: `(semi-realistic, hands, worst quality, blurry, low resolution, low quality:1.2)`,
          seed: `104`,
          resolution: `512x768`,
        });
        oc.thread.messages.push({
          author: "ai",
          content: `<br><img src="${await resizeDataURLWidth(
            dataUrl,
            200
          )}" title="${prompt}">`,
          hiddenFrom: ["ai"],
        });
      })();
    }
  }
  alreadyGenerating = false;
});

async function resizeDataURLWidth(dataURL, newWidth) {
  const blob = await fetch(dataURL).then((res) => res.blob());
  const bitmap = await createImageBitmap(blob);
  const canvas = Object.assign(document.createElement("canvas"), {
    width: newWidth,
    height: (bitmap.height / bitmap.width) * newWidth,
  });
  const ctx = canvas.getContext("2d");
  ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
  return canvas.toDataURL("image/jpeg");
}
Edit
Pub: 01 Jan 2024 01:30 UTC
Edit: 04 Oct 2024 14:51 UTC
Views: 1911