Skip to content
This repository has been archived by the owner on Dec 2, 2018. It is now read-only.

WIP: streamNotifications: complete rewrite #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/cogs/streamNotifications/db/BaseDBManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getDB } from "@utils/db";
import * as Knex from "knex";

export class BaseDBManager {
private constructor() {
throw new Error("This class is not initializable");
}

public static readonly DB = getDB();

public static async isTableExists(tableName: string) {
return BaseDBManager.DB.schema.hasTable(tableName);
}

public static async createTableIfNotExists(tableName: string, callback: (tableBuilder: Knex.TableBuilder) => void) {
if (BaseDBManager.isTableExists(tableName)) {
return;
}

return BaseDBManager.DB.schema.createTableIfNotExists(
tableName,
callback
);
}
}

export default BaseDBManager;
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { BaseController } from "../SubscriptionBasedController";
import { GuildSettingsDB } from "./GuildSettingsDB";
import { GuildSettingsData, fulfillmentCheck, MatureStreamBehavior } from "./GuildSettingsData";

export class GuildSettingsController extends BaseController<GuildSettingsDB, GuildSettingsData> {
private readonly _guildId: string;

constructor(guildId: string, parent: GuildSettingsDB) {
super(parent);

this._guildId = guildId;

// tslint:disable-next-line:prefer-object-spread
this._data = Object.assign(
this._data, {
guildId
}
);
}

public async fetch() {
const currentData = this._data;

const availableData = await this._getData();

if (!availableData) {
this._markCreated(false);

return false;
}

this._data = {
...currentData,
...availableData
};

this._markCreated(true);

return true;
}

public async post() {
const data = this._data;

if (!fulfillmentCheck(data)) {
return false;
}

const currentData = await this._getData();

if (currentData) {
await this._parent.updateSettings(data);
} else {
await this._parent.createSettings(data);

this._markCreated(true);
}

return true;
}

public fulfillmentCheck() {
return fulfillmentCheck(
this._data
);
}

protected async _getData() {
return this._parent.getSettings(
this._guildId
);
}

public resolveGuild() {
const { guildId } = this._data;

if (!guildId) {
return undefined;
}

return $discordBot.guilds.get(
guildId
);
}

public getGuildId() {
return this._data.guildId!;
}

public getMatureBehavior() {
return this._data.matureBehavior;
}

public setMatureBehavior(behavior?: MatureStreamBehavior) {
this._data.matureBehavior = behavior;

return this;
}

public resolveDefaultChannel() {
const { defaultChannelId } = this._data;

if (!defaultChannelId) {
return undefined;
}

const guild = this.resolveGuild();

if (!guild) {
return undefined;
}

return guild.channels.get(defaultChannelId);
}

public getDefaultChannelId() {
return this._data.defaultChannelId;
}

public setDefaultChannelId(id: string) {
if (!id) {
throw new Error("ID cannot be set to null");
}

this._data.defaultChannelId = id;

return this;
}
}
87 changes: 87 additions & 0 deletions src/cogs/streamNotifications/db/GuildSettings/GuildSettingsDB.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as db from "@utils/db";
import BaseDBManager from "../BaseDBManager";
import { addGuildSettingsColumns, GuildSettingsData } from "./GuildSettingsData";

const INIT_MAP = new WeakMap<GuildSettingsDB, boolean>();

export class GuildSettingsDB {
private readonly _tableName: string;
private readonly _db = db.getDB();

constructor(tableName: string) {
if (!tableName) {
throw new Error("No table name specified");
}

this._tableName = tableName;
}

/**
* Initializes and checks the database
*/
public async init() {
await BaseDBManager.createTableIfNotExists(
this._tableName,
(tb) => {
addGuildSettingsColumns(tb);
}
);

INIT_MAP.set(this, true);
}

public async createSettings(data: GuildSettingsData) {
GuildSettingsDB._checkInitDone(this);

return this._db(this._tableName)
.insert(data);
}

public async getSettings(guildId: string) : OptionalSettings {
GuildSettingsDB._checkInitDone(this);

return this._db(this._tableName)
.where({ guildId })
.first();
}

public async updateSettings(data: GuildSettingsData) {
GuildSettingsDB._checkInitDone(this);

return this._db(this._tableName)
.where(
GuildSettingsDB._getSelect(
data
)
)
.update(data);
}

public async deleteSettings(data: GuildSettingsData) {
GuildSettingsDB._checkInitDone(this);

return this._db(this._tableName)
.where(
GuildSettingsDB._getSelect(
data
)
)
.delete();
}

private static _getSelect(data: GuildSettingsData) {
const { guildId } = data;

return { guildId };
}

private static _checkInitDone(instance: GuildSettingsDB) {
if (!INIT_MAP.has(instance)) {
throw new Error("DB controller must be initlized first");
}

return true;
}
}

type OptionalSettings = Promise<GuildSettingsData | undefined>;
66 changes: 66 additions & 0 deletions src/cogs/streamNotifications/db/GuildSettings/GuildSettingsData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { TableBuilder } from "knex";
import { MissingPropertyError, ValueError } from "../SubscriptionBasedController";
import { canBeSnowflake } from "@utils/text";

export type MatureStreamBehavior = "Nothing" | "Ignore" | "Banner";

const MATURE_BEHAVIOR_VALUES: MatureStreamBehavior[] = [
"Nothing",
"Ignore",
"Banner"
];

export type GuildSettingsData = {
/**
* Where notifications will be sent
*/
readonly guildId: string;
/**
* Behavior when sending notification about the mature stream
*/
matureBehavior?: MatureStreamBehavior;
/**
* Discord ID of the default channel for the notifications
*/
defaultChannelId: string;
};

export function addGuildSettingsColumns(tableBuilder: TableBuilder) {
tableBuilder.string("guildId").notNullable();
tableBuilder
.enum(
"matureBehavior",
["Nothing", "Banner", "Ignore"]
)
.nullable();
}

export function fulfillmentCheck(data: Partial<GuildSettingsData>): data is GuildSettingsData {
type D = GuildSettingsData;

const { guildId } = data;

if (!guildId) {
throw new MissingPropertyError<D>("guildId");
}

if (!canBeSnowflake(guildId)) {
throw new ValueError<D>(
"guildId",
"must be a valid guild ID"
);
}

const { matureBehavior } = data;

if (matureBehavior != null) {
if (!MATURE_BEHAVIOR_VALUES.includes(matureBehavior)) {
throw new ValueError<D>(
"matureBehavior",
`"${matureBehavior}" is not valid value, valid values are ${MATURE_BEHAVIOR_VALUES.join("/")}`
);
}
}

return true;
}
Loading