range
action is defined like the following:range
as a for
loop.pipeline
is either an slice (a cslice or normal slice) or map (a _dict _or sdict). We also refer to maps as key-value pairs, as that is what they are (keys corresponding to values). Iterating
is a fancy word for "looping over", or doing an action for every element in the pipeline.range
.addRoleID "123456789"
is incorrect and a bad practice (even though it works, as YAGPDB handles the string in this particular example). The correct way would be addRoleID 123456789
. What is this addRoleID
function we see here? Look no further...addRoleID <role id>
addRoleID
. It's very simple - it adds a role to the user who triggered the command, by ID.giveRoleName <user id> <role name>
and giveRoleID <user id> <role id>
giveRoleName
and giveRoleID
are how you'd do it. It gives the user provided the role ID provided (or role name, for giveRoleName
. Very simple.removeRoleID <role id> (optional delay)
takeRoleName <user id> <role name> (optional delay)
and takeRoleID <user id> <role id> (optional delay)
not
operator - i.e not (hasRoleID 123456789).
hasRoleID <role id>
and hasRoleName <role name>
targetHasRoleID <user id> <role id>
and targetHasRoleName <user id> <role name>
addRoleID
template (or a similar template).range
is to reduce repetitive code. Given a template or function which is executed multiple times (in our case, addRoleID
) with varying arguments, we can put these arguments into a cslice and then loop over it with range, calling the function each iteration. We'll explain the abstract part of this later, but here's the simplified code for the above:{{ $ids := cslice x y z a b c }}
: We construct a slice of role IDs. Very straightforward.\{{ range $ids }}
: Here is where the fun really starts. The range $ids
part declares the range statement itself. We declare it with the range pipeline
syntax rather than range $key, $value := pipeline
syntax as in this specific case we do not need the index of the slice we are iterating over.\{{- addRoleID . -}}
Let's start with the {{-
rather than just {{
: White space is rendered as output in a range action, meaning that if you have newlines or indents and are ranging over a large enough set of data, you may find that you're getting a "Response exceeded 2K characters" error for apparently no reason. For this reason, we strip the whitespace in both directions in every iteration, instead of at just the start and end. You will need to add this ({{-
and -}}
) for every line in your range
action. Note that if you have multiple lines in your range action, -}}
only needs to be added for the last line in the range action, and the ones before it can stay as }}
. Note that if you nest your range actions, you will need to strip that as well.
In this specific case, we do not necessarily need these as there 1) is not enough data for it to hit a 2K character error and 2) we send no text afterwards, so newlines would not be an issue. However, it's good practice and prevents some frustration when you see that strange "Response exceeded 2K characters" error without apparent cause.\addRoleID
function itself. We see the addRoleID
function, but we also see this .
. Normally, the dot refers to all the data available in CCs: for example, .Guild
. However, when in a range
or with
action (covered later in this section) the dot is changed to the current iteration value. In this case, .
would be either x, y, z, a, b or c, as those are the values of the slice $ids
.\{{- end }}
. The end
action closes off the range action, and the {{-
strips all white space to the left (read above for why this is necessary).addRoleName
is not an available template, as of the time of writing) we have to use giveRoleName
. This distinction is very important because we have to use the user ID - you'll understand why later..
is changed to the current iteration value in the range action. This means that in the above code, .
is either x, y, z, a, b, c, or d. This means that it is a _string. _Strings do not have a user property, meaning that this will error. But how will we access the user ID?$
is always set to the starting value in a template (or CC). This means that it has all the properties like $.User
(which is the exact same as .User
). This is a nifty trick when working with range
, and as we will see, with
.$
is a variable (which you can write to). This means that if you added {{ $ := "hello world!" }}
in the above code, $ would no longer have the properties User
and all the other properties on .
seq
comes in. seq
has the syntax seq start end
where start < end
. It generates a sequence which can be expressed as[start, end)
. If you're not familiar with this notation, it simply means that start
is included while end
is not. For example - seq 0 5
produces [0, 1, 2, 3, 4]
.range
, our code would have looked like this:range
- but how? Try it yourself, and if you can't do it, feel free to come back.range
into a variable, however, the method of doing so might not be the most obvious at first. Let's take a look:joinStr
. Note the use of =
rather than :=
(refer to Control Flow 1 for why we do this).with
operator. What is it? What does it do? That's what this bonus chapter will cover.with
is an action, just like if
. It checks whether the pipeline provided is true, if so, it continues - but with one difference. It changes the value of the .
inside the with
action to the pipeline provided to with
. Look at this example for more details:else if
cannot be used with the with
action, only else
. In the optional else
clause, the dot is left unaffected. See below:with
?with
. It is a tool to help shorten your code in some cases, but it does not help readability for others who might not know what with
is. Avoid it unless you know exactly what it does. For example, let's show two cases where with
is used:.
is used extensively and it's not too clear what it is at first glance (there are two with
actions, nested within each other). A better way to write this code would be to write it normally with if
, as with
does not provide any tangible advantage here.with
for the first statement, rather, it is only used for the reFindAllSubmatches
call. This is a much better use case, because if we simply used if
, we would have to repeat that line of code. With with
, in this case, we shorten our code, save function calls, and keep readability.index
can be called with more than 2 arguments? index X 0 1
is equivalent to calling index (index X 0) 1
and so on. This works well with reFindAllSubmatches
, as it returns a 2D slice of matches rather than a normal slice.