Compare commits
10 Commits
57fcb9f625
...
e68272236f
Author | SHA1 | Date | |
---|---|---|---|
e68272236f | |||
febbd1fa8e | |||
0033bea4ab | |||
d120cf9945 | |||
24b97bc5b0 | |||
7ebdb84c98 | |||
30a2114ff4 | |||
dce3b6ecc4 | |||
b2b8bccaa6 | |||
97af77745f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
.env
|
||||
config.json
|
||||
node_modules
|
||||
keyfile.json
|
17
README.md
17
README.md
@ -3,3 +3,20 @@
|
||||
BreadBot helps, BreadBot does the things. Whatever things I'm willing to add to BreadBot.
|
||||
|
||||
BreadBot is the breadiest bread.
|
||||
|
||||
In all seriousness, BreadBot is my first real attempt at a Discord bot. I'm writing it such that it can really only be used in one server at the moment, although that will hopefully change in the future. The intention is really for it only to be used for the Team 2648 Discord server.
|
||||
|
||||
I have a subset of features that I want it to have before I call it "production ready" and a set of features that I want if I'm feeling adventurous and want to put in a lot of work.
|
||||
|
||||
"Production Ready" Features
|
||||
- [ ] Google Calendar Integration for Event Management
|
||||
- [ ] Create/Manage/Remove Events and Calendars
|
||||
- [ ] Add Autocomplete for common elements like Calendar Names, Event Names, Timezones, etc.
|
||||
- [ ] Calendar Announcements for Upcoming Events
|
||||
- [ ] Poll Creation and Results Announcements
|
||||
- [ ] Conversation Archiving (May be Removed)
|
||||
|
||||
"Adventurous (Lots of Work)" Features
|
||||
- [ ] BreadBot Voice Chat Hall Monitor
|
||||
- [ ] Breadle (BreadBot Idle Bread Baking Game)
|
||||
- [ ] BreadBot Calendar Send To Email
|
||||
|
29
breadbot.js
29
breadbot.js
@ -3,22 +3,41 @@ const path = require('node:path');
|
||||
const { Client, Events, GatewayIntentBits, Collection } = require('discord.js');
|
||||
const { token } = require('./config.json');
|
||||
|
||||
const getAllFiles = function(directoryPath, arrayOfFiles) {
|
||||
const files = fs.readdirSync(directoryPath);
|
||||
|
||||
arrayOfFiles = arrayOfFiles || [];
|
||||
|
||||
files.forEach(file => {
|
||||
if (fs.statSync(directoryPath + path.sep + file).isDirectory()) {
|
||||
arrayOfFiles = getAllFiles(directoryPath + path.sep + file, arrayOfFiles);
|
||||
}
|
||||
else {
|
||||
arrayOfFiles.push(path.join(__dirname, directoryPath, path.sep, file));
|
||||
}
|
||||
});
|
||||
|
||||
return arrayOfFiles;
|
||||
};
|
||||
|
||||
const allFiles = [];
|
||||
getAllFiles('.' + path.sep + 'commands', allFiles);
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.commands = new Collection();
|
||||
|
||||
const commandsPath = path.join(__dirname, 'commands');
|
||||
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||
const commandFiles = allFiles.filter(file => file.endsWith('.js'));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const filePath = path.join(commandsPath, file);
|
||||
const command = require(filePath);
|
||||
const command = require(file);
|
||||
|
||||
if ('data' in command && 'execute' in command) {
|
||||
client.commands.set(command.data.name, command);
|
||||
console.log(`[INFO] Loaded command at ${file}`);
|
||||
}
|
||||
else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
console.log(`[WARNING] The command at ${file} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
33
calendarpulltest.js
Normal file
33
calendarpulltest.js
Normal file
@ -0,0 +1,33 @@
|
||||
const { google } = require('googleapis');
|
||||
const { googlePrivateKey, googleClientEmail, googleProjectNumber } = require('./config.json');
|
||||
|
||||
const SCOPES = ['https://www.googleapis.com/auth/calendar'];
|
||||
|
||||
async function main() {
|
||||
const jwtClient = new google.auth.JWT(
|
||||
googleClientEmail,
|
||||
null,
|
||||
googlePrivateKey,
|
||||
SCOPES,
|
||||
);
|
||||
|
||||
const calendar = new google.calendar({
|
||||
version: 'v3',
|
||||
project: googleProjectNumber,
|
||||
auth: jwtClient,
|
||||
});
|
||||
|
||||
calendar.calendarList.list({}, (err, res) => {
|
||||
if (err) {
|
||||
console.log('[ERROR]');
|
||||
console.log(err.errors);
|
||||
return;
|
||||
}
|
||||
console.log(res.data.items.map(x => x.summary + '---' + x.timeZone));
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(e => {
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
37
commands/googlecalendar/addcalendar.js
Normal file
37
commands/googlecalendar/addcalendar.js
Normal file
@ -0,0 +1,37 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { addCalendar } = require('../../utilities/googlecalendar.js');
|
||||
// const { getTimeZones } = require('@vvo/tzdb');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('addcalendar')
|
||||
.setDescription('Creates a new Google Calendar')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The new name for the calendar')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('timezone')
|
||||
.setDescription('The Time Zone of this new calendar, must be in IANA format')
|
||||
.setRequired(true)),
|
||||
// .addChoices(getTimeZones().map(tz => {
|
||||
// return { name: tz.name, value: tz.name };
|
||||
// }))),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const name = interaction.options.getString('name');
|
||||
const timezone = interaction.options.getString('timezone');
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
addCalendar(name, timezone, async (success, message, extra) => {
|
||||
const embedResponse = new EmbedBuilder()
|
||||
.setColor(success ? 0x00FF00 : 0xFF0000)
|
||||
.setTitle(message);
|
||||
|
||||
await interaction.editReply({ embeds: [ embedResponse ] });
|
||||
});
|
||||
},
|
||||
};
|
0
commands/googlecalendar/addevent.js
Normal file
0
commands/googlecalendar/addevent.js
Normal file
27
commands/googlecalendar/deletecalendar.js
Normal file
27
commands/googlecalendar/deletecalendar.js
Normal file
@ -0,0 +1,27 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { deleteCalendar } = require('../../utilities/googlecalendar.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('deletecalendar')
|
||||
.setDescription('Permanently deletes a calendar and it\'s associated events')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the calendar you want to delete')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
const name = interaction.options.getString('name');
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
deleteCalendar(name, async (success, message, extra) => {
|
||||
const embedResponse = new EmbedBuilder()
|
||||
.setColor(success ? 0x0FF00 : 0xFF0000)
|
||||
.setTitle(message);
|
||||
|
||||
await interaction.editReply({ embeds: [ embedResponse ] });
|
||||
});
|
||||
},
|
||||
};
|
0
commands/googlecalendar/deleteevent.js
Normal file
0
commands/googlecalendar/deleteevent.js
Normal file
20
commands/googlecalendar/listcalendar.js
Normal file
20
commands/googlecalendar/listcalendar.js
Normal file
@ -0,0 +1,20 @@
|
||||
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
|
||||
const { getListOfCalendars } = require('../../utilities/googlecalendar');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('listcalendars')
|
||||
.setDescription('Lists the currently available calendars'),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
getListOfCalendars({}, async (success, message, extra) => {
|
||||
const embedResponse = new EmbedBuilder()
|
||||
.setColor(success ? 0x00FF00 : 0xFF0000)
|
||||
.setTitle(message)
|
||||
.setDescription(extra.map(x => x.summary + ' --- ' + x.timeZone).join('\n\n'));
|
||||
|
||||
await interaction.editReply({ embeds: [ embedResponse ] });
|
||||
});
|
||||
},
|
||||
};
|
0
commands/googlecalendar/listevents.js
Normal file
0
commands/googlecalendar/listevents.js
Normal file
0
commands/googlecalendar/modifyevent.js
Normal file
0
commands/googlecalendar/modifyevent.js
Normal file
35
datetesting.js
Normal file
35
datetesting.js
Normal file
@ -0,0 +1,35 @@
|
||||
const readline = require('readline');
|
||||
|
||||
const r1 = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
terminal: false,
|
||||
});
|
||||
|
||||
let date1 = null;
|
||||
let date2 = null;
|
||||
let result = null;
|
||||
|
||||
r1.on('line', line => {
|
||||
if (line.startsWith('date1')) {
|
||||
date1 = new Date(line.split(':')[1]);
|
||||
}
|
||||
else if (line.startsWith('date2')) {
|
||||
date2 = new Date(line.split(':')[1]);
|
||||
}
|
||||
else if (line.startsWith('result')) {
|
||||
if (date1 !== null && date2 !== null) {
|
||||
result = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate(),
|
||||
date2.getHours(), date2.getMinutes(), date2.getSeconds());
|
||||
|
||||
console.log(result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('Bad command');
|
||||
}
|
||||
});
|
||||
|
||||
r1.once('close', () => {
|
||||
console.log('Closing');
|
||||
});
|
@ -1,15 +1,43 @@
|
||||
const { REST, Routes } = require('discord.js');
|
||||
const { clientId, guildId, token } = require('./config.json');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const getAllFiles = function(directoryPath, arrayOfFiles) {
|
||||
const files = fs.readdirSync(directoryPath);
|
||||
|
||||
arrayOfFiles = arrayOfFiles || [];
|
||||
|
||||
files.forEach(file => {
|
||||
if (fs.statSync(directoryPath + path.sep + file).isDirectory()) {
|
||||
arrayOfFiles = getAllFiles(directoryPath + path.sep + file, arrayOfFiles);
|
||||
}
|
||||
else {
|
||||
arrayOfFiles.push(path.join(__dirname, directoryPath, path.sep, file));
|
||||
}
|
||||
});
|
||||
|
||||
return arrayOfFiles;
|
||||
};
|
||||
|
||||
const commands = [];
|
||||
const allFiles = [];
|
||||
getAllFiles('.' + path.sep + 'commands', allFiles);
|
||||
allFiles.forEach(file => {
|
||||
console.log(file);
|
||||
});
|
||||
// Grab all the command files from the commands directory you created earlier
|
||||
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
|
||||
const commandFiles = allFiles.filter(file => file.endsWith('.js'));
|
||||
|
||||
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./commands/${file}`);
|
||||
const command = require(`${file}`);
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.push(command.data.toJSON());
|
||||
}
|
||||
else {
|
||||
console.log(`Skipping ${file} as it is missing one or more required exported fields`);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct and prepare an instance of the REST module
|
||||
|
11
package-lock.json
generated
11
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@vvo/tzdb": "^6.77.0",
|
||||
"discord.js": "^14.6.0",
|
||||
"googleapis": "^109.0.1"
|
||||
},
|
||||
@ -206,6 +207,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vvo/tzdb": {
|
||||
"version": "6.77.0",
|
||||
"resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.77.0.tgz",
|
||||
"integrity": "sha512-t7aN3GAznzt8fQ5enJiM3C7HKPEDBoqKExp2W7nYu2AgS0J9FfMk6rwWhL2jjTe0+27REmO9C+TL3XM2evileQ=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
||||
@ -1975,6 +1981,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@vvo/tzdb": {
|
||||
"version": "6.77.0",
|
||||
"resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.77.0.tgz",
|
||||
"integrity": "sha512-t7aN3GAznzt8fQ5enJiM3C7HKPEDBoqKExp2W7nYu2AgS0J9FfMk6rwWhL2jjTe0+27REmO9C+TL3XM2evileQ=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
|
||||
|
@ -9,6 +9,7 @@
|
||||
"author": "Bradley Bickford",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@vvo/tzdb": "^6.77.0",
|
||||
"discord.js": "^14.6.0",
|
||||
"googleapis": "^109.0.1"
|
||||
},
|
||||
|
122
utilities/googlecalendar.js
Normal file
122
utilities/googlecalendar.js
Normal file
@ -0,0 +1,122 @@
|
||||
const { google } = require('googleapis');
|
||||
const { googlePrivateKey, googleClientEmail, googleProjectNumber } = require('../config.json');
|
||||
const { stdout } = require('node:process');
|
||||
const SCOPES = ['https://www.googleapis.com/auth/calendar'];
|
||||
|
||||
async function getCalendarReference() {
|
||||
const jwtClient = new google.auth.JWT(
|
||||
googleClientEmail,
|
||||
'../keyfile.json',
|
||||
googlePrivateKey,
|
||||
SCOPES,
|
||||
);
|
||||
|
||||
return new google.calendar({
|
||||
version: 'v3',
|
||||
project: googleProjectNumber,
|
||||
auth: jwtClient,
|
||||
});
|
||||
}
|
||||
|
||||
async function doesCalendarExist(calendarName) {
|
||||
const calendarReference = await getCalendarReference();
|
||||
const listResults = await calendarReference.calendarList.list({});
|
||||
|
||||
for (const item of listResults.data.items) {
|
||||
if (item.summary === calendarName) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getListOfCalendars(options, callback) {
|
||||
const calendarReference = await getCalendarReference();
|
||||
calendarReference.calendarList.list(options, async (err, res) => {
|
||||
if (err) {
|
||||
callback(false, 'Failed to retrieve the list of calendars\nAsk Bradley to check Breadbot console');
|
||||
stdout.write('[ERROR]:');
|
||||
console.log(err.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(true, 'Calendar List', res.data.items);
|
||||
});
|
||||
}
|
||||
|
||||
async function addCalendar(calendarName, timezone, callback) {
|
||||
const calendarReference = await getCalendarReference();
|
||||
calendarReference.calendars.insert({
|
||||
resource: {
|
||||
summary: calendarName,
|
||||
timeZone: timezone,
|
||||
},
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async (err, res) => {
|
||||
if (err) {
|
||||
callback(false, 'Failed to create new calendar ' + calendarName + '\nAsk Bradley to check Breadbat console', err);
|
||||
stdout.write('[ERROR]: ');
|
||||
console.log(err.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(true, 'Successfully created new calendar ' + calendarName, null);
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteCalendar(calendarName, callback) {
|
||||
const exists = await doesCalendarExist(calendarName);
|
||||
|
||||
if (exists) {
|
||||
const calendarReference = await getCalendarReference();
|
||||
calendarReference.calendars.delete({
|
||||
calendarId: exists.id,
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
async (err, res) => {
|
||||
if (err) {
|
||||
callback(false, 'Failed to delete ' + calendarName + '\nAsk Bradley to check Breadbot console', err);
|
||||
stdout.write('[ERROR]: ');
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(true, 'Successfully deleted ' + calendarName, null);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback(false, 'The calendar name specified doesn\'t exist', null);
|
||||
}
|
||||
}
|
||||
|
||||
async function addEvent(calendarName, eventName, location, description, startDate, startTime, endDate, endTime) {
|
||||
const exists = await doesCalendarExist(calendarName);
|
||||
|
||||
if (exists) {
|
||||
const calendarReference = await getCalendarReference();
|
||||
calendarReference.events.insert({
|
||||
calendarId: exists.id,
|
||||
resource: {
|
||||
summary: eventName,
|
||||
location: location,
|
||||
description: description,
|
||||
start: {
|
||||
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
else {
|
||||
callback(false, 'The calendar name specified doesn\'t exist', null);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCalendarReference,
|
||||
getListOfCalendars,
|
||||
doesCalendarExist,
|
||||
deleteCalendar,
|
||||
addCalendar,
|
||||
};
|
Loading…
Reference in New Issue
Block a user