···
1
+
import { Database } from "bun:sqlite";
2
+
import type { Block, SlackAPIClient } from "slack-edge";
4
+
export interface SlackMessage {
10
+
status: "pending" | "sent" | "failed";
13
+
export default class SlackMessageQueue {
14
+
private db: Database;
15
+
private slack: SlackAPIClient;
16
+
private isProcessing = false;
17
+
private batchSize = 50;
19
+
constructor(slackClient: SlackAPIClient, dbPath = "slack-queue.db") {
20
+
this.slack = slackClient;
21
+
this.db = new Database(dbPath);
22
+
this.initDatabase();
25
+
private initDatabase() {
27
+
CREATE TABLE IF NOT EXISTS messages (
28
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
33
+
timestamp INTEGER NOT NULL,
34
+
status TEXT NOT NULL
37
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_status ON messages(status)");
40
+
async enqueue(message: SlackMessage): Promise<void> {
41
+
const stmt = this.db.prepare(`
42
+
INSERT INTO messages (userId, channelId, blocks, text, timestamp, status)
43
+
VALUES (?, ?, ?, ?, ?, ?)
47
+
message.userId ?? null,
48
+
message.channelId ?? null,
49
+
JSON.stringify(message.blocks) ?? null,
55
+
if (!this.isProcessing) {
56
+
this.processQueue();
60
+
private async processQueue() {
61
+
if (this.isProcessing) return;
62
+
this.isProcessing = true;
64
+
console.log("Processing queue");
68
+
const messages = this.db
71
+
SELECT * FROM messages
72
+
WHERE status = 'pending'
76
+
.all(this.batchSize) as (SlackMessage & { id: number })[];
78
+
console.log(messages);
79
+
if (messages.length === 0) break;
82
+
messages.map(async (message) => {
84
+
if (message.channelId) {
85
+
await this.slack.chat.postMessage({
86
+
channel: message.channelId,
88
+
JSON.parse(message.blocks as unknown as string) ??
94
+
} else if (message.userId) {
95
+
await this.slack.chat.postMessage({
96
+
channel: message.userId,
98
+
JSON.parse(message.blocks as unknown as string) ??
100
+
text: message.text,
104
+
console.log("Message sent successfully");
110
+
SET status = 'sent'
116
+
console.error("Error sending message", error);
121
+
SET status = 'failed'
131
+
this.isProcessing = false;
135
+
async cleanup(olderThan: number = 7 * 24 * 60 * 60 * 1000) {
136
+
const cutoff = Date.now() - olderThan;
140
+
DELETE FROM messages
141
+
WHERE timestamp < ? AND status != 'pending'
147
+
async queueLength() {
148
+
const result = this.db
151
+
SELECT COUNT(*) as count
153
+
WHERE status = 'pending'
157
+
return (result as { count: number }).count;