Skip to content

Commit f6c5f36

Browse files
committed
Add notes
1 parent 2d360c3 commit f6c5f36

9 files changed

Lines changed: 341 additions & 5 deletions

File tree

src/HuTaoClient.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import FollowManager from "./utils/FollowManager"
1515
import NewsManager from "./utils/NewsManager"
1616
import ReminderManager from "./utils/ReminderManager"
1717
import WebManager from "./utils/WebManager"
18+
import NotesManager from "./utils/NotesManager"
1819

1920
const Logger = log4js.getLogger("main")
2021
const intents = new Intents()
@@ -32,6 +33,7 @@ export default class HuTaoClient extends Discord.Client {
3233
timerManager: TimerManager = new TimerManager()
3334
followManager: FollowManager = new FollowManager()
3435
reminderManager: ReminderManager = new ReminderManager()
36+
notesManager: NotesManager = new NotesManager()
3537

3638
tweetManager: TweetManager = new TweetManager()
3739
newsManager: NewsManager = new NewsManager()

src/commands/misc/noteadd.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { CommandInteraction, Message, MessageEmbed } from "discord.js"
2+
import config from "../../data/config.json"
3+
import client from "../../main"
4+
import Command from "../../utils/Command"
5+
import { CommandSource, SendMessage } from "../../utils/Types"
6+
import { Colors, getCategory, getUser, sendMessage } from "../../utils/Utils"
7+
8+
9+
export default class NoteAdd extends Command {
10+
constructor(name: string) {
11+
super({
12+
name,
13+
category: "Misc",
14+
help: `Create a new note, notes are per category or user.
15+
16+
Requires "Manage Messages" permissions to add, but anyone can see.`,
17+
usage: "noteadd <name>",
18+
aliases: ["na", "createnote", "addnote", "newnote", "an"],
19+
options: [{
20+
name: "name",
21+
description: "Name to show",
22+
type: "STRING",
23+
required: true
24+
}]
25+
})
26+
}
27+
28+
async runInteraction(source: CommandInteraction): Promise<SendMessage | undefined> {
29+
const { options } = source
30+
31+
const name = options.getString("name", true)
32+
33+
return this.run(source, name)
34+
}
35+
36+
async runMessage(source: Message|CommandInteraction, args: string[]): Promise<SendMessage | undefined> {
37+
if (args.length < 1) return this.sendHelp(source)
38+
39+
const name = args.join(" ")
40+
41+
return this.run(source, name)
42+
}
43+
44+
async run(source: CommandSource, subject: string): Promise<SendMessage | undefined> {
45+
const { notesManager } = client
46+
const user = getUser(source)
47+
48+
const guild = source.guild
49+
const category = getCategory(source)
50+
const name = category?.name ?? guild?.name ?? user.username
51+
52+
const guildID = guild?.id ?? "user"
53+
const categoryID = category?.id ?? guild?.id ?? user.id
54+
55+
if (guild)
56+
if (source.member == undefined || typeof source.member.permissions == "string")
57+
return sendMessage(source, "Unable to check permissions.", undefined, true)
58+
else if (!source.member.permissions.has("MANAGE_MESSAGES"))
59+
return sendMessage(source, "You do not have have \"Manage Messages\" permissions in here.", undefined, true)
60+
61+
const notes = notesManager.getNotes(guildID, categoryID)
62+
if (notes.length >= 50) return sendMessage(source, `You can only have up to 50 notes in ${name}, see \`${config.prefix}notes\` for which you have`)
63+
64+
if (subject.length > 256) return sendMessage(source, "Note name too long")
65+
66+
67+
let id = 1
68+
while (notes.some(r => r.id == id) || notesManager.getNoteById(guildID, categoryID, id))
69+
id++
70+
71+
notesManager.addNote(guildID, categoryID, id, subject, user.id)
72+
const reply = sendMessage(source, new MessageEmbed()
73+
.setTitle(`Created note #${id} in ${name}`)
74+
.setColor(Colors.GREEN)
75+
.setDescription(`The note #${id}: \`${subject}\` has been created`)
76+
)
77+
78+
return reply
79+
}
80+
}

src/commands/misc/noteremove.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { CommandInteraction, Message, MessageEmbed } from "discord.js"
2+
import client from "../../main"
3+
import Command from "../../utils/Command"
4+
import { CommandSource, SendMessage } from "../../utils/Types"
5+
import { Colors, getCategory, getUser, sendMessage } from "../../utils/Utils"
6+
7+
8+
export default class NoteRemove extends Command {
9+
constructor(name: string) {
10+
super({
11+
name,
12+
category: "Misc",
13+
help: `Removes a new note, notes are per category or user.
14+
15+
Requires "Manage Messages" permissions to add, but anyone can see.`,
16+
usage: "noteremove <id>",
17+
aliases: ["rn", "dn", "removenote", "deletenote", "delnote", "dnote", "rnote"],
18+
options: [{
19+
name: "id",
20+
description: "ID to remove",
21+
type: "INTEGER",
22+
required: true
23+
}]
24+
})
25+
}
26+
27+
async runInteraction(source: CommandInteraction): Promise<SendMessage | undefined> {
28+
const { options } = source
29+
30+
const id = options.getInteger("id", true)
31+
32+
return this.run(source, id)
33+
}
34+
35+
async runMessage(source: Message|CommandInteraction, args: string[]): Promise<SendMessage | undefined> {
36+
if (args.length < 1) return this.sendHelp(source)
37+
38+
const id = parseInt(args[0])
39+
if (isNaN(id)) return this.sendHelp(source)
40+
41+
return this.run(source, id)
42+
}
43+
44+
async run(source: CommandSource, id: number): Promise<SendMessage | undefined> {
45+
const { notesManager } = client
46+
const user = getUser(source)
47+
48+
const guild = source.guild
49+
const category = getCategory(source)
50+
const name = category?.name ?? guild?.name ?? user.username
51+
52+
const guildID = guild?.id ?? "user"
53+
const categoryID = category?.id ?? guild?.id ?? user.id
54+
55+
if (source.member == undefined || typeof source.member.permissions == "string")
56+
return sendMessage(source, "Unable to check permissions.", undefined, true)
57+
else if (!source.member.permissions.has("MANAGE_MESSAGES"))
58+
return sendMessage(source, "You do not have have \"Manage Messages\" permissions in here.", undefined, true)
59+
60+
const note = notesManager.getNoteById(guildID, categoryID, id)
61+
if (note == undefined) return sendMessage(source, `Could not find note #${id} in ${name}`)
62+
63+
notesManager.deleteNote(guildID, categoryID, id, user.id)
64+
const reply = sendMessage(source, new MessageEmbed()
65+
.setTitle(`Deleted note #${id} in ${name}`)
66+
.setColor(Colors.RED)
67+
.setDescription(`The note #${id}: \`${note.subject}\` has been deleted`)
68+
)
69+
70+
return reply
71+
}
72+
}

src/commands/misc/notes.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { CommandInteraction, Message, MessageEmbed } from "discord.js"
2+
import config from "../../data/config.json"
3+
import client from "../../main"
4+
import Command from "../../utils/Command"
5+
import { CommandSource, Note, SendMessage } from "../../utils/Types"
6+
import { Colors, getCategory, getUser, sendMessage, simplePaginator } from "../../utils/Utils"
7+
8+
9+
export default class Notes extends Command {
10+
constructor(name: string) {
11+
super({
12+
name,
13+
category: "Misc",
14+
help: `View notes, notes are per category or user.
15+
16+
Requires "Manage Messages" permissions to add, but anyone can see.`,
17+
usage: "notes",
18+
aliases: ["viewnotes", "no"],
19+
options: []
20+
})
21+
}
22+
23+
async runInteraction(source: CommandInteraction): Promise<SendMessage | undefined> {
24+
return this.run(source)
25+
}
26+
27+
async runMessage(source: Message|CommandInteraction): Promise<SendMessage | undefined> {
28+
return this.run(source)
29+
}
30+
31+
async run(source: CommandSource): Promise<SendMessage | undefined> {
32+
const { notesManager } = client
33+
const user = getUser(source)
34+
35+
const guild = source.guild
36+
const category = getCategory(source)
37+
const name = category?.name ?? guild?.name ?? user.username
38+
39+
const guildID = guild?.id ?? "user"
40+
const categoryID = category?.id ?? guild?.id ?? user.id
41+
42+
const notes = notesManager.getNotes(guildID, categoryID)
43+
44+
const pages = this.getNotePages(notes)
45+
if (pages.length == 0) return sendMessage(source, `There are no notes saved here, see \`${config.prefix}help noteadd\` on how to add a note`)
46+
47+
await simplePaginator(source, (relativePage, currentPage, maxPages) => this.getNotePage(pages, name, relativePage, currentPage, maxPages), pages.length)
48+
return undefined
49+
}
50+
51+
getNotePages(notes: Note[]): string[] {
52+
const pages: string[] = []
53+
let paging = "", c = 0
54+
for (const note of notes) {
55+
const subject = `\`#${note.id}\`: \`${note.subject}\``
56+
if (paging.length + subject.length > 1800 || ++c > 10) {
57+
pages.push(paging.trim())
58+
paging = subject
59+
c = 1
60+
} else
61+
paging += "\n" + subject
62+
}
63+
if (paging.trim().length > 0) pages.push(paging)
64+
return pages
65+
}
66+
67+
getNotePage(pages: string[], name: string, relativePage: number, currentPage: number, maxPages: number): MessageEmbed | undefined {
68+
if (relativePage >= pages.length)
69+
return undefined
70+
71+
const embed = new MessageEmbed()
72+
.setTitle(`Notes in ${name}`)
73+
.setDescription(pages[relativePage])
74+
.setFooter(`Page ${currentPage} / ${maxPages}`)
75+
.setColor(Colors.GREEN)
76+
77+
return embed
78+
}
79+
}

src/commands/misc/reminders.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default class Reminders extends Command {
1818
options: []
1919
})
2020
}
21+
2122
async runInteraction(source: CommandInteraction): Promise<SendMessage | undefined> {
2223
return this.run(source)
2324
}

src/commands/weapons/weapon.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ Note: this command supports fuzzy search.`,
211211
.setColor(Colors.AQUA)
212212
.setThumbnail(`${data.baseURL}${weapon.icon}`)
213213
.setFooter(`Page ${currentPage} / ${maxPages}`)
214-
.setDescription(weapon.desc + (weapon.placeholder ? "\n\n*This weapon is currently not yet available.*" : ""))
214+
.setDescription((weapon.desc ? weapon.desc : "") + ((weapon.placeholder || !weapon.desc) ? "\n\n*This weapon is currently not yet available.*" : ""))
215215
.addField("Basics", `${weapon.stars}${data.emoji(weapon.weaponType)}`, (weapon.placeholderStats && !weapon.weaponCurve) ? true : false)
216216

217217
const maxAscension = weapon.ascensions?.[weapon.ascensions.length - 1]

src/utils/NotesManager.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import SQLite from "better-sqlite3"
2+
import { ensureDirSync } from "fs-extra"
3+
import log4js from "log4js"
4+
import { Note } from "./Types"
5+
6+
7+
const Logger = log4js.getLogger("NotesManager")
8+
ensureDirSync("data/")
9+
10+
export default class NotesManager {
11+
sql = new SQLite("data/notes.db")
12+
13+
constructor() {
14+
Logger.info("Initializing data")
15+
process.on("exit", () => this.sql.close())
16+
process.on("SIGHUP", () => process.exit(128 + 1))
17+
process.on("SIGINT", () => process.exit(128 + 2))
18+
process.on("SIGTERM", () => process.exit(128 + 15))
19+
20+
this.sql.exec("CREATE TABLE IF NOT EXISTS notes (global_id INTEGER PRIMARY KEY, guild_id TEXT, category_id TEXT, id INTEGER, user TEXT, subject TEXT, timestamp INTEGER)")
21+
this.sql.exec("CREATE INDEX IF NOT EXISTS notes_category ON notes (guild_id, category_id)")
22+
23+
this.addNotesStatement = this.sql.prepare("INSERT OR REPLACE INTO notes (guild_id, category_id, id, user, subject, timestamp) VALUES (@guild_id, @category_id, @id, @user, @subject, @timestamp)")
24+
this.getNoteByCategoryStatement = this.sql.prepare("SELECT * FROM notes WHERE guild_id = @guild_id AND category_id = @category_id")
25+
this.getNoteByIdStatement = this.sql.prepare("SELECT * FROM notes WHERE guild_id = @guild_id AND category_id = @category_id AND id = @id")
26+
this.deleteNoteStatement = this.sql.prepare("DELETE FROM notes WHERE guild_id = @guild_id AND category_id = @category_id AND id = @id")
27+
}
28+
29+
private addNotesStatement: SQLite.Statement
30+
addNote(guildID: string, categoryID: string, id: number, subject: string, userId: string): Note {
31+
const note: Note = {
32+
guild_id: guildID,
33+
category_id: categoryID,
34+
id,
35+
subject,
36+
user: userId,
37+
timestamp: Date.now()
38+
}
39+
40+
this.addNotesStatement.run(note)
41+
Logger.info(`Added new note for ${userId} in ${guildID} / ${categoryID} #${id}: ${subject}`)
42+
return note
43+
}
44+
45+
private getNoteByCategoryStatement: SQLite.Statement
46+
getNotes(guildId: string, categoryId: string): Note[] {
47+
return this.getNoteByCategoryStatement.all({
48+
guild_id: guildId,
49+
category_id: categoryId,
50+
})
51+
}
52+
53+
private getNoteByIdStatement: SQLite.Statement
54+
getNoteById(guildId: string, categoryId: string, id: number): Note {
55+
return this.getNoteByIdStatement.get({
56+
guild_id: guildId,
57+
category_id: categoryId,
58+
id
59+
})
60+
}
61+
62+
private deleteNoteStatement: SQLite.Statement
63+
deleteNote(guildId: string, categoryId: string, id: number, userId: string): void {
64+
this.deleteNoteStatement.run({
65+
guild_id: guildId,
66+
category_id: categoryId,
67+
id
68+
})
69+
Logger.info(`Deleted note by ${userId} in ${guildId} / ${categoryId} #${id}`)
70+
}
71+
}

src/utils/Types.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ export interface Reminder {
4343
duration: number
4444
}
4545

46+
// Notes
47+
export interface Note {
48+
guild_id: string
49+
category_id: string
50+
id: number
51+
user: string
52+
subject: string
53+
timestamp: number
54+
}
55+
4656
// News posts
4757
export interface News {
4858
post: Post
@@ -442,7 +452,7 @@ export enum WeaponType {
442452

443453
export interface Weapon {
444454
name: string
445-
desc: string
455+
desc?: string
446456
placeholder?: true
447457
placeholderStats?: PlaceHolderStats
448458
weaponType: WeaponType

0 commit comments

Comments
 (0)