Updated models and migrations to new spec

This commit is contained in:
2022-08-17 19:36:47 +02:00
parent bc075805e2
commit 05804cb78a
22 changed files with 336 additions and 86 deletions

View File

@@ -34,6 +34,22 @@ Completed bands reward cosmetics and XP (scaled to band size)
Band levels by collecting duplicates (?) as a long term hook Band levels by collecting duplicates (?) as a long term hook
### Guild - Single guild the bot has joined
- int guildID
- int active | 0 inactive, 1 active, 2 banned/blocked
- int adminRoleId | role can configure server specific bot options
- date created
- date updated
### Core - Global bot settings
- bool maintenance
- int[] adminIDs
### User
- int userID
- int active | 0 inactive, 1 active, 2 banned/blocked
- int privacy | 0 public, 1 private
## Profile ## Profile
Steal profile layout from Amaan Steal profile layout from Amaan
with more customization with more customization

View File

@@ -12,4 +12,5 @@ If ran outside the container, DB_HOST must be set to localhost or the container'
```bash ```bash
$npx sequelize db:migrate $npx sequelize db:migrate
$npx sequelize db:seed:all
``` ```

View File

@@ -7,26 +7,19 @@ module.exports = {
.setName("collection") .setName("collection")
.setDescription("List all cards in your collection"), .setDescription("List all cards in your collection"),
async execute(interaction) { async execute(interaction) {
//fetch the user given the userID //fetch the user given the userID and include his cards
const user = await User.findOne({ const user = await User.findOne({
where: { where: {
userID: interaction.member.id discordId: interaction.member.id
}
});
//fetch all cards give the user id
const cards = await Card.findAll({
where: {
ownerID: user.id
}, },
include: [{ include: [{
model: Character, model: Card,
as: "character" include: [Character]
}] }]
}); });
//if the user has no cards, tell him //if the user has no cards, tell him
if (cards.length === 0) { if (user.Cards.length === 0) {
interaction.reply({ interaction.reply({
content: "You have no cards in your collection", content: "You have no cards in your collection",
ephemeral: true ephemeral: true
@@ -36,8 +29,14 @@ module.exports = {
//if the user has cards, list them //if the user has cards, list them
let message = ""; let message = "";
for (let i = 0; i < cards.length; i++) { for (let i = 0; i < user.Cards.length; i++) {
message += `${cards[i].id} - ${cards[i].characterID} ${cards[i].character.name} \n`; message += `${user.Cards[i].id} - ${user.Cards[i].characterId} \n`;
message += `Identifier: ${user.Cards[i].identifier} \n`;
message += `Name: ${user.Cards[i].Character.name} \n`;
message += `Image: ${user.Cards[i].Character.imageURL} \n`;
message += `Quality: ${user.Cards[i].quality} \n`;
message += `Print number: ${user.Cards[i].printNr} \n`;
message += `------------------------ \n`;
} }
interaction.reply({ interaction.reply({
content: message, content: message,

View File

@@ -1,5 +1,6 @@
const { SlashCommandBuilder } = require("@discordjs/builders"); const { SlashCommandBuilder } = require("@discordjs/builders");
const { Card, User } = require("../models"); const { Card, User } = require("../models");
const { customAlphabet } = require("nanoid");
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@@ -16,15 +17,25 @@ module.exports = {
//get user id from database given the userID //get user id from database given the userID
const user = await User.findOne({ const user = await User.findOne({
where: { where: {
userID: interaction.member.id discordId: interaction.member.id
} }
}); });
//create new card with the given character id, and the user id //create new card with the given character id, and the user id
const nanoid = customAlphabet('23456789ABCDEFGHJKLMNPRSTUVWXYZ',6); //Up to 887.503.681
const identifier = nanoid();
const existingCharacterCount = await Card.count({
where: {
characterId: interaction.options.getInteger("id")
}
});
const card = await Card.create({ const card = await Card.create({
characterID: interaction.options.getInteger("id"), characterId: interaction.options.getInteger("id"),
identifier: "00000", identifier: identifier,
ownerID: user.id, quality: 1,
printNr: existingCharacterCount + 1,
userId: user.id
}); });
//reply with the new card id //reply with the new card id

View File

@@ -9,7 +9,7 @@ module.exports = {
async execute(interaction) { async execute(interaction) {
let user = await User.findOne({ let user = await User.findOne({
where: { where: {
userID: interaction.user.id discordId: interaction.user.id
} }
}); });
if (user) { if (user) {
@@ -19,9 +19,8 @@ module.exports = {
}); });
} else { } else {
await User.create({ await User.create({
userID: interaction.user.id, discordId: interaction.user.id,
banned: false, active: 1,
registredAt: new Date()
}); });
interaction.reply({ interaction.reply({
content: "You are now registered", content: "You are now registered",

View File

@@ -10,7 +10,7 @@ module.exports = {
let users = await User.findAll(); let users = await User.findAll();
let userList = ""; let userList = "";
for (let i = 0; i < users.length; i++) { for (let i = 0; i < users.length; i++) {
let username = await interaction.client.users.fetch(users[i].userID); let username = await interaction.client.users.fetch(users[i].discordId);
userList += `${username.username}#${username.discriminator}`; userList += `${username.username}#${username.discriminator}`;
} }
if (userList === "") { if (userList === "") {

View File

@@ -12,15 +12,16 @@ module.exports = {
//check if guild exists in database //check if guild exists in database
let guildData = await Guild.findOne({ let guildData = await Guild.findOne({
where: { where: {
guildID: guild.id guildId: guild.id
} }
}); });
if (!guildData) { if (!guildData) {
//create guild in database //create guild in database
await Guild.create({ await Guild.create({
guildID: guild.id, guildId: guild.id,
ownerID: guild.ownerId, adminRoleId: null,
active: 1
}); });
//send guild registered message to the interations channel //send guild registered message to the interations channel
interaction.channel.send({ interaction.channel.send({
@@ -32,7 +33,7 @@ module.exports = {
//check if the user exists in the database, if not tell him to use the /register command //check if the user exists in the database, if not tell him to use the /register command
let user = await User.findOne({ let user = await User.findOne({
where: { where: {
userID: interaction.member.id discordId: interaction.member.id
} }
}); });
if (!user && interaction.commandName !== "register") { if (!user && interaction.commandName !== "register") {
@@ -52,8 +53,8 @@ module.exports = {
} catch (err) { } catch (err) {
if (err) console.log(err); if (err) console.log(err);
await interaction.reply({ await interaction.reply({
content: "An error occured processing the command", content: `An error occured processing the command :(\n \`\`\`${JSON.stringify(err, null, 2)}\`\`\``,
ephemeral: true ephemeral: false
}); });
} }
} }

View File

@@ -0,0 +1,40 @@
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Bands', {
id: {
allowNull: false,
autoIncrement: false,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
description: {
type: Sequelize.TEXT
},
imageURL: {
type: Sequelize.STRING
},
enabled: {
allowNull: false,
type: Sequelize.BOOLEAN,
defaultValue: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Bands');
}
};

View File

@@ -8,22 +8,27 @@ module.exports = {
primaryKey: true, primaryKey: true,
type: Sequelize.INTEGER type: Sequelize.INTEGER
}, },
guildID: { guildId: {
type: Sequelize.STRING type: Sequelize.BIGINT,
allowNull: false
}, },
adminRoleID: { adminRoleId: {
type: Sequelize.STRING type: Sequelize.BIGINT,
}, },
owderID: { active: {
type: Sequelize.STRING type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 1
}, },
createdAt: { createdAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}, },
updatedAt: { updatedAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
} }
}); });
}, },

View File

@@ -8,23 +8,29 @@ module.exports = {
primaryKey: true, primaryKey: true,
type: Sequelize.INTEGER type: Sequelize.INTEGER
}, },
userID: { discordId: {
type: Sequelize.STRING, type: Sequelize.BIGINT,
allowNull: false allowNull: false
}, },
banned: { active: {
type: Sequelize.BOOLEAN type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 1
}, },
registredAt: { privacy: {
type: Sequelize.DATE type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0
}, },
createdAt: { createdAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}, },
updatedAt: { updatedAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
} }
}); });
}, },

View File

@@ -4,32 +4,41 @@ module.exports = {
await queryInterface.createTable('Characters', { await queryInterface.createTable('Characters', {
id: { id: {
allowNull: false, allowNull: false,
autoIncrement: true, autoIncrement: false,
primaryKey: true, primaryKey: true,
type: Sequelize.INTEGER type: Sequelize.INTEGER
}, },
bandId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Bands',
key: 'id'
}
},
name: { name: {
type: Sequelize.STRING type: Sequelize.STRING
}, },
rarity: {
type: Sequelize.INTEGER
},
description: { description: {
type: Sequelize.STRING type: Sequelize.TEXT
}, },
imageURL: { imageURL: {
type: Sequelize.STRING type: Sequelize.STRING
}, },
imageHash: { enabled: {
type: Sequelize.STRING allowNull: false,
type: Sequelize.BOOLEAN,
defaultValue: false
}, },
createdAt: { createdAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}, },
updatedAt: { updatedAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
} }
}); });
}, },

View File

@@ -10,9 +10,10 @@ module.exports = {
}, },
identifier: { identifier: {
allowNull: false, allowNull: false,
type: Sequelize.STRING type: Sequelize.STRING,
unique: true
}, },
characterID: { characterId: {
allowNull: false, allowNull: false,
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
references: { references: {
@@ -20,7 +21,11 @@ module.exports = {
key: 'id' key: 'id'
} }
}, },
ownerID: { quality: {
allowNull: false,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: false, allowNull: false,
references: { references: {
@@ -28,17 +33,27 @@ module.exports = {
key: 'id' key: 'id'
} }
}, },
imageHash: {
type: Sequelize.STRING
},
enabled: { enabled: {
allowNull: false, allowNull: false,
type: Sequelize.BOOLEAN type: Sequelize.BOOLEAN,
defaultValue: true
},
printNr: {
allowNull: false,
type: Sequelize.INTEGER,
}, },
createdAt: { createdAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}, },
updatedAt: { updatedAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
} }
}); });
}, },

View File

@@ -0,0 +1,36 @@
'use strict';
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('Bot', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
maintenance: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false
},
adminIDs: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
});
},
async down (queryInterface, Sequelize) {
await queryInterface.dropTable('Bot');
}
};

28
models/band.js Normal file
View File

@@ -0,0 +1,28 @@
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Band extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
Band.hasMany(models.Character);
}
}
Band.init({
uniqueId: DataTypes.INTEGER,
name: DataTypes.STRING,
description: DataTypes.TEXT,
imageURL: DataTypes.STRING,
enabled: DataTypes.BOOLEAN
}, {
sequelize,
modelName: 'Band',
});
return Band;
};

24
models/bot.js Normal file
View File

@@ -0,0 +1,24 @@
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Bot extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
}
Bot.init({
maintenance: DataTypes.BOOLEAN,
adminIDs: DataTypes.STRING
}, {
sequelize,
modelName: 'Guild',
});
return Bot;
};

View File

@@ -10,22 +10,18 @@ module.exports = (sequelize, DataTypes) => {
* The `models/index` file will call this method automatically. * The `models/index` file will call this method automatically.
*/ */
static associate(models) { static associate(models) {
Card.belongsTo(models.Character, { Card.belongsTo(models.User);
foreignKey: 'characterID', Card.belongsTo(models.Character);
as: 'character'
});
Card.belongsTo(models.User, {
foreignKey: 'ownerID',
key: 'userId',
as: 'owner'
});
} }
} }
Card.init({ Card.init({
ownerID: DataTypes.STRING,
identifier: DataTypes.STRING, identifier: DataTypes.STRING,
characterID: DataTypes.INTEGER, characterId: DataTypes.INTEGER,
enabled: { type: DataTypes.BOOLEAN, defaultValue: true } quality: DataTypes.INTEGER,
userId: DataTypes.STRING,
imageHash: DataTypes.STRING,
enabled: { type: DataTypes.BOOLEAN, defaultValue: true },
printNr: DataTypes.INTEGER,
}, { }, {
sequelize, sequelize,
modelName: 'Card', modelName: 'Card',

View File

@@ -10,18 +10,16 @@ module.exports = (sequelize, DataTypes) => {
* The `models/index` file will call this method automatically. * The `models/index` file will call this method automatically.
*/ */
static associate(models) { static associate(models) {
Character.hasMany(models.Card, { Character.belongsToMany(models.Card, { through: 'CardCharacter' });
foreignKey: 'characterID', Character.belongsTo(models.Band, { foreignKey: 'bandId', });
as: 'cards'
});
} }
} }
Character.init({ Character.init({
name: DataTypes.STRING, name: DataTypes.STRING,
rarity: DataTypes.INTEGER, bandId: DataTypes.INTEGER,
description: DataTypes.STRING,
imageURL: DataTypes.STRING, imageURL: DataTypes.STRING,
imageHash: DataTypes.STRING description: DataTypes.TEXT,
enabled: DataTypes.BOOLEAN
}, { }, {
sequelize, sequelize,
modelName: 'Character', modelName: 'Character',

View File

@@ -14,9 +14,9 @@ module.exports = (sequelize, DataTypes) => {
} }
} }
Guild.init({ Guild.init({
guildID: DataTypes.STRING, guildId: DataTypes.BIGINT,
adminRoleID: DataTypes.STRING, adminRoleId: DataTypes.BIGINT,
owderID: DataTypes.STRING active: DataTypes.INTEGER,
}, { }, {
sequelize, sequelize,
modelName: 'Guild', modelName: 'Guild',

View File

@@ -11,12 +11,13 @@ module.exports = (sequelize, DataTypes) => {
*/ */
static associate(models) { static associate(models) {
// define association here // define association here
User.hasMany(models.Card);
} }
} }
User.init({ User.init({
userID: DataTypes.STRING, discordId: DataTypes.BIGINT,
banned: DataTypes.BOOLEAN, active: DataTypes.INTEGER,
registredAt: DataTypes.DATE privacy: DataTypes.INTEGER
}, { }, {
sequelize, sequelize,
modelName: 'User', modelName: 'User',

17
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"discord.js": "^13.6.0", "discord.js": "^13.6.0",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"nanoid": "^3.0.0",
"nodemon": "^2.0.15", "nodemon": "^2.0.15",
"sequelize": "^6.19.0", "sequelize": "^6.19.0",
"sequelize-cli": "^6.4.1" "sequelize-cli": "^6.4.1"
@@ -1401,6 +1402,17 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}, },
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/next-tick": { "node_modules/next-tick": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
@@ -3319,6 +3331,11 @@
} }
} }
}, },
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
},
"next-tick": { "next-tick": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",

View File

@@ -15,6 +15,7 @@
"discord.js": "^13.6.0", "discord.js": "^13.6.0",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"nanoid": "^3.0.0",
"nodemon": "^2.0.15", "nodemon": "^2.0.15",
"sequelize": "^6.19.0", "sequelize": "^6.19.0",
"sequelize-cli": "^6.4.1" "sequelize-cli": "^6.4.1"

View File

@@ -0,0 +1,47 @@
'use strict';
module.exports = {
async up (queryInterface, Sequelize) {
/**
* Add seed commands here.
*
* Example:
* await queryInterface.bulkInsert('People', [{
* name: 'John Doe',
* isBetaMember: false
* }], {});
*/
await queryInterface.bulkInsert('Bands', [{
id: 1,
name: 'BAND-MAID',
description: 'Band-Maid (stylized as BAND-MAID) is an all girl rock band from Tokyo that formed in July 2013. The band combines a rock sound with a maid image modeled on Japanese maid cafés.',
imageURL: 'https://cdn.discordapp.com/attachments/851543504831119380/1009467684490063892/unknown.png',
enabled: true
}]);
await queryInterface.bulkInsert('Characters', [{
id: 1,
bandId: 1,
name: 'Miku Kobato',
description: 'Miku Kobato is a Japanese singer, songwriter and guitarist. She is the initial founding member and main lyricist for BAND-MAID.',
imageURL: 'https://cdn.discordapp.com/attachments/851543504831119380/1009468495077048330/unknown.png',
enabled: true
},
{
id: 2,
bandId: 1,
name: 'Akane Hirose',
description: 'Akane Hirose is a Japanese drummer and founding member of BAND-MAID.',
imageURL: 'https://cdn.discordapp.com/attachments/851543504831119380/1009469985296490568/unknown.png',
enabled: true
}]);
},
async down (queryInterface, Sequelize) {
/**
* Add commands to revert seed here.
*
* Example:
* await queryInterface.bulkDelete('People', null, {});
*/
}
};