Compare commits
No commits in common. "5ca047cb3a5e83815651626a512e93352dc6c119bcf75020cf2f3e44cd171f81" and "29832dfce3c02a0f143b9c7ffa0affd81dbb8ab608ead6e45ef55f88244b2081" have entirely different histories.
5ca047cb3a
...
29832dfce3
14 changed files with 169 additions and 202 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "aslobot-matrix",
|
||||
"version": "0.7.0",
|
||||
"version": "0.6.0",
|
||||
"description": "",
|
||||
"license": "ISC",
|
||||
"author": "",
|
||||
|
|
|
|||
|
|
@ -14,12 +14,7 @@
|
|||
"gain": 5,
|
||||
"startingRequirement": 50,
|
||||
"multiplier": 1.25,
|
||||
"timeout": 300000
|
||||
},
|
||||
"money": {
|
||||
"gain": 2,
|
||||
"startingValue": 10,
|
||||
"timeout": 3600000
|
||||
"timeout": 60000
|
||||
},
|
||||
"ai": {
|
||||
"api": {
|
||||
|
|
|
|||
|
|
@ -4,31 +4,14 @@ import { type IUser, type TRole } from "./store/types.js";
|
|||
import type { ILevel } from "./types.js";
|
||||
|
||||
const getUserById = (userId: string): IUser => {
|
||||
const user = state.users.find((user) => user.id === userId);
|
||||
if (!user) {
|
||||
return {
|
||||
return (
|
||||
state.users.find((user) => user.id === userId) ?? {
|
||||
id: ":",
|
||||
role: "NONE",
|
||||
experience: 0,
|
||||
money: 0,
|
||||
aiCost: 0,
|
||||
lastMessageTimestamp: 0,
|
||||
lastExperienceGainTimestamp: 0,
|
||||
lastMoneyGainTimestamp: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (!user.experience) {
|
||||
user.experience = 0;
|
||||
}
|
||||
if (!user.money) {
|
||||
user.money = 0;
|
||||
}
|
||||
if (!user.aiCost) {
|
||||
user.aiCost = 0;
|
||||
}
|
||||
|
||||
return user;
|
||||
);
|
||||
};
|
||||
|
||||
const checkRoles = (roles: TRole[], userId: string) => {
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ import type { ICallbackStore } from "../types.js";
|
|||
import { config } from "../../config.js";
|
||||
import { getTextGemini, getImageGemini } from "../../services/ai/ai.js";
|
||||
import { alts } from "./alts.js";
|
||||
import type { IAdminInstructions } from "./types.js";
|
||||
import { getUserById } from "../../helpers.js";
|
||||
import { prices } from "./prices.js";
|
||||
|
||||
let client: MatrixClient;
|
||||
|
||||
|
|
@ -18,58 +15,40 @@ const registerModuleAI = (
|
|||
callbackStore.messageCallbacks.push({
|
||||
startConditions: [`${config.app.triggerPrefix}ai `],
|
||||
callbackFunc: onAI,
|
||||
allowedRoles: ["USER", "MODERATOR", "ADMIN"],
|
||||
});
|
||||
callbackStore.messageCallbacks.push({
|
||||
startConditions: [`${config.app.triggerPrefix}img `],
|
||||
startConditions: [
|
||||
...alts.map((alt) => `${alt.key} when`),
|
||||
...alts.map((alt) => `${alt.key} ked`),
|
||||
`!img`,
|
||||
],
|
||||
callbackFunc: onImageGen,
|
||||
allowedRoles: ["USER", "MODERATOR", "ADMIN"],
|
||||
});
|
||||
};
|
||||
|
||||
const onAI = async (text: string, roomId: string, sender: string) => {
|
||||
const user = getUserById(sender);
|
||||
|
||||
const onAI = async (text: string, roomId: string) => {
|
||||
if (text.trim().length < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
let textMod = text.replace("!ai", "").trim().toLowerCase();
|
||||
let instructions = {
|
||||
prefferedLanguages: ["english", "slovak"],
|
||||
adminText: "Be concise, try to keep text as short as possible",
|
||||
alts: alts,
|
||||
} as IAdminInstructions;
|
||||
|
||||
textMod = `Admin Instructions:\n${instructions}\nPrompt:\n${textMod}`;
|
||||
|
||||
const responseAI = await getTextGemini(textMod);
|
||||
|
||||
user.aiCost += responseAI.tokens * prices.text;
|
||||
|
||||
client.sendTextMessage(roomId, responseAI.text);
|
||||
const responseAI = await getTextGemini(text.replace("!ai ", ""));
|
||||
client.sendTextMessage(roomId, responseAI);
|
||||
};
|
||||
|
||||
const onImageGen = async (text: string, roomId: string, sender: string) => {
|
||||
const user = getUserById(sender);
|
||||
|
||||
const onImageGen = async (text: string, roomId: string) => {
|
||||
let textMod = text.replace("!img", "").trim().toLowerCase();
|
||||
alts.forEach((alt) => {
|
||||
alt.keys.forEach((key) => {
|
||||
textMod = textMod.replaceAll(key, alt.alt);
|
||||
});
|
||||
textMod = textMod.replaceAll(alt.key, alt.alt);
|
||||
});
|
||||
|
||||
const responseAI = await getImageGemini(textMod);
|
||||
|
||||
user.aiCost += responseAI.tokens * prices.image;
|
||||
if (!responseAI.image || responseAI.image.length < 10) {
|
||||
const buffer = await getImageGemini(textMod);
|
||||
if (!buffer || buffer.length < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageName = `photo-img-gen.png`;
|
||||
|
||||
const uploadResult = await client.uploadContent(responseAI.image, {
|
||||
const uploadResult = await client.uploadContent(buffer, {
|
||||
type: "image/png",
|
||||
name: imageName,
|
||||
});
|
||||
|
|
@ -80,7 +59,7 @@ const onImageGen = async (text: string, roomId: string, sender: string) => {
|
|||
url: uploadResult.content_uri,
|
||||
info: {
|
||||
mimetype: "image/png",
|
||||
size: responseAI.image.length,
|
||||
size: buffer.length,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,73 +2,164 @@ import type { IAIAlt } from "./types.js";
|
|||
|
||||
const alts: IAIAlt[] = [
|
||||
{
|
||||
keys: [
|
||||
"satek",
|
||||
"sateg",
|
||||
"sadeg",
|
||||
"coty",
|
||||
"satko",
|
||||
"sadko",
|
||||
"vlado",
|
||||
"vladko",
|
||||
"vladimir",
|
||||
"macak",
|
||||
"macag",
|
||||
],
|
||||
key: "satek",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
keys: ["macica", "macico"],
|
||||
key: "sateg",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "sadeg",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "satko",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "sadko",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "vlado",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "vladko",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "vladimir",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "macag",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "vladko",
|
||||
alt: "black cat",
|
||||
},
|
||||
{
|
||||
key: "macica",
|
||||
alt: "white cat",
|
||||
},
|
||||
{
|
||||
keys: ["gabor", "gaber", "martin", "marting"],
|
||||
key: "macico",
|
||||
alt: "white cat",
|
||||
},
|
||||
{
|
||||
key: "gabor",
|
||||
alt: "hedgehog",
|
||||
},
|
||||
{
|
||||
keys: [
|
||||
"madys",
|
||||
"mandak",
|
||||
"mandag",
|
||||
"mando",
|
||||
"mandik",
|
||||
"madik",
|
||||
"madeg",
|
||||
"madek",
|
||||
],
|
||||
key: "martin",
|
||||
alt: "hedgehog",
|
||||
},
|
||||
{
|
||||
key: "madys",
|
||||
alt: "beaver",
|
||||
},
|
||||
{
|
||||
keys: ["janys", "jano", "janeg", "janek", "aslanek", "aslan", "aslo"],
|
||||
key: "mandak",
|
||||
alt: "beaver",
|
||||
},
|
||||
{
|
||||
key: "mando",
|
||||
alt: "beaver",
|
||||
},
|
||||
{
|
||||
key: "mandik",
|
||||
alt: "beaver",
|
||||
},
|
||||
{
|
||||
key: "madik",
|
||||
alt: "beaver",
|
||||
},
|
||||
{
|
||||
key: "madeg",
|
||||
alt: "beaver",
|
||||
},
|
||||
{
|
||||
key: "janys",
|
||||
alt: "lion",
|
||||
},
|
||||
{
|
||||
keys: ["marek", "mareg", "macek", "maceg", "rod"],
|
||||
key: "jano",
|
||||
alt: "lion",
|
||||
},
|
||||
{
|
||||
key: "janeg",
|
||||
alt: "lion",
|
||||
},
|
||||
{
|
||||
key: "aslan",
|
||||
alt: "lion",
|
||||
},
|
||||
{
|
||||
key: "aslo",
|
||||
alt: "lion",
|
||||
},
|
||||
{
|
||||
key: "marek",
|
||||
alt: "purple snake",
|
||||
},
|
||||
{
|
||||
keys: ["lara", "kostur", "kosturik"],
|
||||
key: "mareg",
|
||||
alt: "purple snake",
|
||||
},
|
||||
{
|
||||
key: "maceg",
|
||||
alt: "purple snake",
|
||||
},
|
||||
{
|
||||
key: "rod",
|
||||
alt: "purple snake",
|
||||
},
|
||||
{
|
||||
key: "lara",
|
||||
alt: "ginger dog",
|
||||
},
|
||||
{
|
||||
keys: ["tato"],
|
||||
alt: "green troll",
|
||||
key: "kostur",
|
||||
alt: "ginger dog",
|
||||
},
|
||||
{
|
||||
keys: ["mama", "monika", "monik"],
|
||||
alt: "god's peasant",
|
||||
key: "kosturik",
|
||||
alt: "ginger dog",
|
||||
},
|
||||
{
|
||||
keys: ["puk", "pucik", "pucig"],
|
||||
alt: "fat rat",
|
||||
key: "tato",
|
||||
alt: "troll",
|
||||
},
|
||||
{
|
||||
keys: ["becky", "beky", "beki"],
|
||||
alt: "35 year old pink haired giantess",
|
||||
key: "mama",
|
||||
alt: "angel",
|
||||
},
|
||||
{
|
||||
keys: ["veronika", "veronik", "wewe", "wonka"],
|
||||
alt: "lgbt princess with curly hair",
|
||||
key: "monik",
|
||||
alt: "angel",
|
||||
},
|
||||
{
|
||||
key: "puk",
|
||||
alt: "rat",
|
||||
},
|
||||
{
|
||||
key: "becky",
|
||||
alt: "giantess",
|
||||
},
|
||||
{
|
||||
key: "beky",
|
||||
alt: "giantess",
|
||||
},
|
||||
{
|
||||
key: "beki",
|
||||
alt: "giantess",
|
||||
},
|
||||
{
|
||||
key: "veronik",
|
||||
alt: "lgbt princess",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
const prices = {
|
||||
text: 0.000002,
|
||||
image: 0.00003,
|
||||
};
|
||||
|
||||
export { prices };
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
interface IAIAlt {
|
||||
keys: string[];
|
||||
key: string;
|
||||
alt: string;
|
||||
}
|
||||
|
||||
interface IAdminInstructions {
|
||||
prefferedLanguages: string[];
|
||||
adminText: string;
|
||||
alts: IAIAlt[];
|
||||
}
|
||||
|
||||
export { type IAIAlt, type IAdminInstructions };
|
||||
export { type IAIAlt };
|
||||
|
|
|
|||
|
|
@ -61,21 +61,17 @@ const onHelp = (_text: string, roomId: string) => {
|
|||
client.sendHtmlMessage(
|
||||
roomId,
|
||||
"",
|
||||
`<h3>Role: Any</h3>
|
||||
`<h3>Role: User</h3>
|
||||
<ul>
|
||||
<li><b>!ping</b> - Pong!</li>
|
||||
<li><b>!info</b> - Prints information about this bot</li>
|
||||
<li><b>!say {text}</b> - Repeats your message</li>
|
||||
<li><b>!bowling {text}</b> - Repeats your message in bowling</li>
|
||||
<li><b>!help</b> - Prints this help message</li>
|
||||
<li><b>!me</b> - Prints your role, level, experience, money and ai cost</li>
|
||||
<li><b>!level</b> - Prints your level and experience</li>
|
||||
<li><b>!leaderboard</b> - Prints total user ranking</li>
|
||||
<li><b>!aileaderboard</b> - Prints total user ai cost</li>
|
||||
</ul>
|
||||
<h3>Role: User</h3>
|
||||
<ul>
|
||||
<li><b>!ai {text}</b> - Say something to Gemini 3</li>
|
||||
<li><b>!img {text}</b> - Generate an image</li>
|
||||
<li><b>!img {text}</b> - Generate an image (also works with "{name} when/ked {text}")</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
<h3>Role: Moderator</h3>
|
||||
|
|
|
|||
|
|
@ -18,28 +18,15 @@ const onAnyMessage = (
|
|||
id: sender,
|
||||
role: "USER",
|
||||
experience: 0,
|
||||
money: 10,
|
||||
aiCost: 0,
|
||||
lastMessageTimestamp: date,
|
||||
lastExperienceGainTimestamp: date,
|
||||
lastMoneyGainTimestamp: date,
|
||||
});
|
||||
return onAnyMessage(client, _text, roomId, sender);
|
||||
}
|
||||
|
||||
const levelBefore = getLevel(user.experience);
|
||||
|
||||
if (
|
||||
date >
|
||||
user.lastExperienceGainTimestamp + config.app.experience.timeout
|
||||
) {
|
||||
if (date > user.lastMessageTimestamp + config.app.experience.timeout) {
|
||||
user.experience += config.app.experience.gain;
|
||||
user.lastExperienceGainTimestamp = date;
|
||||
}
|
||||
|
||||
if (date > user.lastMoneyGainTimestamp + config.app.money.timeout) {
|
||||
user.money += config.app.money.gain;
|
||||
user.lastMoneyGainTimestamp = date;
|
||||
}
|
||||
user.lastMessageTimestamp = date;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,31 +13,24 @@ const registerModuleUser = (
|
|||
client = matrixClient;
|
||||
|
||||
callbackStore.messageCallbacks.push({
|
||||
startConditions: [`${config.app.triggerPrefix}me`],
|
||||
callbackFunc: onMe,
|
||||
startConditions: [`${config.app.triggerPrefix}level`],
|
||||
callbackFunc: onLevel,
|
||||
});
|
||||
callbackStore.messageCallbacks.push({
|
||||
startConditions: [`${config.app.triggerPrefix}leaderboard`],
|
||||
callbackFunc: onLeaderboard,
|
||||
});
|
||||
callbackStore.messageCallbacks.push({
|
||||
startConditions: [`${config.app.triggerPrefix}aileaderboard`],
|
||||
callbackFunc: onAILeaderboard,
|
||||
});
|
||||
};
|
||||
|
||||
const onMe = (_text: string, roomId: string, sender: string) => {
|
||||
const user = getUserById(sender);
|
||||
const level = getLevel(user.experience);
|
||||
const onLevel = (_text: string, roomId: string, sender: string) => {
|
||||
const level = getLevel(getUserById(sender).experience);
|
||||
|
||||
client.sendHtmlMessage(
|
||||
roomId,
|
||||
"",
|
||||
`<p>Your Role: <b>${user.role}</b></p></br>
|
||||
<p>Your Money: <b>${user.money}</b></p></br>
|
||||
<p>Your AI Cost: <b>${user.aiCost}$</b></p></br>
|
||||
<p>Your Level: <b>${level.level}</b></p></br>
|
||||
<i>Next level progress: <b>${level.experienceInLevel}/${level.expToNextLevel}xp</b></i>`,
|
||||
`<h3>Your Level: ${level.level}</h3>
|
||||
</br>
|
||||
<i>Next level progress: ${level.experienceInLevel}/${level.expToNextLevel}xp</i>`,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -65,28 +58,4 @@ const onLeaderboard = (_text: string, roomId: string) => {
|
|||
);
|
||||
};
|
||||
|
||||
const onAILeaderboard = (_text: string, roomId: string) => {
|
||||
const mapUsersToLeaderboard = (user: IUser): string => {
|
||||
const cost = user.aiCost;
|
||||
const userName = getUserName(user);
|
||||
const userNameMod =
|
||||
userName.charAt(0).toUpperCase() + userName.slice(1);
|
||||
|
||||
return `<li>${userNameMod}: cost <b>${cost}</b></li>`;
|
||||
};
|
||||
|
||||
const users = state.users.sort(
|
||||
(userA, userB) => userB.aiCost - userA.aiCost,
|
||||
);
|
||||
|
||||
client.sendHtmlMessage(
|
||||
roomId,
|
||||
"",
|
||||
`<h3>AI Cost Leaderboard</h3>
|
||||
<ul>
|
||||
${users.map(mapUsersToLeaderboard).join("")}
|
||||
</ul>`,
|
||||
);
|
||||
};
|
||||
|
||||
export { registerModuleUser };
|
||||
|
|
|
|||
|
|
@ -1,24 +1,22 @@
|
|||
import { GoogleGenAI } from "@google/genai";
|
||||
import { config } from "../../config.js";
|
||||
import type { AIResponseImage, AIResponseText } from "./types.js";
|
||||
|
||||
const googleAI = new GoogleGenAI({
|
||||
apiKey: config.app.ai.api.key,
|
||||
});
|
||||
|
||||
const getTextGemini = async (input: string): Promise<AIResponseText> => {
|
||||
const getTextGemini = async (input: string): Promise<string> => {
|
||||
const response = await googleAI.models.generateContent({
|
||||
model: "gemini-3-flash-preview",
|
||||
contents: input,
|
||||
});
|
||||
|
||||
return {
|
||||
text: response.text ?? "AI Error",
|
||||
tokens: response.usageMetadata?.totalTokenCount ?? 0,
|
||||
};
|
||||
return response.text ?? "AI Error";
|
||||
};
|
||||
|
||||
const getImageGemini = async (input: string): Promise<AIResponseImage> => {
|
||||
const getImageGemini = async (
|
||||
input: string,
|
||||
): Promise<Buffer<ArrayBuffer> | undefined> => {
|
||||
const response = await googleAI.models.generateContent({
|
||||
model: "gemini-2.5-flash-image",
|
||||
contents: input,
|
||||
|
|
@ -40,10 +38,7 @@ const getImageGemini = async (input: string): Promise<AIResponseImage> => {
|
|||
}
|
||||
});
|
||||
|
||||
return {
|
||||
image: buffer,
|
||||
tokens: response.usageMetadata?.totalTokenCount ?? 0,
|
||||
};
|
||||
return buffer;
|
||||
};
|
||||
|
||||
export { getTextGemini, getImageGemini };
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
export * from "./ai.js";
|
||||
export * from "./types.js";
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
interface AIResponseText {
|
||||
text: string;
|
||||
tokens: number;
|
||||
}
|
||||
|
||||
interface AIResponseImage {
|
||||
image: Buffer<ArrayBuffer> | undefined;
|
||||
tokens: number;
|
||||
}
|
||||
|
||||
export { type AIResponseText, type AIResponseImage };
|
||||
|
|
@ -6,11 +6,7 @@ interface IUser {
|
|||
id: string;
|
||||
role: TRole;
|
||||
experience: number;
|
||||
money: number;
|
||||
aiCost: number;
|
||||
lastMessageTimestamp: number;
|
||||
lastExperienceGainTimestamp: number;
|
||||
lastMoneyGainTimestamp: number;
|
||||
}
|
||||
|
||||
type TRole = "NONE" | "USER" | "MODERATOR" | "ADMIN";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue