refactor!: rewrite bot core; add AI (Ollama, Gemini), DB, new commands
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import "dotenv/config";
|
||||
import {drizzle, LibSQLDatabase} from "drizzle-orm/libsql";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
export class DatabaseManager {
|
||||
|
||||
static db: LibSQLDatabase;
|
||||
|
||||
static init() {
|
||||
DatabaseManager.db = drizzle(Environment.DB_PATH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import * as fs from "fs";
|
||||
import {Environment} from "../common/environment";
|
||||
|
||||
|
||||
export let muted: Set<number> = new Set<number>();
|
||||
|
||||
type DataJsonFile = {
|
||||
admins: number[]
|
||||
muted: number[]
|
||||
}
|
||||
|
||||
export let jsonFile: DataJsonFile;
|
||||
|
||||
type AnswersJsonFile = {
|
||||
test: string[]
|
||||
prefix: string[]
|
||||
better: string[]
|
||||
who: string[]
|
||||
kick: string[]
|
||||
invite: string[]
|
||||
day: number[]
|
||||
}
|
||||
|
||||
export const testAnswers: string[] = [];
|
||||
export const prefixAnswers: string[] = [];
|
||||
export const betterAnswers: string[] = [];
|
||||
export const whoAnswers: string[] = [];
|
||||
export const kickAnswers: string[] = [];
|
||||
export const inviteAnswers: string[] = [];
|
||||
export const dayAnswers: number[] = [];
|
||||
|
||||
export async function addMute(id: number): Promise<boolean> {
|
||||
if (muted.has(id)) return Promise.resolve(false);
|
||||
|
||||
muted.add(id);
|
||||
await saveData();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
export async function removeMute(id: number): Promise<boolean> {
|
||||
if (!muted.has(id)) return Promise.resolve(false);
|
||||
muted.delete(id);
|
||||
await saveData();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
export async function readData(): Promise<void> {
|
||||
try {
|
||||
jsonFile = JSON.parse(fs.readFileSync(`${Environment.DATA_PATH}/data.json`).toString());
|
||||
|
||||
const admins = jsonFile.admins || [];
|
||||
admins.unshift(Environment.CREATOR_ID);
|
||||
|
||||
Environment.setAdmins(new Set<number>(admins));
|
||||
|
||||
muted = new Set<number>(jsonFile.muted || []);
|
||||
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveData(): Promise<void> {
|
||||
const adminIds: number[] = [];
|
||||
Environment.ADMIN_IDS.forEach(id => adminIds.push(id));
|
||||
jsonFile.admins = adminIds;
|
||||
|
||||
const mutedList: number[] = [];
|
||||
muted.forEach(id => mutedList.push(id));
|
||||
jsonFile.muted = mutedList;
|
||||
|
||||
try {
|
||||
fs.writeFileSync(`${Environment.DATA_PATH}/data.json`, JSON.stringify(jsonFile));
|
||||
return readData();
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function retrieveAnswers(): Promise<void> {
|
||||
try {
|
||||
const json: AnswersJsonFile = JSON.parse(fs.readFileSync(`${Environment.DATA_PATH}/answers.json`).toString());
|
||||
json.test.forEach(e => testAnswers.push(e));
|
||||
json.prefix.forEach(e => prefixAnswers.push(e));
|
||||
json.better.forEach(e => betterAnswers.push(e));
|
||||
json.who.forEach(e => whoAnswers.push(e));
|
||||
json.kick.forEach(e => kickAnswers.push(e));
|
||||
json.invite.forEach(e => inviteAnswers.push(e));
|
||||
json.day.forEach(e => dayAnswers.push(e));
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import {messagesTable} from "./schema";
|
||||
import {DatabaseManager} from "./database-manager";
|
||||
import {StoredMessage} from "../model/stored-message";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
import {inArray} from "drizzle-orm/sql/expressions/conditions";
|
||||
import {Message} from "typescript-telegram-bot-api";
|
||||
import {Dao} from "../base/dao";
|
||||
import {buildExcludedSet} from "../util/utils";
|
||||
|
||||
export class MessageDao extends Dao<StoredMessage> {
|
||||
|
||||
private tag: string = "MessageDao";
|
||||
|
||||
override async getAll(): Promise<StoredMessage[]> {
|
||||
const then = Date.now();
|
||||
|
||||
const messages = await DatabaseManager.db.select().from(messagesTable);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: getAll()`, `took ${diff}ms; size: ${messages.length}`);
|
||||
|
||||
return this.mapFrom(messages);
|
||||
}
|
||||
|
||||
override async getById(params: { chatId: number, id: number }): Promise<StoredMessage | null> {
|
||||
const then = Date.now();
|
||||
|
||||
const messages =
|
||||
await DatabaseManager.db.select()
|
||||
.from(messagesTable)
|
||||
.where(
|
||||
and(
|
||||
eq(messagesTable.chatId, params.chatId),
|
||||
eq(messagesTable.id, params.id)
|
||||
)
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: getById(${params.chatId}, ${params.id})`, `took ${diff}ms; size: ${messages.length}`);
|
||||
|
||||
const m = messages[0];
|
||||
if (!m) return null;
|
||||
return this.mapFrom([m])[0];
|
||||
}
|
||||
|
||||
override async getByIds(params: { chatId: number, ids: number[] }): Promise<StoredMessage[]> {
|
||||
const then = Date.now();
|
||||
|
||||
const messages =
|
||||
await DatabaseManager.db.select()
|
||||
.from(messagesTable)
|
||||
.where(
|
||||
and(
|
||||
eq(messagesTable.chatId, params.chatId),
|
||||
inArray(messagesTable.id, params.ids)
|
||||
)
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: getByIds(${params.chatId}, ${params.ids})`, `took ${diff}ms; size: ${messages.length}`);
|
||||
|
||||
return this.mapFrom(messages);
|
||||
}
|
||||
|
||||
async insert(values: typeof messagesTable.$inferInsert[]): Promise<true> {
|
||||
const then = Date.now();
|
||||
const r = await DatabaseManager.db
|
||||
.insert(messagesTable)
|
||||
.values(values)
|
||||
.onConflictDoUpdate({
|
||||
target: messagesTable.id,
|
||||
set: buildExcludedSet(messagesTable, ["id"])
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: insert(size: ${values.length})`, `took ${diff}ms'; inserted: ${r.rowsAffected}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
mapTo(messages: Message[]): typeof messagesTable.$inferInsert[] {
|
||||
return messages.map(msg => {
|
||||
return {
|
||||
chatId: msg.chat.id,
|
||||
id: msg.message_id,
|
||||
replyToMessageId: msg.reply_to_message?.message_id,
|
||||
fromId: msg.from.id,
|
||||
text: msg.text,
|
||||
date: msg.date,
|
||||
firstName: msg.from.first_name,
|
||||
lastName: msg.from.last_name,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
mapFrom(messages: typeof messagesTable.$inferInsert[]): StoredMessage[] {
|
||||
return messages.map(m => {
|
||||
return {
|
||||
firstName: m.firstName,
|
||||
lastName: m.lastName,
|
||||
chatId: m.chatId,
|
||||
messageId: m.id,
|
||||
replyToMessageId: m.replyToMessageId,
|
||||
fromId: m.fromId,
|
||||
text: m.text,
|
||||
date: m.date
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {int, sqliteTable, text} from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const messagesTable = sqliteTable("messages", {
|
||||
id: int().primaryKey().unique().notNull(),
|
||||
chatId: int().notNull(),
|
||||
replyToMessageId: int(),
|
||||
fromId: int().notNull(),
|
||||
text: text().notNull(),
|
||||
date: int().notNull(),
|
||||
firstName: text().notNull(),
|
||||
lastName: text(),
|
||||
});
|
||||
|
||||
export type MessageInsert = typeof messagesTable.$inferInsert;
|
||||
|
||||
export const usersTable = sqliteTable("users", {
|
||||
id: int().primaryKey().unique().notNull(),
|
||||
isBot: int().notNull(),
|
||||
firstName: text().notNull(),
|
||||
lastName: text(),
|
||||
userName: text(),
|
||||
isPremium: int(),
|
||||
});
|
||||
|
||||
export type UserInsert = typeof usersTable.$inferInsert;
|
||||
@@ -0,0 +1,105 @@
|
||||
import {StoredUser} from "../model/stored-user";
|
||||
import {Dao} from "../base/dao";
|
||||
import {DatabaseManager} from "./database-manager";
|
||||
import {UserInsert, usersTable} from "./schema";
|
||||
import {eq} from "drizzle-orm";
|
||||
import {inArray} from "drizzle-orm/sql/expressions/conditions";
|
||||
import {User} from "typescript-telegram-bot-api";
|
||||
import {boolToInt, buildExcludedSet} from "../util/utils";
|
||||
|
||||
export class UserDao extends Dao<StoredUser> {
|
||||
|
||||
private tag: string = "UserDao";
|
||||
|
||||
override async getAll(): Promise<StoredUser[]> {
|
||||
const then = Date.now();
|
||||
|
||||
const users = await DatabaseManager.db.select().from(usersTable);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: getAll()`, `took ${diff}ms; size: ${users.length}`);
|
||||
|
||||
return this.mapFrom(users);
|
||||
}
|
||||
|
||||
override async getById(params: { id: number }): Promise<StoredUser | null> {
|
||||
const then = Date.now();
|
||||
|
||||
const users =
|
||||
await DatabaseManager.db.select()
|
||||
.from(usersTable)
|
||||
.where(
|
||||
eq(usersTable.id, params.id)
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: getById(${params.id})`, `took ${diff}ms; size: ${users.length}`);
|
||||
|
||||
const u = users[0];
|
||||
if (!u) return null;
|
||||
return this.mapFrom([u])[0];
|
||||
}
|
||||
|
||||
override async getByIds(params: { ids: number[] }): Promise<StoredUser[]> {
|
||||
const then = Date.now();
|
||||
|
||||
const users =
|
||||
await DatabaseManager.db.select()
|
||||
.from(usersTable)
|
||||
.where(
|
||||
inArray(usersTable.id, params.ids)
|
||||
);
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: getByIds(${params.ids})`, `took ${diff}ms; size: ${users.length}`);
|
||||
|
||||
return this.mapFrom(users);
|
||||
}
|
||||
|
||||
override async insert(values: UserInsert[] | UserInsert): Promise<true> {
|
||||
const rows = Array.isArray(values) ? values : [values];
|
||||
|
||||
const then = Date.now();
|
||||
const r = await DatabaseManager.db
|
||||
.insert(usersTable)
|
||||
.values(rows)
|
||||
.onConflictDoUpdate({
|
||||
target: usersTable.id,
|
||||
set: buildExcludedSet(usersTable, ["id"])
|
||||
});
|
||||
|
||||
const now = Date.now();
|
||||
const diff = now - then;
|
||||
console.log(`${this.tag}: insert(size: ${rows.length})`, `took ${diff}ms; inserted: ${r.rowsAffected}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
mapTo(users: User[]): UserInsert[] {
|
||||
return users.map(u => {
|
||||
return {
|
||||
id: u.id,
|
||||
isBot: boolToInt(u.is_bot),
|
||||
firstName: u.first_name,
|
||||
lastName: u.last_name,
|
||||
userName: u.username,
|
||||
isPremium: boolToInt(u.is_premium)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
mapFrom(users: UserInsert[]): StoredUser[] {
|
||||
return users.map(u => {
|
||||
return {
|
||||
id: u.id,
|
||||
isBot: u.isBot === 1,
|
||||
firstName: u.firstName,
|
||||
lastName: u.lastName,
|
||||
userName: u.userName,
|
||||
isPremium: u.isPremium === 1
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user