diff --git a/.gitignore b/.gitignore index 7447f89..8b2226b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/bin \ No newline at end of file +/bin +*.db \ No newline at end of file diff --git a/models/E621Bot.ts b/classes/E621Bot.ts similarity index 91% rename from models/E621Bot.ts rename to classes/E621Bot.ts index f89fc8d..32d232d 100644 --- a/models/E621Bot.ts +++ b/classes/E621Bot.ts @@ -1,40 +1,47 @@ import { Bot, InlineQueryResultBuilder } from "grammy"; +import { + Conversation, + type ConversationFlavor, + conversations, + createConversation, +} from "@grammyjs/conversations"; import { InlineQueryResult } from "grammy/types"; import { E621UrlBuilderPosts } from "./E621UrlBuilderPosts.ts"; import { ONE_MEGABYTE } from "../constants/numbers.ts"; import { E621UrlBuilderPools } from "./E621UrlBuilderPools.ts"; import { poolSearch } from "../constants/urls.ts"; -import { Post } from "./interfaces.ts"; -import { Pool } from "./interfaces.ts"; +import { Post } from "../interfaces.ts"; +import { Pool } from "../interfaces.ts"; import * as strings from "../constants/strings.ts"; import * as urls from "../constants/urls.ts"; import * as numbers from "../constants/numbers.ts"; +import { edit_blacklist } from "../handlers/edit_blacklist.ts"; +import { Context } from "grammy"; + +type e621BotContext = Context & ConversationFlavor & Conversation; /** * E621Bot can get streams of images based on a users inline query */ -export class E621Bot extends Bot { +export class E621Bot extends Bot { telegramtelegramApiKey: string; e621ApiKey: string; - blacklist: string[]; - blacklistPath: string; hits: number; last_hit_time?: string; constructor( telegramApiKey: string, e621ApiKey: string, - blacklist: string[], - blacklistPath: string = "./blacklist.txt", hits: number = 0, last_hit_time?: string, ) { super(telegramApiKey); this.telegramtelegramApiKey = telegramApiKey; this.e621ApiKey = e621ApiKey; - this.blacklist = blacklist; - this.blacklistPath = blacklistPath; this.hits = hits; this.last_hit_time = last_hit_time; + + this.use(conversations()); + this.use(createConversation(edit_blacklist)); } /** @@ -227,9 +234,8 @@ export class E621Bot extends Bot { return urlBuilder; } - processPosts(posts: Post[]): InlineQueryResult[] { + processPosts(posts: Post[], blacklistedTags: string[]): InlineQueryResult[] { const inlineQueryResults: InlineQueryResult[] = []; - post_loop: for (const post in posts) { const tagMatrix: string[][] = []; @@ -239,9 +245,11 @@ export class E621Bot extends Bot { }); const tags = tagMatrix.flat(); + console.log(this.buildBlacklistRegex(blacklistedTags)); + // Check for blacklisted tags for (const tag in tags) { - if (this.buildBlacklistRegex()?.test(tags[tag])) { + if (this.buildBlacklistRegex(blacklistedTags).test(tags[tag])) { console.log("Blacklisted found skipping post!"); continue post_loop; // Skip this post if a blacklisted tag was found } @@ -342,8 +350,8 @@ export class E621Bot extends Bot { return bytes / ONE_MEGABYTE; // Divide number of bytes by the number of bytes equal to one megabytes } - buildBlacklistRegex(): RegExp | null { - if (this.blacklist.length === 0) return null; - return new RegExp("(" + this.blacklist.join("|") + ")"); + buildBlacklistRegex(blacklistedTags: string[]): RegExp { + if (blacklistedTags.length === 0) return new RegExp(""); + return new RegExp("(" + blacklistedTags.join("|") + ")"); } } diff --git a/models/E621UrlBuilderPools.ts b/classes/E621UrlBuilderPools.ts similarity index 95% rename from models/E621UrlBuilderPools.ts rename to classes/E621UrlBuilderPools.ts index b01447a..15b4bc9 100644 --- a/models/E621UrlBuilderPools.ts +++ b/classes/E621UrlBuilderPools.ts @@ -1,5 +1,5 @@ import { POOLS_PAGE_SIZE } from "../constants/numbers.ts"; -import { E621UrlBuilder } from "./interfaces.ts"; +import { E621UrlBuilder } from "../interfaces.ts"; import * as urls from "../constants/urls.ts"; export class E621UrlBuilderPools implements E621UrlBuilder { diff --git a/models/E621UrlBuilderPosts.ts b/classes/E621UrlBuilderPosts.ts similarity index 98% rename from models/E621UrlBuilderPosts.ts rename to classes/E621UrlBuilderPosts.ts index 17d2017..6d591b5 100644 --- a/models/E621UrlBuilderPosts.ts +++ b/classes/E621UrlBuilderPosts.ts @@ -1,6 +1,6 @@ import * as urls from "../constants/urls.ts"; import { API_PAGE_SIZE } from "../constants/numbers.ts"; -import { E621UrlBuilder } from "./interfaces.ts"; +import { E621UrlBuilder } from "../interfaces.ts"; /** * Build an e621 URL based on parameters passed to this class diff --git a/constants/strings.ts b/constants/strings.ts index ece931a..1817ba7 100644 --- a/constants/strings.ts +++ b/constants/strings.ts @@ -49,5 +49,11 @@ export const fileTypes = { }; export const keywordsRegex = "(id|creator|active|inactive|category|order)"; - export const BLACKLIST_PATH = "./blacklist.txt"; +export const defaultBlacklist = + "gore,scat,watersports,young,-rating:s,loli,shota"; + +// DB strings +export const SQL_BASEPATH = "db/sql"; +export const DB_FILE = "db/prod_db/blacklist.db"; +export const TEST_DB_FILE = "db/test_db/blacklist_test.db"; diff --git a/db/migration.ts b/db/migration.ts new file mode 100644 index 0000000..af4487e --- /dev/null +++ b/db/migration.ts @@ -0,0 +1,17 @@ +import { PathLike } from "node:fs"; +import { DatabaseSync } from "node:sqlite"; +import { SQL_BASEPATH } from "../constants/strings.ts"; + +export function createBlacklistDb(dbFile: PathLike) { + try { + const db = new DatabaseSync(dbFile); + const query = Deno.readTextFileSync( + `${SQL_BASEPATH}/create_blacklist_table.sql`, + ).trim(); + + db.prepare(query).run(); + db.close(); + } catch (err) { + console.error(`Failed to create blacklist db: ${err}`); + } +} diff --git a/db/sql/create_blacklist_table.sql b/db/sql/create_blacklist_table.sql new file mode 100644 index 0000000..f0f2438 --- /dev/null +++ b/db/sql/create_blacklist_table.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS user_db ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + telegram_id BIGINT NOT NULL UNIQUE, + blacklist TEXT DEFAULT 'gore,scat,watersports,young,loli,shota' -- Default blacklist for every user on e621 +); \ No newline at end of file diff --git a/db/utils/createDb.ts b/db/utils/createDb.ts new file mode 100644 index 0000000..0b7ad71 --- /dev/null +++ b/db/utils/createDb.ts @@ -0,0 +1,12 @@ +import { PathLike } from "node:fs"; +import { createBlacklistDb } from "../migration.ts"; + +export function createDatabase(dbFile: PathLike): boolean { + try { + createBlacklistDb(dbFile); + return true; + } catch (err) { + console.error(`Failed to create Database: ${dbFile}: ${err}`); + return false; + } +} diff --git a/deno.json b/deno.json index 15a22f6..3a51851 100644 --- a/deno.json +++ b/deno.json @@ -1,11 +1,12 @@ { "tasks": { - "dev": "deno run --allow-net --allow-env --allow-read --watch main.ts", - "test": "deno test --allow-net --allow-env --allow-read ./tests/*", - "bin": "deno compile --allow-net --allow-env --allow-read -o ./bin/E621Bot main.ts", - "bin-arm": "deno compile --allow-net --allow-env --allow-read --target aarch64-unknown-linux-gnu -o ./bin/E621BotArm main.ts" + "dev": "deno run --allow-write --allow-net --allow-env --allow-read --watch main.ts", + "test": "deno test --allow-write --allow-net --allow-env --allow-read ./tests/*", + "bin": "deno compile --allow-write --allow-net --allow-env --allow-read -o ./bin/E621Bot main.ts", + "bin-arm": "deno compile --allow-write --allow-net --allow-env --allow-read --target aarch64-unknown-linux-gnu -o ./bin/E621BotArm main.ts" }, "imports": { + "@grammyjs/conversations": "npm:@grammyjs/conversations@^2.1.1", "@std/assert": "jsr:@std/assert@^1.0.16", "grammy": "npm:grammy@^1.38.4" } diff --git a/deno.lock b/deno.lock index 1806691..56469a9 100644 --- a/deno.lock +++ b/deno.lock @@ -3,6 +3,7 @@ "specifiers": { "jsr:@std/assert@^1.0.16": "1.0.16", "jsr:@std/internal@^1.0.12": "1.0.12", + "npm:@grammyjs/conversations@^2.1.1": "2.1.1_grammy@1.38.4", "npm:grammy@^1.38.4": "1.38.4" }, "jsr": { @@ -17,6 +18,12 @@ } }, "npm": { + "@grammyjs/conversations@2.1.1_grammy@1.38.4": { + "integrity": "sha512-hoxqwSkaXDeU7mzXulpk3A4Cmd6UZO3HU4aPoITX5ekSHK7ZcUEmMl7RhKKkqw3z6zVbbAShQreJoVV5/dDSLA==", + "dependencies": [ + "grammy" + ] + }, "@grammyjs/types@3.22.2": { "integrity": "sha512-uu7DX2ezhnBPozL3bXHmwhLvaFsh59E4QyviNH4Cij7EdVekYrs6mCzeXsa2pDk30l3uXo7DBahlZLzTPtpYZg==" }, @@ -70,6 +77,7 @@ "workspace": { "dependencies": [ "jsr:@std/assert@^1.0.16", + "npm:@grammyjs/conversations@^2.1.1", "npm:grammy@^1.38.4" ] } diff --git a/handlers/edit_blacklist.ts b/handlers/edit_blacklist.ts new file mode 100644 index 0000000..668d3f3 --- /dev/null +++ b/handlers/edit_blacklist.ts @@ -0,0 +1,58 @@ +import { Conversation } from "@grammyjs/conversations"; +import { Context } from "grammy"; +import { User } from "../types/Blacklist.ts"; +import { getUserByTelegramId, updateUser } from "../models/user.ts"; +import { DB_FILE } from "../constants/strings.ts"; + +/** + * Walk user through editing their blacklist + * @param conversation + * @param ctx + */ +export async function edit_blacklist(conversation: Conversation, ctx: Context) { + const user = getUserByTelegramId(ctx.from?.id!, DB_FILE); + if (user) { + await ctx.reply(user.blacklist.join("\n")); + } else { + await ctx.reply(`Failed to retrieve blacklist to copy.`); + throw new Error(`Failed to retrieve blacklist to copy.`); + } + + await ctx.reply( + `Copy and paste the above message then you can edit it and send it back, or you can send a list of space or line seperated VALID + + Here is a list of valid e621 tags for reference there are a LOT of them.`, + { parse_mode: "HTML" }, + ); + const blacklist = (await conversation.form.text()).split("\n"); + + console.log(blacklist); + + const newUserData: User = { + telegramId: ctx.from?.id!, + blacklist: blacklist, + }; + + try { + updateUser(newUserData, DB_FILE); + } catch (err) { + console.error( + `Failed to save blacklist for ${ctx.from?.first_name} id ${ctx.from?.id}: ${err}`, + ); + } + + const newBlacklist = getUserByTelegramId(ctx.from?.id!, DB_FILE); + + if (newBlacklist) { + await ctx.reply( + `Success! Your new blacklist is: +${newBlacklist.blacklist.join("\n")}`, + { parse_mode: "HTML" }, + ); + } else { + console.error(`Failed to retrieve updated blacklist after update`); + ctx.reply( + "Failed to retrieve updated blacklist try running /blacklist to see your changes", + ); + } +} diff --git a/models/interfaces.ts b/interfaces.ts similarity index 100% rename from models/interfaces.ts rename to interfaces.ts diff --git a/main.ts b/main.ts index 06c2cd6..147ea6a 100644 --- a/main.ts +++ b/main.ts @@ -1,23 +1,41 @@ -import { E621Bot } from "./models/E621Bot.ts"; -import { E621UrlBuilderPosts } from "./models/E621UrlBuilderPosts.ts"; -import { E621UrlBuilderPools } from "./models/E621UrlBuilderPools.ts"; -import { Post } from "./models/interfaces.ts"; -import { Pool } from "./models/interfaces.ts"; +import { E621Bot } from "./classes/E621Bot.ts"; +import { E621UrlBuilderPosts } from "./classes/E621UrlBuilderPosts.ts"; +import { E621UrlBuilderPools } from "./classes/E621UrlBuilderPools.ts"; +import { Post } from "./interfaces.ts"; +import { Pool } from "./interfaces.ts"; import * as numbers from "./constants/numbers.ts"; import * as urls from "./constants/urls.ts"; import * as strings from "./constants/strings.ts"; +import { existsSync } from "node:fs"; +import { createDatabase } from "./db/utils/createDb.ts"; +import { + getUserByTelegramId as getUserByTelegramId, + insertUser as insertUser, + userExists as userExists, +} from "./models/user.ts"; if (import.meta.main) { try { - // Load the blacklist - const decoder = new TextDecoder("utf-8"); - const blackListBytes = await Deno.readFile(strings.BLACKLIST_PATH); - const blacklist = decoder.decode(blackListBytes).split("\n"); // Separate blacklist into an array + // Create the directory structure create it and the db it its not there. + if (!existsSync(strings.DB_FILE)) { + if (!existsSync("db/prod_db")) { + Deno.mkdir("db/prod_db", { recursive: true }); + } + console.log("Creating directory structure"); + Deno.mkdir("db/prod_db", { recursive: true }); + + console.log("Attempting to create blacklist database"); + if (!createDatabase(strings.DB_FILE)) { + throw new Error(`Failed to create blacklist DB`); + } + console.log("Database created!"); + } else { + console.log(`Database found at ${strings.DB_FILE}!`); + } const yiffBot = new E621Bot( Deno.env.get("TELEGRAM_BOT_KEY") || "", Deno.env.get("E621_API_KEY") || "", - blacklist, ); yiffBot.command("start", async (ctx) => @@ -39,6 +57,22 @@ if (import.meta.main) { ); }); + yiffBot.command("blacklist", async (ctx) => { + const user = getUserByTelegramId(ctx.from?.id!, strings.DB_FILE); + if (user) { + await ctx.reply( + `This is your current blacklist: \n ${ + user.blacklist.join("\n") + }`, + { parse_mode: "HTML" }, + ); + } + }); + + yiffBot.command("edit_blacklist", async (ctx) => { + await ctx.conversation.enter("edit_blacklist"); + }); + yiffBot.command("help", async (ctx) => { await ctx.reply(strings.helpString, { parse_mode: "HTML" }); }); @@ -152,6 +186,12 @@ if (import.meta.main) { * Handle general searches */ yiffBot.on("inline_query", async (ctx) => { + // Create new user if not exists + if (!userExists(ctx.from.id, strings.DB_FILE)) { + insertUser({ telegramId: ctx.from.id, blacklist: [] }, strings.DB_FILE); + } + const user = getUserByTelegramId(ctx.from.id, strings.DB_FILE); + // Parse the inline query and create a new URL builder object based on the query const urlBuilder = yiffBot.parseInlineQuery( ctx.inlineQuery.query, @@ -184,7 +224,7 @@ if (import.meta.main) { // Grab our data const request = await yiffBot.sendRequest(urlBuilder.buildUrl()); const requestJson = await request.json(); - const postsJson = requestJson.posts; // An array of 320 posts + const postsJson = requestJson.posts; // An array of 50 posts // console.log(postsJson[0]); @@ -207,7 +247,8 @@ if (import.meta.main) { }, ); - const inlineResults = yiffBot.processPosts(posts); + // Process and filter posts through blacklist + const inlineResults = yiffBot.processPosts(posts, user!.blacklist); console.log(`Number of Results Retrieved: ${inlineResults.length}`); let resultBatch; @@ -237,7 +278,8 @@ if (import.meta.main) { `E621Bot Error: ${err.message}:${err.ctx.chosenInlineResult}`, ); }); - yiffBot.start(); + await yiffBot.start(); + console.log("Bot Started!"); } catch (error) { console.error( `Encountered and error while trying to start the bot: ${error}`, diff --git a/models/user.ts b/models/user.ts new file mode 100644 index 0000000..8b728a6 --- /dev/null +++ b/models/user.ts @@ -0,0 +1,118 @@ +import { PathLike } from "node:fs"; +import { DatabaseSync } from "node:sqlite"; +import { User } from "../types/Blacklist.ts"; +import { defaultBlacklist } from "../constants/strings.ts"; + +/** + * Insert a new user + * @param user + * @param dbFile + * @returns StatementResultingChanges + */ +export function insertUser(user: User, dbFile: PathLike) { + try { + const db = new DatabaseSync(dbFile); + const queryResult = db.prepare( + `INSERT INTO user_db (telegram_id) VALUES (?);`, + ).run( + user.telegramId, + ); + db.close(); + return queryResult; + } catch (err) { + console.error(`Failed to insert user: ${err}`); + throw err; + } +} + +/** + * Update an existing user + * @param blacklist + * @param dbFile + * @returns StatementResultingChanges + */ +export function updateUser(user: User, dbFile: PathLike) { + try { + const db = new DatabaseSync(dbFile); + const queryResult = db.prepare( + `UPDATE OR FAIL user_db SET blacklist = ? WHERE telegram_id = ${user.telegramId};`, + ).run( + user.blacklist.join(","), + ); + db.close(); + return queryResult; + } catch (err) { + console.error(`Failed to update user: ${err}`); + throw err; + } +} + +/** + * Delete a user from the db + * @param telegramId Id attatched to blacklist + * @param dbFile Path to database file + * @returns StatementResultingChanges + */ +export function deleteUser(telegramId: number, dbFile: PathLike) { + try { + const db = new DatabaseSync(dbFile); + const queryResult = db.prepare( + `DELETE FROM user_db WHERE telegram_id = ${telegramId};`, + ).run(); + db.close(); + return queryResult; + } catch (err) { + console.error(`Failed to delete user data for ${telegramId}: ${err}`); + throw err; + } +} + +/** + * Grabs a user from the db by owner's telegram id + * @param telegramId + * @param dbFile + * @returns Blacklist + */ +export function getUserByTelegramId( + telegramId: number, + dbFile: PathLike, +): User | undefined { + try { + const db = new DatabaseSync(dbFile); + const queryResult = db.prepare( + `SELECT * FROM user_db WHERE telegram_id = ${telegramId};`, + ).get(); + db.close(); + + console.log(queryResult); + + if (queryResult) { + return { + id: Number(queryResult.id), + telegramId: Number(queryResult.telegram_id), + blacklist: String(queryResult.blacklist).split(",") || + defaultBlacklist.split(","), + }; + } + } catch (err) { + console.error( + `Failed to retrieve user ${telegramId}: ${err}`, + ); + throw err; + } +} + +export function userExists(telegramId: number, dbFile: PathLike) { + try { + const blacklist = getUserByTelegramId(telegramId, dbFile); + if (blacklist) { + return true; + } else { + return false; + } + } catch (err) { + console.error( + `Failed to qery the database for user: ${telegramId}: ${err}`, + ); + } +} diff --git a/tests/migration_tests.ts b/tests/migration_tests.ts new file mode 100644 index 0000000..dab9f54 --- /dev/null +++ b/tests/migration_tests.ts @@ -0,0 +1,25 @@ +import { assertEquals } from "@std/assert/equals"; +import { DatabaseSync } from "node:sqlite"; +import { createBlacklistDb } from "../db/migration.ts"; +import { assertNotEquals } from "@std/assert/not-equals"; +import { TEST_DB_FILE } from "../constants/strings.ts"; +import { existsSync } from "node:fs"; + +if (!existsSync("db/test_db")) { + Deno.mkdir("db/test_db", { recursive: true }); +} + +Deno.test(function testCreateBlacklistDb() { + createBlacklistDb(TEST_DB_FILE); + + // Get table + const db = new DatabaseSync(TEST_DB_FILE); + const table = db.prepare( + `SELECT name FROM sqlite_master WHERE type ='table' AND name = 'user_db';`, + ).get(); + db.close(); + + assertNotEquals(table, undefined); + assertEquals(table?.name, "user_db"); + Deno.removeSync(TEST_DB_FILE); +}); diff --git a/tests/misc_tests.ts b/tests/misc_tests.ts index 792e498..4524732 100644 --- a/tests/misc_tests.ts +++ b/tests/misc_tests.ts @@ -1,6 +1,6 @@ import { assertEquals } from "@std/assert/equals"; -import { E621Bot } from "../models/E621Bot.ts"; -import { E621UrlBuilderPools } from "../models/E621UrlBuilderPools.ts"; +import { E621Bot } from "../classes/E621Bot.ts"; +import { E621UrlBuilderPools } from "../classes/E621UrlBuilderPools.ts"; import * as urls from "../constants/urls.ts"; // TODO: Write test for processPosts() @@ -17,7 +17,6 @@ Deno.test(async function sendRequestTest() { const testBot = new E621Bot( Deno.env.get("TELEGRAM_BOT_KEY") || "", Deno.env.get("E621_API_KEY") || "", - new Array(), ); const testResponse = await testBot.sendRequest(testUrl); await testResponse.body?.cancel(); // Cancel test request @@ -28,7 +27,6 @@ Deno.test(function calcMegabytesTest() { const testBot = new E621Bot( Deno.env.get("TELEGRAM_BOT_KEY") || "", Deno.env.get("E621_API_KEY") || "", - new Array(), ); const testValue = 1024; // bytes assertEquals(Math.ceil(testBot.calcMegabytes(testValue)), 1); @@ -43,10 +41,9 @@ Deno.test(function buildBlacklistRegexTest() { const testBot = new E621Bot( Deno.env.get("TELEGRAM_BOT_KEY") || "", Deno.env.get("E621_API_KEY") || "", - blacklist, ); assertEquals( - testBot.buildBlacklistRegex(), + testBot.buildBlacklistRegex(blacklist), /(feces|murder|waterworks)/, ); }); diff --git a/tests/parse_inline_query_tests.ts b/tests/parse_inline_query_tests.ts index 1f2e15d..b8557f4 100644 --- a/tests/parse_inline_query_tests.ts +++ b/tests/parse_inline_query_tests.ts @@ -1,7 +1,7 @@ import { assertEquals } from "@std/assert/equals"; -import { E621Bot } from "../models/E621Bot.ts"; -import { E621UrlBuilderPools } from "../models/E621UrlBuilderPools.ts"; -import { E621UrlBuilderPosts } from "../models/E621UrlBuilderPosts.ts"; +import { E621Bot } from "../classes/E621Bot.ts"; +import { E621UrlBuilderPools } from "../classes/E621UrlBuilderPools.ts"; +import { E621UrlBuilderPosts } from "../classes/E621UrlBuilderPosts.ts"; import * as urls from "../constants/urls.ts"; import * as numbers from "../constants/numbers.ts"; @@ -12,7 +12,6 @@ Deno.test(function parseInlineQueryTest() { const testBot = new E621Bot( Deno.env.get("TELEGRAM_BOT_KEY") || "", Deno.env.get("E621_API_KEY") || "", - new Array(), ); // Create our test queries @@ -213,7 +212,6 @@ Deno.test(function parseInlineQueryPoolsTest() { const testBot = new E621Bot( Deno.env.get("TELEGRAM_BOT_KEY") || "", Deno.env.get("E621_API_KEY") || "", - new Array(), ); // Define test queries for each case diff --git a/tests/url_builder_tests.ts b/tests/url_builder_tests.ts index 336812b..d0bb695 100644 --- a/tests/url_builder_tests.ts +++ b/tests/url_builder_tests.ts @@ -1,6 +1,6 @@ import { assertEquals } from "@std/assert/equals"; import { API_PAGE_SIZE } from "../constants/numbers.ts"; -import { E621UrlBuilderPosts } from "../models/E621UrlBuilderPosts.ts"; +import { E621UrlBuilderPosts } from "../classes/E621UrlBuilderPosts.ts"; Deno.test(function buildUrlPostsTest() { const testUrl = diff --git a/tests/user_tests.ts b/tests/user_tests.ts new file mode 100644 index 0000000..a72d0ca --- /dev/null +++ b/tests/user_tests.ts @@ -0,0 +1,90 @@ +import { existsSync } from "node:fs"; +import { TEST_DB_FILE } from "../constants/strings.ts"; +import { createBlacklistDb } from "../db/migration.ts"; +import { + deleteUser, + getUserByTelegramId as getUserByTelegramId, + insertUser, + updateUser, + userExists, +} from "../models/user.ts"; +import { User } from "../types/Blacklist.ts"; +import { assertEquals } from "@std/assert/equals"; +import { assertObjectMatch } from "@std/assert/object-match"; + +const testBlacklist: User = { + id: 1, + telegramId: 12345, + blacklist: [ + "gore", + "scat", + "watersports", + "young", + "loli", + "shota", + ], // Default tags +}; + +if (!existsSync("db/test_db")) { + Deno.mkdir("db/test_db", { recursive: true }); +} + +Deno.test(function testInsertBlacklist() { + createBlacklistDb(TEST_DB_FILE); + + const queryResult = insertUser(testBlacklist, TEST_DB_FILE); + assertEquals(queryResult?.changes, 1); + assertEquals(queryResult?.lastInsertRowid, 1); + Deno.removeSync(TEST_DB_FILE); +}); + +Deno.test(function testGetUserByTelegramId() { + createBlacklistDb(TEST_DB_FILE); + insertUser(testBlacklist, TEST_DB_FILE); + + const user = getUserByTelegramId(12345, TEST_DB_FILE); + + console.log(user); + + assertObjectMatch(user!, testBlacklist); + Deno.removeSync(TEST_DB_FILE); +}); + +Deno.test(function testUpdateUser() { + createBlacklistDb(TEST_DB_FILE); + insertUser(testBlacklist, TEST_DB_FILE); + + const updatedBlacklist = testBlacklist; + + updatedBlacklist.blacklist.push("test", "tags"); + + const queryResult = updateUser(updatedBlacklist, TEST_DB_FILE); + + assertEquals(queryResult?.changes, 1); + assertEquals(queryResult?.lastInsertRowid, 0); + Deno.removeSync(TEST_DB_FILE); +}); + +Deno.test(function testDeleteUser() { + createBlacklistDb(TEST_DB_FILE); + insertUser(testBlacklist, TEST_DB_FILE); + + const queryResult = deleteUser(testBlacklist.telegramId, TEST_DB_FILE); + assertEquals(queryResult?.changes, 1); + assertEquals(queryResult?.lastInsertRowid, 0); + Deno.removeSync(TEST_DB_FILE); +}); + +Deno.test(function testUserExist() { + createBlacklistDb(TEST_DB_FILE); + insertUser(testBlacklist, TEST_DB_FILE); + + const result1 = userExists(12345, TEST_DB_FILE); + assertEquals(result1, true); + + deleteUser(12345, TEST_DB_FILE); + + const result2 = userExists(12345, TEST_DB_FILE); + assertEquals(result2, false); + Deno.removeSync(TEST_DB_FILE); +}); diff --git a/types/Blacklist.ts b/types/Blacklist.ts new file mode 100644 index 0000000..e529c33 --- /dev/null +++ b/types/Blacklist.ts @@ -0,0 +1,5 @@ +export type User = { + id?: number; + telegramId: number; + blacklist: string[]; +};