How to create a custom command leaderboard?

I made a command for a channel i moderate that allows people to order a slice of pizza every 15 minutes, and a command that will keep count so they can check how many they’ve had. However i would like to make another command that shows the total of everyone’s pizzas, rather than having to input the command for each individual person.

Something like “!sliceboard : ‘username’ has had 10, ‘username’ has had 8, ‘username’ has had 5”

The idea is that the command will update itself automatically. Otherwise, i’d be spending more time manually inserting usernames and scores than enjoying the stream or fulfilling other moderator tasks.

If this isn’t something nightbot is capable of and you think/know of another bot that might be able to do it please let me know.

Hey @rattoota!

It all depends on how you implemented the first commands, we can’t help without that information.

You can use JavaScript with Nightbot thanks to the $(eval) variable, so you can do a lot of things.

Please note however that Twitch’s chat has a limit of 500 characters per messages (half that for YouTube), and with a leaderboard command you’ll quickly reach that limit, and then the command won’t work anymore.
Maybe putting that much effort into a command that has a very limited life-span isn’t so good of an idea?

1 Like

These are the commands and aliases

!slicegive

$(eval a=decodeURIComponent(`$(querystring)`);a&&`$(twitch $(query))`!=`Unknown Twitch Channel`?`PUBLIC_TOKEN&data=~${a}~`:`PUBLIC_TOKEN`)

Alias:

!slicegive2

$(eval a=`$(urlfetch https://twitch.center/customapi/addquote?token=$(query))`;b=decodeURIComponent(`$(querystring)`);b.includes(`PUBLIC_TOKEN`)?(a.includes(`Successfully added entry`)?` @${b.split(`&data=`)[1].replace(/~/g,``)} just grabbed a slice of their pie! Next one will be ready in 15 minutes! Check how many you've had with !slices`:`That is an invalid Twitch name!`):` `)

!slicecheck

$(eval u=decodeURIComponent(`$(querystring $(touser))`);r=new RegExp(`~${u}~`,`gi`);a=`$(urlfetch json https://twitch.center/customapi/quote/list?token=PRIVATE_TOKEN)`.match(r);b=a?a.length:0;`@${u} has had ${b} slices.`)

!slicereset

$(eval `$(urlfetch https://twitch.center/customapi/delquote?token=PUBLIC_TOKEN&clear=1)`==`All entries have been deleted`?`Thanks for eating at Noodles' Pizzeria. Come back soon!`:`Try again...`)
1 Like

Could you please edit your message and put 4 spaces on a new line (preceded by an empty line) before each commands so it’s easier for me to look at your code?


***commandName***

    $(eval code)

gives:

commandName

$(eval code)
1 Like

Yep i fixed it for you

1 Like

Thank you!


I’m not sure I understand how your code works, have you tested it, is it working properly? It doesn’t seem to do coherent actions. Also, you inverted the PUBLIC_TOKEN and PRIVATE_TOKEN in your commands, the PUBLIC_TOKEN is 8 characters long, while the PRIVATE_TOKEN is double that: 16.

From what I understand, you want people to be able to give slice of pizza to each others, without being able to grab them for themselves, have I got that right?
Keeping this assumption in mind, I’ll correct the code and simplify it:

!slicegive:

$(eval '$(touser)' != '$(user)' && !'$(twitch $(touser))'.includes('Error') ? 'PRIVATE_TOKEN&data=$(touser)' : ' ';)

Here I’m making sure that a chatter doesn’t grab a slice for themselves: '$(touser)' != '$(user)'; and that if the Twitch username doesn’t exist, it’ll give an error later: '$(twitch $(touser))'.includes('Error'): Nightbot’s error doesn’t say “Unknown Twitch Channel”, but “Error: Channel not found.”, and making the test only on the existence of the channel ignores all the other possible errors that we want to consider. I’m also simplifying the entries to the quote list — you don’t really need the tilde (~) separator — by making it the targeted user.

!slicegive2 (consider renaming it _slicegive, that’s the convention we use):

$(eval a = '$(urlfetch https://twitch.center/customapi/addquote?token=$(query))'; b = '$(query)'; b.includes('PRIVATE_TOKEN') ? a.includes('Successfully added entry') ? `${b.replace('PRIVATE_TOKEN&data=', '')} just received a slice of pizza! Next one will be ready in 5 minutes! Check how many you've had with !slicecheck` : a : 'That\'s an invalid Twitch name! / You can\'t grab a slice for yourself!';)

Here you were forgetting to remove the PRIVATE_TOKEN from your output, this is dangerous as people would be able to edit your quote list; the invalid Twitch name error was misplaced, and finally, the maximum command cooldown is 5 minutes with Nightbot, not 15.

!slicecheck:

$(eval u = '$(user)'.toLowerCase(); a = '$(urlfetch json https://twitch.center/customapi/quote/list?token=PUBLIC_TOKEN)'.split(/\d+\.\s/); a.shift(); m = a.map(b => b.toLowerCase()); s = m.filter(n => n === u).length; `${u} has had ${s} slices.`;)

Just a simplification to go along with the few changes I’ve done to !slicegive.


So here’s what I’m thinking to solve your problem: since we store usernames to count how many slice each and everyone got, we can use the same process to build your leaderboard, the idea is to start from either side of the list, and try to find any matches to the username, add up how many we found, and repeat the process for each entry, if the username of one entry has already been counted, skip it.

My concern is that this has a bad Big O Notation rating, meaning the larger the list gets, the longer it’ll take to compute, and Nightbot limits the amount of computation time you get so one doesn’t use all the power of the bot for themselves, so it might occasionally time out, in which case the solution is to reset the list. I tried to mitigate that as much as possible with my code, but working with the character limitation, so it’s not perfect. Also, as mentioned previously, you’ll reach Twitch’s character limitation pretty quickly.

Anyway, here’s how I’d write the code for your !sliceboard command:

$(eval a = '$(urlfetch json https://twitch.center/customapi/quote/list?token=PUBLIC_TOKEN)'.split(/\d+\.\s/); a.shift(); m = a.map(b => b.toLowerCase()); u = Array.from(new Set(m).values()); r = []; u.forEach(v => r.push({ 'u': v, 'a': m.filter(n => n === v).length })); r.sort((s, t) => t.a - s.a); o = r.map(s => `${s.u}: ${s.a}`); o.join(' | ').slice(0, 400);)

First we grab the entire list in an array, then we make all the usernames lowercase in case there are variations in how people enter the usernames, then we create a list of unique usernames, and finally we count how many times an username appears in the full list, after that we rank them from highest to lowest, and then we build the response.

1 Like

Btw, if you wanted to make it so that users can grab slices for themselves or give them to other people, then !slicegive should be modified as such:

$(eval !'$(twitch $(touser))'.includes('Error') ? 'PRIVATE_TOKEN&data=$(touser)' : ' ';)

And if you wanted to make it so that users grab slices for themselves, then !slicegive is even simpler, and you don’t need the alias _slicegive (I’d rename it !slicegrab too):

$(eval a = '$(urlfetch https://twitch.center/customapi/addquote?token=PRIVATE_TOKEN&data=$(user))'; a.includes('Successfully added entry') ? '$(user) just grabbed a slice of pizza! Next one will be ready in 5 minutes! Check how many you\'ve had with !slicecheck' : a;)

In that case, since users won’t be entering other’s usernames or theirs, you can also remove all the .toLowerCase() and m = a.map(b => b.toLowerCase()); from !slicecheck and !sliceboard, be careful that you also need to replace all the references to m by a (1 in !slicecheck, and 2 in !sliceboard).

1 Like

Thank you so much for the help :grin: Also sorry about the mixed up private and public tokens. I forgot which one had more characters when editing it to post for you lol

1 Like

Haha, that’s fine, don’t worry, I understood they were mixed up because I know the API, it was a heads up for you so you don’t publicly share the PRIVATE_TOKEN by mistake, and so you understand the code I wrote.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.