better-giveaways
Version:
A modern, feature-rich Discord giveaway manager with TypeScript support, flexible storage adapters, and comprehensive event system
188 lines (187 loc) • 6.56 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONAdapter = void 0;
const fs_1 = __importDefault(require("fs"));
/**
* File-based storage adapter using JSON format.
*
* This adapter provides persistent storage for giveaway data using a local JSON file.
* It's ideal for simple deployments, development environments, or small bots where
* database setup might be overkill.
*
* Features:
* - Automatic file creation if it doesn't exist
* - In-memory caching for better performance
* - Atomic write operations to prevent data corruption
* - Human-readable JSON format for easy debugging
*
* @example
* ```typescript
* import { JSONAdapter } from 'better-giveaways';
*
* // Use default file location (./giveaways.json)
* const adapter = new JSONAdapter();
*
* // Or specify a custom file path
* const adapter = new JSONAdapter('./data/my-giveaways.json');
*
* // Use with GiveawayManager
* const giveawayManager = new GiveawayManager(client, adapter, options);
* ```
*/
class JSONAdapter {
/**
* Creates a new JSONAdapter instance.
*
* @param filePath - The path to the JSON file for storing giveaway data.
* Defaults to './giveaways.json' in the current working directory.
* The file will be created automatically if it doesn't exist.
*
* @example
* ```typescript
* // Use default location
* const adapter = new JSONAdapter();
*
* // Use custom location
* const adapter = new JSONAdapter('./data/giveaways.json');
*
* // Use absolute path
* const adapter = new JSONAdapter('/var/lib/bot/giveaways.json');
* ```
*/
constructor(filePath = "./giveaways.json") {
this.cache = [];
this.filePath = filePath;
}
/**
* Loads giveaway data from the JSON file into memory cache.
*
* This method handles file creation if the file doesn't exist and provides
* error handling for corrupted or inaccessible files.
*
* @private
* @throws {Error} If the file exists but cannot be read or parsed
*/
load() {
return __awaiter(this, void 0, void 0, function* () {
try {
const data = yield fs_1.default.readFileSync(this.filePath, "utf-8");
this.cache = JSON.parse(data);
}
catch (err) {
if (err.code === "ENOENT") {
this.cache = [];
yield this.saveFile();
}
else {
throw err;
}
}
});
}
/**
* Writes the current cache to the JSON file.
*
* The data is written with pretty formatting (2-space indentation) for
* better readability when inspecting the file manually.
*
* @private
* @throws {Error} If the file cannot be written (permissions, disk space, etc.)
*/
saveFile() {
return __awaiter(this, void 0, void 0, function* () {
yield fs_1.default.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2));
});
}
/**
* Saves or updates giveaway data.
*
* If a giveaway with the same ID already exists, it will be updated.
* Otherwise, a new giveaway will be added to the storage.
*
* @param data - The giveaway data to save
* @throws {Error} If the file cannot be written
*/
save(data) {
return __awaiter(this, void 0, void 0, function* () {
yield this.load();
const index = this.cache.findIndex((g) => g.giveawayId === data.giveawayId);
if (index !== -1) {
this.cache[index] = data;
}
else {
this.cache.push(data);
}
yield this.saveFile();
});
}
/**
* Retrieves a specific giveaway by ID.
*
* @param id - The unique giveaway ID to retrieve
* @returns The giveaway data or null if not found
* @throws {Error} If the file cannot be read
*/
get(id) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
yield this.load();
return (_a = this.cache.find((g) => g.giveawayId === id)) !== null && _a !== void 0 ? _a : null;
});
}
/**
* Deletes a giveaway from storage.
*
* @param id - The unique giveaway ID to delete
* @throws {Error} If the file cannot be written
*/
delete(id) {
return __awaiter(this, void 0, void 0, function* () {
yield this.load();
this.cache = this.cache.filter((g) => g.giveawayId !== id);
yield this.saveFile();
});
}
/**
* Retrieves all giveaways from storage.
*
* @returns A copy of all giveaway data (to prevent external modification of cache)
* @throws {Error} If the file cannot be read
*/
getAll() {
return __awaiter(this, void 0, void 0, function* () {
yield this.load();
return [...this.cache];
});
}
/**
* Updates an existing giveaway with new data.
*
* This implementation deletes the old giveaway and saves the new data,
* ensuring the giveaway ID can be changed if needed.
*
* @param id - The current giveaway ID to update
* @param data - The new giveaway data
* @throws {Error} If the file cannot be written
*/
edit(id, data) {
return __awaiter(this, void 0, void 0, function* () {
yield this.load();
yield this.delete(id);
yield this.save(data);
});
}
}
exports.JSONAdapter = JSONAdapter;