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 ! (" + current.who + ")"); } else { msg.channel.send(":red_circle: Now playing: **" + current.name + "**, started !"); } } 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 += " ()"; 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 += "- (), the stream will be offline :no_entry_sign:\n"; } else { if (set.who) { ret += "- (): **" + set.name + "** (" + set.who + ")\n"; } else { ret += "- (): **" + 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 += " ()"; 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 \n"; } else { ret += res.set.name + " plays on \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 ""; } 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 + "__ "; } else { line += " :no_entry_sign: __Not currently live__."; } if (next !== null) { line += " :arrow_forward: Next: __" + next.name + "__ "; } 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;