fix: make it work with new vite
This commit is contained in:
@@ -43,7 +43,7 @@ export const performanceTable = sqliteTable("performance", {
|
||||
|
||||
export const imageTable = sqliteTable("image", {
|
||||
createdAt: integer("created_at", { mode: "timestamp" }).default(
|
||||
sql`(current_timestamp)`,
|
||||
sql`(unixepoch())`,
|
||||
),
|
||||
url: text().notNull(),
|
||||
average: text().notNull(),
|
||||
|
||||
@@ -4,7 +4,6 @@ import path from "node:path";
|
||||
|
||||
const DB_FILE = "file:" + path.resolve(DATA_DIR, "db.sqlite");
|
||||
|
||||
// You can specify any property from the libsql connection options
|
||||
export const db = drizzle({
|
||||
connection: {
|
||||
url: DB_FILE,
|
||||
|
||||
@@ -36,9 +36,10 @@ export async function getLogs() {
|
||||
.map((line) => {
|
||||
const [date, ...rest] = line.split(" | ");
|
||||
const parsed = JSON.parse(rest.join(" | ")) as Log;
|
||||
const dateObj = new Date(date);
|
||||
return {
|
||||
...parsed,
|
||||
date: new Date(date),
|
||||
date: isNaN(dateObj.getTime()) ? new Date() : dateObj,
|
||||
} as Log;
|
||||
});
|
||||
|
||||
|
||||
105
lib/promise.ts
105
lib/promise.ts
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* Interface zur Beschreibung eines eingereihten Promises in der `PromiseQueue`.
|
||||
*/
|
||||
interface QueuedPromise<T = unknown> {
|
||||
promise: () => Promise<T>;
|
||||
resolve: (value: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: unknown) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine einfache Promise Queue, die es ermöglicht mehrere Aufgaben in kontrollierter
|
||||
* Reihenfolge abzuarbeiten.
|
||||
*
|
||||
* Lizenz: CC BY-NC-SA 4.0
|
||||
* (c) Peter Müller <peter@crycode.de> (https://crycode.de/promise-queue-in-typescript)
|
||||
*/
|
||||
export class PromiseQueue {
|
||||
/**
|
||||
* Eingereihte Promises.
|
||||
*/
|
||||
private queue: QueuedPromise<unknown>[] = [];
|
||||
|
||||
/**
|
||||
* Indikator, dass aktuell ein Promise abgearbeitet wird.
|
||||
*/
|
||||
private working = false;
|
||||
|
||||
/**
|
||||
* Ein Promise einreihen.
|
||||
* Dies fügt das Promise der Warteschlange hinzu. Wenn die Warteschlange leer
|
||||
* ist, dann wird das Promise sofort gestartet.
|
||||
* @param promise Funktion, die das Promise zurückgibt.
|
||||
* @returns Ein Promise, welches eingelöst (oder zurückgewiesen) wird sobald das eingereihte Promise abgearbeitet ist.
|
||||
*/
|
||||
public enqueue<T = void>(promise: () => Promise<T>): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.queue.push({
|
||||
promise,
|
||||
resolve: resolve as (value: unknown) => void,
|
||||
reject,
|
||||
});
|
||||
this.dequeue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Das erste Promise aus der Warteschlange holen und starten, sofern nicht
|
||||
* bereits ein Promise aktiv ist.
|
||||
* @returns `true` wenn ein Promise aus der Warteschlange gestartet wurde oder `false` wenn bereits ein Promise aktiv oder die Warteschlange leer ist.
|
||||
*/
|
||||
private dequeue(): boolean {
|
||||
if (this.working) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const item = this.queue.shift();
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.working = true;
|
||||
item.promise()
|
||||
.then((value) => {
|
||||
item.resolve(value);
|
||||
})
|
||||
.catch((err) => {
|
||||
item.reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
this.working = false;
|
||||
this.dequeue();
|
||||
});
|
||||
} catch (err) {
|
||||
item.reject(err);
|
||||
this.working = false;
|
||||
this.dequeue();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConcurrentPromiseQueue {
|
||||
/**
|
||||
* Eingereihte Promises.
|
||||
*/
|
||||
private queues: PromiseQueue[] = [];
|
||||
|
||||
constructor(concurrency: number = 1) {
|
||||
this.queues = Array.from({ length: concurrency }).map(() => {
|
||||
return new PromiseQueue();
|
||||
});
|
||||
}
|
||||
|
||||
private queueIndex = 0;
|
||||
private getQueue() {
|
||||
this.queueIndex = (this.queueIndex + 1) % this.queues.length;
|
||||
return this.queues[this.queueIndex];
|
||||
}
|
||||
|
||||
public enqueue<T = void>(promise: () => Promise<T>): Promise<T> {
|
||||
return this.getQueue().enqueue(promise);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,9 @@ export async function createRecommendationResource(
|
||||
const d = typeof datePublished === "string"
|
||||
? new Date(datePublished)
|
||||
: datePublished;
|
||||
resource.year = d.getFullYear();
|
||||
if (!isNaN(d.getTime())) {
|
||||
resource.year = d.getFullYear();
|
||||
}
|
||||
}
|
||||
|
||||
cache.set(cacheId, JSON.stringify(resource));
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { transcribe } from "@lib/openai.ts";
|
||||
import { createResource } from "@lib/marka/index.ts";
|
||||
import { createLogger } from "./log/index.ts";
|
||||
import { convertOggToMp3 } from "./helpers.ts";
|
||||
|
||||
const log = createLogger("taskManager");
|
||||
|
||||
// In-memory task state
|
||||
const activeTasks: Record<
|
||||
string,
|
||||
{
|
||||
noteName: string;
|
||||
entries: Array<
|
||||
{ type: string; content: string | ArrayBufferLike; fileName?: string }
|
||||
>;
|
||||
}
|
||||
> = {};
|
||||
|
||||
export function startTask(chatId: string, noteName: string) {
|
||||
activeTasks[chatId] = { noteName, entries: [] };
|
||||
log.info(`Started note: ${noteName}`);
|
||||
}
|
||||
|
||||
export async function endTask(chatId: string): Promise<string | null> {
|
||||
const task = activeTasks[chatId];
|
||||
if (!task) return null;
|
||||
|
||||
log.info("Ending note", task.noteName);
|
||||
|
||||
let finalNote = `# ${task.noteName}\n\n`;
|
||||
|
||||
const photoTasks: { content: ArrayBuffer; path: string }[] = [];
|
||||
|
||||
let photoIndex = 0;
|
||||
for (const entry of task.entries) {
|
||||
if (entry.type === "text") {
|
||||
finalNote += entry.content + "\n\n";
|
||||
} else if (entry.type === "voice") {
|
||||
try {
|
||||
log.info("Converting OGG to MP3");
|
||||
const mp3Data = await convertOggToMp3(entry.content as ArrayBuffer);
|
||||
log.info("Finished converting OGG to MP3, transcribing...");
|
||||
const transcript = await transcribe(mp3Data);
|
||||
finalNote += `**Voice Transcript:**\n${transcript}\n\n`;
|
||||
log.info("Finished transcribing");
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
finalNote += "**[Voice message could not be transcribed]**\n\n";
|
||||
}
|
||||
} else if (entry.type === "photo") {
|
||||
const photoUrl = `${
|
||||
task.noteName.replace(/\.md$/, "")
|
||||
}/photo-${photoIndex++}.jpg`;
|
||||
|
||||
finalNote += `**Photo**:\n ${photoUrl}\n\n`;
|
||||
photoTasks.push({
|
||||
content: entry.content as ArrayBuffer,
|
||||
path: photoUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (const entry of photoTasks) {
|
||||
await createResource(entry.path, entry.content);
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("Error creating photo document:", err);
|
||||
}
|
||||
try {
|
||||
await createResource(task.noteName, finalNote);
|
||||
} catch (error) {
|
||||
log.error("Error creating document:", error);
|
||||
return error instanceof Error
|
||||
? error.toString()
|
||||
: "Error creating document";
|
||||
}
|
||||
|
||||
delete activeTasks[chatId];
|
||||
log.debug({ finalNote });
|
||||
return finalNote;
|
||||
}
|
||||
|
||||
export function addTextEntry(chatId: string, text: string) {
|
||||
const task = activeTasks[chatId];
|
||||
if (!task) return;
|
||||
const entry = { type: "text", content: text };
|
||||
log.debug("New Entry", entry);
|
||||
task.entries.push(entry);
|
||||
}
|
||||
|
||||
export function addVoiceEntry(chatId: string, buffer: ArrayBufferLike) {
|
||||
const task = activeTasks[chatId];
|
||||
if (!task) return;
|
||||
const entry = { type: "voice", content: buffer };
|
||||
log.debug("New Entry", entry);
|
||||
task.entries.push(entry);
|
||||
}
|
||||
|
||||
export function addPhotoEntry(
|
||||
chatId: string,
|
||||
buffer: ArrayBufferLike,
|
||||
fileName: string,
|
||||
) {
|
||||
const task = activeTasks[chatId];
|
||||
if (!task) return;
|
||||
const entry = { type: "photo", content: buffer, fileName };
|
||||
log.debug("New Entry", entry);
|
||||
task.entries.push(entry);
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Bot } from "grammy";
|
||||
import { TELEGRAM_API_KEY } from "@lib/env.ts";
|
||||
import { createLogger } from "./log/index.ts";
|
||||
|
||||
const bot = new Bot(TELEGRAM_API_KEY);
|
||||
const log = createLogger("telegram");
|
||||
|
||||
import * as manager from "./taskManager.ts";
|
||||
|
||||
async function downloadFile(filePath: string): Promise<Uint8Array> {
|
||||
log.info(`Downloading file from path: ${filePath}`);
|
||||
const url = `https://api.telegram.org/file/bot${bot.token}/${filePath}`;
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to download file: " + response.statusText);
|
||||
}
|
||||
log.info("File downloaded successfully");
|
||||
const buffer = await response.arrayBuffer();
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
bot.command("start", async (ctx) => {
|
||||
log.info("Received /start command");
|
||||
const [_, noteName] = ctx.message?.text?.split(" ") || [];
|
||||
if (!noteName) {
|
||||
return ctx.reply("Please provide a note name. Usage: /start NoteName");
|
||||
}
|
||||
manager.startTask(ctx.chat.id.toString(), noteName);
|
||||
await ctx.reply(`Started note: ${noteName}`);
|
||||
});
|
||||
|
||||
bot.command("end", async (ctx) => {
|
||||
log.info("Received /end command");
|
||||
const finalNote = await manager.endTask(ctx.chat.id.toString());
|
||||
if (!finalNote) return ctx.reply("No active note found.");
|
||||
|
||||
try {
|
||||
await ctx.reply("Note complete. Here is your markdown:");
|
||||
await ctx.reply(finalNote, { parse_mode: "MarkdownV2" });
|
||||
} catch (err) {
|
||||
console.error("Error sending final note:", err);
|
||||
}
|
||||
});
|
||||
|
||||
bot.on("message:text", (ctx) => {
|
||||
log.info("Received text message");
|
||||
manager.addTextEntry(ctx.chat.id.toString(), ctx.message.text);
|
||||
});
|
||||
|
||||
bot.on("message:voice", async (ctx) => {
|
||||
log.info("Received photo message");
|
||||
log.info("Received voice message");
|
||||
const file = await ctx.getFile();
|
||||
const buffer = await downloadFile(file.file_path!);
|
||||
manager.addVoiceEntry(ctx.chat.id.toString(), buffer.buffer);
|
||||
});
|
||||
|
||||
bot.on("message:photo", async (ctx) => {
|
||||
const file = await ctx.getFile();
|
||||
const buffer = await downloadFile(file.file_path!);
|
||||
const fileName = file.file_path!.split("/").pop()!;
|
||||
manager.addPhotoEntry(ctx.chat.id.toString(), buffer.buffer, fileName);
|
||||
});
|
||||
|
||||
bot.start();
|
||||
Reference in New Issue
Block a user