This commit is contained in:
Isaac 2021-02-16 14:48:04 +00:00
parent dc902b6daf
commit cf8dff2a5b
No known key found for this signature in database
GPG Key ID: 279D1F53391CED07
10 changed files with 1065 additions and 410 deletions

View File

@ -1,57 +1,3 @@
# Please download from [releases](https://github.com/eartharoid/DiscordTickets/releases) or [v2 branch](https://github.com/eartharoid/DiscordTickets/tree/v2) - master branch is currently a work in progress # Please download from [releases](https://github.com/eartharoid/DiscordTickets/releases) or [v2 branch](https://github.com/eartharoid/DiscordTickets/tree/v2) - master branch is currently a work in progress
https://discord.com/oauth2/authorize?permissions=8&scope=applications.commands%20bot&client_id=... https://discord.com/oauth2/authorize?permissions=8&scope=applications.commands%20bot&client_id=...
# DiscordTickets
[![Run on Repl.it](https://repl.it/badge/github/eartharoid/DiscordTickets)](https://repl.it/github/eartharoid/DiscordTickets) [![GitHub issues](https://img.shields.io/github/issues/eartharoid/DiscordTickets?style=flat-square)](https://github.com/eartharoid/DiscordTickets/issues) [![GitHub stars](https://img.shields.io/github/stars/eartharoid/DiscordTickets?style=flat-square)](https://github.com/eartharoid/DiscordTickets/stargazers) [![GitHub forks](https://img.shields.io/github/forks/eartharoid/DiscordTickets?style=flat-square)](https://github.com/eartharoid/DiscordTickets/network) [![GitHub license](https://img.shields.io/github/license/eartharoid/DiscordTickets?style=flat-square)](https://github.com/eartharoid/DiscordTickets/blob/master/LICENSE) ![Codacy grade](https://img.shields.io/codacy/grade/14e6851c85444424b75b8bc3f93e93db?logo=codacy&style=flat-square) [![Discord support server](https://discordapp.com/api/guilds/451745464480432129/embed.png?style=shield)](https://discord.gg/pXc9vyC)
DiscordTickets is a Discord bot for managing support ticket channels, to allow you and your team to provide better and quicker assistance to your members/customers. It is a free, open-source, and self-hosted alternative to [Ticket Tool](https://tickettool.xyz/) and [TicketsBot](https://ticketsbot.net/) premium/whitelabel plans.
## Features
- Highly customisable
- Ticket "panel" / "widget" (react to embed to create ticket)
- Simple commands
- Supports multiple databases: SQLite (easy, default), MySQL (recommend) storage, MariaDB, PostgreSQL and Microsoft SQL Server
- Most (all of the best) features of the premium bots, for free
- Self-hosted with your bot application, your logo and guild name
- Optional express.js server for web archives: [DiscordTickets-Portal](https://github.com/eartharoid/DiscordTickets-Portal/)
## Screenshot
*The mention and embed text are customisable. The images is randomly selected and can be changed or disabled.*
![Screenshot](https://i.imgur.com/Y1uCqJJ.gif)
## Instructions
For installation, configuration, usage, and other information, **go to the [wiki](https://github.com/Eartharoid/DiscordTickets/wiki).**
Basic installation support will be provided on Discord, however if you really don't know what you're doing, you can purchase the [premium support setup service](https://ko-fi.com/s/34ccccfbd6) and I will install the bot and portal (archives web server) for you.
## Support
**[Go to the wiki](https://github.com/Eartharoid/DiscordTickets/wiki)**, or ask for help in [#support](https://discordapp.com/channels/451745464480432129/475351519516950548) on [Discord](https://discord.gg/pXc9vyC).
[![Discord](https://discordapp.com/api/guilds/451745464480432129/widget.png?style=banner4)](https://discord.gg/pXc9vyC)
## Donate
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/eartharoid)
---
<div align="center">
<a href="https://private-hosting.eu/">
<img src="https://i.imgur.com/eSXhP9C.png"/>
</a>
<br />
<br />
<b>
<a href="https://private-hosting.eu/">Sponsored by Private-Hosting</a>
</b>
<br>
<sub>
Cheap and powerful servers with 100% uptime.
</sub>
</div>

View File

@ -22,3 +22,74 @@ a code:hover {
/* text-decoration: underline; */ /* text-decoration: underline; */
font-weight: bold; font-weight: bold;
} }
footer.sponsorship {
text-align: center
}
footer.sponsorship hr {
display: inline-block;
width: 1.6rem;
margin: 0 .7rem;
vertical-align: middle;
border-bottom: 2px solid var(--md-default-fg-color--lighter)
}
footer.sponsorship:hover hr {
border-color: var(--md-accent-fg-color)
}
footer.sponsorship:not(:hover) .twemoji.heart-throb-hover svg {
color: var(--md-default-fg-color--lighter) !important
}
.twemoji.heart-throb svg,
.twemoji.heart-throb-hover svg {
position: relative;
color: var(--md-heart);
animation: pulse 1.5s ease infinite
}
@keyframes pulse {
0% {
transform: scale(1)
}
40% {
color: var(--md-heart-big);
transform: scale(1.3)
}
50% {
transform: scale(1.2)
}
60% {
color: var(--md-heart-big);
transform: scale(1.3)
}
100% {
transform: scale(1)
}
}
footer.sponsorship {
text-align: center
}
footer.sponsorship hr {
display: inline-block;
width: 1.6rem;
margin: 0 .7rem;
vertical-align: middle;
border-bottom: 2px solid var(--md-default-fg-color--lighter)
}
footer.sponsorship:hover hr {
border-color: var(--md-accent-fg-color)
}
footer.sponsorship:not(:hover) .twemoji.heart-throb-hover svg {
color: var(--md-default-fg-color--lighter) !important
}

View File

@ -34,21 +34,26 @@
"keyv": "^4.0.3", "keyv": "^4.0.3",
"leekslazylogger": "^2.0.7", "leekslazylogger": "^2.0.7",
"node-fetch": "^2.6.1", "node-fetch": "^2.6.1",
"sequelize": "^6.5.0",
"terminal-link": "^2.1.1" "terminal-link": "^2.1.1"
}, },
"devDependencies": {
"eslint": "^7.20.0",
"mariadb": "^2.5.2",
"mysql2": "^2.2.5",
"nodemon": "^2.0.7",
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
"tedious": "^11.0.3"
},
"optionalDependencies": { "optionalDependencies": {
"@keyv/sqlite": "^2.0.2" "sqlite3": "^5.0.2"
}, },
"peerDependencies": { "peerDependencies": {
"@keyv/mongo": "^1.1.0", "mariadb": "^2.5.2",
"@keyv/mysql": "^1.1.4", "mysql2": "^2.2.5",
"@keyv/postgres": "^1.0.16" "pg": "^8.5.1",
}, "pg-hstore": "^2.3.3",
"devDependencies": { "tedious": "^11.0.3"
"@keyv/mongo": "^1.1.0",
"@keyv/mysql": "^1.1.4",
"@keyv/postgres": "^1.0.16",
"eslint": "^7.20.0",
"nodemon": "^2.0.7"
} }
} }

File diff suppressed because it is too large Load Diff

42
src/database/dialects.js Normal file
View File

@ -0,0 +1,42 @@
module.exports = {
sqlite: {
name: 'SQLite',
dialect: 'sqlite',
packages: ['sqlite3'],
},
mysql: {
name: 'MySQL',
dialect: 'mysql',
packages: ['mysql2']
},
maria: {
name: 'MariaDB',
dialect: 'mariadb',
package: ['mariadb']
},
mariadb: {
name: 'MariaDB',
dialect: 'mariadb',
package: ['mariadb']
},
postgre: {
name: 'PostgreSQL',
dialect: 'postgres',
packages: ['pg', 'pg-hstore']
},
postgres: {
name: 'PostgreSQL',
dialect: 'postgres',
packages: ['pg', 'pg-hstore']
},
postgresql: {
name: 'PostgreSQL',
dialect: 'postgres',
packages: ['pg', 'pg-hstore']
},
microsoft: {
name: 'Microsoft SQL',
dialect: 'mssql',
packages: ['tedious']
},
};

126
src/database/index.js Normal file
View File

@ -0,0 +1,126 @@
const {
Sequelize,
DataTypes
} = require('sequelize');
const { path } = require('../utils/fs');
const config = require('../../user/config');
const types = require('./dialects');
const supported = Object.keys(types);
module.exports = async (log) => {
const {
DB_TYPE,
DB_HOST,
DB_USER,
DB_PASS,
DB_NAME
} = process.env;
let type = (DB_TYPE || 'sqlite').toLowerCase();
if (!supported.includes(type)) {
log.report('Invalid database type');
throw new Error(`DB_TYPE (${type}) is not a valid type`);
}
try {
types[type].packages.forEach(pkg => require(pkg));
} catch {
log.report('Specified database type is not installed');
let required = types[type].packages.map(i => `"${i}"`).join(' and ');
throw new Error(`Please install the package(s) for your selected database type: ${required}`);
}
let sequelize;
if (type === 'sqlite') {
log.info('Using SQLite storage');
sequelize = new Sequelize({
dialect: types[type].dialect,
storage: path('./user/database.sqlite'),
logging: log.debug
});
} else {
log.info(`Connecting to ${types[type].name} database...`);
sequelize = new Sequelize(DB_NAME, DB_USER, DB_PASS, {
dialect: types[type].dialect,
host: DB_HOST,
logging: log.debug
});
}
try {
await sequelize.authenticate();
log.success('Connected to database successfully');
} catch (error) {
log.warn('Unable to connect to database');
log.error(error);
}
/* let models = {};
let files = fs.readdirSync(path('src/database/models/')).filter(file => file.endsWith('.js'));
for (let file of files) {
let table = require(`./models/${file}`);
let model = sequelize.define(table.name, table.model);
models[table.name] = model;
} */
const Guild = sequelize.define('Guild', {
id: {
type: DataTypes.CHAR(18),
primaryKey: true
},
prefix: {
type: DataTypes.STRING, // STRING(255) = VARCHAR(255)
defaultValue: config.defaults.prefix
},
locale: {
type: DataTypes.STRING,
defaultValue: config.defaults.locale
}
}, {
tableName: 'guilds'
});
const Ticket = sequelize.define('Ticket', {
id: {
type: DataTypes.CHAR(18),
primaryKey: true
},
guild: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: Guild,
key: 'id'
},
}
}, {
tableName: 'tickets'
});
// eslint-disable-next-line no-unused-vars
const Message = sequelize.define('Message', {
id: {
type: DataTypes.CHAR(18),
primaryKey: true
},
ticket: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: Ticket,
key: 'id'
},
}
}, {
tableName: 'messages'
});
sequelize.sync();
return sequelize.models;
};

View File

@ -28,9 +28,11 @@ const { version } = require('../package.json');
const fs = require('fs'); const fs = require('fs');
const { path } = require('./utils/fs'); const { path } = require('./utils/fs');
if (!fs.existsSync(path('./.env'))) return console.log('Please make a copy of \'example.env\' called \'.env\''); if (!fs.existsSync(path('./.env'))) return console.log('Please make a copy of \'example.env\' called \'.env\'');
if (!fs.existsSync(path('./user/config.js'))) return console.log('Please make a copy of \'example.config.js\' called \'config.js\''); if (!fs.existsSync(path('./user/config.js'))) return console.log('Please make a copy of \'user/example.config.js\' called \'user/config.js\'');
require('dotenv').config(); require('dotenv').config({
path: path('./.env')
});
const config = require('../user/config'); const config = require('../user/config');
@ -43,7 +45,6 @@ const log = new Logger({
}); });
const terminalLink = require('terminal-link');
log.report = error => { log.report = error => {
let report = [ let report = [
'<< Issue report >>', '<< Issue report >>',
@ -57,6 +58,9 @@ log.report = error => {
if (error) log.error(error); if (error) log.error(error);
}; };
const terminalLink = require('terminal-link');
const I18n = require('@eartharoid/i18n');
const { Client } = require('discord.js'); const { Client } = require('discord.js');
class Bot extends Client { class Bot extends Client {
constructor() { constructor() {
@ -66,8 +70,9 @@ class Bot extends Client {
Object.assign(this, { Object.assign(this, {
config, config,
db: require('./modules/database')(log), db: require('./database')(log),
log, log,
i18n: new I18n(path('./src/locales'), 'en-GB')
}); });
this.log.info('Connecting to Discord API'); this.log.info('Connecting to Discord API');

1
src/locales/en-GB.json Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -1,75 +0,0 @@
const Keyv = require('keyv');
const { homepage } = require('../../package.json');
const { path } = require('../utils/fs');
const types = {
sqlite: 'sqlite',
mysql: 'mysql',
mongo: 'mongo',
mongodb: 'mongo',
postgre: 'postgres',
postgres: 'postgres',
postgresql: 'postgres',
};
const supported = Object.keys(types);
module.exports = (log) => {
let type = (process.env.DB_TYPE || 'sqlite').toLowerCase();
if (!supported.includes(type)) {
log.report('Invalid database type');
throw new Error(`DB_TYPE (${type}) is not a valid type`);
}
try {
require(`@keyv/${types[type]}`);
} catch {
log.report('Specified database type is not installed');
throw new Error(`"@keyv/${types[type]}" is not installed, please install is manually`);
}
const {
DB_HOST,
DB_USER,
DB_PASS,
DB_NAME
} = process.env;
let database;
switch (type) {
case 'mysql':
database = `mysql://${DB_USER}:${DB_PASS}@${DB_HOST}/${DB_NAME}`;
log.info('Using MySQL as database backend');
break;
case 'mongo':
case 'mongodb':
database = `mongodb://${DB_USER}:${DB_PASS}@${DB_HOST}/${DB_NAME}`;
log.info('Using MongoDB as database backend');
break;
case 'postgre':
case 'postgres':
case 'postgresql':
database = `postgresql://${DB_USER}:${DB_PASS}@${DB_HOST}/${DB_NAME}`;
log.info('Using Postgres as database backend');
break;
default: // sqlite
database = `sqlite://${path('./user/database.sqlite')}`;
log.info('Using SQLite as database backend');
break;
}
return {
servers: new Keyv(database, {
namespace: 'servers'
}),
tickets: new Keyv(database, {
namespace: 'tickets'
}),
messages: new Keyv(database, {
namespace: 'messages'
})
};
};

View File

@ -39,11 +39,12 @@ module.exports = {
{ {
activity: 'for new tickets | %shelp', activity: 'for new tickets | %shelp',
type: 'WATCHING' type: 'WATCHING'
} },
], ],
defaults: { defaults: {
prefix: '-', prefix: '-',
colour: '#009999', colour: '#009999',
locale: 'en-GB'
}, },
logs: { logs: {
enabled: true, enabled: true,