Compare commits

..

No commits in common. "Slash-V14" and "akairo" have entirely different histories.

296 changed files with 11075 additions and 10190 deletions

View file

@ -1,20 +0,0 @@
token=YourToken
clientId=BotClientId
guildId=DevGuildId
ownerId=OwnerUserId
statusChannel=CHannelIdForStatus
uptimeURL=UptimeKumaOrWhateverStatusThingYouUseOrJustLeaveEmpty
uptimeInterval=60
twiConsumer=TwitterConsumerToken
twiConsumerSecret=TwitterConsumerSecretToken
twiToken=TwitterToken
twiTokenSecret=TwitterSecretToken
twiChannel=ChannelWhereJustTheTwitterLinkAreSent
twiLogChannel=ChannelWhereTheDetailedInfoOfTheCommandIsSent
botsggToken=APITokenForBots.gg
botsggEndpoint=https://discord.bots.gg/api/v1
stableHordeApi=0000000000
stableHordeID=0000
NODE_ENV=development
ytdlpMaxResolution=720
proxy=socks5://localhost:3128

28
.eslintrc.json Normal file
View file

@ -0,0 +1,28 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"no-console": [
"off"
],
"indent": [
"error",
"tab"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

View file

@ -1,37 +0,0 @@
---
name: "Bug"
about: "Use this if you found a bug in the bot."
title: "[BUG] "
ref: "main"
labels:
- bug
---
<!-- Please provide the commit of the bot at the time of the report. You can find it by doing /about or haha about. -->
**Commit number**
**Bot varient**
<!-- Put a x between the brackets to signify the version you used. -->
- [ ] Slash <!-- The current and latest version that use the slash commands. -->
- [ ] Legacy <!-- The old version of the bot with the "haha" prefix. -->
**Describe the issue**
**Intended result**
**Screenshots**
**Steps to reproduce the issue**
-
-
-
-
**Did someone already report that bug?**
- [ ] Yes <!-- If you have to put yes you don't need to submit that bug report. -->
- [ ] No

View file

@ -1,19 +0,0 @@
---
name: "Feature request"
about: "Use this if you want a new feature in the bot."
title: "[Feature request] "
ref: "main"
labels:
- Feature request
---
**Describe the feature you want**
**Did someone already request that feature?**
- [ ] Yes <!-- If you have to put yes you don't need to submit that feature request. -->
- [ ] No

67
.gitignore vendored
View file

@ -1,16 +1,55 @@
.env
node_modules/
config/config.json
json/board/
unloaded/
database.sqlite3
tmp/*.js
# root /
bin/yt-dlp*
bin/HandBrakeCLI*
bin/upload.sh
bin/dectalk
config.json
.DS_Store
node_modules
.vscode
error/
asset/ytp/sources
asset/ytp/music
asset/ytp/sounds
# video
video.mp4
videoReady.mp4
SPOILER_video.mp4
SPOILER_videoReady.mp4
## images
img/frame001.png
img/spb.png
img/de.png
img/nolight.png
img/memeInput.gif
img/meme.gif
spb.png
### audio
tts.mp3
ttsvc.mp3
music.mp3
dectalk.wav
dectalkvc.wav
sam.wav
samvc.wav
# json
json/blacklist.json
json/customresponse.json
board/*.json
tag/*.json
welcome/*.json
bye/*.json
webhook/*.json
json/censor.json
json/uncensor.json
# other
dectalk/
*_unloaded.js
asset/ytp/userVid/*.mp4
asset/ytp/music/
asset/ytp/resources/
asset/ytp/sounds/
asset/ytp/sources/

17
.vscode/launch.json vendored
View file

@ -1,17 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/index.js"
}
]
}

83
README.md Normal file
View file

@ -0,0 +1,83 @@
# Haha Yes
A multi function discord bot.
## Getting Started
These instructions will get you a copy of the project up and running on your local machine
### Prerequisites
You need to install the following
* https://github.com/Automattic/node-canvas
* sequelize-cli (``npm install -g sequelize-cli``)
* mysql
* ffmpeg (Optional but recommanded: for all command that require to interact with voice chat and [vid2gif.js](commands/utility/vid2gif.js), [vidshittyfier.js](commands/fun/vidshittyfier.js) and [ytp.js](commands/fun/ytp.js))
* handbrake-cli (Optional but recommanded: for [download.js](commands/utility/download.js))
* apngasm (Optional: for [nolight.js](commands/images/nolight))
* [Google credentials](https://cloud.google.com/docs/authentication/getting-started) (Optional: for [tts.js](commands/fun/tts/tts.js) and [ttsvc.js](commands/fun/tts/ttsvc.js), without that it will spam error on startup but not important)
* Wine (Optional: required for linux/mac for [dectalk.js](commands/fun/tts/dectalk.js) and [dectalkvc.js](commands/fun/tts/dectalkvc.js))
* xvfb (Optional: for wine if using headless server)
* waon (Optional: used to convert sound files to midi for [midify.js](commands/fun/midify.js))
* timidity (Optional: used to convert the midi files back to mp3 for [midify.js](commands/fun/midify.js))
### Installing
```
git clone https://git.namejeff.xyz/Supositware/Haha-Yes
cd discordbot
npm install
sequelize db:migrate
```
If the youtube-dl module didn't install youtube-dl by himself you can go in ``node_modules/youtube-dl/scripts`` and run ``node download.js``
Configure [config.json](config-exemple.jsonc) and [config/config.json](config/config-example.json )
To run the bot either use pm2
```
npm install -g pm2
pm2 start index.js --name(insert name)
```
or with node ``node index.js``
If on linux you can also do
``nohup node index.js &``
To use [ytp.js](commands/fun/ytp.js)
1. Download the folder 'sounds', 'music', 'resources', 'sources' from [YTPPlus](https://github.com/philosophofee/YTPPlus)
2. Put them in the [asset/ytp](asset/ytp) folder
To use dectalk on linux you will need
1. Get dectalk
2. install wine
3. install Xvfb & run `Xvfb :0 -screen 0 1024x768x16 &`
## Built With
* [Discord.JS](https://github.com/discordjs/discord.js) - The discord api used
* [Discord-Akairo](https://github.com/1Computer1/discord-akairo) - The framework used for Discord.JS
## Authors
* **Loïc Bersier**
## Donation link
[![Paypal](https://www.paypalobjects.com/en_US/CH/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/paypalme2/supositware/)
## License
This project is licensed under the **GNU Affero General Public License v3.0** License - see the [LICENSE](LICENSE) file for details
## Acknowledgments
* [discord.JS team](https://github.com/discordjs/discord.js)
* [1computer1](https://github.com/1Computer1/) for discord-akairo & the help command from [hoshi](https://github.com/1Computer1/hoshi)
* [Rantionary](https://github.com/RantLang/Rantionary) for there dictionnary.
* Tina the Cyclops girl#0064 for inspiring me for making this bot
* [Jetbrains](https://www.jetbrains.com/?from=Hahayesdiscordbot) for providing their IDE free of charges!
<img src="https://its.gamingti.me/XT8F.svg" width=20%></img>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
asset/img/frame002.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
asset/img/gold.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
asset/impact.ttf Normal file

Binary file not shown.

BIN
asset/times.ttf Normal file

Binary file not shown.

View file

@ -0,0 +1 @@
# You Will need to edit the config files to includes absolute path

View file

@ -0,0 +1 @@
soundfont /restricted/hahayes/hahaTest/asset/timidity/sf/MCNBS_3_3_4.sf2

View file

@ -0,0 +1 @@
soundfont /restricted/hahayes/hahaTest/asset/timidity/sf/E3Kay_s_Epic_Soundfont.sf2

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,6 @@
# Credits
MCNBS_3_3_4.sf2 - [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0/deed.en) Author: Stuff by David
E3Kay's Epic Soundfont - [Creative Commons Attribution 3.0 Unported](http://creativecommons.org/licenses/by-3/4.0/deed.en) Author: E3Kay

View file

View file

@ -1,171 +0,0 @@
/* TODO
*
* To be merged with commands/AI/txt2img.js
*
*/
import { SlashCommandBuilder, EmbedBuilder, AttachmentBuilder, ButtonBuilder, ActionRowBuilder, ButtonStyle } from 'discord.js';
import fetch from 'node-fetch';
import os from 'node:os';
import fs from 'node:fs';
import stream from 'node:stream';
import util from 'node:util';
import db from '../../models/index.js';
const { stableHordeApi, stableHordeID } = process.env;
export default {
data: new SlashCommandBuilder()
.setName('img2img')
.setDescription('AI generated image with stable diffusion (If credit are low it may be slow)')
.addAttachmentOption(option =>
option.setName('image')
.setDescription('Image you want to modify')
.setRequired(true))
.addStringOption(option =>
option.setName('prompt')
.setDescription('What do you want the AI to generate?')
.setRequired(true)),
category: 'AI',
alias: ['i2i'],
async execute(interaction, args, client) {
await interaction.deferReply();
const streamPipeline = util.promisify(stream.pipeline);
const res = await fetch(args.image.url);
if (!res.ok) return interaction.editReply('An error has occured while trying to download your image.');
await streamPipeline(res.body, fs.createWriteStream(`${os.tmpdir()}/${args.image.name}.webp`));
const b64Image = fs.readFileSync(`${os.tmpdir()}/${args.image.name}.webp`, { encoding: 'base64' });
generate(interaction, args.prompt, client, b64Image);
},
};
async function generate(i, prompt, client, b64Img) {
console.log('Generating image');
const body = {
prompt: prompt,
params: {
n: 1,
width: 512,
height: 512,
},
cfg_scale: 9,
use_gfpgan: true,
use_real_esrgan: true,
use_ldsr: true,
use_upscaling: true,
steps: 50,
nsfw: i.channel.nsfw ? true : false,
censor_nsfw: i.channel.nsfw ? true : false,
source_image: b64Img,
source_processing: 'img2img',
shared: true,
};
const isOptOut = await db.optout.findOne({ where: { userID: i.user.id } });
if (isOptOut) {
body.shared = false;
}
const fetchParameters = {
method: 'post',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json', 'apikey': stableHordeApi },
};
let response = await fetch('https://stablehorde.net/api/v2/generate/async', fetchParameters);
response = await response.json();
if (!response.id) {
console.log(response);
return i.editReply({ content: `An error has occured, please try again later. \`${response.message}\`` });
}
let wait_time = 5000;
let checkURL = `https://stablehorde.net/api/v2/generate/check/${response.id}`;
const checking = setInterval(async () => {
const checkResult = await checkGeneration(checkURL);
if (checkResult === undefined) return;
if (!checkResult.done) {
if (checkResult.wait_time === -1) {
console.log(checkResult.raw);
return i.editReply({ content: `An error has occured, please try again later. \`${checkResult.raw.message}\`` });
}
if (checkResult.wait_time < 0) {
console.log(checkResult.raw);
clearInterval(checking);
return i.editReply({ content: 'No servers are currently available to fulfill your request, please try again later.' });
}
if (checkResult.wait_time === 0) {
checkURL = `https://stablehorde.net/api/v2/generate/status/${response.id}`;
}
wait_time = checkResult.wait_time;
}
else if (checkResult.done && checkResult.image) {
clearInterval(checking);
let creditResponse = await fetch(`https://stablehorde.net/api/v2/users/${stableHordeID}`);
creditResponse = await creditResponse.json();
const streamPipeline = util.promisify(stream.pipeline);
const res = await fetch(checkResult.image);
if (!res.ok) return i.editReply('An error has occured while trying to download your image.');
await streamPipeline(res.body, fs.createWriteStream(`${os.tmpdir()}/${i.id}.webp`));
const generatedImg = new AttachmentBuilder(`${os.tmpdir()}/${i.id}.webp`);
const stableEmbed = new EmbedBuilder()
.setColor(i.member ? i.member.displayHexColor : 'Navy')
.setTitle(prompt)
.setURL('https://aqualxx.github.io/stable-ui/')
.setImage(`attachment://${i.id}.webp`)
.setFooter({ text: `**Credit left: ${creditResponse.kudos}** Seed: ${checkResult.seed} worker ID: ${checkResult.worker_id} worker name: ${checkResult.worker_name}` });
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`regenerate${i.user.id}${i.id}`)
.setLabel('🔄 Regenerate')
.setStyle(ButtonStyle.Primary),
);
await i.editReply({ embeds: [stableEmbed], components: [row], files: [generatedImg] });
listenButton(client, i, prompt);
}
}, wait_time);
}
async function checkGeneration(url) {
let check = await fetch(url);
check = await check.json();
if (!check.is_possible) {
return { done: false, wait_time: -1, raw: check };
}
if (check.done) {
if (!check.generations) {
return { done: false, wait_time: check.wait_time * 1000, raw: check };
}
return { done: true, image: check.generations[0].img, seed: check.generations[0].seed, worker_id: check.generations[0].worker_id, worker_name: check.generations[0].worker_name, raw: check };
}
}
async function listenButton(client, interaction, prompt) {
client.once('interactionCreate', async (interactionMenu) => {
if (!interactionMenu.isButton()) return;
await interactionMenu.update({ components: [] });
if (interactionMenu.customId === `regenerate${interactionMenu.user.id}${interaction.id}`) {
await interactionMenu.deferReply();
await generate(interactionMenu, prompt, client);
}
});
}

View file

@ -1,145 +0,0 @@
/* TODO
*
* To be merged with commands/AI/img2img.js
*
*/
import { SlashCommandBuilder, EmbedBuilder, AttachmentBuilder, ButtonBuilder, ActionRowBuilder, ButtonStyle } from 'discord.js';
import fetch from 'node-fetch';
import os from 'node:os';
import fs from 'node:fs';
import stream from 'node:stream';
import util from 'node:util';
import db from '../../models/index.js';
const { stableHordeApi, stableHordeID } = process.env;
export default {
data: new SlashCommandBuilder()
.setName('txt2img')
.setDescription('AI generated image with stable diffusion (If credit are low it may be slow)')
.addStringOption(option =>
option.setName('prompt')
.setDescription('What do you want the AI to generate?')
.setRequired(true)),
category: 'AI',
alias: ['t2i'],
async execute(interaction, args, client) {
await interaction.deferReply();
generate(interaction, args.prompt, client);
},
};
async function generate(i, prompt, client) {
const body = {
prompt: prompt,
params: {
n: 1,
width: 512,
height: 512,
},
cfg_scale: 9,
use_gfpgan: true,
use_real_esrgan: true,
use_ldsr: true,
use_upscaling: true,
steps: 50,
nsfw: i.channel.nsfw ? true : false,
censor_nsfw: i.channel.nsfw ? true : false,
shared: true,
};
const isOptOut = await db.optout.findOne({ where: { userID: i.user.id } });
if (isOptOut) {
body.shared = false;
}
const fetchParameters = {
method: 'post',
body: JSON.stringify(body),
headers: { 'Content-Type': 'application/json', 'apikey': stableHordeApi },
};
let response = await fetch('https://stablehorde.net/api/v2/generate/async', fetchParameters);
response = await response.json();
let wait_time = 5000;
let checkURL = `https://stablehorde.net/api/v2/generate/check/${response.id}`;
const checking = setInterval(async () => {
const checkResult = await checkGeneration(checkURL);
if (checkResult === undefined) return;
if (!checkResult.done) {
if (checkResult.wait_time < 0) {
clearInterval(checking);
return i.editReply({ content: 'No servers are currently available to fulfill your request, please try again later.' });
}
if (checkResult.wait_time === 0) {
checkURL = `https://stablehorde.net/api/v2/generate/status/${response.id}`;
}
wait_time = checkResult.wait_time;
}
else if (checkResult.done && checkResult.image) {
clearInterval(checking);
let creditResponse = await fetch(`https://stablehorde.net/api/v2/users/${stableHordeID}`);
creditResponse = await creditResponse.json();
const streamPipeline = util.promisify(stream.pipeline);
const res = await fetch(checkResult.image);
if (!res.ok) return i.editReply('An error has occured while trying to download your image.');
await streamPipeline(res.body, fs.createWriteStream(`${os.tmpdir()}/${i.id}.webp`));
const generatedImg = new AttachmentBuilder(`${os.tmpdir()}/${i.id}.webp`);
const stableEmbed = new EmbedBuilder()
.setColor(i.member ? i.member.displayHexColor : 'Navy')
.setTitle(prompt)
.setURL('https://aqualxx.github.io/stable-ui/')
.setImage(`attachment://${i.id}.webp`)
.setFooter({ text: `**Credit left: ${creditResponse.kudos}** Seed: ${checkResult.seed} worker ID: ${checkResult.worker_id} worker name: ${checkResult.worker_name}` });
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`regenerate${i.user.id}${i.id}`)
.setLabel('🔄 Regenerate')
.setStyle(ButtonStyle.Primary),
);
await i.editReply({ embeds: [stableEmbed], components: [row], files: [generatedImg] });
listenButton(client, i, prompt);
}
}, wait_time);
}
async function checkGeneration(url) {
let check = await fetch(url);
check = await check.json();
if (!check.is_possible) {
return { done: false, wait_time: -1 };
}
if (check.done) {
if (!check.generations) {
return { done: false, wait_time: check.wait_time * 1000 };
}
return { done: true, image: check.generations[0].img, seed: check.generations[0].seed, worker_id: check.generations[0].worker_id, worker_name: check.generations[0].worker_name };
}
}
async function listenButton(client, interaction, prompt) {
client.once('interactionCreate', async (interactionMenu) => {
if (!interactionMenu.isButton()) return;
await interactionMenu.update({ components: [] });
if (interactionMenu.customId === `regenerate${interactionMenu.user.id}${interaction.id}`) {
await interactionMenu.deferReply();
await generate(interactionMenu, prompt, client);
}
});
}

View file

@ -1,62 +1,45 @@
import { SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionFlagsBits } from 'discord.js';
import db from '../../models/index.js';
const { Command } = require('discord-akairo');
const autoResponseStat = require('../../models').autoresponseStat;
export default {
data: new SlashCommandBuilder()
.setName('autoresponse')
.setDescription('Enable or disable autoresponse')
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
category: 'admin',
async execute(interaction, args, client) {
const autoresponseStat = await db.autoresponseStat.findOne({ where: { serverID: interaction.guild.id } });
if (!autoresponseStat) {
const body = { serverID: interaction.guild.id, stat: 'enable' };
await db.autoresponseStat.create(body);
return await interaction.reply({ content: 'Autoresponse has been enabled.', ephemeral: true });
class autoresponseCommand extends Command {
constructor() {
super('autoresponse', {
aliases: ['autoresponse'],
category: 'admin',
args: [
{
id: 'stat',
type: 'string',
prompt: {
start: 'Do you want to **enable** or **disable** auto response?',
}
}
],
clientPermissions: ['SEND_MESSAGES'],
userPermissions: ['MANAGE_MESSAGES'],
channel: 'guild',
description: {
content: 'enable/disable autoresponse',
usage: '[enable/disable]',
examples: ['enable']
}
});
}
async exec(message, args) {
if (args.stat.toLowerCase() == 'enable' || args.stat.toLowerCase() == 'disable') {
const autoresponseStat = await autoResponseStat.findOne({where: {serverID: message.guild.id}});
if (!autoresponseStat) {
const body = {serverID: message.guild.id, stat: args.stat};
autoResponseStat.create(body);
return message.channel.send(`Autoresponse have been ${args.stat}d`);
} else {
const body = {serverID: message.guild.id, stat: args.stat};
autoResponseStat.update(body, {where: {serverID: message.guild.id}});
return message.channel.send(`Autoresponse have been ${args.stat}d`);
}
}
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`yes${interaction.user.id}${interaction.id}`)
.setLabel('Yes')
.setStyle(ButtonStyle.Primary),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`no${interaction.user.id}${interaction.id}`)
.setLabel('No')
.setStyle(ButtonStyle.Danger),
);
if (autoresponseStat.stat === 'enable') {
await interaction.reply({ content: 'Autoresponse is already enabled, do you wish to disable it?', components: [row], ephemeral: true });
}
else {
const body = { serverID: interaction.guild.id, stat: 'enable' };
await db.autoresponseStat.update(body, { where: { serverID: interaction.guild.id } });
return interaction.editReply({ content: 'Auto response has been enabled.', ephemeral: true });
}
return listenButton(client, interaction, interaction.user);
},
};
async function listenButton(client, interaction, user = interaction.user, originalId = interaction.id) {
client.once('interactionCreate', async (interactionMenu) => {
if (user !== interactionMenu.user) return listenButton(client, interaction, user, originalId);
if (!interactionMenu.isButton()) return;
await interactionMenu.update({ components: [] });
if (interactionMenu.customId === `yes${interaction.user.id}${originalId}`) {
const body = { serverID: interaction.guild.id, stat: 'disable' };
await db.autoresponseStat.update(body, { where: { serverID: interaction.guild.id } });
return interaction.editReply({ content: 'Auto response has been disabled.', ephemeral: true });
}
else {
return interaction.editReply({ content: 'Nothing has been changed.', ephemeral: true });
}
});
}
}
module.exports = autoresponseCommand;

70
commands/admin/ban.js Normal file
View file

@ -0,0 +1,70 @@
const { Command } = require('discord-akairo');
class BanCommand extends Command {
constructor() {
super('ban', {
aliases: ['ban'],
category: 'admin',
args: [
{
id: 'user',
type: 'string',
prompt: {
start: 'which user do you want to ban?',
retry: 'This doesn\'t look like a user, please try again!'
}
},
{
id: 'reasons',
type: 'string',
prompt: {
start: 'For what reasons?',
optional: true
},
match: 'rest'
}
],
clientPermissions: ['BAN_MEMBERS', 'SEND_MESSAGES'],
userPermissions: ['BAN_MEMBERS'],
channel: 'guild',
description: {
content: 'Ban user | For hackban precise the userid',
usage: '[@user] [reason] OR [userID] [reason]',
examples: ['@user big dumb dumb', 'userID hackban']
}
});
}
async exec(message, args) {
let reasons = args.reasons;
let user = args.user;
if (message.mentions.members.first())
user = message.mentions.members.first().id;
if(user === this.client.user.id)
return message.channel.send('Can\'t ban me fool!');
if(!reasons)
reasons = 'Nothing have been specified';
if(user === message.author.id)
return message.channel.send('Why would you ban yourself ?');
if (message.mentions.members.first()) {
user = message.mentions.members.first();
await user.send(`You have been banned from **${message.guild.name}** for the following reasons: "**${reasons}**"`, {files: ['./asset/vid/You_Have_Been_Banned_From_Mickey_Mouse_Club.mp4']})
.catch(() => console.log('could not send message to the concerned user'));
return user.ban({reason: `Banned by : ${message.author.username} for the following reasons : ${reasons}`})
.then(() => message.reply(`${user.user.username} was succesfully banned with the following reasons "${reasons}".`))
//.catch(() => message.reply('Uh oh, an error has occurred! can the bot ban this user?'));
.catch(err => console.error(err));
} else {
message.guild.members.ban(user, {reason: `Banned by : ${message.author.username} for the following reasons : ${reasons}`})
.then(() => message.reply(`user ID ${args.user} was succesfully hackbanned with the following reasons "${reasons}".`))
//.catch(() => message.reply('Uh oh, an error has occurred! can the bot ban this user?'));
.catch(err => console.error(err));
}
}
}
module.exports = BanCommand;

71
commands/admin/banword.js Normal file
View file

@ -0,0 +1,71 @@
const { Command } = require('discord-akairo');
const safe = require('safe-regex');
const BannedWords = require('../../models').bannedWords;
class BannedWordsCommand extends Command {
constructor() {
super('BannedWords', {
aliases: ['bannedword', 'banword', 'unbanword', 'censor', 'uncensor', 'blacklistword', 'blacklist', 'unblacklist'],
category: 'admin',
userPermissions: ['MANAGE_MESSAGES'],
clientPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES'],
args: [
{
id: 'word',
type: 'string',
match: 'rest'
},
{
id: 'remove',
match: 'flag',
flag: '--remove'
},
{
id: 'removeall',
match: 'flag',
flag: '--removeall'
}
],
channel: 'guild',
description: {
content: 'Ban word on the server. use the unbanword alias to delete a banned word, unbanword alias and --removeaall to remove every banned word',
usage: '[word to ban]',
examples: ['owo']
}
});
}
async exec(message, args) {
if (!safe(message.content)) return;
if (!args.word) args.word = '';
args.word = args.word.replace(/[\u0250-\ue007]/g, '');
const bannedWords = await BannedWords.findOne({where: {word: args.word.toLowerCase(), serverID: message.guild.id}});
if (message.util.parsed.alias == 'unbanword' || message.util.parsed.alias == 'uncensor' || message.util.parsed.alias == 'unblacklist' || args.remove || args.removeall) {
if (args.removeall) {
BannedWords.destroy({where: {serverID: message.guild.id}});
return message.channel.send('The banned words have been reset.');
}
if (bannedWords) {
BannedWords.destroy({where: {word: args.word.toLowerCase(), serverID: message.guild.id}});
return message.channel.send(`The word ${args.word.toLowerCase()} is no longer banned`);
} else {
return message.channel.send('There was no word to unban');
}
}
if (!args.word) return message.channel.send('Please specify a word to ban!');
if (!bannedWords) {
const body = {word: args.word.toLowerCase(), serverID: message.guild.id};
await BannedWords.create(body);
return message.channel.send(`The word ${args.word.toLowerCase()} has been banned`);
} else {
message.channel.send('This word is already banned');
}
}
}
module.exports = BannedWordsCommand;

View file

@ -1,74 +1,75 @@
import { SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionFlagsBits } from 'discord.js';
import db from '../../models/index.js';
const { Command } = require('discord-akairo');
const leaveChannel = require('../../models').leaveChannel;
export default {
data: new SlashCommandBuilder()
.setName('bye')
.setDescription('Set a leave message')
.addStringOption(option =>
option.setName('message')
.setDescription('The message you want the bot to say when someone leave in the current channel.')),
category: 'admin',
userPermissions: [PermissionFlagsBits.ManageChannels],
async execute(interaction, args, client) {
const leave = await db.leaveChannel.findOne({ where: { guildID: interaction.guild.id } });
if (!leave && !args.message) {
return interaction.reply({ content: 'You need a message for me to say anything!', ephemeral: true });
}
else if (!leave) {
const body = { guildID: interaction.guild.id, channelID: interaction.channel.id, message: args.message };
await db.leaveChannel.create(body);
return interaction.reply({ content: `The leave message have been set with ${args.message}`, ephemeral: true });
}
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`edit${interaction.user.id}${interaction.id}`)
.setLabel('Edit')
.setStyle(ButtonStyle.Primary),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`remove${interaction.user.id}${interaction.id}`)
.setLabel('Remove')
.setStyle(ButtonStyle.Danger),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`nothing${interaction.user.id}${interaction.id}`)
.setLabel('Do nothing')
.setStyle(ButtonStyle.Secondary),
);
await interaction.reply({ content: 'The server already has a message set, do you want to edit it or remove it?', components: [row], ephemeral: true });
return listenButton(client, interaction, args, interaction.user);
},
};
async function listenButton(client, interaction, args, user = interaction.user, originalId = interaction.id) {
client.once('interactionCreate', async (interactionMenu) => {
if (user !== interactionMenu.user) return listenButton(client, interaction, args, user, originalId);
if (!interactionMenu.isButton()) return;
await interactionMenu.update({ components: [] });
if (interactionMenu.customId === `edit${interaction.user.id}${originalId}`) {
if (!args.message) {
return interaction.reply({ content: 'You need to input a message for me to edit!', ephemeral: true });
class byeCommand extends Command {
constructor() {
super('bye', {
aliases: ['bye', 'leave'],
category: 'admin',
channel: 'guild',
userPermissions: ['MANAGE_CHANNELS'],
clientPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'remove',
match: 'flag',
flag: '--remove'
},
{
id: 'message',
type: 'string',
match: 'rest',
default: '[member] just left the server :('
}
],
description: {
content: 'Send a message to the current channel when a person leave, you can use [member] to show the member username and [server] to show the name of the server',
usage: '[bye message]',
examples: ['[member] left the server, he deserve a ban']
}
});
}
async exec(message, args) {
const leave = await leaveChannel.findOne({where: {guildID: message.guild.id}});
if (args.remove) {
if (leave) {
leave.destroy({where: {guildID: message.guild.id, channelID: message.channel.id}});
return message.channel.send('successfully deleted the leave message');
} else {
return message.channel.send('Did not find the a leave message, are you sure you have one setup?');
}
const body = { guildID: interaction.guild.id, channelID: interaction.channel.id, message: args.message };
await db.leaveChannel.update(body, { where: { guildID: interaction.guild.id } });
return interaction.editReply({ content: `The leave message has been set to ${args.message}`, ephemeral: true });
}
else if (interactionMenu.customId === `remove${interaction.user.id}${originalId}`) {
db.leaveChannel.destroy({ where: { guildID: interaction.guild.id, channelID: interaction.channel.id } });
return interaction.editReply({ content: 'The leave message has been deleted.', ephemeral: true });
if (!args.message) {
return message.channel.send('Please provide a message');
}
else {
return interaction.editReply({ content: 'Nothing has been changed.', ephemeral: true });
if (!leave) {
const body = {guildID: message.guild.id, channelID: message.channel.id, message: args.message};
await leaveChannel.create(body);
return message.channel.send(`The leave message have been set with ${args.message}`);
} else {
message.channel.send('The server already have a leave message, do you want to replace it? y/n');
const filter = m => m.content && m.author.id == message.author.id;
message.channel.awaitMessages(filter, {time: 5000, max: 1, errors: ['time'] })
.then(async messages => {
let messageContent = messages.map(messages => messages.content);
if (messageContent[0] === 'y' || messageContent[0] === 'yes') {
const body = {guildID: message.guild.id, channelID: message.channel.id, message: args.message};
await leaveChannel.update(body, {where: {guildID: message.guild.id}});
return message.channel.send(`The leave message have been set with ${args.message}`);
} else {
return message.channel.send('Not updating.');
}
})
.catch(err => {
console.error(err);
return message.channel.send('Took too long to answer. didin\'t update anything.');
});
}
});
}
}
module.exports = byeCommand;

View file

@ -0,0 +1,45 @@
const { Command } = require('discord-akairo');
const commandblock = require('../../models').commandBlock;
class commandblockCommand extends Command {
constructor() {
super('commandblock', {
aliases: ['commandblock', 'blockcommand'],
category: 'admin',
args: [
{
id: 'command',
type: 'command',
prompt: {
start: 'What command do you want to block?',
retry: 'Not a valid command, please try again'
}
}
],
clientPermissions: ['SEND_MESSAGES'],
userPermissions: ['ADMINISTRATOR'],
channel: 'guild',
description: {
content: 'Block a command. Execute that command again to unblock a command',
usage: '[command name]',
examples: ['owned']
}
});
}
async exec(message, args) {
if (args.command.id == 'commandblock') return message.channel.send('Whoa there, i can\'t let you block this command or else how would you unblock it?');
const blocked = await commandblock.findOne({where: {serverID: message.guild.id, command: args.command.id}});
if (!blocked) {
const body = {serverID: message.guild.id, command: args.command.id};
commandblock.create(body);
return message.channel.send(`Blocked command ${args.command.id}`);
} else {
commandblock.destroy({where: {serverID: message.guild.id, command: args.command.id}});
return message.channel.send(`The command ${args.command.id} has been unblocked`);
}
}
}
module.exports = commandblockCommand;

View file

@ -0,0 +1,52 @@
const { Command } = require('discord-akairo');
const commandblockuser = require('../../models').commandblockuser;
class commandblockuserCommand extends Command {
constructor() {
super('commandblockuser', {
aliases: ['commandblockuser', 'userblockcommand'],
category: 'admin',
args: [
{
id: 'command',
type: 'command',
prompt: {
start: 'What command do you want to block?',
retry: 'Not a valid command, please try again'
}
},
{
id: 'user',
type: 'user',
prompt: {
start: 'Which user you want to block?'
}
}
],
clientPermissions: ['SEND_MESSAGES'],
userPermissions: ['ADMINISTRATOR'],
channel: 'guild',
description: {
content: 'Block a command from a user. Execute that command again to unblock a command',
usage: '[command name] [@user]',
examples: ['owned @supositware']
}
});
}
async exec(message, args) {
if (args.command.id == 'commandblockuser') return message.channel.send('Whoa there, i can\'t let you block this command or else how would you unblock it?');
const blocked = await commandblockuser.findOne({where: {serverID: message.guild.id, userID: args.user.id, command: args.command.id}});
if (!blocked) {
const body = {serverID: message.guild.id, userID: args.user.id, command: args.command.id};
commandblockuser.create(body);
return message.channel.send(`Blocked command ${args.command.id}`);
} else {
commandblockuser.destroy({where: {serverID: message.guild.id, userID: args.user.id, command: args.command.id}});
return message.channel.send(`The command ${args.command.id} has been unblocked`);
}
}
}
module.exports = commandblockuserCommand;

View file

@ -0,0 +1,43 @@
const { Command } = require('discord-akairo');
const joinChannel = require('../../models').joinChannel;
class fakejoinCommand extends Command {
constructor() {
super('fakejoin', {
aliases: ['fakejoin'],
category: 'admin',
channel: 'guild',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'user',
type: 'user',
match: 'rest'
}
],
description: {
content: 'Fake join message',
usage: '[text]',
examples: ['Supositware']
}
});
}
async exec(message, args) {
let member;
const join = await joinChannel.findOne({where: {guildID: message.guild.id}});
if (join) {
if (args.user)
member = message.guild.members.resolve(args.user.id);
else
member = message.guild.members.resolve(message.author.id);
} else {
return message.reply('There is no join channel setup');
}
this.client.emit('guildMemberAdd', member);
}
}
module.exports = fakejoinCommand;

View file

@ -0,0 +1,43 @@
const { Command } = require('discord-akairo');
const leaveChannel = require('../../models').leaveChannel;
class fakeleaveCommand extends Command {
constructor() {
super('fakeleave', {
aliases: ['fakeleave'],
category: 'admin',
channel: 'guild',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'user',
type: 'user',
match: 'rest'
}
],
description: {
content: 'Fake leave message',
usage: '[user]',
examples: ['Supositware']
}
});
}
async exec(message, args) {
let member;
const leave = await leaveChannel.findOne({where: {guildID: message.guild.id}});
if (leave) {
if (args.user)
member = message.guild.members.resolve(args.user.id);
else
member = message.guild.members.resolve(message.author.id);
} else {
return message.reply('There is no leave channel setup');
}
this.client.emit('guildMemberRemove', member);
}
}
module.exports = fakeleaveCommand;

57
commands/admin/kick.js Normal file
View file

@ -0,0 +1,57 @@
const { Command } = require('discord-akairo');
class KickCommand extends Command {
constructor() {
super('kick', {
aliases: ['kick'],
category: 'admin',
args: [
{
id: 'member',
type: 'member',
prompt: {
start: 'which member do you want to ban?',
}
},
{
id: 'reasons',
type: 'string',
prompt: {
start: 'For what reasons?',
optional: true
},
match: 'rest'
}
],
clientPermissions: ['KICK_MEMBERS', 'SEND_MESSAGES'],
userPermissions: ['KICK_MEMBERS'],
channel: 'guild',
description: {
content: 'Kick user',
usage: '[@user] [reason]',
examples: ['@user big dumb dumb']
}
});
}
async exec(message, args) {
let member = args.member;
let reasons = args.reasons;
if(member === this.client.user)
return message.channel.send('Cant kick me fool');
if(member.id === message.author.id)
return message.channel.send('Why would you kick yourself ?');
if(!reasons)
reasons = 'Nothing have been specified.';
await member.send(`You have been kicked from **${message.guild.name}** for the following reasons: "**${reasons}**"`)
.catch(() => console.log('could not send message to the concerned user'));
return member.kick({reason: `Kicked by : ${message.author.username} for the following reasons: ${reasons}`})
.then(() => message.reply(`${member.user.username} was succesfully kicked with the following reasons "${reasons}".`))
.catch(err => console.error(err));
}
}
module.exports = KickCommand;

51
commands/admin/log.js Normal file
View file

@ -0,0 +1,51 @@
const { Command } = require('discord-akairo');
const LogStats = require('../../models').LogStats;
class logCommand extends Command {
constructor() {
super('log', {
aliases: ['log', 'logging'],
category: 'admin',
userPermissions: ['MANAGE_MESSAGES'],
clientPermissions: ['MANAGE_GUILD'],
channel: 'guild',
description: {
content: 'Setup logging in current channel (W.I.P)',
usage: '',
examples: ['']
}
});
}
async exec(message, args) {
const logStats = await LogStats.findOne({where: {guild: message.guild.id}});
const ownerID = this.client.ownerID;
if (!logStats) {
const body = {guild: message.guild.id, channel: message.channel.id};
await LogStats.create(body);
return message.channel.send('Logging has been enabled on this channel');
} else if (logStats.get('ownerID') == message.author.id || message.member.hasPermission('ADMINISTRATOR') || message.author.id == ownerID) {
message.channel.send('The log channel is already setup, do you want to delete it? y/n');
const filter = m => m.content && m.author.id == message.author.id;
message.channel.awaitMessages(filter, {time: 5000, max: 1, errors: ['time'] })
.then(async messages => {
let messageContent = messages.map(messages => messages.content.toLowerCase());
if (messageContent[0] === 'y' || messageContent[0] === 'yes') {
await LogStats.destroy({where: {guild: message.guild.id}});
return message.channel.send('Log channel has been disabled!');
} else {
return message.channel.send('Not updating.');
}
})
.catch(err => {
console.error(err);
return message.channel.send('Took too long to answer. didin\'t change anything.');
});
} else {
return message.channel.send(`You are not the owner of this tag, if you think it is problematic ask an admin to remove it by doing ${this.client.commandHandler.prefix[0]}tag ${args.trigger} --remove`);
}
}
}
module.exports = logCommand;

34
commands/admin/prune.js Normal file
View file

@ -0,0 +1,34 @@
const { Command } = require('discord-akairo');
class PruneCommand extends Command {
constructor() {
super('Prune', {
aliases: ['Prune', 'clean', 'purge', 'clear'],
category: 'admin',
args: [
{
id: 'amount',
prompt: {
start: 'How many message should i delete?',
},
type: 'integer'
}
],
clientPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES'],
userPermissions: ['MANAGE_MESSAGES'],
channel: 'guild',
description: {
content: 'Bulk delete messages',
usage: '[amount]',
examples: ['50']
}
});
}
async exec(message,args) {
if (args.amount >= 100) return;
message.channel.bulkDelete(args.amount + 1, true);
}
}
module.exports = PruneCommand;

View file

@ -1,62 +1,45 @@
import { SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionFlagsBits } from 'discord.js';
import db from '../../models/index.js';
const { Command } = require('discord-akairo');
const quotationStat = require('../../models').quotationStat;
export default {
data: new SlashCommandBuilder()
.setName('quotation')
.setDescription('Enable or disable quotations')
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
category: 'admin',
async execute(interaction, args, client) {
const quotationstat = await db.quotationStat.findOne({ where: { serverID: interaction.guild.id } });
class quotationCommand extends Command {
constructor() {
super('quotation', {
aliases: ['quotation'],
category: 'admin',
args: [
{
id: 'stat',
type: 'string',
prompt: {
start: 'Do you want to **enable** or **disable** quotation?',
}
}
],
clientPermissions: ['SEND_MESSAGES'],
userPermissions: ['MANAGE_MESSAGES'],
channel: 'guild',
description: {
content: 'enable/disable quotation',
usage: '[enable/disable]',
examples: ['enable']
}
});
}
if (!quotationstat) {
const body = { serverID: interaction.guild.id, stat: 'enable' };
await db.quotationStat.create(body);
return await interaction.reply({ content: 'Quotation has been enabled.', ephemeral: true });
async exec(message, args) {
if (args.stat.toLowerCase() == 'enable' || args.stat.toLowerCase() == 'disable') {
const quotationstat = await quotationStat.findOne({where: {serverID: message.guild.id}});
if (!quotationstat) {
const body = {serverID: message.guild.id, stat: args.stat};
quotationStat.create(body);
return message.channel.send(`Quotation has been ${args.stat}d`);
} else {
const body = {serverID: message.guild.id, stat: args.stat};
quotationStat.update(body, {where: {serverID: message.guild.id}});
return message.channel.send(`Quotation has been ${args.stat}d`);
}
}
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`yes${interaction.user.id}${interaction.id}`)
.setLabel('Yes')
.setStyle(ButtonStyle.Primary),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`no${interaction.user.id}${interaction.id}`)
.setLabel('No')
.setStyle(ButtonStyle.Danger),
);
if (quotationstat.stat === 'enable') {
await interaction.reply({ content: 'Quotation is already enabled, do you wish to disable it?', components: [row], ephemeral: true });
}
else {
const body = { serverID: interaction.guild.id, stat: 'enable' };
await db.autoresponseStat.update(body, { where: { serverID: interaction.guild.id } });
return interaction.editReply({ content: 'Quotation has been enabled.', ephemeral: true });
}
return listenButton(client, interaction, interaction.user);
},
};
async function listenButton(client, interaction, user = interaction.user, originalId = interaction.id) {
client.once('interactionCreate', async (interactionMenu) => {
if (user !== interactionMenu.user) return listenButton(client, interaction, user, originalId);
if (!interactionMenu.isButton()) return;
interactionMenu.update({ components: [] });
if (interactionMenu.customId === `yes${interaction.user.id}${originalId}`) {
await db.quotationStat.destroy({ where: { serverID: interaction.guild.id } });
return interaction.editReply({ content: 'Quotation has been disabled.', ephemeral: true });
}
else {
return interaction.editReply({ content: 'Nothing has been changed.', ephemeral: true });
}
});
}
}
module.exports = quotationCommand;

View file

@ -1,38 +1,66 @@
import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js';
import fs from 'node:fs';
const { Command } = require('discord-akairo');
const fs = require('fs');
export default {
data: new SlashCommandBuilder()
.setName('shameboard')
.setDescription('Set shameboard to the current channel.')
.addStringOption(option =>
option.setName('emote')
.setDescription('The emote that should be used to enter the shameboard.'))
.addStringOption(option =>
option.setName('count')
.setDescription('How many react for it to enter shameboard.'))
.addBooleanOption(option =>
option.setName('remove')
.setDescription('Remove the shameboard')
.setRequired(false)),
category: 'admin',
userPermissions: [PermissionFlagsBits.ManageChannels],
async execute(interaction, args) {
if (args.remove) {
fs.unlink(`./json/board/shame${interaction.guild.id}.json`, (err) => {
if (err) {return interaction.reply('There is no shameboard');}
return interaction.reply('Deleted the shameboard');
});
}
else {
if (!args.emote || !args.count) return interaction.reply('You are missing the emote or the count arg!');
fs.writeFile(`./json/board/shame${interaction.guild.id}.json`, `{"shameboard": "${interaction.channel.id}", "emote": "${args.emote}", "count": "${args.count}"}`, (err) => {
class shameboardCommand extends Command {
constructor() {
super('shameboard', {
aliases: ['shameboard'],
category: 'admin',
channel: 'guild',
userPermissions: ['MANAGE_CHANNELS'],
args: [
{
id: 'emote',
type: 'string',
prompt: {
start: 'What emote should be used to enter the shameboard?',
optional: true
},
default: '👎',
unordered: true
},
{
id: 'count',
prompt: {
start: 'How many times should that emote be reacted to enter the shameboard?',
optional: true
},
type: 'integer',
default: '4',
unordered: true
},
{
id: 'remove',
match: 'flag',
flag: '--remove'
}
],
description: {
content: 'Set shameobard in the current channel. --remove to remove the shameboard',
usage: '[emote] [minimum number required to enter shameboard]',
examples: ['']
}
});
}
async exec(message, args) {
if (!args.remove) {
let shameboardChannel = message.channel.id;
fs.writeFile(`./board/shame${message.guild.id}.json`, `{"shameboard": "${shameboardChannel}" , "emote": "${args.emote}", "count": "${args.count}"}`, function (err) {
if (err) {
console.log(err);
}
});
return interaction.reply(`This channel have been set as the shameboard with ${args.emote} with the minimum of ${args.count}`);
return message.channel.send(`This channel have been set as the shameboard with ${args.emote} with the minimum of ${args.count}`);
} else {
fs.unlink(`./board/shame${message.guild.id}.json`, function (err) {
if (err) return message.channel.send('There is no shameboard');
return message.channel.send('Deleted the shameboard');
});
}
},
};
}
}
module.exports = shameboardCommand;

View file

@ -0,0 +1,65 @@
const { Command } = require('discord-akairo');
class SlowmodeCommand extends Command {
constructor() {
super('Slowmode', {
aliases: ['slowmode', 'slow', 'cooldown'],
category: 'admin',
args: [
{
id: 'slowmodeNumber',
prompt: {
start: 'what do you want the delay to be between each message?',
},
type: 'integer'
},
{
id: 'realtime',
prompt: {
start: 'For how long should the slowmode last?',
optional: true
},
type: 'integer',
}
],
clientPermissions: ['MANAGE_CHANNELS'],
userPermissions: ['MANAGE_MESSAGES'],
channel: 'guild',
description: {
content: 'Put a channel in slowmode',
usage: '[1-120 slowmode] [Number of minutes the slowmode stay active]',
examples: ['5 60']
}
});
}
async exec(message,args) {
try {
let slowmodeNumber = args.slowmodeNumber;
let realtime = args.realtime;
if (slowmodeNumber > 120)
return message.channel.send('Slowmode can only be set to 120 seconds or lower!');
message.channel.setRateLimitPerUser(slowmodeNumber);
if (realtime) {
let time = 60000 * realtime;
message.channel.send(`Slowmode have been set to ${slowmodeNumber} seconds and will end in ${realtime} minutes!`);
setTimeout (function (){
message.channel.setRateLimitPerUser(0);
return message.channel.send('Slowmode is now disabled!');
}, time);
} else {
if (slowmodeNumber == 0)
return message.channel.send('Slowmode have been disabled!');
return message.channel.send(`Slowmode have been set to ${slowmodeNumber} seconds!`);
}
} catch (err) {
console.error(err);
}
}
}
module.exports = SlowmodeCommand;

View file

@ -1,38 +1,64 @@
import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js';
import fs from 'node:fs';
const { Command } = require('discord-akairo');
const fs = require('fs');
export default {
data: new SlashCommandBuilder()
.setName('starboard')
.setDescription('Set starboard to the current channel.')
.addStringOption(option =>
option.setName('emote')
.setDescription('The emote that should be used to enter the starboard.'))
.addStringOption(option =>
option.setName('count')
.setDescription('How many react for it to enter starboard.'))
.addBooleanOption(option =>
option.setName('remove')
.setDescription('Remove the starboard')
.setRequired(false)),
category: 'admin',
userPermissions: [PermissionFlagsBits.ManageChannels],
async execute(interaction, args) {
if (args.remove) {
fs.unlink(`./json/board/star${interaction.guild.id}.json`, (err) => {
if (err) {return interaction.reply('There is no starboard');}
return interaction.reply('Deleted the starboard');
});
}
else {
if (!args.emote || !args.count) return interaction.reply('You are missing the emote or the count arg!');
fs.writeFile(`./json/board/star${interaction.guild.id}.json`, `{"starboard": "${interaction.channel.id}", "emote": "${args.emote}", "count": "${args.count}"}`, (err) => {
class StarBoardCommand extends Command {
constructor() {
super('starboard', {
aliases: ['starboard'],
category: 'admin',
channel: 'guild',
userPermissions: ['MANAGE_CHANNELS'],
args: [
{
id: 'emote',
type: 'string',
prompt: {
start: 'What emote should be used to enter the shameboard?',
optional: true
},
default: '👍',
},
{
id: 'count',
type: 'integer',
prompt: {
start: 'How many times should that emote be reacted to enter the shameboard?',
optional: true
},
default: '4',
},
{
id: 'remove',
match: 'flag',
flag: '--remove'
}
],
description: {
content: 'Set starboard to the current channel. --remove to remove the starboard',
usage: '[emote] [minimum number required to enter starboard]',
examples: ['']
}
});
}
async exec(message, args) {
if (!args.remove) {
let starboardChannel = message.channel.id;
fs.writeFile(`./board/star${message.guild.id}.json`, `{"starboard": "${starboardChannel}", "emote": "${args.emote}", "count": "${args.count}"}`, function (err) {
if (err) {
console.log(err);
}
});
return interaction.reply(`This channel have been set as the starboard with ${args.emote} with the minimum of ${args.count}`);
return message.channel.send(`This channel have been set as the starboard with ${args.emote} with the minimum of ${args.count}`);
} else {
fs.unlink(`./board/star${message.guild.id}.json`, function (err) {
if (err) return message.channel.send('There is no shameboard');
return message.channel.send('Deleted the starboard');
});
}
},
};
}
}
module.exports = StarBoardCommand;

View file

@ -1,127 +1,114 @@
import { SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionFlagsBits, PermissionsBitField } from 'discord.js';
import os from 'node:os';
import fs from 'node:fs';
const { Command } = require('discord-akairo');
const Tag = require('../../models').Tag;
import db from '../../models/index.js';
const { ownerId } = process.env;
export default {
data: new SlashCommandBuilder()
.setName('tag')
.setDescription('Create custom autoresponse')
.addStringOption(option =>
option.setName('trigger')
.setDescription('The strings that will trigger the tag')
.setRequired(false))
.addStringOption(option =>
option.setName('response')
.setDescription('What it will answer back')
.setRequired(false))
.addBooleanOption(option =>
option.setName('remove')
.setDescription('(ADMIN ONLY!) Remove the tag')
.setRequired(false))
.addBooleanOption(option =>
option.setName('list')
.setDescription('List all the tags for the server')
.setRequired(false)),
category: 'admin',
userPermissions: [PermissionFlagsBits.ManageChannels],
async execute(interaction, args, client) {
await interaction.deferReply();
if (args.list) {
let tagList = await db.Tag.findAll({ attributes: ['trigger', 'response', 'ownerID'], where: { serverID: interaction.guild.id } });
if (args.trigger) {
tagList = await db.Tag.findOne({ attributes: ['trigger', 'response', 'ownerID'], where: { trigger: args.trigger, serverID: interaction.guild.id } });
class TagCommand extends Command {
constructor() {
super('tag', {
aliases: ['tag'],
category: 'admin',
userPermissions: ['MANAGE_MESSAGES'],
args: [
{
id: 'trigger',
type: 'string',
},
{
id: 'remove',
match: 'flag',
flag: '--remove'
},
{
id: 'reset',
match: 'flag',
flag: '--reset'
},
{
id: 'response',
type: 'string',
match: 'rest',
}
],
channel: 'guild',
description: {
content: 'Create custom autoresponse (--remove to delete a tag, --reset to delete EVERY tag on the server) [Click here to see the complete list of "tag"](https://cdn.discordapp.com/attachments/502198809355354133/561043193949585418/unknown.png) (Need "" if the trigger contains spaces)',
usage: '[trigger] [response]',
examples: ['"do you know da wea" Fuck off dead meme', 'hello Hello [author], how are you today?', 'hello --remove']
}
});
}
if (!tagList) return interaction.editReply('It looks like the server has no tags.');
async exec(message, args) {
const tag = await Tag.findOne({where: {trigger: args.trigger, serverID: message.guild.id}});
const ownerID = this.client.ownerID;
const path = `${os.tmpdir()}/${interaction.guild.id}.json`;
fs.writeFile(path, JSON.stringify(tagList, null, 2), function(err) {
if (err) return console.error(err);
});
return interaction.editReply({ files: [path] });
if (args.reset) {
if (message.member.hasPermission('ADMINISTRATOR')) {
message.channel.send('Are you sure you want to delete EVERY tag? There is no way to recover them. y/n');
const filter = m => m.content && m.author.id == message.author.id;
return message.channel.awaitMessages(filter, {time: 5000, max: 1, errors: ['time'] })
.then(async messages => {
let messageContent = messages.map(messages => messages.content.toLowerCase());
if (messageContent[0] === 'y' || messageContent[0] === 'yes') {
Tag.destroy({where: {serverID: message.guild.id}});
return message.channel.send('Tags have been reset.');
} else {
return message.channel.send('Not reseting.');
}
})
.catch(err => {
console.error(err);
return message.channel.send('Took too long to answer. didin\'t update anything.');
});
} else {
return message.channel.send('Only person with the `ADMINISTRATOR` rank can reset tags.');
}
}
const tag = await db.Tag.findOne({ where: { trigger: args.trigger, serverID: interaction.guild.id } });
if (args.remove) {
if (tag) {
if (tag.get('ownerID') == interaction.user.id || interaction.member.permissionsIn(interaction.channel).has(PermissionsBitField.Flags.Administrator) || interaction.user.id == ownerId) {
db.Tag.destroy({ where: { trigger: args.trigger, serverID: interaction.guild.id } });
return interaction.editReply('successfully deleted the following tag: ' + args.trigger);
if (tag.get('ownerID') == message.author.id || message.member.hasPermission('ADMINISTRATOR') || message.author.id == ownerID) {
Tag.destroy({where: {trigger: args.trigger, serverID: message.guild.id}});
return message.channel.send('successfully deleted the following tag: ' + args.trigger);
} else {
return message.channel.send(`You are not the owner of this tag, if you think it is problematic ask an admin to remove it by doing ${this.client.commandHandler.prefix[0]}tag ${args.trigger} --remove`);
}
else {
return interaction.editReply(`You are not the owner of this tag, if you think it is problematic ask a user with the 'Administrator' permission to remove it by doing ${this.client.commandHandler.prefix[0]}tag ${args.trigger} --remove`);
}
}
else {
return interaction.editReply('Did not find the specified tag, are you sure it exist?');
} else {
return message.channel.send('Did not find the specified tag, are you sure it exist?');
}
}
if (!args.trigger) return interaction.editReply('You need to specify what you want me to respond to.');
if (!args.response) return interaction.editReply('You need to specify what you want me to answer with.');
if (!args.trigger) return message.channel.send('Please provide a trigger in order to create a tag.');
if (!args.response) return message.channel.send('Please provide the response for that tag');
if (!tag) {
const body = { trigger: args.trigger, response: args.response, ownerID: interaction.user.id, serverID: interaction.guild.id };
await db.Tag.create(body);
return interaction.editReply(`tag have been set to ${args.trigger} : ${args.response}`);
const body = {trigger: args.trigger, response: args.response, ownerID: message.author.id, serverID: message.guild.id};
await Tag.create(body);
return message.channel.send(`tag have been set to ${args.trigger} : ${args.response}`);
} else if (tag.get('ownerID') == message.author.id || message.member.hasPermission('ADMINISTRATOR') || message.author.id == ownerID) {
message.channel.send('This tag already exist, do you want to update it? y/n');
const filter = m => m.content && m.author.id == message.author.id;
message.channel.awaitMessages(filter, {time: 5000, max: 1, errors: ['time'] })
.then(async messages => {
let messageContent = messages.map(messages => messages.content.toLowerCase());
if (messageContent[0] === 'y' || messageContent[0] === 'yes') {
const body = {trigger: args.trigger, response: args.response, ownerID: message.author.id, serverID: message.guild.id};
await Tag.update(body, {where: {trigger: args.trigger, serverID: message.guild.id}});
return message.channel.send(`tag have been set to ${args.trigger} : ${args.response}`);
} else {
return message.channel.send('Not updating.');
}
})
.catch(err => {
console.error(err);
return message.channel.send('Took too long to answer. didin\'t update anything.');
});
} else {
return message.channel.send(`You are not the owner of this tag, if you think it is problematic ask an admin to remove it by doing ${this.client.commandHandler.prefix[0]}tag ${args.trigger} --remove`);
}
else if (tag.get('ownerID') == interaction.user.id || interaction.member.permissionsIn(interaction.channel).has('ADMINISTRATOR') || interaction.user.id == ownerId) {
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`edit${interaction.user.id}${interaction.id}`)
.setLabel('Edit')
.setStyle(ButtonStyle.Primary),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`remove${interaction.user.id}${interaction.id}`)
.setLabel('Remove')
.setStyle(ButtonStyle.Danger),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`nothing${interaction.user.id}${interaction.id}`)
.setLabel('Do nothing')
.setStyle(ButtonStyle.Secondary),
);
await interaction.editReply({ content: 'This tag already exist, do you want to update it, remove it or do nothing?', components: [row], ephemeral: true });
return listenButton(client, interaction, args, interaction.user);
}
else {
return interaction.editReply(`You are not the owner of this tag, if you think it is problematic ask an admin to remove it by doing ${this.client.commandHandler.prefix[0]}tag ${args.trigger} --remove`);
}
},
};
async function listenButton(client, interaction, args, user = interaction.user, originalId = interaction.id) {
client.once('interactionCreate', async (interactionMenu) => {
if (user !== interactionMenu.user) return listenButton(client, interaction, args, user, originalId);
if (!interactionMenu.isButton()) return;
await interactionMenu.update({ components: [] });
if (interactionMenu.customId === `edit${interaction.user.id}${originalId}`) {
const body = { trigger: args.trigger, response: args.response, ownerID: interaction.user.id, serverID: interaction.guild.id };
db.Tag.update(body, { where: { serverID: interaction.guild.id } });
return interaction.editReply({ content: `The tag ${args.trigger} has been set to ${args.response}`, ephemeral: true });
}
else if (interactionMenu.customId === `remove${interaction.user.id}${originalId}`) {
db.Tag.destroy({ where: { trigger: args.trigger, serverID: interaction.guild.id } });
return interaction.editReply({ content: `The tag ${args.trigger} has been deleted`, ephemeral: true });
}
else {
return interaction.editReply({ content: 'Nothing has been changed.', ephemeral: true });
}
});
}
}
module.exports = TagCommand;

40
commands/admin/unban.js Normal file
View file

@ -0,0 +1,40 @@
const { Command } = require('discord-akairo');
class UnbanCommand extends Command {
constructor() {
super('unban', {
aliases: ['unban'],
category: 'admin',
args: [
{
id: 'member',
type: 'integer',
prompt: {
start: 'which member do you want to unban?',
retry: 'This doesn\'t look like an ID, please try again'
}
}
],
clientPermissions: ['BAN_MEMBERS'],
userPermissions: ['BAN_MEMBERS'],
channel: 'guild',
description: {
content: 'unban users',
usage: '[user id]',
examples: ['267065637183029248']
}
});
}
async exec(message, args) {
message.guild.members.unban(args.member.toString())
.then(() => {
return message.reply('user was succesfully unbanned.');
})
.catch(() => {
return message.reply('Could not unban this user, is he banned in the first place?');
});
}
}
module.exports = UnbanCommand;

View file

@ -1,75 +1,74 @@
import { SlashCommandBuilder, ButtonBuilder, ButtonStyle, ActionRowBuilder, PermissionFlagsBits } from 'discord.js';
import db from '../../models/index.js';
const { Command } = require('discord-akairo');
const joinChannel = require('../../models').joinChannel;
export default {
data: new SlashCommandBuilder()
.setName('welcome')
.setDescription('Set a join message')
.addStringOption(option =>
option.setName('message')
.setDescription('The message you want the bot to say when someone join in the current channel.')),
category: 'admin',
userPermissions: [PermissionFlagsBits.ManageChannels],
async execute(interaction, args, client) {
const join = await db.joinChannel.findOne({ where: { guildID: interaction.guild.id } });
if (!join && !args.message) {
return interaction.reply({ content: 'You need a message for me to say anything!', ephemeral: true });
}
else if (!join) {
const body = { guildID: interaction.guild.id, channelID: interaction.channel.id, message: args.message };
await db.joinChannel.create(body);
return interaction.reply({ content: `The join message have been set with ${args.message}`, ephemeral: true });
}
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId(`edit${interaction.user.id}${interaction.id}`)
.setLabel('Edit')
.setStyle(ButtonStyle.Primary),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`remove${interaction.user.id}${interaction.id}`)
.setLabel('Remove')
.setStyle(ButtonStyle.Danger),
)
.addComponents(
new ButtonBuilder()
.setCustomId(`nothing${interaction.user.id}${interaction.id}`)
.setLabel('Do nothing')
.setStyle(ButtonStyle.Secondary),
);
await interaction.reply({ content: 'The server already has a message set, do you want to edit it or remove it?', components: [row], ephemeral: true });
return listenButton(client, interaction, args, interaction.user);
},
};
async function listenButton(client, interaction, args, user = interaction.user, originalId = interaction.id) {
client.once('interactionCreate', async (interactionMenu) => {
if (user !== interactionMenu.user) return listenButton(client, interaction, args, user, originalId);
if (!interactionMenu.isButton()) return;
await interactionMenu.update({ components: [] });
if (interactionMenu.customId === `edit${interaction.user.id}${originalId}`) {
if (!args.message) {
return interaction.reply({ content: 'You need to input a message for me to edit!', ephemeral: true });
class welcomeCommand extends Command {
constructor() {
super('welcome', {
aliases: ['welcome', 'join'],
category: 'admin',
channel: 'guild',
userPermissions: ['MANAGE_CHANNELS'],
args: [
{
id: 'remove',
match: 'flag',
flag: '--remove'
},
{
id: 'message',
type: 'string',
match: 'rest',
default: 'Welcome [member] to [server]!'
}
],
description: {
content: 'Send a message to the current channel when a person join, you can use [member] to show the member username and [server] to show the name of the server',
usage: '[welcome message]',
examples: ['everyone welcome [adjectives] [member] and welcome on [server]']
}
});
}
async exec(message, args) {
const join = await joinChannel.findOne({where: {guildID: message.guild.id}});
if (args.remove) {
if (join) {
join.destroy({where: {guildID: message.guild.id, channelID: message.channel.id}});
return message.channel.send('successfully deleted the join message');
} else {
return message.channel.send('Did not find the a join message, are you sure you have one setup?');
}
const body = { guildID: interaction.guild.id, channelID: interaction.channel.id, message: args.message };
await db.joinChannel.update(body, { where: { guildID: interaction.guild.id } });
return interaction.editReply({ content: `The join message has been set to ${args.message}`, ephemeral: true });
}
else if (interactionMenu.customId === `remove${interaction.user.id}${originalId}`) {
db.joinChannel.destroy({ where: { guildID: interaction.guild.id, channelID: interaction.channel.id } });
return interaction.editReply({ content: 'The join message has been deleted.', ephemeral: true });
if (!args.message) {
return message.channel.send('Please provide a message');
}
else {
return interaction.editReply({ content: 'Nothing has been changed.', ephemeral: true });
if (!join) {
const body = {guildID: message.guild.id, channelID: message.channel.id, message: args.message};
await joinChannel.create(body);
return message.channel.send(`The join message have been set with ${args.message}`);
} else {
message.channel.send('The server already have a join message, do you want to replace it? y/n');
const filter = m => m.content && m.author.id == message.author.id;
message.channel.awaitMessages(filter, {time: 5000, max: 1, errors: ['time'] })
.then(async messages => {
let messageContent = messages.map(messages => messages.content);
if (messageContent[0] === 'y' || messageContent[0] === 'yes') {
const body = {guildID: message.guild.id, channelID: message.channel.id, message: args.message};
await joinChannel.update(body, {where: {guildID: message.guild.id}});
return message.channel.send(`The join message have been set with ${args.message}`);
} else {
return message.channel.send('Not updating.');
}
})
.catch(err => {
console.error(err);
return message.channel.send('Took too long to answer. didin\'t update anything.');
});
}
});
}
}
module.exports = welcomeCommand;

View file

@ -0,0 +1,68 @@
const { Command } = require('discord-akairo');
const whitelistWord = require('../../models').whitelistWord;
class whitelistWordCommand extends Command {
constructor() {
super('whitelistWord', {
aliases: ['whitelistWord', 'unwhitelistword', 'whitelist', 'unwhitelist'],
category: 'admin',
userPermissions: ['MANAGE_MESSAGES'],
clientPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES'],
args: [
{
id: 'word',
type: 'string',
match: 'rest'
},
{
id: 'remove',
match: 'flag',
flag: '--remove'
},
{
id: 'removeall',
match: 'flag',
flag: '--removeall'
}
],
channel: 'guild',
description: {
content: 'Whitelist word so it is not affected by the banned word, unwhitelistword alias and --removeaall to remove every banned whitelisted word',
usage: '[word to whitelist]',
examples: ['sexuality']
}
});
}
async exec(message, args) {
if (!args.word) args.word = '';
args.word = args.word.replace(/[\u0250-\ue007]/g, '');
const WhitelistWord = await whitelistWord.findOne({where: {word: args.word.toLowerCase(), serverID: message.guild.id}});
if (message.util.parsed.alias == 'unwhitelistword' || message.util.parsed.alias == 'unwhitelist' || args.remove || args.removeall) {
if (args.removeall) {
whitelistWord.destroy({where: {serverID: message.guild.id}});
return message.channel.send('The whitelisted words has been reset.');
}
if (WhitelistWord) {
whitelistWord.destroy({where: {word: args.word.toLowerCase(), serverID: message.guild.id}});
return message.channel.send(`The word ${args.word.toLowerCase()} is no longer whitelisted`);
} else {
return message.channel.send('There was no word to unwhitelist');
}
}
if (!args.word) return message.channel.send('Please specify a word to whiteliste!');
if (!WhitelistWord) {
const body = {word: args.word.toLowerCase(), serverID: message.guild.id};
await whitelistWord.create(body);
return message.channel.send(`The word ${args.word.toLowerCase()} has been whitelisted`);
} else {
message.channel.send('This word is already whitelisted');
}
}
}
module.exports = whitelistWordCommand;

View file

@ -1,61 +1,65 @@
import { SlashCommandBuilder } from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import TurndownService from 'turndown';
const turndown = new TurndownService();
import fetch from 'node-fetch';
const { Command } = require('discord-akairo');
const fetch = require('node-fetch');
const boards = require('4chan-boards');
const Turndown = require('turndown');
let turndown = new Turndown();
import fourChan from '../../json/4chan.json' with {type: 'json'};
class FourchanCommand extends Command {
constructor() {
super('4chan', {
aliases: ['4chan'],
clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
category: 'fun',
export default {
data: new SlashCommandBuilder()
.setName('4chan')
.setDescription('Send random images from a 4chan board of your choosing!')
.addStringOption(option =>
option.setName('board')
.setDescription('The board you wish to see')
.setRequired(true)),
category: 'fun',
async execute(interaction, args) {
let board = args.board;
args: [
{
id: 'board',
type: 'string',
prompt: {
start: 'Which board do you want to browse?',
},
match: 'rest'
}
],
description: {
content: 'Send random images from a 4chan board of your choosing!',
usage: '[board]',
examples: ['vg']
}
});
}
if (fourChan[board] == undefined) {
return interaction.reply({ content: 'Uh oh! The board you are looking for does not exist? You think this is a mistake? Please send a feedback telling me so!', ephemeral: true });
}
async exec(message, args) {
if (boards.getType(args.board) === boards.NSFW && !message.channel.nsfw) return message.channel.send('Sorry, this board only works in nsfw channels!');
if (fourChan[board].nsfw && !interaction.channel.nsfw) {
return interaction.reply({ content: 'Uh oh! This is a NSFW board! Try again in a NSFW channel!', ephemeral: true });
}
await interaction.deferReply({ ephemeral: false });
if (!args.board) return;
args.board = args.board.replace(/\//g, '');
board = board.replace(/\//g, '');
let i = Math.floor((Math.random() * 5) + 1);
fetch(`https://a.4cdn.org/${board}/${i}.json`).then((response) => {
fetch(`https://a.4cdn.org/${args.board}/${i}.json`).then((response) => {
return response.json();
}).then((response) => {
if (!response.threads) {
return interaction.editReply('Not a valid board! Try again!');
}
if (!response.threads)
return message.channel.send('Not a valid board! Try again!');
i = Math.floor((Math.random() * response.threads.length) + 1);
// Loop until it found a threads
while (!response.threads[i]) {
while(!response.threads[i]) {
i = Math.floor((Math.random() * response.threads.length) + 1);
}
// If post is sticky search again
while (response.threads[i].posts[0].sticky == 1 || !response.threads[i].posts) {
while(response.threads[i].posts[0].sticky == 1 || !response.threads[i].posts) {
i = Math.floor((Math.random() * response.threads.length));
}
let title = response.threads[i].posts[0].sub;
let description = response.threads[i].posts[0].com;
let boardName = fourChan[board].title;
if (boardName == undefined) {
boardName = board;
}
let boardName = boards.getName(args.board);
if (boardName == undefined) boardName = args.board;
// If title or description is undefined, change it to "no title/description"
if (!description) {
@ -66,28 +70,28 @@ export default {
title = 'No title';
}
const FourchanEmbed = new EmbedBuilder()
.setColor(interaction.member ? interaction.member.displayHexColor : 'Navy')
const FourchanEmbed = this.client.util.embed()
.setColor(message.member ? message.member.displayHexColor : 'NAVY')
.setTitle(turndown.turndown(title))
.setDescription(turndown.turndown(description))
.setImage(`https://i.4cdn.org/${board}/${response.threads[i].posts[0].tim}${response.threads[i].posts[0].ext}`)
.setURL(`https://boards.4chan.org/${board}/thread/${response.threads[i].posts[0].no}/${response.threads[i].posts[0].semantic_url}`)
.setFooter({ text: `${boardName} | ${response.threads[i].posts[0].name} | ${response.threads[i].posts[0].no} | ${response.threads[i].posts[0].now}` });
.setImage(`https://i.4cdn.org/${args.board}/${response.threads[i].posts[0].tim}${response.threads[i].posts[0].ext}`)
.setURL(`https://boards.4chan.org/${args.board}/thread/${response.threads[i].posts[0].no}/${response.threads[i].posts[0].semantic_url}`)
.setFooter(`${boardName} | ${response.threads[i].posts[0].name} | ${response.threads[i].posts[0].no} | ${response.threads[i].posts[0].now}`);
// If file type dosen't work on embed, send it as a link
if (response.threads[i].posts[0].ext == '.webm' || response.threads[i].posts[0].ext == '.pdf' || response.threads[i].posts[0].ext == '.swf') {
interaction.editReply({ embeds: [FourchanEmbed] });
interaction.followUp(`https://i.4cdn.org/${board}/${response.threads[i].posts[0].tim}${response.threads[i].posts[0].ext}`);
message.channel.send(FourchanEmbed);
message.channel.send(`https://i.4cdn.org/${args.board}/${response.threads[i].posts[0].tim}${response.threads[i].posts[0].ext}`);
}
else {
interaction.editReply({ embeds: [FourchanEmbed] });
} else {
message.channel.send(FourchanEmbed);
}
})
.catch((err) => {
if (err.type == 'invalid-json') return interaction.editReply('Could not find the board! Try again!');
if (err.type == 'invalid-json') return message.channel.send('Could not find the board! Try again!');
console.error(err);
return interaction.editReply('Uh-oh, an error has occurred! Try again! If this keeps happening, tell the developers!');
return message.channel.send('Uh-oh, an error has occurred! Try again! If this keeps happening, tell the developers!');
});
},
};
}
}
module.exports = FourchanCommand;

58
commands/fun/asciify.js Normal file
View file

@ -0,0 +1,58 @@
const { Command } = require('discord-akairo');
const attachment = require('../../utils/attachment');
const os = require('os');
const fs = require('fs');
const asciify = require('asciify-image');
let options = {
fit: 'box',
width: 200,
height: 50,
color: false
};
class asciifyCommand extends Command {
constructor() {
super('asciify', {
aliases: ['asciify'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'link',
type: 'url',
}
],
cooldown: 600000,
ratelimit: 2,
description: {
content: 'Transform your image into ASCII! (This can be a bit spammy, so be careful!)',
usage: '[image in attachment]',
examples: ['image in attachment']
}
});
}
async exec(message, args) {
let url;
if (args.link)
url = args.link.href;
else
url = await attachment(message);
return asciify(url, options, function (err, asciified) {
if (err) throw err;
// Print to console
fs.writeFile(`${os.tmpdir()}/${message.id}ascii.txt`, asciified, function (err) {
if (err) {
console.log(err);
}
return message.channel.send({files: [`${os.tmpdir()}/${message.id}ascii.txt`]});
});
//return message.channel.send(asciified, { split: true, code: true });
});
}
}
module.exports = asciifyCommand;

View file

@ -1,60 +1,91 @@
/* TODO
*
* Merge with commands/fun/image2audio.js
*
*/
import { SlashCommandBuilder } from 'discord.js';
import fs from 'node:fs';
import os from 'node:os';
import fetch from 'node-fetch';
import util from 'node:util';
import stream from 'node:stream';
import utils from '../../utils/videos.js';
const { Command } = require('discord-akairo');
const attachment = require('../../utils/attachment');
const ffmpeg = require('fluent-ffmpeg');
const fetch = require('node-fetch');
const fs = require('fs');
const os = require('os');
export default {
data: new SlashCommandBuilder()
.setName('audio2image')
.setDescription('Transform an audio file into an image.')
.addAttachmentOption(option =>
option.setName('audio')
.setDescription('The audio that will become image.')
.setRequired(true)),
category: 'fun',
alias: ['a2i'],
async execute(interaction, args) {
if (!args.audio) return interaction.reply('Please attach an image with your message.');
await interaction.deferReply();
ifExistDelete(`${os.tmpdir()}/${args.audio.name}`);
ifExistDelete(`${os.tmpdir()}/${args.audio.name}.png`);
ifExistDelete(`${os.tmpdir()}/${args.audio.name}.sw`);
ifExistDelete(`${os.tmpdir()}/${args.audio.name}.mp3`);
const streamPipeline = util.promisify(stream.pipeline);
const res = await fetch(args.audio.url);
if (!res.ok) return interaction.editReply('An error has occured while trying to download your image.');
await streamPipeline(res.body, fs.createWriteStream(`${os.tmpdir()}/${args.audio.name}`));
await utils.ffmpeg(['-i', `${os.tmpdir()}/${args.audio.name}`, '-sample_rate', '44100', '-ac', '1', '-f', 's16le', '-acodec', 'pcm_s16le', `${os.tmpdir()}/${args.audio.name}.sw`]);
await utils.ffmpeg(['-pixel_format', 'rgb24', '-video_size', '128x128', '-f', 'rawvideo', '-i', `${os.tmpdir()}/${args.audio.name}.sw`, '-frames:v', '1', `${os.tmpdir()}/${args.audio.name}.png`]);
const file = fs.statSync(`${os.tmpdir()}/${args.audio.name}.png`);
const fileSize = (file.size / 1000000.0).toFixed(2);
if (fileSize > await utils.getMaxFileSize(interaction.guild)) return interaction.editReply('error');
interaction.editReply({ content: `Image file is ${fileSize} MB` });
return interaction.followUp({ files: [`${os.tmpdir()}/${args.audio.name}.png`] });
},
};
async function ifExistDelete(path) {
if (fs.existsSync(path)) {
fs.rm(path, (err) => {
console.log('deleted');
if (err) {
return;
class audio2imageCommand extends Command {
constructor() {
super('audio2image', {
aliases: ['audio2image', 'a2i'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'video_size',
match: 'option',
flag: '--size',
default: '640x480'
},
{
id: 'link',
type: 'url',
}
],
description: {
content: 'Transform an audio file into an image.Use --size (a number) to get a bigger image! (NOTE: bigger image might fail, so be careful!)',
usage: '[link to audio] [--size anumber]',
examples: ['https://cdn.discordapp.com/attachments/532987690145021982/682654351772221480/jeff.wav (optional) --size 1920x1080']
}
});
}
async exec(message, args) {
let url;
if (args.link)
url = args.link.href;
else
url = await attachment(message);
let loadingmsg = await message.channel.send('Processing <a:loadingmin:527579785212329984>');
if (!url) return message.channel.send('Please attach an audio file or use an url');
fetch(url)
.then(res => {
const dest = fs.createWriteStream(`${os.tmpdir()}/${message.id}`);
res.body.pipe(dest);
dest.on('finish', () => {
ffmpeg(`${os.tmpdir()}/${message.id}`) // Convert to raw pcm
.audioBitrate(44100)
.audioChannels(1)
.format('s16le')
.audioCodec('pcm_s16le')
.output(`${os.tmpdir()}/${message.id}1.sw`)
.on('error', (err, stdout, stderr) => {
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Uh oh, an error has occurred!');
})
.on('end', () => {
ffmpeg()
.input(`${os.tmpdir()}/${message.id}1.sw`)
.inputOption('-pixel_format rgb24')
.inputOption(`-video_size ${args.video_size}`)
.inputFormat('rawvideo')
.frames('1')
.output(`${os.tmpdir()}/a2i${message.id}.png`)
.on('error', (err, stdout, stderr) => {
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Uh oh, an error has occurred! The image size is most likely bigger than the content! Try again!');
})
.on('end', () => {
console.log('finished');
loadingmsg.delete();
let file = fs.statSync(`${os.tmpdir()}/a2i${message.id}.png`);
let fileSize = (file.size / 1000000.0).toFixed(2);
return message.channel.send(`Image is ${fileSize} MB for ${args.video_size} resolution`, {files: [`${os.tmpdir()}/a2i${message.id}.png`]})
.catch(() => {
return message.channel.send(`End result is too big to fit on discord! File is ${fileSize} MB! Unless it's GB or even TB, in which case that is a huge image!`); });
})
.run();
})
.run();
});
});
}
}
module.exports = audio2imageCommand;

55
commands/fun/cleverbot.js Normal file
View file

@ -0,0 +1,55 @@
const { Command } = require('discord-akairo');
const cleverbot = require('cleverbot-free');
let conversation = {};
class CleverBotCommand extends Command {
constructor() {
super('CleverBot', {
aliases: ['CleverBot', 'cb'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'message',
type: 'string',
prompt: {
start: 'What do you want to say to cleverbot?'
},
match: 'rest'
},
],
description: {
content: 'Talk to cleverbot!',
usage: '[message]',
examples: ['Hello']
}
});
}
async exec(message, args) {
let loadingmsg = await message.reply('Processing! <a:loadingmin:527579785212329984>');
if (!conversation[message.guild.id]) conversation[message.guild.id] = [];
if (!conversation[0]) {
cleverbot(args.message).then(response => {
conversation[message.guild.id].push(args.message);
conversation[message.guild.id].push(response);
return message.reply(response)
.then(() => {
loadingmsg.delete();
});
});
} else {
cleverbot(args.message, conversation[message.guild.id]).then(response => {
conversation[message.guild.id].push(args.message);
conversation[message.guild.id].push(response);
return message.reply(response)
.then(() => {
loadingmsg.delete();
});
});
}
console.log(conversation);
}
}
module.exports = CleverBotCommand;

95
commands/fun/curses.js Normal file
View file

@ -0,0 +1,95 @@
const { Command } = require('discord-akairo');
const path = require('path');
const os = require('os');
const fs = require('fs');
const { Buffer } = require('buffer');
const attachment = require('../../utils/attachment');
const downloader = require('../../utils/download');
class cursesCommand extends Command {
constructor() {
super('curses', {
aliases: ['curses', 'curse'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'link',
type: 'url',
},
{
id: 'webm',
match: 'flag',
flag: ['--webm']
},
],
description: {
content: 'Mess with video length. webm = expanding length and mp4 = very long length',
usage: '[link or attachment]',
examples: ['https://www.youtube.com/watch?v=QLCPgZ51poU']
}
});
}
async exec(message, args) {
let replaceAt = function(string, index, replacement) {
return string.substr(0, index) + replacement + string.substr(index + replacement.length);
};
let link;
if (args.link)
link = args.link.href;
else
link = await attachment(message);
if (!link) return message.channel.send('Please specify the URL of the video you want to curse.');
let ext = path.extname(link.toLowerCase());
console.log(ext);
if (ext !== '.webm' && ext !== '.mp4')
ext = '.mp4';
if (args.webm) ext = '.webm';
let loadingmsg = await message.channel.send('Processing <a:loadingmin:527579785212329984>');
downloader(link, [`--format=${ext.replace('.', '')}`], `${os.tmpdir()}/${message.id}${ext}`)
.on('error', async err => {
loadingmsg.delete();
console.error(err);
return message.channel.send(err, { code: true });
})
.on('end', output => {
let file = fs.readFileSync(output).toString('hex');
let searchHex = '6d766864';
let replaceHex = '0000180FFFFFF7F';
let skipByte = 34;
let endResult;
if (ext === '.webm') {
searchHex = '2ad7b1';
replaceHex = '42FFB060';
skipByte = 8;
endResult = replaceAt(file, file.indexOf(searchHex) + file.substring(file.indexOf(searchHex)).indexOf('4489') + skipByte, replaceHex);
} else {
endResult = replaceAt(file, file.indexOf(searchHex) + skipByte, replaceHex);
}
fs.writeFileSync(`${os.tmpdir()}/cursed${message.id}${ext}`, Buffer.from(endResult, 'hex'));
message.delete();
return message.channel.send(`Cursed by ${message.author}`, {files: [`${os.tmpdir()}/cursed${message.id}${ext}`]})
.catch(err => {
console.error(err);
return message.channel.send('Video is too big! try again with something smaller');
})
.then(() => {
loadingmsg.delete();
});
});
}
}
module.exports = cursesCommand;

View file

@ -0,0 +1,29 @@
const { Command } = require('discord-akairo');
const fs = require('fs');
const fetch = require('node-fetch');
class dosentexistCommand extends Command {
constructor() {
super('dosentexist', {
aliases: ['doesntexist', 'thispersondoesnotexist', 'de'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
description: {
content: 'Send images from thispersondoesnotexist.com!',
usage: '',
examples: ['']
}
});
}
async exec(message) {
fetch('https://thispersondoesnotexist.com/image')
.then(res => {
const dest = fs.createWriteStream('./asset/img/de.png');
res.body.pipe(dest);
dest.on('finish', () => {
return message.reply({files: ['./asset/img/de.png']});
});
}); }
}
module.exports = dosentexistCommand;

25
commands/fun/explosm.js Normal file
View file

@ -0,0 +1,25 @@
const { Command } = require('discord-akairo');
const extract = require('meta-extractor');
class explosmCommand extends Command {
constructor() {
super('explosm', {
aliases: ['explosm', 'rcg'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
description: {
content: 'Comic randomly generated from http://explosm.net/rcg',
usage: '',
examples: ['']
}
});
}
async exec(message) {
extract({ uri: 'http://explosm.net/rcg' }, (err, res) => {
return message.reply({files: [res.ogImage]});
});
}
}
module.exports = explosmCommand;

76
commands/fun/fakebot.js Normal file
View file

@ -0,0 +1,76 @@
const { Command } = require('discord-akairo');
class fakebotCommand extends Command {
constructor() {
super('fakebot', {
aliases: ['fakebot', 'fakeuser', 'fakemember'],
category: 'fun',
clientPermissions: ['MANAGE_WEBHOOKS'],
args: [
{
id: 'user',
type: 'user',
prompt: {
start: 'Who should i fake?',
retry: 'Didn\'t find any user named like that, please say the name again.'
}
},
{
id: 'message',
type: 'string',
prompt: {
start: 'What message should i send?',
},
match: 'rest',
}
],
channel: 'guild',
description: {
content: 'Fake a bot/user with webhook',
usage: '[user] [message]',
examples: ['Supositware#1616 hello!']
}
});
}
async exec(message, args) {
let Attachment = (message.attachments).array();
let url;
let username = args.user.username;
let member = message.guild.members.resolve(args.user.id);
// Get attachment link
if (Attachment[0]) {
url = Attachment[0].url;
}
// Show nickname if user is in guild
if (member) {
if (member.nickname) {
username = member.nickname;
}
}
message.channel.createWebhook(username, {
avatar: args.user.displayAvatarURL(),
reason: `Fakebot/user command triggered by: ${message.author.username}`
})
.then(webhook => {
// Have to edit after creation otherwise the picture doesn't get applied
webhook.edit({
name: username,
avatar: args.user.displayAvatarURL(),
reason: `Fakebot/user command triggered by: ${message.author.username}`
});
this.client.fetchWebhook(webhook.id, webhook.token)
.then(webhook => {
message.delete();
if (url)
webhook.send(args.message, {files: [url]});
else
webhook.send(args.message);
});
});
}
}
module.exports = fakebotCommand;

View file

@ -1,49 +0,0 @@
import { SlashCommandBuilder } from 'discord.js';
import { PermissionFlagsBits } from 'discord.js';
export default {
data: new SlashCommandBuilder()
.setName('fakeuser')
.setDescription('Fake a user with webhooks')
.addMentionableOption(option =>
option.setName('user')
.setDescription('Who do you want to fake?')
.setRequired(true))
.addStringOption(option =>
option.setName('message')
.setDescription('What message do you want me to send?')
.setRequired(true))
.addAttachmentOption(option =>
option.setName('image')
.setDescription('Optional attachment.')
.setRequired(false)),
category: 'fun',
clientPermissions: [ PermissionFlagsBits.ManageWebhooks ],
async execute(interaction, args) {
await interaction.deferReply({ ephemeral: true });
await interaction.guild.members.fetch();
const member = args.user;
const message = args.message;
const attachment = args.image;
const username = member.nickname ? member.nickname : member.user.username;
const webhook = await interaction.channel.createWebhook({
name: username,
avatar: member.user.displayAvatarURL(),
reason: `Fakebot/user command triggered by: ${interaction.user.username}`,
});
if (attachment) {
await webhook.send({ content: message, files: [attachment] });
}
else {
await webhook.send({ content: message });
}
await webhook.delete(`Fakebot/user command triggered by: ${interaction.user.username}`);
if (interaction.isMessage) {
await interaction.delete();
await interaction.deleteReply();
}
else {
await interaction.editReply({ content: `Faked the user ${member}` });
}
},
};

View file

@ -1,93 +0,0 @@
// Tried something to make it work purely with slash commands but it did not work.
// The interaction created from a modal lack the showModal function so I can't just call another modal on top
// A "solution" for this that I could see is creating a button between each response asking the player to continue or stop which would create a new interaction and (maybe) allow to display a new modal
import { SlashCommandBuilder, ActionRowBuilder, TextInputBuilder, SelectMenuBuilder, ModalBuilder, TextInputStyle, InteractionType } from 'discord.js';
export default {
data: new SlashCommandBuilder()
.setName('guess')
.setDescription('Guess the number'),
category: 'fun',
async execute(interaction, args, client) {
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
.setCustomId('difficulty')
.setPlaceholder('Nothing selected')
.addOptions([
{ label: 'Easy', value: '100' },
{ label: 'Normal', value: '1000' },
{ label: 'Hard', value: '10000' },
]),
);
await interaction.reply({ content: 'Which difficulty do you want to play?', ephemeral: true, components: [row] });
let numberTry = 0;
let secretnumber = 0;
client.on('interactionCreate', async (interactionMenu) => {
if (interaction.user !== interactionMenu.user) return;
const modal = new ModalBuilder()
.setCustomId('guessModal')
.setTitle('Your guess');
const textRow = new ActionRowBuilder()
.addComponents(
new TextInputBuilder()
.setCustomId('input')
.setLabel('What is the number?')
.setStyle(TextInputStyle.Short),
);
modal.addComponents(textRow);
async function tryAgain(input) {
if (input != secretnumber) {
if (input > secretnumber) {
modal.setTitle('Its less!\nWhat is the number?');
}
else if (input < secretnumber) {
modal.setTitle('Its more!\nWhat is the number?');
}
}
await interactionMenu.showModal(modal);
}
async function checkNumber(input) {
numberTry++;
if (input.toLowerCase() === 'stop') {
return interaction.reply('Ok, let\'s stop playing :(');
}
else if (input != secretnumber) {
console.log('trying again');
tryAgain(input);
}
else if (numberTry > 1) {
return interaction.reply(`Congratulations! You won! It took you ${numberTry} turns!`);
}
else {
return interaction.reply('Congratulations! You won! It took you 1 Turn!');
}
}
if (interactionMenu.type === InteractionType.ModalSubmit) {
if (interactionMenu.customId === 'guessModal') {
const input = interactionMenu.fields.getTextInputValue('input');
checkNumber(input);
}
}
else if (interactionMenu.isSelectMenu()) {
if (interactionMenu.customId === 'difficulty') {
secretnumber = Math.floor((Math.random() * parseInt(interactionMenu.values[0])) + 1);
console.log(secretnumber);
// await interaction.followUp({ content: 'What is the number?', ephemeral: true });
await interactionMenu.showModal(modal);
}
}
});
},
};

24
commands/fun/ib.js Normal file
View file

@ -0,0 +1,24 @@
const { Command } = require('discord-akairo');
const fetch = require('node-fetch');
class InspiroBotCommand extends Command {
constructor() {
super('InspiroBot', {
aliases: ['inspirobot', 'ib'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
description: {
content: 'Send images from Inspirobot',
usage: '',
examples: ['']
}
});
}
async exec(message) {
fetch('http://inspirobot.me/api?generate=true')
.then(res => res.text())
.then(body => message.channel.send({files: [body]}));
}
}
module.exports = InspiroBotCommand;

View file

@ -1,59 +1,87 @@
/* TODO
*
* Merge with commands/fun/audio2image.js
*
*/
import { SlashCommandBuilder } from 'discord.js';
import fs from 'node:fs';
import os from 'node:os';
import fetch from 'node-fetch';
import util from 'node:util';
import stream from 'node:stream';
import utils from '../../utils/videos.js';
const { Command } = require('discord-akairo');
const attachment = require('../../utils/attachment');
const ffmpeg = require('fluent-ffmpeg');
const fetch = require('node-fetch');
const fs = require('fs');
const os = require('os');
export default {
data: new SlashCommandBuilder()
.setName('image2audio')
.setDescription('Transform an image binary data into audio ( MIGHT BE VERY LOUD )')
.addAttachmentOption(option =>
option.setName('img')
.setDescription('The image that will become audio. Only tested with png and jpg.')
.setRequired(true)),
category: 'fun',
alias: ['i2a'],
async execute(interaction, args) {
if (!args.img) return interaction.reply('Please attach an image with your message.');
await interaction.deferReply();
ifExistDelete(`${os.tmpdir()}/${args.img.name}`);
ifExistDelete(`${os.tmpdir()}/1${args.img.name}`);
ifExistDelete(`${os.tmpdir()}/${args.img.name}.mp3`);
const streamPipeline = util.promisify(stream.pipeline);
const res = await fetch(args.img.url);
if (!res.ok) return interaction.editReply('An error has occured while trying to download your image.');
await streamPipeline(res.body, fs.createWriteStream(`${os.tmpdir()}/${args.img.name}`));
await utils.ffmpeg(['-i', `${os.tmpdir()}/${args.img.name}`, '-f', 'rawvideo', `${os.tmpdir()}/1${args.img.name}`]);
await utils.ffmpeg(['-sample_rate', '44100', '-ac', '1', '-f', 's16le', '-i', `${os.tmpdir()}/1${args.img.name}`, `${os.tmpdir()}/${args.img.name}.mp3`]);
const file = fs.statSync(`${os.tmpdir()}/${args.img.name}.mp3`);
const fileSize = (file.size / 1000000.0).toFixed(2);
if (fileSize > await utils.getMaxFileSize(interaction.guild)) return interaction.editReply('error');
interaction.editReply({ content: `Audio file is ${fileSize} MB` });
return interaction.followUp({ files: [`${os.tmpdir()}/${args.img.name}.mp3`] });
},
};
async function ifExistDelete(path) {
if (fs.existsSync(path)) {
fs.rm(path, (err) => {
console.log('deleted');
if (err) {
return;
class image2audioCommand extends Command {
constructor() {
super('image2audio', {
aliases: ['image2audio', 'i2a'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'link',
type: 'url',
},
{
id: 'wav',
type: 'flag',
flag: '--wav'
}
],
description: {
content: 'Transform an image binary data into audio ( MIGHT BECOME EAR RAPE ) --wav to get wav output',
usage: '[link to image]',
examples: ['https://cdn.discordapp.com/attachments/532987690145021982/682694359313022987/a2i682694012309864452.png']
}
});
}
async exec(message, args) {
let url;
if (args.link)
url = args.link.href;
else
url = await attachment(message);
let loadingmsg = await message.channel.send('Processing <a:loadingmin:527579785212329984>');
if (!url) return message.channel.send('Please attach an image or use an url');
fetch(url)
.then(res => {
const dest = fs.createWriteStream(`${os.tmpdir()}/${message.id}.png`);
res.body.pipe(dest);
dest.on('finish', () => {
ffmpeg(`${os.tmpdir()}/${message.id}.png`)
.format('rawvideo')
.output(`${os.tmpdir()}/${message.id}1.png`)
.on('error', (err, stdout, stderr) => {
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Uh oh, an error has occurred!');
})
.on('end', () => {
ffmpeg()
.audioBitrate(44100)
.audioChannels(1)
.input(`${os.tmpdir()}/${message.id}1.png`)
.inputFormat('s16le')
.output(`${os.tmpdir()}/i2a_${message.id}.${args.wav ? 'wav' : 'mp3'}`)
.on('error', (err, stdout, stderr) => {
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Uh oh, an error has occurred!');
})
.on('end', () => {
console.log('finished');
loadingmsg.delete();
let file = fs.statSync(`${os.tmpdir()}/i2a_${message.id}.${args.wav ? 'wav' : 'mp3'}`);
let fileSize = (file.size / 1000000.0).toFixed(2);
return message.channel.send(`Audio file is ${fileSize} MB`, {files: [`${os.tmpdir()}/i2a_${message.id}.${args.wav ? 'wav' : 'mp3'}`]})
.catch(() => {
return message.channel.send(`End result is too big to fit on discord! File is ${fileSize} MB`);
});
})
.run();
})
.run();
});
});
}
}
module.exports = image2audioCommand;

34
commands/fun/imgur.js Normal file
View file

@ -0,0 +1,34 @@
const { Command } = require('discord-akairo');
const fetch = require('node-fetch');
class ImgurCommand extends Command {
constructor() {
super('imgur', {
aliases: ['imgur', 'badmeme'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
description: {
content: 'Send some random images from imgur',
usage: '',
examples: ['']
}
});
}
async exec(message) {
fetch('https://api.imgur.com/3/gallery/hot/day?showViral=true&mature=false&perPage=100&album_previews=true', {
headers: { 'Authorization': 'Client-ID e4cb6948f80f295' },
}).then((response) => {
return response.json();
}).then((response) => {
if (response.success == 'false')
return message.channel.send('An error has occurred');
const i = Math.floor((Math.random() * response.data.length));
message.channel.send(`**${response.data[i].title}**\n${response.data[i].link}`);
});
}
}
module.exports = ImgurCommand;

View file

@ -1,15 +0,0 @@
import { SlashCommandBuilder } from 'discord.js';
import fetch from 'node-fetch';
export default {
data: new SlashCommandBuilder()
.setName('inspirobot')
.setDescription('Get an image from inspirobot'),
category: 'fun',
alias: ['ib'],
async execute(interaction) {
fetch('http://inspirobot.me/api?generate=true')
.then(res => res.text())
.then(body => interaction.reply({ files: [body] }));
},
};

133
commands/fun/midify.js Normal file
View file

@ -0,0 +1,133 @@
const { Command } = require('discord-akairo');
const attachment = require('../../utils/attachment');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const downloader = require('../../utils/download');
const os = require('os');
const ffmpeg = require('fluent-ffmpeg');
class midifyCommand extends Command {
constructor() {
super('midify', {
aliases: ['midify', 'wav2midi', 'w2m', 'mp32midi', 'm2m', 'sound2midi', 's2m'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'link',
type: 'url',
match: 'rest'
},
{
id: 'raw',
match: 'flag',
flag: ['--raw']
},
{
id: 'noteblock',
match: 'flag',
flag: ['--noteblock']
},
{
id: 'voice',
match: 'flag',
flag: ['--voice']
}
],
description: {
content: 'Transform the audio into midi --raw to get the .mid file',
usage: '[link to video/music/whatever you want to be midi]',
examples: ['https://www.youtube.com/watch?v=kXYiU_JCYtU']
}
});
}
async exec(message, args) {
let url;
if (args.link)
url = args.link.href;
else
url = await attachment(message);
let input = `${os.tmpdir()}/${message.id}`;
let input2 = `${os.tmpdir()}/${message.id}.wav`;
let output = `${os.tmpdir()}/${message.id}.mid`;
let output2 = `${os.tmpdir()}/${message.id}.mp3`;
let loadingmsg = await message.channel.send('Processing (this can take some time) <a:loadingmin:527579785212329984>');
if (url) {
downloader(url, null, input)
.on('error', (err) => {
loadingmsg.delete();
return message.channel.send(err, { code: true });
})
.on('end', output => {
// Convert to wav
ffmpeg()
.input(output)
.output(input2)
.on('end', () => {
midify();
})
.on('error', (err, stdout, stderr) => {
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Oh no! an error has occurred during the conversion, are you sure it is a valid file?');
})
.run();
});
} else {
return message.channel.send('You need a valid video link!');
}
function midify() {
// wav to midi
exec(`waon -i ${input2} -o ${output}`)
.then(() => {
if (args.raw) {
loadingmsg.delete();
return message.channel.send({files: [output]})
.catch(err => {
console.error(err);
loadingmsg.delete();
return message.channel.send('On no! an error just occurred! perhaps the file is too big?');
});
}
let option;
if (args.noteblock) {
option = '-c ./asset/timidity/config/noteblock.cfg';
} else if (args.voice) {
option = '-c ./asset/timidity/config/voice.cfg';
}
// midi to mp3 so we can listen from discord
exec(`timidity ${output} ${option} -Ow -o - | ffmpeg -i - -acodec libmp3lame -ab 64k ${output2}`)
.then(() => {
loadingmsg.delete();
return message.channel.send({files: [output2]})
.catch(err => {
console.error(err);
loadingmsg.delete();
return message.channel.send('On no! an error just occurred! perhaps the file is too big?');
});
})
.catch(err => {
console.error(err);
return message.channel.send('Oh no! an error has occurred during the conversion, are you sure it is a valid file?');
});
})
.catch(err => {
console.error(err);
return message.channel.send('Oh no! an error has occurred during the conversion, are you sure it is a valid file?');
});
}
}
}
module.exports = midifyCommand;

66
commands/fun/petittube.js Normal file
View file

@ -0,0 +1,66 @@
const { Command } = require('discord-akairo');
const { proxy } = require('../../config.json');
const fetch = require('node-fetch');
const cheerio = require('cheerio');
class PetitTubeCommand extends Command {
constructor() {
super('petittube', {
aliases: ['petittube', 'pt'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'proxy',
match: 'option',
flag: ['--proxy'],
},
{
id: 'listproxy',
match: 'flag',
flag: ['--listproxy', '--proxylist']
}
],
description: {
content: 'Fetch a video from https://petittube.com/',
usage: '',
examples: ['']
}
});
}
async exec(message, args) {
if (args.listproxy) {
let proxys = [];
let i = 0;
proxy.forEach(proxy => {
i++;
proxys.push(`[${i}] ${ proxy.hideip ? '[IP HIDDEN]' : proxy.ip.substring(0, proxy.ip.length - 5)} - ${proxy.country}`);
});
const Embed = this.client.util.embed()
.setColor(message.member ? message.member.displayHexColor : 'NAVY')
.setTitle('List of available proxy')
.setDescription(proxys.join('\n'))
.setFooter('You can help me get more proxy by either donating to me or providing a proxy for me');
return message.channel.send(Embed);
}
const response = await fetch('https://petittube.com/');
const body = await response.text();
const $ = cheerio.load(body);
const url = $('iframe')[0].attribs.src;
if (args.proxy) {
args.proxy = args.proxy -1;
if (!proxy[args.proxy]) args.proxy = 0;
}
this.client.commandHandler.runCommand(message, this.client.commandHandler.findCommand('download'), { link: new URL(url), proxy: args.proxy, spoiler: !message.channel.nsfw, caption: message.channel.nsfw ? '' : 'Video might be NSFW as always, be careful!'});
}
}
module.exports = PetitTubeCommand;

View file

@ -1,46 +1,61 @@
import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
import fetch from 'node-fetch';
const { Command } = require('discord-akairo');
const fetch = require('node-fetch');
export default {
data: new SlashCommandBuilder()
.setName('reddit')
.setDescription('Send random images from the subreddit you choose')
.addStringOption(option =>
option.setName('subreddit')
.setDescription('The subreddit you wish to see')
.setRequired(true)),
category: 'fun',
async execute(interaction, args) {
await interaction.deferReply({ ephemeral: false });
const subreddit = args.subreddit;
fetch('https://www.reddit.com/r/' + subreddit + '.json?limit=100').then((response) => {
class RedditCommand extends Command {
constructor() {
super('reddit', {
aliases: ['reddit'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
args: [
{
id: 'sub',
type: 'string',
prompt: {
start: 'What subreddit do you want to browse?',
optional: true
},
default: 'random',
match: 'rest'
}
],
description: {
content: 'Send random images from the subreddit you choose',
usage: '[subreddit]',
examples: ['2meirl4meirl']
}
});
}
async exec(message, args) {
//let i = 0;
//let a = 0;
if (!args.sub)
return;
fetch('https://www.reddit.com/r/' + args.sub + '.json?limit=100').then((response) => {
return response.json();
}).then((response) => {
if (response.error == 404) {
return interaction.editReply('Not a valid subreddit');
}
if (response.data.dist == 0) {
return interaction.editReply('Not a valid subreddit');
console.log(response);
if (response.error == 404)
return message.reply('Not a valid subreddit');
}
const i = Math.floor((Math.random() * response.data.children.length));
if (response.data.children[i].data.over_18 == true && !interaction.channel.nsfw) {
return interaction.editReply('No nsfw');
}
if (response.data.dist == 0)
return message.reply('Not a valid subreddit');
let description = response.data.children[i].data.selftext;
if (description === '') {
description = 'No description.';
}
const redditEmbed = new EmbedBuilder()
.setColor(interaction.member ? interaction.member.displayHexColor : 'Navy')
let i = Math.floor((Math.random() * response.data.children.length));
if (response.data.children[i].data.over_18 == true && !message.channel.nsfw)
return message.reply('No nsfw');
const redditEmbed = this.client.util.embed()
.setColor(message.member ? message.member.displayHexColor : 'NAVY')
.setTitle(response.data.children[i].data.title)
.setDescription(description)
.setDescription(response.data.children[i].data.selftext)
.setURL('https://reddit.com' + response.data.children[i].data.permalink)
.setFooter({ text: `/r/${response.data.children[i].data.subreddit} | ⬆ ${response.data.children[i].data.ups} 🗨 ${response.data.children[i].data.num_comments}` });
.setFooter(`/r/${response.data.children[i].data.subreddit} | ⬆ ${response.data.children[i].data.ups} 🗨 ${response.data.children[i].data.num_comments}`);
interaction.followUp({ embeds: [redditEmbed] });
interaction.followUp(response.data.children[i].data.url);
message.reply(redditEmbed);
message.reply(response.data.children[i].data.url);
});
},
};
}
}
module.exports = RedditCommand;

47
commands/fun/spb.js Normal file
View file

@ -0,0 +1,47 @@
const { Command } = require('discord-akairo');
const fs = require('fs');
const fetch = require('node-fetch');
const os = require('os');
class spbCommand extends Command {
constructor() {
super('spb', {
aliases: ['spb'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'link',
type: 'url',
prompt: {
start: 'Need a shitpostbot5000 template link!',
}
}
],
description: {
content: 'Generate a meme from template you send with spb5k (ONLY WORK WITH TEMPLATES)',
usage: '',
examples: ['']
}
});
}
async exec(message, args) {
if (!args.link || !args.link.href.includes('shitpostbot.com/template/')) {
return message.reply('Need a Shitpostbot 5000 template link!\nYou can find them here! <https://www.shitpostbot.com/gallery/templates>');
}
let link = args.link.href.replace('template', 'preview');
fetch(link)
.then(res => {
const dest = fs.createWriteStream(`${os.tmpdir()}/${message.id}.jpg`);
res.body.pipe(dest);
dest.on('finish', () => {
return message.reply({files: [`${os.tmpdir()}/${message.id}.jpg`]});
});
});
}
}
module.exports = spbCommand;

View file

@ -0,0 +1,59 @@
const { Command } = require('discord-akairo');
const { execFile } = require('child_process');
const rand = require('../../../rand.js');
class dectalkCommand extends Command {
constructor() {
super('dectalk', {
aliases: ['dectalk', 'dec'],
category: 'fun',
clientPermissions: ['ATTACH_FILES'],
args: [
{
id: 'decMessage',
type: 'string',
prompt: {
start: 'Write something so i can say it back in dectalk',
},
match: 'rest'
}
],
description: {
content: 'Send a wav of what you wrote into .wav with dectalk',
usage: '[text]',
examples: ['This command is very epic']
}
});
}
async exec(message, args) {
args.decMessage = rand.random(args.decMessage, message);
let output = `${message.id}_dectalk.wav`;
let decMessage = '[:phoneme on]' + args.decMessage;
let loadingmsg = await message.channel.send('Processing ( this can take some time ) <a:loadingmin:527579785212329984>');
if (process.platform === 'win32') {
execFile('say.exe', ['-w', output, `${decMessage}`], {cwd: './dectalk/'}, (error, stdout, stderr) => {
sendMessage(output, error, stdout, stderr);
});
} else if (process.platform === 'linux' || process.platform === 'darwin') {
execFile('wine', ['say.exe', '-w', output, `${decMessage}`], {cwd: './dectalk/'}, (error, stdout, stderr) => {
sendMessage(`./dectalk/${output}`, error, stdout, stderr);
});
}
async function sendMessage(file, error, stdout, stderr) {
console.error(stdout);
loadingmsg.delete();
if (error) {
console.error(stderr);
console.error(error);
return message.channel.send('Oh no! an error has occurred!');
}
return message.channel.send({files: [file]});
}
}
}
module.exports = dectalkCommand;

View file

@ -0,0 +1,81 @@
const { Command } = require('discord-akairo');
const { execFile } = require('child_process');
const rand = require('../../../rand.js');
class dectalkvcCommand extends Command {
constructor() {
super('dectalkvc', {
aliases: ['dectalkvc', 'decvc'],
category: 'fun',
clientPermissions: ['SPEAK'],
args: [
{
id: 'decMessage',
type: 'string',
prompt: {
start: 'Write something so i can say it back in dectalk',
},
match: 'rest'
}
],
description: {
content: 'Repeat what you sent in the voice chat you are currently in',
usage: '[text]',
examples: ['This command is very epic']
}
});
}
async exec(message, args) {
args.decMessage = rand.random(args.decMessage, message);
let output = `${message.id}_dectalk.wav`;
let decMessage = '[:phoneme on] ' + args.decMessage;
let loadingmsg = await message.channel.send('Processing ( this can take some time ) <a:loadingmin:527579785212329984>');
if (process.platform === 'win32') {
execFile('say.exe', ['-w', output, `${decMessage}`], {cwd: './dectalk/'}, async (error, stdout, stderr) => {
if (error) {
loadingmsg.delete();
console.error(stdout);
console.error(stderr);
console.error(error);
return message.channel.send('Oh no! an error has occurred!');
}
loadingmsg.delete();
playinVC(`./dectalk/${output}`);
});
} else if (process.platform === 'linux' || process.platform === 'darwin') {
execFile('wine', ['say.exe', '-w', output, `${decMessage}`], {cwd: './dectalk/'}, async (error, stdout, stderr) => {
if (error) {
loadingmsg.delete();
console.error(stdout);
console.error(stderr);
console.error(error);
return message.channel.send('Oh no! an error has occurred!');
}
loadingmsg.delete();
playinVC(`./dectalk/${output}`);
});
}
async function playinVC(file) {
const voiceChannel = message.member.voice.channel;
if (!voiceChannel) return message.channel.send('Please enter a voice channel first.');
try {
const connection = await voiceChannel.join();
const dispatcher = connection.play(file);
dispatcher.once('finish', () => voiceChannel.leave());
dispatcher.once('error', () => voiceChannel.leave());
return null;
} catch (err) {
voiceChannel.leave();
return message.reply(`Oh no, an error occurred: \`${err.message}\`.`);
}
}
}
}
module.exports = dectalkvcCommand;

93
commands/fun/tts/sam.js Normal file
View file

@ -0,0 +1,93 @@
const { Command } = require('discord-akairo');
const axios = require('axios');
const fs = require('fs');
const os = require('os');
const rand = require('../../../rand.js');
class samCommand extends Command {
constructor() {
super('sam', {
aliases: ['sam'],
category: 'fun',
clientPermissions: ['ATTACH_FILES'],
args: [
{
id: 'samMessage',
type: 'string',
prompt: {
start: 'Write something so i can say it back in sam',
},
match: 'rest'
}
],
description: {
content: 'Send a mp3 of what you wrote in Microsoft Sam tts, can change speed and pitch with [speed:a number] and [pitch:a number]',
usage: '[text]',
examples: ['Here comes the roflcopter soisoisoisoisoi']
}
});
}
async exec(message, args) {
args.samMessage = rand.random(args.samMessage, message);
let pitch;
if (args.samMessage.includes('[pitch:')) {
pitch = args.samMessage.split(/(\[pitch:.*?])/);
for (let i = 0, l = pitch.length; i < l; i++) {
if (pitch[i].includes('[pitch:')) {
pitch = pitch[i].replace('[pitch:', '').slice(0, -1);
args.samMessage = args.samMessage.replace(/(\[pitch:.*?])/g, '');
i = pitch.length;
}
}
if (pitch > 200)
pitch = 200;
else if (pitch < 50)
pitch = 50;
} else {
pitch = 100;
}
let speed;
if (args.samMessage.includes('[speed:')) {
speed = args.samMessage.split(/(\[speed:.*?])/);
for (let i = 0, l = speed.length; i < l; i++) {
if (speed[i].includes('[speed:')) {
speed = speed[i].replace('[speed:', '').slice(0, -1);
args.samMessage = args.samMessage.replace(/(\[speed:.*?])/g, '');
i = speed.length;
}
}
if (speed > 450)
speed = 450;
else if (speed < 30)
speed = 30;
} else {
speed = 150;
}
args.samMessage = args.samMessage.replace('\n', ' ');
args.samMessage = encodeURI(args.samMessage);
return axios.request({
responseType: 'arraybuffer',
url: `https://tetyys.com/SAPI4/SAPI4?text=${args.samMessage}&voice=Sam&pitch=${pitch}&speed=${speed}`,
method: 'get',
headers: {
'Content-Type': 'audio/mpeg',
},
})
.catch((err) => {
console.error(err);
return message.channel.send(`Uh oh, an error has occurred! please try again later.\n${err}`);
})
.then((result) => {
const outputFilename = `${os.tmpdir}/${message.id}_sam.wav`;
fs.writeFileSync(outputFilename, result.data);
return message.channel.send({files: [outputFilename]});
});
}
}
module.exports = samCommand;

107
commands/fun/tts/samvc.js Normal file
View file

@ -0,0 +1,107 @@
const { Command } = require('discord-akairo');
const axios = require('axios');
const fs = require('fs');
const os = require('os');
const rand = require('../../../rand.js');
class samvcCommand extends Command {
constructor() {
super('samvc', {
aliases: ['samvc'],
category: 'fun',
clientPermissions: ['SPEAK'],
args: [
{
id: 'samMessage',
type: 'string',
prompt: {
start: 'Write something so i can say it back in sam',
},
match: 'rest'
}
],
description: {
content: 'Repeat what you said in voice chat with Microsoft Sam tts, can change speed and pitch with [speed:a number] and [pitch:a]',
usage: '[text]',
examples: ['Here comes the roflcopter soisoisoisoisoi']
}
});
}
async exec(message, args) {
args.samMessage = rand.random(args.samMessage, message);
let pitch;
if (args.samMessage.includes('[pitch:')) {
pitch = args.samMessage.split(/(\[pitch:.*?])/);
for (let i = 0, l = pitch.length; i < l; i++) {
if (pitch[i].includes('[pitch:')) {
pitch = pitch[i].replace('[pitch:', '').slice(0, -1);
args.samMessage = args.samMessage.replace(/(\[pitch:.*?])/g, '');
i = pitch.length;
}
}
if (pitch > 200)
pitch = 200;
else if (pitch < 50)
pitch = 50;
} else {
pitch = 100;
}
let speed;
if (args.samMessage.includes('[speed:')) {
speed = args.samMessage.split(/(\[speed:.*?])/);
for (let i = 0, l = speed.length; i < l; i++) {
if (speed[i].includes('[speed:')) {
speed = speed[i].replace('[speed:', '').slice(0, -1);
args.samMessage = args.samMessage.replace(/(\[speed:.*?])/g, '');
i = speed.length;
}
}
if (speed > 450)
speed = 450;
else if (speed < 30)
speed = 30;
} else {
speed = 150;
}
args.samMessage = args.samMessage.replace('\n', ' ');
args.samMessage = encodeURI(args.samMessage);
return axios.request({
responseType: 'arraybuffer',
url: `https://tetyys.com/SAPI4/SAPI4?text=${args.samMessage}&voice=Sam&pitch=${pitch}&speed=${speed}`,
method: 'get',
headers: {
'Content-Type': 'audio/mpeg',
},
})
.catch((err) => {
console.error(err);
return message.channel.send(`Uh oh, an error has occurred! please try again later.\n${err}`);
})
.then(async (result) => {
const outputFilename = `${os.tmpdir}/${message.id}_sam.wav`;
fs.writeFile(outputFilename, result.data, async function(err) {
if (err) console.error(err);
const voiceChannel = message.member.voice.channel;
if (!voiceChannel) return message.channel.send('Please enter a voice channel first.');
try {
const connection = await voiceChannel.join();
const dispatcher = connection.play(outputFilename);
dispatcher.once('finish', () => voiceChannel.leave());
dispatcher.once('error', () => voiceChannel.leave());
return null;
} catch (err) {
voiceChannel.leave();
return message.reply(`Oh no, an error occurred: \`${err.message}\`.`);
}
});
});
}
}
module.exports = samvcCommand;

70
commands/fun/tts/tts.js Normal file
View file

@ -0,0 +1,70 @@
const { Command } = require('discord-akairo');
const textToSpeech = require('@google-cloud/text-to-speech');
const rand = require('../../../rand.js');
const gclient = new textToSpeech.TextToSpeechClient();
const fs = require('fs');
const os = require('os');
class TtsCommand extends Command {
constructor() {
super('tts', {
aliases: ['tts'],
category: 'fun',
clientPermissions: ['ATTACH_FILES'],
args: [
{
id: 'text',
type: 'string',
prompt: {
start: 'Write something so i can say it back in Google tts',
},
match: 'rest'
}
],
description: {
content: 'Send a mp3 of what you wrote in tts',
usage: '[text]',
examples: ['hello']
}
});
}
async exec(message, args) {
let text = args.text;
let output = `${os.tmpdir()}/${message.id}_tts.mp3`;
text = rand.random(text, message);
// Construct the request
const request = {
input: { text: text },
// Select the language and SSML Voice Gender (optional)
voice: { languageCode: 'en-US', ssmlGender: 'NEUTRAL' },
// Select the type of audio encoding
audioConfig: { audioEncoding: 'MP3' },
};
// Performs the Text-to-Speech request
gclient.synthesizeSpeech(request, (err, response) => {
if (err) {
console.error('ERROR:', err);
return;
}
// Write the binary audio content to a local file
fs.writeFile(output, response.audioContent, 'binary', err => {
if (err) {
console.error('ERROR:', err);
message.channel.send('An error has occurred, the message is probably too long');
return;
}
console.log('Audio content written to file: tts.mp3');
message.channel.send({ files: [output] });
});
});
}
}
module.exports = TtsCommand;

79
commands/fun/tts/ttsvc.js Normal file
View file

@ -0,0 +1,79 @@
const { Command } = require('discord-akairo');
const textToSpeech = require('@google-cloud/text-to-speech');
const rand = require('../../../rand.js');
const gclient = new textToSpeech.TextToSpeechClient();
const fs = require('fs');
const os = require('os');
class TtsvcCommand extends Command {
constructor() {
super('ttsvc', {
aliases: ['ttsvc'],
category: 'fun',
clientPermissions: ['SPEAK'],
args: [
{
id: 'text',
type: 'string',
prompt: {
start: 'Write something so i can say it back in Google tts',
},
match: 'rest'
}
],
description: {
content: 'Say what you wrote in voice channel',
usage: '[text]',
examples: ['hello']
}
});
}
async exec(message, args) {
let text = args.text;
let output = `${os.tmpdir()}/${message.id}_tts.mp3`;
text = rand.random(text, message);
// Construct the request
const request = {
input: { text: text },
// Select the language and SSML Voice Gender (optional)
voice: { languageCode: 'en-US', ssmlGender: 'NEUTRAL' },
// Select the type of audio encoding
audioConfig: { audioEncoding: 'MP3' },
};
// Performs the Text-to-Speech request
gclient.synthesizeSpeech(request, (err, response) => {
if (err) {
console.error('ERROR:', err);
return;
}
// Write the binary audio content to a local file
fs.writeFile(output, response.audioContent, 'binary', async err => {
if (err) {
console.error('ERROR:', err);
message.channel.send('An error has occurred, the message is probably too long');
return;
}
const voiceChannel = message.member.voice.channel;
if (!voiceChannel) return message.channel.send('Please enter a voice channel first.');
try {
const connection = await voiceChannel.join();
const dispatcher = connection.play(output);
dispatcher.once('finish', () => voiceChannel.leave());
dispatcher.once('error', () => voiceChannel.leave());
return null;
} catch (err) {
voiceChannel.leave();
return message.reply(`Oh no, an error occurred: \`${err.message}\`.`);
}
});
});
}
}
module.exports = TtsvcCommand;

View file

@ -1,204 +0,0 @@
import { SlashCommandBuilder } from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import { TwitterApi } from 'twitter-api-v2';
import fetch from 'node-fetch';
import os from 'node:os';
import fs from 'node:fs';
import util from 'node:util';
import stream from 'node:stream';
import db from '../../models/index.js';
import wordToCensor from '../../json/censor.json' with {type: 'json'};
const { twiConsumer, twiConsumerSecret, twiToken, twiTokenSecret, twiChannel, twiLogChannel } = process.env;
const Blacklists = db.Blacklists;
export default {
data: new SlashCommandBuilder()
.setName('tweet')
.setDescription('Send tweet from the bot twitter account. Please do not use it for advertisement and keep it english')
.addStringOption(option =>
option.setName('content')
.setDescription('!THIS IS NOT FEEDBACK! The content of the tweet you want to send me.')
.setRequired(false))
.addAttachmentOption(option =>
option.setName('image')
.setDescription('Optional attachment (Image only.)')
.setRequired(false)),
category: 'fun',
ratelimit: 3,
cooldown: 86400,
guildOnly: true,
async execute(interaction, args, client) {
const content = args.content;
const attachment = args.image;
if (!content && !attachment) {
return interaction.reply({ content: 'Uh oh! You are missing any content for me to tweet!', ephemeral: true });
}
await interaction.deferReply({ ephemeral: false });
let tweet = content;
const date = new Date();
// If guild is less than 1 month old don't accept the tweet
if (interaction.guild.createdAt > date.setMonth(date.getMonth() - 1)) {
await interaction.editReply({ content: 'The server need to be 1 month old to be able to use this command!' });
return;
}
// Reset the date for the next check
date.setTime(Date.now());
// If the bot has been in the guild for less than 1 week don't accept the tweet.
if (interaction.guild.createdAt > date.setDate(date.getDate() - 7)) {
await interaction.editReply({ content: 'I need to be in this server for a week to be able to use this command!' });
}
// Reset the date for the next check
date.setTime(Date.now());
// If account is less than 6 months old don't accept the tweet ( alt prevention )
if (interaction.user.createdAt > date.setMonth(date.getMonth() - 6)) {
await interaction.editReply({ content: 'Your account is too new to be able to use this command!' });
return;
}
// Reset the date for the next check
date.setTime(Date.now());
// If account is less than 1 year old don't accept attachment
if (attachment && interaction.user.createdAt > date.setFullYear(date.getFullYear() - 1)) {
await interaction.editReply({ content: 'Your account need to be 1 year or older to be able to send attachment!' });
return;
}
if (tweet) {
// remove zero width space
tweet = tweet.replace('', '');
// This should only happen if someone tweets a zero width space
if (tweet.length === 0) {
return interaction.reply({ content: 'Uh oh! You are missing any content for me to tweet!', ephemeral: true });
}
wordToCensor.forEach(async word => {
if (tweet.toLowerCase().includes(word.toLowerCase())) {
const body = { type:'tweet', uid: interaction.user.id, reason: 'Automatic ban from banned word.' };
Blacklists.create(body);
await interaction.editReply({ content: 'Sike, you just posted cringe! Enjoy the blacklist :)' });
return;
}
});
// Detect banned word (Blacklist the user directly)
/* No worky (I don't remember what the fuck I wrote here)
if (wordToCensor.includes(tweet) || wordToCensor.includes(tweet.substring(0, tweet.length - 1)) || wordToCensor.includes(tweet.substring(1, tweet.length))) {
const body = { type:'tweet', uid: interaction.user.id, reason: 'Automatic ban from banned word.' };
Blacklists.create(body);
await interaction.editReply({ content: 'Sike, you just posted cringe! Enjoy the blacklist :)' });
return;
}
*/
// Very simple link detection
if (new RegExp('([a-zA-Z0-9]+://)?([a-zA-Z0-9_]+:[a-zA-Z0-9_]+@)?([a-zA-Z0-9.-]+\\.[A-Za-z]{2,4})(:[0-9]+)?(/.*)?').test(tweet) && !tweet.includes('twitter.com')) {
await interaction.editReply({ content: 'You may not tweet links outside of twitter.com' });
return;
}
// Do not allow discord invites
if (tweet.includes('discord.gg') || tweet.includes('discord.com/invite/')) {
await interaction.editReply({ content: 'No discord invite allowed.' });
return;
}
}
const userClient = new TwitterApi({
appKey: twiConsumer,
appSecret: twiConsumerSecret,
accessToken: twiToken,
accessSecret: twiTokenSecret,
});
try {
// Make sure there is an attachment and if its an image
if (attachment) {
if (attachment.name.toLowerCase().endsWith('.jpg') || attachment.name.toLowerCase().endsWith('.png') || attachment.name.toLowerCase().endsWith('.gif')) {
const streamPipeline = util.promisify(stream.pipeline);
const res = await fetch(attachment.url);
if (!res.ok) return interaction.editReply('An error has occured while trying to download your image.');
await streamPipeline(res.body, fs.createWriteStream(`${os.tmpdir()}/${attachment.name}`));
const file = fs.statSync(`${os.tmpdir()}/${attachment.name}`);
const fileSize = file.size / 1000000.0;
if ((attachment.name.toLowerCase().endsWith('.jpg') || attachment.name.toLowerCase().endsWith('.png')) && fileSize > 5) {
return interaction.editReply({ content: 'Images can\'t be larger than 5 MB!' });
}
else if (attachment.name.toLowerCase().endsWith('.gif') && fileSize > 15) {
return interaction.editReply({ content: 'Gifs can\'t be larger than 15 MB!' });
}
const image = await userClient.v1.uploadMedia(`${os.tmpdir()}/${attachment.name}`);
Tweet(image);
}
else {
await interaction.editReply({ content: 'File type not supported, you can only send jpg/png/gif' });
return;
}
}
else {
Tweet();
}
}
catch (err) {
console.error(err);
await interaction.editReply({ content: 'Oh no, an error has occurred :(' });
return;
}
async function Tweet(img) {
let options = null;
if (img) {
options = { media: { media_ids: new Array(img) } };
}
const tweeted = await userClient.v2.tweet(tweet, options);
const tweetid = tweeted.data.id;
const FunnyWords = ['oppaGangnamStyle', '69', '420', 'cum', 'funnyMan', 'GUCCISmartToilet', 'TwitterForClowns', 'fart', 'ok', 'hi', 'howAreYou', 'WhatsNinePlusTen', '21'];
const TweetLink = `https://vxtwitter.com/${FunnyWords[Math.floor((Math.random() * FunnyWords.length))]}/status/${tweetid}`;
let channel = await client.channels.resolve(twiChannel);
channel.send(TweetLink);
const Embed = new EmbedBuilder()
.setAuthor({ name: interaction.user.username, iconURL: interaction.user.displayAvatarURL() })
.setDescription(tweet ? tweet : 'No content.')
.addFields(
{ name: 'Link', value: TweetLink, inline: true },
{ name: 'Tweet ID', value: tweetid, inline: true },
{ name: 'Channel ID', value: interaction.channel.id, inline: true },
{ name: 'Message ID', value: interaction.id, inline: true },
{ name: 'Author', value: `${interaction.user.username} (${interaction.user.id})`, inline: true },
)
.setTimestamp();
if (interaction.guild) {
Embed.addFields(
{ name: 'Guild', value: `${interaction.guild.name} (${interaction.guild.id})`, inline: true },
{ name: 'message link', value: `https://discord.com/channels/${interaction.guild.id}/${interaction.channel.id}/${interaction.id}`, inline: true },
);
}
else {
Embed.addFields({ name: 'message link', value: `https://discord.com/channels/@me/${interaction.channel.id}/${interaction.id}` });
}
if (attachment) Embed.setImage(attachment.url);
channel = await client.channels.resolve(twiLogChannel);
channel.send({ embeds: [Embed] });
return interaction.editReply({ content: `Go see ur epic tweet ${TweetLink}` });
}
},
};

View file

@ -0,0 +1,99 @@
const { Command } = require('discord-akairo');
const os = require('os');
const ffmpeg = require('fluent-ffmpeg');
const attachment = require('../../utils/attachment');
const downloader = require('../../utils/download');
class vidshittyfierCommand extends Command {
constructor() {
super('vidshittyfier', {
aliases: ['vidshittyfier', 'vs', 'shittyfier', 'vid2shit', 'v2s'],
category: 'fun',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'link',
type: 'url',
unordered: true
},
{
id: 'compression',
type: 'boolean',
default: false,
unordered: true
}
],
description: {
content: 'Compress your videos and lower their quality!',
usage: '[link to video] [lighter compression (True or false)]',
examples: ['']
}
});
}
async exec(message, args) {
let vid;
if (args.link)
vid = args.link.href;
else
vid = await attachment(message);
let output = `${os.tmpdir()}/tmp${message.id}.mp4`;
let output2 = `${os.tmpdir()}/Shittyfied${message.id}.mp4`;
let compression = '30k';
let audioCompression = '60k';
if (args.compression) {
compression = '50k';
audioCompression = '100k';
}
let loadingmsg = await message.channel.send('Processing <a:loadingmin:527579785212329984>');
downloader(vid, null, `${os.tmpdir()}/${message.id}.mp4`)
.on('error', async err => {
loadingmsg.delete();
return message.channel.send(err, { code: true });
})
.on('end', async downloadOutput => {
let ffmpegCommand = ffmpeg(downloadOutput);
ffmpegCommand.videoFilters('scale=iw/4:ih/4');
ffmpegCommand.output(output);
ffmpegCommand.run();
ffmpegCommand.on('error', (err, stdout, stderr) => {
loadingmsg.delete();
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Uh oh, an error has occurred!' + err);
});
ffmpegCommand.on('end', () => {
let ffmpegCommand = ffmpeg(downloadOutput);
ffmpegCommand.videoFilters('scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2');
ffmpegCommand.videoCodec('libx264');
ffmpegCommand.fps(15);
ffmpegCommand.videoBitrate(compression);
ffmpegCommand.audioBitrate(audioCompression);
ffmpegCommand.output(output2);
ffmpegCommand.run();
ffmpegCommand.on('error', (err, stdout, stderr) => {
loadingmsg.delete();
console.error(`${err}\n${stdout}\n${stderr}`);
return message.channel.send('Uh oh, an error has occurred!' + err);
});
ffmpegCommand.on('end', () => {
loadingmsg.delete();
message.delete();
return message.channel.send(`Shittyfied by ${message.author}`, {files: [output2]})
.catch(err => {
console.error(err);
return message.channel.send(`${err.name}: ${err.message} ${err.message === 'Request entity too large' ? 'The file size is too big' : ''}`);
});
});
});
});
}
}
module.exports = vidshittyfierCommand;

View file

@ -1,37 +1,306 @@
import { SlashCommandBuilder } from 'discord.js';
import fs from 'node:fs';
import os from 'node:os';
import YTPGenerator from 'ytpplus-node';
const { Command } = require('discord-akairo');
const { proxy } = require('../../config.json');
const YTPGenerator = require('ytpplus-node');
const os = require('os');
const fs = require('fs');
const attachment = require('../../utils/attachment');
const downloader = require('../../utils/download');
const md5File = require('md5-file');
const ytpHash = require('../../models').ytpHash;
const { ytpChannel } = require('../../config.json');
//const ytpblacklist = require('../../models').ytpblacklist;
export default {
data: new SlashCommandBuilder()
.setName('ytp')
.setDescription('Generate a YTP')
.addBooleanOption(option =>
option.setName('force')
.setDescription('Force the generation of the video in non-nsfw channel.')
.setRequired(false)),
category: 'fun',
ratelimit: 2,
cooldown: 60,
parallelLimit: 30,
async execute(interaction, args) {
if (!interaction.channel.nsfw && !args.force) return interaction.reply(`Please execute this command in an NSFW channel ( Content might not be NSFW but since the video are user submitted better safe than sorry ) OR do \`\`${interaction.prefix}ytp --force\`\` to make the command work outside of nsfw channel BE AWARE THAT IT WON'T CHANGE THE FINAL RESULT SO NSFW CAN STILL HAPPEN`);
const MAX_CLIPS = 20;
class ytpCommand extends Command {
constructor() {
super('ytp', {
aliases: ['ytp', 'ytpplus', 'ytp+'],
category: 'fun',
clientPermissions: ['ATTACH_FILES', 'SEND_MESSAGES'],
args: [
{
id: 'add',
match: 'flag',
flag: ['--add']
},
{
id: 'pool',
match: 'flag',
flag: ['--pool']
},
{
id: 'force',
match: 'flag',
flag: ['--force']
},
{
id: 'randomSound',
match: 'flag',
flag: ['--randomSound']
},
{
id: 'randomSoundMute',
match: 'flag',
flag: ['--randomSoundMute']
},
{
id: 'reverse',
match: 'flag',
flag: ['--reverse']
},
{
id: 'chorus',
match: 'flag',
flag: ['--chorus']
},
{
id: 'vibrato',
match: 'flag',
flag: ['--vibrato']
},
{
id: 'highPitch',
match: 'flag',
flag: ['--highPitch']
},
{
id: 'lowPitch',
match: 'flag',
flag: ['--lowPitch']
},
{
id: 'speedUp',
match: 'flag',
flag: ['--speedUp']
},
{
id: 'slowDown',
match: 'flag',
flag: ['--slowDown']
},
{
id: 'dance',
match: 'flag',
flag: ['--dance']
},
{
id: 'squidward',
match: 'flag',
flag: ['--squidward']
},
{
id: 'how',
match: 'flag',
flag: ['--how']
},
{
id: 'debug',
match: 'flag',
flag: ['--debug']
},
{
id: 'link',
type: 'url',
prompt: {
start: 'Please send the URL of which video you want to download. Say `cancel` to stop the command',
retry: 'Please send a valid URL of the video you want to download. Say `cancel` to stop the command',
optional: true,
},
unordered: true
},
{
id: 'max',
type: 'string',
unordered: true
},
{
id: 'proxy',
match: 'option',
flag: ['--proxy'],
},
{
id: 'listproxy',
match: 'flag',
flag: ['--listproxy', '--proxylist']
}
],
description: {
content: 'Generate random ytp\n--add with a link or attachment to add a video to the pool, only .mp4 work **(Everything submitted here is logged and can get you blacklisted if you post lots of repeated content, post porn/gore, post TOS breaking content)**\n--pool to see how many vid there is currently in the pool\n--force to make the command work outside of nsfw channel BE AWARE THAT IT WON\'T CHANGE THE FINAL RESULT SO NSFW CAN STILL HAPPEN\n`--proxy #` to select a proxy, `--listproxy` to see a list of proxy',
usage: '(OPTIONAL) | [Minimum length of clip] [Max length of clip]',
examples: ['5 10', '--add https://www.youtube.com/watch?v=6n3pFFPSlW4', '--add https://www.youtube.com/watch?v=6n3pFFPSlW4 --proxy 1', '--listproxy']
}
});
}
async exec(message, args) {
if (args.pool) {
let mp4 = [];
fs.readdirSync('./asset/ytp/userVid/').forEach(file => {
if (file.endsWith('mp4')) {
mp4.push(file);
}
});
return message.reply(`There is currently ${mp4.length} videos, you can add yours by doing \`\`${this.client.commandHandler.prefix[0]}ytp --add (link or attachment)\`\``);
}
if (args.listproxy) {
let proxys = [];
let i = 0;
proxy.forEach(proxy => {
i++;
proxys.push(`[${i}] ${ proxy.hideip ? '[IP HIDDEN]' : proxy.ip.substring(0, proxy.ip.length - 5)} - ${proxy.country}`);
});
const Embed = this.client.util.embed()
.setColor(message.member ? message.member.displayHexColor : 'NAVY')
.setTitle('List of available proxy')
.setDescription(proxys.join('\n'))
.setFooter('You can help me get more proxy by either donating to me or providing a proxy for me');
return message.reply(Embed);
}
if (args.add) {
/*
const blacklist = await ytpblacklist.findOne({where: {userID:message.author.id}});
if (blacklist) {
return message.reply(`You have been blacklisted for the following reasons: \`${blacklist.get('reason')}\` be less naughty next time.`);
}
*/
if (args.proxy && !args.proxyAuto) { // args.proxyAuto is only provided when the command is run after a error 429
args.proxy = args.proxy -1;
if (!proxy[args.proxy]) args.proxy = 0;
}
let loadingmsg = await message.reply('Downloading <a:loadingmin:527579785212329984>');
let url;
if (args.link)
url = args.link.href;
else
url = await attachment(message);
if (url) {
let options = ['--format=mp4'];
if (args.proxy) {
options.push('--proxy');
options.push(proxy[args.proxy].ip);
}
return downloader(url, options, `./asset/ytp/userVid/${message.id}.mp4`)
.on('error', (err) => {
if (err.includes('HTTP Error 429: Too Many Requests')) {
if (args.proxy != null) {
args.proxy = args.proxy + 1;
} else {
args.proxy = 0;
args.proxyAuto = true;
}
if (!proxy[args.proxy]) return message.reply('`HTTP Error 429: Too Many Requests.`\nThe website you tried to download from probably has the bot blocked along with its proxy');
loadingmsg.delete();
return this.client.commandHandler.runCommand(message, this.client.commandHandler.findCommand('ytp'), args);
}
if (err.includes('Error: status code 403')) return message.reply('`HTTP Error 403: Forbidden.`\nThe video you tried to download is not publicly available therefor the bot can\'t download it.');
return message.reply(err, { code: true });
})
.on('end', async output => {
const hash = md5File.sync(output);
const ytphash = await ytpHash.findOne({where: {hash: hash}});
if (ytphash) {
fs.unlinkSync(output);
loadingmsg.delete();
return message.reply('This video is a duplicate... Not adding.');
} else {
let file = fs.statSync(output);
let fileSize = file.size / 1000000.0;
if (fileSize > 50) {
fs.unlinkSync(output);
loadingmsg.delete();
return message.reply('Video too big.. Not adding.');
}
const body = {hash: hash, messageID: message.id};
await ytpHash.create(body);
}
let mp4 = [];
fs.readdirSync('./asset/ytp/userVid/').forEach(file => {
if (file.endsWith('mp4')) {
mp4.push(file);
}
});
// (Hopefully) limit video to 2k
if (mp4.length > 2000) {
let file = mp4.sort((a, b) => {
let time1 = fs.statSync(`./asset/ytp/userVid/${b}`).ctime;
let time2 = fs.statSync(`./asset/ytp/userVid/${a}`).ctime;
if (time1 < time2) return 1;
if (time1 > time2) return -1;
return 0;
}).slice(0,1);
console.log(file);
fs.unlinkSync(`./asset/ytp/userVid/${file[0]}`);
}
loadingmsg.delete();
message.reply(`Video successfully added to the pool! There is now ${mp4.length} videos`);
const Embed = this.client.util.embed()
.setAuthor(message.author.username, message.author.displayAvatarURL())
.addField('Channel ID', message.channel.id, true)
.addField('Messsage ID', message.id, true)
.addField('Author', `${message.author.username} (${message.author.id})`, true)
.setTimestamp();
if (message.guild) {
Embed.addField('Guild', `${message.guild.name} (${message.guild.id})`, true);
Embed.addField('Message link', `https://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.id}`);
} else {
Embed.addField('Message link', `https://discord.com/channels/@me/${message.channel.id}/${message.id}`);
}
let channel = this.client.channels.resolve(ytpChannel);
return channel.send(url, {embed: Embed});
});
} else {
loadingmsg.delete();
return message.reply('You need a valid video link!');
}
}
if (!message.channel.nsfw && !args.force) return message.reply(`Please execute this command in an NSFW channel ( Content might not be NSFW but since the video are user submitted better safe than sorry ) OR do \`\`${this.client.commandHandler.prefix[0]}ytp --force\`\` to make the command work outside of nsfw channel BE AWARE THAT IT WON'T CHANGE THE FINAL RESULT SO NSFW CAN STILL HAPPEN`);
// Read userVid folder and select random vid and only take .mp4
const mp4 = [];
const asset = [];
let mp4 = [];
let asset = [];
// Count number of total vid
fs.readdirSync('./asset/ytp/userVid/').forEach(file => {
if (file.endsWith('mp4')) {
mp4.push(file);
}
});
const MAX_CLIPS = 20;
// Select random vid depending on the amount of MAX_CLIPS
for (let i = 0; i < MAX_CLIPS; i++) {
const random = Math.floor(Math.random() * mp4.length);
const vid = `./asset/ytp/userVid/${mp4[random]}`;
let random = Math.floor(Math.random() * mp4.length);
let vid = `./asset/ytp/userVid/${mp4[random]}`;
if (mp4[random].endsWith('mp4')) {
if (!asset.includes(vid)) {
asset.push(vid);
@ -39,11 +308,13 @@ export default {
}
}
const loadingmsg = await interaction.reply(`Processing, this can take a ***long*** time, i'll ping you when I finished <a:loadingmin:527579785212329984>\nSome info: There are currently ${mp4.length} videos, why not add yours? You can do so with the \`\`addytp\`\` command.\nLike ytp? Why not check out https://ytp.namejeff.xyz/`);
let loadingmsg = await message.reply(`Processing, this can take a ***long*** time, i'll ping you when i finished <a:loadingmin:527579785212329984>\nSome info: There are currently ${mp4.length} videos, you can add yours by doing \`\`${this.client.commandHandler.prefix[0]}ytp --add (link or attachment)\`\`\nLike ytp? Why not check out https://ytp.namejeff.xyz/`);
const options = {
debug: false,
MAX_STREAM_DURATION: Math.floor((Math.random() * 3) + 1),
let options = {
debug: args.debug,
MIN_STREAM_DURATION: args.link ? Math.floor(args.link) : null,
MAX_STREAM_DURATION: args.link && args.max ? args.max : Math.floor((Math.random() * 3) + 1), // Random duration of video clip
sources: './asset/ytp/sources/',
sounds: './asset/ytp/sounds/',
music: './asset/ytp/music/',
@ -52,39 +323,58 @@ export default {
sourceList: asset,
intro: args.force ? './asset/ytp/intro.mp4' : null,
outro: './asset/ytp/outro.mp4',
OUTPUT_FILE: `${os.tmpdir()}/${interaction.id}_YTP.mp4`,
OUTPUT_FILE: `${os.tmpdir()}/${message.id}_YTP.mp4`,
MAX_CLIPS: MAX_CLIPS,
transitions: true,
showFileNames: true,
effects: {
effect_RandomSound: true,
effect_RandomSoundMute: true,
effect_Reverse: true,
effect_Chorus: true,
effect_Vibrato: true,
effect_HighPitch: true,
effect_LowPitch: true,
effect_SpeedUp: true,
effect_SlowDown: true,
effect_Dance: true,
effect_Squidward: true,
effect_How: true,
},
effect_RandomSound: !args.randomSound,
effect_RandomSoundMute: !args.randomSoundMute,
effect_Reverse: !args.reverse,
effect_Chorus: !args.chorus,
effect_Vibrato: !args.vibrato,
effect_HighPitch: !args.highPitch,
effect_LowPitch: !args.lowPitch,
effect_SpeedUp: !args.speedUp,
effect_SlowDown: !args.slowDown,
effect_Dance: !args.dance,
effect_Squidward: !args.squidward,
effect_How: !args.how
}
};
await new YTPGenerator().configurateAndGo(options)
new YTPGenerator().configurateAndGo(options)
.then(() => {
md5File(`${os.tmpdir()}/${message.id}_YTP.mp4`).then(async hash => {
const body = {hash: hash, messageID: message.id};
await ytpHash.create(body);
});
loadingmsg.delete();
return interaction.followUp({ content: 'Here is your YTP! Remember, it might contain nsfw, so be careful!', files: [`${os.tmpdir()}/${interaction.id}_YTP.mp4`] })
return message.reply('Here is your YTP! Remember, it might contain nsfw, so be careful!', {files: [`${os.tmpdir()}/${message.id}_YTP.mp4`]})
.catch(err => {
console.error(err);
return interaction.followUp({ files: [`./asset/ytp/error${Math.floor(Math.random() * 2) + 1}.mp4`] });
return message.reply('Whoops, look like the vid might be too big for discord, my bad, please try again');
});
})
.catch(err => {
console.error(err);
loadingmsg.delete();
return interaction.followUp({ files: [`./asset/ytp/error${Math.floor(Math.random() * 2) + 1}.mp4`] });
if (!args.retry)
args.retry = 0;
else
args.retry += 1;
if (args.retry === 3) {
return message.reply({files: [Math.random() < 0.5 ? './asset/ytp/error1.mp4' : './asset/ytp/error2.mp4']})
.catch(err => { // In case it can't send the video for some reason
console.error(err);
return message.reply('Oh no, an error has occurred! please try again. If this happens alot, you should report this to the developers.');
});
}
return this.client.commandHandler.runCommand(message, this.client.commandHandler.findCommand('ytp'), args);
});
},
};
}
}
module.exports = ytpCommand;

265
commands/fun/ytp.js.dev Normal file
View file

@ -0,0 +1,265 @@
const { Command } = require('discord-akairo');
const YTPGenerator = require('ytpplus-node');
const os = require('os');
const fs = require('fs');
const youtubedl = require('youtube-dl');
let queue = 0;
class ytpCommand extends Command {
constructor() {
super('ytp', {
aliases: ['ytp', 'ytpplus', 'ytp+'],
category: 'fun',
clientPermissions: ['ATTACH_FILES', 'SEND_MESSAGES'],
args: [
{
id: 'add',
match: 'flag',
flag: ['--add']
},
{
id: 'pool',
match: 'flag',
flag: ['--pool']
},
{
id: 'force',
match: 'flag',
flag: ['--force']
},
{
id: 'randomSound',
match: 'flag',
flag: ['--randomSound']
},
{
id: 'randomSoundMute',
match: 'flag',
flag: ['--randomSoundMute']
},
{
id: 'reverse',
match: 'flag',
flag: ['--reverse']
},
{
id: 'chorus',
match: 'flag',
flag: ['--chorus']
},
{
id: 'vibrato',
match: 'flag',
flag: ['--vibrato']
},
{
id: 'highPitch',
match: 'flag',
flag: ['--highPitch']
},
{
id: 'lowPitch',
match: 'flag',
flag: ['--lowPitch']
},
{
id: 'speedUp',
match: 'flag',
flag: ['--speedUp']
},
{
id: 'slowDown',
match: 'flag',
flag: ['--slowDown']
},
{
id: 'dance',
match: 'flag',
flag: ['--dance']
},
{
id: 'squidward',
match: 'flag',
flag: ['--squidward']
},
{
id: 'debug',
match: 'flag',
flag: ['--debug']
},
{
id: 'link',
type: 'string'
}
],
description: {
content: 'Generate random ytp | --add with a link or attachment to add a video to the pool, only .mp4 work | --pool to see how many vid there is currently in the pool | --force to make the command work outside of nsfw channel BE AWARE THAT IT WON\'T CHANGE THE FINAL RESULT SO NSFW CAN STILL HAPPEN',
usage: '',
examples: ['']
}
});
}
async exec(message, args) {
console.log(queue);
let prefix = this.client.commandHandler.prefix[0];
let MAX_CLIPS = 20;
if (args.pool) {
let mp4 = [];
fs.readdirSync('./asset/ytp/userVid/').forEach(file => {
if (file.endsWith('mp4')) {
mp4.push(file);
}
});
return message.channel.send(`There is currently ${mp4.length} videos, you can add yours by doing \`\`${prefix}ytp --add (link or attachment)\`\``);
}
if (args.add) {
let loadingmsg = await message.channel.send('Downloading <a:loadingmin:527579785212329984>');
let Attachment = (message.attachments).array();
let url = args.link;
// Get attachment link
if (Attachment[0] && !args.link) {
url = Attachment[0].url;
}
if (url) {
return youtubedl.exec(url, ['--no-playlist','--max-filesize', '50m', '--format=mp4', '-o', `./asset/ytp/userVid/${message.id}.mp4`], {}, function(err, output) {
console.log(output);
if (err) {
console.error(err);
loadingmsg.delete();
return message.channel.send('An error has occured, I can\'t download from the link you provided. Is it an mp4?');
} else {
if (output[2]) {
if (output[2].includes('File is larger than max-filesize')) {
loadingmsg.delete();
return message.channel.send(output[2]);
}
}
let mp4 = [];
fs.readdirSync('./asset/ytp/userVid/').forEach(file => {
if (file.endsWith('mp4')) {
mp4.push(file);
}
});
loadingmsg.delete();
return message.reply(`Video sucessfully added to the pool! There is now ${mp4.length} videos`);
}
});
} else {
loadingmsg.delete();
return message.channel.send('You need a valid video link!');
}
}
if (!message.channel.nsfw && !args.force) return message.channel.send(`Please execute this command in an NSFW channel ( Content might not be NSFW but since the video are user submitted better safe than sorry ) OR do \`\`${prefix}ytp --force\`\` to make the command work outside of nsfw channel BE AWARE THAT IT WON'T CHANGE THE FINAL RESULT SO NSFW CAN STILL HAPPEN`);
if (queue >= 1) {
let curQueue = queue;
message.channel.send(`The execution of this command has been placed in a queue, you are #${curQueue}/${queue} in the queue`);
let reminder = setInterval(() => {
message.channel.send(`The execution of this command has been placed in a queue, you are #${curQueue}/${queue} in the queue`);
}, 30000);
let checkQueue = setInterval(() => {
console.log(queue);
console.log(curQueue);
if (curQueue < 1) {
clearInterval(checkQueue);
clearInterval(reminder);
makeYTP();
}
if (queue < curQueue) curQueue = queue;
}, 1000);
//queue++;
} else {
//queue++;
makeYTP();
}
queue++;
async function makeYTP() {
console.log('making ytp');
// Read userVid folder and select random vid and only take .mp4
let mp4 = [];
let asset = [];
let files = fs.readdirSync('./asset/ytp/userVid/');
// Count number of total vid
files.forEach(file => {
if (file.endsWith('mp4')) {
mp4.push(file);
}
});
// Select random vid depending on the amount of MAX_CLIPS
for (let i = 0; i < MAX_CLIPS; i++) {
let random = Math.floor(Math.random() * files.length);
let vid = `./asset/ytp/userVid/${files[random]}`;
if (files[random].endsWith('mp4')) {
if (!asset.includes(vid)) {
asset.push(vid);
}
}
}
let loadingmsg = await message.channel.send(`Processing, this can take a **long** time, i'll ping you when i finished <a:loadingmin:527579785212329984>\nSome info: There is currently ${mp4.length} videos, you can add yours by doing \`\`${prefix}ytp --add (link or attachment)\`\``);
let options = {
debug: args.debug,
MAX_STREAM_DURATION: args.link ? args.link : Math.floor((Math.random() * 3) + 1), // Random duration of video clip
sources: './asset/ytp/sources/',
sounds: './asset/ytp/sounds/',
music: './asset/ytp/music/',
resources: './asset/ytp/resources/',
temp: os.tmpdir(),
sourceList: asset,
outro: './asset/ytp/outro.mp4', // Need an outro or it won't work
OUTPUT_FILE: `${os.tmpdir()}/${message.id}_YTP.mp4`,
MAX_CLIPS: MAX_CLIPS,
transitions: true,
effects: {
effect_RandomSound: args.randomSound,
effect_RandomSoundMute: args.randomSoundMute,
effect_Reverse: args.reverse,
effect_Chorus: args.chorus,
effect_Vibrato: args.vibrato,
effect_HighPitch: args.highPitch,
effect_LowPitch: args.lowPitch,
effect_SpeedUp: args.speedUp,
effect_SlowDown: args.slowDown,
effect_Dance: args.dance,
effect_Squidward: args.squidward
}
};
new YTPGenerator().configurateAndGo(options)
.then(() => {
queue--;
loadingmsg.delete();
return message.reply('Here is your YTP!', {files: [`${os.tmpdir()}/${message.id}_YTP.mp4`]})
.catch(() => {
return message.channel.send('Whoops, look like the vid might be too big for discord, my bad, please try again');
});
})
.catch(err => {
queue--;
console.error(err);
loadingmsg.delete();
return message.reply('Oh no! An error has occured!');
});
}
}
}
module.exports = ytpCommand;

View file

@ -0,0 +1,33 @@
const { Command } = require('discord-akairo');
const fetch = require('node-fetch');
class AdviceCommand extends Command {
constructor() {
super('advice', {
aliases: ['advice'],
category: 'general',
clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
description: {
content: 'Send some random advices',
usage: '',
examples: ['']
}
});
}
async exec(message) {
fetch('http://api.adviceslip.com/advice').then((response) => {
return response.json();
}).then((response) => {
const adviceEmbed = this.client.util.embed()
.setColor(message.member ? message.member.displayHexColor : 'NAVY')
.setTitle(response.slip.id)
.setDescription(response.slip.advice);
message.reply(adviceEmbed);
});
}
}
module.exports = AdviceCommand;

37
commands/general/clap.js Normal file
View file

@ -0,0 +1,37 @@
const { Command } = require('discord-akairo');
const rand = require('../../rand.js');
class ClapCommand extends Command {
constructor() {
super('clap', {
aliases: ['clap'],
category: 'general',
clientPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'text',
type: 'string',
prompt: {
start: 'Write something so i can replace the space with 👏',
},
match: 'rest'
}
],
description: {
content: 'replace 👏 the 👏 spaces 👏 with 👏 clap 👏',
usage: '[text]',
examples: ['replace the spaces with clap']
}
});
}
async exec(message, args) {
if (!args.text)
return;
args.text = rand.random(args.text, message);
let clap = args.text.replace(/ /g, ' 👏 ');
message.reply(`${clap} 👏`);
}
}
module.exports = ClapCommand;

View file

@ -0,0 +1,44 @@
const { Command } = require('discord-akairo');
const emojiCharacters = require('../../emojiCharacters');
const rand = require('../../rand.js');
class EmotesayCommand extends Command {
constructor() {
super('emotesay', {
aliases: ['emotesay', 'esay'],
category: 'general',
clientPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'text',
type: 'string',
prompt: {
start: 'Write something so i can replace the space with dancing emote',
},
match: 'rest'
}
],
description: {
content: 'Replace the text you send with dancing letters',
usage: '[text]',
examples: ['Hello']
}
});
}
async exec(message, args) {
let text = args.text;
if (!text)
return;
text = rand.random(text, message);
message.delete();
let emojiArray = [];
for (let i = 0; i < text.length; i++)
emojiArray[i] = emojiCharacters[text.toLowerCase().split('')[i]];
message.channel.send(emojiArray.join(''));
}
}
module.exports = EmotesayCommand;

66
commands/general/facebook Normal file
View file

@ -0,0 +1,66 @@
const { Command } = require('discord-akairo');
const Filter = require('bad-words');
let filter = new Filter();
const fetch = require('node-fetch');
const reload = require('auto-reload');
const rand = require('../../rand.js');
const { fbChannel, fbToken } = require('../../config.json');
class facebookCommand extends Command {
constructor() {
super('facebook', {
aliases: ['facebook', 'fb'],
category: 'general',
args: [
{
id: 'text',
type: 'string',
match: 'rest'
}
],
description: {
content: 'Post your message to the bot facebook page',
usage: '[text]',
examples: ['epic']
}
});
}
async exec(message, args) {
let text = args.text;
let censor = reload('../../json/censor.json');
let uncensor = reload('../../json/uncensor.json');
filter.addWords(...censor);
filter.removeWords(...uncensor);
const blacklist = reload('../../json/Blacklist.json');
const channel = this.client.channels.get(fbChannel);
if (blacklist.includes(message.author.id)) {
return message.channel.send('You have been blacklisted from this command... be less naughty next time.');
}
//Filter out swear word
text = filter.clean(text);
text = rand.random(text, message);
text = encodeURI(text);
fetch(`https://graph.facebook.com/v3.2/1254967721332652/feed?message=${text}&access_token=${fbToken}`, {
method: 'post',
}).then((response) => {
return response.json();
}).then((response) => {
console.log(response);
let postID;
if (response.id) {
postID = response.id.slice(17);
}
message.channel.send(`Go see ur epic post https://www.facebook.com/HahaYesDiscord/posts/${postID}`);
channel.send(`AUTHOR: ${message.author.username} (${message.author.id}) Sent: ${args.text}\nhttps://www.facebook.com/HahaYesDiscord/posts/${postID}`);
});
}
}
module.exports = facebookCommand;

View file

@ -0,0 +1,49 @@
const { Command } = require('discord-akairo');
const fetch = require('node-fetch');
const { memerToken } = require('../../config.json');
class memerclubCommand extends Command {
constructor() {
super('memerclub', {
aliases: ['memerclub'],
category: 'general',
clientPermissions: ['SEND_MESSAGES', 'ATTACH_FILES'],
args: [
{
id: 'text',
type: 'string',
default: '',
match: 'rest'
}
],
description: {
content: 'Post whatever you like on https://memerclub.gamingti.me ! ( no rules, go wild )',
usage: '',
examples: ['']
}
});
}
async exec(message, args) {
let Attachment = (message.attachments).array();
let img = '';
if (!Attachment[0] && !args.text) return message.channel.send('You need to input something for me to post!');
if (Attachment[0]) {
img = Attachment[0].url;
}
if (args.text)
if (args.text.includes('discord.gg')) return message.channel.send('No discord invite allowed.');
fetch(`https://memerclub.gamingti.me/api/post/?token=${memerToken}&text=${encodeURI(args.text)}&image=${img}`)
.then((response) => {
return response.json();
}).then((response) => {
console.log(response);
if (response.error) return message.channel.send(response.error);
message.channel.send(`Go check your epic post!\nhttps://memerclub.gamingti.me/post/${response.uuid}`)
});
}
}
module.exports = memerclubCommand;

View file

@ -0,0 +1,34 @@
const { Command } = require('discord-akairo');
class randomizerCommand extends Command {
constructor() {
super('randomizer', {
aliases: ['randomizer'],
category: 'general',
clientPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'text',
type: 'string',
prompt: {
start: 'Need words to randomize',
},
match: 'rest'
}
],
description: {
content: 'Choose one random word',
usage: '[multiples words]',
examples: ['Hello bye']
}
});
}
async exec(message, args) {
let words = args.text.split(' ');
return message.reply(words[Math.floor((Math.random() * words.length))]);
}
}
module.exports = randomizerCommand;

168
commands/general/say.js Normal file
View file

@ -0,0 +1,168 @@
const { Command } = require('discord-akairo');
const { MessageEmbed } = require('discord.js');
const rand = require('../../rand.js');
class SayCommand extends Command {
constructor() {
super('say', {
aliases: ['say', 'sayd'],
category: 'general',
clientPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'text',
type: 'string',
prompt: {
start: 'Write something so i can say it back',
},
match: 'rest'
}
],
description: {
content: 'Repeat what you say, (Use sayd to delete your message) [Click here to see the complete list of "tag"](https://cdn.discordapp.com/attachments/502198809355354133/561043193949585418/unknown.png)',
usage: '[text]',
examples: ['[member] is a big [adverbs] [verbs]']
}
});
}
async exec(message, args) {
let text = args.text;
if (!text)
return;
text = rand.random(text, message);
let attach = '';
if (text.includes('[attach:')) {
attach = text.split(/(\[attach:.*?])/);
for (let i = 0, l = attach.length; i < l; i++) {
if (attach[i].includes('[attach:')) {
attach = attach[i].replace('[attach:', '').slice(0, -1);
i = attach.length;
}
}
text = text.replace(/(\[attach:.*?])/, '');
}
// THIS SECTION IS VERY VERY BAD MUST CHANGE
if (text.includes('[embed]')) {
text = text.replace(/\[embed\]/, ' ');
let title = '';
let desc = '';
let image;
let thumbnail;
let footer = '';
let color;
if (text.includes('[embedImage:')) {
image = text.split(/(\[embedImage:.*?])/);
for (let i = 0, l = image.length; i < l; i++) {
if (image[i].includes('[embedImage:')) {
image = image[i].replace('[embedImage:', '').slice(0, -1);
text = text.replace(/(\[embedimage:.*?])/g, '');
i = image.length;
}
}
}
if (text.includes('[embedThumbnail:')) {
thumbnail = text.split(/(\[embedThumbnail:.*?])/);
for (let i = 0, l = thumbnail.length; i < l; i++) {
if (thumbnail[i].includes('[embedThumbnail:')) {
thumbnail = thumbnail[i].replace('[embedThumbnail:', '').slice(0, -1);
text = text.replace(/(\[embedThumbnail:.*?])/g, '');
i = thumbnail.length;
}
}
}
if (text.includes('[embedColor:')) {
color = text.split(/(\[embedColor:.*?])/);
for (let i = 0, l = color.length; i < l; i++) {
if (color[i].includes('[embedColor:')) {
color = color[i].replace('[embedColor:', '').slice(0, -1);
text = text.replace(/(\[embedColor:.*?])/g, '');
i = color.length;
}
}
}
if (text.includes('[embedTitle:')) {
title = text.split(/(\[embedTitle:.*?])/);
for (let i = 0, l = title.length; i < l; i++) {
if (title[i].includes('[embedTitle:')) {
title = title[i].replace('[embedTitle:', '').slice(0, -1);
text = text.replace(/(\[embedTitle:.*?])/g, '');
i = title.length;
}
}
}
if (text.includes('[embedFooter:')) {
footer = text.split(/(\[embedFooter:.*?])/);
for (let i = 0, l = footer.length; i < l; i++) {
if (footer[i].includes('[embedFooter:')) {
footer = footer[i].replace('[embedFooter:', '').slice(0, -1);
text = text.replace(/(\[embedFooter:.*?])/g, '');
i = footer.length;
}
}
}
if (text.includes('[embedDesc:')) {
desc = text.split(/(\[embedDesc:.*?])/);
for (let i = 0, l = desc.length; i < l; i++) {
if (desc[i].includes('[embedDesc:')) {
desc = desc[i].replace('[embedDesc:', '').slice(0, -1);
i = desc.length;
}
}
}
const embed = new MessageEmbed()
.setColor(color)
.setTitle(title)
.setImage(image)
.setThumbnail(thumbnail)
.setDescription(desc)
.setFooter(footer)
.setTimestamp();
if (attach) {
if (args.delete)
message.delete();
return message.channel.send(embed, {files: [attach]});
} else {
if (args.delete)
message.delete();
return message.channel.send(embed);
}
}
// Send the final text
if (attach) {
if (message.util.parsed.alias == 'sayd')
if (message.channel.permissionsFor(this.client.user).has('MANAGE_MESSAGES'))
message.delete();
else
message.channel.send('Im missing he `MANAGE_MESSAGES` perm to delete your message!');
return message.channel.send(text, {files: [attach]});
} else {
if (message.util.parsed.alias == 'sayd')
if (message.channel.permissionsFor(this.client.user).has('MANAGE_MESSAGES'))
message.delete();
else
message.channel.send('Im missing he `MANAGE_MESSAGES` perm to delete your message!');
return message.channel.send(text);
}
}
}
module.exports = SayCommand;

Some files were not shown because too many files have changed in this diff Show more