mirror of
https://github.com/gizmo4487/O-Bot.git
synced 2025-05-30 20:22:31 -05:00
Complete rewrite - Added slash commands
This commit is contained in:
parent
382ac41d7f
commit
6cae1d1eea
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
o_sound
|
||||||
|
.env
|
||||||
|
deploy-commands*.js
|
||||||
|
delete-command-*.js
|
||||||
|
*.wav
|
||||||
|
*.mp3
|
38
README.md
38
README.md
@ -4,47 +4,31 @@ A Discord bot themed around O, a character in SEGA's Puyo Puyo Tetris
|
|||||||
**Adapted from the tutorial at https://www.digitaltrends.com/gaming/how-to-make-a-discord-bot/**
|
**Adapted from the tutorial at https://www.digitaltrends.com/gaming/how-to-make-a-discord-bot/**
|
||||||
|
|
||||||
## Dependencies and setup
|
## Dependencies and setup
|
||||||
Run ``npm install`` in the bot folder to install dependencies.
|
* Run ``npm install`` in the bot folder to install dependencies.
|
||||||
In order for ``o!listen`` to work, this bot **must** run on Linux with the "sox" program installed:
|
* Create a ``.env`` file in the root directory and populate ``TOKEN`` with your Discord bot token.
|
||||||
|
* To run: ``node --env-file=.env o-bot.js``
|
||||||
|
|
||||||
``sudo apt install sox``
|
You may notice that slash commands are missing after you first invite the bot. You can fix this by following [this guide](https://discordjs.guide/creating-your-bot/command-deployment.html).
|
||||||
|
|
||||||
This is used to convert the raw PCM audio into a WAV file for uploading.
|
|
||||||
|
|
||||||
Additionally, a file named "ready.wav" containing 100 ms of silence should be placed in the O-Bot root directory. This is used by ``o!listen`` to get O-Bot out of a perpetual speaking state.
|
|
||||||
|
|
||||||
## A word about audio files
|
## A word about audio files
|
||||||
O's voice lines are **not** included in this repository. They can be found in your (hopefully legal) copy of Puyo Puyo Tetris. By default, O-Bot looks for audio files numbered in hexadecimal from "00000000.wav" to "00000028.wav" in each voice folder. These folders should be "[O-Bot]/o_sound", "[O-Bot]/o_sound/alt", "[O-Bot]/o_sound/j", and "[O-Bot]/o_sound/j_alt".
|
O's voice lines are **not** included in this repository. They can be found in your (hopefully legal) copy of Puyo Puyo Tetris. By default, O-Bot looks for audio files numbered in hexadecimal from "00000000.wav" to "00000028.wav" in each voice folder. These folders should be "[O-Bot]/o_sound", "[O-Bot]/o_sound/alt", "[O-Bot]/o_sound/jp", and "[O-Bot]/o_sound/jp_alt".
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
### General
|
### General
|
||||||
* ``o!help``: Display help message
|
* ``/help``: Display help message
|
||||||
* ``o!pi``: Make O speak
|
* ``/pi``: Make O speak
|
||||||
|
|
||||||
### Voice
|
### Voice
|
||||||
* ``o!voice``: O-Bot will join a voice channel and play one of O's voice lines
|
* ``/voice [type]``: O-Bot will join a voice channel and play one of O's voice lines. ``type`` can be ``Normal``, ``Alt``, ``JP``, or ``JP Alt``. The default is ``Normal``.
|
||||||
* ``o!altvoice``: Same as ``o!voice``, but with O's alternate voice
|
* ``/radio``: Stream audio from an internet radio station located at https://radio.gizmo4487.dev
|
||||||
* ``o!jvoice``: Same as ``o!voice``, but with O's default Japanese voice
|
|
||||||
* ``o!jaltvoice``: Same as ``o!voice``, but with O's alternate Japanese voice
|
|
||||||
* ``o!listen``: O-Bot will record your voice until you stop speaking, then send a WAV file to the text channel you ran the command in
|
|
||||||
|
|
||||||
### Other
|
|
||||||
* ``o!tetrio-rooms``: Get the current room list for the game [TETR.IO](https://tetr.io)
|
|
||||||
* ``o!randomhex``: Generate a random hexadecimal number from 0x00 to 0x28 (used to debug randomHex() for selecting which WAV file to play)
|
|
||||||
* ``o!time``: Get the current time (including seconds) in a 12-hour format
|
|
||||||
* ``o!twitch``: Get link to my Twitch channel
|
|
||||||
* ``o!donate``: Get link to my Streamlabs donation page
|
|
||||||
|
|
||||||
*More commands to follow*
|
*More commands to follow*
|
||||||
|
|
||||||
## Invitation Link
|
## Invitation Link
|
||||||
To add O-Bot to your server, [click here](https://discordapp.com/oauth2/authorize?&client_id=688221134751399992&scope=bot&permissions=68672)
|
To add O-Bot to your server, [click here](https://discord.com/oauth2/authorize?client_id=688221134751399992)
|
||||||
|
|
||||||
## More Information
|
## More Information
|
||||||
Be sure O-Bot has permission to read messages, send messages, and speak in voice channels.
|
Be sure O-Bot has permission to speak in voice channels.
|
||||||
|
|
||||||
**Please note that 'auth.json' is intentionally missing. If you're looking for tokens, go to Chuck E. Cheese!**
|
|
||||||
|
|
||||||
*TETR.IO created by osk*
|
|
||||||
|
|
||||||
*O-Bot created by gizmo4487*
|
*O-Bot created by gizmo4487*
|
||||||
|
36
cmd/text/help.js
Normal file
36
cmd/text/help.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const { EmbedBuilder, MessageFlags, SlashCommandBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('help')
|
||||||
|
.setDescription('Show the help page'),
|
||||||
|
async execute(interaction) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle("O-Bot Help")
|
||||||
|
.setDescription("Type `/` to view a list of commands.")
|
||||||
|
.addFields(
|
||||||
|
{
|
||||||
|
name: "Commands",
|
||||||
|
value: "`/pi`: Make O-Bot say something in text chat\n\n`/voice [type]`: Make O-Bot join the voice channel you're currently in and speak. `type` can be `Normal`, `Alt`, `JP`, or `JP Alt`.\nIf left blank, the default is `Normal`.\n\n`/radio`: Enjoy listening to high-quality rips all day long with a 24/7 radio featuring SiIvaGunner's finest! Also available [here](https://radio.gizmo4487.dev).\n\n`/leave`: Disconnect O-Bot from the voice channel\n\n`/help`: Show this page",
|
||||||
|
inline: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "YouTube",
|
||||||
|
value: "[gizmo4487](https://youtube.com/@gizmo4487)",
|
||||||
|
inline: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Twitch",
|
||||||
|
value: "[notengonombreusario](https://twitch.tv/notengonombreusario)",
|
||||||
|
inline: true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.setColor("#ffe511")
|
||||||
|
.setFooter({
|
||||||
|
text: "Created by gizmo4487",
|
||||||
|
iconURL: "https://gizmo4487.dev/O.jpg",
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.reply({ embeds: [embed], flags: MessageFlags.Ephemeral });
|
||||||
|
},
|
||||||
|
};
|
10
cmd/text/pi.js
Normal file
10
cmd/text/pi.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('pi')
|
||||||
|
.setDescription('Make O-Bot say something'),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.reply('Pipi? Pipi-pipipipipi!');
|
||||||
|
},
|
||||||
|
};
|
33
cmd/util/RadioPlayer.js
Normal file
33
cmd/util/RadioPlayer.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { createAudioResource, createAudioPlayer, NoSubscriberBehavior } = require('@discordjs/voice');
|
||||||
|
class RadioPlayer {
|
||||||
|
static audioPlayer = null;
|
||||||
|
|
||||||
|
static async getAudioPlayer(url) {
|
||||||
|
if(this.audioPlayer == null) {
|
||||||
|
let data = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'O-Bot'
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
if(data.status == 200) {
|
||||||
|
console.log('Creating audio resource');
|
||||||
|
var fileStream = data.body;
|
||||||
|
var res = createAudioResource(fileStream, {inlineVolume: true});
|
||||||
|
//res.volume.setVolume(2);
|
||||||
|
this.audioPlayer = createAudioPlayer({
|
||||||
|
behaviors: {noSubscriber: NoSubscriberBehavior.Play},
|
||||||
|
});
|
||||||
|
this.audioPlayer.play(res);
|
||||||
|
return this.audioPlayer;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw Error('Radio is down!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.audioPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = RadioPlayer;
|
15
cmd/util/util.js
Normal file
15
cmd/util/util.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
function randomHex(){
|
||||||
|
let hex="";
|
||||||
|
let randNum = 0;
|
||||||
|
randNum = (Math.floor(Math.random()*Math.floor(41)));
|
||||||
|
if(randNum<16){
|
||||||
|
hex = "0" + randNum.toString(16);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
hex = randNum.toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { randomHex };
|
67
cmd/util/vc.js
Normal file
67
cmd/util/vc.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
const { MessageFlags } = require('discord.js');
|
||||||
|
const { createAudioPlayer, createAudioResource, getVoiceConnection, joinVoiceChannel } = require('@discordjs/voice');
|
||||||
|
const RadioPlayer = require('./RadioPlayer.js');
|
||||||
|
async function play(interaction, sound){
|
||||||
|
try{
|
||||||
|
const member = interaction.member;
|
||||||
|
const VC = member.voice.channel;
|
||||||
|
if(!VC){
|
||||||
|
return(
|
||||||
|
{
|
||||||
|
content: 'Join a voice channel and try again!',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
else{
|
||||||
|
var connection = joinVoiceChannel({
|
||||||
|
channelId: VC.id,
|
||||||
|
guildId: VC.guild.id,
|
||||||
|
adapterCreator: VC.guild.voiceAdapterCreator,
|
||||||
|
});
|
||||||
|
if(sound.startsWith('http')) {
|
||||||
|
var player = await RadioPlayer.getAudioPlayer(sound);
|
||||||
|
connection.subscribe(player);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('Playing local file');
|
||||||
|
var resource = createAudioResource(sound);
|
||||||
|
var player = createAudioPlayer();
|
||||||
|
//console.log('Playing ' + sound);
|
||||||
|
connection.subscribe(player);
|
||||||
|
player.play(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(error){
|
||||||
|
console.error(error);
|
||||||
|
return(
|
||||||
|
{
|
||||||
|
content: 'Something went wrong! Do I have permission to join the voice channel and speak?',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
return('Now speaking in voice channel!');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect(interaction) {
|
||||||
|
const connection = getVoiceConnection(interaction.guildId);
|
||||||
|
if(connection) {
|
||||||
|
connection.destroy();
|
||||||
|
return("Bye!");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return(
|
||||||
|
{
|
||||||
|
content: 'Can\'t disconnect me because I\'m not in a voice channel!',
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = { play, disconnect };
|
12
cmd/voice/leave.js
Normal file
12
cmd/voice/leave.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const vcutil = require('../util/vc.js');
|
||||||
|
const util = require('../util/util.js');
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('leave')
|
||||||
|
.setDescription('Disconnect O-Bot from the voice channel'),
|
||||||
|
async execute(interaction) {
|
||||||
|
const response = await vcutil.disconnect(interaction);
|
||||||
|
await interaction.reply(response);
|
||||||
|
},
|
||||||
|
};
|
11
cmd/voice/radio.js
Normal file
11
cmd/voice/radio.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const vcutil = require('../util/vc.js');
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('radio')
|
||||||
|
.setDescription('Play 24/7 SiIvaGunner music in a voice channel'),
|
||||||
|
async execute(interaction) {
|
||||||
|
const response = await vcutil.play(interaction, 'http://localhost:8000/live');
|
||||||
|
await interaction.reply(response);
|
||||||
|
},
|
||||||
|
};
|
25
cmd/voice/voice.js
Normal file
25
cmd/voice/voice.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const vcutil = require('../util/vc.js');
|
||||||
|
const util = require('../util/util.js');
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('voice')
|
||||||
|
.setDescription('Make O-Bot join a voice channel and speak')
|
||||||
|
.addStringOption(option =>
|
||||||
|
option.setName('type')
|
||||||
|
.setDescription('Type of voice clip to play')
|
||||||
|
.setMaxLength(6)
|
||||||
|
.setRequired(false)
|
||||||
|
.addChoices(
|
||||||
|
{ name: 'Normal', value: 'normal' },
|
||||||
|
{ name: 'Alt', value: 'alt' },
|
||||||
|
{ name: 'JP', value: 'jp' },
|
||||||
|
{ name: 'JP Alt', value: 'jp_alt' },
|
||||||
|
)),
|
||||||
|
async execute(interaction) {
|
||||||
|
const voiceOption = interaction.options.getString('type') ?? 'normal';
|
||||||
|
const prefix = 'o_sound' + (voiceOption!='normal'?'/' + voiceOption:'');
|
||||||
|
const response = await vcutil.play(interaction, prefix + "/000000" + util.randomHex() + ".wav");
|
||||||
|
await interaction.reply(response);
|
||||||
|
},
|
||||||
|
};
|
386
o-bot.js
386
o-bot.js
@ -1,345 +1,81 @@
|
|||||||
const Discord = require('discord.js');
|
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
|
||||||
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
|
const fetch = require('node-fetch');
|
||||||
const Constants = require('discord.js/src/util/Constants.js');
|
const fs = require('node:fs');
|
||||||
const fs = require('fs');
|
const path = require('node:path');
|
||||||
Constants.DefaultOptions.ws.properties.$browser='Discord Android';
|
|
||||||
const auth = require('./auth.json');
|
|
||||||
var sound;
|
|
||||||
const {exec} = require("child_process");
|
|
||||||
|
|
||||||
// Create bot object
|
// Create bot object
|
||||||
const bot = new Discord.Client({autoReconnect:true});
|
const bot = new Client({
|
||||||
|
intents: [
|
||||||
|
GatewayIntentBits.Guilds,
|
||||||
|
GatewayIntentBits.GuildVoiceStates,
|
||||||
|
],
|
||||||
|
autoReconnect:true,
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.commands = new Collection();
|
||||||
|
// Begin slash command setup
|
||||||
|
const cmdFoldersPath = path.join(__dirname, 'cmd');
|
||||||
|
const cmdFolders = fs.readdirSync(cmdFoldersPath);
|
||||||
|
|
||||||
|
for(const folder of cmdFolders) {
|
||||||
|
const cmdPath = path.join(cmdFoldersPath, folder);
|
||||||
|
const cmdFiles = fs.readdirSync(cmdPath).filter(file => file.endsWith('.js'));
|
||||||
|
for(const file of cmdFiles) {
|
||||||
|
const filePath = path.join(cmdPath, file);
|
||||||
|
const cmd = require(filePath);
|
||||||
|
if('data' in cmd && 'execute' in cmd) {
|
||||||
|
console.log('Adding command: ' + cmd.data.name);
|
||||||
|
bot.commands.set(cmd.data.name, cmd);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('[WARN] The command at ' + filePath + ' is missing a required "data" or "execute" property!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End slash command setup
|
||||||
// Display console message when logged in
|
// Display console message when logged in
|
||||||
bot.once('ready', () => {
|
bot.once('ready', () => {
|
||||||
console.log('O-Bot is ready!');
|
console.log('O-Bot is ready!');
|
||||||
bot.user.setActivity('Puyo Puyo\u2122Tetris\u00AE 2',{type: 'PLAYING'});
|
|
||||||
|
|
||||||
// Uncomment to set status to "idle"
|
|
||||||
/*bot.user.setPresence({
|
|
||||||
status: 'idle'
|
|
||||||
});*/
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reconnecting
|
// Reconnecting
|
||||||
bot.on("reconnecting", () => {
|
bot.on("reconnecting", () => {
|
||||||
console.log("Reconnecting!");
|
console.log("Reconnecting!");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Resume
|
// Resume
|
||||||
bot.on("resume", () => {
|
bot.on("resume", () => {
|
||||||
console.log("Connection restored!");
|
console.log("Connection restored!");
|
||||||
bot.user.setActivity('Puyo Puyo\u2122Tetris\u00AE 2',{type: 'PLAYING'});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//bot.on('debug', console.log);
|
|
||||||
bot.on('message', message => {
|
|
||||||
bot.user.setActivity('Puyo Puyo\u2122Tetris\u00AE 2',{type: 'PLAYING'});
|
|
||||||
// Prefix is 'o!'
|
|
||||||
if (message.content.substring(0, 2) == 'o!') {
|
|
||||||
var args = message.content.substring(2).split(' ');
|
|
||||||
var cmd = args[0];
|
|
||||||
|
|
||||||
args = args.splice(1);
|
|
||||||
|
|
||||||
// Commands
|
// Handle slash commands
|
||||||
switch(cmd) {
|
bot.on(Events.InteractionCreate, async interaction => {
|
||||||
|
if(!interaction.isChatInputCommand()) return;
|
||||||
// o!help
|
|
||||||
case 'help':
|
|
||||||
message.channel.send("Commands:\n\n**General**\n``o!help``: Display this message\n``o!pi``: Make O speak\n\n**Voice**\n``o!voice``: Make O speak for real\n``o!altvoice``: Make O speak in his alternate voice\n``o!jvoice``: Play one of O's Japanese voice lines\n``o!jaltvoice``: Play one of O's alternate Japanese voice lines\n``o!listen``: O-Bot will record your voice and send it back to you! (These recordings are NOT stored locally.)\n``o!dc``: Make O-Bot leave the voice channel\n\n**Other**\n``o!tetrio-rooms``: Show all open rooms in TETR.IO\n``o!twitch``: Get link for O-Bot creator's Twitch channel\n``o!donate``: Feed O-Bot");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!pi
|
|
||||||
case 'pi':
|
|
||||||
message.channel.send('Pipi? Pipi-pipipipipi!');
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!voice
|
|
||||||
case 'voice':
|
|
||||||
play(message, "o_sound/000000" + randomHex() + ".wav");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!altvoice
|
|
||||||
case 'altvoice':
|
|
||||||
play(message, "o_sound/alt/000000" + randomHex() + ".wav");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!jvoice
|
|
||||||
case 'jvoice':
|
|
||||||
play(message, "o_sound/j/000000" + randomHex() + ".wav");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!jaltvoice
|
|
||||||
case 'jaltvoice':
|
|
||||||
play(message, "o_sound/j_alt/000000" + randomHex() + ".wav");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!twitch
|
const cmd = interaction.client.commands.get(interaction.commandName);
|
||||||
case 'twitch':
|
if(!cmd) {
|
||||||
message.channel.send('Pipi!\nhttps://twitch.tv/notengonombreusario');
|
console.error('No matching command ' + interaction.commandName + ' was found!');
|
||||||
break;
|
return;
|
||||||
|
}
|
||||||
// o!donate
|
|
||||||
case 'donate':
|
|
||||||
message.channel.send('Pipipiii!!\nhttps://streamlabs.com/notengonombreusario');
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!dc
|
|
||||||
case 'dc':
|
|
||||||
if(!message.member.voice.channel){
|
|
||||||
message.channel.send("``o!dc`` can only be run while you are in a voice channel!");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
message.channel.send("Disconnecting!");
|
|
||||||
message.member.voice.channel.leave();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'leave':
|
|
||||||
if(!message.member.voice.channel){
|
|
||||||
message.channel.send("``o!leave`` can only be run while you are in a voice channel!");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
message.channel.send("Disconnecting!");
|
|
||||||
message.member.voice.channel.leave();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
// o!randomhex
|
|
||||||
case 'randomhex':
|
|
||||||
message.channel.send("Your random hexadecimal number is " + randomHex());
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!time
|
|
||||||
case 'time':
|
|
||||||
message.channel.send("The current time is " + getTime());
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!listen
|
|
||||||
case 'listen':
|
|
||||||
listen(message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// o!tetrio-rooms
|
|
||||||
case 'tetrio-rooms':
|
|
||||||
message.channel.startTyping();
|
|
||||||
httpGet(message, "https://tetr.io/api/rooms");
|
|
||||||
//message.channel.send("Command temporarily unavailable");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Invalid command
|
|
||||||
default:
|
|
||||||
message.channel.send("Pipi...\nInvalid command! Use ``o!help`` to list available commands!");
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
try {
|
||||||
// End of command list
|
await cmd.execute(interaction);
|
||||||
}
|
}
|
||||||
}
|
catch(error) {
|
||||||
|
const errorMsg = `Uh-oh...something went wrong. Try again later!`;
|
||||||
|
console.error(error);
|
||||||
|
if(interaction.replied || interaction.deferred) {
|
||||||
|
await interaction.followUp({
|
||||||
|
content: errorMsg,
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await interaction.reply({
|
||||||
|
content: errorMsg,
|
||||||
|
flags: MessageFlags.Ephemeral
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
bot.login(process.env.TOKEN);
|
||||||
async function play(message, sound){
|
|
||||||
try{
|
|
||||||
const VC = message.member.voice.channel;
|
|
||||||
if(!message.guild){
|
|
||||||
message.channel.send("The bot cannot join private voice chats.")
|
|
||||||
}
|
|
||||||
|
|
||||||
else if(!VC){
|
|
||||||
message.channel.send("Join a voice channel and try again!");
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (!VC.permissionsFor(message.client.user).has('CONNECT') || !VC.permissionsFor(message.client.user).has('SPEAK')) {
|
|
||||||
message.channel.send('Missing join/speak permissions!');
|
|
||||||
}
|
|
||||||
|
|
||||||
else{
|
|
||||||
// Here we try to join the voicechat and save our connection into our object.
|
|
||||||
var connection = await VC.join();
|
|
||||||
//message.channel.send("Playing!");
|
|
||||||
console
|
|
||||||
const dispatcher = connection.play(sound);
|
|
||||||
dispatcher.on("finish", () => {
|
|
||||||
//message.channel.send("Disconnecting!");
|
|
||||||
dispatcher.destroy();
|
|
||||||
//VC.leave();
|
|
||||||
})
|
|
||||||
.on('error', error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch(error){
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomHex(){
|
|
||||||
let hex="";
|
|
||||||
let randNum = 0;
|
|
||||||
randNum = (Math.floor(Math.random()*Math.floor(41)));
|
|
||||||
if(randNum<16){
|
|
||||||
hex = "0" + randNum.toString(16);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
hex = randNum.toString(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTime(){
|
|
||||||
var date = new Date();
|
|
||||||
var hr = date.getHours();
|
|
||||||
var AMPM = hr>=12?"PM":"AM";
|
|
||||||
hr=((hr+11)%12+1);
|
|
||||||
|
|
||||||
var min = date.getMinutes();
|
|
||||||
if(min < 10){
|
|
||||||
min = "0" + min;
|
|
||||||
}
|
|
||||||
var sec = date.getSeconds();
|
|
||||||
if(sec < 10){
|
|
||||||
sec = "0" + sec;
|
|
||||||
}
|
|
||||||
var time = hr + ":" + min + ":" + sec + " " + AMPM;
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listen(message){
|
|
||||||
try{
|
|
||||||
var date = new Date();
|
|
||||||
const VC = message.member.voice.channel;
|
|
||||||
if(!message.guild){
|
|
||||||
message.channel.send("The bot cannot join private voice chats.");
|
|
||||||
}
|
|
||||||
|
|
||||||
else if(!VC){
|
|
||||||
message.channel.send("Join a voice channel and try again!");
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (!VC.permissionsFor(message.client.user).has('CONNECT') || !VC.permissionsFor(message.client.user).has('SPEAK')) {
|
|
||||||
message.channel.send('Missing join/speak permissions!');
|
|
||||||
}
|
|
||||||
|
|
||||||
else{
|
|
||||||
|
|
||||||
// Play silence, then start listening
|
|
||||||
var connection = await VC.join();
|
|
||||||
var user = message.member.user;
|
|
||||||
play(message, "ready.wav");
|
|
||||||
setTimeout(async () => {
|
|
||||||
message.channel.send("Speak now!");
|
|
||||||
|
|
||||||
const audio = connection.receiver.createStream(user,{mode: 'pcm'});
|
|
||||||
var filename = message.member.user.username + "_" + date.getTime();
|
|
||||||
audio.pipe(fs.createWriteStream(filename));
|
|
||||||
var cmd = "sox -t raw -r 48000 -e signed -b 16 -c 2 " + filename + " " + filename + ".wav";
|
|
||||||
var delcmd = "rm " + filename + ".wav" + " && rm " + filename;
|
|
||||||
var wavfile = filename + ".wav";
|
|
||||||
|
|
||||||
audio.on('end', async (filename) => {
|
|
||||||
message.channel.send("Finished!");
|
|
||||||
audio.destroy();
|
|
||||||
|
|
||||||
exec(cmd, (error, stdout, stderr) => {
|
|
||||||
if(error){
|
|
||||||
console.log(error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(stderr){
|
|
||||||
console.log(stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(stdout);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() =>{
|
|
||||||
message.channel.send({
|
|
||||||
files: [wavfile]
|
|
||||||
});
|
|
||||||
},500);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
exec(delcmd, (error, stdout, stderr) => {
|
|
||||||
if(error){
|
|
||||||
console.log(error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(stderr){
|
|
||||||
console.log(stderr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(stdout);
|
|
||||||
})
|
|
||||||
},1000);
|
|
||||||
})
|
|
||||||
|
|
||||||
.on('error', error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
}, 200);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch(error){
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function printRooms(message, jsonInput){
|
|
||||||
var additionalText = "";
|
|
||||||
var roomList = "";
|
|
||||||
var roomState = "";
|
|
||||||
roomStats = JSON.parse(jsonInput);
|
|
||||||
message.channel.send("Room list:");
|
|
||||||
for(i in roomStats.rooms){
|
|
||||||
additionalText = "";
|
|
||||||
if(roomStats.rooms[i].meta.userlimit>0){
|
|
||||||
additionalText = "/" + roomStats.rooms[i].meta.userlimit;
|
|
||||||
}
|
|
||||||
if(roomStats.rooms[i].playercount > roomStats.rooms[i].playingplayers){
|
|
||||||
additionalText = additionalText + " " + "+" + (roomStats.rooms[i].playercount - roomStats.rooms[i].playingplayers);
|
|
||||||
}
|
|
||||||
roomList = roomList + "\n" + roomStats.rooms[i].meta.name + " -- " + roomStats.rooms[i].playingplayers + additionalText + "\n" + roomStats.rooms[i].state + "\nhttps://tetr.io/#" + roomStats.rooms[i].id + "\n\n";
|
|
||||||
}
|
|
||||||
message.channel.send(roomList);
|
|
||||||
message.channel.stopTyping();
|
|
||||||
|
|
||||||
//console.log(jsonInput);
|
|
||||||
//console.log(roomStats.rooms);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function httpGet(message, URL)
|
|
||||||
{
|
|
||||||
var xmlHttp = new XMLHttpRequest();
|
|
||||||
xmlHttp.open( "GET", URL, true);
|
|
||||||
xmlHttp.setRequestHeader("Authorization", "Bearer " + auth.tetrio_token);
|
|
||||||
xmlHttp.onload = function(e){
|
|
||||||
if(xmlHttp.readyState === 4){
|
|
||||||
if(xmlHttp.status === 200){
|
|
||||||
printRooms(message, xmlHttp.responseText);
|
|
||||||
} else{
|
|
||||||
console.error(xmlHttp.statusText);
|
|
||||||
message.channel.stopTyping();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xmlHttp.onerror = function (e){
|
|
||||||
console.error(xmlHttp.statusText);
|
|
||||||
message.channel.stopTyping();
|
|
||||||
};
|
|
||||||
xmlHttp.send( null );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bot.login(auth.token);
|
|
||||||
|
3545
package-lock.json
generated
3545
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,14 +6,16 @@
|
|||||||
"author": "gizmo4487",
|
"author": "gizmo4487",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/opus": "^0.1.0",
|
"@discordjs/opus": "^0.1.0",
|
||||||
|
"@discordjs/voice": "^0.16.0",
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
"discord.js": "^12.0.2",
|
"discord.js": "14",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"erlpack": "github:discordapp/erlpack",
|
"erlpack": "github:discordapp/erlpack",
|
||||||
"ffmpeg-static": "^3.0.0",
|
"ffmpeg-static": "^3.0.0",
|
||||||
"install-peerdeps": "^2.0.1",
|
"install-peerdeps": "^2.0.1",
|
||||||
"install-peers": "^1.0.3",
|
"install-peers": "^1.0.3",
|
||||||
"libsodium-wrappers": "^0.7.6",
|
"libsodium-wrappers": "^0.7.6",
|
||||||
|
"opusscript": "^0.0.8",
|
||||||
"xmlhttprequest": "^1.8.0",
|
"xmlhttprequest": "^1.8.0",
|
||||||
"zlib-sync": "^0.1.6"
|
"zlib-sync": "^0.1.6"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user