add code from codecat
This commit is contained in:
parent
f0607b82e7
commit
0b8f9210d3
51 changed files with 3639 additions and 0 deletions
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
|||
FROM codecatt/reddit-radio:base
|
||||
|
||||
ENV CONFIG_FILE="./config/config.toml"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . /app
|
||||
|
||||
CMD ["node", "index.js"]
|
18
Dockerfile.base
Normal file
18
Dockerfile.base
Normal file
|
@ -0,0 +1,18 @@
|
|||
FROM node:16-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./package.json /app/package.json
|
||||
|
||||
RUN apk add --no-cache ffmpeg \
|
||||
&& apk add --no-cache --virtual .build-deps \
|
||||
g++ \
|
||||
gcc \
|
||||
libgcc \
|
||||
make \
|
||||
autoconf \
|
||||
libtool \
|
||||
automake \
|
||||
python3 \
|
||||
&& npm install \
|
||||
&& apk del .build-deps
|
118
Radio.js
Normal file
118
Radio.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
var discord = require("discord.js");
|
||||
|
||||
class Radio
|
||||
{
|
||||
constructor(config, radioconfig)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
this.name = radioconfig.name;
|
||||
this.url = radioconfig.url;
|
||||
|
||||
this.client = new discord.Client({
|
||||
intents: [
|
||||
// List of intents: https://discord.com/developers/docs/topics/gateway#list-of-intents
|
||||
discord.Intents.FLAGS.GUILDS,
|
||||
discord.Intents.FLAGS.GUILD_VOICE_STATES,
|
||||
],
|
||||
});
|
||||
|
||||
this.running = false;
|
||||
this.voice_connection = false;
|
||||
this.voice_dispatcher = false;
|
||||
|
||||
this.channel = false;
|
||||
|
||||
this.client.on("ready", () => {
|
||||
console.log("Radio client \"" + this.name + "\" connected!");
|
||||
|
||||
this.client.channels.fetch(radioconfig.channel).then(channel => {
|
||||
this.channel = channel;
|
||||
if (channel.members.size > 0) {
|
||||
this.joinChannel();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.client.on("error", (e) => {
|
||||
console.log("Radio bot error:", e);
|
||||
});
|
||||
|
||||
this.client.on("voiceStateUpdate", (o, n) => {
|
||||
if (n.channel == this.channel) {
|
||||
console.log("Someone joined \"" + this.name + "\": " + this.channel.members.size);
|
||||
if (!this.running) {
|
||||
this.running = true;
|
||||
this.joinChannel();
|
||||
}
|
||||
} else if (o.channel == this.channel && n.channel != this.channel) {
|
||||
console.log("Someone left \"" + this.name + "\": " + this.channel.members.size);
|
||||
if (this.running && this.channel.members.size == 1) {
|
||||
this.running = false;
|
||||
this.leaveChannel();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.client.login(radioconfig.token);
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
console.log("Stopping radio \"" + this.name + "\"...");
|
||||
return this.client.destroy();
|
||||
}
|
||||
|
||||
joinChannel()
|
||||
{
|
||||
console.log("Joining and starting \"" + this.name + "\"!");
|
||||
this.running = true;
|
||||
|
||||
this.channel.join().then((conn) => {
|
||||
this.voice_connection = conn;
|
||||
this.voice_connection.on("disconnect", () => {
|
||||
this.voice_connection = false;
|
||||
});
|
||||
this.voice_connection.on("newSession", () => {
|
||||
this.startBroadcast();
|
||||
});
|
||||
this.voice_connection.on("error", (err) => {
|
||||
console.log("Error: " + err);
|
||||
});
|
||||
this.startBroadcast();
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
leaveChannel()
|
||||
{
|
||||
console.log("Leaving \"" + this.name + "\".");
|
||||
this.running = false;
|
||||
|
||||
if (this.voice_dispatcher !== false) {
|
||||
this.voice_dispatcher.end();
|
||||
}
|
||||
|
||||
if (this.voice_connection !== false) {
|
||||
this.voice_connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
startBroadcast()
|
||||
{
|
||||
if (this.voice_connection === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.voice_dispatcher !== false) {
|
||||
this.voice_dispatcher.end();
|
||||
}
|
||||
|
||||
this.voice_dispatcher = this.voice_connection.play(this.url, this.config.voice);
|
||||
this.voice_dispatcher.on("end", (reason) => {
|
||||
console.log("Radio voice \"" + this.name + "\" ended: \"" + reason + "\"");
|
||||
this.voice_dispatcher = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Radio;
|
445
RedditRadio.js
Normal file
445
RedditRadio.js
Normal file
|
@ -0,0 +1,445 @@
|
|||
var discord = require("discord.js");
|
||||
var colors = require("colors");
|
||||
var moment = require("moment-timezone");
|
||||
|
||||
var process = require("process");
|
||||
var fs = require("fs");
|
||||
var https = require("https");
|
||||
|
||||
var cmdsplit = require("./cmdsplit");
|
||||
var Radio = require("./Radio");
|
||||
var MongoClient = require("mongodb").MongoClient;
|
||||
|
||||
function findCommand(obj, cmdID)
|
||||
{
|
||||
var cmdName = "onCmd" + cmdID;
|
||||
var cmdRegex = new RegExp("^" + cmdName + "$", "i");
|
||||
|
||||
var prototype = Object.getPrototypeOf(obj);
|
||||
var props = Object.getOwnPropertyNames(prototype);
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var key = props[i];
|
||||
if (!key.startsWith("onCmd")) {
|
||||
continue;
|
||||
}
|
||||
if (key.match(cmdRegex)) {
|
||||
return obj[key];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class RedditRadio
|
||||
{
|
||||
constructor(config)
|
||||
{
|
||||
this.config = config;
|
||||
this.readyPromises = [];
|
||||
|
||||
moment.tz.setDefault(this.config.discord.timezone || "Europe/Amsterdam");
|
||||
|
||||
console.log('Discord.js version', discord.version);
|
||||
|
||||
this.client = new discord.Client({
|
||||
intents: [
|
||||
// List of intents: https://discord.com/developers/docs/topics/gateway#list-of-intents
|
||||
discord.Intents.FLAGS.GUILDS,
|
||||
discord.Intents.FLAGS.GUILD_MEMBERS,
|
||||
discord.Intents.FLAGS.GUILD_BANS,
|
||||
discord.Intents.FLAGS.GUILD_MESSAGES,
|
||||
discord.Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
|
||||
discord.Intents.FLAGS.DIRECT_MESSAGES,
|
||||
],
|
||||
});
|
||||
this.client.on("messageCreate", (msg) => { this.onMessage(msg, false); });
|
||||
this.client.on("messageUpdate", (oldMsg, newMsg) => { this.onMessageUpdate(oldMsg, newMsg); });
|
||||
this.client.on("messageDelete", (msg) => { this.onMessageDelete(msg); });
|
||||
this.client.on("guildMemberAdd", (member) => { this.onMemberJoin(member); });
|
||||
this.readyPromises.push(this.client.login(this.config.discord.token));
|
||||
|
||||
this.modules = [];
|
||||
|
||||
if (this.config.database) {
|
||||
this.mongoclient = new MongoClient(this.config.database.url, { useUnifiedTopology: true });
|
||||
this.readyPromises.push(this.mongoclient.connect());
|
||||
}
|
||||
|
||||
/** @type {discord.TextChannel} */
|
||||
this.logChannel = null;
|
||||
/** @type {discord.TextChannel} */
|
||||
this.dmChannel = null;
|
||||
/** @type {discord.TextChannel} */
|
||||
this.errorChannel = null;
|
||||
}
|
||||
|
||||
loadConfigModules()
|
||||
{
|
||||
if (!this.config.modules) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var name in this.config.modules) {
|
||||
var moduleClass = require('./modules/' + name);
|
||||
if (!moduleClass) {
|
||||
console.error('Unable to find module with name "' + name + '"!');
|
||||
continue;
|
||||
}
|
||||
|
||||
var configs = this.config.modules[name];
|
||||
|
||||
console.log('Module: "' + name + '" (' + configs.length + ' instances)');
|
||||
|
||||
for (let config of configs) {
|
||||
var newModule = new moduleClass(config, this.client, this);
|
||||
this.modules.push(newModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReady()
|
||||
{
|
||||
/*
|
||||
this.client.guilds.cache.tap(guild => {
|
||||
guild.members.fetch().then(() => {
|
||||
console.log('Cached ' + guild.members.size + ' members in ' + guild.name);
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
this.client.user.setActivity(this.config.discord.activity);
|
||||
|
||||
this.client.channels.fetch(this.config.discord.logchannel).then(logChannel => this.logChannel = logChannel);
|
||||
this.client.channels.fetch(this.config.discord.dmchannel).then(dmChannel => this.dmChannel = dmChannel);
|
||||
this.client.channels.fetch(this.config.discord.errorchannel).then(errorChannel => this.errorChannel = errorChannel);
|
||||
|
||||
if (this.mongoclient) {
|
||||
this.mongodb = this.mongoclient.db(this.config.database.db);
|
||||
console.log("Database connected.");
|
||||
}
|
||||
|
||||
console.log("Client ready, loading modules...");
|
||||
|
||||
this.loadConfigModules();
|
||||
|
||||
console.log("Modules loaded!");
|
||||
|
||||
setInterval(() => { this.onTick(); }, 1000);
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
Promise.all(this.readyPromises).then(() => {
|
||||
this.onReady();
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
var promises = [];
|
||||
|
||||
console.log("Stopping client...");
|
||||
promises.push(this.client.destroy());
|
||||
|
||||
if (this.mongoclient) {
|
||||
console.log("Stopping MongoDB...");
|
||||
promises.push(this.mongoclient.close());
|
||||
}
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
console.log("Client stopped.");
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
addLogMessage(text, fromMember)
|
||||
{
|
||||
if (!this.logChannel) {
|
||||
console.log("Couldn't log because we couldn't find the log channel:", text);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromMember) {
|
||||
text += " (via " + fromMember.user.username + ")";
|
||||
}
|
||||
|
||||
console.log("Log: " + text);
|
||||
this.logChannel.send(":robot: " + text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given member is an admin.
|
||||
* @param {discord.GuildMember} member
|
||||
*/
|
||||
isAdmin(member)
|
||||
{
|
||||
return member.permissions.has(discord.Permissions.FLAGS.ADMINISTRATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given member is a moderator.
|
||||
* @param {discord.GuildMember} member
|
||||
*/
|
||||
isMod(member)
|
||||
{
|
||||
return member.permissions.has(discord.Permissions.FLAGS.MANAGE_MESSAGES);
|
||||
}
|
||||
|
||||
onTick()
|
||||
{
|
||||
for (var i = 0; i < this.modules.length; i++) {
|
||||
var m = this.modules[i];
|
||||
if (m.onTick) {
|
||||
m.onTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMemberJoin(member)
|
||||
{
|
||||
console.log("User joined: " + member + " (" + member.user.username + ")");
|
||||
|
||||
for (var i = 0; i < this.modules.length; i++) {
|
||||
var m = this.modules[i];
|
||||
if (m.onMemberJoin) {
|
||||
m.onMemberJoin(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleError(ex)
|
||||
{
|
||||
console.log(ex);
|
||||
if (this.errorChannel) {
|
||||
this.errorChannel.send(":octagonal_sign: Bot error!\n```\n" + ex.toString() + "\n```");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
async onMessage(msg, edited)
|
||||
{
|
||||
// Ignore our own messages
|
||||
if (msg.author == this.client.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore DM's
|
||||
if (msg.member === null && msg.guild === null) {
|
||||
// Log DM's
|
||||
var logUsername = msg.author.username + '#' + msg.author.discriminator;
|
||||
console.warn("Ignored a DM from " + logUsername.brightWhite + ": \"" + msg.content + "\"");
|
||||
|
||||
// Send DM's to the DM channel
|
||||
if (this.dmChannel) {
|
||||
this.dmChannel.send(":mailbox_with_mail: " + msg.author.toString() + ": `" + msg.content + "`");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore webhooks
|
||||
if (msg.webhookID) {
|
||||
console.warn("Ignored webhook: \"" + msg.content + "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we have a member (sometimes this is null if their status is offline)
|
||||
if (msg.member === null) {
|
||||
console.warn("Member is null, fetching member now");
|
||||
msg.member = await msg.guild.fetchMember(msg.author);
|
||||
}
|
||||
|
||||
// Log line
|
||||
var logUsername = msg.author.username + '#' + msg.author.discriminator;
|
||||
if (this.isAdmin(msg.member)) {
|
||||
logUsername = logUsername.red;
|
||||
} else if (this.isMod(msg.member)) {
|
||||
logUsername = logUsername.yellow;
|
||||
} else {
|
||||
logUsername = logUsername.brightWhite;
|
||||
}
|
||||
|
||||
console.log('[' + moment().format('MMM Do LTS') + '] '
|
||||
+ logUsername
|
||||
+ ' in ' + ('#' + msg.channel.name).green.underline + ': '
|
||||
+ (edited ? '(edited) '.gray : '')
|
||||
+ '"' + msg.content + '"');
|
||||
|
||||
for (var i = 0; i < this.modules.length; i++) {
|
||||
var m = this.modules[i];
|
||||
if (m.onMessage) {
|
||||
try {
|
||||
if (m.onMessage(msg, edited)) {
|
||||
return;
|
||||
}
|
||||
} catch (ex) { this.handleError(ex); }
|
||||
}
|
||||
}
|
||||
|
||||
if (!msg.content.startsWith(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parse = cmdsplit(msg.content);
|
||||
var cmdID = parse[0].slice(1);
|
||||
cmdID = cmdID.charAt(0).toUpperCase() + cmdID.slice(1);
|
||||
if (!cmdID.match(/^[a-z]+$/i)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cmdFound = false;
|
||||
|
||||
var cmdFunc = findCommand(this, cmdID);
|
||||
if (cmdFunc) {
|
||||
if (msg.member !== null) {
|
||||
console.log("Built-in command from \"" + msg.member.user.username + "\": " + cmdID);
|
||||
} else {
|
||||
console.log("Module command from offline member: " + cmdID);
|
||||
}
|
||||
|
||||
try {
|
||||
var r = cmdFunc.apply(this, [ msg ].concat(parse.slice(1)));
|
||||
if (r && r.catch) {
|
||||
r.catch(ex => this.handleError(ex));
|
||||
}
|
||||
} catch (ex) { this.handleError(ex); }
|
||||
cmdFound = true;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.modules.length; i++) {
|
||||
var m = this.modules[i];
|
||||
|
||||
var cmdFunc = findCommand(m, cmdID);
|
||||
if (!cmdFunc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msg.member !== null) {
|
||||
console.log("Module command from \"" + msg.member.user.username + "\": " + cmdID);
|
||||
} else {
|
||||
console.log("Module command from offline member: " + cmdID);
|
||||
}
|
||||
|
||||
try {
|
||||
var r = cmdFunc.apply(m, [ msg ].concat(parse.slice(1)));
|
||||
if (r && r.catch) {
|
||||
r.catch(ex => this.handleError(ex));
|
||||
}
|
||||
} catch (ex) { this.handleError(ex); }
|
||||
cmdFound = true;
|
||||
}
|
||||
|
||||
if (!cmdFound) {
|
||||
console.log("Unknown command: \"" + cmdID + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
async onMessageUpdate(oldMsg, newMsg)
|
||||
{
|
||||
if (oldMsg.content != newMsg.content) {
|
||||
this.onMessage(newMsg, true);
|
||||
}
|
||||
}
|
||||
|
||||
async onMessageDelete(msg)
|
||||
{
|
||||
console.log("Message deleted: \"" + msg.content + "\"");
|
||||
}
|
||||
|
||||
onCmdGithub(msg)
|
||||
{
|
||||
msg.channel.send("My code is on Github! :robot: https://github.com/codecat/reddit-radio");
|
||||
}
|
||||
|
||||
/*
|
||||
//TODO: Move this to a module
|
||||
onCmdWeather(msg)
|
||||
{
|
||||
var url = "https://api.darksky.net/forecast/" + this.config.weather.apikey + "/" + this.config.weather.coords + "?units=auto";
|
||||
https.get(url, (res) => {
|
||||
var data = "";
|
||||
res.setEncoding("utf8");
|
||||
res.on("data", function(chunk) { data += chunk; });
|
||||
res.on("end", () => {
|
||||
try {
|
||||
var obj = JSON.parse(data);
|
||||
var ret = "**The weather at Defqon.1 is currently:** (powered by darksky.net)\n";
|
||||
ret += "*" + obj.currently.summary + "* / **" + obj.currently.temperature + "\u2103 (" + Math.round((obj.currently.temperature * 9/5) + 32) + "\u2109)** / " + Math.round(obj.currently.humidity * 100) + "% humidity\n";
|
||||
ret += "UV index " + obj.currently.uvIndex + ", wind speed " + obj.currently.windSpeed + " m/s";
|
||||
msg.channel.send(ret);
|
||||
} catch (err) {
|
||||
msg.channel.send("I failed to get the weather... :sob:");
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
//TODO: Change this to .timeout and move this to its own module
|
||||
/*
|
||||
onCmdMute(msg)
|
||||
{
|
||||
if (!this.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var memberID of msg.mentions.members.keys()) {
|
||||
var member = msg.mentions.members.get(memberID);
|
||||
member.roles.remove(this.config.discord.mutedrole);
|
||||
|
||||
this.addLogMessage("Muted " + member.user.username, msg.member);
|
||||
}
|
||||
|
||||
msg.delete();
|
||||
}
|
||||
|
||||
onCmdUnmute(msg)
|
||||
{
|
||||
if (!this.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var memberID of msg.mentions.members.keys()) {
|
||||
var member = msg.mentions.members.get(memberID);
|
||||
member.roles.add(this.config.discord.mutedrole);
|
||||
|
||||
this.addLogMessage("Unmuted " + member.user.username, msg.member);
|
||||
}
|
||||
|
||||
msg.delete();
|
||||
}
|
||||
*/
|
||||
|
||||
formatMilliseconds(ms)
|
||||
{
|
||||
var sec = Math.floor(ms / 1000);
|
||||
|
||||
var secs = sec % 60;
|
||||
var mins = Math.floor(sec / 60) % 60;
|
||||
var hours = Math.floor(sec / 60 / 60) % 60;
|
||||
|
||||
var ret = "";
|
||||
if (hours > 0) {
|
||||
ret += hours + "h";
|
||||
}
|
||||
if (mins > 0) {
|
||||
ret += mins + "m";
|
||||
}
|
||||
ret += secs + "s";
|
||||
return ret;
|
||||
}
|
||||
|
||||
onCmdTime(msg)
|
||||
{
|
||||
var date = moment();
|
||||
var text = "The local time is: **" + date.format("HH:mm") + "** (<https://time.is/CET>)";
|
||||
msg.channel.send(text);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RedditRadio;
|
101
Startup.js
Normal file
101
Startup.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
var RedditRadio = require("./RedditRadio");
|
||||
var toml = require("toml");
|
||||
var fs = require("fs");
|
||||
var Radio = require("./Radio");
|
||||
|
||||
class Startup
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.RADIO = 'radio';
|
||||
this.RADIOS = 'radios';
|
||||
this.NO_RADIO = 'no-radio';
|
||||
this.REGULAR_BOT = 'regular-bot';
|
||||
|
||||
this.radios = [];
|
||||
|
||||
let configFile = process.env.CONFIG_FILE || "config.toml";
|
||||
this.config = toml.parse(fs.readFileSync(configFile, "utf8"));
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
let args = this.parseArgs();
|
||||
switch (args.type) {
|
||||
case this.RADIO: this.setupRadio(args.name); break;
|
||||
case this.RADIOS: this.setupRadios(); break;
|
||||
case this.NO_RADIO: this.setupNoRadio(); break;
|
||||
default: this.startBot(this.config); break;
|
||||
}
|
||||
}
|
||||
|
||||
setupNoRadio()
|
||||
{
|
||||
let config = this.config;
|
||||
delete config.radios;
|
||||
this.startBot(config);
|
||||
}
|
||||
|
||||
setupRadio(name)
|
||||
{
|
||||
if (this.config.radios === undefined) {
|
||||
console.error('No radios are defined in the config file, exiting...');
|
||||
process.exit(1);
|
||||
}
|
||||
for (let i = 0; i < this.config.radios.length; i++) {
|
||||
if (this.config.radios[i].name === name) {
|
||||
this.startRadio(this.config.radios[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setupSignals();
|
||||
}
|
||||
|
||||
setupRadios()
|
||||
{
|
||||
for (var i = 0; i < this.config.radios.length; i++) {
|
||||
this.startRadio(this.config.radios[i]);
|
||||
}
|
||||
this.setupSignals();
|
||||
}
|
||||
|
||||
startRadio(radioConfig)
|
||||
{
|
||||
this.radios.push(new Radio(this.config, radioConfig));
|
||||
}
|
||||
|
||||
startBot(config)
|
||||
{
|
||||
this.bot = new RedditRadio(config);
|
||||
this.bot.start();
|
||||
|
||||
this.setupSignals();
|
||||
}
|
||||
|
||||
setupSignals()
|
||||
{
|
||||
var stopHandler = () => {
|
||||
if (this.bot) {
|
||||
this.bot.stop();
|
||||
}
|
||||
for (var i = 0; i < this.radios.length; i++) {
|
||||
this.radios[i].stop();
|
||||
}
|
||||
};
|
||||
process.on("SIGINT", stopHandler); // Ctrl+C
|
||||
process.on("SIGTERM", stopHandler); // Terminate
|
||||
}
|
||||
|
||||
parseArgs()
|
||||
{
|
||||
let args = process.argv.splice(2);
|
||||
switch (args[0]) {
|
||||
case '--radio': return { type: this.RADIO, name: args[1]};
|
||||
case '--radios': return { type: this.RADIOS };
|
||||
case '--no-radios': return { type: this.NO_RADIO };
|
||||
default: return { type: this.REGULAR_BOT };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Startup;
|
54
cmdsplit.js
Normal file
54
cmdsplit.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
module.exports = function(str, options) {
|
||||
var ret = [];
|
||||
|
||||
var buffer = "";
|
||||
var inString = false;
|
||||
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str[i];
|
||||
|
||||
// literals
|
||||
if (c == "\\" && i + 1 < str.length) {
|
||||
buffer += str[++i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// strings
|
||||
if (c == "\"") {
|
||||
if (inString) {
|
||||
// string ends
|
||||
inString = false;
|
||||
ret.push(buffer);
|
||||
buffer = "";
|
||||
if (i + 1 < str.length && str[i + 1] == " ") {
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
// string starts
|
||||
inString = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// words
|
||||
if (c == " ") {
|
||||
if (inString) {
|
||||
buffer += " ";
|
||||
} else {
|
||||
ret.push(buffer);
|
||||
buffer = "";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// characters
|
||||
buffer += c;
|
||||
}
|
||||
|
||||
// last word
|
||||
if (buffer != "") {
|
||||
ret.push(buffer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
30
config.example.toml
Normal file
30
config.example.toml
Normal file
|
@ -0,0 +1,30 @@
|
|||
[discord]
|
||||
token = "main_bot_token"
|
||||
timezone = "Europe/Amsterdam"
|
||||
logchannel = "647767937054277642"
|
||||
dmchannel = "736529983706628146"
|
||||
|
||||
#[database]
|
||||
#url = "mongodb://localhost:27017"
|
||||
#db = "redditradio"
|
||||
|
||||
[filter]
|
||||
badwords = "(thepiratebay|badwords)"
|
||||
|
||||
[[modules.qdance]]
|
||||
|
||||
#[[modules.event]]
|
||||
#file = "events/Qlimax2019.json"
|
||||
|
||||
#[[modules.eventquick]]
|
||||
#channel = "651911168650248208"
|
||||
|
||||
[[radios]]
|
||||
token = "radio_bot_1_token"
|
||||
channel = "330121740758024193"
|
||||
name = "Q-dance Radio"
|
||||
url = "http://audio.true.nl/qdance-hard"
|
||||
|
||||
[voice]
|
||||
passes = 2
|
||||
bitrate = 44100
|
62
events/Corona_2020_03_14.json
Normal file
62
events/Corona_2020_03_14.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
[
|
||||
{
|
||||
"stage": "I AM HARDSTYLE - Born & Raised",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":warning:",
|
||||
"url": "https://www.youtube.com/watch?v=Y9KxiNOKaCI",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 0,
|
||||
"sets": [
|
||||
[ 2020, 3, 14, 20, 0, "Toneshifterz" ],
|
||||
[ 2020, 3, 14, 21, 0, "Brennan Heart" ],
|
||||
[ 2020, 3, 14, 22, 0, "Code Black" ],
|
||||
[ 2020, 3, 14, 23, 0, "Brennan Heart, Code Black, Dailucia & Toneshifterz" ],
|
||||
[ 2020, 3, 15, 0, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.youtube.com/watch?v=Y9KxiNOKaCI>**",
|
||||
"^\\.(corona)$": "Due to many hardstyle events being canceled due to the COVID-19 pandemic, several DJ's are setting up their own livestream events. For all Corona/Event related news: <https://redd.it/fh1kvr>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"stage": "Quarantine Hardstyle Sessions",
|
||||
"channel": "688383827407667272",
|
||||
"emoji": ":wolf:",
|
||||
"url": "https://www.facebook.com/Qdance/videos/518799009013830/",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 0,
|
||||
"sets": [
|
||||
[ 2020, 3, 14, 22, 0, "Early Hardstyle by Jones" ],
|
||||
[ 2020, 3, 14, 22, 30, "Frequencerz" ],
|
||||
[ 2020, 3, 14, 23, 0, "Ran-D & Adaro" ],
|
||||
[ 2020, 3, 14, 23, 30, "Devin Wild" ],
|
||||
[ 2020, 3, 15, 0, 0, "B-Front" ],
|
||||
[ 2020, 3, 15, 0, 30, "Ending" ],
|
||||
[ 2020, 3, 15, 1, 35 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.facebook.com/Qdance/videos/518799009013830/>**",
|
||||
"^\\.(mc|host)$": ":microphone: Hosted by **MC Da Syndrone**.",
|
||||
"^\\.(corona)$": "Due to many hardstyle events being canceled due to the COVID-19 pandemic, several DJ's are setting up their own livestream events. For all Corona/Event related news: <https://redd.it/fh1kvr>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"stage": "United We Stand",
|
||||
"channel": "688470315579605189",
|
||||
"emoji": "<:frontliner:376092032361299979>",
|
||||
"url": "https://www.youtube.com/watch?v=6B8OU5aQR_4",
|
||||
"unconfirmed": true,
|
||||
"streamdelay": 0,
|
||||
"sets": [
|
||||
[ 2020, 3, 14, 21, 0, "Blasterjaxx" ],
|
||||
[ 2020, 3, 14, 22, 0, "Frontliner" ],
|
||||
[ 2020, 3, 14, 22, 45, "Blasterjaxx & Frontliner" ],
|
||||
[ 2020, 3, 14, 23, 15, "Mr. Polska" ],
|
||||
[ 2020, 3, 14, 23, 30 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.youtube.com/watch?v=6B8OU5aQR_4>**",
|
||||
"^\\.(corona)$": "Due to many hardstyle events being canceled due to the COVID-19 pandemic, several DJ's are setting up their own livestream events. For all Corona/Event related news: <https://redd.it/fh1kvr>"
|
||||
}
|
||||
}
|
||||
]
|
27
events/Corona_2020_03_20.json
Normal file
27
events/Corona_2020_03_20.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
[
|
||||
{
|
||||
"stage": "Quarantine Hardstyle Sessions",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":wolf:",
|
||||
"url": "https://www.youtube.com/watch?v=tA6ZsLKm_SU",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 0,
|
||||
"sets": [
|
||||
[ 2020, 3, 20, 19, 0, "Unrivalled Podcast ft. Delius" ],
|
||||
[ 2020, 3, 20, 20, 0, "Frequencerz (Hardstyle Classics)" ],
|
||||
[ 2020, 3, 20, 20, 30, "B-Freqz" ],
|
||||
[ 2020, 3, 20, 21, 0, "Rejecta" ],
|
||||
[ 2020, 3, 20, 21, 30, "Radianze" ],
|
||||
[ 2020, 3, 20, 22, 0, "Digital Punk" ],
|
||||
[ 2020, 3, 20, 22, 30, "D-Fence" ],
|
||||
[ 2020, 3, 20, 23, 0, "Ending" ],
|
||||
[ 2020, 3, 21, 0, 0, "Feels birthday man" ],
|
||||
[ 2020, 3, 21, 0, 17 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.youtube.com/watch?v=tA6ZsLKm_SU>**",
|
||||
"^\\.(mc|host)$": ":microphone: Hosted by **MC Da Syndrome**.",
|
||||
"^\\.(corona)$": "Due to many hardstyle events being canceled due to the COVID-19 pandemic, several DJ's are setting up their own livestream events. For all Corona/Event related news: <https://redd.it/fh1kvr>"
|
||||
}
|
||||
}
|
||||
]
|
24
events/Corona_2020_04_12.json
Normal file
24
events/Corona_2020_04_12.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
[
|
||||
{
|
||||
"stage": "I AM HARDSTYLE",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":warning:",
|
||||
"url": "https://www.youtube.com/watch?v=0jWt1ZQd7Gg",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 0,
|
||||
"sets": [
|
||||
[ 2020, 4, 12, 19, 0, "Code Black" ],
|
||||
[ 2020, 4, 12, 19, 45, "Dailucia" ],
|
||||
[ 2020, 4, 12, 20, 15, "Aftershock" ],
|
||||
[ 2020, 4, 12, 20, 45, "Toneshifterz" ],
|
||||
[ 2020, 4, 12, 21, 15, "Brennan Heart" ],
|
||||
[ 2020, 4, 12, 22, 15, "Radical Redemption & MC Nolz" ],
|
||||
[ 2020, 4, 12, 23, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.youtube.com/watch?v=0jWt1ZQd7Gg>**",
|
||||
"^\\.(corona)$": "Due to many hardstyle events being canceled due to the COVID-19 pandemic, several DJ's are setting up their own livestream events. For all Corona/Event related news: <https://redd.it/fh1kvr>",
|
||||
"^\\.(ultrawide)$": "For the ultrawide viewers, this userscript is nice: <https://greasyfork.org/en/scripts/396195-video-zooming-buttons>"
|
||||
}
|
||||
}
|
||||
]
|
37
events/Dediqated2020.json
Normal file
37
events/Dediqated2020.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:qdance:328585093553586176>",
|
||||
"url": "https://www.q-dance.com/en/videos/dediqated-live",
|
||||
"unconfirmed": true,
|
||||
"streamdelay": 3,
|
||||
"sets": [
|
||||
[ 2020, 2, 8, 12, 50, "Early Rave Rebels", "Buzz Fuzz, Franky Jones, Gizmo, The Darkraver" ],
|
||||
[ 2020, 2, 8, 13, 13, "The Qlubtempo Parade", "ASYS, DJ Luna, Dana, Pavo, Pila" ],
|
||||
[ 2020, 2, 8, 13, 41, "Fusion Records", "Donkey Rollers, The Pitcher, Zany" ],
|
||||
[ 2020, 2, 8, 13, 58, "Bella Hardstyle Italia", "Tatanka, TNT, Zatox" ],
|
||||
[ 2020, 2, 8, 14, 30, "Masters of Melody Part 1", "Bass Modulators, Frontliner, Max Enforcer" ],
|
||||
[ 2020, 2, 8, 15, 0, "Praise the Reverse Bass", "DJ Isaac, Technoboy, Tuneboy" ],
|
||||
[ 2020, 2, 8, 15, 20, "Viva Hollandia", "Deepack, Dr. Rude, Outsiders, Ruthless, The Viper" ],
|
||||
[ 2020, 2, 8, 15, 58, "World of Madness", "Headhunterz, Noisecontrollers, Wildstylez" ],
|
||||
[ 2020, 2, 8, 16, 44, "Millenium Hardcore Mayhem", "Art of Fighters, Endymion, Evil Activities, Noize Supperssor, Nosferatu, Promo" ],
|
||||
[ 2020, 2, 8, 17, 16, "Scantraxx Recordz", "D-Block & S-te-Fan, Devin Wild, The Prophet" ],
|
||||
[ 2020, 2, 8, 17, 37, "The Land Down Under", "Audiofreq, Code Black, Outbreak" ],
|
||||
[ 2020, 2, 8, 17, 52, "The Best of Power Hour", "Neophyte, The Stunned Guys, Zazafront and many more" ],
|
||||
[ 2020, 2, 8, 18, 53, "Dirty Workz", "Coone, Da Tweekaz, Hard Driver, Psyko Punkz" ],
|
||||
[ 2020, 2, 8, 19, 20, "Birth of Raw", "Adaro, Alpha Twins, B-Front, Crypsis, Digital Punk, E-Force, Ran-D" ],
|
||||
[ 2020, 2, 8, 20, 4, "Masters of Melody Part 2", "Atmozfears, Audiotricz, KELTEK, Sound Rush" ],
|
||||
[ 2020, 2, 8, 20, 36, "Future Heroes", "D-Sturb, Frequencerz, Rebelion, Sefa, Sub Zero Project, Warface" ],
|
||||
[ 2020, 2, 8, 21, 22, "Hardcore 2.0", "Dr. Peacock, Korsakoff, Mad Dog, Partyraiser" ],
|
||||
[ 2020, 2, 8, 22, 0, "Hardstyle Top 25 of All-Time" ],
|
||||
[ 2020, 2, 8, 22, 50, "The Endshow" ],
|
||||
[ 2020, 2, 8, 23, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(top100|top25)": "Find the all-time top 100 here: <#675627527208697856>",
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.q-dance.com/en/videos/dediqated-live>**",
|
||||
"^\\.(mc|host)$": ":microphone: The MCs are **Villain**, **DV8**, and **Ruffian**."
|
||||
}
|
||||
}
|
||||
]
|
117
events/Defqon2018.json
Normal file
117
events/Defqon2018.json
Normal file
|
@ -0,0 +1,117 @@
|
|||
[
|
||||
{
|
||||
"stage": "red",
|
||||
"mc": "DV8 & Villain",
|
||||
"channel": "459077085013606401",
|
||||
"emoji": ":red_circle:",
|
||||
"url": "https://www.q-dance.com/en/videos/defqon-1-livestream-red",
|
||||
"sets": [
|
||||
[ 23, 13, 0, "ANDY SVGE" ],
|
||||
[ 23, 14, 0, "Sound Rush" ],
|
||||
[ 23, 15, 0, "Noisecontrollers" ],
|
||||
[ 23, 16, 0, "POWER HOUR" ],
|
||||
[ 23, 17, 0, "Code Black" ],
|
||||
[ 23, 18, 0, "D-Block & S-te-Fan" ],
|
||||
[ 23, 19, 0, "Wildstylez" ],
|
||||
[ 23, 20, 0, "Ran-D (LIVE)" ],
|
||||
[ 23, 20, 45, "Warface" ],
|
||||
[ 23, 21, 45, "Sub Zero Project" ],
|
||||
[ 23, 22, 30, "The Endshow" ],
|
||||
[ 23, 23, 0, "Nothing" ],
|
||||
|
||||
[ 24, 15, 0, "Zazafront" ],
|
||||
[ 24, 15, 30, "Wasted Penguinz & Adrenalize" ],
|
||||
[ 24, 16, 15, "Ransomnia & Friends" ],
|
||||
[ 24, 16, 45, "Psyko Punkz" ],
|
||||
[ 24, 17, 30, "Dee-Block & S-te-Pack" ],
|
||||
[ 24, 18, 15, "Zatox (LIVE)" ],
|
||||
[ 24, 18, 45, "Peacock in Concert" ],
|
||||
[ 24, 19, 15, "Tweekacore & Darren Styles" ],
|
||||
[ 24, 20, 0, "Brennan Heart (Uncertain)" ],
|
||||
[ 24, 21, 0, "Coone" ],
|
||||
[ 24, 22, 0, "Project One" ],
|
||||
[ 24, 23, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "blue",
|
||||
"mc": "Nolz",
|
||||
"channel": "459077096665382942",
|
||||
"emoji": ":large_blue_circle:",
|
||||
"url": "https://www.q-dance.com/en/videos/defqon-1-livestream-blue",
|
||||
"sets": [
|
||||
[ 22, 20, 0, "Headhunterz" ],
|
||||
[ 22, 21, 0, "Bass Modulators" ],
|
||||
[ 22, 22, 0, "Atmozfears" ],
|
||||
[ 22, 23, 0, "B-Front & Phuture Noize" ],
|
||||
[ 23, 0, 0, "Frequencerz" ],
|
||||
[ 23, 1, 0, "Nothing" ],
|
||||
|
||||
[ 23, 13, 0, "Requiem" ],
|
||||
[ 23, 14, 0, "Digital Punk" ],
|
||||
[ 23, 15, 0, "E-Force" ],
|
||||
[ 23, 16, 0, "Regain (LIVE) (Uncertain)" ],
|
||||
[ 23, 16, 30, "Jason Payne" ],
|
||||
[ 23, 18, 0, "B-Freqz" ],
|
||||
[ 23, 18, 30, "Adaro" ],
|
||||
[ 23, 19, 30, "Radical Redemption (LIVE)" ],
|
||||
[ 23, 20, 0, "Rebelion" ],
|
||||
[ 23, 21, 0, "Main Concern" ],
|
||||
[ 23, 22, 0, "Nothing" ],
|
||||
|
||||
[ 24, 13, 0, "Myst" ],
|
||||
[ 24, 14, 30, "Act of Rage" ],
|
||||
[ 24, 15, 30, "Clockartz" ],
|
||||
[ 24, 16, 30, "Endymion (LIVE)" ],
|
||||
[ 24, 17, 0, "Crypsis & Chain Reaction present \"Unlike Others\"" ],
|
||||
[ 24, 17, 45, "Sub Sonik" ],
|
||||
[ 24, 18, 45, "D-Sturb (LIVE)" ],
|
||||
[ 24, 19, 15, "Frequencerz \"Stealth Mode\"" ],
|
||||
[ 24, 19, 45, "Phuture Noize presents Black Mirror Society (Showcase)" ],
|
||||
[ 24, 20, 30, "Deetox - \"Bring the Riot\" (LIVE)" ],
|
||||
[ 24, 21, 0, "Delete VIP (Uncertain)" ],
|
||||
[ 24, 21, 30, "Rebel[ut]ion" ],
|
||||
[ 24, 22, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "black",
|
||||
"mc": "Diesel",
|
||||
"channel": "459077105137745941",
|
||||
"emoji": ":black_circle:",
|
||||
"url": "https://www.q-dance.com/en/videos/defqon-1-livestream-black",
|
||||
"sets": [
|
||||
[ 22, 20, 0, "Paul Elstak" ],
|
||||
[ 22, 21, 0, "20 Years of Endymion (LIVE)" ],
|
||||
[ 22, 22, 0, "Korsakoff" ],
|
||||
[ 22, 23, 0, "Tha Playah" ],
|
||||
[ 23, 0, 0, "Destructive Tendencies" ],
|
||||
[ 23, 1, 0, "Nothing" ],
|
||||
|
||||
[ 23, 13, 0, "Neophyte" ],
|
||||
[ 23, 14, 0, "Mad Dog" ],
|
||||
[ 23, 15, 0, "Noize Suppressor" ],
|
||||
[ 23, 16, 0, "Art of Fighters" ],
|
||||
[ 23, 17, 0, "Miss K8" ],
|
||||
[ 23, 18, 0, "N-Vitral presents \"Bombsquad\" (Uncertain)" ],
|
||||
[ 23, 18, 30, "Angerfist" ],
|
||||
[ 23, 19, 30, "Andy The Core" ],
|
||||
[ 23, 20, 30, "The Sickest Squad" ],
|
||||
[ 23, 21, 30, "Spitnoise" ],
|
||||
[ 23, 22, 30, "Nothing" ],
|
||||
|
||||
[ 24, 14, 0, "The Melodyst" ],
|
||||
[ 24, 15, 0, "Anime" ],
|
||||
[ 24, 16, 0, "Nosferatu" ],
|
||||
[ 24, 17, 0, "Evil Activities" ],
|
||||
[ 24, 18, 0, "I:GOR (Uncertain)" ],
|
||||
[ 24, 19, 0, "Deadly Guns" ],
|
||||
[ 24, 20, 0, "D-Fence" ],
|
||||
[ 24, 21, 0, "Partyraiser & Bulletproof" ],
|
||||
[ 24, 22, 0, "Sefa" ],
|
||||
[ 24, 23, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
27
events/Defqon2018_Aus.json
Normal file
27
events/Defqon2018_Aus.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
[
|
||||
{
|
||||
"stage": "red",
|
||||
"mc": "Villain",
|
||||
"channel": "489754437624266766",
|
||||
"emoji": ":red_circle:",
|
||||
"url": "https://www.q-dance.com/en/",
|
||||
"sets": [
|
||||
[ 15, 8, 15, "TNT" ],
|
||||
[ 15, 9, 15, "Defqon.1 Legends presents: A Decade of Dedication" ],
|
||||
[ 15, 10, 15, "Sub Zero Project" ],
|
||||
[ 15, 10, 40, "Da Tweekaz" ],
|
||||
[ 15, 11, 38, "Coone" ],
|
||||
[ 15, 12, 38, "Headhunterz" ],
|
||||
[ 15, 13, 50, "The Endshow" ],
|
||||
[ 15, 14, 6, "Nothing" ],
|
||||
[ 15, 16, 0, "TNT (Rebroadcast)" ],
|
||||
[ 15, 17, 0, "Defqon.1 Legends presents: A Decade of Dedication (Rebroadcast)" ],
|
||||
[ 15, 18, 0, "Sub Zero Project (Rebroadcast)" ],
|
||||
[ 15, 18, 30, "Da Tweekaz (Rebroadcast)" ],
|
||||
[ 15, 19, 30, "Coone (Rebroadcast)" ],
|
||||
[ 15, 20, 30, "Headhunterz (Rebroadcast)" ],
|
||||
[ 15, 21, 45, "The Endshow (Rebroadcast)" ],
|
||||
[ 15, 22, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
117
events/Defqon2018_test.json
Normal file
117
events/Defqon2018_test.json
Normal file
|
@ -0,0 +1,117 @@
|
|||
[
|
||||
{
|
||||
"stage": "red",
|
||||
"mc": "DV8 & Villain",
|
||||
"channel": "459077085013606401",
|
||||
"emoji": ":red_circle:",
|
||||
"url": "https://www.q-dance.com/en/news/defqon-1-2018-or-livestream",
|
||||
"sets": [
|
||||
[ 23, 13, 0, "ANDY SVGE" ],
|
||||
[ 23, 14, 0, "Sound Rush" ],
|
||||
[ 23, 15, 0, "Noisecontrollers" ],
|
||||
[ 23, 16, 0, "POWER HOUR" ],
|
||||
[ 23, 17, 0, "Code Black" ],
|
||||
[ 23, 18, 0, "D-Block & S-te-Fan" ],
|
||||
[ 23, 19, 0, "Wildstylez" ],
|
||||
[ 23, 20, 0, "Ran-D (LIVE)" ],
|
||||
[ 23, 20, 45, "Warface" ],
|
||||
[ 23, 21, 45, "Sub Zero Project" ],
|
||||
[ 23, 22, 30, "The Endshow" ],
|
||||
[ 23, 23, 0, "Nothing" ],
|
||||
|
||||
[ 24, 15, 0, "Zazafront" ],
|
||||
[ 24, 15, 30, "Wasted Penguinz & Adrenalize" ],
|
||||
[ 24, 16, 15, "Ransomnia & Friends" ],
|
||||
[ 24, 16, 45, "Psyko Punkz" ],
|
||||
[ 24, 17, 30, "Dee-Block & S-te-Pack" ],
|
||||
[ 24, 18, 15, "Zatox (LIVE)" ],
|
||||
[ 24, 18, 45, "Tweekacore & Darren Styles" ],
|
||||
[ 24, 19, 30, "Peacock in Concert" ],
|
||||
[ 24, 20, 0, "Nothing" ],
|
||||
[ 24, 21, 0, "Coone" ],
|
||||
[ 24, 22, 0, "Project One" ],
|
||||
[ 24, 23, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "blue",
|
||||
"mc": "Villain",
|
||||
"channel": "459077096665382942",
|
||||
"emoji": ":large_blue_circle:",
|
||||
"url": "https://www.q-dance.com/en/news/defqon-1-2018-or-livestream",
|
||||
"sets": [
|
||||
[ 20, 21, 38, "Headhunterz" ],
|
||||
[ 22, 21, 0, "Bass Modulators" ],
|
||||
[ 22, 22, 0, "Atmozfears" ],
|
||||
[ 22, 23, 0, "B-Front & Phuture Noize" ],
|
||||
[ 23, 0, 0, "Frequencerz" ],
|
||||
[ 23, 1, 0, "Nothing" ],
|
||||
|
||||
[ 23, 13, 0, "Requiem" ],
|
||||
[ 23, 14, 0, "Digital Punk" ],
|
||||
[ 23, 15, 0, "E-Force" ],
|
||||
[ 23, 16, 0, "Nothing" ],
|
||||
[ 23, 16, 30, "Jason Payne" ],
|
||||
[ 23, 18, 0, "B-Freqz" ],
|
||||
[ 23, 18, 30, "Adaro" ],
|
||||
[ 23, 19, 30, "Radical Redemption (LIVE)" ],
|
||||
[ 23, 20, 0, "Rebelion" ],
|
||||
[ 23, 21, 0, "Main Concern" ],
|
||||
[ 23, 22, 0, "Nothing" ],
|
||||
|
||||
[ 24, 13, 0, "Myst" ],
|
||||
[ 24, 14, 30, "Act of Rage" ],
|
||||
[ 24, 15, 30, "Clockartz" ],
|
||||
[ 24, 16, 30, "Endymion (LIVE)" ],
|
||||
[ 24, 17, 0, "Crypsis & Chain Reaction present \"Unlike Others\"" ],
|
||||
[ 24, 17, 45, "Sub Sonik" ],
|
||||
[ 24, 18, 45, "D-Sturb (LIVE)" ],
|
||||
[ 24, 19, 15, "Frequencerz \"Stealth Mode\"" ],
|
||||
[ 24, 19, 45, "Phuture Noize presents Black Mirror Society (Showcase)" ],
|
||||
[ 24, 20, 30, "Deetox - \"Bring the Riot\" (LIVE)" ],
|
||||
[ 24, 21, 0, "Nothing" ],
|
||||
[ 24, 21, 30, "Rebel[ut]ion" ],
|
||||
[ 24, 22, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "black",
|
||||
"mc": "Ruffian",
|
||||
"channel": "459077105137745941",
|
||||
"emoji": ":black_circle:",
|
||||
"url": "https://www.q-dance.com/en/news/defqon-1-2018-or-livestream",
|
||||
"sets": [
|
||||
[ 22, 20, 0, "Paul Elstak" ],
|
||||
[ 22, 21, 0, "20 Years of Endymion (LIVE)" ],
|
||||
[ 22, 22, 0, "Korsakoff" ],
|
||||
[ 22, 23, 0, "Tha Playah" ],
|
||||
[ 23, 0, 0, "Destructive Tendencies" ],
|
||||
[ 23, 1, 0, "Nothing" ],
|
||||
|
||||
[ 23, 13, 0, "Neophyte" ],
|
||||
[ 23, 14, 0, "Mad Dog" ],
|
||||
[ 23, 15, 0, "Nothing" ],
|
||||
[ 23, 16, 0, "Art of Fighters" ],
|
||||
[ 23, 17, 0, "Miss K8" ],
|
||||
[ 23, 18, 0, "Nothing" ],
|
||||
[ 23, 18, 30, "Angerfist" ],
|
||||
[ 23, 19, 30, "Andy The Core" ],
|
||||
[ 23, 20, 30, "The Sickest Squad" ],
|
||||
[ 23, 21, 30, "Spitnoise" ],
|
||||
[ 23, 22, 30, "Nothing" ],
|
||||
|
||||
[ 24, 14, 0, "The Melodyst" ],
|
||||
[ 24, 15, 0, "Anime" ],
|
||||
[ 24, 16, 0, "Nosferatu" ],
|
||||
[ 24, 17, 0, "Evil Activities" ],
|
||||
[ 24, 18, 0, "Nothing" ],
|
||||
[ 24, 19, 0, "Deadly Guns" ],
|
||||
[ 24, 20, 0, "D-Fence" ],
|
||||
[ 24, 21, 0, "Partyraiser & Bulletproof" ],
|
||||
[ 24, 22, 0, "Sefa" ],
|
||||
[ 24, 23, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
352
events/Defqon2019.json
Normal file
352
events/Defqon2019.json
Normal file
|
@ -0,0 +1,352 @@
|
|||
[
|
||||
{
|
||||
"stage": "Red",
|
||||
"mc": "Villain & DV8",
|
||||
"channel": "591927725594378240",
|
||||
"emoji": ":heart:",
|
||||
"url": "https://www.q-dance.com/en/videos/red-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 3, "Toneshifterz" ],
|
||||
[ 2019, 6, 29, 12, 3, "Adrenalize & Devin Wild" ],
|
||||
[ 2019, 6, 29, 13, 3, "Coone" ],
|
||||
[ 2019, 6, 29, 13, 48, "Noisecontrollers" ],
|
||||
[ 2019, 6, 29, 14, 33, "Sound Rush" ],
|
||||
[ 2019, 6, 29, 15, 18, "Headhunterz" ],
|
||||
[ 2019, 6, 29, 16, 3, "Power Hour" ],
|
||||
[ 2019, 6, 29, 17, 3, "Coone, Headhunterz,Noisecontrollers & Sound Rush" ],
|
||||
[ 2019, 6, 29, 18, 3, "KELTEK" ],
|
||||
[ 2019, 6, 29, 19, 3, "Frequencerz & Phuture Noize" ],
|
||||
[ 2019, 6, 29, 20, 3, "D-Sturb" ],
|
||||
[ 2019, 6, 29, 21, 3, "Radical Redemption" ],
|
||||
[ 2019, 6, 29, 21, 48, "B-Front" ],
|
||||
[ 2019, 6, 29, 22, 38, "The Endshow" ],
|
||||
[ 2019, 6, 29, 23, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 15, 3, "Code Black & Atmozfears" ],
|
||||
[ 2019, 6, 30, 16, 18, "Audiotricz: The Next Chapter [LIVE]" ],
|
||||
[ 2019, 6, 30, 17, 3, "D-Block & S-te-Fan & DJ Isaac" ],
|
||||
[ 2019, 6, 30, 18, 3, "Wildstylez" ],
|
||||
[ 2019, 6, 30, 19, 3, "Gunz for Hire" ],
|
||||
[ 2019, 6, 30, 20, 3, "Defqon.1 Legends" ],
|
||||
[ 2019, 6, 30, 21, 18, "Sub Zero Project" ],
|
||||
[ 2019, 6, 30, 22, 13, "Sefa" ],
|
||||
[ 2019, 6, 30, 22, 53, "The Closing Ritual" ],
|
||||
[ 2019, 6, 30, 23, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Blue",
|
||||
"mc": "Villain & Livid & Nolz",
|
||||
"channel": "591928007351074816",
|
||||
"emoji": ":blue_heart:",
|
||||
"url": "https://www.q-dance.com/en/videos/blue-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 28, 20, 3, "Brennan Heart" ],
|
||||
[ 2019, 6, 28, 21, 33, "Bass Modulators" ],
|
||||
[ 2019, 6, 28, 22, 33, "D-Block & S-te-Fan" ],
|
||||
[ 2019, 6, 28, 23, 33, "Ran-D" ],
|
||||
[ 2019, 6, 29, 0, 33, "Phuture Noize [THE SPOTLIGHT]" ],
|
||||
[ 2019, 6, 29, 1, 0, "Nothing" ],
|
||||
[ 2019, 6, 29, 11, 3, "D-Attack" ],
|
||||
[ 2019, 6, 29, 12, 3, "Bass Chaserz" ],
|
||||
[ 2019, 6, 29, 13, 3, "Sub Sonik" ],
|
||||
[ 2019, 6, 29, 14, 3, "Deetox presents Revival" ],
|
||||
[ 2019, 6, 29, 15, 3, "Hard Driver [LIVE]" ],
|
||||
[ 2019, 6, 29, 15, 33, "Malice" ],
|
||||
[ 2019, 6, 29, 17, 3, "Digital Punk" ],
|
||||
[ 2019, 6, 29, 18, 3, "Rejecta" ],
|
||||
[ 2019, 6, 29, 19, 3, "Warface" ],
|
||||
[ 2019, 6, 29, 20, 3, "Regain" ],
|
||||
[ 2019, 6, 29, 21, 3, "Rebelion" ],
|
||||
[ 2019, 6, 29, 22, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 0, 0, "Psyko Punkz" ],
|
||||
[ 2019, 6, 30, 1, 0, "Zatox" ],
|
||||
[ 2019, 6, 30, 2, 0, "Rebelion & Delete" ],
|
||||
[ 2019, 6, 30, 3, 0, "The Sickest Squad" ],
|
||||
[ 2019, 6, 30, 4, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 11, 3, "Chain Reaction" ],
|
||||
[ 2019, 6, 30, 12, 3, "Degos & Re-Done" ],
|
||||
[ 2019, 6, 30, 13, 3, "Endymion" ],
|
||||
[ 2019, 6, 30, 14, 3, "Clockartz" ],
|
||||
[ 2019, 6, 30, 14, 48, "Requiem presents The Reckoning" ],
|
||||
[ 2019, 6, 30, 15, 18, "Titan" ],
|
||||
[ 2019, 6, 30, 15, 33, "Act of Rage" ],
|
||||
[ 2019, 6, 30, 16, 33, "Adaro" ],
|
||||
[ 2019, 6, 30, 17, 33, "MYST" ],
|
||||
[ 2019, 6, 30, 18, 33, "Jason Payne" ],
|
||||
[ 2019, 6, 30, 19, 33, "Killshot" ],
|
||||
[ 2019, 6, 30, 20, 33, "E-Force & Luna" ],
|
||||
[ 2019, 6, 30, 21, 33, "10 Years of Thera" ],
|
||||
[ 2019, 6, 30, 22, 33, "Delete VIP" ],
|
||||
[ 2019, 6, 30, 23, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Black",
|
||||
"mc": "Da Mouth of Madness & Alee",
|
||||
"channel": "591928033523269632",
|
||||
"emoji": ":black_heart:",
|
||||
"url": "https://www.q-dance.com/en/videos/black-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 28, 20, 3, "Promo" ],
|
||||
[ 2019, 6, 28, 21, 33, "Evil Activities" ],
|
||||
[ 2019, 6, 28, 22, 33, "AniMe" ],
|
||||
[ 2019, 6, 28, 23, 33, "Angerfist" ],
|
||||
[ 2019, 6, 29, 0, 33, "Dr. Peacock [THE SPOTLIGHT]" ],
|
||||
[ 2019, 6, 29, 1, 0, "Nothing" ],
|
||||
[ 2019, 6, 29, 11, 3, "Access One" ],
|
||||
[ 2019, 6, 29, 12, 33, "Amada & Yoshiko" ],
|
||||
[ 2019, 6, 29, 14, 3, "Wasted Mind" ],
|
||||
[ 2019, 6, 29, 15, 3, "Dither presents Tools of Demolition" ],
|
||||
[ 2019, 6, 29, 16, 3, "Neophyte" ],
|
||||
[ 2019, 6, 29, 17, 3, "N-Vitral" ],
|
||||
[ 2019, 6, 29, 18, 3, "Nosferatu" ],
|
||||
[ 2019, 6, 29, 19, 3, "Angerfist & I:GOR" ],
|
||||
[ 2019, 6, 29, 20, 3, "Deadly Guns" ],
|
||||
[ 2019, 6, 29, 21, 3, "Andy The Core" ],
|
||||
[ 2019, 6, 29, 22, 3, "Destructive Tendencies" ],
|
||||
[ 2019, 6, 29, 23, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 12, 3, "The Viper" ],
|
||||
[ 2019, 6, 30, 13, 3, "The Melodyst" ],
|
||||
[ 2019, 6, 30, 14, 3, "Mad Dog" ],
|
||||
[ 2019, 6, 30, 15, 3, "Korsakoff" ],
|
||||
[ 2019, 6, 30, 16, 3, "Tha Playah 'Sick and Twisted showcase'" ],
|
||||
[ 2019, 6, 30, 17, 3, "D-Fence" ],
|
||||
[ 2019, 6, 30, 18, 3, "Partyraiser, Hard Effectz & Bulletproof" ],
|
||||
[ 2019, 6, 30, 19, 3, "GPF [THE PRERECORDED SEX SHOW V69 UNMASTERED TEST.WMA]" ],
|
||||
[ 2019, 6, 30, 19, 45, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "UV",
|
||||
"mc": "DL & DV8 & Da Syndrome",
|
||||
"channel": "591928065416757248",
|
||||
"emoji": ":purple_heart:",
|
||||
"url": "https://www.q-dance.com/en/videos/uv-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 0, "Sephyx" ],
|
||||
[ 2019, 6, 29, 12, 0, "Retrospect" ],
|
||||
[ 2019, 6, 29, 13, 0, "Primeshock" ],
|
||||
[ 2019, 6, 29, 14, 0, "Audiofreq" ],
|
||||
[ 2019, 6, 29, 15, 0, "Max Enforcer & ANDY SVGE" ],
|
||||
[ 2019, 6, 29, 16, 0, "Jay Reeve presents A Higher State" ],
|
||||
[ 2019, 6, 29, 17, 0, "DJ The Prophet" ],
|
||||
[ 2019, 6, 29, 17, 45, "Zany presents DNA" ],
|
||||
[ 2019, 6, 29, 18, 45, "Demi Kanon" ],
|
||||
[ 2019, 6, 29, 19, 30, "Frontliner" ],
|
||||
[ 2019, 6, 29, 20, 30, "Da Tweekaz" ],
|
||||
[ 2019, 6, 29, 21, 30, "Wasted Penguinz" ],
|
||||
[ 2019, 6, 29, 22, 30, "Nothing" ],
|
||||
[ 2019, 6, 30, 13, 3, "Mental Twister" ],
|
||||
[ 2019, 6, 30, 14, 3, "Dr. Phunk & MANDY" ],
|
||||
[ 2019, 6, 30, 15, 3, "Ransom" ],
|
||||
[ 2019, 6, 30, 15, 48, "Mark with a K & MC Chucky" ],
|
||||
[ 2019, 6, 30, 16, 33, "Dr. Ruthless" ],
|
||||
[ 2019, 6, 30, 17, 18, "Outsiders & The Partysquad & The Darkraver" ],
|
||||
[ 2019, 6, 30, 18, 18, "Frequencerz presents GET WACK!" ],
|
||||
[ 2019, 6, 30, 19, 18, "ZazaFront" ],
|
||||
[ 2019, 6, 30, 20, 3, "Mashup Jack" ],
|
||||
[ 2019, 6, 30, 20, 48, "Paul Elstak" ],
|
||||
[ 2019, 6, 30, 21, 30, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Magenta",
|
||||
"mc": "Ruffian & DL",
|
||||
"channel": "591928254877794324",
|
||||
"emoji": ":loud_sound:",
|
||||
"url": "https://www.q-dance.com/en/videos/magenta-audio-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 0, "Sunny D" ],
|
||||
[ 2019, 6, 29, 12, 30, "Pila & The Scientist" ],
|
||||
[ 2019, 6, 29, 14, 0, "Trilok & Chiren" ],
|
||||
[ 2019, 6, 29, 15, 30, "ASYS" ],
|
||||
[ 2019, 6, 29, 17, 0, "Dana" ],
|
||||
[ 2019, 6, 29, 18, 0, "Activator" ],
|
||||
[ 2019, 6, 29, 19, 0, "DJ Isaac" ],
|
||||
[ 2019, 6, 29, 20, 0, "Luna" ],
|
||||
[ 2019, 6, 29, 21, 30, "Pavo" ],
|
||||
[ 2019, 6, 29, 23, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 11, 0, "Lip DJ" ],
|
||||
[ 2019, 6, 30, 12, 30, "A-Lusion" ],
|
||||
[ 2019, 6, 30, 14, 0, "Ivan Carsten" ],
|
||||
[ 2019, 6, 30, 15, 0, "Geck-o" ],
|
||||
[ 2019, 6, 30, 16, 0, "The Pitcher" ],
|
||||
[ 2019, 6, 30, 17, 0, "Tat & Zat" ],
|
||||
[ 2019, 6, 30, 18, 0, "Charly Lownoise" ],
|
||||
[ 2019, 6, 30, 19, 0, "Deepack" ],
|
||||
[ 2019, 6, 30, 20, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Indigo",
|
||||
"mc": "Focus & Livid",
|
||||
"channel": "591928282295828480",
|
||||
"emoji": ":loud_sound:",
|
||||
"url": "https://www.q-dance.com/en/videos/indigo-audio-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 0, "Imperatorz" ],
|
||||
[ 2019, 6, 29, 12, 0, "Aversion" ],
|
||||
[ 2019, 6, 29, 13, 0, "Crystal Mad" ],
|
||||
[ 2019, 6, 29, 14, 0, "Neroz" ],
|
||||
[ 2019, 6, 29, 15, 0, "Luminite" ],
|
||||
[ 2019, 6, 29, 16, 0, "Retaliation" ],
|
||||
[ 2019, 6, 29, 17, 0, "Ncrypta" ],
|
||||
[ 2019, 6, 29, 18, 0, "The Apexx Machine" ],
|
||||
[ 2019, 6, 29, 19, 0, "Rooler" ],
|
||||
[ 2019, 6, 29, 20, 0, "The Purge" ],
|
||||
[ 2019, 6, 29, 21, 0, "Mind Dimension" ],
|
||||
[ 2019, 6, 29, 22, 0, "Riot Shift" ],
|
||||
[ 2019, 6, 29, 23, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 11, 0, "Dark Pact" ],
|
||||
[ 2019, 6, 30, 12, 0, "Tartaros" ],
|
||||
[ 2019, 6, 30, 13, 0, "The Classics Machine" ],
|
||||
[ 2019, 6, 30, 14, 0, "RVAGE" ],
|
||||
[ 2019, 6, 30, 15, 0, "Vertile" ],
|
||||
[ 2019, 6, 30, 16, 0, "Prefix & Density" ],
|
||||
[ 2019, 6, 30, 17, 0, "Typhoon" ],
|
||||
[ 2019, 6, 30, 18, 0, "Bloodlust" ],
|
||||
[ 2019, 6, 30, 19, 0, "Thyron" ],
|
||||
[ 2019, 6, 30, 20, 0, "Chris One" ],
|
||||
[ 2019, 6, 30, 21, 0, "Unresolved" ],
|
||||
[ 2019, 6, 30, 22, 0, "Caine" ],
|
||||
[ 2019, 6, 30, 23, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Yellow",
|
||||
"mc": "No-iD & RG",
|
||||
"channel": "591928310582345738",
|
||||
"emoji": ":loud_sound:",
|
||||
"url": "https://www.q-dance.com/en/videos/yellow-audio-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 0, "Streiks & Kratchs" ],
|
||||
[ 2019, 6, 29, 12, 0, "Mr. Ivex" ],
|
||||
[ 2019, 6, 29, 13, 0, "Trespassed & Hard Effectz" ],
|
||||
[ 2019, 6, 29, 14, 0, "Lady Dammage" ],
|
||||
[ 2019, 6, 29, 15, 0, "Unproven" ],
|
||||
[ 2019, 6, 29, 16, 0, "BillX" ],
|
||||
[ 2019, 6, 29, 17, 0, "Crypton" ],
|
||||
[ 2019, 6, 29, 18, 0, "Spitnoise" ],
|
||||
[ 2019, 6, 29, 19, 0, "Partyraiser & Bulletproof" ],
|
||||
[ 2019, 6, 29, 20, 0, "F. NøIzE" ],
|
||||
[ 2019, 6, 29, 21, 0, "The Destroyer" ],
|
||||
[ 2019, 6, 29, 22, 0, "Dissoactive" ],
|
||||
[ 2019, 6, 29, 23, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 12, 0, "Sprinky" ],
|
||||
[ 2019, 6, 30, 13, 30, "Hyrule War" ],
|
||||
[ 2019, 6, 30, 14, 30, "Remzcore" ],
|
||||
[ 2019, 6, 30, 15, 30, "Super Trash Bros. [LIVE]" ],
|
||||
[ 2019, 6, 30, 16, 0, "Frenchcore Familia" ],
|
||||
[ 2019, 6, 30, 17, 0, "Lunatic" ],
|
||||
[ 2019, 6, 30, 18, 0, "Vandal!sm" ],
|
||||
[ 2019, 6, 30, 19, 0, "System Overload" ],
|
||||
[ 2019, 6, 30, 20, 0, "Angernoizer & Cryogenic" ],
|
||||
[ 2019, 6, 30, 21, 0, "Chaotic Hostility" ],
|
||||
[ 2019, 6, 30, 22, 0, "DRS" ],
|
||||
[ 2019, 6, 30, 23, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Gold",
|
||||
"mc": "Da Mouth of Madness & Ruffian",
|
||||
"channel": "591928331159601154",
|
||||
"emoji": ":loud_sound:",
|
||||
"url": "https://www.q-dance.com/en/videos/gold-audio-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 0, "The Raver" ],
|
||||
[ 2019, 6, 29, 12, 30, "Petrov" ],
|
||||
[ 2019, 6, 29, 14, 0, "Panic" ],
|
||||
[ 2019, 6, 29, 15, 0, "The Stunned Guys" ],
|
||||
[ 2019, 6, 29, 16, 0, "Uzi" ],
|
||||
[ 2019, 6, 29, 17, 0, "DJ DUNE" ],
|
||||
[ 2019, 6, 29, 18, 0, "Mental Theo" ],
|
||||
[ 2019, 6, 29, 19, 0, "The Darkraver & Vince" ],
|
||||
[ 2019, 6, 29, 20, 0, "Buzz Fuzz" ],
|
||||
[ 2019, 6, 29, 21, 30, "Frantic Freak" ],
|
||||
[ 2019, 6, 29, 23, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 11, 0, "Sequence & Ominous" ],
|
||||
[ 2019, 6, 30, 12, 30, "Ophidian & Ruffneck" ],
|
||||
[ 2019, 6, 30, 14, 0, "Re-Style" ],
|
||||
[ 2019, 6, 30, 15, 0, "Art of Fighters" ],
|
||||
[ 2019, 6, 30, 16, 0, "Endymion" ],
|
||||
[ 2019, 6, 30, 17, 0, "Noize Suppressor" ],
|
||||
[ 2019, 6, 30, 18, 0, "Synapse & Sei2ure" ],
|
||||
[ 2019, 6, 30, 19, 0, "Unexist" ],
|
||||
[ 2019, 6, 30, 20, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Silver",
|
||||
"mc": "Dart",
|
||||
"channel": "591928389938446356",
|
||||
"emoji": ":loud_sound:",
|
||||
"url": "https://www.q-dance.com/en/videos/silver-audio-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 12, 0, "Mindustries" ],
|
||||
[ 2019, 6, 29, 13, 0, "Manu Le Malin" ],
|
||||
[ 2019, 6, 29, 14, 0, "The Clamps" ],
|
||||
[ 2019, 6, 29, 15, 30, "Penta" ],
|
||||
[ 2019, 6, 29, 16, 30, "Deathmachine" ],
|
||||
[ 2019, 6, 29, 18, 0, "Khaoz Engine" ],
|
||||
[ 2019, 6, 29, 19, 30, "Bryan Fury" ],
|
||||
[ 2019, 6, 29, 20, 30, "Hellfish" ],
|
||||
[ 2019, 6, 29, 22, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 12, 0, "Strange Arrival" ],
|
||||
[ 2019, 6, 30, 13, 0, "Somniac One" ],
|
||||
[ 2019, 6, 30, 14, 0, "Katharsys" ],
|
||||
[ 2019, 6, 30, 15, 0, "Ophidian as Raziel" ],
|
||||
[ 2019, 6, 30, 16, 0, "The Outside Agency" ],
|
||||
[ 2019, 6, 30, 17, 0, "The DJ Producer" ],
|
||||
[ 2019, 6, 30, 18, 0, "The Satan" ],
|
||||
[ 2019, 6, 30, 19, 0, "Akira" ],
|
||||
[ 2019, 6, 30, 20, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "Purple",
|
||||
"mc": "Dash & Le Prince",
|
||||
"channel": "591928410209517568",
|
||||
"emoji": ":loud_sound:",
|
||||
"url": "https://www.q-dance.com/en/videos/purple-audio-live-2019",
|
||||
"sets": [
|
||||
[ 2019, 6, 29, 11, 0, "Solstice" ],
|
||||
[ 2019, 6, 29, 12, 30, "Nexone" ],
|
||||
[ 2019, 6, 29, 14, 0, "Jesse Jax" ],
|
||||
[ 2019, 6, 29, 15, 0, "PRDX" ],
|
||||
[ 2019, 6, 29, 16, 0, "STK" ],
|
||||
[ 2019, 6, 29, 17, 0, "Bestia" ],
|
||||
[ 2019, 6, 29, 18, 0, "Kuzak" ],
|
||||
[ 2019, 6, 29, 19, 0, "Dawnfire" ],
|
||||
[ 2019, 6, 29, 20, 0, "Imperial" ],
|
||||
[ 2019, 6, 29, 21, 0, "Unifire" ],
|
||||
[ 2019, 6, 29, 22, 0, "Nothing" ],
|
||||
[ 2019, 6, 30, 15, 0, "Charter" ],
|
||||
[ 2019, 6, 30, 15, 45, "Envine" ],
|
||||
[ 2019, 6, 30, 16, 30, "Helix" ],
|
||||
[ 2019, 6, 30, 17, 15, "Attack" ],
|
||||
[ 2019, 6, 30, 18, 0, "Stormerz" ],
|
||||
[ 2019, 6, 30, 19, 0, "Emphasis" ],
|
||||
[ 2019, 6, 30, 20, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
]
|
||||
}
|
||||
]
|
35
events/Epiq2019.json
Normal file
35
events/Epiq2019.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:epiqE:661533593356468244><:epiqP:661533593088294933><:epiqI:661533593323044874><:epiqQ:661533593343885353>",
|
||||
"url": "https://www.q-dance.com/en/radio/",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 1,
|
||||
"sets": [
|
||||
[ 2019, 12, 31, 17, 45, "Q-dance Hardstyle Top 100 to 11" ],
|
||||
[ 2019, 12, 31, 20, 30, "Primeshock" ],
|
||||
[ 2019, 12, 31, 21, 30, "Demi Kanon" ],
|
||||
[ 2019, 12, 31, 22, 15, "Sound Rush vs. Demi Kanon" ],
|
||||
[ 2019, 12, 31, 22, 45, "Sound Rush" ],
|
||||
[ 2019, 12, 31, 23, 40, "Hardstyle Top 10" ],
|
||||
[ 2020, 1, 1, 0, 0, "Frequencerz" ],
|
||||
[ 2020, 1, 1, 0, 45, "Devin Wild" ],
|
||||
[ 2020, 1, 1, 1, 30, "Devin Wild vs. Rebelion" ],
|
||||
[ 2020, 1, 1, 2, 0, "Psyko Punkz vs. Frequencerz" ],
|
||||
[ 2020, 1, 1, 2, 30, "Psyko Punkz" ],
|
||||
[ 2020, 1, 1, 3, 0, "D-Block & S-te-Fan vs. D-Sturb" ],
|
||||
[ 2020, 1, 1, 4, 0, "TILT MODE" ],
|
||||
[ 2020, 1, 1, 4, 10, "Rebelion" ],
|
||||
[ 2020, 1, 1, 5, 0, "Act of Rage" ],
|
||||
[ 2020, 1, 1, 5, 45, "Act of Rage vs. Sefa" ],
|
||||
[ 2020, 1, 1, 6, 15, "Sefa" ],
|
||||
[ 2020, 1, 1, 7, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(top100|top10)": "Find the Q-dance Top 100 here: <#661622907109244939>",
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Listen to the livestream in our **Q-dance Radio voice channel** or here: **<https://www.q-dance.com/en/radio/>**",
|
||||
"^\\.(mc|host)$": ":microphone: The top 100 is hosted by **Tellem**. The MC during Epiq is **Villan**."
|
||||
}
|
||||
}
|
||||
]
|
29
events/HardBass2019.json
Normal file
29
events/HardBass2019.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"mc": "DV8 & Villain",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":blue_heart::green_heart::yellow_heart::heart:",
|
||||
"url": "https://live.b2s.nl/",
|
||||
"sets": [
|
||||
[ 2019, 2, 9, 21, 0, "Zany" ],
|
||||
[ 2019, 2, 9, 22, 0, "TEAM BLUE (Coone, D-Block & S-te-Fan, Wildstylez)" ],
|
||||
[ 2019, 2, 9, 23, 30, "Headhunterz (LIVE)" ],
|
||||
[ 2019, 2, 10, 0, 0, "TEAM GREEN (Atmozfears, B-Front, Noisecontrollers)" ],
|
||||
[ 2019, 2, 10, 1, 30, "I AM HARDSTYLE Take Over (Brennan Heart, Code Black, Toneshifterz) (LIVE)" ],
|
||||
[ 2019, 2, 10, 2, 15, "Heroes of Hard Bass show" ],
|
||||
[ 2019, 2, 10, 3, 0, "TEAM YELLOW (Frequencerz, Phuture Noize, Sub Zero Project)" ],
|
||||
[ 2019, 2, 10, 4, 30, "Ran-D - We Rule The Night (Showcase) (LIVE)" ],
|
||||
[ 2019, 2, 10, 5, 0, "TEAM RED (E-Force, Radical Redemption, Rejecta)" ],
|
||||
[ 2019, 2, 10, 6, 30, "End of Line (Warface, Delete, Killshot) (LIVE)" ],
|
||||
[ 2019, 2, 10, 7, 0, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
"You can remove ads by logging in to the b2s website with a b2s account (or a Q-dance account).",
|
||||
"Disable your adblocker to fix common issues with the stream player.",
|
||||
"There's only 1 camera for the warmup set, it'll change soon.",
|
||||
"Yes, <@193774894075346944> is recording: <https://old.reddit.com/r/hardstyle/aoybih>",
|
||||
"The timer is counting down until the end of Hard Bass. (hh:mm:ss.mm)"
|
||||
]
|
||||
}
|
||||
]
|
23
events/Holland.json
Normal file
23
events/Holland.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
{
|
||||
"stage": "de-huiskamer",
|
||||
"mc": "DV8",
|
||||
"channel": "495260892535980032",
|
||||
"emoji": ":flag_nl:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-stream-from-x-qlusive-holland-xxl",
|
||||
"sets": [
|
||||
[ 29, 22, 5, "Zany" ],
|
||||
[ 29, 23, 5, "Demi Kanon" ],
|
||||
[ 29, 23, 53, "Bass Modulators" ],
|
||||
[ 30, 0, 50, "The Partysquad & Outsiders" ],
|
||||
[ 30, 1, 48, "Coone's Inburgeringscursus" ],
|
||||
[ 30, 2, 35, "Ransom & Dr. Rude" ],
|
||||
[ 30, 3, 20, "Psyko Punkz" ],
|
||||
[ 30, 4, 5, "Frequencerz" ],
|
||||
[ 30, 5, 5, "Zazafront" ],
|
||||
[ 30, 5, 35, "Bass Chaserz" ],
|
||||
[ 30, 6, 20, "Dr. Peacock" ],
|
||||
[ 30, 7, 5, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
20
events/Holland2019.json
Normal file
20
events/Holland2019.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"mc": "DV8",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":flag_nl:",
|
||||
"url": "https://www.q-dance.com/en/radio/",
|
||||
"sets": [
|
||||
[ 2019, 9, 28, 23, 0, "Retroshock (Primeshock & Retrospect)" ],
|
||||
[ 2019, 9, 29, 0, 0, "Dr. Ruthless" ],
|
||||
[ 2019, 9, 29, 1, 0, "Sound Rush" ],
|
||||
[ 2019, 9, 29, 2, 0, "Nothing" ],
|
||||
[ 2019, 9, 29, 3, 0, "Outsiders" ],
|
||||
[ 2019, 9, 29, 4, 0, "De Nachtbrakers: Bass Chaserz, Degos & Re-Done, Endymion" ],
|
||||
[ 2019, 9, 29, 5, 0, "Warface presenteert Warfeest" ],
|
||||
[ 2019, 9, 29, 6, 0, "D-Fence" ],
|
||||
[ 2019, 9, 29, 7, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
25
events/Impaqt2019.json
Normal file
25
events/Impaqt2019.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
[
|
||||
{
|
||||
"stage": "Colossus",
|
||||
"mc": "Villain",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/radio/",
|
||||
"sets": [
|
||||
[ 2019, 9, 7, 14, 0, "Primeshock" ],
|
||||
[ 2019, 9, 7, 15, 0, "KELTEK" ],
|
||||
[ 2019, 9, 7, 16, 0, "Atmozfears & Sound Rush present: 2//\\\\\\\\1" ],
|
||||
[ 2019, 9, 7, 16, 30, "Noisecontrollers & Devin Wild" ],
|
||||
[ 2019, 9, 7, 17, 30, "Wildstylez" ],
|
||||
[ 2019, 9, 7, 18, 30, "Sefa & Rooler" ],
|
||||
[ 2019, 9, 7, 19, 0, "Zatox" ],
|
||||
[ 2019, 9, 7, 20, 0, "Phuture Noize" ],
|
||||
[ 2019, 9, 7, 21, 0, "D-Block & S-te-Fan" ],
|
||||
[ 2019, 9, 7, 22, 0, "Ran-D" ],
|
||||
[ 2019, 9, 7, 23, 0, "Warface" ],
|
||||
[ 2019, 9, 8, 0, 0, "Rebelion" ],
|
||||
[ 2019, 9, 8, 1, 0, "Miss K8" ],
|
||||
[ 2019, 9, 8, 2, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
22
events/Mysteryland.json
Normal file
22
events/Mysteryland.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"stage": "mysteryland",
|
||||
"mc": "Villain",
|
||||
"channel": "481392155022196747",
|
||||
"emoji": ":bird:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-stream",
|
||||
"sets": [
|
||||
[ 25, 14, 0, "ANDY SVGE" ],
|
||||
[ 25, 15, 30, "Ruthless" ],
|
||||
[ 25, 16, 30, "Brennan Heart" ],
|
||||
[ 25, 17, 30, "Code Black" ],
|
||||
[ 25, 19, 0, "Nothing" ],
|
||||
[ 25, 19, 33, "Wildstylez (w/o Hardwell)" ],
|
||||
[ 25, 20, 0, "Atmozfears" ],
|
||||
[ 25, 21, 0, "B-Front" ],
|
||||
[ 25, 22, 0, "Sub Zero Project" ],
|
||||
[ 25, 22, 50, "Endshow" ],
|
||||
[ 25, 23, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
112
events/Qbase2018.json
Normal file
112
events/Qbase2018.json
Normal file
|
@ -0,0 +1,112 @@
|
|||
[
|
||||
{
|
||||
"stage": "openair",
|
||||
"mc": "DV8",
|
||||
"channel": "487970506910203914",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-open-air",
|
||||
"sets": [
|
||||
[ 8, 17, 0, "Retrospect" ],
|
||||
[ 8, 18, 30, "Adrenalize" ],
|
||||
[ 8, 20, 0, "KELTEK" ],
|
||||
[ 8, 21, 0, "D-Block & S-te-Fan" ],
|
||||
[ 8, 22, 0, "Sound Rush" ],
|
||||
[ 8, 23, 0, "Coone" ],
|
||||
[ 9, 0, 0, "Atmozfears" ],
|
||||
[ 9, 1, 30, "Noisecontrollers" ],
|
||||
[ 9, 3, 0, "Ran-D & B-Front" ],
|
||||
[ 9, 4, 0, "Nothing" ],
|
||||
[ 9, 6, 30, "Sefa" ],
|
||||
[ 9, 7, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "hangar",
|
||||
"mc": "Livid & Nolz",
|
||||
"channel": "487970523784019979",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-hangar",
|
||||
"sets": [
|
||||
[ 8, 17, 0, "D-Attack" ],
|
||||
[ 8, 18, 30, "Rejecta" ],
|
||||
[ 8, 20, 0, "Myst vs. Degos & Re-Done" ],
|
||||
[ 8, 21, 30, "Deetox" ],
|
||||
[ 8, 22, 45, "Adaro" ],
|
||||
[ 9, 0, 0, "Frequencerz" ],
|
||||
[ 9, 1, 0, "Bass Chaserz" ],
|
||||
[ 9, 2, 0, "Nothing" ],
|
||||
[ 9, 2, 30, "E-Force" ],
|
||||
[ 9, 4, 0, "Nothing" ],
|
||||
[ 9, 5, 0, "Rebelion" ],
|
||||
[ 9, 6, 0, "Jason Payne & Apexx" ],
|
||||
[ 9, 7, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "thunderdome",
|
||||
"mc": "Da Mouth of Madness",
|
||||
"channel": "488016911838347276",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-thunderdome",
|
||||
"sets": [
|
||||
[ 8, 17, 0, "Waxweazle" ],
|
||||
[ 8, 18, 30, "Vince" ],
|
||||
[ 8, 20, 0, "MD&A" ],
|
||||
[ 8, 20, 30, "Predator" ],
|
||||
[ 8, 22, 0, "Evil Activities" ],
|
||||
[ 8, 23, 30, "Nosferatu" ],
|
||||
[ 9, 0, 30, "The Melodyst" ],
|
||||
[ 9, 1, 30, "Promo" ],
|
||||
[ 9, 3, 0, "I:GOR" ],
|
||||
[ 9, 4, 0, "Unexist" ],
|
||||
[ 9, 5, 0, "Andy The Core & Lady Dammage" ],
|
||||
[ 9, 6, 0, "Spitnoise" ],
|
||||
[ 9, 7, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "bkjn",
|
||||
"mc": "No-ID & RG",
|
||||
"channel": "488018562124873741",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-bkjn",
|
||||
"sets": [
|
||||
[ 8, 18, 0, "Hard Effectz vs. Stampede" ],
|
||||
[ 8, 19, 0, "Hyrule War vs. Mr. Ivex" ],
|
||||
[ 8, 20, 0, "Cryogenic" ],
|
||||
[ 8, 21, 0, "Para Italia vs. Aggressive" ],
|
||||
[ 8, 22, 0, "Super Trash Bros [LIVE]" ],
|
||||
[ 8, 22, 30, "Rob Gee & Lunatic present: BKJN Music Showcase" ],
|
||||
[ 8, 23, 45, "Sefa" ],
|
||||
[ 9, 0, 15, "Vandalism" ],
|
||||
[ 9, 1, 0, "Repix" ],
|
||||
[ 9, 2, 0, "Omkara [LIVE]" ],
|
||||
[ 9, 2, 30, "Dr. Peacock" ],
|
||||
[ 9, 3, 30, "Chaotic Hostility & Chrono" ],
|
||||
[ 9, 4, 30, "The Vizitor vs. MBK" ],
|
||||
[ 9, 5, 30, "ELITE [LIVE]" ],
|
||||
[ 9, 6, 0, "Nothing"]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "evolution",
|
||||
"mc": "DL",
|
||||
"channel": "488019382094528512",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-evolution",
|
||||
"sets": [
|
||||
[ 8, 20, 0, "The Pitcher & Retrospect" ],
|
||||
[ 8, 21, 45, "Coone & Ruthless" ],
|
||||
[ 8, 22, 30, "Nightbreed Crew" ],
|
||||
[ 9, 0, 0, "DJ The Prophet & Devin Wild" ],
|
||||
[ 9, 1, 0, "Deetox & Sound Rush" ],
|
||||
[ 9, 2, 30, "D-Fence & Panic" ],
|
||||
[ 9, 3, 45, "Rooler" ],
|
||||
[ 9, 5, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
30
events/Qbase2018_Single.json
Normal file
30
events/Qbase2018_Single.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
[
|
||||
{
|
||||
"stage": "qbase",
|
||||
"mc": "DV8 & Livid & Nolz",
|
||||
"channel": "487537296581722112",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/radio/",
|
||||
"sets": [
|
||||
[ 8, 17, 0, "Retrospect / D-Attack" ],
|
||||
[ 8, 18, 30, "Adrenalize / Rejecta" ],
|
||||
[ 8, 20, 0, "KELTEK / Myst vs. Degos & Re-Done" ],
|
||||
[ 8, 21, 0, "D-Block & S-te-Fan / Myst vs. Degos & Re-Done" ],
|
||||
[ 8, 21, 30, "D-Block & S-te-Fan / Deetox" ],
|
||||
[ 8, 22, 0, "Sound Rush / Deetox" ],
|
||||
[ 8, 22, 45, "Sound Rush / Adaro" ],
|
||||
[ 8, 23, 0, "Coone / Adaro" ],
|
||||
[ 9, 0, 0, "Atmozfears / Frequencerz" ],
|
||||
[ 9, 1, 0, "Atmozfears / Bass Chaserz" ],
|
||||
[ 9, 1, 30, "Noisecontrollers / Bass Chaserz" ],
|
||||
[ 9, 2, 0, "Noisecontrollers" ],
|
||||
[ 9, 2, 30, "Noisecontrollers / E-Force" ],
|
||||
[ 9, 3, 0, "Ran-D & B-Front / E-Force" ],
|
||||
[ 9, 4, 0, "???" ],
|
||||
[ 9, 5, 0, "Rebelion" ],
|
||||
[ 9, 6, 0, "Jason Payne & Apexx" ],
|
||||
[ 9, 6, 30, "Sefa / Jason Payne & Apexx" ],
|
||||
[ 9, 7, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
46
events/Qbase2018_Two.json
Normal file
46
events/Qbase2018_Two.json
Normal file
|
@ -0,0 +1,46 @@
|
|||
[
|
||||
{
|
||||
"stage": "openair",
|
||||
"mc": "DV8",
|
||||
"channel": "487970506910203914",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-open-air",
|
||||
"sets": [
|
||||
[ 8, 17, 0, "Retrospect" ],
|
||||
[ 8, 18, 30, "Adrenalize" ],
|
||||
[ 8, 20, 0, "KELTEK" ],
|
||||
[ 8, 21, 0, "D-Block & S-te-Fan" ],
|
||||
[ 8, 22, 0, "Sound Rush" ],
|
||||
[ 8, 23, 0, "Coone" ],
|
||||
[ 9, 0, 0, "Atmozfears" ],
|
||||
[ 9, 1, 30, "Noisecontrollers" ],
|
||||
[ 9, 3, 0, "Ran-D & B-Front" ],
|
||||
[ 9, 4, 0, "Nothing" ],
|
||||
[ 9, 6, 30, "Sefa" ],
|
||||
[ 9, 7, 0, "Nothing" ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"stage": "hangar",
|
||||
"mc": "Livid & Nolz",
|
||||
"channel": "487970523784019979",
|
||||
"emoji": ":airplane:",
|
||||
"url": "https://www.q-dance.com/en/videos/q-dance-live-hangar",
|
||||
"sets": [
|
||||
[ 8, 17, 0, "D-Attack" ],
|
||||
[ 8, 18, 30, "Rejecta" ],
|
||||
[ 8, 20, 0, "Myst vs. Degos & Re-Done" ],
|
||||
[ 8, 21, 30, "Deetox" ],
|
||||
[ 8, 22, 45, "Adaro" ],
|
||||
[ 9, 0, 0, "Frequencerz" ],
|
||||
[ 9, 1, 0, "Bass Chaserz" ],
|
||||
[ 9, 2, 0, "Nothing" ],
|
||||
[ 9, 2, 30, "E-Force" ],
|
||||
[ 9, 4, 0, "Nothing" ],
|
||||
[ 9, 5, 0, "Rebelion" ],
|
||||
[ 9, 6, 0, "Jason Payne & Apexx" ],
|
||||
[ 9, 7, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
23
events/Qlimax2018.json
Normal file
23
events/Qlimax2018.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"mc": "Villain",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":flag_nl:",
|
||||
"url": "https://www.q-dance.com/en/",
|
||||
"sets": [
|
||||
[ 24, 21, 30, "Luna" ],
|
||||
[ 24, 22, 30, "Sound Rush" ],
|
||||
[ 24, 23, 30, "Coone" ],
|
||||
[ 25, 0, 30, "Bass Modulators" ],
|
||||
[ 25, 1, 30, "Wildstylez (Live)" ],
|
||||
[ 25, 2, 1, "Sub Zero Project" ],
|
||||
[ 25, 3, 16, "Tweekacore" ],
|
||||
[ 25, 4, 1, "Phuture Noize" ],
|
||||
[ 25, 5, 1, "Sub Zero Project & Phuture Noize" ],
|
||||
[ 25, 5, 16, "B-Freqz" ],
|
||||
[ 25, 6, 1, "Dr. Peacock" ],
|
||||
[ 25, 7, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
31
events/Qlimax2019.json
Normal file
31
events/Qlimax2019.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:qlimax:438815681706721281>",
|
||||
"url": "https://www.q-dance.com/en/events/qlimax/qlimax-2019/live",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 4,
|
||||
"sets": [
|
||||
[ 2019, 11, 23, 21, 45, "The Qreator" ],
|
||||
[ 2019, 11, 23, 22, 45, "KELTEK" ],
|
||||
[ 2019, 11, 23, 23, 30, "Sound Rush" ],
|
||||
[ 2019, 11, 24, 0, 30, "B-Front" ],
|
||||
[ 2019, 11, 24, 0, 40, "D-Block & S-te-Fan" ],
|
||||
[ 2019, 11, 24, 1, 30, "Headhunterz" ],
|
||||
[ 2019, 11, 24, 2, 30, "B-Front" ],
|
||||
[ 2019, 11, 24, 3, 30, "Ran-D \"We Rule The Night\"" ],
|
||||
[ 2019, 11, 24, 4, 15, "D-Sturb" ],
|
||||
[ 2019, 11, 24, 5, 0, "Rejecta (LIVE)" ],
|
||||
[ 2019, 11, 24, 5, 30, "Radical Redemption" ],
|
||||
[ 2019, 11, 24, 6, 15, "Miss K8" ],
|
||||
[ 2019, 11, 24, 7, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Watch the livestream here: **<https://www.q-dance.com/en/events/qlimax/qlimax-2019/live>** (Note: This is a paid premium livestream!)",
|
||||
"^\\.(mc|host)$": ":microphone: The MC is **Villan**, and **Nolz** for the final 2 sets",
|
||||
"^\\.restream$": "The Qlimax stream is a **premium** livestream. As such, we consider all unofficial free re-streams as piracy. Do not post or ask for restreams.",
|
||||
"^\\.qreator$": "It is not confirmed yet who *The Qreator* is. It was not revealed during their set."
|
||||
}
|
||||
}
|
||||
]
|
29
events/Qonnect.json
Normal file
29
events/Qonnect.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
[
|
||||
{
|
||||
"stage": "The Studio",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:qdance:328585093553586176>",
|
||||
"url": "https://www.q-dance.com/en/videos/livestream-qonnect",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 3,
|
||||
"sets": [
|
||||
[ 2020, 3, 21, 13, 0, "Demi Kanon" ],
|
||||
[ 2020, 3, 21, 14, 0, "Primeshock" ],
|
||||
[ 2020, 3, 21, 15, 0, "Clockartz" ],
|
||||
[ 2020, 3, 21, 16, 0, "Adrenalize" ],
|
||||
[ 2020, 3, 21, 16, 45, "Audiotricz" ],
|
||||
[ 2020, 3, 21, 17, 50, "Outsiders" ],
|
||||
[ 2020, 3, 21, 18, 45, "Sound Rush" ],
|
||||
[ 2020, 3, 21, 19, 45, "Audiofreq" ],
|
||||
[ 2020, 3, 21, 20, 0, "Headhunterz" ],
|
||||
[ 2020, 3, 21, 21, 0, "Devin Wild" ],
|
||||
[ 2020, 3, 21, 22, 10, "Hard Driver" ],
|
||||
[ 2020, 3, 21, 23, 10, "Dr. Peacock" ],
|
||||
[ 2020, 3, 21, 23, 40 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.q-dance.com/en/videos/livestream-qonnect>**",
|
||||
"^\\.(mc|host)$": ":microphone: The hosts between 13:00 and 19:00 CET are **Tellem** and **Adrenalize**. Between 19:00 and 23:00 CET it's **E-Life** and **Audiofreq**."
|
||||
}
|
||||
}
|
||||
]
|
33
events/Qonnect2.json
Normal file
33
events/Qonnect2.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
[
|
||||
{
|
||||
"stage": "The Studio",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:qdance:328585093553586176>",
|
||||
"url": "https://live.q-dance.com/",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 0,
|
||||
"sets": [
|
||||
[ 2020, 4, 4, 18, 5, "Degos & Re-Done" ],
|
||||
[ 2020, 4, 4, 18, 35, "MYST" ],
|
||||
[ 2020, 4, 4, 19, 15, "Hard Driver" ],
|
||||
[ 2020, 4, 4, 19, 45, "Apexx" ],
|
||||
[ 2020, 4, 4, 20, 20, "B-Front" ],
|
||||
[ 2020, 4, 4, 20, 55, "Adaro" ],
|
||||
[ 2020, 4, 4, 21, 30, "Bloodlust" ],
|
||||
[ 2020, 4, 4, 22, 5, "Vertile" ],
|
||||
[ 2020, 4, 4, 22, 35, "Rejecta" ],
|
||||
[ 2020, 4, 4, 23, 15, "The Qreator: The Ultimate QAPITAL Mix" ],
|
||||
[ 2020, 4, 4, 23, 45, "Act of Rage" ],
|
||||
[ 2020, 4, 5, 0, 20, "Rebelion" ],
|
||||
[ 2020, 4, 5, 0, 50, "Rooler" ],
|
||||
[ 2020, 4, 5, 1, 25, "Invector" ],
|
||||
[ 2020, 4, 5, 1, 55, "Thyron" ],
|
||||
[ 2020, 4, 5, 2, 25, "Imperatorz" ],
|
||||
[ 2020, 4, 5, 3, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://live.q-dance.com/>**",
|
||||
"^\\.(mc|host)$": ":microphone: The hosts are Tellem & Livid."
|
||||
}
|
||||
}
|
||||
]
|
29
events/Qonnect3.json
Normal file
29
events/Qonnect3.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
[
|
||||
{
|
||||
"stage": "The Studio",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:qdance:328585093553586176>",
|
||||
"url": "https://live.q-dance.com/",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 3,
|
||||
"sets": [
|
||||
[ 2020, 4, 11, 13, 0, "Retrospect" ],
|
||||
[ 2020, 4, 11, 14, 0, "Villain" ],
|
||||
[ 2020, 4, 11, 15, 0, "Coone" ],
|
||||
[ 2020, 4, 11, 16, 0, "Da Tweekaz" ],
|
||||
[ 2020, 4, 11, 17, 0, "Zatox" ],
|
||||
[ 2020, 4, 11, 18, 0, "Darkraver (Vinyl Mix at Six)" ],
|
||||
[ 2020, 4, 11, 19, 0, "KELTEK" ],
|
||||
[ 2020, 4, 11, 20, 0, "Headhunterz" ],
|
||||
[ 2020, 4, 11, 21, 0, "Atmozfears" ],
|
||||
[ 2020, 4, 11, 22, 0, "Crypsis" ],
|
||||
[ 2020, 4, 11, 23, 0, "Sefa (LIVE)" ],
|
||||
[ 2020, 4, 12, 0, 0 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://live.q-dance.com/>**",
|
||||
"^\\.(mc|host)$": ":microphone: The hosts between 13:00 and 18:00 are Adrenalize and Tellem. During 18:00 and 23:30 it's Audiofreq & E-life.",
|
||||
"^\\.(hidechat|removechat|fuckchat)$": ":thinking: Press F12 and paste this into the console to hide the chat on live.q-dance.com: ```sc=document.getElementById(\"scrollContainer\");sc.classList.remove(\"col-l--9\");sc.classList.remove(\"col-m--8\");sc.classList.add(\"col-l--12\");sc.classList.remove(\"col-m--12\");```"
|
||||
}
|
||||
}
|
||||
]
|
26
events/Reverze2019.json
Normal file
26
events/Reverze2019.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
[
|
||||
{
|
||||
"stage": "sportpaleis",
|
||||
"mc": "Villain & Chucky",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":fire:",
|
||||
"url": "https://www.youtube.com/user/bassevents/live",
|
||||
"sets": [
|
||||
[ 2019, 2, 23, 21, 30, "Sound Rush" ],
|
||||
[ 2019, 2, 23, 22, 30, "Sephyx vs Devin Wild" ],
|
||||
[ 2019, 2, 23, 23, 30, "Refuzion (recorded from 20:30)" ],
|
||||
[ 2019, 2, 24, 0, 0, "Headhunterz" ],
|
||||
[ 2019, 2, 24, 1, 0, "KELTEK (LIVE)" ],
|
||||
[ 2019, 2, 24, 1, 30, "Coone & Brennan Heart" ],
|
||||
[ 2019, 2, 24, 2, 30, "Tweekacore (LIVE)" ],
|
||||
[ 2019, 2, 24, 3, 0, "Mark with a K & Warface (LIVE)" ],
|
||||
[ 2019, 2, 24, 3, 30, "Reverze Flashback by Dark-E & Pat B" ],
|
||||
[ 2019, 2, 24, 4, 15, "Phuture Noize \"Black Mirror Society\" (LIVE)" ],
|
||||
[ 2019, 2, 24, 4, 45, "Nothing" ]
|
||||
],
|
||||
"faq": [
|
||||
"D-Block & S-te-Fan's \"Ghost Stories\" set is not livestreamed. It is instead replaced with the first half of Refuzion's set.",
|
||||
"Yes, someone is definitely recording this. Including Bass Events."
|
||||
]
|
||||
}
|
||||
]
|
29
events/Reverze2020.json
Normal file
29
events/Reverze2020.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
[
|
||||
{
|
||||
"stage": "sportpaleis",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": "<:bassevents:685859019973460136>",
|
||||
"url": "https://www.youtube.com/watch?v=QP0AGJ0avRs",
|
||||
"unconfirmed": false,
|
||||
"streamdelay": 3,
|
||||
"sets": [
|
||||
[ 2020, 3, 7, 21, 0, "Refuzion" ],
|
||||
[ 2020, 3, 7, 21, 30, "Mandy" ],
|
||||
[ 2020, 3, 7, 22, 15, "Keltek vs. Sound Rush" ],
|
||||
[ 2020, 3, 7, 23, 15, "Psyko Punkz" ],
|
||||
[ 2020, 3, 8, 0, 0, "Brennan Heart: 15 Years Reverze Special" ],
|
||||
[ 2020, 3, 8, 0, 45, "The Elite", "Da Tweekaz, Coone, Hard Driver" ],
|
||||
[ 2020, 3, 8, 1, 45, "D-Block & S-te-Fan" ],
|
||||
[ 2020, 3, 8, 2, 45, "Sub Zero Project: Rave Into Space (LIVE)" ],
|
||||
[ 2020, 3, 8, 3, 15, "15 Years Reverze Flashback", "Pat B, Dark-E, Mark with a K & MC Chucky" ],
|
||||
[ 2020, 3, 8, 4, 0, "Warface & D-Sturb: Synchronised" ],
|
||||
[ 2020, 3, 8, 4, 45, "Minus Militia: The Code of Conduct (LIVE)" ],
|
||||
[ 2020, 3, 8, 5, 15 ]
|
||||
],
|
||||
"responses": {
|
||||
"^\\.(url|stream|link|watch)$": ":tv: Tune in to the livestream here: **<https://www.youtube.com/watch?v=QP0AGJ0avRs>**",
|
||||
"^\\.(mc|host)$": ":microphone: The MC is **Villain**.",
|
||||
"^\\.(lotto|arena|stages)$": ":warning: **ONLY** the Sportpaleis is being streamed, the Lotto Arena stage is **NOT** being streamed."
|
||||
}
|
||||
}
|
||||
]
|
47
events/TomorrowlandDominator2018.json
Normal file
47
events/TomorrowlandDominator2018.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
[
|
||||
{
|
||||
"stage": "tomorrowland",
|
||||
"mc": "Villain",
|
||||
"channel": "469913276592029706",
|
||||
"emoji": ":one:",
|
||||
"url": "https://live.tomorrowland.com/",
|
||||
"sets": [
|
||||
[ 21, 13, 0, "Demi Kanon" ],
|
||||
[ 21, 14, 0, "Mandy & Adrenalize" ],
|
||||
[ 21, 15, 0, "Audiotricz" ],
|
||||
[ 21, 16, 0, "Da Tweekaz" ],
|
||||
[ 21, 17, 0, "Wildstylez" ],
|
||||
[ 21, 18, 0, "Brennan Heart" ],
|
||||
[ 21, 19, 0, "Atmozfears" ],
|
||||
[ 21, 20, 0, "Zatox" ],
|
||||
[ 21, 21, 0, "Ran-D" ],
|
||||
[ 21, 22, 0, "Sub Zero Project" ],
|
||||
[ 21, 23, 0, "Korsakoff" ],
|
||||
[ 22, 0, 0, "Nothing" ],
|
||||
|
||||
[ 28, 12, 0, "Ransom" ],
|
||||
[ 28, 13, 0, "Refuzion" ],
|
||||
[ 28, 14, 0, "Sound Rush" ],
|
||||
[ 28, 15, 0, "Wasted Penguinz" ],
|
||||
[ 28, 16, 0, "Mark With a K & MC Chucky" ],
|
||||
[ 28, 17, 0, "Bass Modulators" ],
|
||||
[ 28, 18, 0, "Code Black" ],
|
||||
[ 28, 19, 0, "Noisecontrollers" ],
|
||||
[ 28, 20, 0, "Psyko Punkz" ],
|
||||
[ 28, 21, 0, "Frequencerz" ],
|
||||
[ 28, 22, 0, "B-Front" ],
|
||||
[ 28, 23, 0, "Partyraiser" ],
|
||||
[ 29, 0, 0, "Nothing" ],
|
||||
|
||||
[ 29, 16, 0, "Da Tweekaz" ],
|
||||
[ 29, 17, 0, "Wildstylez" ],
|
||||
[ 29, 18, 0, "Coone" ],
|
||||
[ 29, 19, 0, "Pablo Discobar" ],
|
||||
[ 29, 19, 30, "Darren Styles" ],
|
||||
[ 29, 20, 15, "Zatox" ],
|
||||
[ 29, 21, 15, "Coone vs. Hard Driver" ],
|
||||
[ 29, 22, 15, "Gunz for Hire" ],
|
||||
[ 29, 23, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
21
events/Tweekaz.json
Normal file
21
events/Tweekaz.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"mc": "Villain",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":duck::wine_glass:",
|
||||
"url": "https://www.q-dance.com/en/radio/",
|
||||
"sets": [
|
||||
[ 2019, 1, 26, 23, 0, "Sephyx & Refuzion" ],
|
||||
[ 2019, 1, 27, 0, 0, "Code Black" ],
|
||||
[ 2019, 1, 27, 1, 0, "Da Tweekaz" ],
|
||||
[ 2019, 1, 27, 2, 0, "Coone, Hard Driver & Da Tweekaz \"Dirty Workz Elite\"" ],
|
||||
[ 2019, 1, 27, 3, 15, "D-Block & S-te-Fan" ],
|
||||
[ 2019, 1, 27, 4, 15, "Tweekacore" ],
|
||||
[ 2019, 1, 27, 4, 45, "Sub Zero Project vs. Da Tweekaz" ],
|
||||
[ 2019, 1, 27, 5, 30, "D-Sturb" ],
|
||||
[ 2019, 1, 27, 6, 15, "Destructive Tendencies vs. Da Tweekaz" ],
|
||||
[ 2019, 1, 27, 7, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
28
events/WOWWOW2018.json
Normal file
28
events/WOWWOW2018.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
[
|
||||
{
|
||||
"stage": "mainstage",
|
||||
"mc": "Villain",
|
||||
"channel": "319525278978277407",
|
||||
"emoji": ":champagne::tada:",
|
||||
"url": "https://www.q-dance.com/en/radio/",
|
||||
"sets": [
|
||||
[ 2018, 12, 31, 21, 0, "Max Enforcer" ],
|
||||
[ 2018, 12, 31, 22, 0, "Mark with a K presents: Belgium" ],
|
||||
[ 2018, 12, 31, 23, 0, "KELTEK: Best euphoric breakthrough" ],
|
||||
[ 2018, 12, 31, 23, 40, "Hardstyle Top 10" ],
|
||||
[ 2019, 1, 1, 0, 0, "Brennan Heart" ],
|
||||
[ 2019, 1, 1, 0, 45, "Best of X-Qlusive Frequencerz" ],
|
||||
[ 2019, 1, 1, 1, 30, "POWER HOUR in 10 minutes" ],
|
||||
[ 2019, 1, 1, 1, 40, "Noisecontrollers & Audiotricz presents: Spirit of Hardstyle" ],
|
||||
[ 2019, 1, 1, 2, 20, "Atmozfears: Q-BASE anthem 2018" ],
|
||||
[ 2019, 1, 1, 3, 0, "Ran-D presents: Roughstate 2018" ],
|
||||
[ 2019, 1, 1, 3, 30, "ZazaFront: X-Qlusive Holland set" ],
|
||||
[ 2019, 1, 1, 4, 0, "Sub Zero Project: Qlimax anthem 2018" ],
|
||||
[ 2019, 1, 1, 4, 15, "Rejecta: Best RAW Talent" ],
|
||||
[ 2019, 1, 1, 4, 45, "Phuture Noize: QAPITAL anthem 2018" ],
|
||||
[ 2019, 1, 1, 5, 15, "Rebelion: QAPITAL anthem 2019" ],
|
||||
[ 2019, 1, 1, 6, 0, "Partyraiser presents: Hardcore 2018" ],
|
||||
[ 2019, 1, 1, 7, 0, "Nothing" ]
|
||||
]
|
||||
}
|
||||
]
|
4
index.js
Normal file
4
index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
var Startup = require("./Startup");
|
||||
|
||||
let startup = new Startup();
|
||||
startup.run();
|
29
modules/autoreact.js
Normal file
29
modules/autoreact.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
const discord = require("discord.js");
|
||||
|
||||
class AutoReactModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (edited) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.content.match(new RegExp(this.config.match, "i"))) {
|
||||
msg.react(this.config.emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AutoReactModule;
|
513
modules/event.js
Normal file
513
modules/event.js
Normal file
|
@ -0,0 +1,513 @@
|
|||
var fs = require("fs");
|
||||
var moment = require("moment");
|
||||
|
||||
var cmdsplit = require("./../cmdsplit");
|
||||
|
||||
class EventSchedule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.event = config;
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
|
||||
this.lastNotFound = new Date(1970, 1, 1);
|
||||
this.lastNow = new Date(1970, 1, 1);
|
||||
|
||||
this.loadSchedule(this.event.file);
|
||||
}
|
||||
|
||||
loadSchedule(filename)
|
||||
{
|
||||
console.log('Loading schedule: "' + filename + '"');
|
||||
|
||||
this.schedule = JSON.parse(fs.readFileSync(filename));
|
||||
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
let stage = this.schedule[i];
|
||||
|
||||
console.log("Event channel: " + stage.channel);
|
||||
this.client.channels.fetch(stage.channel).then(channel => {
|
||||
stage.channel = channel;
|
||||
this.updateChannel(stage);
|
||||
}).catch(() => {
|
||||
console.error("Unable to find channel for stage \"" + stage.stage + "\"");
|
||||
});
|
||||
|
||||
var newResponses = [];
|
||||
for (var expression in stage.responses) {
|
||||
let newResponse = {
|
||||
match: new RegExp(expression, "i"),
|
||||
msg: stage.responses[expression]
|
||||
};
|
||||
newResponses.push(newResponse);
|
||||
}
|
||||
stage.responses = newResponses;
|
||||
|
||||
stage.channelExtra = null;
|
||||
if (stage.extra_channel !== undefined) {
|
||||
this.client.channels.fetch(stage.extra_channel).then(channel => {
|
||||
stage.channelExtra = channel;
|
||||
}).catch(() => {
|
||||
console.error("Unable to find channel for stage \"" + stage.stage + "\"");
|
||||
});
|
||||
}
|
||||
|
||||
var streamDelay = stage.streamdelay;
|
||||
|
||||
for (var j = 0; j < stage.sets.length; j++) {
|
||||
var set = stage.sets[j];
|
||||
var dateArray = set.slice(0, 5);
|
||||
dateArray[1] -= 1; // months are 0-indexed, for some reason. even in the moment library!
|
||||
|
||||
var setDate = moment(dateArray).add(streamDelay, 'm');
|
||||
var newSet = {
|
||||
date: setDate,
|
||||
name: set[5],
|
||||
report: moment() > setDate,
|
||||
report_5min: moment().add(5, 'm') > setDate,
|
||||
nothing: (set[5] === undefined || set[5] == "Nothing"),
|
||||
who: set[6]
|
||||
};
|
||||
|
||||
stage.sets[j] = newSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getStage(stage)
|
||||
{
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
var s = this.schedule[i];
|
||||
if (s.stage == stage) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getStageByChannel(channel)
|
||||
{
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
var s = this.schedule[i];
|
||||
if (s.channel == channel) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findSets(query)
|
||||
{
|
||||
query = query.toLowerCase();
|
||||
|
||||
var ret = [];
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
var stage = this.schedule[i];
|
||||
for (var j = 0; j < stage.sets.length; j++) {
|
||||
var set = stage.sets[j];
|
||||
if (set.nothing) {
|
||||
continue;
|
||||
}
|
||||
if (set.name.toLowerCase().indexOf(query) != -1 || (set.who && set.who.toLowerCase().indexOf(query) != -1)) {
|
||||
ret.push({
|
||||
set: set,
|
||||
stage: stage
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
getCurrentSet(stage)
|
||||
{
|
||||
var date = new Date();
|
||||
|
||||
for (var i = 0; i < stage.sets.length; i++) {
|
||||
var set = stage.sets[i];
|
||||
if (date < set.date) {
|
||||
if (i == 0) {
|
||||
// Happens if no set has started yet on this stage
|
||||
return null;
|
||||
}
|
||||
return stage.sets[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Happens if the last set has been played
|
||||
return stage.sets[stage.sets.length - 1];
|
||||
}
|
||||
|
||||
getNextSet(stage)
|
||||
{
|
||||
var date = new Date();
|
||||
|
||||
for (var i = 0; i < stage.sets.length; i++) {
|
||||
var set = stage.sets[i];
|
||||
if (date < set.date) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
// Happens if this is the final set on this stage
|
||||
return null;
|
||||
}
|
||||
|
||||
getNextLiveSet(stage)
|
||||
{
|
||||
var date = new Date();
|
||||
|
||||
for (var i = 0; i < stage.sets.length; i++) {
|
||||
var set = stage.sets[i];
|
||||
if (date < set.date && !set.nothing) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
// Happens if this is the final set on this stage
|
||||
return null;
|
||||
}
|
||||
|
||||
onTick()
|
||||
{
|
||||
var date = moment();
|
||||
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
var stage = this.schedule[i];
|
||||
|
||||
var current = this.getCurrentSet(stage);
|
||||
if (current !== null) {
|
||||
if (!current.report) {
|
||||
current.report = true;
|
||||
if (!current.nothing) {
|
||||
console.log("Starting now: " + current.name);
|
||||
var msg = ":red_circle: STARTING NOW: **" + current.name + "**";
|
||||
stage.channel.send(msg);
|
||||
if (stage.channelExtra) {
|
||||
stage.channelExtra.send(msg);
|
||||
}
|
||||
} else {
|
||||
console.log("Stream is not live anymore.");
|
||||
var next = this.getNextSet(stage);
|
||||
if (next !== null && !next.nothing) {
|
||||
var msg = ":no_entry_sign: Stream is no longer live. Next set is at **" + this.getTimeString(next.date) + "**!";
|
||||
stage.channel.send(msg);
|
||||
if (stage.channelExtra) {
|
||||
stage.channelExtra.send(msg);
|
||||
}
|
||||
} else {
|
||||
var msg = ":tada: This is the end of the livestream. Thanks for watching.";
|
||||
stage.channel.send(msg);
|
||||
if (stage.channelExtra) {
|
||||
stage.channelExtra.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.updateChannel(stage);
|
||||
}
|
||||
}
|
||||
|
||||
var next = this.getNextSet(stage);
|
||||
if (next !== null && !next.nothing) {
|
||||
if (date.clone().add(5, 'm') > next.date && !next.report_5min) {
|
||||
next.report_5min = true;
|
||||
console.log("Starting in 5 minutes: " + next.name);
|
||||
var msg = ":warning: **" + next.name + "** starts in 5 minutes!";
|
||||
stage.channel.send(msg);
|
||||
if (stage.channelExtra) {
|
||||
stage.channelExtra.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCmdCurrent(msg) { this.onCmdNp(msg); }
|
||||
onCmdNow(msg) { this.onCmdNp(msg); }
|
||||
onCmdNp(msg)
|
||||
{
|
||||
var stage = this.getStageByChannel(msg.channel);
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
var current = this.getCurrentSet(stage);
|
||||
if (current !== null && !current.nothing) {
|
||||
if (current.who) {
|
||||
msg.channel.send(":red_circle: Now playing: **" + current.name + "**, started <t:" + current.date.unix() + ":R>! (" + current.who + ")");
|
||||
} else {
|
||||
msg.channel.send(":red_circle: Now playing: **" + current.name + "**, started <t:" + current.date.unix() + ":R>!");
|
||||
}
|
||||
} else {
|
||||
msg.channel.send("Nobody's playing right now.");
|
||||
}
|
||||
}
|
||||
|
||||
onCmdNext(msg)
|
||||
{
|
||||
var stage = this.getStageByChannel(msg.channel);
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
var next = this.getNextSet(stage);
|
||||
if (next) {
|
||||
var localTime = "**" + this.getTimeString(next.date) + "**";
|
||||
localTime += " (<t:" + next.date.unix() + ":R>)";
|
||||
|
||||
if (next.name) {
|
||||
if (next.who) {
|
||||
msg.channel.send(":arrow_forward: Next up: **" + next.name + "**, at " + localTime + " (" + next.who + ")");
|
||||
} else {
|
||||
msg.channel.send(":arrow_forward: Next up: **" + next.name + "**, at " + localTime);
|
||||
}
|
||||
} else {
|
||||
msg.channel.send(":arrow_forward: The stream ends at " + localTime);
|
||||
}
|
||||
} else {
|
||||
msg.channel.send("There's nothing playing next.");
|
||||
}
|
||||
}
|
||||
|
||||
getScheduleString(stage, limit, starttime)
|
||||
{
|
||||
var ret = "";
|
||||
|
||||
if (stage.unconfirmed) {
|
||||
ret = ":warning: **Note:** Set times are not confirmed!\n";
|
||||
}
|
||||
|
||||
if (!starttime) {
|
||||
starttime = moment(stage.sets[0].date).clone().subtract(1, 'm');
|
||||
}
|
||||
|
||||
var lines = 0;
|
||||
for (var i = 0; i < stage.sets.length; i++) {
|
||||
var set = stage.sets[i];
|
||||
if (starttime > set.date) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (limit && lines == limit) {
|
||||
ret += "(limited, use `.fullschedule` for the full schedule)\n";
|
||||
break;
|
||||
}
|
||||
lines++;
|
||||
|
||||
if (set.nothing) {
|
||||
ret += "- <t:" + set.date.unix() + ":t> (<t:" + set.date.unix() + ":R>), the stream will be offline :no_entry_sign:\n";
|
||||
} else {
|
||||
if (set.who) {
|
||||
ret += "- <t:" + set.date.unix() + ":t> (<t:" + set.date.unix() + ":R>): **" + set.name + "** (" + set.who + ")\n";
|
||||
} else {
|
||||
ret += "- <t:" + set.date.unix() + ":t> (<t:" + set.date.unix() + ":R>): **" + set.name + "**\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lines == 0) {
|
||||
ret = "We have nothing left! :frowning:";
|
||||
} else if (limit) {
|
||||
ret = ":calendar_spiral: Next " + limit + " sets are:\n" + ret.trim();
|
||||
} else {
|
||||
ret = ":calendar_spiral: The full schedule:\n" + ret.trim();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
onCmdTimetable(msg) { this.onCmdSchedule(msg); }
|
||||
onCmdSched(msg) { this.onCmdSchedule(msg); }
|
||||
onCmdSchedule(msg)
|
||||
{
|
||||
var stage = this.getStageByChannel(msg.channel);
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.channel.send(this.getScheduleString(stage, 5, moment()));
|
||||
}
|
||||
|
||||
onCmdFullSched(msg) { this.onCmdFullSchedule(msg); }
|
||||
onCmdFullSchedule(msg)
|
||||
{
|
||||
var stage = this.getStageByChannel(msg.channel);
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
var text = this.getScheduleString(stage);
|
||||
while (text.length > 0) {
|
||||
msg.author.send(text.substr(0, 2000)).catch(console.error);
|
||||
text = text.substr(2000);
|
||||
}
|
||||
|
||||
msg.reply("I've DM'd you the full schedule.");
|
||||
}
|
||||
|
||||
onCmdFind(msg)
|
||||
{
|
||||
var query = Array.from(arguments).slice(1).join(" ").trim();
|
||||
if (query.length < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
var results = this.findSets(query);
|
||||
|
||||
var ret = "";
|
||||
if (results.length == 0) {
|
||||
ret = "I found nothing :frowning:";
|
||||
// Avoid spamming "I found nothing" when jokers do .find a meaning of life
|
||||
var now = new Date();
|
||||
if ((now - this.lastNotFound) < 60 * 1000) {
|
||||
return;
|
||||
}
|
||||
this.lastNotFound = now;
|
||||
} else {
|
||||
var date = new Date();
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var res = results[i];
|
||||
|
||||
var localTime = "**" + this.getTimeString(res.set.date) + "**";
|
||||
localTime += " (<t:" + res.set.date.unix() + ":R>)";
|
||||
|
||||
var stageMessage = "";
|
||||
if (this.schedule.length > 1) {
|
||||
stageMessage = " on **" + res.stage.stage + "** " + res.stage.emoji + " stage!";
|
||||
}
|
||||
|
||||
if (date > res.date) {
|
||||
ret += res.set.name + " already played on <t:" + res.set.date.unix() + ":F>\n";
|
||||
} else {
|
||||
ret += res.set.name + " plays on <t:" + res.set.date.unix() + ":F>\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msg.channel.send(":calendar_spiral: " + ret.trim());
|
||||
}
|
||||
|
||||
onCmdReloadSchedule(msg)
|
||||
{
|
||||
if (!this.bot.isAdmin(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadSchedule(this.event.file);
|
||||
msg.reply("schedule reloaded!");
|
||||
}
|
||||
|
||||
onMessage(msg)
|
||||
{
|
||||
var inStage = false;
|
||||
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
var stage = this.schedule[i];
|
||||
if (stage.channel.id != msg.channel.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inStage = true;
|
||||
|
||||
for (var j = 0; j < stage.responses.length; j++) {
|
||||
var r = stage.responses[j];
|
||||
var match = msg.content.match(r.match);
|
||||
if (match) {
|
||||
var sendMessage = r.msg;
|
||||
for (var k = 0; k < match.length; k++) {
|
||||
sendMessage = sendMessage.replace("$" + k, match[k]);
|
||||
}
|
||||
msg.channel.send(sendMessage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isCommand = msg.content.startsWith(".");
|
||||
|
||||
var parse = [];
|
||||
if (isCommand) {
|
||||
parse = cmdsplit(msg.content);
|
||||
}
|
||||
|
||||
// Outside-channel schedule command
|
||||
if (isCommand && this.schedule.length > 1 && !inStage && (parse[0] == ".schedule" || parse[0] == ".timetable" || parse[0] == ".sched" || parse[0] == ".current" || parse[0] == ".now")) {
|
||||
// Avoid spamming long .now message when jokers spam .now
|
||||
var now = new Date();
|
||||
if ((now - this.lastNow) < 60 * 1000) {
|
||||
return true;
|
||||
}
|
||||
this.lastNow = now;
|
||||
|
||||
var ret = "**LIVE**\n";
|
||||
for (var i = 0; i < this.schedule.length; i++) {
|
||||
var stage = this.schedule[i];
|
||||
|
||||
var current = this.getCurrentSet(stage);
|
||||
var next = this.getNextSet(stage);
|
||||
|
||||
if (current === null && next !== null && next.date.date() != moment(now).date()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current !== null && !current.nothing) {
|
||||
ret += stage.emoji + " " + stage.stage + ": **" + current.name + "**";
|
||||
} else {
|
||||
ret += stage.emoji + " " + stage.stage + ": Not live";
|
||||
}
|
||||
|
||||
if (next !== null && !next.nothing) {
|
||||
ret += ", next: " + next.name;
|
||||
} else {
|
||||
ret += ".";
|
||||
}
|
||||
|
||||
ret += " " + stage.channel.toString() + "\n";
|
||||
}
|
||||
msg.channel.send(ret.trim());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getTimeString(date)
|
||||
{
|
||||
return "<t:" + date.unix() + ":t>";
|
||||
}
|
||||
|
||||
updateChannel(stage)
|
||||
{
|
||||
if (typeof(stage.channel) == "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
var line = "";
|
||||
|
||||
var current = this.getCurrentSet(stage);
|
||||
var next = this.getNextLiveSet(stage);
|
||||
|
||||
if ((current === null || current.nothing) && next === null) {
|
||||
line += " :tada: Thanks for watching.";
|
||||
} else {
|
||||
if (current !== null && !current.nothing) {
|
||||
line += " __" + current.name + "__ <t:" + current.date.unix() + ":R>";
|
||||
} else {
|
||||
line += " :no_entry_sign: __Not currently live__.";
|
||||
}
|
||||
|
||||
if (next !== null) {
|
||||
line += " :arrow_forward: Next: __" + next.name + "__ <t:" + next.date.unix() + ":R>";
|
||||
} else {
|
||||
line += " :warning: This is the last set!";
|
||||
}
|
||||
|
||||
line += " :link: " + stage.url;
|
||||
}
|
||||
|
||||
stage.channel.setTopic(stage.emoji + " " + line, "Automated bot action for event");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EventSchedule;
|
133
modules/eventquick.js
Normal file
133
modules/eventquick.js
Normal file
|
@ -0,0 +1,133 @@
|
|||
class EventQuickModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.event = config;
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
|
||||
this.current = '';
|
||||
this.ended = false;
|
||||
if (this.event.current !== undefined) {
|
||||
this.current = this.event.current;
|
||||
}
|
||||
|
||||
this.channel = client.channels.resolve(this.event.channel);
|
||||
this.updateChannel();
|
||||
}
|
||||
|
||||
onCmdIlink(msg, link)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.event.link = link;
|
||||
|
||||
console.log("New impromptu event link: " + this.event.link);
|
||||
msg.channel.send(":link: The livestream can be found here: <" + this.event.link + ">");
|
||||
|
||||
msg.delete();
|
||||
this.updateChannel();
|
||||
}
|
||||
|
||||
onCmdInow(msg)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (arguments.length == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.current = Array.from(arguments).slice(1).join(" ").trim();
|
||||
this.ended = false;
|
||||
|
||||
console.log("Starting now: " + this.current);
|
||||
msg.channel.send(":red_circle: STARTING NOW: **" + this.current + "**");
|
||||
|
||||
msg.delete();
|
||||
this.updateChannel();
|
||||
}
|
||||
|
||||
onCmdIoffline(msg)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.current = "";
|
||||
this.ended = false;
|
||||
|
||||
console.log("Stream is offline!");
|
||||
msg.channel.send(":no_entry_sign: Stream is temporarily offline.");
|
||||
|
||||
msg.delete();
|
||||
this.updateChannel();
|
||||
}
|
||||
|
||||
onCmdIend(msg)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.current = "";
|
||||
this.ended = true;
|
||||
|
||||
console.log("End of event!");
|
||||
msg.channel.send(":tada: Thank you for tuning in.");
|
||||
|
||||
msg.delete();
|
||||
this.updateChannel();
|
||||
}
|
||||
|
||||
onCmdCurrent(msg) { this.onCmdNp(msg); }
|
||||
onCmdNow(msg) { this.onCmdNp(msg); }
|
||||
onCmdNp(msg)
|
||||
{
|
||||
if (this.current != "") {
|
||||
msg.channel.send(":red_circle: Now playing: **" + this.current + "**");
|
||||
} else {
|
||||
msg.channel.send(":robot: There's currently no set playing.");
|
||||
}
|
||||
}
|
||||
|
||||
onCmdNext(msg)
|
||||
{
|
||||
msg.channel.send(":robot: This is an event without a timetable.");
|
||||
}
|
||||
|
||||
onCmdLink(msg)
|
||||
{
|
||||
msg.channel.send(":link: The livestream can be found here: <" + this.event.link + ">");
|
||||
}
|
||||
|
||||
updateChannel()
|
||||
{
|
||||
var line = "";
|
||||
|
||||
if (this.ended) {
|
||||
line = ":tada: Thank you for tuning in.";
|
||||
} else {
|
||||
if (this.current == "") {
|
||||
line = ":no_entry_sign: Not currently live.";
|
||||
} else {
|
||||
line = "__" + this.current + "__";
|
||||
}
|
||||
|
||||
if (this.event.link !== undefined) {
|
||||
line += " :link: " + this.event.link;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.event.emoji) {
|
||||
line = this.event.emoji + " " + line;
|
||||
}
|
||||
|
||||
this.channel.setTopic(line);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EventQuickModule;
|
70
modules/filter.js
Normal file
70
modules/filter.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
const discord = require("discord.js");
|
||||
const RedditRadio = require("../RedditRadio");
|
||||
|
||||
class FilterModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
|
||||
/** @type {RedditRadio} */
|
||||
this.bot = bot;
|
||||
|
||||
if (this.config.channel) {
|
||||
if (!this.config.channels) {
|
||||
this.config.channels = [];
|
||||
}
|
||||
this.config.channels.push(this.config.channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var shouldDelete = false;
|
||||
|
||||
// Only filter if we're in the right channel
|
||||
if (this.config.channels) {
|
||||
var isInChannel = false;
|
||||
for (const channelID of this.config.channels) {
|
||||
if (msg.channel.id == channelID) {
|
||||
isInChannel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isInChannel) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for bad words (case insensitive)
|
||||
if (this.config.words && msg.content.toLowerCase().match(this.config.words)) {
|
||||
shouldDelete = true;
|
||||
}
|
||||
|
||||
// Check for bad tokens (case sensitive)
|
||||
if (this.config.tokens && msg.content.match(this.config.tokens)) {
|
||||
shouldDelete = true;
|
||||
}
|
||||
|
||||
if (shouldDelete) {
|
||||
var usermessage = this.config.usermessage || "Your recent message has been automatically deleted. Please take another look at the rules in #info. We automatically delete messages for things like piracy and advertising.";
|
||||
|
||||
msg.delete();
|
||||
this.bot.addLogMessage("Deleted unwanted message from " + msg.author.toString() + " in " + msg.channel.toString() + ": `" + msg.content.replace('`', '\\`') + "`");
|
||||
msg.author.send(usermessage).catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FilterModule;
|
34
modules/filteremotes.js
Normal file
34
modules/filteremotes.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
const discord = require("discord.js");
|
||||
const RedditRadio = require("../RedditRadio");
|
||||
|
||||
class FilterEmotesModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
|
||||
/** @type {RedditRadio} */
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
var limit = this.config.limit || 14;
|
||||
|
||||
var emotes = msg.content.toLowerCase().match(/(<a?:[^:]+:[0-9]+>|\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/g);
|
||||
if (emotes && emotes.length > limit) {
|
||||
msg.delete();
|
||||
this.bot.addLogMessage("Deleted message from " + msg.member.toString() + " in " + msg.channel.toString() + " that contained " + emotes.length + " emotes");
|
||||
msg.author.send("You posted too many emojis. Calm down a little bit!").catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FilterEmotesModule;
|
45
modules/filterinvite.js
Normal file
45
modules/filterinvite.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
const discord = require("discord.js");
|
||||
const RedditRadio = require("../RedditRadio");
|
||||
|
||||
class FilterInviteModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
|
||||
/** @type {RedditRadio} */
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var inviteLinks = msg.content.matchAll(/(discord\.gg|discord\.com\/invite|discordapp\.com\/invite)\/([A-Za-z0-9]+)/gi);
|
||||
for (const link of inviteLinks) {
|
||||
var inviteCode = link[2];
|
||||
|
||||
var isWhitelisted = false;
|
||||
if (this.config.whitelist) {
|
||||
isWhitelisted = (this.config.whitelist.indexOf(inviteCode) != -1);
|
||||
}
|
||||
|
||||
if (!isWhitelisted) {
|
||||
msg.delete();
|
||||
this.bot.addLogMessage("Deleted Discord invite link from " + msg.author.toString() + " in " + msg.channel.toString() + ": `" + msg.content.replace('`', '\\`') + "`");
|
||||
msg.author.send("Your recent message has been automatically deleted. Please do not post Discord invite links without prior permission from a moderator or admin.").catch(console.error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FilterInviteModule;
|
92
modules/filterlink.js
Normal file
92
modules/filterlink.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
const discord = require("discord.js");
|
||||
const RedditRadio = require("../RedditRadio");
|
||||
|
||||
var moment = require("moment");
|
||||
|
||||
class FilterLinkModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
|
||||
/** @type {RedditRadio} */
|
||||
this.bot = bot;
|
||||
|
||||
/** @type {String[]} */
|
||||
this.permitted = [];
|
||||
}
|
||||
|
||||
isPermitted(member)
|
||||
{
|
||||
var delay = this.config.minutes || 60;
|
||||
var minutes = moment().diff(member.joinedTimestamp, "minutes");
|
||||
|
||||
if (minutes >= delay) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check the list of permitted users with .permit
|
||||
var permittedIndex = this.permitted.indexOf(member.id);
|
||||
if (permittedIndex != -1) {
|
||||
if (minutes >= delay) {
|
||||
this.permitted.splice(permittedIndex, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (msg.content.match(/https?:\/\//i) || msg.content.match(/\.[a-z]{2,3}\//i) || msg.content.match(/(bit.ly|shorturl.at|tiny.cc)/i)) {
|
||||
msg.guild.members.fetch(msg.author).then((member) => {
|
||||
if (this.isPermitted(member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.delete();
|
||||
msg.author.send("Your recent message has been automatically deleted. Brand new members can't post links for a short while, to combat spam. Check #info for more information about the rules. If you think this message is in error, please DM one of the mods.").catch(console.error);
|
||||
|
||||
var minutes = moment().diff(member.joinedTimestamp, "minutes");
|
||||
this.bot.addLogMessage("Deleted link from " + member.toString() + " in " + msg.channel.toString() + " who joined " + minutes + " minutes ago. Deleted message:\n```" + msg.content + "```");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {String} user
|
||||
*/
|
||||
onCmdPermit(msg, user)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mentions = "";
|
||||
var num = 0;
|
||||
msg.mentions.members.each(member => {
|
||||
if (this.isPermitted(member)) {
|
||||
return;
|
||||
}
|
||||
this.permitted.push(member.id);
|
||||
mentions += member.toString() + " ";
|
||||
num++;
|
||||
});
|
||||
|
||||
if (num > 0) {
|
||||
msg.channel.send(mentions + "A moderator has permitted you to post links!");
|
||||
this.bot.addLogMessage(msg.member.toString() + " has permitted " + mentions + "to post links");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FilterLinkModule;
|
55
modules/fun.js
Normal file
55
modules/fun.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
const discord = require("discord.js");
|
||||
const RedditRadio = require("../RedditRadio");
|
||||
|
||||
class FunModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
|
||||
/** @type {RedditRadio} */
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (msg.content.toLowerCase() == "good bot") {
|
||||
msg.channel.send(msg.member.toString() + " Thanks");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.content.toLowerCase() == "bad bot") {
|
||||
msg.channel.send(msg.member.toString() + " I'm sorry :sob: If I did something wrong, you can report a bug! <https://github.com/codecat/reddit-radio/issues>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.content.toLowerCase() == "kut bot") {
|
||||
msg.channel.send(msg.member.toString() + " nou sorry hoor");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.content.toLowerCase().indexOf("am i the only one") != -1 && msg.member !== null) {
|
||||
msg.channel.send(msg.member.toString() + " Probably not.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.content.toLowerCase().indexOf(".shrug") != -1) {
|
||||
msg.channel.send("\xaf\\\\\\_<:headykappa:330110432209797123>\\_/\xaf");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.content.toLowerCase() == "<@!327816989114630145> <a:catjam150:760555281414881380>") {
|
||||
msg.channel.send("<a:catjam150:760555281414881380>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FunModule;
|
29
modules/joinreact.js
Normal file
29
modules/joinreact.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
const discord = require("discord.js");
|
||||
|
||||
class JoinReactModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
* @param {Boolean} edited
|
||||
*/
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (msg.system && msg.type == "GUILD_MEMBER_JOIN") {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
msg.react(this.config.emoji || "👋");
|
||||
} catch (err) { console.error(err); }
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JoinReactModule;
|
29
modules/poll.js
Normal file
29
modules/poll.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
const discord = require("discord.js");
|
||||
const RedditRadio = require("../RedditRadio");
|
||||
|
||||
class PollModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
|
||||
/** @type {RedditRadio} */
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.Message} msg
|
||||
*/
|
||||
onCmdPoll(msg)
|
||||
{
|
||||
(async () => {
|
||||
await msg.react("👍");
|
||||
await msg.react("👎");
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PollModule;
|
212
modules/producing.js
Normal file
212
modules/producing.js
Normal file
|
@ -0,0 +1,212 @@
|
|||
var colors = require("colors");
|
||||
var moment = require("moment");
|
||||
|
||||
const ffmpeg = require('fluent-ffmpeg');
|
||||
|
||||
class ProducingModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
|
||||
this.client.on("messageReactionAdd", (r, user) => { this.onMessageReactionAdd(r, user); });
|
||||
this.client.on("messageReactionRemove", (r, user) => { this.onMessageReactionRemove(r, user); });
|
||||
|
||||
if (!this.bot.mongodb) {
|
||||
console.error("The producing module requires MongoDB to be connected to a database!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.collUsers = this.bot.mongodb.collection("users");
|
||||
this.collFiles = this.bot.mongodb.collection("files");
|
||||
this.collFilesFeedback = this.bot.mongodb.collection("files_feedback");
|
||||
}
|
||||
|
||||
async getOrCreateUser(id)
|
||||
{
|
||||
var user = await this.collUsers.findOne({ id: id });
|
||||
if (!user) {
|
||||
user = {
|
||||
id: id,
|
||||
files_uploaded: 0,
|
||||
feedback_given: 0
|
||||
};
|
||||
await this.collUsers.insertOne(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
async getNumberOfFeedbackGiven(userId)
|
||||
{
|
||||
var result = await this.collFilesFeedback.aggregate([
|
||||
{ $match: { user: userId } },
|
||||
{ $group: { _id: "$msg", count: { $sum: 1 } } },
|
||||
{ $count: "count" }
|
||||
]).next();
|
||||
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
return result.count;
|
||||
}
|
||||
|
||||
onMessageReactionAdd(r, user)
|
||||
{
|
||||
if (user == this.client.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = r.message;
|
||||
if (msg.channel.id != this.config.channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (user == msg.author) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.reactions.indexOf(r.emoji.name) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.collFilesFeedback.insertOne({
|
||||
time: new Date(),
|
||||
msg: msg.id,
|
||||
msg_user: msg.author.id,
|
||||
user: user.id,
|
||||
emoji: r.emoji.name
|
||||
});
|
||||
}
|
||||
|
||||
onMessageReactionRemove(r, user)
|
||||
{
|
||||
if (user == this.client.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = r.message;
|
||||
if (msg.channel.id != this.config.channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (user == msg.author) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.reactions.indexOf(r.emoji.name) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.collFilesFeedback.deleteOne({
|
||||
msg: msg.id,
|
||||
user: user.id,
|
||||
emoji: r.emoji.name
|
||||
});
|
||||
}
|
||||
|
||||
onMessage(msg, edited)
|
||||
{
|
||||
if (msg.channel.id != this.config.channel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (edited) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
var user = await this.getOrCreateUser(msg.author.id);
|
||||
|
||||
var filenames = "";
|
||||
var numFiles = 0;
|
||||
|
||||
msg.attachments.each(async a => {
|
||||
if (!a.name.match(/.*\.(wav|mp3|ogg|flac)/)) {
|
||||
return;
|
||||
}
|
||||
|
||||
filenames += a.name + " ";
|
||||
numFiles++;
|
||||
|
||||
var logUsername = msg.author.username + '#' + msg.author.discriminator;
|
||||
console.log(logUsername + " uploaded " + a.name.red.underline);
|
||||
|
||||
this.collUsers.updateOne({ id: user.id }, {
|
||||
$inc: { files_uploaded: 1 }
|
||||
});
|
||||
|
||||
/*
|
||||
statsmessage = false
|
||||
feedbackmessage = false
|
||||
spectrumpic = false
|
||||
*/
|
||||
|
||||
if (this.config.spectrumpic) {
|
||||
new Promise((resolve, reject) => {
|
||||
let path = '/tmp/waveform-' + msg.id + '.png';
|
||||
let cmd = ffmpeg(a.url);
|
||||
cmd.complexFilter([
|
||||
'[0:a] showspectrumpic=s=400x70:color=nebulae:legend=false [tmp1]',
|
||||
//'[0:a] showwavespic=s=400x70:colors=0xFFFFFFFF:filter=peak [tmp2]',
|
||||
//'[tmp1][tmp2] overlay=y=0:format=rgb:alpha=premultiplied [tmp3]',
|
||||
'[tmp1] drawbox=0:0:400:70:black',
|
||||
]);
|
||||
cmd.frames(1);
|
||||
cmd.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
cmd.on('end', () => {
|
||||
msg.channel.send({
|
||||
files: [{
|
||||
attachment: path,
|
||||
name: 'waveform.png',
|
||||
}],
|
||||
}).then(resolve).catch(reject);
|
||||
});
|
||||
cmd.save(path);
|
||||
}).catch(err => {
|
||||
console.error('ffmpeg waveform failed!', err);
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.config.reactions.length; i++) {
|
||||
await msg.react(this.config.reactions[i]);
|
||||
}
|
||||
});
|
||||
|
||||
if (numFiles > 0) {
|
||||
var numFeedbackGiven = await this.getNumberOfFeedbackGiven(user.id);
|
||||
|
||||
if (this.config.statsmessage) {
|
||||
msg.channel.send("**Give " + msg.member.displayName + " your feedback!** :outbox_tray: " + (user.files_uploaded + 1) + " / :bulb: " + numFeedbackGiven);
|
||||
}
|
||||
|
||||
if (this.config.feedbackmessage) {
|
||||
if (numFeedbackGiven < user.files_uploaded) {
|
||||
msg.channel.send(msg.member.toString() + " Remember to give others feedback, too! :ok_hand:");
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async onCmdStats(msg)
|
||||
{
|
||||
if (msg.channel.id != this.config.channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await this.getOrCreateUser(msg.author.id);
|
||||
|
||||
var numFeedbackReceived = await this.collFilesFeedback.countDocuments({ msg_user: user.id });
|
||||
var numFeedbackGiven = await this.getNumberOfFeedbackGiven(user.id);
|
||||
|
||||
msg.channel.send(":bar_chart: " + msg.member.toString() + ", you have uploaded **" + (user.files_uploaded || 0) + "** files, given **" + numFeedbackGiven + "** feedback reactions, and received **" + numFeedbackReceived + "**.");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProducingModule;
|
65
modules/qdance.js
Normal file
65
modules/qdance.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
var discord = require("discord.js");
|
||||
var http = require("https");
|
||||
|
||||
class QdanceModule
|
||||
{
|
||||
get(dir)
|
||||
{
|
||||
return new Promise((resolve, reject) => {
|
||||
http.get("https://feed.q-dance.com/onair", function(res) {
|
||||
var data = "";
|
||||
res.setEncoding("utf8");
|
||||
res.on("data", function(chunk) { data += chunk; });
|
||||
res.on("end", function() {
|
||||
var obj = JSON.parse(data);
|
||||
if (dir == -1) {
|
||||
resolve(obj.TrackData.PreviousPlaying);
|
||||
} else if (dir == 0) {
|
||||
resolve(obj.TrackData.NowPlaying);
|
||||
} else if (dir == 1) {
|
||||
resolve(obj.TrackData.NextPlaying);
|
||||
}
|
||||
reject('Unknown track direction!');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
makeEmbed(track, title)
|
||||
{
|
||||
var embed = new discord.MessageEmbed({
|
||||
title: title,
|
||||
description: track.Artist + " - " + track.Title,
|
||||
hexColor: "#D26F1C"
|
||||
});
|
||||
embed.setAuthor("Q-dance Radio", "https://4o4.nl/20170908JHxVy.png");
|
||||
embed.setThumbnail(track.CoverImage);
|
||||
return embed;
|
||||
}
|
||||
|
||||
async onCmdQdnp(msg)
|
||||
{
|
||||
var track = await this.get(0);
|
||||
msg.channel.send({
|
||||
embeds: [ this.makeEmbed(track, "Q-dance Radio is now playing:") ],
|
||||
});
|
||||
}
|
||||
|
||||
async onCmdQdnext(msg)
|
||||
{
|
||||
var track = await this.get(1);
|
||||
msg.channel.send({
|
||||
embeds: [ this.makeEmbed(track, "Next track on Q-dance Radio:") ],
|
||||
});
|
||||
}
|
||||
|
||||
async onCmdQdprev(msg)
|
||||
{
|
||||
var track = await this.get(-1);
|
||||
msg.channel.send({
|
||||
embeds: [ this.makeEmbed(track, "Previous track on Q-dance Radio:") ],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = QdanceModule;
|
27
modules/sus.js
Normal file
27
modules/sus.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
const discord = require("discord.js");
|
||||
|
||||
var moment = require("moment");
|
||||
|
||||
class SusModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.GuildMember} member
|
||||
*/
|
||||
onMemberJoin(member)
|
||||
{
|
||||
if (moment().diff(member.user.createdAt, "hours") < 48) {
|
||||
this.bot.addLogMessage("<:skepticalpepe:743455915935662133> Brand new user account joined: " + member.user.toString()
|
||||
+ " (account created <t:" + moment(member.user.createdAt).unix() + ":R>)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SusModule;
|
60
modules/tools.js
Normal file
60
modules/tools.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
var moment = require("moment");
|
||||
|
||||
class ToolsModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
onCmdJoinTime(msg) { this.onCmdJoinDate(msg); }
|
||||
onCmdJoinDate(msg)
|
||||
{
|
||||
msg.guild.members.fetch(msg.author).then((member) => {
|
||||
var joinedAt = moment(member.joinedTimestamp);
|
||||
msg.reply("you joined this server **<t:" + joinedAt.unix() + ":R>** - <t:" + joinedAt.unix() + ":F>");
|
||||
});
|
||||
}
|
||||
|
||||
onCmdSlow(msg, seconds)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.channel.setRateLimitPerUser(parseInt(seconds));
|
||||
msg.delete();
|
||||
}
|
||||
|
||||
async onCmdUserInfo(msg, user)
|
||||
{
|
||||
if (!this.bot.isMod(msg.member)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var member = await msg.guild.members.fetch(user);
|
||||
} catch {
|
||||
var fetcheduser = await this.client.users.fetch(user);
|
||||
var createdAt = moment(fetcheduser.createdTimestamp);
|
||||
|
||||
msg.channel.send(
|
||||
"**Info for non-member " + fetcheduser.tag + "**:\n" +
|
||||
":alarm_clock: Account age: **<t:" + createdAt.unix() + ":R>** - <t:" + createdAt.unix() + ":F>"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var joinedAt = moment(member.joinedTimestamp);
|
||||
var createdAt = moment(member.user.createdTimestamp);
|
||||
|
||||
msg.channel.send(
|
||||
"**Info for member " + member.toString() + "**:\n" +
|
||||
":alarm_clock: Join time: **<t:" + joinedAt.unix() + ":R>** - <t:" + joinedAt.unix() + ":F>\n" +
|
||||
":alarm_clock: Account age: **<t:" + createdAt.unix() + ":R>** - <t:" + createdAt.unix() + ":F>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ToolsModule;
|
40
modules/welcome.js
Normal file
40
modules/welcome.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
const discord = require("discord.js");
|
||||
|
||||
class WelcomeModule
|
||||
{
|
||||
constructor(config, client, bot)
|
||||
{
|
||||
this.config = config;
|
||||
/** @type {discord.Client} */
|
||||
this.client = client;
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {discord.GuildMember} member
|
||||
*/
|
||||
async onMemberJoin(member)
|
||||
{
|
||||
var msg = this.config.messageprefix;
|
||||
|
||||
var msgIndex = Math.floor(Math.random() * this.config.messages.length);
|
||||
msg += ' ' + this.config.messages[msgIndex];
|
||||
msg = msg.replace('<name>', '**' + member.user.username + '**');
|
||||
//msg = msg.replace('<name>', member.toString());
|
||||
//^ Commented out because mentions for newly joined members are broken on the client
|
||||
|
||||
var channel = await this.client.channels.fetch(this.config.channel);
|
||||
var message = await channel.send(msg);
|
||||
/*
|
||||
var message = await channel.send({
|
||||
content: msg,
|
||||
allowedMentions: {
|
||||
users: []
|
||||
}
|
||||
});
|
||||
*/
|
||||
message.react(this.config.emoji || "👋");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WelcomeModule;
|
26
package.json
Normal file
26
package.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "redditradio",
|
||||
"version": "1.0.0",
|
||||
"description": "discord.gg/hardstyle",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@discordjs/opus": "^0.3.2",
|
||||
"colors": "^1.4.0",
|
||||
"discord.js": "^13.1.0",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"follow-redirects": "^1.14.4",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.33",
|
||||
"mongodb": "^3.7.1",
|
||||
"sodium": "^3.0.2",
|
||||
"toml": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"radio": "node index.js --radio",
|
||||
"radios": "node index.js --radios",
|
||||
"no-radio": "node index.js --no-radios"
|
||||
},
|
||||
"author": "Codecat"
|
||||
}
|
Loading…
Reference in a new issue