Bots Home
|
Create an App
Fredda3bot
Author:
fredda3
Description
Source Code
Launch Bot
Current Users
Created by:
Fredda3
// 2018-12-15 // // The slug is the center of a room, and next important are the clients and their words. // This bot presents options to the clients; it shows them what they can get for tokens. // It also shows when a task was triggered, e.g. after a client has tipped for the task. // It presents all that information in a consistent style easy to understand by clients. // The bot avoids writing more than neccessary. It will never push away words of humans. // // A task is a single action the slug performs, e.g. blow a kiss // A bid is a pair key -> task, e.g. 7 -> blow a kiss // A job maintains a set of bids, e.g. bot.dice or bot.tick // A row maintains jobs to be presented in one line. The row "menu" contains the jobs "menu", "roll", "tick". // Thus a single line presents TipMenu bids, TicketShow bids and bids to throw dice once or five times. // Each of the other rows just contain one single job: Dice tasks, goals or lush levels. // cb.settings_choices = [ { label: 'Tokens to throw dice, 0 for no dice', name: 'tokens', type: 'int', minValue: 0, defaultValue: 12 }, { label: 'Minutes to wait between announcements', name: 'notice_wait_time', type: 'int', minValue: 0, defaultValue: 7 }, { label: 'Lines written in chat between announcements', name: 'notice_wait_lines', type: 'int', minValue: 0, defaultValue: 15 }, { label: 'Goal? Enter additional room subject here, e.g. tags.', name: 'room_title', type: 'str', required: false }, { label: 'Lush? try: 1+ 3s low, 15+ 10s medium, 99+ 1m high', name: 'lush_levels', type: 'str', required: false }, { label: 'Change command prefix if other bots use the same prefix.', name: 'cmd_prefix', type: 'str', minLength: 1, defaultValue: '#' }, ]; Object.prototype.forAll = function(fun) { for (let key of Object.keys(this)) fun(key,this[key]); }; Object.prototype.isEmpty = function() { return ! Object.keys(this).length; }; const uni = { heart: { blue: "\u{1f499}", green: "\u{1f49a}", yellow: "\u{1f49b}", purple: "\u{1f49c}", ribbon: "\u{1f49d}", black: "\u{1f5a4}", }, dice: "\u{1f3b2}", flag: "\u{1f3c1}", carrot: "\u{1f955}", arts: "\u{1f3ad}", postal: "\u{1f4ef}", speaker: "\u{1f508}", lightbulb: "\u{1f4a1}", arrow: "\u279c", star: "\u2b50", sparks: "\u2728", geviert: "\u2001", ensp: "\u2002", emsp: "\u2003", emsp13: "\u2004", emsp14: "\u2005", ellipsis: "\u2026", one: "\u278a", } const colour = { // ensures a consistent style tout: "#C060F0", // fg colour for touted notes only: "#EDFEFF", // bg colour for only for you tick: "#FFFFCC", // bg colour for ticket owner pale: "#CCCCCC", // fg colour for other people tipp: "#FFFF5F", // normal tip notes are khaki } String.prototype.tout = function(uname) { // advertise what clients can do if (!this) return; cb.sendNotice(this,uname,"",colour.tout); } // this style is used to tell clients what they can tip for String.prototype.note = function(uname) { // print a notice to ... if (!this) return; if (uname) cb.sendNotice (this, uname, colour.only); // ... just one user else cb.sendNotice (this, "", "", "", "bold"); // ... the whole audience } // the public style usually reports that an action has been triggered Array.prototype.note = function(uname) { var a; while (a=this.shift(),a) a.note(uname); } String.prototype.slug = function(uname) { // write to uname and keep the slug informed if (!uname) uname = cb.room_slug; this.note(uname); if (uname==cb.room_slug) return; (" ... to "+uname+": "+line).slug(); } function Memo() { // accumulate some lines of text this.text = ""; this.wrap = ""; } Memo.prototype.line = function(s) { if (!s) return; this.text += this.wrap+s; this.wrap = "\n"; } reply = (function () { // one global memo used to accumulate a global note const memo = new Memo(); return function(s) { memo.line(s); if (s===undefined && memo.text) { memo.text.note(); memo.text = ""; memo.wrap = ""; } // we use that to keep lines in order. CB tends to swap notices } }) (); var milliPerSecond = 1000; function Timer(fun,arg) { this.tid = undefined; this.cb = function(t) { // not in prototype - callbacks are per timer return function() { // ensure t remains visible for the inner function t.tid = undefined; fun(arg); } } (this/*,fun,arg*/); } Timer.prototype.end = function() { if (this.tid === undefined) return; cb.cancelTimeout(this.tid); this.tid = undefined; } Timer.prototype.set = function(s) { this.end(); this.tid = cb.setTimeout(this.cb,s*milliPerSecond); } const numOfDice = 3; const cmdPrefix = cb.settings.cmd_prefix; const rollToken = parseInt(cb.settings.tokens); function doHelp(topic,uname,error) { const tops = { goal: [ "After a goal of tokens is reached you perform the announced action.", "The bot can handle several goals and it updates the room name to show the current goal.", "In the bot settings you can add text to the room name, for example, the tags you use.", "#goal 200 Take off Bra % you promise to remove the bra after a total of 200 tokens.", "#goal +200 Strip Dance % you strip after highest goal plus 200 tokens.", "#goal 400 % you delete the goal at 400 tokens", ], lush: [ "If you want to tell the clients that you need tips to get excited, restart Tip+Dice.", "In the pop-up window with all configurations, there is one line reserved for the Lush.", "You just need one single ling line to describe all vibrator levels, for example:", "1+3sLow, 15+5sMedium, 99+10sHigh, 200+20sUltra", "That means for 1 or more tokens the Lush vibrates 3 seconds in low intensity,", "for 15 or more tokens Lush vibrates 5 seconds in medium intensity, and so on.", ], menu: [ "Delete tasks from tip menu or offer new tasks once or often.", "#menu 9 = Blow Kiss % Offer a new task 'Blow Kiss' for 9 tokens.", "#menu 9 - Drink % Only once, drink if anybody tips 9 tokens.", "#menu 9 @ % Delete the task for 9 tokens.", "#menu % Restore the complete tip menu.", ], dice: [ "Delete tasks from dice menu or offer new tasks once or often.", "#dice 3 = Strip Dance % Striptease if the dice show 1+1+1.", "#dice 18 = Blow Kiss % ... when the dice show 6+6+6.", "#dice 13 - Strip % Only once, strip when the dice sum up to 13.", "#dice 11 @ % Delete the original task for a sum of 11.", "#dice % Restore all dice tasks.", ], show: [ "A Ticket Show is visible only to clients whoy buy a ticket. Try it:", "#show 17 8m Sexy Dance", "The bot announces a ticket show. 17 tokens buy a ticket.", "After a few minuntes, the bot asks you to type:", "#show run", "After you typed it, only ticket owners see you, and you perform the show.", "After 8 minuntes the bot asks you to type:", "#show end", "... and then everything is back to normal, everybody can see you.", ], tail: [ "#tail Ceterum censeo ... % Append a line to the advertisement.", "#tail another line % Append another line to the advertisement.", "#tail % Delete all tailing lines.", ], }; let memo = new Memo; if (error) memo.line("Error in "+topic+": "+error+"\n"+cmdPrefix+"help "+topic); for ( line of ( tops[topic] || [ "This app supports several commands. They are explained by examples.", "Explanations look like that: % This is not a part of a command.", "#stat % show statistics about how you earned tokens.", "#roll % throw dice yourself. No tokens for you, but clients have fun.", "#tipp 5 % tip yourself. No real tokens, but you can test the bot.", "#help goal_ % how to set several "+chains.goal.separ+" Goals.", "#help lush_ % how to love a "+chains.lush.separ+" Vibrator.", "#help menu % how to change the "+chains.menu.separ+" Tip Menu.", "#help dice_ % how to change the "+chains.dice.separ+" Dice Tasks.", "#help show % how to run a "+uni.arts+" Ticket Show.", "#help tail__ % how to add lines "+uni.ellipsis+" to the Tail.", ] )) memo.line(" "+uni.geviert+" "+line); memo.text.replace(/%/g," "+uni.geviert+" "+uni.lightbulb).replace(/#/g,cmdPrefix).replace(/_/g,uni.ensp).note(uname); } function Job(properties) { // for lush, goal, tick, roll // .decor is a string to decorate each bid, e.g. heart, die, flag, carot Object.assign(this,properties); this.zero(); } Job.prototype.handles = function(key) { return this.sale[key] ? true : (key in this.sale) ? false : this.bids[key] ? true : false; } Job.prototype.actWhy = function(uname) { // Why will the slug act? return uname+" tipped "+this.key; } Job.prototype.actHow = function(uname) { // How will the slug act? const key = this.key; const list = (key in this.sale) ? this.sale : this.bids; const task = list[key]; if (this.once[key]) { delete this.once[key]; delete this.sale[key]; } return task; } Job.prototype.act = function(uname,key) { // act on a tip and return a tip notice this.key = key; const act1 = this.actWhy(uname); // dice.actWhy() changes the key ... const act2 = this.actHow(uname); // ... thus actHow() uses the new key if (act2) reply(act1+" "+uni.star+" "+act2); // by default, tip notice is the only action } Job.prototype.bidWhen = function(key,separ) { return separ+" "+key; } Job.prototype.bidDeco = function(key) { return this.sale[key] ? uni.sparks+uni.one.substring(!this.once[key]) : this.decor||""; } Job.prototype.bidWhat = function(key) { return this.sale[key] || this.bids[key]; } Job.prototype.bid = function(key,separ) { const deco = this.bidDeco(key); return this.bidWhen(key,separ)+" "+uni.arrow+" "+deco+" ".substring(!deco)+this.bidWhat(key); } Job.prototype.init = function() { } Job.prototype.zero = function() { this.bids = {}; this.sale = {}; this.once = {}; this.init(); } function Dir(properties) { // .mini, .maxi, .cname, .preset, .hint, .scan - menu, dice Job.call(this,properties); } Dir.prototype = Object.create(Job.prototype); Dir.prototype.constructor = Dir; Dir.prototype.init = function() { let j = 0; for (let i=this.mini; i<=this.maxi; i++, j++) { const name = this.cname + i; // 1st run: prepare settings_choices for the slug cb.settings_choices.push({ name: name, type: 'str', label: this.hint(i), defaultValue: j<this.preset.length ? this.preset[j] : "", required: false }); // 2nd run: evaluate the settings filled by the slug if(!(name in cb.settings)) continue; const task = this.scan(cb.settings[name]); if (task) this.bids[task.key] = task.val; } }; Dir.prototype.alter = function(line) { if (line==="") { // no arguments: restore all this.sale = {}; this.once = {}; } else { // first argument must be amount or sum let m = /^([1-9]\d*) ?(\S.*)?$/.exec(line); if (!m) return "First argument must be a number"; const amnt = parseInt(m[1]); if (!m[2]) { delete this.sale[amnt]; delete this.once[amnt]; } else if (m[2]!="@") { m = /^([-=]) ?(\S.*)$/.exec(m[2]); if (!m) return "after the number, we need a '-' or '=', followed by a task"; this.sale[amnt] = m[2]; this.once[amnt] = m[1]=='-'; } else this.sale[amnt] = null; } }; // <amnt> = <task> ==> create a new task in TipMenu or RollDice // <amnt> - <task> ==> same, but the task will happen only once // <amnt> @ ==> delete the task from TipMenu or RollDice // <amnt> ==> restore the original task for the amount // ==> restore all tasks of TipMenu or RollDice function Chain(properties) // title, separ, [jobs] { Object.assign(this,properties); }; Chain.prototype.merge = function() { }; Chain.prototype.bid = function() { let map = {}; for (let job of this.jobs) { job.bids.forAll ( key => { if ( ! (key in job.sale ) ) map[key]=job; } ); job.sale.forAll ( key => { if ( job.sale[key] ) map[key]=job; } ); } let line = ""; map.forAll( (key,job) => line += " "+uni.emsp13+job.bid(key,this.separ) ) return line ? this.title+":"+line : null; }; Chain.prototype.act = function(uname,key) { let jobs = this.jobs; let job = null; find: { let k=jobs.length; while (k) { job = jobs[--k]; if (job.sale[key]) break find; if (key in job.sale) continue; if (job.bids[key]) break find; } return false; } job.act(uname,key); }; bot = { total: 0, count: {}, stat: function(uname) { let line = "statistics:"; this.count.forAll ( (key,val) => line += " "+val+"*"+key ); (line+" total: "+this.total).note(uname); }, }; bot.lush = new Job ({ bidWhen : function(key,separ) { return separ+" "+key+"+"; }, init: function() { // e.g. 1+ 3s low, 15+ 10s medium, 99+ 1m high if(!("lush_levels" in cb.settings)) return; let line = cb.settings.lush_levels; for (let step of line.split(",")) { const split = /^(\d+)\s*\+\s*(\d+)\s*([sSmM])\s*(\S.*)$/.exec(step.trim()); if (!split||split.length<5) break; this.bids[split[1]] = split[2]+" "+(split[3].toLowerCase()=="s"?"sec":"min")+" "+split[4]; } }, }); bot.goal = new Job ({ nameTail: cb.settings.room_title, nameUse: !!cb.settings.room_title, nameNow: "tohu wa bohu", bidWhen : function(key,separ) { return separ+" "+"+"+key; }, roomUpdate: function() { if (!this.nameUse) return; let keys = Object.keys(this.bids); let nameNew = keys.length ? "Goal: "+this.bids[keys[0]]+"!" : ""; if (this.nameTail.trim()) { if (nameNew) nameNew += " "+uni.geviert+" "; nameNew += this.nameTail; } if (this.nameNow == nameNew) return; cb.changeRoomSubject(nameNew); this.nameNow = nameNew; }, tip: function(amnt) { let keys = Object.keys(this.bids); for (let key of keys) { if (amnt<key) { brag.lines.have += 3*brag.lines.need*amnt/key; this.bids[key-amnt] = this.bids[key]; } else reply("Goal achieved "+uni.star+" "+this.bids[key]); delete this.bids[key]; } this.roomUpdate(); }, scanCmd: function(line,uname) { let keys = Object.keys(this.bids); let m = /(\+?) ?(\d+) ?(\S.*)?$/.exec(line); if (!m) return "Bad command line"; let key = parseInt(m[2]); if (!key) return "Number of Tokens missing"; if (m[1]&&keys.length) key += (keys[keys.length-1]-0); // +200$ means highest goal plus 200 tokens if (m[3]) { ("Goal for "+key+" tokens "+uni.flag+" "+m[3]).tout(); this.bids[key] = m[3]; this.nameUse = true; } else do if (this.bids[key]) { delete this.bids[key]; break; } while(key--); this.roomUpdate(); }, init: function() { this.roomUpdate(); } }); bot.tick = new Job ({ decor: uni.arts, par: {}, actHow: function(uname) { this.addUser(uname); return "Ticket for "+this.bids[this.key]; }, countUsers: function() { return cb.limitCam_allUsersWithAccess().length; }, showRun: function(par) { if (!par.cost) "First announce the show, like:\n#show $ 25 wait 5 time 10 Pillow Dance".slug(uname); else if (!cb.limitCam_isRunning()) { this.timBefore.end(); let d = new Date(); d = new Date(d.getTime() + par.full*60000); let h = ("0"+d.getUTCHours()).slice(-2); let m = ("0"+d.getUTCMinutes()).slice(-2); let s = ("0"+d.getUTCSeconds()).slice(-2); cb.limitCam_start ("... until "+h+":"+m+":"+s+" UTC\nTip "+par.cost+" to view\n"+par.task); ("Ticket Show starting now! You can still tip "+par.cost+" to see "+par.task).note(); this.timDuring.set(par.full*60); } else "Ticket Show is already running".slug(uname); return true; }, showEnd: function(par) { this.timBefore.end(); this.timDuring.end(); this.bids = {}; if (par.cost) { ((cb.limitCam_isRunning()?"Thank you for watching ":"No tickets sold, thus we cancel ")+par.task).note(); cb.limitCam_stop(); cb.limitCam_removeAllUsers(); par.cost = 0; } else "Nothing to end, there is no ticket show announced".slug(uname); return true; }, showNew: function(par,line,uname) { let m = /^(\d+) (\d+) ?[mM] (\S.*)$/.exec(line); if (!m) { ("illegal ticket show parameters: "+line).slug(uname); return false; }; par.cost = parseInt(m[1]); par.full = parseInt(m[2]); par.task = m[3]; par.wait = 4; this.bids[par.cost] = par.task; this.buyBefore(); return true; }, buyBefore: function() { const par = bot.tick.par; const when = !par.wait ? "immediately" : par.wait==1 ? "in one minute" : "in "+par.wait+" minutes"; ("Ticket Show starts "+when+". Tip "+par.cost+" to see "+par.full+" minutes of "+uni.arts+" "+par.task).tout(); if (par.wait) { bot.tick.timBefore.set(60); par.wait--; } else if (!bot.tick.countUsers()) bot.tick.showEnd(par); else ("run your ticket show now! Type: "+cmdPrefix+"show run").slug(); }, buyDuring: function() { "The show will end soon.".note(); ("end your ticket show now! Type: "+cmdPrefix+"show end").slug(); }, addUser: function(invitee) { cb.limitCam_addUsers([invitee]); return true; }, scanCmd: function(args,line,uname) { let par = this.par; switch (args[0]) { case 'add': return this.addUser(args[1]); case 'run': return this.showRun(par); case 'end': return this.showEnd(par); default: return this.showNew(par,line,uname); } doHelp("show",uname); }, init: function() { }, }); bot.tick.timBefore = new Timer(bot.tick.buyBefore); bot.tick.timDuring = new Timer(bot.tick.buyDuring); bot.menu = new Dir ({ cname: "tipme1_", mini: 1, maxi: 20, preset: [ "5 PM", "6 Thank you!", "7 Blow a Kiss", "15 Show legs", "20 Comb Hair", "25 Bend over", ], hint: function(key) { return "Fee and Task for Tip Menu"; }, scan: function(line) { // split "10 P M" or " 10: P M" or " 10--P M " into 10 and "P M" const split = /^\s*(\d+)\W+(\w.*\S)\s*$/.exec(line); // ignore blank in some places^ if (split == null) return null; const amt=parseInt(split[1]); if (!amt) return null; return {key:amt,val:split[2]}; }, }); bot.dice = new Dir ({ cname: "prize1_", mini: numOfDice, maxi: numOfDice*6, preset: [ "Oil Pussy", "Oil Tits", "Flash Pussy", "Flash Tits", "Flash Ass", "Shake Ass", "Dance", "Smile", "Lick Lips", "Turn around", "Your Name in Air", "Remove 1 Garment 3 min", "Hair Bra 3 min", "Hand Bra 3 min", "Topless 3 min", "Strip Dance", ], actWhy: function() { const memory = 5; // we use our limited memory of dice results to manipulate luck ... var record = Array(memory).fill(0); // ... just in case we get too many repetitions. return function(uname) { // What triggers a task? We throw dice! let rep = 99; do { var line = ""; // describes the process and result of throwing dice. var sum = 0; // The key, which here is the result of throwing the dice. for (var i = 0; i < numOfDice; i++) { var iacta = Math.floor(Math.random()*6) + 1; line += "[" + iacta + "] "; sum += iacta; } if (!bot.dice.handles(sum)) continue; var bad1 = 0, bad2 = 0; for (var i=0; i<memory; i++) { for (var j=i; ++j<memory;) if (record[i]==record[j]) bad1 += i+j; if (record[i]==sum) bad2 += i+memory; } // multiple repetitions are a bit less likely to happen now. if (Math.random()*(memory+bad1+bad2+bad1*bad2)>bad1*bad2) break; } while (rep--); record.shift(), record.push(sum); this.key = sum; return line+uname+" threw "+sum; }; } (), hint: function() { const dice = numOfDice; const vals= 6; const full = Math.pow(vals,dice); var digs = 2; var dezi = 100; while (2*dezi<full) digs++,dezi*=10; var hits = [1]; for (let i=0; i<dice; i++) { // This approach scales well even for many dice var sums = Array(hits.length+vals-1).fill(0); for (let i=0; i<hits.length; i++) for (let j=0; j<vals; j++) sums[i+j]+=hits[i]; hits = sums; } hits = Array(dice).fill(0).concat(hits); return function (index) { const rate = hits[index]; var prop = Math.round(dezi*rate/full); var line = "%"; for (var i=digs; i; i--) { line = prop%10+line, prop=Math.floor(prop/10); if (i==3) line = "."+line; } return "Task for sum "+index+", chance is "+rate+"/"+full+" = "+line; }; } (), scan: function(line) { // get the task, no key, and return key and task if (!rollToken) return null; if (!this.points) this.points = numOfDice; const p=this.points++; // The key is simply the next available dice result. return {key:p,val:(line.length?line:"Thank You")}; // task is empty? Be friendly! }, }); bot.roll = new Job ({ roll1: rollToken, // 1x dice roll roll5: 0, // discount price for 5 dice rolls many: function(key) { return key==this.roll5 ? 5 : 1 }, bidDeco: function(key) { return uni.dice.repeat(this.many(key)); }, act: function(uname,key) { for ( let count = this.many(key); count; count-- ) bot.dice.act(uname,0); }, fill: function(key,task) { if (!key) return; this.bids[key] = task; }, init: function() { f5: for (let i=0; i*2<this.roll1; i++) for (let j=-1; j<2; j+=2) { const k = 4*this.roll1+i*j; if (k in bot.menu.bids) continue; this.roll5 = k; // discount price for 5 dice rolls break f5; } if (this.roll1) this.bids[this.roll1] = " Throw Dice"; if (this.roll5) this.bids[this.roll5] = " Throw 5 times"; }, }); const chains = { tail: "", goal: new Chain ( { separ: uni.flag, title: "Towards the Goal", jobs: [bot.goal] } ), lush: new Chain ( { separ: uni.carrot, title: "Trigger my Lush", jobs: [bot.lush] } ), menu: new Chain ( { separ: uni.heart.purple, title: "Tip for Action", jobs: [bot.menu,bot.roll,bot.tick] } ), dice: new Chain ( { separ: uni.dice, title: "Throw the Dice", jobs: [bot.dice] } ), }; chains.brag = function(uname) { let memo = new Memo(); for ( let d of [chains.goal,chains.lush,chains.menu,chains.dice] ) memo.line(d.bid()); (memo.text+this.tail).tout(uname); } var brag = { once: function(uname) { chains.brag(uname); }, show: function() { this.once(); this.lines.have = 0; this.delay.have = 0; }, check: function() { const p = this.delay.have/this.delay.need; const q = this.lines.have/this.lines.need; if ((1+p)*(1+q)>4) this.show(); }, nudge: function() { brag.delay.have += 1/12; brag.check(); brag.tim.set(5); }, delay: { need: parseInt(cb.settings.notice_wait_time) || 7, have: 0, }, lines: { // number of normal lines seen after last touting need: parseInt(cb.settings.notice_wait_lines) || 12, have: 0, }, }; brag.tim = new Timer(brag.nudge); function doTip (uname,amount) { bot.count[amount] = (bot.count[amount]||0)+1, bot.total += amount; chains.menu.act(uname,amount); bot.goal.tip(amount); brag.lines.have++; brag.check(); } function doUsage (uname,topic,error) { if (error) doHelp(topic,uname,error); } cb.onEnter (function(usr) { brag.once(usr.user); }); cb.onTip (function(tip) { doTip(tip.from_user,parseInt(tip.amount)); reply(); }); cb.onMessage (function(msg) { const uname = msg.user; ( function() { if (!msg.is_mod && uname != cb.room_slug) return; const head = msg.m.indexOf(cmdPrefix); // We can't just use the begin of the line because ... if (head < 0) return; // ... some bots decorate lines by prefixes before we see them. if (head && uname != cb.room_slug) return; // maybe moderator explains a command to slug let line = msg.m.substring(head+cmdPrefix.length).trim().replace(/\s+/g,' '); const cmd = line.substring(0,4).toLowerCase(); // refine that in case we get commands ... line = line.substring(4).trim(); // .... which are not exactly 4 characters in length. const args = line.toLowerCase().split(' '); switch (cmd) { case 'pass': // author teaches slug msg.m = line; return; case 'fast': // authos is impatient milliPerSecond = 222; break; case 'help': // slug learns about bot doHelp(args[0],uname); break; case 'tipp': // slug when triggers bot functions let amnt = parseInt(args[0]); cb.sendNotice (uname+" whipped "+amnt+" token"+"s".substring(amnt<2), "", colour.tipp, "", "bold"); doTip(uname,amnt); break; case 'stat': // slug wants to know what people hat tipped bot.stat(uname); break; case 'roll': // slug rolls dice herself bot.dice.act(uname,0); break; case 'show': // slug starts a ticket show bot.tick.scanCmd(args,line,uname); break; case 'goal': // slug sets a goal doUsage(uname,"goal",bot.goal.scanCmd(line,uname)); break; case 'menu': // slug changes a Tip Menu task case 'dice': // slug changes a Dice Sum taks { const job = bot[cmd]; const err = job.alter(line); if (err) doUsage(uname,cmd,err); else brag.show(); } break; case 'tail': chains.tail = args[0] ? chains.tail+"\n"+uni.ellipsis+" "+args[0] : ""; break; default: return; } msg.background = colour.only; msg["X-Spam"] = true; // Don't print command messages to chat. if (msg.is_mod) (uname+" commands: "+msg.m).slug(); } ) (); if (!bot.tick.par||uname==cb.room_slug); else if (cb.limitCam_userHasAccess(uname)) msg.background = colour.tick; else if (cb.limitCam_isRunning()) msg.c = colour.pale; if(!msg["X-Spam"]) { brag.lines.have++; brag.check(); } reply(); return msg; }); ("Lines in this color are visible only for you. For help type: "+cmdPrefix+"help").slug(); brag.show(); brag.nudge();
© Copyright Chaturbate 2011- 2024. All Rights Reserved.