"Go is all about type... Type is life." // William Kennedy


All available data that can be used in YAGPDB's templating "engine" which is slightly modified version of Golang's stdlib text/template package; more in depth and info about actions, pipelines and global functions like printf, index, len,etc > https://golang.org/pkg/text/template/ . This section is meant to be a concise and to the point reference document for all available templates/functions. For detailed explanations and syntax guide refer to the learning resource.
Legend: at current state this is still prone to formatting errors, but everything in a code block should refer to a function, parts of a template's action-structure or output returned by YAGPDB; single word/literal-structure in italics refers to type. Methods and fields (e.g. .Append, .User) are usually kept in standard formatting. If argument for a function is optional, it's enclosed in parenthesis ( ). If there are many optional arguments possible, it's usually denoted by 3-dot ...ellipsis. If functions or methods are denoted with an accent, tilde ~, they are not yet deployed in actual YAGPDB bot but are already in master code branch.
Always put curly brackets around the data and "actions you perform" you want to formulate as a template's action-structure like this: {{.User.Username}}
This {{ ... }} syntax of having two curly brackets aka braces around context is always necessary to form a template's control structure also known as an action with methods and functions stated below.
Templating system uses standard ASCII quotation marks: 0x22 > " for straight double quotes, 0x27 > 'for apostrophes and 0x60 ` for backticks; so make sure no "smart-quotes" are being used.

The Dot and Variables

The dot (also known as cursor) {{ . }} encompasses all active data available for use in the templating system, in other words it always refers to current context. From official docs > "Execution of the template walks the structure and sets the cursor, represented by a period . and called "dot", to the value at the current location in the structure as execution proceeds." All following fields/methods/objects like User/Guild/Member/Channel etc are all part of that dot-structure and there are some more in tables below. $ has a special significance in templates, it is set to the starting value of a dot. This means you have access to the global context from anywhere - e.g., inside range/with actions. $ for global context would cease to work if you redefine it inside template, to recover it {{ $ := . }}. $ also denotes the beginning of a variable, which maybe be initialized inside a template action. So data passed around template pipeline can be initialized using syntax > $variable := value. Previously declared variable can also be assigned with new data > $variable = value, it has to have a white-space before it or control panel will error out. Variable scope extends to the end action of the control structure (if, with, or range) in which it is declared, or to the end of custom command if there are no control structures - call it global scope.
The ID of currently executing custom command in type of int64.
Shows run count of triggered custom command, although this is not going to be 100% accurate as it's cached up to 30 minutes.
Returns boolean true/false whether guild is premium of YAGPDB or not.


A powerful component of templates is the ability to stack actions - like function calls, together - chaining one after another. This is done by using pipes |. Borrowed from Unix pipes, the concept is simple: each pipeline’s output becomes the input of the following pipe. One limitation of the pipes is that they can only work with a single value and that value becomes the last parameter of the next pipeline. Example: {{randInt 41 | add 2}} would pipelinerandInt function's return to addition add as second parameter and it would be added to 2; this more simplified would be like {{40 | add 2}} with return 42. If written normally, it would be {{ add 2 (randInt 41) }}. Same pipeline but using a variable is also useful one -{{$x:=40 | add 2}} would not return anything as printout, 40 still goes through pipeline to addition and 42 is stored to variable $x whereas {{($x:=40) | add 2}} would return 42 and store 40 to $x.
Pipes are useful in select cases to shorten code and in some cases improve readability, but they should not be overused. In most cases, pipes are unnecessary and cause a dip in readability that helps nobody.


The user's username together with discriminator.
The user's avatar ID.
.User.AvatarURL "256"
Gives the URL for user's avatar, argument "256" is the size of the picture and can increase/decrease twofold (e.g. 512, 1024 or 128, 64 etc.).
Determines whether the target user is a bot - if yes, it will return true.
The user's discriminator (The four digits after a person's username).
The user's ID.
Mentions user.
The user's username together with discriminator as string type.
The user's username.
Only works with join and leave messages (not join dms). It will determine does the username contain an invite link.
Only works with join and leave messages (not join DMs). This can be used to send the real username to a staff channel when invites are censored.
userArg mention/userID
Function that can be used to retrieve .User object from a mention or userID.
{{(userArg .User.ID).Mention}} mentions triggering user. Explained more in this section's snippets.
pastUsernames userID offset
Returns a slice of type [ ]*logs.CCNameChange having fields .Name and .Time of previous 15 usernames and skips offset number in that list.
{{range pastUsernames .User.ID 0}} {{.Name}} - {{.Time.Format "Jan _2 2006"}} {{end}}
pastNicknames userID offset
Same as pastUsernames.

This section's snippets:

{{(userArg .Guild.OwnerID).String}} this template's action-structure returns Guild/Server owner's username and discriminator as of type string. First, userArg function is given .Guild.OwnerID as argument (what it does, explained below). The parentheses surrounding them make userArg function return .User as .User object which is handled further by .String method (ref..User.String), giving a result like > YAGPDB#8760.

Guild / Server

Outputs the AFK channel ID.
Outputs the time when a user gets moved into the AFK channel while not being active.
Outputs a slice of channels in the guild with type []dstate.ChannelState.
Outputs the default message notification setting for the guild.
Outputs whether guild is embeddable (e.g. widget) or not, true / false. DEPRECATED.
Outputs a list of emojis in the guild with type discordgo.Emoji.
Outputs the explicit content filter level for the guild.
The list of enabled guild features of type []string.
Outputs the icon hash ID of the guild's icon. Setting full icon URL is explained here.
Outputs the ID of the guild.
Outputs the number of users on a guild.
required MFA level for the guild. If enabled, members with moderation powers will be required to have 2-factor authentication enabled in order to exercise moderation powers.
Outputs the name of the guild.
Outputs the ID of the owner.
Outputs the region of the guild.
Outputs all roles and indexing them gives more information about the role. For example {{len .Guild.Roles}} gives you how many roles are there in that guild. Role struct has following fields.
Outputs the splash hash ID of the guild's splash.
The id of the channel where guild notices such as welcome messages and boost events are posted.
Outputs the required verification level for the guild.
Outputs a slice of voice states (users connected to VCs) with type []discordgo.VoiceState.
Outputs the channel ID for the server widget.
Outputs whether or not the Server Widget is enabled or not.
.Guild.GetChannel id
Gets the channel with the ID provided, returning a *dstate.ChannelState.
.Guild.GetEmoji id
Gets the guild emoji with the ID provided, returning a *discordgo.Emoji.
.Guild.GetMemberPermissions channelID memberID memberRoles
Computes the permissions that the member has in the channel provided, taking into account the roles of the member. Example: {{.Guild.GetMemberPermissions .Channel.ID .Member.User.ID .Member.Roles}} would retrieve the permission bit the triggering member has in the context channel.
.Guild.GetRole id
Gets the role with the ID provided, returning a *discordgo.Role.
.Guild.GetVoiceState userID
Gets the voice state of the user ID provided, returning a *discordgo.VoiceState. Example code to show if user is in VC or not: {{if .Guild.GetVoiceState .User.ID}} user is in voice channel {{else}} user is not in voice channel {{end}}


When member joined the guild/server of type discordgo.Timestamp. Method .Parse will convert this to of type time.Time.
The nickname for this member.
A slice of role IDs that the member has.
Underlying user on which the member is based on.
editNickname "newNick"
Edits triggering user's nickname, argument has to be of type string. YAGPDB's highest role has to be above the highest role of the member.
getMember mention/userID
Function returns Member object having above methods.
{{(getMember .User.ID).JoinedAt}} is the same as {{.Member.JoinedAt}}
Returns the count of online users/members on current server.
DEPRECATED. This will no longer work properly and will likely be removed in a future update. Returns the count of online bots.


Bitrate used; only set on voice channels.
Guild ID of the channel.
The ID of the channel.
Whether the channel is private.
Whether the channel is a thread.
Mentions the channel object.
The name of the channel.
Outputs whether this channel is NSFW or not.
The ID of the channel's parent (category), returns 0 if none.
Channel position from top-down.
The topic of the channel.
The type of the channel. Explained here.
editChannelName channel "newName"
Function edits channel's name. channel can be either ID, "name" or even nil if triggered in that channel name change is intended to happen. "newName" has to be of type string. For example >{{editChannelName nil (joinStr "" "YAG - " (randInt 1000))}}
editChannelTopic channel "newTopic"
Function edits channel's topic/description. channel can be either ID, "name" or even nil if triggered in that channel name change is intended to happen. "newTopic" has to be of type string. For example >
{{editChannelTopic nil "YAG is cool"}}
getChannel channel
Function returns full channel object of given channel argument which can be either its ID, name or nil for triggering channel, and is of type *dstate.ChannelState. For example > {{(getChannel nil).Name}} returns the name of the channel command was triggered in.
getChannelOrThread channel
Returns type*templates.CtxChannel corresponding to Channel object.
getThread channel
Returns type *templates.CtxChannel corresponding to Channel object.


Attachments of this message (slice of attachment objects).
Author of the message (User object).
Channel ID this message is in.
Text content on this message.
The time at which the last edit of the message occurred, if it has been edited. As with .Message.Timestamp, it is of type discordgo.Timestamp.
Embeds of this message (slice of embed objects).
Guild ID in which the message is.
ID of the message.
Discord link to the message. *
Whether the message mentions everyone.
The roles mentioned in the message, returned as a slice of type discordgo.IDSlice.
Users this message mentions, returned as a slice of type []*discordgo.User.
Whether this message is pinned.
Reactions on this message (only available from getMessage).
Message object associated by message_reference, like a message that was replied to.
Timestamp of the message in type discordgo.Timestamp (use .Message.Timestamp.Parse to get type time.Time and .Parse.String method returns type string).
Whether the message is text-to-speech. *
The type of the message.
If the message is generated by a webhook, this is the webhook's id
List of everything that is passed to .Message.Content. .Args is a slice of type string.
.Cmd is of type string and shows all arguments that trigger custom command, part of .Args. Starting from {{index .Args 0}}.
List of all the arguments passed after .Cmd (.Cmd is the actual trigger) .CmdArgs is a slice of type string. Examples in misc. snippets.
"Strips" or cuts off the triggering part of the message and prints out everything else after that. Bear in mind, when using regex as trigger, for example "day" and input message is "Have a nice day my dear YAG!" output will be "my dear YAG!" - rest is cut off.
* denotes field that will not have proper return when using getMessage function.
More information about the Message object can be found here.


This is available and part of the dot when reaction trigger type is used.
Returns reaction object which has following fields UserID, MessageID, Emoji.(ID/Name/...), ChannelID, GuildID. Emoji.ID is the ID of the emoji for custom emojis, and Emoji.Name will hold the unicode emoji if its a default one. (otherwise the name of the custom emoji).
Returns type string, an correctly formatted API name for use in the MessageReactions endpoints. For custom emojis it is emojiname:ID.
Returns a correctly formatted emoji for use in Message content and embeds. It's equal to <:.Reaction.Emoji.APIName> and <a:.Reaction.Emoji.APIName> for animated emojis.
Returns a boolean type bool true/false indicating whether reaction was added or removed.
Returns the message object reaction was added to.
{{range .ReactionMessage.Reactions}} {{.Count}} - {{.Emoji.Name}} {{end}}
Returns emoji count and their name.
Has an alias .Message and it works the same way.


Time in general uses Golang's time package library > https://golang.org/pkg/time/#time and also this although slightly different syntax all applies here > https://gobyexample.com/time.
Gives you Discord Epoch time in time.Time. {{.DiscordEpoch.Unix}} would return in seconds > 1420070400.
Gives you Unix Epoch time in time.Time.
Variable of time.Duration type and returns 1 hour > 1h0m0s.
Variable of time.Duration type and returns 1 minute > 1m0s.
Variable of time.Duration type and returns 1 second > 1s.
Gets the current time, value is of type time.Time which can be used in a custom embed. More info here.
formatTime Time "arg"
Outputs given time in RFC822 formatting, first argument Time shows it needs to be of type time.Time, also with extra layout if second argument is given - e.g. {{formatTime currentUserCreated "3:04PM"}} would output 11:22AM if that would have been user's creating time.
Returns given integer (whole number) or time.Duration argument in nanoseconds in human readable format - as how long it would take to get towards given time - e.g. {{humanizeDurationHours 9000000000000000000}} returns 285 years 20 weeks 6 days and 16 hours. More in Snippets.
Sames as humanizeDurationHours, this time duration is returned in minutes - e.g. {{humanizeDurationMinutes 3500000000000}} would return 58 minutes.
Sames as both humanize functions above, this time duration is returned in seconds - e.g. {{humanizeDurationSeconds 3500000000000}} would return 58 minutes and 20 seconds.
Returns time passed since given argument of type time.Time in human readable format - e.g. {{humanizeTimeSinceDays currentUserCreated}}
newDate year month day hour minute second [timezone]
Returns new type time.Time object in UTC using given syntax (all arguments need to be of type int), for example > {{humanizeDurationHours ((newDate 2059 1 2 12 34 56).Sub currentTime)}} will give you how much time till year 2059 January 2nd 12:34:56. More examples in Snippets.
timezone is an optional string argument which uses golang's LoadLocation function and ZONEINFO syntax. For example: {{newDate 2020 4 20 12 34 56 "Atlantic/Reykjavik"}} would return that time in GMT+0.
Discord Timestamp Styles referenced here can be done using print function e.g.
{{print "<t:" currentTime.Unix ":F>"}} for "Long Date/Time" formatting.

This section's snippets:

  • To demonstrate humanizeDurationHours and also how to parse a timestamp, output will be like whois command shows user's join server age. {{humanizeDurationHours (currentTime.Sub .Member.JoinedAt.Parse)}}
  • To demonstrate newDate to get Epoch times. {{$unixEpoch := newDate 1970 1 1 0 0 0}} in seconds > {{$unixEpoch.Unix}} {{$discordEpoch := newDate 2015 1 1 0 0 0}} in seconds > {{$discordEpoch.Unix}}

Custom Types

Golang has built-in primitive data types (int, string, bool, float64, ...) and built-in composite data types (array, slice, map, ...) which also are used in custom commands. YAGPDB's templating "engine" has currently two user-defined, custom data types - templates.Slice and templates.SDict. There are other custom data types used like discordgo.Timestamp, but these are outside of the main code of YAGPDB, so not explained here further. Type time.Time is covered in its own section. Custom Types section discusses functions that initialize values carrying those templates.Slice (abridged to cslice), templates.SDict (abridged to sdict) types and their methods. Both types handle type interface{} element. It's called an empty interface which allows a value to be of any type. So any argument of any type given is handled. (In "custom commands"-wise mainly primitive data types, but slices as well.)
Reference type-like behaviour: Slices and dictionaries in CC exhibit reference-type like behavior, which may be undesirable in certain situations. That is, if you have a variable $x that holds a slice/dictionary, writing $y := $x and then mutating $y via Append/Set/Del/etc. will modify $x as well. For example:
{{ $x := sdict "k" "v" }}
{{ $y := $x }}
{{ $y.Set "k" "v2" }} {{/* modify $y */}}
{{ $x }}
{{/* k has value v2 on $x as well -
that is, modifying $y changed $x too. */}}
If this behaviour is undesirable, copy the slice/dictionary via cslice.AppendSlice or a range + Set call .
{{ $x := sdict "k" "v" }}
{{ $y := sdict }}
{{ range $k, $v := $x }} {{- $y.Set $k $v -}} {{ end }}
{{ $y.Set "k" "v2" }}
{{ $x }} {{/* $x is unmodified - k still has value v */}}
Note that this performs a shallow copy, not a deep copy - if you want the latter you will need to perform the aforementioned operation recursively.


templates.Slice - This is a custom composite data type defined using an underlying data type []interface{} . It is of kind slice (similar to array) having interface{} type as its value and can be initialized using cslice function. Retrieving specific element inside templates.Slice is by indexing its position number.
cslice value1 value2 ...
Function creates a slice of type templates.Slice that can be used elsewhere (as an argument for cembed and sdict for example).
Example: cslice 1 "2" (dict "three" 3) 4.5 returns [1 2 map[three:3] 4.5], having length of 4 and index positions from 0 to 3. Notice that thanks to type interface{} value, templates.Slice elements' inherent type does not change.
.Append arg
Creates a new cslice having given argument appended fully by its type to current value. Has max size of 10 000 length.
.AppendSlice arg
Creates a new cslice from argument of type slice appended/joined with current value. Has max size of 10 000 length.
.Set int value
Changes/sets given int argument as index position of current cslice to new value. Note that .Set can only set indexes which already exist in the slice.
.StringSlice strict-flag
Compares slice contents - are they of type string, based on the strict-flag which is boolean and is by default false. Under these circumstances if the element is a string then those elements will be included as a part of the []string slice and rest simply ignored. Also time.Time elements - their default string notation will be included. If none are string an empty []string slice is returned.
If strict-flag is set to true it will return a []string only if all elements are pure string, else <no value> is returned.
Example in this section's Snippets.

This section's snippets:

  • To demonstrate .StringSlice {{(cslice currentTime.Month 42 "YAPGDB").StringSlice}} will return a slice [February YAGPDB]. If the flag would have been set to true - {{...).StringSlice true}}, all elements in that slice were not strings and <no value> is returned.
General example:
Creating a new cslice: {{ $x := (cslice "red" "red") }} **{{ $x }}**
Appending to current cslice data
and assigning newly created cslice to same variable:
{{ $x = $x.Append "green" }} **{{ $x }}**
Setting current cslice value in position 1:
{{ $x.Set 1 "blue" }} **{{ $x }}**
Appending a slice to current cslice data
but not assigning newly created cslice to same variable:
**{{ $x.AppendSlice (cslice "yellow" "magenta") }}**
Variable is still: **{{ $x }}**
Type of variable: **{{ printf "%T" $x }}**


templates.SDict - This is a custom composite data type defined on an underlying data type map[string]interface{}. This is of kind map having string type as its key and interface{} type as that key's value and can be initialized using sdict function. A map is key-value store. This means you store value and you access that value by a key. Map is an unordered list and the number of parameters to form key-value pairs must be even, difference to regular map is that templates.SDict is ordered by its key. Retrieving specific element inside templates.Sdict is by indexing its key.
sdict "key1" value1 "key2" value2 ...
Like dict function, creating a templates.SDict type map, key must be of type string. Can be used for example in cembed. If only one argument is passed to sdict function having type map[string]interface{}; for example .ExecData and data retrieved from database can be of such type if sdict was used, it is converted to a new sdict.
Example: sdict "one" 1 "two" 2 "three" (cslice 3 4) "five" 5.5 returns unordered map[five:5.5 one:1 three:[3 4] two:2], having length of four and index positions are its keys. Notice that thanks to type interface{} value, templates.SmDict elements' inherent type does not change.
.Del "key"
Deletes given key from sdict.
.Get "key"
Retrieves given key from sdict.
.Set "key" value
Changes/sets given key to a new value or creates new one, if no such key exists in sdict.
Creating sdict: {{ $x := sdict "color1" "green" "color2" "red" }} **{{ $x }}**
Retrieving key "color2": **{{ $x.Get "color2" }}**
Changing "color2" to "yellow": {{ $x.Set "color2" "yellow" }} **{{ $x }}**
Adding "color3" as "blue": {{ $x.Set "color3" "blue" }} **{{ $x }}**
Deleting key "color1" {{ $x.Del "color1" }} and whole sdict: **{{ $x }}**
Tip: Previously, when saving cslices, sdicts, and dicts into database, they were serialized into their underlying native types - slices and maps. This meant that if you wanted to get the custom type back, you needed to convert manually, e.g. {{cslice.AppendSlice $dbSlice}} or {{sdict $dbDict}}. Recent changes to YAG have changed this: values with custom types are now serialized properly, making manual conversion unnecessary.


Functions are underappreciated. In general, not just in templates. // Rob Pike

Type conversion

json value
Traverses given value through MarshalJSON (more here) and returns it as type string. For example {{json .TimeHour}} outputs type string; before this .TimeHour was of type time.Duration. Basically it's good to use if multistep type conversion is needed (toString (toInt value) ) and certain parts of cembed need this for example.
structToSdict struct
Function converts exported field-value pairs of a struct to an sdict. For example it is useful for editing embeds, rather than having to reconstruct the embed field by field manually. Exampe: {{$x := cembed "title" "Something rules!" "color" 0x89aa00}} {{$x.Title}} {{$x = structToSdict $x}} {{- $x.Set "Title" "No, YAGPDB rules!!!" -}} {{$x.Title}} {{$x}} will return No, YAGPDB rules!!! and whole sdict-mapped cembed.
toByte "arg"
Function converts input to a slice of bytes - meaning []uint8. {{toByte "YAG€"}} would output [89 65 71 226 130 172]. toString is capable of converting that slice back to string.
Converts the argument, number or string to type time.Duration - more duration related methods here. Number represents nanoseconds. String can be with time modifier (second, minute, hour, day etc) s, m, h, d, w, mo, y,without a modifier string will be converted to minutes. Usage:(toDuration x). Example in section's Snippets.
Converts argument (int or string type of a number) to type float64. Usage: (toFloat x). Function will return 0, if type can't be converted to float64.
Converts something into an integer of type int. Usage: (toInt x). Function will return 0, if type can't be converted to int.
Converts something into an int64. Usage: (toInt64 x). Function will return 0, if type can't be converted to int64.
toRune "arg"
Function converts input to a slice of runes - meaning []int32. {{toRune "YAG€"}}would output [89 65 71 8364]. These two functions - the one above, are good for further analysis of Unicode strings. toString is capable of converting that slice back to string.
Has alias str. Converts some other types into a string. Usage: (toString x).

This section's snippets:

  • To demonstrate toDuration, outputs 12 hours from current time in UTC. {{(currentTime.Add (toDuration (mult 12 .TimeHour))).Format "15:04"}}is the same as{{(currentTime.Add (toDuration "12h")).Format "15:04"}} or{{(currentTime.Add (toDuration 43200000000000)).Format "15:04"}}
Tip: You can convert a Unicode code point back to its string equivalent using printf "%c". For example, printf "%c" 99 would result in the string c as 99 is the Unicode code point for c.printf is briefly covered later on in the next section, further documentation can be found here. Cheat sheet here.

String manipulation

joinStr "separator" "str1" (arg1)(arg2) "str2" ...
Joins several strings into one, separated by the first argument"separator", example:{{joinStr "" "1" "2" "3"}} returns 123. Also if functions have string, []string or easily convertible return, they can be used inside joinStr e.g. {{joinStr "" "Let's calculate " (add (mult 13 3) 1 2) ", was returned at " (currentTime.Format "15:04") "."}}
lower "string"
Converts the string to lowercase.
print, printf, println
These are GO template package's predefined functions and are aliases for fmt.Sprint, fmt.Sprintf and fmt.Sprintln. Formatting is also discussed here. printf cheat sheet here. printf is usable for example to determine the type of the value > {{printf "%T" currentTime}} outputs currentTime functions output value type of time.Time. In many cases, printf is a great alternative to joinStr for concatenate strings.
reFind "regex" "string"
Compares "string" to regex pattern and returns first match. {{reFind "AG" "YAGPDB is cool!"}}returns AG (regex pattern is case sensitive).
reFindAll "regex" "string" [count]
Adds all regex matches from the "string" to a slice. Example in section's Snippets. Optional count determines how many matches are made. Example: {{reFindAll "a*" "abaabaccadaaae" 4}} would return [a aa a ].
reFindAllSubmatches "regex" "string" [count]
Returns whole-pattern matches and also the sub-matches within those matches as slices inside a slice. {{reFindAllSubmatches "(?i)y([a-z]+)g" "youngish YAGPDB"}} returns [[young oun] [YAG A]] (regex pattern here is case insensitive). Optional count works the same way as for reFindAll. So example above with count set to 1 would return [[young oun]].
reReplace "regex" "string1" "string2"
Replaces "string1" contents with "string2" at regex match point. {{reReplace "I am" "I am cool!" "YAGPDB is"}}returns YAGPDB is cool! (regex pattern here is case sensitive).
reSplit "regex" "string" [count]
reSplit slices string into substrings separated by the regex expression and returns a slice of the substrings between those expression matches. The optional count determines the number of substrings to return. If count is negative number the function returns all substrings, if 0 then none. If count is bigger than 0 it returns at most n substrings, the last substring being the unsplit remainder.
Example: {{ $x := reSplit "a" "yagpdb has a lot of fame" 5}}
{{$x}} {{index $x 3}} would return [y gpdb h s lot of f me] and lot of f.
slice "string"|slice integer (integer2)
Function's first argument must be of type string or slice.
Outputs the "string" after cutting/slicing off integer (numeric) value of symbols (actually starting the string's index from integer through integer2) - e.g. {{slice "Fox runs" 2}}outputs x runs. When using also integer2 - e.g. {{slice "Fox runs" 1 7}}, it outputs ox run. For slicing whole arguments, let's say words, see example in section's Snippets.
This slice function is not the same as basic dynamically-sized slice data type discussed in this reference doc. Also it's custom, not having 3-indices as the default one from text/template package.
split "string" "sepr"
Splits given "string" to substrings separated by "sepr"arg and returns new slice of the substrings between given separator e.g. {{split "YAG, is cool!" ","}} returns [YAG is cool!] slice where YAG is at index position 0 and is cool! at index position 1. Example also in section's Snippets.
title "string"
Returns string with the first letter of each word capitalized.
upper "string"
Converts the string to uppercase.
urlescape "string"
Escapes the string so it can be safely placed inside a URL path segment - e.g. "Hello, YAGPDB!" becomes "Hello%2C%20YAGPDB%21"
Special information we can include in the string is escape sequences. Escape sequences are two (or more) characters, the first of which is a backslash \, which gives the remaining characters special meaning - let's call them metacharacters. The most common escape sequence you will encounter is \n, which means "newline".
With regular expression patterns - when using quotes you have to "double-escape" metacharacters starting with backslash. You can use backquotes/ticks to simplify this:{{reFind "\\d+" (toString 42)}} versus {{reFind `\d+` (toString 42)}}

This section's snippets:

  • {{$args:= (joinStr " " (slice .CmdArgs 1))}} Saves all the arguments except the first one to a variable $args.
  • To demonstrate usage of split function. > {{$x := "Hello, World, YAGPDB, here!"}} {{range $k, $v := (split $x ", ")}}Word {{$k}}: __{{$v}}__ {{end}}
  • To demonstrate usage of reFindAll. > Before regex: {{$msg := "1 YAGPDB and over 100000 servers conquered."}} {{$re2 := reFindAll "[0-9]+" $msg}} {{$msg}} After regex matches: {{joinStr " " "Only" (index $re2 0) "YAGPDB and already" (index $re2 1) "servers captured."}}

Math functions

add x y z ...
Returns x + y + z + ..., detects first number's type - is it int or float and based on that adds. (use toFloat on the first argument to force floating point math.){{add 5 4 3 2 -1}} sums all these numbers and returns 13.
sub x y z ...
Returns x - y -z - ... Works like add, just subtracts.
mult x y z ...
Multiplication, like add or div, detects first number's type. {{mult 3.14 2}} returns 6.28
div x y z ...
Division, like add or mult, detects first number's type first. {{div 11 3}} returns 3 whereas {{div 11.1 3}} returns 3.6999999999999997
fdiv x y z ...
Meant specifically for floating point numbers division.
log x base
Log is a logarithm function using (log base of x). Arguments can be any type of numbers, as long as they follow logarithm logic. Return value is of type float64. If base argument is not given It is using natural logarithm (base e - The Euler's constant) as default, also is the default to change the base.{{ log "123" 2 }} will return 6.94251450533924.
mod x y
Mod returns the floating-point remainder of x/y. mod 17 3 returns 2 of type float64.
pow x y
Pow returns x**y, the base-x exponential of y which have to be both numbers. Type is returned as float64. {{ pow 2 3 }} returns 8.
randInt (stop, or start stop)
Returns a random integer between 0 and stop, or start - stop if two args are provided.
Result will be start <= random number < stop. Example in section's Snippets.
Returns the nearest integer, rounding half away from zero. Regular rounding > 10.4 is 10 and 10.5 is 11. All round functions return type float64, so use conversion functions to get integers. For more complex rounding, example in section's Snippets.
Returns the least integer value greater than or equal to input or rounds up. {{roundCeil 1.1}} returns 2.
Returns the greatest integer value less than or equal to input or rounds down. {{roundFloor 1.9}} returns 1.
Returns the nearest integer, rounding ties to even. {{roundEven 10.5}} returns 10 {{roundEven 11.5}} returns 12.
Returns the square root of a number as type float64. {{sqrt 49}} returns 7, {{sqrt 12.34 | printf "%.4f"}} returns 3.5128

This section's snippets:

  • {{$d := randInt 10}} Stores random int into variable $d (a random number from 0-9).
  • To demonstrate rounding float to 2 decimal places. {{div (round (mult 12.3456 100)) 100}} returns 12.35 {{div (roundFloor (mult 12.3456 100)) 100}} returns 12.34

Message functions

addMessageReactions channel messageID emojis...
Same as addReactions or addResponseReactions, but can be used on any messages using its ID. channel can be either nil, channel's ID or its name. Example in section's Snippets.
addReactions "👍" "👎" ...
Adds each emoji as a reaction to the message that triggered the command (recognizes Unicode emojis and emojiName:emojiID).
addResponseReactions "👍" "👎" ...
Adds each emoji as a reaction to the response message (recognizes Unicode emojis and emojiName:emojiID).
complexMessage "content" args "embed" args "file" args
complexMessage creates a so-called bundle of different message fields for sendMessage... functions to send them out all together. Its arguments need to be preceded by predefined keys "content" for regular text, "embed" for embed arguments created by cembed or sdict, "file" for printing out content as a file (max 100 000 characters ca100kB). Example in this section's Snippets.
complexMessageEdit "content" args "embed" args
Special case for editMessage function - either if complexMessage is involved or works even with regular message. Has two parameters "content" and "embed" to edit regular text part or embed part. If "embed" is set to nil, it deletes whole embed. Example in this section's Snippets.
deleteAllMessageReactions channel messageID (emojis...)
Deletes all reactions pointed message has. channel can be ID, "name" or nil. emojis argument is optional and works like it's described for the function deleteMessageReaction.
deleteMessageReaction channel messageID userID emojis...
Deletes reaction(s) from a message. channel can be ID, "name" or nil. emojis argument can be up to 10 emojis, syntax is emojiName for Unicode/Discord's default emojis and emojiName:emojiID for custom emotes. Example: {{deleteMessageReaction nil (index .Args 1) .User.ID "👍" "👎"}} will delete current user's reactions with thumbsUp/Down emotes from current running channel's message which ID is given to command as first argument (index .Args 1). Also usable with Reaction trigger.
deleteMessage channel messageID (delay)
Deletes message with given messageID from channel. Channel can be either nil, channel's ID or its name. (delay) is optional and like following two delete functions, it defaults to 10 seconds, max being 1 day or 86400 seconds. Example in section's Snippets.
deleteResponse (delay)
Deletes the response after a certain time from optional delay argument (max 86400 seconds = 1 day). Defaults to 10 seconds.
deleteTrigger (delay)
Deletes the trigger after a certain time from optional delay argument (max 86400 seconds = 1 day). Defaults to 10 seconds.
editMessage channel messageID newMessageContent
Edits the message in channel, channel can be either nil, channel's ID or "name". Light example in section's Snippets.
editMessageNoEscape channel messageID newMessageContent
Edits the message in channel and has same logic in escaping characters as sendMessageNoEscape.
getMessage channelID messageID
Returns a Message object.
sendDM "message here"
Sends the user a direct message, only one DM can be sent per custom command (accepts embed objects). YAG will only DM triggering user.
sendMessage channel message
Sends message (string or embed) in channel, channel can be either nil, the channel ID or the channel's "name".
sendMessageNoEscape channel message
Sends message (string or embed) in channel, channel can be either nil, the channel ID or the channel "name". Doesn't escape mentions (e.g. role mentions or @here/@everyone).
sendMessageRetID channel message
Same as sendMessage, but also returns messageID to assigned variable for later use. Example in section's Snippets.
sendMessageNoEscapeRetID channel message
Same as sendMessageNoEscape, but also returns messageID to assigned variable for later use.

This section's snippets:

  • Sends message to current channel nil and gets messageID to variable $x. Also adds reactions to this message. After 5 seconds, deletes that message. >
    {{$x := sendMessageRetID nil "Hello there!"}} {{addMessageReactions nil $x "👍" "👎"}} {{deleteMessage nil $x 5}}
  • To demonstrate sleep and slightly also editMessage functions. > {{$x := sendMessageRetID nil "Hello"}} {{sleep 3}} {{editMessage nil $x "There"}} {{sleep 3}} {{sendMessage nil "We all know, that"}} {{sleep 3}} YAGPDB rules!
  • To demonstrate usage of complexMessage with sendMessage. {{sendMessage nil (complexMessage "content" "Who rules?" "embed" (cembed "description" "YAGPDB of course!" "color" 0x89aa00) "file" "Here we print something nice - you all are doing awesome!")}}
  • To demonstrate usage of complexMessageEdit with editMessage. {{$mID := sendMessageRetID nil (complexMessage "content" "You know what is..." "embed" (cembed "title" "FUN!?" "color" 0xaa8900))}} {{sleep 3}} {{editMessage nil $mID (complexMessageEdit "embed" (cembed "title" "YAGPDB!" "color" 0x89aa00) "content" "Yes, it's always working with...")}}{{sleep 3}}{{editMessage nil $mID (complexMessageEdit "embed" nil "content" "Embed deleted, goodbye YAG!")}}{{deleteMessage nil $mID 3}}


Mentions @everyone.
Mentions @here.
mentionRoleName "rolename"
Mentions the first role found with the provided name (case-insensitive).
mentionRoleID roleID
Mentions the role found with the provided ID.

This section's snippets:

  • <@{{.User.ID}}> Outputs a mention to the user that called the command and is the same as {{.User.Mention}}
  • <@###########> Mentions the user that has the ID ###### (See How to get IDs to get ID).
  • <#&&&&&&&&&&&> Mentions the channel that has ID &&&&&& (See How to get IDs to get ID).
  • <@&##########> Mentions the role with ID ######## (listroles command gives roleIDs). This is usable for example with {{sendMessageNoEscape nil "Welcome to role <@&11111111...>"}}. Mentioning that role has to be enabled server- side in Discord.

Role functions

addRoleID roleID
Adds the role with the given ID to the user that triggered the command (use the listroles command for a list of roles).
addRoleName roleName
Adds the role with given name to the user that triggered the command (use the listroles command for a list of roles).
getRole role
Returns a role object of type *discordgo.Role. role can be either role's ID or role's name.
giveRoleID userID roleID
Gives a role by ID to the target.
giveRoleName userID "roleName"
Gives a role by name to the target.
hasRoleID roleID
Returns true if the user has the role with the specified ID (use the listroles command for a list of roles).
hasRoleName "rolename"
Returns true if the user has the role with the specified name (case-insensitive).
removeRoleID roleID (delay)
Removes the role with the given ID from the user that triggered the command (use the listroles command for a list of roles). Delay is optional argument in seconds.
removeRoleName roleName (delay)
Removes the role with given name from the user that triggered the command (use the listroles command for a list of roles). Delay is optional argument in seconds.
setRoles userID roles
Overwrites the roles of the given user using the slice of roles provided, which should be a slice of role IDs. IDs can be ints or strings. Example: {{setRoles .User.ID cslice}} would clear the roles of the triggering user.
takeRoleID userID roleID (delay)
Takes away a role by ID from the target. Delay is optional argument in seconds.
takeRoleName userID "roleName" (delay)
Takes away a role by name from the target. Delay is optional argument in seconds.
targetHasRoleID userID roleID
Returns true if the given user has the role with the specified ID (use the listroles command for a list of roles). Example in section's Snippets.
targetHasRoleName userID "roleName"
Returns true if the given user has the role with the specified name (case-insensitive).

This section's snippets:

  • To demonstrate usage of targetHasRoleID. > {{$x := (userArg (index .Args 1)).ID}} {{if targetHasRoleID $x ############}} Has the Role! {{else}} Does not have the role! {{end}}

Current User

Returns value of type time.Time and shows when the current user was created.
The account age of the current user in more human readable format (Eg:3 days 2 hours).
The account age of the current user in minutes.


Returns a random adjective.
cembed "list of embed values"
Function to generate embed inside custom command. More in-depth here.
cslice, sdict
These functions are covered in their own section here.
dict key1 value1 key2 value2 ...
Creates an unordered collection of key-value pairs, a dictionary so to say. The number of parameters to form key-value pairs must be even. Example here. Keys and values can be of any type. Key is not restricted to string only as in case with sdict. dict also has helper methods .Del, .Get and .Set and they function the same way as sdict ones discussed here.
exec "command" "args" "args" "args" ...
Executes a YAGPDB command (e.g. roll, kick etc) in a custom command. Exec can be run max 5 times per command. If real command returns an embed - exec will return raw data of type embed, so you can use embed fields for better formatting - e.g. {{$resp := exec "whois"}} {{$resp.Title}} Joined at > {{(index $resp.Fields 4).Value}} will return the title (username#discriminator) and "Joined at" field's value from whois command. NB! This will not work for un/nn commands!
exec syntax is exec "command" arguments - this means you format it the same way as you would type the command regularly, just without the prefix, e.g. if you want to clear 2 messages and avoiding the pinned message > {{exec "clear 2 -nopin"}}, where "command" part is whole "clear 2 -nopin". If you change that number inside CC somewhere then you have to use arguments part of exec formatting > {{$x := 2}} {{exec "clear" $x "-nopin"}} Here "clear" is the "command" and it is followed by arguments, one variable $x and one string "-nopin". Last example is the same as {{exec (joinStr " " "clear" $x "-nopin")}}(also notice the space in joinStr separator).
execAdmin "command" "args" "args" "args" ...
Functions same way as exec but effectively runs the command as the bot user (YAGPDB). This has essentially the same effect as if a user with the same permissions and roles as YAGPDB ran the command: for example, if YAGPDB had ban members permission but the user which ran the command did not, {{exec "ban" 12345}} would error due to insufficient permissions but {{execAdmin "ban" 12345}} would succeed.
hasPrefix string prefix
hasPrefix tests whether the given string begins with prefix and returns bool. Example > {{hasPrefix "YAGPDB" "YAG"}} returns true.
hasSuffix string suffix
hasSuffix tests whether the given string ends with suffix and returns bool.
Example > {{hasSuffix "YAGPDB" "YAG"}} returns false.
humanizeThousands arg
This function places comma to separate groups of thousands of a number. arg can be int or string, has to be a whole number, e.g. {{humanizeThousands "1234567890"}} will return 1,234,567,890
in list value
Returns bool true/false whether case-sensitive value is in list/slice. {{ in (cslice "YAGPDB" "is cool") "yagpdb" }} returns false.
index arg ...keys
Returns the result of indexing its first argument by the following arguments. Each indexed item must be a map, slice or array.
Example: {{index .Args 1}} returns first argument after trigger which is always at position 0.
More than one positional keys can be used, in pseudo-code:
index X 0 1 is equivalent to calling index (index X 0) 1
inFold list value
Same as in, but is case-insensitive. {{inFold (cslice "YAGPDB" "is cool") "yagpdb"}} returns true.
kindOf value (flag)
This function helps to determine what kind of data type we are dealing with. flag part is a bool and if set as true (false is optional) returns the value where given value points to. Example: {{kindOf cembed false}} {{kindOf cembed true}} will return ptr and struct.
len arg
Returns the integer length of its argument. arg can be an array, slice, map, or string.
{{ len (cslice 1 2 3) }}
returns 3.
Returns a random noun.
parseArgs required_args error_message ...carg
Checks the arguments for a specific type. Has methods .Get and .IsSet.
carg "type" "name" is required by parseArgs and it defines the type of argument for parseArgs.
range slice/array
Iterates (loops) over the given slice or array and sets successive elements as active data (the dot) to be further handled inside the range action. Example usage here. More in-depth here.
sendTemplate channel templateName data
Function sends a formulated template to another channel. Channel is like always either name, number or nil; and returns messageID.
Example: {{define "logsTemplate"}}This text will output on different channel, you can also use functions like {{currentTime}}. {{.TemplateArgs}} would be additional data sent out. {{end}}
Now we call that "logs" in the same custom command.{{sendTemplate "logs" "logsTemplate" "YAG rules!"}}
sendTemplateDM templateName data
Works the same way as function above. Only channel's name is not in the arguments. YAGPDB will only DM the triggering user.
seq start stop
Creates a new slice of type int, beginning from start number, increasing in sequence and ending at stop (not included). {{seq -4 2}} returns a slice [ -4 -3 -2 -1 0 1 ]. Sequence's max length is 10 000.
sort slice (...args)
Sorts a slice with optional arguments. Numbers are sorted before strings. Arguments are presented in a sdict as keys having bool values. Example > {{sort (cslice "YAGPDB" 42 "Alphabet" 111 33.3) (sdict "reverse" true "subslices" true "emptyslices" false)}}would return [111 42 33.3 YAGPDB Alphabet]
Argument keys:
"reverse" reverses the order if true.
"subslices" makes the function return a set of subslices based on input type/kind if true.
"Emptyslices" returns all possible slices if true, helpful for indexing.
shuffle list
Returns a shuffled, randomized version of a list/slice.
sleep seconds
Pauses execution of template's action-structure inside custom command for max 60 seconds combined. Argumentsecondsis an integer (whole number). Example in Snippets.


execCC calls are limited to 1 / CC for non-premium users and 10 / CC for premium users.
execCC ccID channel delay data
Function that executes another custom command specified by ccID,max recursion depth is 2 (using .StackDepth shows the current depth) and it's rate-limited strictly at max 10 delayed custom commands executed per channel per minute, if you go over that it will be simply thrown away. Argument channel can be nil, channel's ID or name. Thedelay argument is execution delay of another CC is in seconds. The data argument is content that you pass to the other executed custom command. To retrieve that data you use .ExecData. This example is important > execCC example also next snippet which shows you same thing run using the same custom command > Snippets.
scheduleUniqueCC ccID channel delay key data
Same as execCCexcept there can only be 1 scheduled cc execution per server per key, if key already exists then it is overwritten with the new data and delay (as above, in seconds).
An example would be a mute command that schedules the unmute action sometime in the future. However, let's say you use the unmute command again on the same user, you would want to override the last scheduled unmute to the new one. This can be used for that.
cancelScheduledUniqueCC ccID key
Cancels a previously scheduled custom command execution using scheduleUniqueCC

This section's snippets:

  • To demonstrate execCC and .ExecData using the same CC.
{{ $yag := "YAGPDB rules! " }}
{{ $ctr := 0 }} {{ $yourCCID := .CCID }}
{{ if .ExecData }}
{{ $ctr = add .ExecData.number 1 }}
{{ $yag = joinStr "" $yag $ctr }} {{ .ExecData.YAGPDB }}
{{ else }}
So, someone rules.
{{ $ctr = add $ctr 1 }} {{ $yag = joinStr "" $yag 1 }}
{{ end }}
{{ if lt $ctr 5 }}
{{ execCC $yourCCID nil 10 (sdict "YAGPDB" $yag "number" $ctr) }}
{{ else }} FUN'S OVER! {{ end }}


Ticket functions are limited to 1 call per custom command for both normal and premium guilds.
createTicket author topic
Creates a new ticket with the author and topic provided. Author can be nil (to use the triggering member); user ID in form of a string or an integer; a user struct; or a member struct. The topic must be a string. Returns a template ticket struct on success.

Template Ticket

Author ID of the ticket.
The Discord tag of the author of the ticket, formatted like username#discrim.
Channel ID of the ticket.
Time that the ticket was closed, of type null.Time. This is, for the most part, useless in custom commands.
Time that the ticket was created.
Guild ID of the ticket.
The ticket ID.
Log ID of the ticket.
Title of the ticket.


You have access to a basic set of Database functions, this is almost a key value store ordered by the key and value combined.
You can have max 50 * user_count (or 500 * user_count for premium) values in the database, if you go above this all new write functions will fail, this value is also cached so it won't be detected immediately when you go above nor immediately when you're under again.
Patterns are basic PostgreSQL patterns, not Regexp: An underscore (_) matches any single character; a percent sign (%) matches any sequence of zero or more characters.
Keys can be max 256 bytes long and has to be strings or numbers. Values can be anything, but if their serialized representation exceeds 100kB an error will be raised.
You can just pass a userIDof 0 to make it global (or any other number, but 0 is safe). There can be 10 database interactions per CC, out of which dbTop/BottomEntries, dbCount, dbGetPattern, and dbDelMultiple may only be run twice. (50,10 for premium users).
Learning resources cover database more in-depth. Example here.
dbBottomEntries pattern amount nSkip
Returns amount (max 100)top entries from the database, sorted by the value in a ascending order.
dbCount (userID|key|query)
Returns the count of all database entries which are not expired. Optional arguments: if userID is given, counts entries for that userID; if key, only those keys are counted; and if query is provided, it should be a sdict with the following options:
  • userID - only counts entries with that user ID, defaults to counting entries with any user ID
  • pattern - only counts entries with names matching the pattern given, defaults to counting entries with any name.
dbDel userID key
Deletes the specified key for the specified value from the database.
dbDelByID userID ID
Deletes database entry by its ID.
dbDelMultiple query amount skip
Deletes amount (max 100) entries from the database matching the criteria provided. query should be a sdict with the following options:
  • userID - only deletes entries with the user ID provided, defaults to deleting entries with any ID.
  • pattern - only deletes entries with a name matching the pattern given.
  • reverse - if true, starts deleting entries with the lowest values first; otherwise starts deleting entries with the highest values first. Default is false.
Returns the number of rows that got deleted or an error.
dbGet userID key
Retrieves a value from the database for the specified user, this returns DBEntry object.
dbGetPattern userID pattern amount nSkip
Retrieves up toamount (max 100)entries from the database in ascending order.
dbGetPatternReverse userID pattern amount nSkip
Retrievesamount (max 100)entries from the database in descending order.
dbIncr userID key incrBy
Increments the value for specified key for the specified user, if there was no value then it will be set to incrBy . Also returns the entry's current, increased value.
dbRank query userID key
Returns the rank of the entry specified by the user ID and key provided in the set of entries matching the criteria provided. query specifies the set of entries that should be considered, and should be a sdict with the following options:
  • userID - only includes entries with that user ID, defaults to including entries with any user ID
  • pattern - only includes database's key entries with names matching the pattern given, defaults to counting entries with any name
  • reverse - if true, entries with lower value have higher rank; otherwise entries with higher value have higher rank. Default is false.
dbSet userID key value
Sets the value for the specified key for the specific userID to the specified value. userID can be any number of type int64. Values are stored either as of type float64 (for numbers, oct or hex) or as varying type in bytes (for slices, maps, strings etc) depending on input argument.
dbSetExpire userID key value ttl
Same as dbSet but with an expiration in seconds.
dbTopEntries pattern amount nSkip
Returns amount (max 100)top entries from the database, sorted by the value in a descending order.
Note about saving numbers into database: As stated above, database stores numbers as type float64. If you save a large number into database like an int64 (which IDs are), the value will be truncated. To avoid this behavior, you can stringify the number before saving and convert it back to its original type when retrieving it. Example: {{$v := .User.ID}} {{dbSet 0 "userid" (str $v)}} {{$fromDB := toInt (dbGet 0 "user_id").Value}}
dict key values are also retrieved as int64, so to use them for indexing one has to e.g. index $x (toInt64 0)


ID of the entry.
ID of the server.
Value of userID argument or ID of the user if for example .User.ID was used for dbSet.
User object of type discordgo.User having only .ID field, .Mention is still usable with correct userID field entry.
When this entry was created.
When this entry was last updated.
When entry will expire.
The key of the entry.
The value of the entry.

Conditional branching

Branching using if action's pipeline and comparison operators - these operators don't need to be inside if branch. if statements always need to have an enclosing end.
eq , though often used with 2 arguments (eq x y) can actually be used with more than 2. If there are more than 2 arguments, it checks whether the first argument is equal to any one of the following arguments. This behaviour is unique to eq.
Comparison operators always require the same type: i.e comparing 1.23 and 1 would throw incompatible types for comparison error as they are not the same type (one is float, the other int). To fix this, you should convert both to the same type -> for example, toFloat 1.
{{if (condition)}} output {{end}}
Initialization statement can also be inside if statement with conditional statement, limiting the initialized scope to that if statement. {{$x := 24}} {{if eq ($x := 42) 42}} Inside: {{$x}} {{end}} Outside: {{$x}}
else if
{{if (condition)}} output1 {{else if (condition)}} output2 {{end}}
You can have as manyelse ifstatements as many different conditionals you have.
{{if (condition)}} output1 {{else}} output2 {{end}}
{{if not (condition)}} output {{end}}
{{if and (cond1) (cond2) (cond3)}} output {{end}}
{{if or (cond1) (cond2) (cond3)}} output {{end}}
Equal: eq
{{if eq .Channel.ID ########}} output {{end}}
Not equal: ne
{{$x := 7}} {{$y := 8}} {{ne $x $y}} returns true
Less than: lt
{{if lt (len .Args) 5}} output {{end}}
Less than or equal: le
{{$x := 7}} {{$y := 8}} {{le $x $y}} returns true
Greater than: gt
{{if gt (len .Args) 1}} output {{end}}
Greater than or equal: ge
{{$x := 7}} {{$y := 8}} {{ge $x $y}} returns false

Range action

rangeiterates over element values in variety of data structures in pipeline - slices/arrays, maps or channels. The dot . is set to successive elements of those data structures and output will follow execution. If the value of pipeline has zero length, nothing is output or if an {{else}} action is used, that section will be executed.
Affected dot inside range is important because methods mentioned above in this documentation:.Server.ID, .Message.Content etc are all already using the dot on the pipeline and if they are not carried over to the range control structure directly, these fields do not exists and template will error out. Getting those values inside range and also with action would need $.User.ID for example. range on slices/arrays provides both the index and element for each entry; range on map iterates over key/element pairs. If a range action initializes a variable, that variable is set to the successive elements of the iteration. Range can also declare two variables, separated by a comma and set by index and element or key and element pair. In case of only one variable, it is assigned the element. Like if, rangeis concluded with{{end}}action and declared variable scope inside range extends to that point.
{{/* range over a slice */}}
{{ range $index, $element := cslice "YAGPDB" "IS COOL!" }}
{{ $index }} : {{ $element }} {{ end }}
{{/* range on a map */}}
{{ range $key, $value := dict "SO" "SAY" "WE" "ALL!" }}
{{ $key }} : {{ $value }} {{ end }}
{{/* range with else and variable scope */}}
{{ range seq 1 1 }} no output {{ else }} output here {{ end }}
{{ $x := 42 }} {{ range $x := seq 2 4 }} {{ $x }} {{ end }} {{ $x }}
Custom command response was longer than 2k (contact an admin on the server...) This is quite common error users will get whilst using range. Simple example to reproduce it: {{ range seq 0 1000 }} {{ $x := . }} {{ end }} HELLO! This will happen because of whitespaces and newlines, so make sure you one-line the range or trim spaces, in this context {{- $x := . -}}

With action

with lets you assign and carry pipeline value with its type as a dot . inside that control structure, it's like a shorthand. If the value of the pipeline is empty, dot is unaffected and when {{else}} is used, that branch is executed instead. Affected dot inside with is important because methods mentioned above in this documentation:.Server.ID, .Message.Content etc are all already using the dot on the pipeline and if they are not carried over to the with control structure directly, these fields do not exists and template will error out. Getting those values inside with and also range action would need $.User.ID for example.
Like if and range actions, with is concluded using {{end}} and variable scope extends to that point.
{{/* Shows the scope and how dot is affected by object's value in pipeline */}}
{{ $x := "42" }} {{ with and ($z:= seq 0 5) ($x := seq 0 10) }}
len $x: `{{ len $x }}`
{{/* "and" function uses $x as last value for dot */}}
same as len dot: `{{ len . }}`
but len $z is `{{ len $z }}` {{ end }}
Outer-scope $x len however: {{ len $x }}
{{/* when there's no value, dot is unaffected */}}
{{ with false }} dot is unaffected {{ else }} printing here {{ .CCID }} {{ end }}