JavaScript for Nexus Newbies Part 2: My First Not-So-Simple Alias

edited March 2016 in Client Help
Part 1: http://forums.achaea.com/discussion/4584/javascript-for-nexus-newbies-part-1

The thing we're going to build today is the first part of a small package that I actually use. The package is for talking to people OOCly. It's polite to always preface an OOC tell with some indication that it's OOC, but it's also a pain - who wants to type // or [OOC] or OOC: in every single tell or reply? I'm way too lazy for that and given how inconsistent a lot of the people I'm talking to are about marking OOC tells, I think I'm probably not alone in that.

This pair of posts is long, but don't be intimidated. It should be pretty easy to follow and it shouldn't be too overwhelming if you take it step by step. We're not actually doing that much, I'm just trying to make sure it's all fully explained so you can understand what you're actually doing.

Today we'll get the basics of the package done. Next week we'll reorganise it and in the process we'll learn the last of our JavaScript fundamentals (and one pretty advanced concept!). Once we've got the fundamentals nailed down, we can move on to some more exciting stuff like GMCP!

I realise this week's project isn't terribly exciting, but it's still useful and it'll teach a lot. Bear with me!

So first things first, we're going to make a package, so go to the package manager and make a new package. Don't be shy about making packages! Divide your code up into logical packages as often as possible so you can easily share it with people! I called mine "oocly" because I think that sounds more clever than it probably does. Give it a description while you're at it.

The Basic OOC Alias

Let's get started with the basic alias for our tell.

I've decided that I want my command to look like this:
ooc Sarapis hi!
And I want it to send something like this:
tell Sarapis *achaean OOC: Hi!
Since I'm usually speaking my city language and my OOC tells might be to friends in other cities, I decided to make sure my OOC tells are always in Achaean too. Convenient!

So go to the reflexes tab, use the drop down to make sure we're in our new package, and make an alias. I'm going to call mine OOC Tell. Make sure to set the matching type to regular expression and the Action to Execute Script, just like last time.

The Regex Pattern

First we'll need our regex pattern. This one is going to have some new stuff in it. In fact, it's going to have the majority of the regex you'll probably ever use.
^ooc +([\w']+) +(.*)$
Let's break that down just like last time.

First we know that ^ is the beginning of the line and ooc is just the letters ooc. Then we've just got a space - don't get tricked here, that space just means "match a space", exactly like any other letter. We know what the $ at the end is too - that just matches the end of the line.

Now we get to the first new thing. That + is called a quantifier. The plus quantifier means "one or more of the previous thing". So what we're saying here is "one or more spaces". We want the alias to go off whether we type "ooc Sarapis hi!" or "ooc     Sarapis hi!".

Next it starts to get really crazy. Let's look at what's inside that first set of parentheses: [\w']

Brackets like that define a character class, so if we do something like [aq2], we're saying "match an a, a q, or a 2". Pretty simple! We can also do ranges, like [a-cq2], which matches a, b, c, q, and 2.

Then we have this weird \w thing. This is one of the built-in character classes in regex. \w just means [a-zA-Z_0-9], so it matches any upper or lowercase letter, any number, or an underscore. This is supposed to be a shortcut for "word" characters (hence the "w" in \w).

So what [\w'] means is: any word character (any uppercase or lowercase letter, any number, or an underscore) or the character ' (there are some denizens with a ' in their name - I'm not sure why you'd want to send them an OOC tell, but whatever!).

Then we see that + again. It means one or more of the previous thing, so we're matching one or more [\w'].

What's that get us? A name! Names are made up of one or more word characters (and sometimes a ' or two).

We put it in parentheses because our script is going to actually need this name. We'll need to know who to send the tell to. Parentheses define a capture group. It's just a way of saying "okay, whenever this regex matches and the alias fires, please save whatever this part of the match was, because I'm going to need to use it". We'll see how to use it in a second.

So after that capture group, then we've just got one or more spaces again. We don't care how many spaces are between the name and the message of the tell - we want it to go off if you type "tell Sarapis    hi!" too.

Then we've got a second capture group. In this one we've got a . and a *

The . is a lot like the \w, but it's even more broad. The . means any character. It'll match an a or a b or a # or a space or anything at all.

The * is a quantifier just like the +, but instead of meaning one or more, it means zero or more. Actually, maybe this should be a + too, shouldn't it? We don't want to do "tell Sarapis " and have it send Sarapis a tell that says "OOC: ", so let's change that to a +! A bad habit a lot of coders develop is using * when they should be using +, so let's try to avoid that here. Another bad habit is using . when you could use something more specific like \w or another character class, but we really do want to be able to send anything in the body of the tell, so we'll have to keep it here.

So now we've got:
^ooc +([\w']+) +(.+)$
So to sum up, that regex says: "watch for any string that contains, in this order: the beginning of the line, the letter o, the letter o, the letter c, one or more spaces, one or more word character or ' (and save that part of the match for later use), one or more spaces, one or more of any character (and save that part of the match for later use), the end of the line.

Well, that's about enough regex for a lifetime. You know just about all the regex you'll probably ever need now. The only really common thing you might need that wasn't covered here was \d, which is just a shortcut for [0-9], so it matches any number. From now on I'm not going to explain the regex in as much detail. If you need a refresher, google up a regex cheatsheet (there are thousands of them floating around). If you want to learn more in-depth, check out www.regular-expressions.info (you can easily learn all of regex, including the more-complicated stuff I haven't gone over and won't go over, in one afternoon).

The Script

This should be pretty easy. All we're going to do is call send_command, just like before. But this time we're not going to hard-code-in what to send beforehand, we're going to use those capture groups!

Our script will look like this:
send_command("tell " + args[1] + " *achaean OOC: " + args[2], true);
Remember that send_command takes two arguments separated by a comma. We always want to use true as the second argument to send_command, that way what we're sending is never going to end up triggering some random alias instead of just sending our text to the game.

Let's break down that first argument: "tell " + args[1] + " *achaean OOC: " + args[2]

We know what "tell " means and what " *achaean OOC: " mean. They're just strings, exactly like we sent last time. (Notice those spaces! They're not mistakes! We don't want to send "tellSarapis*achaean OOC:Hi!").

Now we need to get at that stuff in the capture groups from our regex. We do that using the built-in args array. This is an array that capture groups get saved to. Every trigger and alias has its own separate args array. What is an array? Think of it like a bunch of numbered slots. So args[0] is the first slot - JavaScript always starts counting at 0 (Nexus automatically puts the whole match into args[0], which isn't often very useful, but is occasionally handy). Then args[1] is the second slot in the args array, where whatever matches the first capture group gets saved and args[2] is the third slot where the second capture group gets saved, etc.

(args is an array that Nexus makes and fills for us, but we can actually make our own arrays too and that can be really useful. There's nothing magic about arrays, they're just numbered lists of things. We'll see how to do that some other time.)

Remember that our first capture group was supposed to capture the name of the person we want to send our OOC tell to. So we'll use args[1] for the recipient of our tell.

Our second capture group was supposed to capture the message we actually wanted to send, so we'll use args[2] for the content of the tell we want to send.

The thing our capture groups capture is actual text, which we remember comes in the form of strings.

So if we do "ooc Sarapis hi!", then args[0] is "ooc Sarapis hi!" (the whole match), args[1] is "Sarapis" (the string that matches our first capture group), and args[2] is "hi!" (the string that matches our second capture group).

So now we've got four strings:

"tell "
args[1]
" *achaean OOC: "
args[2]

But send_command doesn't take that many arguments, it only takes one string. We need to glue all of these together. We do that in JavaScript with +. So "blah" + "bloob" is the string "blahbloob". We just glue all of our four strings together with + and we've got our one string to pass to send_command.

Gluing strings together like this is called string concatenation. We're concatenating four strings into one string.

You might be starting to get confused about + here and that's a pretty reasonable confusion. + pulls triple-duty for us now. We can use it to add numbers, we can use it to glue strings together, and it means "one or more of the last thing" in regex. You're not crazy, this really is pretty confusing. Other languages use a different symbol for string concatenation (Lua, like in Mudlet, concatenates with ..) to make this less confusing and avoid some potential errors we'll probably have to talk about later. But for now it should be pretty easy: if + is between strings, it's concatenating them, if it's between numbers it's adding them, and if it's in a regex it means "one or more of the last thing".

So now we should be able to understand our script: we're just gluing together some strings and some stuff from our capture groups and sending it.

Did Something Go Wrong?

I haven't set you up for failure like last time, but maybe something isn't working the way you expect it to. First we want to check our regex with a display_notice just like last time. Let's quickly remind ourselves how to do that and have it display our capture groups too. Our script will look like this:
display_notice("Our alias fired!");
display_notice("Capture group 1: " + args[1]);
display_notice("Capture group 2: " + args[2]);
You should be well-equipped to understand how and why this works by now. If you're not sure, now's a good time to review before continuing.

Just like I mentioned in the last tutorial, you're going to want to get used to writing scripts like this - they'll let you see if your regex is working or if it's just some part of the script that isn't working right.

In fact, here's a useful trick. If your script isn't working, do something like this:
//send_command("tell " + args[1] + " *achaean OOC: " + args[2], true);

display_notice("Our alias fired!");
display_notice("Capture group 1: " + args[1]);
display_notice("Capture group 2: " + args[2]);
Remember the // thing? That's a comment, it doesn't get run. What we just did is commonly called commenting out code. This way we can test our alias with display notices, but we don't have to worry about typing in our script again. There's a way to comment out multiple lines at once so you don't have to type // at the beginning of every line in a long block of stuff you want to comment out, but I'l leave it up to you to google JavaScript comments if you want to learn that.

Comments

  • Two quick notes: "stard" in But I'm Even Lazier should say "start", and "We only have one capture in our oocr group now" in the last script section should say "We only have one capture group in our oocr pattern now".
  • I'm not sure why, but the way you lay out and present information glues me to my seat until I compulsively finish reading it all. Thanks for the write ups.
  • Come to think of it, if my textbooks would have been this well written, I wouldn't be watching my life slowly swirl down the toilet bowel.
  • SzanthaxSzanthax San Diego
    Agree withs future targossan @Procelean



  • This is really well done once again Tael, thanks!

Sign In or Register to comment.