Skip to content

Commit

Permalink
feat: [fixes #5] collection based quote storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Lutonite committed Mar 17, 2022
1 parent f967e64 commit 60e2e69
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 79 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"no-plusplus": "off",
"no-restricted-imports": ["error", {
"paths": [
"firebase-admin"
"firebase-admin",
"firebase-admin/lib/*"
]
}],
"no-restricted-syntax": "off",
Expand Down
195 changes: 119 additions & 76 deletions src/commands/rentsch.command.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import type { GuildDocument } from '#src/database/guild-document';
import type { GuildQuote } from '#src/database/guild-quote';
import { errorEmbed, successEmbed } from '#src/utils/embed-utils';
import { getGuildCollection, getGuildData } from '#src/utils/firestore-utils';
import { converter, getGuildCollection } from '#src/utils/firestore-utils';
import { ApplyOptions } from '@sapphire/decorators';
import {
fetch,
FetchMediaContentTypes,
FetchResultTypes,
} from '@sapphire/fetch';
import type { Args } from '@sapphire/framework';
import { send } from '@sapphire/plugin-editable-commands';
import { SubCommandPluginCommand } from '@sapphire/plugin-subcommands';
import { type Message, MessageEmbed } from 'discord.js';
import { Guild, type Message, MessageEmbed } from 'discord.js';
import {
type CollectionReference,
FieldPath,
type Query,
} from 'firebase-admin/firestore';

@ApplyOptions<SubCommandPluginCommand.Options>({
name: 'rentsch',
Expand All @@ -19,7 +25,7 @@ import { type Message, MessageEmbed } from 'discord.js';
{ input: 'get', default: true },
'add',
'edit',
'remove',
'delete',
'help',
'import',
'export',
Expand All @@ -35,16 +41,27 @@ export default class RentschCommand extends SubCommandPluginCommand {
return;
}

const guildData = await getGuildData(message.guild);
const rrhQuotes = guildData.data()?.quotes?.rentsch ?? [];
const quotesQuery = await this.getQuotesQuery(message.guild);
const size = await quotesQuery
.select(FieldPath.documentId())
.get()
.then((c) => c.docs.length);

const randomIndex = Math.floor(Math.random() * size);
const quote = await quotesQuery.offset(randomIndex).limit(1).get();

if (quote.empty) {
await send(message, {
embeds: [errorEmbed('There are no quotes')],
});
return;
}

const randomIndex = Math.floor(Math.random() * rrhQuotes.length);
const quote = rrhQuotes[randomIndex];
const embed = new MessageEmbed()
.setDescription(quote)
.setFooter({ text: `© Rentsch - ID: ${randomIndex}` });
.setDescription(quote.docs[0].data().content)
.setFooter({ text: `© Rentsch - ID: ${quote.docs[0].id}` });

message.channel.send({
await send(message, {
embeds: [embed],
});
}
Expand All @@ -58,22 +75,13 @@ export default class RentschCommand extends SubCommandPluginCommand {

const content = await args.rest('string');

const guildDb = await getGuildCollection(message.guild);
const guildDbData = await guildDb.get();
const quote: GuildQuote = { category: 'rentsch', content };

const quotes: GuildDocument['quotes'] = {
rentsch: [...(guildDbData.data()?.quotes?.rentsch ?? []), content],
};
await guildDb.set({ quotes }, { merge: true });
const quotesDb = await this.getQuotesCollection(message.guild);
const doc = await quotesDb.add(quote);

message.channel.send({
embeds: [
successEmbed(
`Quote successfully added.\nID: ${
quotes.rentsch.length - 1
}`,
),
],
await send(message, {
embeds: [successEmbed(`Quote successfully added.\nID: ${doc.id}`)],
});
}

Expand All @@ -84,29 +92,39 @@ export default class RentschCommand extends SubCommandPluginCommand {
return;
}

const id = await args.pick('number');
const id = await args.pick('string');
const content = await args.rest('string');

const guildDb = await getGuildCollection(message.guild);
const guildDbData = await guildDb.get();
const rrhQuotes = guildDbData.data()?.quotes?.rentsch ?? [];
const quotesDb = await this.getQuotesQuery(message.guild);
const quoteQueryRef = quotesDb.where(FieldPath.documentId(), '==', id);

try {
const quoteQuery = await quoteQueryRef.get();
if (quoteQuery.empty || !quoteQuery.docs[0].exists) {
await send(message, {
embeds: [errorEmbed(`No quote found with ID ${id}`)],
});
return;
}

const quote = quoteQuery.docs[0];
await quote.ref.update({
content,
});

if (!rrhQuotes[id]) {
message.channel.send({
embeds: [errorEmbed('Unknown quote ID.')],
await send(message, {
embeds: [
successEmbed(`Quote ${quote.id} successfully edited.`),
],
});

return;
} catch (e: unknown) {
logger.debug('[firestore] update error', e);
await send(message, {
embeds: [errorEmbed(`No quote found with ID ${id}`)],
});
}

rrhQuotes[id] = content;
const quotes: GuildDocument['quotes'] = {
rentsch: rrhQuotes,
};
await guildDb.set({ quotes }, { merge: true });

message.channel.send({
embeds: [successEmbed(`Quote #${id} successfully edited.`)],
});
}

public async delete(message: Message, args: Args) {
Expand All @@ -116,28 +134,38 @@ export default class RentschCommand extends SubCommandPluginCommand {
return;
}

const id = await args.pick('number');
const id = await args.pick('string');

const guildDb = await getGuildCollection(message.guild);
const guildDbData = await guildDb.get();
const rrhQuotes = guildDbData.data()?.quotes?.rentsch ?? [];
const quotesDb = await this.getQuotesQuery(message.guild);
const quoteQueryRef = quotesDb.where(FieldPath.documentId(), '==', id);

if (!rrhQuotes[id]) {
message.channel.send({
embeds: [errorEmbed('Unknown quote ID.')],
try {
const quoteQuery = await quoteQueryRef.get();
if (quoteQuery.empty || !quoteQuery.docs[0].exists) {
await send(message, {
embeds: [errorEmbed(`No quote found with ID ${id}`)],
});
return;
}

const quote = quoteQuery.docs[0];
await quote.ref.delete({
exists: true,
});
return;
}

rrhQuotes.splice(id, 1);
const quotes: GuildDocument['quotes'] = {
rentsch: rrhQuotes,
};
await guildDb.set({ quotes }, { merge: true });
await send(message, {
embeds: [
successEmbed(`Quote ${quote.id} successfully deleted.`),
],
});

message.channel.send({
embeds: [successEmbed(`Quote #${id} successfully deleted.`)],
});
return;
} catch (e: unknown) {
logger.debug('[firestore] update error', e);
await send(message, {
embeds: [errorEmbed(`No quote found with ID ${id}`)],
});
}
}

public async import(message: Message) {
Expand All @@ -154,13 +182,7 @@ export default class RentschCommand extends SubCommandPluginCommand {
return;
}

const guildDb = await getGuildCollection(message.guild);
const guildDbData = await guildDb.get();

const quotes: GuildDocument['quotes'] = {
rentsch: guildDbData.data()?.quotes?.rentsch ?? [],
};

const guildDb = await this.getQuotesCollection(message.guild);
await Promise.all(
message.attachments
.filter(
Expand All @@ -176,10 +198,17 @@ export default class RentschCommand extends SubCommandPluginCommand {
),
),
).then((allQuotes) =>
allQuotes.forEach((quoteFile) => quotes.rentsch.push(...quoteFile)),
Promise.all(
allQuotes.flatMap((quoteFile) =>
quoteFile.map((content) =>
guildDb.doc().set({
category: 'rentsch',
content,
}),
),
),
),
);

await guildDb.set({ quotes }, { merge: true });
message.channel.send({
embeds: [successEmbed('Import successful.')],
});
Expand All @@ -192,19 +221,19 @@ export default class RentschCommand extends SubCommandPluginCommand {
return;
}

const guildDb = await getGuildCollection(message.guild);
const guildDbData = await guildDb.get();

const quotes: GuildDocument['quotes'] = {
rentsch: guildDbData.data()?.quotes?.rentsch ?? [],
};
const quotesQueryRef = await this.getQuotesQuery(message.guild);
const quotesQuery = await quotesQueryRef.get();

message.channel.send({
content: '✅ Export successful',
files: [
{
attachment: Buffer.from(
JSON.stringify(quotes.rentsch, null, 2),
JSON.stringify(
quotesQuery.docs.map((d) => d.data().content),
null,
2,
),
'utf-8',
),
name: 'rrh.json',
Expand Down Expand Up @@ -243,4 +272,18 @@ export default class RentschCommand extends SubCommandPluginCommand {
],
});
}

private getQuotesCollection(
guild: Guild,
): Promise<CollectionReference<GuildQuote>> {
return getGuildCollection(guild).then((c) =>
c.collection('quotes').withConverter(converter<GuildQuote>()),
);
}

private getQuotesQuery(guild: Guild): Promise<Query<GuildQuote>> {
return this.getQuotesCollection(guild).then((qc) =>
qc.where('category', '==', 'rentsch'),
);
}
}
2 changes: 0 additions & 2 deletions src/database/guild-document.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
export interface GuildDocument {
counters: Record<CounterId, number>;
quotes: Record<QuoteId, string[]>;
}

export type CounterId = 'beers';
export type QuoteId = 'rentsch';
6 changes: 6 additions & 0 deletions src/database/guild-quote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface GuildQuote {
category: QuoteCategory;
content: string;
}

export type QuoteCategory = 'rentsch';

0 comments on commit 60e2e69

Please sign in to comment.