Bots Home
|
Create an App
SimpleTimer
Author:
dickavatar
Description
Source Code
Launch Bot
Current Users
Created by:
Dickavatar
/* * Copyright 2019 Dick Avatar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Change Log * * 1.0.0 * - Initial release. */ const VERSION = '1.0.0-RC1' const broadcaster = cb.room_slug // Configuration options when the bot is started // cb.settings_choices = [ // {name:'start', label:'Start a timer', type:'str', defaultValue:'/t'}, // {name:'stop', label:'Stop a timer', type:'str', defaultValue:'/t stop'}, // {name:'list', label:'List running timers', type:'str', defaultValue: '/t list'}, // {name:'help', label:'Help command', type:'str', defaultValue:'/t help'} // ] // Simplify calling sendNotice since we only send notices to the broadcaster. function notify(message) { cb.sendNotice(message, broadcaster) } function format_time(t) { // Get the time values let h = t.getUTCHours() let m = t.getUTCMinutes() let s = t.getUTCSeconds() // let h = t.getHours() // let m = t.getMinutes() // let s = t.getSeconds() // Pad with leading zeroes if needed. if (h < 10) h = '0' + h if (m < 10) m = '0' + m if (s < 10) s = '0' + s return h + ":" + m + ":" + s } /* * Information about each timer that is started. */ class TimerData { constructor(id, duration, message, timer) { // Timer ID assigned by the CB server. this.id = id // How long (in milliseconds) the timer will run. this.duration = duration // The message to display when the timer expires. this.message = message let now = Date.now() // When the timer will end. this.end = now + duration // Human readable format for the start time. this.started = format_time(new Date(now)) // Human readable format for the duration. this.time = format_time(new Date(duration)) this.timer = timer } delete() { cbjs.arrayRemove(this.timer.timer_ids, this.id) delete this.timer.timer_data[this.id] } toString() { // Calucate the time remaining. let t = this.end - Date.now() let remaining = format_time(new Date(t)) return `${this.id} - Started at ${this.started} UTC for ${this.time} -- ${remaining} remaining: "${this.message}"` } } class Timer { constructor() { // The ID values for all running timers. this.timer_ids = [] // Information about each timer using the ID as key. this.timer_data = {} } start(duration, message) { let n = parseInt(duration, 10) if (isNaN(n)) { notify("Invalid timer duration: " + duration) return } let msec = n * 60000 let data = new TimerData('', msec, message, this) let handler = function() { data.delete() // cbjs.arrayRemove(this.timer_ids, data.id) // delete this.timer_data[data.id] notify(message) } let id = cb.setTimeout(handler, msec) data['id'] = id this.timer_data[id] = data this.timer_ids.push(id) notify(`Timer ${id} started for ${data.time}`) // let cleanup = function() { // cb.log(`Cleanup for ${id}`) // if (timer_ids.includes(id)) { // cb.log(`Removing id ${id} from timers list`) // timer_ids = cbjs.arrayRemove(timer_ids, id) // } // if (timer_data[id] != null) { // cb.log(`Removing timer data for ${id}`) // delete timer_data[id] // } // } // let delay = msec + 100 // cb.setTimeout(cleanup, delay) return id } stop(id) { cb.log(`cancel timer ${id}`) if (id == null) { notify('No time ID provided.') return } if (typeof id === 'string') { id = parseInt(id) if (isNaN(id)) { notify(`Invalid timer ID`) return } } cb.log('attempting to cancel timer.') cb.cancelTimeout(id) this.timer_ids = cbjs.arrayRemove(this.timer_ids, id) if (this.timer_data[id] != null) { delete this.timer_data[id] } cb.log(`There are ${timer_ids.length} timers still running.`) notify(`Timer ${id} cancelled.`) } list() { if (this.timer_ids.length === 0) { notify('There are no running timers.') return } // cb.log(timer_ids) let msg = `The following timers are running:` for (var i = 0; i < this.timer_ids.length; ++i) { let id = this.timer_ids[i] let info = this.timer_data[id] if (info != null) { msg = msg + '\n' + info } } notify(msg) } } // We define these here to help IDEs let start_cmd = '/t' let stop_cmd = '/t stop' let list_cmd = '/t list' let help_cmd = '/t help' // The singleton object that manages all the CB timers. let timer = new Timer() // Object to map command names to the action (function) to perform. let actions = {} // The real help message is built in the init() function. let help_message = 'No help available.' // When testing in the ADK our init() function is going to get called // twice. So we set a flag to prevent dual executions. let initialized = false // Parse the cb.settings, generate the help message, and map our action // handlers. All of these tasks require the command strings the broadcaster // configured in the setup page. function init() { // Quit if we have been initialized. Otherwise set the flag and continue. if (initialized) return initialized = true // Get the command names (prefix strings) that the broadcaster entered // in the setup page. // start_cmd = cb.settings.start // stop_cmd = cb.settings.stop // list_cmd = cb.settings.list // timer_help_cmd = cb.settings.help // Now we can add the functions for each command to the actions map. actions[start_cmd] = function(args) { let d = args.shift() let t = parseInt(d) if (isNaN(t)) { notify('Invalid timer duration ' + d) return } let message = "Time up." if (args.length > 0) { message = args.join(' ') } let id = timer.start(d, message) notify('Started timer ' + id) } actions[list_cmd] = function(args) { timer.list() } actions[stop_cmd] = function(args) { let id = args.shift() timer.stop(id) } actions[help_cmd] = function(args) { notify(help_message) } // Build the string that will be used for the help message. let header = 'Available Timer commands\n' let data = [ [start_cmd, 'start a timer'], [stop_cmd, 'stop a running timer'], [list_cmd, 'list all running timers'], [help_cmd, 'display this help message'] ] let start_example = monospace(`${start_cmd} 10 Hello world`) let stop_example = monospace(`${stop_cmd} 3`) let list_example = monospace(list_cmd) let footer = ` ${start_example} When a timer is started the id assigned by the CB be server will be displayed. This id can be used to stop the timer early if needed. ${stop_example} The running timers and their id values can be displayed with: ${list_example}` help_message = header + make_table(data, " : ") + footer } function start(args) { let d = args.shift() let t = parseInt(d) if (isNaN(t)) { notify('Invalid timer duration ' + d) return } let message = "Time up." if (args.length > 0) { message = args.join(' ') } let id = timer.start(d, message) notify('Started timer ' + id) } /* * Message handler. */ function _onMessage(message) { // Only process messages from the broadcaster. let user = message['user'] if (user !== cb.room_slug) { return message } // See if we have one of the command messages let msg = message['m'] let args = msg.split(' ') let cmd = args.shift() if (cmd === '/t') { message['X-Spam'] = true let arg = args[0] if (arg === 'stop') { timer.stop(args[1]) } else if (arg === 'list') { timer.list() } else if (arg === 'help') { notify(help_message) } else { // It looks like a timer start command. start(args) } } // let action = actions[cmd] // if (action != null) { // message['X-Spam'] = true // action(args) // } return message } cb.onMessage(_onMessage) /*** The remaining functions are for formatting tables using monospaced characters. ***/ function transpose(ch) { let code = ch.codePointAt(0) const zero = 48 //'0'.charCodeAt(0) const nine = 57 //'9'.charCodeAt(0) const A = 65 //'A'.charCodeAt(0) const Z = 90 //'Z'.charCodeAt(0) const a = 97 //'a'.charCodeAt(0) const z = 122 //'z'.charCodeAt(0) const MONO_A = 120432; // Start of uppercase A-Z const MONO_a = 120458; // Start of lowercase a-z const MONO_0 = 120802; // Start of digits 0-9 const space = '\u{2007}' // 0x2007 is a non-breaking "figure" space (the width of digits 0..9) const tab = space.repeat(4) if (zero <= code && code <= nine) { return String.fromCodePoint(MONO_0 + code - zero) } if (A <= code && code <= Z) { return String.fromCodePoint(MONO_A + code - A) } if (a <= code && code <= z) { return String.fromCodePoint(MONO_a + code - a) } // Return space characters with non-breaking spaces if (code === 32) return space // Return anything else we don't recognize as I can not find // monospace punctuation return ch //String.fromCodePoint(code) } // Converts the input string to monospaced characters. function monospace(s) { let result = '' for (let i = 0; i < s.length; ++i) { result += transpose(s.charAt(i)) } return result } function pad_row(row, lengths, sep) { let n = lengths[0] - row[0].length let pad = ' '.repeat(n) let string = row[0] + pad for (let i = 1; i < row.length; ++i) { let n = lengths[i] - row[i].length let pad = ' '.repeat(n) string += sep + row[i] + pad } return string } function max(list) { let largest = -Infinity for (let i = 0; i < list.length; ++i) { let n = list[i] if (n > largest) largest = n } return largest } function make_table(table, sep=' | ') { let cols = table[0].length cb.log(`There are ${cols} columns`) let pads = [] for (let i = 0; i < cols; ++i) { let w = max(table.map(row => row[i].length)) cb.log(`col ${i} width: ${w}`) pads.push(w) } let string = pad_row(table[0], pads, sep) for (let i = 1; i < table.length; ++i) { string += '\n' string += pad_row(table[i], pads, sep) } return monospace(string) } // function pad(list) { // // First find the longest prefix. // let longest = Math.max.apply(null, table.map(row => row[0].length)) // let result = '' // for (let i = 0; i < list.length; ++i) { // let item = list[i] // let cmd = item[0] // let n = longest - cmd.length // let padding = ' '.repeat(n) // // cb.log(`Adding ${n} spaces to ${cmd}`) // if (i > 0) { // result += '\n' // } // result += monospace(cmd + padding + ' : ' + item[1]) // } // return result // } cb.setTimeout(init, 200)
© Copyright Chaturbate 2011- 2024. All Rights Reserved.