* ci: 💚 remove arm64 build (#497) (#498)

I had to remove arm64 platform from the images since prisma client do not seem to support arm64 yet

* User Experience Improvements (#507)

* ci: 💚 remove arm64 build (#497)

I had to remove arm64 platform from the images since prisma client do not seem to support arm64 yet

* Update dependency prettier to v2.8.0 (#502)

* fix: 📦 add missing package prisma

* ci: 💚 remove arm64 build (#497) (#498) (#499)

I had to remove arm64 platform from the images since prisma client do not seem to support arm64 yet

* Update Dockerfile

* Update schema.prisma

* Configure Renovate (#500)

* Add renovate.json

* Format code with prettier

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>

* Update dependency prettier to v2.8.0

Co-authored-by: Vermium Sifell <vermium@zyner.org>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>

* Update dependency @types/uuid to v9 (#503)

* fix: 📦 add missing package prisma

* ci: 💚 remove arm64 build (#497) (#498) (#499)

I had to remove arm64 platform from the images since prisma client do not seem to support arm64 yet

* Update Dockerfile

* Update schema.prisma

* Configure Renovate (#500)

* Add renovate.json

* Format code with prettier

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>

* Fix invite URL (#504)

* Update dependency @types/uuid to v9

Co-authored-by: Vermium Sifell <vermium@zyner.org>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
Co-authored-by: Kreusada <67752638+Kreusada@users.noreply.github.com>

* Update dependency lint-staged to v13.0.4 (#501)

* fix: 📦 add missing package prisma

* ci: 💚 remove arm64 build (#497) (#498) (#499)

I had to remove arm64 platform from the images since prisma client do not seem to support arm64 yet

* Update Dockerfile

* Update schema.prisma

* Configure Renovate (#500)

* Add renovate.json

* Format code with prettier

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>

* Fix invite URL (#504)

* Update dependency lint-staged to v13.0.4

Co-authored-by: Vermium Sifell <vermium@zyner.org>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
Co-authored-by: Kreusada <67752638+Kreusada@users.noreply.github.com>

* It's all about User Experience (#505)

* chore(ux): 🚸 Improved credits balance messages

Improved how messages is written by using "you" form and make it less cluttered

* feat(ux): 🚸 Added version to utility stats

Added a running version field to utility stats to show hosters and their users if they support commands that may is too new for their running version

* chore(nodejs): 🔖 change version to v1.1.0

This is to make sure that package.json has corrcet version tagged inside of it

* chore(ux): 🚸 updated "on cooldown" message

Makes more sense to talk in a more positive tone than a negative tone

* style(ux): 🚚 utility => utils

Renaming utility category to utils makes it easier for users to retype commands

* chore(ux): 🚸 Improved utils about

"/utils about" now shows information about the current bot instance, such as uptime, total users and guilds and version

Removed information about Xyter in the bot. Instead users are expected to go to out documentation to learn more

* chore(ux): 🚸 Improved /utils avatar

Improved messages for /utils avatar and added a link for people to know that they could download the image

* refactor(ux): 🚸 Removed /utils ping

Removed /utils ping since /utils about now includes latency related information

Merged /utils ping into /utils about

* refactor(ux): 🚸 Removed /utils stats

Removed /utils stats since now /utils about includes information about bot instance

Merged /utils stats into /utils about

* chore(ux): 🚸 Improved /fun meme

Improved UX for /fun meme and added a link button for viewing the post

* chore(ux): 🚸 /reputation view => /reputation check and improved UX

Renamed /reputation view to /reputation check as it makes more sense, also improved messages for better user experience

Moved /reputation view to /reputation check

* chore(ux): 🚸 Moved /reputation give => /reputation repute and improved UX

/reputation repute makes more sense than /reputation give and I also updated messages to be more user engaging

Moved /reputation give to /reputation repute

* chore(ux): 🚸 Improved error messages

I changed so description would be the error message instead of the error object. I also updated the title and added a report problem button that links to the official discord server

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
Co-authored-by: Kreusada <67752638+Kreusada@users.noreply.github.com>

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
Co-authored-by: Kreusada <67752638+Kreusada@users.noreply.github.com>
This commit is contained in:
Axel Olausson Holtenäs 2022-12-04 17:48:25 +01:00 committed by GitHub
parent 461d254f0b
commit 7b34763660
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 291 additions and 337 deletions

96
package-lock.json generated
View file

@ -25,7 +25,7 @@
"devDependencies": {
"@types/chance": "1.1.3",
"@types/node-schedule": "2.1.0",
"@types/uuid": "8.3.4",
"@types/uuid": "9.0.0",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"eslint": "^8.26.0",
@ -33,9 +33,9 @@
"eslint-plugin-import": "2.26.0",
"eslint-plugin-no-loops": "0.3.0",
"eslint-plugin-prettier": "4.2.1",
"lint-staged": "13.0.3",
"lint-staged": "13.0.4",
"nodemon": "2.0.20",
"prettier": "2.7.1",
"prettier": "2.8.0",
"prisma": "^4.5.0",
"semantic-release": "^19.0.5"
}
@ -845,9 +845,9 @@
"dev": true
},
"node_modules/@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==",
"dev": true
},
"node_modules/@types/ws": {
@ -3974,9 +3974,9 @@
}
},
"node_modules/lilconfig": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz",
"integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
"dev": true,
"engines": {
"node": ">=10"
@ -3989,24 +3989,24 @@
"dev": true
},
"node_modules/lint-staged": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz",
"integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==",
"version": "13.0.4",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.4.tgz",
"integrity": "sha512-HxlHCXoYRsq9QCby5wFozmZW00hMs/9e3l+/dz6Qr8Kle4UH0kJTdABAbqhzG+3pcG6QjL9kz7NgGBfph+a5dw==",
"dev": true,
"dependencies": {
"cli-truncate": "^3.1.0",
"colorette": "^2.0.17",
"commander": "^9.3.0",
"colorette": "^2.0.19",
"commander": "^9.4.1",
"debug": "^4.3.4",
"execa": "^6.1.0",
"lilconfig": "2.0.5",
"listr2": "^4.0.5",
"lilconfig": "2.0.6",
"listr2": "^5.0.5",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"object-inspect": "^1.12.2",
"pidtree": "^0.6.0",
"string-argv": "^0.3.1",
"yaml": "^2.1.1"
"yaml": "^2.1.3"
},
"bin": {
"lint-staged": "bin/lint-staged.js"
@ -4019,22 +4019,22 @@
}
},
"node_modules/listr2": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz",
"integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz",
"integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==",
"dev": true,
"dependencies": {
"cli-truncate": "^2.1.0",
"colorette": "^2.0.16",
"colorette": "^2.0.19",
"log-update": "^4.0.0",
"p-map": "^4.0.0",
"rfdc": "^1.3.0",
"rxjs": "^7.5.5",
"rxjs": "^7.5.7",
"through": "^2.3.8",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
"node": "^14.13.1 || >=16.0.0"
},
"peerDependencies": {
"enquirer": ">= 2.3.0 < 3"
@ -7779,9 +7779,9 @@
}
},
"node_modules/prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz",
"integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
@ -10327,9 +10327,9 @@
"dev": true
},
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==",
"dev": true
},
"@types/ws": {
@ -12640,9 +12640,9 @@
}
},
"lilconfig": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz",
"integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
"dev": true
},
"lines-and-columns": {
@ -12652,38 +12652,38 @@
"dev": true
},
"lint-staged": {
"version": "13.0.3",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz",
"integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==",
"version": "13.0.4",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.4.tgz",
"integrity": "sha512-HxlHCXoYRsq9QCby5wFozmZW00hMs/9e3l+/dz6Qr8Kle4UH0kJTdABAbqhzG+3pcG6QjL9kz7NgGBfph+a5dw==",
"dev": true,
"requires": {
"cli-truncate": "^3.1.0",
"colorette": "^2.0.17",
"commander": "^9.3.0",
"colorette": "^2.0.19",
"commander": "^9.4.1",
"debug": "^4.3.4",
"execa": "^6.1.0",
"lilconfig": "2.0.5",
"listr2": "^4.0.5",
"lilconfig": "2.0.6",
"listr2": "^5.0.5",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"object-inspect": "^1.12.2",
"pidtree": "^0.6.0",
"string-argv": "^0.3.1",
"yaml": "^2.1.1"
"yaml": "^2.1.3"
}
},
"listr2": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz",
"integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz",
"integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==",
"dev": true,
"requires": {
"cli-truncate": "^2.1.0",
"colorette": "^2.0.16",
"colorette": "^2.0.19",
"log-update": "^4.0.0",
"p-map": "^4.0.0",
"rfdc": "^1.3.0",
"rxjs": "^7.5.5",
"rxjs": "^7.5.7",
"through": "^2.3.8",
"wrap-ansi": "^7.0.0"
},
@ -15320,9 +15320,9 @@
"dev": true
},
"prettier": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
"integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz",
"integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==",
"dev": true
},
"prettier-linter-helpers": {

View file

@ -1,6 +1,6 @@
{
"name": "xyter",
"version": "3.1.0",
"version": "v1.1.0",
"description": "Earn credits while chatting! And more",
"main": "dist/index.js",
"scripts": {
@ -49,7 +49,7 @@
"devDependencies": {
"@types/chance": "1.1.3",
"@types/node-schedule": "2.1.0",
"@types/uuid": "8.3.4",
"@types/uuid": "9.0.0",
"@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0",
"eslint": "^8.26.0",
@ -57,9 +57,9 @@
"eslint-plugin-import": "2.26.0",
"eslint-plugin-no-loops": "0.3.0",
"eslint-plugin-prettier": "4.2.1",
"lint-staged": "13.0.3",
"lint-staged": "13.0.4",
"nodemon": "2.0.20",
"prettier": "2.7.1",
"prettier": "2.8.0",
"prisma": "^4.5.0",
"semantic-release": "^19.0.5"
},

View file

@ -9,11 +9,9 @@ import logger from "../../../../middlewares/logger";
export const builder = (command: SlashCommandSubcommandBuilder) => {
return command
.setName("balance")
.setDescription(`View a user's balance`)
.setDescription(`Check balance`)
.addUserOption((option) =>
option
.setName("target")
.setDescription(`The user whose balance you want to view`)
option.setName("target").setDescription(`Account you want to check`)
);
};
@ -24,15 +22,15 @@ export const execute = async (interaction: CommandInteraction) => {
// 2. Destructure interaction object.
const { options, user, guild } = interaction;
if (!guild) throw new Error("Guild not found");
if (!user) throw new Error("User not found");
if (!options) throw new Error("Options not found");
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
if (!options) throw new Error("Options unavailable");
// 3. Get options from interaction.
const target = options.getUser("target");
// 4. Create base embeds.
const EmbedSuccess = await BaseEmbedSuccess(guild, "[:dollar:] Balance");
const EmbedSuccess = await BaseEmbedSuccess(guild, ":credit_card:︱Balance");
// 5. Upsert the user in the database.
const createGuildMember = await prisma.guildMember.upsert({
@ -71,15 +69,14 @@ export const execute = async (interaction: CommandInteraction) => {
},
});
logger.silly(createGuildMember);
if (!createGuildMember) throw new Error("No guild member exists.");
// 6. Send embed.
await interaction.editReply({
embeds: [
EmbedSuccess.setDescription(
target
? `${target} has ${createGuildMember.creditsEarned} credits.`
: `You have ${createGuildMember.creditsEarned} credits.`
? `${target} has ${createGuildMember.creditsEarned} coins in his account.`
: `You have ${createGuildMember.creditsEarned} coins in your account.`
),
],
});

View file

@ -1,5 +1,8 @@
import axios from "axios";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
@ -10,15 +13,15 @@ import cooldown from "../../../../middlewares/cooldown";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("meme").setDescription("Get a meme from r/memes)");
return command.setName("meme").setDescription("Random memes from r/memes");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { guild, user, commandId } = interaction;
if (!guild) throw new Error("Guild not found");
if (!user) throw new Error("User not found");
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
await cooldown(guild, user, commandId, 15);
@ -30,35 +33,25 @@ export default {
const response = res.data[0].data.children;
const content = response[0].data;
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("View post")
.setStyle(ButtonStyle.Link)
.setEmoji("🔗")
.setURL(`https://reddit.com${content.permalink}`)
);
const embed = new EmbedBuilder()
.setAuthor({
name: content.title,
iconURL:
"https://www.redditinc.com/assets/images/site/reddit-logo.png",
url: `https://reddit.com${content.permalink}`,
})
.setTitle("[:sweat_smile:] Meme")
.addFields([
{
name: "Author",
value: `[${content.author}](https://reddit.com/user/${content.author})`,
inline: true,
},
{
name: "Votes",
value: `${content.ups}/${content.downs}`,
inline: true,
},
])
.setTitle(`😆︱Meme`)
.setDescription(`**${content.title}**`)
.setTimestamp(new Date())
.setImage(content.url)
.setFooter({
text: embedConfig.footerText,
iconURL: embedConfig.footerIcon,
text: `👍 ${content.ups}︱👎 ${content.downs}`,
})
.setColor(embedConfig.successColor);
await interaction.editReply({ embeds: [embed] });
await interaction.editReply({ embeds: [embed], components: [buttons] });
return;
})
.catch((error) => {

View file

@ -2,27 +2,27 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleGive from "./modules/give";
import moduleView from "./modules/view";
import moduleCheck from "./modules/check";
import moduleRepute from "./modules/repute";
// Function
export const builder = new SlashCommandBuilder()
.setName("reputation")
.setDescription("Manage reputation.")
.setDescription("See and repute users to show other how trustworthy they are")
.setDMPermission(false)
// Modules
.addSubcommand(moduleGive.builder)
.addSubcommand(moduleView.builder);
.addSubcommand(moduleRepute.builder)
.addSubcommand(moduleCheck.builder);
// Execute function
export const execute = async (interaction: ChatInputCommandInteraction) => {
if (interaction.options.getSubcommand() === "give") {
await moduleGive.execute(interaction);
if (interaction.options.getSubcommand() === "repute") {
await moduleRepute.execute(interaction);
return;
}
if (interaction.options.getSubcommand() === "view") {
await moduleView.execute(interaction);
if (interaction.options.getSubcommand() === "check") {
await moduleCheck.execute(interaction);
return;
}
};

View file

@ -11,33 +11,33 @@ import logger from "../../../../middlewares/logger";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("view")
.setDescription("View a user's reputation value")
.setName("check")
.setDescription("Check reputation")
.addUserOption((option) =>
option
.setName("target")
.setDescription("The user you want to check.")
.setRequired(true)
.setName("account")
.setDescription("The account you checking")
.setRequired(false)
);
},
execute: async (interaction: ChatInputCommandInteraction) => {
await deferReply(interaction, true);
const { options, guild } = interaction;
const { options, guild, user } = interaction;
const { successColor, footerText, footerIcon } = await getEmbedConfig(
guild
);
const optionTarget = options?.getUser("target");
const optionAccount = options?.getUser("account");
if (!guild) throw new Error("Guild is undefined");
if (!optionTarget) throw new Error("Target is not defined");
if (!guild) throw new Error("Server unavailable");
if (!user) throw new Error("User unavailable");
const createGuildMember = await prisma.guildMember.upsert({
where: {
userId_guildId: {
userId: optionTarget.id,
userId: (optionAccount || user).id,
guildId: guild.id,
},
},
@ -46,10 +46,10 @@ export default {
user: {
connectOrCreate: {
create: {
id: optionTarget.id,
id: (optionAccount || user).id,
},
where: {
id: optionTarget.id,
id: (optionAccount || user).id,
},
},
},
@ -72,10 +72,26 @@ export default {
logger.silly(createGuildMember);
const reputationType = async (reputation: number) => {
if (reputation > 0) return `negative reputation of ${reputation}`;
if (reputation < 0) return `positive reputation of ${reputation}`;
return "neutral reputation";
};
const interactionEmbed = new EmbedBuilder()
.setTitle("[:loudspeaker:] View")
.setTitle(
optionAccount
? `:loudspeaker:︱Showing ${optionAccount.username}'s reputation`
: ":loudspeaker:︱Showing your reputation"
)
.setDescription(
`${optionTarget} has ${createGuildMember.user.reputationsEarned}.`
optionAccount
? `${optionAccount} have a ${await reputationType(
createGuildMember.user.reputationsEarned
)}`
: `You have a ${await reputationType(
createGuildMember.user.reputationsEarned
)}`
)
.setTimestamp()
.setColor(successColor)

View file

@ -2,6 +2,6 @@ import { User } from "discord.js";
export default (to: User | null, from: User | null) => {
if (from?.id === to?.id) {
throw new Error("You cannot give reputation to yourself.");
throw new Error("You can only repute other users");
}
};

View file

@ -14,18 +14,18 @@ import cooldown from "../../../../middlewares/cooldown";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("give")
.setDescription("Give reputation to a user")
.setName("repute")
.setDescription("Repute an account")
.addUserOption((option) =>
option
.setName("target")
.setDescription("The user you want to repute.")
.setName("account")
.setDescription("The account you repute")
.setRequired(true)
)
.addStringOption((option) =>
option
.setName("type")
.setDescription("What type of reputation you want to repute")
.setDescription("Type of reputation")
.setRequired(true)
.addChoices(
{ name: "Positive", value: "positive" },
@ -45,14 +45,14 @@ export default {
guild
);
const optionTarget = options?.getUser("target");
const optionAccount = options?.getUser("account");
const optionType = options?.getString("type");
if (!guild) throw new Error("Guild is undefined");
if (!optionTarget) throw new Error("Target is not defined");
if (!guild) throw new Error("Server unavailable");
if (!optionAccount) throw new Error("User unavailable");
// Pre-checks
noSelfReputation(optionTarget, user);
noSelfReputation(optionAccount, user);
// Check if user is on cooldown otherwise create one
await cooldown(
@ -66,7 +66,7 @@ export default {
case "positive": {
const createUser = await prisma.user.upsert({
where: {
id: optionTarget.id,
id: optionAccount.id,
},
update: {
reputationsEarned: {
@ -74,7 +74,7 @@ export default {
},
},
create: {
id: optionTarget.id,
id: optionAccount.id,
reputationsEarned: 1,
},
});
@ -85,7 +85,7 @@ export default {
case "negative": {
const createUser = await prisma.user.upsert({
where: {
id: optionTarget.id,
id: optionAccount.id,
},
update: {
reputationsEarned: {
@ -93,7 +93,7 @@ export default {
},
},
create: {
id: optionTarget.id,
id: optionAccount.id,
reputationsEarned: -1,
},
});
@ -107,9 +107,9 @@ export default {
}
const interactionEmbed = new EmbedBuilder()
.setTitle("[:loudspeaker:] Give")
.setTitle(`:loudspeaker:︱Reputing ${optionAccount.username}`)
.setDescription(
`You have given a ${optionType} repute to ${optionTarget}`
`You have given a ${optionType} repute to ${optionAccount}!`
)
.setTimestamp()
.setColor(successColor)

View file

@ -1,75 +0,0 @@
// Dependencies
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("about").setDescription("About this bot!)");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Source Code")
.setStyle(ButtonStyle.Link)
.setEmoji("📄")
.setURL("https://github.com/ZynerOrg/xyter"),
new ButtonBuilder()
.setLabel("Documentation")
.setStyle(ButtonStyle.Link)
.setEmoji("📚")
.setURL("https://xyter.zyner.org"),
new ButtonBuilder()
.setLabel("Website")
.setStyle(ButtonStyle.Link)
.setEmoji("🌐")
.setURL("https://zyner.org"),
new ButtonBuilder()
.setLabel("Get Help")
.setStyle(ButtonStyle.Link)
.setEmoji("💬")
.setURL("https://discord.zyner.org"),
new ButtonBuilder()
.setLabel(`Hosted by ${process.env.BOT_HOSTER_NAME}`)
.setStyle(ButtonStyle.Link)
.setEmoji("⚒️")
.setURL(`${process.env.BOT_HOSTER_URL}`)
);
const interactionEmbed = new EmbedBuilder()
.setColor(successColor)
.setTitle("[:tools:] About")
.setDescription(
`
**Xyter**'s goal is to provide a __privacy-friendly__ discord bot.
We created **Xyter** to **replace the mess** of having a dozen or so bots in __your__ community.
On top of this, you can also see our **source code** for **security** and **privacy** issues.
As well as making your own **fork** of the bot, you can also get **help** from our community.
Developed with by **Zyner**, a non-profit project by teens.
`
)
.setTimestamp()
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
components: [buttons],
});
},
};

View file

@ -1,45 +0,0 @@
// Dependencies
import {
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
// Configurations
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("ping").setDescription("Ping this bot");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const interactionEmbed = new EmbedBuilder()
.setTitle("[:tools:] Ping")
.addFields(
{
name: "📦 Deliver Latency",
value: `${Math.abs(Date.now() - interaction.createdTimestamp)} ms`,
inline: true,
},
{
name: "🤖 API Latency",
value: `${Math.round(interaction.client.ws.ping)} ms`,
inline: true,
}
)
.setTimestamp()
.setColor(successColor)
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
});
},
};

View file

@ -1,71 +0,0 @@
import {
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
import getEmbedConfig from "../../../../helpers/getEmbedData";
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command.setName("stats").setDescription("Check bot statistics!)");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { client } = interaction;
if (client?.uptime === null) return;
let totalSeconds = client?.uptime / 1000;
const days = Math?.floor(totalSeconds / 86400);
totalSeconds %= 86400;
const hours = Math?.floor(totalSeconds / 3600);
totalSeconds %= 3600;
const minutes = Math?.floor(totalSeconds / 60);
const seconds = Math?.floor(totalSeconds % 60);
const uptime = `${days} days, ${hours} hours, ${minutes} minutes and ${seconds} seconds`;
const interactionEmbed = new EmbedBuilder()
.setColor(successColor)
.setTitle("[:hammer:] Stats")
.setDescription("Below you can see a list of statistics about the bot.")
.setTimestamp()
.addFields(
{
name: "⏰ Latency",
value: `${Date?.now() - interaction?.createdTimestamp} ms`,
inline: true,
},
{
name: "⏰ API Latency",
value: `${Math?.round(client?.ws?.ping)} ms`,
inline: true,
},
{
name: "⏰ Uptime",
value: `${uptime}`,
inline: false,
},
{
name: "📈 Guilds",
value: `${client?.guilds?.cache?.size}`,
inline: true,
},
{
name: "📈 Users (non-unique)",
value: `${client?.guilds?.cache?.reduce(
(acc, guild) => acc + guild?.memberCount,
0
)}`,
inline: true,
}
)
.setFooter({ text: footerText, iconURL: footerIcon });
interaction?.editReply({ embeds: [interactionEmbed] });
},
};

View file

@ -3,18 +3,14 @@ import { ChatInputCommandInteraction, SlashCommandBuilder } from "discord.js";
// Modules
import moduleAbout from "./modules/about";
import moduleAvatar from "./modules/avatar";
import modulePing from "./modules/ping";
import moduleStats from "./modules/stats";
export const builder = new SlashCommandBuilder()
.setName("utility")
.setName("utils")
.setDescription("Common utility.")
// Modules
.addSubcommand(moduleAbout.builder)
.addSubcommand(moduleStats.builder)
.addSubcommand(moduleAvatar.builder)
.addSubcommand(modulePing.builder);
.addSubcommand(moduleAvatar.builder);
// Execute the command
export const execute = async (interaction: ChatInputCommandInteraction) => {
@ -22,15 +18,9 @@ export const execute = async (interaction: ChatInputCommandInteraction) => {
case "about":
await moduleAbout.execute(interaction);
break;
case "stats":
await moduleStats.execute(interaction);
break;
case "avatar":
await moduleAvatar.execute(interaction);
break;
case "ping":
await modulePing.execute(interaction);
break;
default:
throw new Error(
`Unknown subcommand: ${interaction.options.getSubcommand()}`

View file

@ -0,0 +1,130 @@
/* eslint-disable no-loops/no-loops */
// Dependencies
import { formatDuration, intervalToDuration, subMilliseconds } from "date-fns";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
CommandInteraction,
EmbedBuilder,
SlashCommandSubcommandBuilder,
} from "discord.js";
import deferReply from "../../../../handlers/deferReply";
// Configurations
import getEmbedConfig from "../../../../helpers/getEmbedData";
// Function
export default {
builder: (command: SlashCommandSubcommandBuilder) => {
return command
.setName("about")
.setDescription("Check information about this instance");
},
execute: async (interaction: CommandInteraction) => {
await deferReply(interaction, false);
if (!interaction.guild) throw new Error("You need to be in a guild");
// await cooldown(
// interaction.guild,
// interaction.user,
// interaction.commandId,
// 3600
// );
const { successColor, footerText, footerIcon } = await getEmbedConfig(
interaction.guild
);
const { client } = interaction;
if (client?.uptime === null) return;
let totalSeconds = client?.uptime / 1000;
const days = Math?.floor(totalSeconds / 86400);
totalSeconds %= 86400;
const hours = Math?.floor(totalSeconds / 3600);
totalSeconds %= 3600;
const minutes = Math?.floor(totalSeconds / 60);
const seconds = Math?.floor(totalSeconds % 60);
const uptime = `${days} days, ${hours} hours, ${minutes} minutes and ${seconds} seconds`;
// // Initialize a storage for the user ids
// const userIds = new Set();
// // Iterate over all guilds (always cached)
// for await (const guild of client.guilds.cache.values()) {
// // Fetch all guild members and iterate over them
// for await (const member of (await guild.members.fetch()).values()) {
// // Fetch the user, if user already cached, returns value from cache
// // Will probably always return from cache
// const user = await client.users.fetch(member.id);
// // Check if user id is not already in set and user is not a bot
// if (!userIds.has(user.id) && !user.bot) {
// // Add unique user id to our set
// userIds.add(user.id);
// }
// }
// }
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Support")
.setStyle(ButtonStyle.Link)
.setEmoji("💬")
.setURL("https://discord.zyner.org"),
new ButtonBuilder()
.setLabel("Documentation")
.setStyle(ButtonStyle.Link)
.setEmoji("📚")
.setURL("https://xyter.zyner.org")
);
const interactionEmbed = new EmbedBuilder()
.setColor(successColor)
.setTitle(":toolbox:︱About this instance")
.setDescription(
`This bot instance is hosted by [${process.env.BOT_HOSTER_NAME}](${process.env.BOT_HOSTER_URL}) who might have modified the [source code](https://github.com/ZynerOrg/xyter).`
)
.setFields(
{
name: "Latency",
value: `${Math.round(client.ws.ping)} ms`,
inline: true,
},
{
name: "Servers (cached)",
value: `${client.guilds.cache.size}`,
inline: true,
},
{
name: "Users (cached)",
value: `${client.guilds.cache.reduce(
(a, g) => a + g.memberCount,
0
)}`,
inline: true,
},
{
name: "Version",
value: `[${process.env.npm_package_version}](https://github.com/ZynerOrg/xyter/releases/tag/${process.env.npm_package_version})`,
inline: true,
},
{
name: "Since last restart",
value: `${formatDuration(
intervalToDuration({
start: subMilliseconds(new Date(), client.uptime),
end: new Date(),
})
)}`,
inline: true,
}
)
.setTimestamp()
.setFooter({ text: footerText, iconURL: footerIcon });
await interaction.editReply({
embeds: [interactionEmbed],
components: [buttons],
});
},
};

View file

@ -28,15 +28,21 @@ export default {
const targetUser = userOption || interaction.user;
const embed = new EmbedBuilder()
.setTitle("[:tools:] Avatar")
.setTitle(":toolbox:︱Avatar")
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon });
const avatarUrl = targetUser.displayAvatarURL();
return interaction.editReply({
embeds: [
embed
.setDescription(`${targetUser.username}'s avatar:`)
.setThumbnail(targetUser.displayAvatarURL())
.setDescription(
userOption
? `You can also [download it here](${avatarUrl})!`
: `Your avatar is available to [download here](${avatarUrl}).`
)
.setThumbnail(avatarUrl)
.setColor(successColor),
],
});

View file

@ -1,11 +1,13 @@
import {
ActionRowBuilder,
BaseInteraction,
ButtonBuilder,
ButtonInteraction,
ButtonStyle,
ChatInputCommandInteraction,
CommandInteraction,
EmbedBuilder,
} from "discord.js";
import capitalizeFirstLetter from "../../../helpers/capitalizeFirstLetter";
import getEmbedConfig from "../../../helpers/getEmbedData";
import button from "./button";
import command from "./command";
@ -25,15 +27,24 @@ export const handleCommandInteraction = async (
);
await command(<ChatInputCommandInteraction>interaction).catch((err) => {
const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Report Problem")
.setStyle(ButtonStyle.Link)
.setEmoji("📝")
.setURL("https://discord.zyner.org")
);
return interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle(`[:x:] ${capitalizeFirstLetter(interaction.commandName)}`)
.setDescription(`${err}`)
.setTitle(`:no_entry_sign:︱Your request failed`)
.setDescription(`${err.message}`)
.setColor(errorColor)
.setTimestamp(new Date())
.setFooter({ text: footerText, iconURL: footerIcon }),
],
components: [buttons],
});
});
};

View file

@ -51,7 +51,9 @@ export default async (
);
}
throw new Error(`You are still on cooldown for ${duration}`);
throw new Error(
`You need to wait for ${duration} before you can do that again`
);
}
const createCooldown = await prisma.cooldown.upsert({