Source: index.js

const db = require('quick.db');

class Auction {
	/**
	 * @constructor
	 * @param {Object} options
	 * @param {string} options.channelId - The ID of the auction channel.
	 * @param {string} options.guildId - The ID of the auction guild.
	 * @param {string} options.item - The Item/Prize of the auction.
	 * @param {number} options.price - The Price of the auction.
	 * @param {string} [options.hostedBy] - The Hoster/Manager of the auction.
	 * @param {number} [options.startedAt] - The date the auction is started at.
	 * @param {string} [options.winner] - The winner of the auction.
	 * @param {number} [options.bidLimit] - The bid limit of the auction.
	 * @param {AuctionManager} manager - The Manager of the auction.
	 */
	constructor(options, manager) {
		const { channelId, guildId, item, price, hostedBy, startedAt, winner, bidLimit } = options;
		if (!(typeof channelId == 'string')) throw 'ChannelID is not instance of String!';
		/**
		 * The ChannelID of the auction.
		 * @member {string}
		 */
		this.channelId = channelId;

		if (!(typeof guildId == 'string')) throw 'GuildID is not instance of String!';
		/**
		 * The GuildID of the auction.
		 * @member {string}
		 */
		this.guildId = guildId;

		if (!(typeof item == 'string')) throw 'Item is not instance of String!';
		/**
		 * The item of the auction.
		 * @member {string}
		 */
		this.item = item;

		if (!(typeof price == 'number')) throw 'Price is not instance of Number!';
		if (price < 0) throw 'Price is not instance of String!';
		/**
		 * The Price of the auction.
		 * @member
		 */
		this.price = price;

		if (hostedBy != undefined) {
			if (!(typeof hostedBy == 'string')) throw 'HostedBy is not instance of String!';
			/**
			 * The ID of the host/manager of the auction.
			 * @member {string}
			 */
			this.hostedBy = hostedBy;
		}

		/**
		 * The Date that the auction is started at.
		 * @member {number}
		 */
		this.startedAt = startedAt == undefined ? Date.now() : startedAt;

		/**
		 * The Manager the auction belongs to.
		 * @member {AuctionManager}
		 */
		this.manager = manager;

		/**
		 * The WinnerID of the auction, if there's no winner this value will be set to blank string.
		 * @member {string}
		 */
		this.winner = winner == undefined ? '' : winner;

		/**
		 * The Bid Limit of the auction.
		 * @member {number}
		 */
		this.bidLimit = 0;
	}

	/**
	 * Saves edits on the auction.
	 */
	async save() {
		await this.manager.editAuction(this.guildId, this.channelId, this);
	}

	/**
	 * Deletes the auction.
	 */
	async delete() {
		await this.manager.deleteAuction(this.guildId, this.channelId);
	}

	/**
	 * @param {number} price - The new bid on the auction
	 * @param {string} user - The Person who bid on the auction
	 */
	async bid(price, user) {
		if (this.price >= price) throw 'Bid amount is lower than current bid!';
		if (this.price + this.bidLimit >= price) throw 'Bid amount does not pass the bid limit!';
		this.price = price;
		this.winner = user;
	}

	/**
	 * Sets a new winner to the auction.
	 * @param {string} user - The new winner to the auction.
	 */
	async setWinner(user) {
		this.winner = user;
	}

	/**
	 * Sets a new price to the auction.
	 * @param {number} price - The new price of the auction.
	 */
	async setPrice(price) {
		this.price = price;
	}

	/**
	 * Sets a new bid limit to the auction.
	 * @param {number} bidLimit - The new bid limit of the auction.
	 */
	async setBidLimit(bidLimit) {
		this.bidLimit = bidLimit;
	}

	/**
	 * Sets a new item to the auction.
	 * @param {string} item - The new item of the auction.
	 */
	async setItem(item) {
		this.item = item;
	}

	/**
	 * @returns {number} - How much the auction lasted in milliseconds
	 */
	async auctionLength() {
		return Date.now() - this.startedAt;
	}
}

class AuctionManager {
	/**
	 * @constructor
	 * @param {Object} managerOptions - The options of the manager
	 */
	constructor() {
		this.db = new db.table('auctions');
	}

	/**
	 * @method
	 * @private
	 * @param {Auction} auction - The Auction to set in the database.
	 * @returns {Promise<Auction>}
	 */
	async createAuction(auction) {
		this.db.set(`${auction.guildId}/${auction.channelId}`, auction);
		return this.promise(auction);
	}

	/**
	 * @method
	 * @private
	 * @param {string} guildId - The Guild ID of the auction to fetch.
	 * @param {string} channelId - The Channel ID of the auction to fetch.
	 * @returns {Promise<Auction>}
	 */
	async getAuction(guildId, channelId) {
		return this.promise(this.db.get(`${guildId}/${channelId}`));
	}

	/**
	 * @method
	 * @private
	 * @param {string} guildId - The Guild ID of the auction to delete.
	 * @param {string} channelId - The Channel ID of the auction to delete.
	 * @returns {Promise<undefined>} 
	 */
	async deleteAuction(guildId, channelId) {
		this.db.delete(`${guildId}/${channelId}`);
		return this.promise(undefined);
	}

	/**
	 * @private
	 * @returns {Promise<string[]>}
	 */
	async getAllAuctions() {
		return this.promise(this.db.all().map((vrbl) => vrbl.data));
	}

	/**
	 * @method
	 * @private
	 * @param {string} guildId - The Guild ID of the auction to edit.
	 * @param {string} channelId - The Channel ID of the auction to edit.
	 * @param {Auction} updatedAuction - The auction to replace the current auction with.
	 * @returns {Promise<Auction>}
	 */
	async editAuction(guildId, channelId, updatedAuction) {
		this.db.set(`${guildId}/${channelId}`, updatedAuction);
		return this.promise(updatedAuction);
	}

	/**
	 * @param {Object} auctionStartOptions
	 * @param {string} auctionStartOptions.item - The item to auction.
	 * @param {string} auctionStartOptions.price - The starting price of the auction.
	 * @param {string} auctionStartOptions.channelId - The channel id to start the auction in.
	 * @param {string} auctionStartOptions.guildId - The guild id to start the auction in.
	 * @param {string} [auctionStartOptions.hostedBy] - The user who started the auction
	 * @returns {Promise<Auction>} - The Auction that was started
	 */
	async start(auctionStartOptions) {
		const { item, price, channelId, guildId, hostedBy } = auctionStartOptions;
		const aucToStart = new Auction({ item, price, hostedBy, guildId, channelId }, this);
		await this.createAuction(aucToStart);
		return this.promise(aucToStart);
	}

	/**
	 * @param {string} guildId - The guild ID of the auction.
	 * @param {string} channelId - The channel ID of the auction.
	 * @returns {Promise<Auction>}
	 */
	async fetch(guildId, channelId) {
		if (!await this.exists(guildId, channelId)) return undefined;
		const fetched = await this.getAuction(guildId, channelId);
		return this.promise(
			new Auction(
				{
					channelId,
					guildId,
					item: fetched.item,
					hostedBy: fetched.hostedBy,
					price: fetched.price,
					bidLimit: fetched.bidLimit,
					startedAt: fetched.startedAt,
					winner: fetched.winner
				},
				this
			)
		);
	}

	/**
	 * Fetches all auctions
	 * @returns {Promise<Auction[]>}
	 */
	async fetchAll() {
		let aucs = await this.getAllAuctions();
		aucs = aucs.map((val) => JSON.parse(val));
		aucs.map(
			(auc) =>
				new Auction(
					{
						channelId: auc,
						guildId,
						item: fetched.item,
						hostedBy: fetched.hostedBy,
						price: fetched.price,
						bidLimit: fetched.bidLimit,
						startedAt: fetched.startedAt,
						winner: fetched.winner
					},
					this
				)
		);

		return this.promise(aucs);
	}

	/**
	 * 
	 * @param {string} guildId 
	 * @param {string} channelId  
	 * @returns {boolean}
	 * @private
	 */
	async hasAuction(guildId, channelId) {
		return this.promise(this.db.has(`${guildId}/${channelId}`));
	}

	/**
	 * 
	 * @param {string} guildId - The guild ID of the auction
	 * @param {string} channelId - The channel ID of the auction
	 * @returns {Promise<boolean>}
	 */
	async exists(guildId, channelId) {
		return this.promise(this.hasAuction(guildId, channelId));
	}

	/**	
	 * @method
	 * @private
	 */
	promise(data) {
		return new Promise((resolve) => {
			resolve(data);
		});
	}
}

module.exports = {
	AuctionManager
};