diff --git a/.gitignore b/.gitignore index 7392527..e1c8acd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,13 +3,10 @@ .vscode/ node_modules/ prisma/ +./user/ # files *.env* *.db* *.log -user/config.yml -user/**/*.* -!user/**/.gitkeep -!user/templates/* summary.md diff --git a/db/sqlite/schema.prisma b/db/sqlite/schema.prisma index 7c080b0..e6458ce 100644 --- a/db/sqlite/schema.prisma +++ b/db/sqlite/schema.prisma @@ -4,7 +4,7 @@ generator client { datasource db { provider = "sqlite" - url = "file:../user/database.db" + url = env("DB_CONNECTION_URL") } model ArchivedChannel { diff --git a/scripts/postinstall.js b/scripts/postinstall.js index 49b0ceb..78ef91b 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -4,9 +4,16 @@ const fs = require('fs-extra'); const util = require('util'); const exec = util.promisify(require('child_process').exec); const { short } = require('leeks.js'); +const { + resolve, join, +} = require('path'); const fallback = { prisma: './node_modules/prisma/build/index.js' }; +function pathify(path) { + return resolve(__dirname, '../', path); +} + function log(...strings) { console.log(short('&9[postinstall]&r'), ...strings); } @@ -24,7 +31,7 @@ async function npx(cmd) { const { stderr, stdout, - } = await exec(cmd); + } = await exec(cmd, { cwd: pathify('./') }); // { env } = process.env if (stdout) console.log(stdout.toString()); if (stderr) console.log(stderr.toString()); } @@ -42,15 +49,20 @@ if (!providers.includes(provider)) throw new Error(`DB_PROVIDER must be one of: log(`provider=${provider}`); log(`copying ${provider} schema & migrations`); -if (fs.existsSync('./prisma')) { +if (fs.existsSync(pathify('./prisma'))) { fs.rmSync('./prisma', { force: true, recursive: true, }); } else { - fs.mkdirSync('./prisma'); + fs.mkdirSync(pathify('./prisma')); +} +fs.copySync(pathify(`./db/${provider}`), pathify('./prisma')); // copy schema & migrations + +if (provider === 'sqlite' && !process.env.DB_CONNECTION_URL) { + process.env.DB_CONNECTION_URL = 'file:' + join(process.cwd(), './user/database.db'); + log(`set DB_CONNECTION_URL=${process.env.DB_CONNECTION_URL}`); } -fs.copySync(`./db/${provider}`, './prisma'); // copy schema & migrations (async () => { await npx('prisma generate'); diff --git a/scripts/start.sh b/scripts/start.sh index 89fede7..fb9728c 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,10 +1,16 @@ -#!/bin/sh +#!/usr/bin/env bash + +source="${BASH_SOURCE}" +base_dir=$(dirname $(dirname "$source")) echo "Checking environment..." -node scripts/preinstall +script=scripts/preinstall +node "$base_dir/$script" echo "Preparing the database..." -node scripts/postinstall +script=scripts/postinstall +node "$base_dir/$script" echo "Starting..." -node src/ \ No newline at end of file +script=src/ +node "$base_dir/$script" diff --git a/src/client.js b/src/client.js index 25f43c9..ce63190 100644 --- a/src/client.js +++ b/src/client.js @@ -15,25 +15,28 @@ const ms = require('ms'); module.exports = class Client extends FrameworkClient { constructor(config, log) { - super({ - intents: [ - ...[ - GatewayIntentBits.DirectMessages, - GatewayIntentBits.DirectMessageReactions, - GatewayIntentBits.DirectMessageTyping, - GatewayIntentBits.MessageContent, - GatewayIntentBits.Guilds, - GatewayIntentBits.GuildMembers, - GatewayIntentBits.GuildMessages, + super( + { + intents: [ + ...[ + GatewayIntentBits.DirectMessages, + GatewayIntentBits.DirectMessageReactions, + GatewayIntentBits.DirectMessageTyping, + GatewayIntentBits.MessageContent, + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessages, + ], + ...(process.env.PUBLIC_BOT !== 'true' ? [GatewayIntentBits.GuildPresences] : []), ], - ...(process.env.PUBLIC_BOT !== 'true' ? [GatewayIntentBits.GuildPresences] : []), - ], - partials: [ - Partials.Channel, - Partials.Message, - Partials.Reaction, - ], - }); + partials: [ + Partials.Channel, + Partials.Message, + Partials.Reaction, + ], + }, + { baseDir: __dirname }, + ); const locales = {}; fs.readdirSync(join(__dirname, 'i18n')) @@ -57,13 +60,19 @@ module.exports = class Client extends FrameworkClient { const levels = ['error', 'info', 'warn']; if (this.config.logs.level === 'debug') levels.push('query'); - /** @type {PrismaClient} */ - this.prisma = new PrismaClient({ + const prisma_options = { log: levels.map(level => ({ emit: 'event', level, })), - }); + }; + + if (process.env.DB_PROVIDER === 'sqlite' && !process.env.DB_CONNECTION_URL) { + prisma_options.datasources = { db: { url:'file:' + join(process.cwd(), './user/database.db') } }; + } + + /** @type {PrismaClient} */ + this.prisma = new PrismaClient(prisma_options); this.prisma.$on('error', e => this.log.error.prisma(`${e.target} ${e.message}`)); this.prisma.$on('info', e => this.log.info.prisma(`${e.target} ${e.message}`)); diff --git a/src/index.js b/src/index.js index 62126c6..dc77ec8 100644 --- a/src/index.js +++ b/src/index.js @@ -29,6 +29,7 @@ console.log(banner(pkg.version)); // print big title const semver = require('semver'); const { colours } = require('leeks.js'); +const path = require('path'); // check node version if (!semver.satisfies(process.versions.node, pkg.engines.node)) { @@ -36,6 +37,16 @@ if (!semver.satisfies(process.versions.node, pkg.engines.node)) { process.exit(1); } +const base_dir = path.resolve(path.join(__dirname, '../')); +const cwd = path.resolve(process.cwd()); +if (base_dir !== cwd) { + console.log('\x07' + colours.yellowBright('Warning: The current working directory is not the same as the base directory.')); + console.log(colours.yellowBright('This may result in unexpected behaviour, particularly with missing environment variables.')); + console.log(' Base directory: ' + colours.gray(base_dir)); + console.log(' CWD: ' + colours.gray(cwd)); + console.log(colours.blueBright(' Learn more at https://lnk.earth/dt-cwd.')); +} + // this could be done first, but then there would be no banner :( process.env.NODE_ENV ??= 'production'; // make sure NODE_ENV is set require('./env').load(); // load and check environment variables @@ -46,16 +57,12 @@ const logger = require('./lib/logger'); const Client = require('./client'); const http = require('./http'); +// user directory may or may not exist depending on if sqlite is being used +// so the config file could be missing even though the directory exists if (!fs.existsSync('./user/config.yml')) { - const examplePath = './user/example.config.yml'; - if (!fs.existsSync(examplePath)) { - console.log('\x07' + colours.redBright('The config file does not exist, and the example file is missing so cannot be copied from.')); - process.exit(1); - } else { - console.log('Creating config file...'); - fs.copyFileSync(examplePath, './user/config.yml'); - console.log(`Copied config to ${'./user/config.yml'}`); - } + console.log('The config file does not exist, copying defaults...'); + fs.cpSync(path.join(__dirname, 'user'), './user', { recursive: true }); + console.log('Created user directory at', path.join(cwd, 'user')); } const config = YAML.parse(fs.readFileSync('./user/config.yml', 'utf8')); diff --git a/src/user/avatars/.gitkeep b/src/user/avatars/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/user/example.config.yml b/src/user/config.yml similarity index 100% rename from user/example.config.yml rename to src/user/config.yml diff --git a/src/user/templates/transcript.md.mustache b/src/user/templates/transcript.md.mustache new file mode 100644 index 0000000..cb69b1d --- /dev/null +++ b/src/user/templates/transcript.md.mustache @@ -0,0 +1,30 @@ +#{{ channelName }} ticket transcript + +--- + +* ID: {{ ticket.id }} ({{ guildName }}) +* Number: {{ ticket.category.name }} #{{ ticket.number }} +* Topic: {{ #ticket.topic }}{{ . }}{{ /ticket.topic }}{{ ^ticket.topic }}(no topic){{ /ticket.topic }} +* Created on: {{ #ticket }}{{ createdAtFull }}{{ /ticket }} +* Created by: {{ #ticket.createdBy }}"{{ displayName }}" @{{ username }}#{{ discriminator }}{{ /ticket.createdBy }} +* Closed on: {{ #ticket }}{{ closedAtFull }}{{ /ticket }} +* Closed by: {{ #ticket.closedBy }}"{{ displayName }}" @{{ username }}#{{ discriminator }}{{ /ticket.closedBy }}{{ ^ticket.closedBy }}(automated){{ /ticket.closedBy }} +* Closed because: {{ #ticket.closedReason }}{{ ticket.closedReason }}{{ /ticket.closedReason }}{{ ^ticket.closedReason }}(no reason){{ /ticket.closedReason }} +* Claimed by: {{ #ticket.claimedBy }}"{{ displayName }}" @{{ username }}#{{ discriminator }}{{ /ticket.claimedBy }}{{ ^ticket.claimedBy }}(not claimed){{ /ticket.claimedBy }} +{{ #ticket.feedback }} +* Feedback: + * Rating: {{ rating }}/5 + * Comment: {{ comment }}{{ ^comment }}(no comment){{ /comment }} +{{ /ticket.feedback }} +* Participants: +{{ #ticket.archivedUsers }} + * "{{ displayName }}" @{{ username }}#{{ discriminator }} ({{ userId }}) +{{ /ticket.archivedUsers }} +* Pinned messages: {{ #pinned }}{{ . }}{{ /pinned }} + +--- + +{{ #ticket.archivedMessages }} +<{{ number }}> [{{ createdAtTimestamp }}] {{author.displayName}}: {{ text }} + +{{ /ticket.archivedMessages }} \ No newline at end of file diff --git a/src/user/uploads/.gitkeep b/src/user/uploads/.gitkeep new file mode 100644 index 0000000..e69de29