MusicGen / db.js
000x's picture
Upload 16 files
3760017 verified
Raw History Blame Contribute Delete
4.39 kB
'use strict';
// Single database: PostgreSQL (DATABASE_URL). Holds the file list AND the
// WhatsApp session, so the app needs no local data folder.
const { Pool } = require('pg');
const { BufferJSON, initAuthCreds, proto } = require('@whiskeysockets/baileys');
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not set. Add your PostgreSQL URL to .env (or Railway variables).');
}
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
// Set DATABASE_SSL=true for hosted Postgres that requires SSL (Neon, Supabase, Railway public URL, etc.)
ssl: process.env.DATABASE_SSL === 'true' ? { rejectUnauthorized: false } : undefined,
max: 5,
});
pool.on('error', (e) => console.error('Postgres pool error:', e.message));
async function init() {
await pool.query(`
CREATE TABLE IF NOT EXISTS bot_files (
code TEXT NOT NULL,
title TEXT NOT NULL,
url TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'document',
sent INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS bot_files_code_lower ON bot_files (lower(code));
CREATE TABLE IF NOT EXISTS bot_wa_auth (
id TEXT PRIMARY KEY,
data TEXT NOT NULL
);
`);
}
// ---------------------------------------------------------------------------
// File list
// ---------------------------------------------------------------------------
const toItem = (r) => ({
code: r.code,
title: r.title,
url: r.url,
type: r.type,
sent: r.sent,
createdAt: r.created_at.toISOString(),
});
async function listFiles() {
const { rows } = await pool.query('SELECT * FROM bot_files ORDER BY created_at, code');
return rows.map(toItem);
}
async function findFile(code) {
const { rows } = await pool.query('SELECT * FROM bot_files WHERE lower(code) = lower($1)', [code]);
return rows[0] ? toItem(rows[0]) : null;
}
// Throws with code 23505 if the code is already used (unique index).
async function addFile({ code, title, url, type }) {
const { rows } = await pool.query(
'INSERT INTO bot_files (code, title, url, type) VALUES ($1, $2, $3, $4) RETURNING *',
[code, title, url, type]
);
return toItem(rows[0]);
}
async function deleteFile(code) {
const { rowCount } = await pool.query('DELETE FROM bot_files WHERE lower(code) = lower($1)', [code]);
return rowCount;
}
async function markSent(code) {
await pool.query('UPDATE bot_files SET sent = sent + 1 WHERE lower(code) = lower($1)', [code]);
}
// ---------------------------------------------------------------------------
// WhatsApp session (Baileys auth state stored in bot_wa_auth)
// ---------------------------------------------------------------------------
async function useSqlAuthState() {
const read = async (id) => {
const { rows } = await pool.query('SELECT data FROM bot_wa_auth WHERE id = $1', [id]);
return rows[0] ? JSON.parse(rows[0].data, BufferJSON.reviver) : null;
};
const write = (id, value) => pool.query(
'INSERT INTO bot_wa_auth (id, data) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET data = EXCLUDED.data',
[id, JSON.stringify(value, BufferJSON.replacer)]
);
const remove = (id) => pool.query('DELETE FROM bot_wa_auth WHERE id = $1', [id]);
const creds = (await read('creds')) || initAuthCreds();
const keys = {
get: async (type, ids) => {
const out = {};
await Promise.all(ids.map(async (id) => {
let value = await read(`${type}-${id}`);
if (type === 'app-state-sync-key' && value) {
value = proto.Message.AppStateSyncKeyData.fromObject(value);
}
out[id] = value;
}));
return out;
},
set: async (data) => {
const jobs = [];
for (const category of Object.keys(data)) {
for (const id of Object.keys(data[category])) {
const value = data[category][id];
const name = `${category}-${id}`;
jobs.push(value ? write(name, value) : remove(name));
}
}
await Promise.all(jobs);
},
};
return {
state: { creds, keys },
saveCreds: () => write('creds', creds),
};
}
async function clearAuth() {
await pool.query('DELETE FROM bot_wa_auth');
}
module.exports = {
pool, init,
listFiles, findFile, addFile, deleteFile, markSent,
useSqlAuthState, clearAuth,
};