diff --git a/package.json b/package.json index d8479c6..fed095b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "license": "MIT", "dependencies": { "discord.js": "^14.7.1", - "dokdo": "^0.6.2", "dotenv": "^16.0.3", "mysql2": "^3.1.0" }, @@ -13,6 +12,7 @@ "@migan/prettier-config": "^1.0.0", "@types/jest": "^29.4.0", "@types/node": "^18.11.18", + "cross-env": "^7.0.3", "jest": "^29.4.0", "prettier": "^2.8.3", "ts-jest": "^29.0.5", @@ -22,8 +22,8 @@ }, "scripts": { "build": "tsup", - "dev": "ts-node src", - "start": "node dist", + "dev": "cross-env NODE_ENV=development ts-node src", + "start": "cross-env NODE_ENV=production node dist", "test": "jest" }, "prettier": "@migan/prettier-config" diff --git a/src/Client.ts b/src/Client.ts index 48e68db..778eb2a 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -5,8 +5,7 @@ import { GatewayIntentBits, TextChannel, } from 'discord.js' -import { Command, noPerm, ChatBot } from './modules' -import Dokdo from 'dokdo' +import { Command, noPerm, ChatBot, NODE_ENV } from './modules' import { readdirSync } from 'node:fs' import { join } from 'node:path' import 'dotenv/config' @@ -27,12 +26,14 @@ export default class MuffinAI extends Client { } public override login(): Promise { + if (NODE_ENV === 'development') this.on('debug', console.info) this.chatBot.train(this) readdirSync(join(__dirname, 'Commands')).forEach(file => { const a = require(join(__dirname, 'Commands', file)) const b: Command = new a.default() this.#modules.set(b.name, b) + if (NODE_ENV === 'development') console.log(b.name) }) this.once('ready', () => { @@ -43,17 +44,15 @@ export default class MuffinAI extends Client { console.log(`먹힐 준비 완료`) }).on('messageCreate', async msg => { if (msg.author.bot) return - await new Dokdo(this, { - prefix, - noPerm, - aliases: ['테스트'], - owners: ['415135882006495242'], - }).run(msg) if (msg.content.startsWith('머핀아 ')) { if (msg.channel instanceof TextChannel) { if (msg.channel.nsfw) return await msg.channel.sendTyping() - await msg.channel.send(await this.chatBot.getResponse(msg)) + this.chatBot // + .getResponse(msg) + .then(response => { + msg.channel.send(response) + }) } } else if (msg.content.startsWith(prefix)) { if (msg.channel instanceof TextChannel) if (msg.channel.nsfw) return @@ -61,12 +60,13 @@ export default class MuffinAI extends Client { const args: string[] = msg.content .slice(prefix.length) .trim() - .split('/ +/g') - - const command = this.#modules.get(args.toString()) + .split(/ +/g) + if (NODE_ENV === 'development') console.log(args) + const command = this.#modules.get(args.shift()!.toLowerCase()) if (!command) return if (command.noPerm && msg.author.id !== '415135882006495242') return await noPerm(msg) + command.execute(msg, args) } }) @@ -75,7 +75,6 @@ export default class MuffinAI extends Client { public override destroy() { super.destroy() - process.exit() } } diff --git a/src/Commands/테스트.ts b/src/Commands/테스트.ts new file mode 100644 index 0000000..1534c6c --- /dev/null +++ b/src/Commands/테스트.ts @@ -0,0 +1,20 @@ +import { Command } from '../modules' +import { Message } from 'discord.js' +import { inspect } from 'node:util' + +export default class extends Command { + public constructor() { + super('테스트', true) + } + public async execute(msg: Message, args: string[]) { + if (!args) return msg.channel.send('전달받은 인자가 없습니다.') + try { + const a = eval(args.join(' ')) + if (inspect(a) === `'${msg.client.token}'`) + return msg.channel.send('[discord_token]') + await msg.channel.send(`\`\`\`js\n${inspect(a)}\n\`\`\``) + } catch (err) { + await msg.channel.send(`\`\`\`js\n${err}\n\`\`\``) + } + } +} diff --git a/src/Commands/학습데이터량.ts b/src/Commands/학습데이터량.ts index 97d39b6..07671ee 100644 --- a/src/Commands/학습데이터량.ts +++ b/src/Commands/학습데이터량.ts @@ -7,9 +7,9 @@ export default class extends Command { } public async execute(msg: Message, args: string[]) { const conn = await database.getConnection() - const [rows] = await conn.query('SELECT * FROM statement;') + const [rows] = await conn.query('SELECT * FROM statement;') const muffin: ResponseData[] = [] - ;(rows as ResponseData[]).forEach(row => { + rows.forEach(row => { if (row.persona === 'muffin') muffin.push(row) else return }) diff --git a/src/modules/ChatBot.ts b/src/modules/ChatBot.ts index 6fadeee..4e92d55 100644 --- a/src/modules/ChatBot.ts +++ b/src/modules/ChatBot.ts @@ -1,42 +1,41 @@ import type { Client, Message } from 'discord.js' -import database, { ResponseData } from './Database' +import database, { ResponseData } from './database' export default class ChatBot { public async getResponse(msg: Message): Promise { const conn = await database.getConnection() const request = msg.content.replace('머핀아 ', '') - console.log(`⌨️ㅣ${request}`) - const [rows] = await conn.query('SELECT * FROM statement;') - let response = (rows as ResponseData[])[ - Math.floor(Math.random() * (rows as ResponseData[]).length) - ].text + console.log(`req: ${request}`) + const [rows] = await conn.query('SELECT * FROM statement;') + let response = rows[Math.floor(Math.random() * rows.length)].text if (!response) response = '살ㄹ려주세요' - - console.log(`🍰ㅣ${response}`) + console.log(`res: ${response}`) conn.release() return response } - public async train(client: Client): Promise { + public train(client: Client): ChatBot { client.on('messageCreate', async msg => { - const conn = await database.getConnection() if (msg.author.bot) return - if (msg.author.id !== '1026185545837191238') return - const response = this.getResponse(msg) - const result = await conn.query('SELECT * FROM statement;') - const rows = result[0] as ResponseData[] - await conn.beginTransaction() - try { - await conn.execute( - `INSERT INTO statement(id, text, persona, in_response_to) VALUES(?, ?, ?, ?);`, - [++rows[rows.length - 1].id, msg.content, 'muffin', response] + const conn = await database.getConnection() + if (msg.author.id === '1026185545837191238') { + const response = await this.getResponse(msg) + const [rows] = await conn.query( + 'SELECT * FROM statement;' ) - await conn.commit() - } catch (err) { - console.log(err) - await conn.rollback() - } finally { - conn.release() + try { + await conn.beginTransaction() + await conn.execute( + 'INSERT INTO statement (id, text, persona, in_response_to) VALUES (?, ?, ?, ?);', + [++rows[rows.length - 1].id, msg.content, 'muffin', response] + ) + await conn.commit() + } catch (err) { + console.log(err) + await conn.rollback() + } finally { + conn.release() + } } }) return this diff --git a/src/modules/Database.ts b/src/modules/database.ts similarity index 64% rename from src/modules/Database.ts rename to src/modules/database.ts index 1629b56..8556595 100644 --- a/src/modules/Database.ts +++ b/src/modules/database.ts @@ -1,7 +1,7 @@ -import { createPool } from 'mysql2/promise' +import { PoolOptions, createPool, RowDataPacket } from 'mysql2/promise' import 'dotenv/config' -export interface ResponseData { +export interface ResponseData extends RowDataPacket { id: number text: string search_text: string @@ -12,10 +12,13 @@ export interface ResponseData { persona: string } -export default createPool({ +export const config: PoolOptions = { host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DATABASE, port: (process.env.MYSQL_PORT as unknown as number) || 3306, -}) + enableKeepAlive: true, +} + +export default createPool(config) diff --git a/src/modules/env.ts b/src/modules/env.ts new file mode 100644 index 0000000..cb301e8 --- /dev/null +++ b/src/modules/env.ts @@ -0,0 +1,5 @@ +type NODE_ENV_TYPE = 'production' | 'development' | string + +export const NODE_ENV: NODE_ENV_TYPE = process.env.NODE_ENV + ? process.env.NODE_ENV + : 'production' diff --git a/src/modules/index.ts b/src/modules/index.ts index 01f1aff..3ffcaf3 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -1,5 +1,6 @@ import ChatBot from './ChatBot' import Command from './Command' -import database, { ResponseData } from './Database' +import database, { ResponseData, config } from './database' import noPerm from './noPerm' -export { ChatBot, Command, database, noPerm, ResponseData } +import { NODE_ENV } from './env' +export { ChatBot, Command, database, noPerm, ResponseData, config, NODE_ENV } diff --git a/tests/Database.test.ts b/tests/Database.test.ts deleted file mode 100644 index ee06d4a..0000000 --- a/tests/Database.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* -import Database from '../src/modules/Database' -import sqlite3 from 'sqlite3' - -describe('Test Database', () => { - const DBPATH = `${__dirname}/../db/db.sqlite3` - const db = new sqlite3.Database(DBPATH) - const getData = () => { - return new Promise((resolve, reject) => { - db.all('SELECT * FROM statement;', async (err, rows) => { - if (err) reject(err) - resolve(rows) - }).close() - }) - } - - test('Get rows', () => { - getData().then(async rows => - expect(await new Database(DBPATH).all()).toEqual(rows) - ) - }) - - test('Insert row', () => { - const db = new Database(DBPATH) - return db.all().then(async data1 => { - db.run( - 'INSERT INTO statement(id, text) VALUES(?, ?)', - [++data1[data1.length - 1].id, 'TEST'], - err => { - if (err) throw err - } - ) - const data2 = await db.all() - expect(data1[data1.length - 1]).not.toEqual(data2[data2.length - 1]) - }) - }) - - test('Delete row', () => { - const db = new Database(DBPATH) - return db.all().then(async data1 => { - db.run('DELETE FROM statement WHERE text=?;', ['TEST'], err => { - if (err) throw err - }) - const data2 = await db.all() - expect(data1[data1.length - 1]).not.toEqual(data2[data2.length - 1]) - }) - }) -}) -*/ diff --git a/tests/database.test.ts b/tests/database.test.ts new file mode 100644 index 0000000..4c51fd3 --- /dev/null +++ b/tests/database.test.ts @@ -0,0 +1,13 @@ +import { database, config } from '../src/modules' +import { createConnection } from 'mysql2/promise' + +describe('test database system', () => { + test('Validate rows', async () => { + return database.getConnection().then(async conn1 => { + const [rows1] = await conn1.query('SELECT * FROM statement;') + const conn2 = await createConnection(config) + const [rows2] = await conn2.query(`SELECT * FROM statement;`) + expect(rows1).toEqual(rows2) + }) + }) +}) diff --git a/yarn.lock b/yarn.lock index 50d1c19..4af7ab3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1135,7 +1135,14 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.3: +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^7.0.1, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1211,13 +1218,6 @@ discord.js@^14.7.1: undici "^5.13.0" ws "^8.11.0" -dokdo@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/dokdo/-/dokdo-0.6.2.tgz#1e276999f9230f0df54f561992f33a79044d31dc" - integrity sha512-o0m3SSFok+OOvX+Oh8hD17Gx4K/AXSeIv7nZtLPmjkUWDzXO4d4mgQK1s82PDy/QOkDEipV9mm5x8gUNoYHRqA== - dependencies: - node-fetch "^2.6.1" - dotenv@^16.0.3: version "16.0.3" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" @@ -2311,13 +2311,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-fetch@^2.6.1: - version "2.6.8" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" - integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== - dependencies: - whatwg-url "^5.0.0" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -2808,11 +2801,6 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -2942,24 +2930,11 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"