diff --git a/content/moderation/moderation-overview.md b/content/moderation/moderation-overview.md index ae24e2c..6bf8b6d 100644 --- a/content/moderation/moderation-overview.md +++ b/content/moderation/moderation-overview.md @@ -289,19 +289,19 @@ Example: {{/* Number of warnings at which action is to be taken (eg: for action to take place at 4 warnings set threshold to 4) */}} {{ $threshold := 4 }} {{ define "punish_check" }} - {{ $threshold := toInt .TemplateArgs }} - {{ $warnCount := len (getWarnings .User) }} - {{if ge $warns_count $count}} - {{/* Add required code for action to be taken when warning count exceeds the threshold. */}} - Add code here - {{/* Reset warnings for the user. Delete if you do not wish to reset the warnings. */}} - {{ $silent := execAdmin "clearwarnings" .User }} - {{ end }} + {{ $threshold := toInt .TemplateArgs }} + {{ $warnCount := len (getWarnings .User) }} + {{if ge $warns_count $count}} + {{/* Add required code for action to be taken when warning count exceeds the threshold. */}} + Add code here + {{/* Reset warnings for the user. Delete if you do not wish to reset the warnings. */}} + {{ $silent := execAdmin "clearwarnings" .User }} + {{ end }} {{ end }} {{ if .Channel }} {{ $s := sendTemplate .Channel.ID "punish_check" $count }} {{ else }} - {{/* no context channel; use an arbitrary one */}} + {{/* no context channel; use an arbitrary one */}} {{ $s := sendTemplate (index .Guild.Channels 0).ID "punish_check" $count }} {{ end }} ``` diff --git a/content/reference/100.gif b/content/reference/100.gif new file mode 100644 index 0000000..0e646bc Binary files /dev/null and b/content/reference/100.gif differ diff --git a/content/reference/_index.md b/content/reference/_index.md new file mode 100644 index 0000000..e36e7e3 --- /dev/null +++ b/content/reference/_index.md @@ -0,0 +1,10 @@ ++++ +archetype = "chapter" +title = "Reference" +weight = 8 ++++ + +Reference documentation provides further information about advanced customization with YAGPDB, particularly focused +around coding with the custom scripting language. + +{{%children containerstyle="div" style="h2" description="true" %}} diff --git a/content/reference/ablobaww1.gif b/content/reference/ablobaww1.gif new file mode 100644 index 0000000..c3355bb Binary files /dev/null and b/content/reference/ablobaww1.gif differ diff --git a/content/reference/animated_emotes.md b/content/reference/animated_emotes.md new file mode 100644 index 0000000..8d721b0 --- /dev/null +++ b/content/reference/animated_emotes.md @@ -0,0 +1,55 @@ ++++ +title = 'Animated Emotes' +weight = 6 ++++ + +A guide on use of animated emojis in various contexts across YAGPDB, and a library of a few available animated emojis on the public instance. + + + +To use the emotes as reaction in the trigger message: `{{ addReactions "name:id" "..." "..." }}` \ +To use the emotes as reaction in the response message, replace `addReactions` with `addResponseReactions`\ +To use the emotes as text-form entry inside a custom command: \ + +|**Name**|

addReactions

Text

| **Image**| +|-| -| -| +|whoop|

whoop:448566329922813953

<a:whoop:448566329922813953>

| ![whoop](https://cdn.discordapp.com/emojis/448566329922813953.gif?v=1)| +|​whoop2|

whoop2:448846256312156173

<a:whoop2:448846256312156173>

| ![​whoop2](https://cdn.discordapp.com/emojis/448846256312156173.gif?v=1)| +|whoop3|

whoop3:448846255976742944

<a:whoop3:448846255976742944>

| ![whoop3](https://cdn.discordapp.com/emojis/448846255976742944.gif?v=1)| +|whoop4|

whoop4:448846256500899841

<a:whoop4:448846256500899841>

| ![whoop4](https://cdn.discordapp.com/emojis/448846256500899841.gif?v=1)| +|whoop5|

whoop5:448846256450830336

<a:whoop5:448846256450830336>

| ![whoop5](https://cdn.discordapp.com/emojis/448846256450830336.gif?v=1)| +|whoop6|

whoop6:448846256295378945

<a:whoop6:448846256295378945>

| ![whoop6](https://cdn.discordapp.com/emojis/448846256295378945.gif?v=1)| +|whoop7|

whoop7:448846256437985282

<a:whoop7:448846256437985282>

| ![whoop7](https://cdn.discordapp.com/emojis/448846256437985282.gif?v=1)| +|whoop8|

whoop8:448846256589111296

<a:whoop8:448846256589111296>

| ![whoop8](https://cdn.discordapp.com/emojis/448846256589111296.gif?v=1)| +|ablobbouncing|

ablobbouncing:448846256018554891

<a:ablobbouncing:448846256018554891>

| ![ablobbouncing](https://cdn.discordapp.com/emojis/448846256018554891.gif?v=1)| +|cool\_whop|

cool_whoop:448846256366813214

<a:cool_whoop:448846256366813214>

| ![cool_whop](https://cdn.discordapp.com/emojis/448846256366813214.gif?v=1)| +|popcorn\_parrot|

popcorn_parrot:448846256026943493

<a:popcorn_parrot:448846256026943493>

| ![popcorn_parrot](https://cdn.discordapp.com/emojis/448846256026943493.gif?v=1)| +|owo|

owo:449200425854304257

<a:owo:449200425854304257>

| ![owo](https://cdn.discordapp.com/emojis/449200425854304257.gif?v=1)| +|partyJonas|

partyJonas:458241678449442817

<a:partyJonas:458241678449442817>

| ![partyJonas](https://cdn.discordapp.com/emojis/458241678449442817.gif?v=1)| +|BOI|

BOI:462941202518638603

<a:BOI:462941202518638603>

| ![BOI](https://cdn.discordapp.com/emojis/462941202518638603.gif?v=1)| +|ablobaww1|

ablobaww1:434490222655438849

<a:ablobaww1:434490222655438849>

| ![ablobaww1](ablobaww1.gif)| +|no\_u|

no_u:480787880021852191

<a:no_u:480787880021852191>

| ![no_u](no_u.gif)| +|youtried|

youtried:480811110275219456

<a:youtried:480811110275219456>

| ![youtried](youtried.gif)| +|THINKING|

THINKING:480811102729666614

<a:THINKING:480811102729666614>

| ![THINKING](THINKING.gif)| +|shakyshaky|

shakyshaky:480811093212528640

<a:shakyshaky:480811093212528640>

| ![shakyshaky](shakyshaky.gif)| +|REEEEEEEEEEEEEEEEEEEE|

REEEEEEEEEEEEEEEEEEEE:480811084349964300

<a:REEEEEEEEEEEEEEEEEEEE:480811084349964300>

| ![REEEEEEEEEEEEEEEEEEEE](REEEEEEEEEEEEEEEEEEEE.gif)| +|rageping|

rageping:480811071930761242

<a:rageping:480811071930761242>

| ![rageping](rageping.gif)| +|owowhatsthis|

owowhatsthis:480811064108384256

<a:owowhatsthis:480811064108384256>

| ![owowhatsthis](owowhatsthis.gif)| +|oOoO|

oOoO:480811052775374858

<a:oOoO:480811052775374858>

| ![oOoO](oOoO.gif)| +|oofoof|

oofoof:480811044424515625

<a:oofoof:480811044424515625>

| ![oofoof](oofoof.gif)| +|okSpin|

okSpin:480811036547612692

<a:okSpin:480811036547612692>

| ![okSpin](okSpin.gif)| +|movingeyes|

movingeyes:480811027097976832

<a:movingeyes:480811027097976832>

| ![movingeyes](movingeyes.gif)| +|loading1|

loading1:480811012556193804

<a:loading1:480811012556193804>

| ![loading1](loading1.gif)| +|loading2|

loading2:480811019611144216

<a:loading2:480811019611144216>

| ![loading2](loading2.gif)| +|KMS|

KMS:480811001306939395

<a:KMS:480811001306939395>

| ![KMS](KMS.gif)| +|dealwithit|

dealwithit:480810992595369985

<a:dealwithit:480810992595369985>

| ![dealwithit](dealwithit.gif)| +|dancingwumpus|

dancingwumpus:480810984320008198

<a:dancingwumpus:480810984320008198>

| ![dancingwumpus](dancingwumpus.gif)| +|clapclap|

clapclap:480810974232707073

<a:clapclap:480810974232707073>

| ![clapclap](clapclap.gif)| +|blobwave|

blobwave:480810966939074560

<a:blobwave:480810966939074560>

| ![blobwave](blobwave.gif)| +|blobbounce|

blobbounce:480810958621769756

<a:blobbounce:480810958621769756>

| ![blobbounce](blobbounce.gif)| +|banhammer|

banhammer:480810952049295360

<a:banhammer:480810952049295360>

| ![banhammer](banhammer.gif)| +|100|

100:480810944616988673

<a:100:480810944616988673>

| ![100](100.gif)| +|load\_windows10|

load_windows10:705481616092430366

<a:load_windows10:705481616092430366>

| ![load_windows10](load_windows10.gif)| +|twitch\_turnover1|

twitch_turnover1:825786228623933491

<a:twitch_turnover1:825786228623933491>

| ![twitch_turnover1](twitch_turnover1.gif)| + +If you want to contribute to this database, just ping **Michdi#1602** on the support server. YAGPDB needs to be on the server where you store the emojis. diff --git a/content/reference/banhammer.gif b/content/reference/banhammer.gif new file mode 100644 index 0000000..d98db20 Binary files /dev/null and b/content/reference/banhammer.gif differ diff --git a/content/reference/blobbounce.gif b/content/reference/blobbounce.gif new file mode 100644 index 0000000..1fee356 Binary files /dev/null and b/content/reference/blobbounce.gif differ diff --git a/content/reference/blobwave.gif b/content/reference/blobwave.gif new file mode 100644 index 0000000..d28597f Binary files /dev/null and b/content/reference/blobwave.gif differ diff --git a/content/reference/clapclap.gif b/content/reference/clapclap.gif new file mode 100644 index 0000000..c01f75b Binary files /dev/null and b/content/reference/clapclap.gif differ diff --git a/content/reference/custom-command-examples.md b/content/reference/custom-command-examples.md new file mode 100644 index 0000000..55984db --- /dev/null +++ b/content/reference/custom-command-examples.md @@ -0,0 +1,510 @@ ++++ +title = 'Custom Commands Examples' +weight = 2 ++++ + +Prebuilt custom commands for use as a learning reference. + + + +{{% notice warning %}} + +Note: This page is no longer updated with the latest versions of codes/commands. If you plan to copy paste codes for +your server, see [this website](https://yagpdb-cc.github.io) which is frequently updated with new community-contributed +commands. + +{{% /notice %}} + +{{% notice style="note" %}} + +This isn't the actual page about custom commands. A brief overview about custom commands can be found +[here](/custom-commands). Please take notice, some of examples presented here are not up to date +with current capabilities of YAGPDB. Please visit our [support server](https://discord.gg/0vYlUK2XBKldPSMY) for newer +solutions. + +{{% /notice %}} + +### Controlled randomizer example + +YAGPDB has a built-in random response system for custom commands, but sometimes you may want to control the chances for +certain responses to occur. You can do this by creating a singular response and creating a variable with randInt. Then +use an if else if statement like this to print out your desired output. + +```go +{{$var := randInt 100}} + +{{if lt $var 10}} +This has a 10% chance of being triggered +{{else if lt $var 35}} +This has a 25% chance of being triggered +{{else}} +This has a 65% chance of being triggered +{{end}} +``` + +### Silent execution of commands or storage in a variable + +This command is to be placed in the welcome message. It filters out people with invites in their name. Make sure that +the checkbox **Censor server invites in usernames?** and the ban command are enabled on your server. + +You might not want the response for the executed command to show. You can suppress the response of a command like the +following: + +Trigger type: `Join message in server channel` + +```go +{{if .UsernameHasInvite}} +{{$silent := execAdmin "ban" .User.ID "ad blocked"}} +{{else}} +{{/* Replace this with your normal join message or leave it as it is */}} +{{end}} +``` + +### Range example + +This command will teach you on how the range function works. It can iterate over many items including but not limited to +a `cslice`, slice, `sdict`, and `dict` + +This particular command loops over a cslice and a sdict. + +Trigger type: `Command` Trigger: `range` + +```go +{{/* range can iterate over many things, let's start with slice */}} +{{ $slice := cslice "YAGPDB " "is " "cool!" }} +{{/* Here, we range over with 1 argument, meaning the dot will be set to current iteration value */}} +{{ range $slice -}} + {{ . }} +{{- end -}} +{{ $map := sdict "foo" "bar" "hello" "world" }} +{{- /* Now, we range with two arguments - $k will be the KEY, $v will be VALUE (note dot is still set to current iteration value */}} +{{- range $k, $v := $map }} +{{ $k }} - {{ $v }} +{{- end }} +``` + +`$k` is the index for arrays / cslices (starting at 0) or the key for maps and sdicts, while `$v` is the current word in +your input that you are on. + +Range will work on any kind of slice/array. for example. If we wanted to look for all the entries in our database we can +use range and index through them all in the following. + +```go +{{$lb := dbTopEntries "%" 100 0}} +{{range $lb}} +{{.UserID}} **:** {{.Key}} **:** {{.Value}} +{{end}} +``` + +Note that we can go through everything that is in $lb with range. + +### Dictionary example + +A dictionary does not currently have a lot of practical use, because YAGPDB has a data type more suited for most use +cases - `sdict`. However, sdict only supports string keys, which means that in the case you want non-string keys, you +will have to use `dict`. + +Trigger type: `Command` Trigger: `dict` + +```go +{{ $dict := dict 0 "foobar" "hello" "world" }} +{{/* Retrieve value with integer key with index */}} +0 - {{ index $dict 0 -}} +{{/* Retrieve value with string key using dot notation */}} +hello - {{ $dict.hello }} +``` + +### parseArgs example + +The `parseArgs` template can check if specific arguments are given. If not, it will return a custom error message. It +also checks if specific args are of a specific type and simplifies the argument management. Available types for `carg` +are: + +* `int` (whole number) +* `string` (text) +* `user` (user mentions as type user) +* `userid` (mentions or the user's ID, as integer) +* `channel` (channel mention or ID, as type channel) +* `role` (role name or ID, as type _\*discordgo.Role_) +* `duration` (duration as integer or string with optional time modifier - s,m,h, etc...) +* `member` (mentions or the user's ID, as type member) + +Trigger type: `Command` Trigger: `send` + +```go +{{$args := parseArgs 2 "Syntax is " + (carg "channel" "channel to send to") + (carg "string" "text to send")}} + +{{sendMessage ($args.Get 0).ID ($args.Get 1)}} +``` + +### Countdown example (Exec CC) + +This example consists of two custom commands, and after copy/paste `REPLACE-WITH-...` arguments need to be replaced by +actual custom command ID's in your system. This custom command is very complex, uses very many advanced functions, all +it does, constructs a 10 second countdown timer command-system for given starting time. + +```go +{{$args := parseArgs 2 "" + (carg "duration" "countdown-duration") + (carg "string" "countdown-message")}} + +{{$t := currentTime.Add ($args.Get 0)}} +{{$mID := sendMessageRetID nil (print "countdown starting... " $t.String)}} +{{execCC REPLACE-WITH-NEXT-CC-ID nil 0 (sdict "MessageID" $mID "T" $t "Message" ($args.Get 1)) }} +``` + +Second part of the custom commands, here we see, how `data`-part of exeCC was made in previous custom command as +`sdict`and now we are calling those keys with `.ExecData` - for example `.ExecData.MessageID` sets new variable the same +as stated in previous code. + +```go +{{$timeLeft := .ExecData.T.Sub currentTime}} +{{$cntDownMessageHeader := print "Countdown Timer: " .ExecData.Message}} +{{$formattedTimeLeft := humanizeDurationSeconds $timeLeft}} + +{{$t := .ExecData.T}} +{{$mID := .ExecData.MessageID}} +{{$ts := .TimeSecond}} + +{{if lt $timeLeft (mult .TimeSecond 30)}} + {{range seq 1 (toInt $timeLeft.Seconds) }} + {{$timeLeft := $t.Sub currentTime}} + {{$formattedTimeLeft := humanizeDurationSeconds $timeLeft}} + + {{editMessage nil $mID (print $cntDownMessageHeader "\nTime left: " $formattedTimeLeft " seconds")}} + {{if gt $timeLeft $ts}} {{sleep 1}} {{end}} + {{end}} + {{editMessage nil .ExecData.MessageID (print $cntDownMessageHeader "\nTime left: **ENDED**")}} +{{else}} + {{editMessage nil .ExecData.MessageID (print $cntDownMessageHeader "\nTime left: " $formattedTimeLeft)}} + {{execCC .CCID nil 10 .ExecData}} +{{end}} +``` + +![Jonas using YAGPDB testing bot here, same execCC custom commands.](<../.gitbook/assets/unknown (1).png>) + +### Database example + +This is a simple note taking system containing 3 separate custom commands. Also note that the actual name of the key +inserted to database begins with "notes\_". + +#### Save note + +```go +{{$args := parseArgs 2 "" + (carg "string" "key") + (carg "string" "value")}} + +{{dbSet .User.ID (print "notes_" ($args.Get 0)) ($args.Get 1)}} +Saved `{{$args.Get 0}}` as `{{$args.Get 1}}` +``` + +#### Get note + +```go +{{$key := print "notes_" .StrippedMsg}} +{{$note := dbGet .User.ID $key}} +{{if $note}} + +{{$strippedKey := slice $key 6 (len $key)}} +Note: `{{$strippedKey}}` Created {{humanizeTimeSinceDays $note.CreatedAt}} ago: +{{$note.Value}} + +{{else}}Couldn't find any note like that :({{end}} +``` + +#### List user's notes + +```go +{{$notes := dbGetPattern .User.ID "notes_%" 100 0}} +{{range $notes}} +{{- $strippedKey := slice .Key 6 (len .Key)}} +`{{$strippedKey}}` created {{humanizeTimeSinceDays .CreatedAt}} ago +{{- else}} +You don't have any notes :( +{{end}} +``` + +### Cooldown Example + +With YAGPDB's database system, you can now add cooldowns to you custom commands. You can either make them global +cooldowns or a per user cooldown. + +```go +{{/* CONFIGURATION HERE CHANGE VALUES AS NEEDED */}} + +{{/* 0 for per user, 1 for global */}} +{{$isGlobal := 1}} +{{/* name your cooldown name (anything works) */}} +{{$name := "replace with name here"}} +{{/* Length of the cooldown (in seconds) */}} +{{$lengthSec := 10}} + +{{/* CREATING VARIABLES DO NOT TOUCH */}} +{{$id := 0}} +{{$key := print "cooldown_" $name}} +{{if eq $isGlobal 0}} +{{$id = .User.ID}} +{{end}} + + +{{if dbGet (toInt64 $id) $key}} +{{/* Code to execute when cooldown is active */}} +{{else}} +{{/* Create cooldown entry */}} +{{dbSetExpire (toInt64 $id) $key "cooldown" $lengthSec}} + +{{/* YOUR COMMAND HERE */}} +{{end}} +``` + +## User submitted custom commands + +### Counter Command + +> By **Timcampy#5636** + +With YAGPDB's database system, I made a command to have users count from 0 and keep counting to the next number. +Relatively simple command that involves database and type conversion. + +Trigger type: `Regex` Trigger: `\A` + +`BE SURE TO RESTRICT THE COMMAND TO A SINGLE CHANNEL` + +```go +{{/* If you are not doing (no twice msg in a row) or (role assignment for latest user) you can remove counter_user and by extension everything to do with $lastUser*/}} + +{{/* First time running command, set up initial values*/}} +{{$lastUser := dbGet 118 "counter_user"}} +{{if $lastUser}} +{{else}} +{{dbSet 118 "counter_user" 0}} +{{dbSet 118 "counter_count" "0"}} +{{end}} + +{{/* OPTIONAL: this is just to prevent one person to type all the numbers themselves */}} +{{/* If current user ID matches the user who last successfully ran command */}} +{{if eq (toFloat $lastUser.Value) (toFloat .User.ID)}} +{{deleteTrigger 0}} +{{sendDM "You can not send a msg twice in a row"}} +{{else}} + + +{{$next := dbGet 118 "counter_count"}} + +{{/* If message is equal to the expected next number , update counter */}} +{{if eq (toInt .StrippedMsg) (toInt ($next.Value))}} +{{dbSet 118 "counter_count" (add (toInt ($next.Value)) 1)}} +{{$name := (add (toInt ($next.Value)) 1)}} +{{editChannelName .Channel.ID (joinStr "" "count-to-" $name )}} +{{/* OPTIONAL count tracker per user, Delete if you don't want to use */}} +{{$key := joinStr "" "counter_tracker_" .User.ID}} +{{$userCount := dbGet 118 $key}} +{{if $userCount}} +{{dbSet 118 $key (add (toInt ($userCount.Value)) 1)}} +{{else}} +{{dbSet 118 $key 1}} +{{end}} + +{{/* OPTIONAL: If you don't want to give a role to the latest person delete everything but dbset */}} +{{/* Give new user role, take role back from old user and update latest user */}} +{{/* (UPDATE THE ROLEID) */}} +{{giveRoleID .User.ID 606891664396648474}} +{{$tmpUser := (userArg (toInt $lastUser.Value))}} +{{/* check if its a valid user or not */}} +{{if $tmpUser}} +{{takeRoleID ($tmpUser.ID) 606891664396648474}} +{{end}} +{{dbSet 118 "counter_user" (toString .User.ID)}} +{{else}} + +{{/* Message did not match expected next value */}} +{{deleteTrigger 0}} +{{/* Removed Because too annoying :^) */}} +{{/*sendDM "That is not the next number, learn how to count :)"*/}} +{{end}} +{{end}} +``` + +### GiveRole command for specific roles + +> By **GryTrean#8957** + +This command will allow you to give a role to someone, making sure that the role given is in a list of allowed roles. We +use the `{{giveRoleName }}` template which allows us to give a user a role by name. We also make sure that +the command has the correct number of arguments and if not, we give a response with the correct usage of the command. To +add a new exception to the roles that can be given, you simply add another role in line 2. You could also make the +command take away roles from someone instead of giving them by simply using the `{{takeRoleName}}` template instead of +`{{giveRoleName}}`. + +Trigger type: `Command` Trigger: `giveRoleName` + +```go +{{if eq (len .Args) 3}} + {{$allowedRoles := (cslice "Patron" "Quality Patron" "Paypal Donors")}} + {{$role := (index .CmdArgs 1)}} + + {{if in $allowedRoles $role}} + {{giveRoleName (userArg (index .CmdArgs 0)) $role}} + Gave {{$role}} to {{index .CmdArgs 0}}! :white_check_mark: + {{else}} + You can't use this command to give that role to someone! :x: + {{end}} +{{else}} + Correct usage of the command: -giverole "" +{{end}} +``` + +### Broadcast command + +> By **GryTrean#8957** \ +> Updated by: **Timcampy#5636** + +This command lets the bot send a message to another channel. It uses embeds so you can see `sdict`(dictionary but with +only string keys), `sendMessage`, and `cembed`in action. + +Trigger type: `Command` Trigger: `bc` + +```go +{{if eq (len .Args) 3}} + {{$channel := (index .CmdArgs 0)}} + {{$msg:= (joinStr " " (slice .CmdArgs 1))}} + + {{$footer1 := (sdict "text" (joinStr "" "This broadcast was sent by " (.User.Username) ""))}} + {{$msgEmbed := cembed "title" "Broadcast!" "description" ($msg) "color" 16763904 "footer" ($footer1)}} + + {{sendMessage $channel $msgEmbed}} + + {{$desc := (joinStr "" "User " (.User.Username) " broadcasted a message in #" ($channel) "")}} + {{$footer := (sdict "text" (joinStr "" "The broadcast was sent on " (exec "ctime") ""))}} + + {{$embed := cembed "title" "Broadcast sent!" "description" ($desc) "color" 4325120 "footer" ($footer) "fields" (cslice (sdict "name" "Broadcasted message:" "value" ($msg)))}} + + {{sendMessage nil $embed}} + +{{else}} + Correct usage of the command: -bc "" "" +{{end}} +``` + +### Avatar command + +> By: **L-z#7749** + +This command does a good job at using a little bit of everything. Which include but is not limited to, `conditional +statement`, `assigning values to variable`, `getting command arguments`, `using template code`, and `creating embeds`. +If you are able to understand everything in this command, you are at a very good place in being able to make advanced +custom commands. + +Trigger type: `Command` Trigger: `avatar` + +```go +{{$ln := (len .Args)}} +{{$sizes := (cslice "16" "32" "64" "128" "256" "512" "1024" "2048" "4096")}} +{{$err1 := "Wrong image size input format! Possible values: 16, 32, 64, 128, 256, 512, 1024, 2048, 4096."}} +{{$err2 := "Unknown user :("}} +{{$color := 1478046}} +{{if gt $ln 1}} + {{$1 := (index .Args 1)}} + {{if ($user := userArg $1)}} + {{if gt $ln 2}} + {{$2 := (index .Args 2)}} + {{if in $sizes $2}} + {{$out := $user.AvatarURL (toString $2)}} + {{$emb := cembed "color" $color "image" (sdict "url" $out)}} + {{sendMessage nil $emb}} + {{else}} + {{$err1}} + {{end}} + {{else}} + {{$out := $user.AvatarURL "512"}} + {{$emb := cembed "color" $color "image" (sdict "url" $out)}} + {{sendMessage nil $emb}} + {{end}} + {{else if gt $ln 2}} + {{$2 := (index .Args 2)}} + {{if ($user := userArg $2)}} + {{if in $sizes $1}} + {{$out := $user.AvatarURL (toString $1)}} + {{$emb := cembed "color" $color "image" (sdict "url" $out)}} + {{sendMessage nil $emb}} + {{else}} + {{$err1}} + {{end}} + {{else}} + {{$err2}} + {{end}} + {{else if in $sizes $1}} + {{$out := .User.AvatarURL (toString $1)}} + {{$emb := cembed "color" $color "image" (sdict "url" $out)}} + {{sendMessage nil $emb}} + {{else}} + {{$err1}} + {{end}} +{{else}} + {{$out := .User.AvatarURL "512"}} + {{$emb := cembed "color" $color "image" (sdict "url" $out)}} + {{sendMessage nil $emb}} +{{end}} +``` + +### Suggestion command + +> By: **Michdi#1602** + +This command is used to replace suggestion bots. You can adapt it to your needs. + +Trigger type: `Command` Trigger: `suggest` + +```go +{{ $channel := 476178740133494784 }} {{/* Replace this with your suggestion channel ID */}} + +{{if gt (len .Args) 1}} +Suggestion submitted. +{{ $embed := cembed +"description" (joinStr " " .CmdArgs) +"color" 9021952 +"author" (sdict "name" (joinStr "" .User.Username "#" .User.Discriminator) "url" "" "icon_url" (.User.AvatarURL "512")) +"timestamp" currentTime +}} +{{ $id := (sendMessageNoEscapeRetID $channel $embed) }} +{{ addMessageReactions $channel $id "upvote:524907425531428864" "downvote:524907425032175638" }} +{{else}} +Correct usage: `-suggest ` +{{end}} +{{deleteResponse 5}} +{{deleteTrigger 5}} +``` + +### Big emote command + +> By: **CHamburr#2591**\ +> ****Updated by: **Joe\_#2447** + +This command uses the `reFindAllSubmatches` template as well as the `printf` template, and will enlarge custom emotes, +whether still or animated. This will also work for emotes that are from the servers YAGPDB is not in, as it gets the +emote file directly from Discord's database. + +Trigger type: `Command` Trigger: `bigemote` + +```go +{{ $matches := reFindAllSubmatches `<(a)?:.*?:(\d+)>` .StrippedMsg }} +{{ if $matches }} + {{ $animated := index $matches 0 1 }} + {{ $id := index $matches 0 2 }} + {{ $ext := ".png" }} + {{ if $animated }} {{ $ext = ".gif" }} {{ end }} + {{ $url := printf "https://cdn.discordapp.com/emojis/%s%s" $id $ext }} + {{ sendMessage nil (cembed + "title" "❯ Big Emoji" + "url" $url + "color" 14232643 + "image" (sdict "url" $url) + "footer" (sdict "text" (joinStr "" "Emoji ID: " $id)) + ) }} +{{ else }} + **Usage:** `-bigemoji `. +{{ end }} +``` diff --git a/content/reference/custom-commands-limits.md b/content/reference/custom-commands-limits.md new file mode 100644 index 0000000..52f28ba --- /dev/null +++ b/content/reference/custom-commands-limits.md @@ -0,0 +1,88 @@ ++++ +title = 'Custom Commands Limits' +weight = 2 ++++ + +Various limits in YAGPDB custom commands (CC) for smooth functioning of the bot and misuse prevention. + + + +## OVERALL + +* **Max amount of CCs:** 100/250 (free/prem) +* **Max CCs that can be triggered by a single action:** 3/5 (free/prem) +* **Character limit:** 10k (5k for join/leave msg, warn dm, etc...) +* **Limit writer:** 25kB +* **Max operations:** 1M/2.5M (free/prem) +* **Response Character Limit:** 2k +* **Generic API based Action call limit:** 100 per CC +* **State Lock based Actions:** 500 per CC (mentionRoleName/ID ; hasRoleName ; targetHasRoleName/ID) + +## CALLING A CC + +### execCC + +* **Calls per CC:** 1/10 (free/prem) -> counter key "runcc" +* **StackDepth limit:** 2 (executing with 0 delay) +* **Delay limit:** int64 limit (292 years) + +### scheduleUniqueCC + +* **Calls per CC:** 1/10 (free/prem) -> counter key "runcc" +* **Delay limit:** int64 limit (292 years) +* There can only be 1 per server per key + +### cancelScheduledUniqueCC + +* **Calls per CC:** 10/10 (free/prem) -> counter key "cancelcc" + +## CONTEXT + +* **Max file size (complexMessage):** 100kB +* **joinStr max string length:** 1000kB +* **sendDM:** 1 call per CC -> counter key "send\_dm" +* **sendTemplate/sendTemplateDM:** 3 calls per CC -> counter key "exec\_child" +* **addReactions:** 20 calls per CC -> counter key "add\_reaction\_trigger". Each reaction added counts towards the limit. +* **addResponseReactions:** 20 calls per CC -> counter key "add\_reaction\_response". Each reaction added counts towards the limit. +* **addMessageReactions:** 20 calls per CC -> counter key "add\_reaction\_message". Each reaction added counts towards the limit. +* **deleteMessageReaction: 1**0 calls per CC -> counter key "del\_reaction\_message". Each removed added counts towards the limit. +* **editChannelName/Topic:** 10 calls per CC -> counter key "edit\_channel" +* **regex cache limit:** 10 (this means you cant have more than 10 different regexes on a CC) +* **onlineCount:** 1 call per cc -> counter key "online\_users" +* **onlineCountBots:** 1 call per cc -> counter key "online\_bots" +* **editNickname:** 2 calls per cc -> counter key "edit\_nick" +* **Append/AppendSlice limit:** 10k size limit of resulting slice +* **exec/execAdmin:** 5 calls per cc -> no key +* **deleteResponse/deleteMessage/deleteTrigger max delay:** 86400s +* **take/removeRoleID/Name max delay:** int64 limit (292 years) +* **sleep:** 60 seconds + +## DATABASE + +### Overall Limits + +* **Max amount of DBs:** Membercount \*50\*1/10(free/prem) +* **Key length limit:** 256 +* **Expire limit:** int64 limit (292 years) +* **Value size limit:** 100kB + +### Database Interactions + +* **Calls per CC:** 10/50 (free/prem) -> counter key "db\_interactions" +* Valid for all database commands -> + * dbDel/dbDelByID + * dbGet + * dbIncr + * dbSet/dbSetExpire + +### Database Multiple Entry Interactions + +Multiple entries all count to general "db\_interactions" limit as well. + +* **Calls per CC:** 2/10 (free/prem) -> counter key "db\_multiple" +* Valid for all database multiple entry related commands -> + * dbCount + * dbDelMultiple + * dbGetPattern + * dbRank + * dbTopEntries diff --git a/content/reference/custom-embeds.md b/content/reference/custom-embeds.md new file mode 100644 index 0000000..3087b8e --- /dev/null +++ b/content/reference/custom-embeds.md @@ -0,0 +1,245 @@ ++++ +title = 'Custom Embeds' +weight = 3 ++++ + +A guide to creating custom embeds in various contexts across YAGPDB. + + + +{{% notice warning %}} + +Embeds have limits, summarized [here](https://discord.com/developers/docs/resources/channel#embed-object-embed-limits). + +{{% /notice %}} + +{{% notice style="note" %}} + +Custom Embeds with the `-customembed` command don't work in custom commands. If you want to know how you can use embeds +in custom commands, scroll down to [Embeds in Custom Commands](#embeds-in-custom-commands). + +{{% /notice %}} + +## The `customembed` command + +One method of sending an embed with YAGPDB is using the command `customembed` (or for short, `ce`). + +### Create embeds by hand + +YAGPDB accepts embeds in JSON following the rules of [this +format](https://discordapp.com/developers/docs/resources/channel#embed-object). + +There, we'll take a look at the **Embed Objects**. You can add a value to each of these objects. A very simple embed +would look like this: + +```javascript +{ "title": "This is my title", "description": "This is my description." } +``` + +The output of this would look like the following: + +![An Example of the Custom Embed Command](custom_embed_example.png) + +Let's break this down: We start of with the customembed command `-ce`. After this, I start my object (the embed) with a +curly brace. Then we have the name of the object (title) and the value of it (This is my title). We separate data with +commas. After that we have the same thing again, but for the description. In the end we close the object (embed) with +another curly brace. + +You can add the multiple objects to this, but keep in mind that Discord limits your message to 2000 characters. + +#### The syntax of JSON + +The syntax of json is pretty easy. You start off with a curly brace (`{`) and end with a curly brace (`}`). Between +this, you can add names and their according values. Data (a name and a value) get separated by commas (`,`) . Around +strings (text) you wrap two quotation marks (`""`), but nothing around integers (whole numbers) or booleans (true or +false statements). You can play around with this a bit. + +|Special character| Escaped output| +|-| -| +|Quotation mark (")| \\"| +|Backslash (\\)| \\\\| +|Slash (/)| \\/| +|Backspace| \b| +|Form feed| \f| +|New line| \n| +|Carriage return| \r| +|Horizontal tab| \t| + +### Create embeds with a generator + +Creating embeds with a generator can be more difficult if you don't need any difficult features. If you want your embed +to be super shiny, you can use [this embed generator](https://leovoel.github.io/embed-visualizer/). YAGPDB does not use +the first part of its code, so you have to remove the following: + +````javascript +{ + "content": "this `supports` __a__ **subset** *of* ~~markdown~~ 😃 ```js\nfunction foo(bar) {\n console.log(bar);\n}\n\nfoo(1);```", + "embed": +```` + +and the last curly brace (`}`). After this you can just copy and paste it into Discord: + +![Result of the Embed Generator](embed_generator_result.png) + +## Embeds in Custom Commands + +{{% notice warning %}} + +Embeds in custom commands are a little more difficult. Also, there is no generator that you could use for this. **Please only proceed if you have a good amount of knowledge about custom commands and templates in general.** + +{{% /notice %}} + +To start off, we'll take a look at this example and break it down: + +```go +{{ $embed := cembed "title" "This is my title" "description" "This is my description." }} +{{ sendMessage nil $embed }} +``` + +First off, I start creating my embed by defining it in a variable called `embed` in the well-known templating curly +braces. This makes it a bit clearer as your embed can get difficult to read. After this I start my embed object with +`cembed` (custom embed) to indicate what the following strings mean. We don't follow the json syntax here and only +define everything one after the other ("`name`" "`value`" et cetera). Now we use the objects for discord embeds from the +[developer page](https://discordapp.com/developers/docs/resources/channel#embed-object) again. So far, so good. In the +end we send our embed with the sendMessage template. `nil` sends it in the same channel, but you could also replace it +with a channel name or ID (or send the embed with sendDM as a direct message). + +Next, we'll take a look at this more lavish example: + +{{% notice style="green" %}} + +To make your code readable, especially for large embeds, **indents** may be used, as YAGPDB's templating system allows this sort of formatting. + +{{% /notice %}} + +{{% notice title="Custom Command \"embed\"" %}} + +```go +{{ $advice := execAdmin "advice" }} +{{ $topic := execAdmin "topic" }} +{{ $catfact := execAdmin "catfact" }} +{{ $avatar := print "https://cdn.discordapp.com/avatars/" .User.ID "/" .User.Avatar ".png" }} +{{ $botAvatar := .BotUser.AvatarURL "512" }} + +{{ $embed := cembed + "title" (print "Hello there, " .User.Username "!") + "url" (.User.AvatarURL "512") + "description" "This is an embed in a custom command. To see the code behind it, do `-cc embed`." + "color" 4645612 + "fields" (cslice + (sdict "name" "Advice" "value" $advice "inline" false) + (sdict "name" "Topic" "value" $topic "inline" false) + (sdict "name" "Cat Fact" "value" $catfact "inline" false) + (sdict "name" "Member Count" "value" (toString .Guild.MemberCount) "inline" true) + (sdict "name" "Guild ID" "value" (toString .Guild.ID) "inline" true) + ) + "author" + (sdict + "name" "YAGPDB.xyz!" + "url" "https://yagpdb.xyz/manage" + "icon_url" (.BotUser.AvatarURL "512") ) + "thumbnail" (sdict "url" $avatar) + "image" (sdict "url" $botAvatar) + "footer" + (sdict + "text" "YAGPDB.xyz since" + "icon_url" $botAvatar) + "timestamp" .Member.JoinedAt +}} +{{/* this line is here to show raw cembed output */}} {{ $embed }} +{{ sendMessage nil $embed }} +``` + +{{% /notice %}} + +In this example, we can ignore lines 1 to 5. I'm just defining some variables there which I am later using in my embed. +One thing to notice is the two ways of getting user's avatar URL for variables `$avatar` and `$botAvatar`. Line 7 starts +with our already known definition of the embed. Then I start with the first object, the title. Notice how I use `print` +to join two strings (text snippets) together.\ +\ +Next, we have the description. We can use markdown of Discord in here. After that object, I define the color. The color +is given as integer and you can convert a hex color to it +[here](https://www.binaryhexconverter.com/hex-to-decimal-converter). + +Up next, I have added some fields. This is a bit more difficult, but doable if you have understood it once. Let's break +it down in this example: + +```go +"fields" (cslice + (sdict "name" "Title of field 1" "value" "Description of field 1" "inline" false) + (sdict "name" "Title of field 2" "value" "Description of field 2" "inline" false) +) +``` + +This would add two fields being displayed under each other. If you set inline to true, they would try to get displayed +next to each other. You can add multiple fields with sdict. + +Following the fields I have added the author. Again the author object is nested and can have up to four fields. For +this, we use sdict again. I define the name, the url and the icon url for it. The same applies to the avatar and footer. +In the end I use `.Guild.JoinedAt` as timestamp to display when the first person joined this server and close the +template. After this we just have to send the embed using `SendMessage` or `SendDM`. Here is our result: + +![Result of Embedding with Custom Command](embed_custom_command_result.png) + +### Display an image + +You can display an image by simply pasting the link to it in the response, or by doing it fancy this way:\ +(make sure to replace the link with your own image ;)) + +Trigger type: command trigger: `imageembed` + +```go +{{ $embed := cembed "image" (sdict "url" "https://i.imgur.com/ttIwOmn.png") }} +{{ sendMessage nil $embed }} +``` + +### Using colors + +Discord uses color decimal. [SpyColor](https://www.spycolor.com) is a good tool to pick colors, or convert hex to decimal. + +![An example of getting the color decimal value using SpyColor](spycolor.png) + +## The `simpleembed` command + +Simple embeds are easier to use than custom embeds as they do not require any knowledge of json. Their downside is that +they don't support all Discord embed fields from the embed structure, for example fields. You can create a simple embed +with the `simpleembed` command, `se` for short. + +Simple embeds work with switches, here is a list of them all: + +|Switch| Description| +|-| -| +|-channel| Optional channel to send in.| +|-message| Optional message ID to edit.| +|-content| Text content for the message.| +|-title| Title field.| +|-desc| Description field.| +|-color| Color field, either in hex or a color name.| +|-url| URL field for embed.| +|-thumbnail| URL to an image for thumbnail field.| +|-image| URL to an image.| +|-author| Author field.| +|-authoricon| URL for the icon in 'author' field.| +|-authorurl| Url of the 'author' field| +|-footer| Footer field.| +|-footericon| URL to an image for footer icon field.| + +The values for simple embeds need to bet placed within quotes: + +```go +-se -title "This is my title" -desc "This is my description" -thumbnail "https://via.placeholder.com/300/" +``` + +This generates the following embed: + +![SimpleEmbed Result](simpleembed_example.png) + +You can play around with this command a bit, it's really easy to use. + +{{% notice info %}} + +Simple embeds can be used in custom commands: + +`{{execAdmin "se" "-desc" "This is my description"}}` + +{{% /notice %}} diff --git a/content/reference/custom_embed_example.png b/content/reference/custom_embed_example.png new file mode 100644 index 0000000..09c8177 Binary files /dev/null and b/content/reference/custom_embed_example.png differ diff --git a/content/reference/dancingwumpus.gif b/content/reference/dancingwumpus.gif new file mode 100644 index 0000000..9223965 Binary files /dev/null and b/content/reference/dancingwumpus.gif differ diff --git a/content/reference/dealwithit.gif b/content/reference/dealwithit.gif new file mode 100644 index 0000000..f5f149e Binary files /dev/null and b/content/reference/dealwithit.gif differ diff --git a/content/reference/embed_custom_command_result.png b/content/reference/embed_custom_command_result.png new file mode 100644 index 0000000..8cb2f38 Binary files /dev/null and b/content/reference/embed_custom_command_result.png differ diff --git a/content/reference/embed_generator_result.png b/content/reference/embed_generator_result.png new file mode 100644 index 0000000..d23804b Binary files /dev/null and b/content/reference/embed_generator_result.png differ diff --git a/content/reference/how-to-get-ids.md b/content/reference/how-to-get-ids.md new file mode 100644 index 0000000..b9d76e8 --- /dev/null +++ b/content/reference/how-to-get-ids.md @@ -0,0 +1,22 @@ ++++ +title = 'How to Get IDs' +weight = 4 ++++ + +Details on obtaining IDs for users, channels, roles, etc. for use within YAGPDB. + + + +**User IDs:** Can be found by mentioning the user then adding a \ such as `\@YAGPDB.xyz#8760` . Alternatively if you have developer mode on, you can right click and select Copy ID. [How to enable developer mode in Discord](https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-). + +**Channel IDs:** Can be found by mentioning the channel then adding a \ such as `\#announcements`. Alternatively if you have developer mode on, you can right click on the channel and select Copy ID. + +**Role IDs**: Use the `listroles`command. + +**Emote IDs:** + +If it is a **custom emote**, adding a \ in front of the emote such as `\:yag:` will display the name along with the ID such as `<:yag:277569741932068864>`. On an Android device remove backslash and enclose `:yag:` inside back-ticks `` `:yag:`. `` + +If it is an **animated emote**, do the same steps as a normal emote. If you do not have Discord Nitro, you can have a friend or a bot use the emote and right click on the emote to open its link. The ID will be a part of the URL. + +If it is a **default emote**, look up the Unicode for the emote on Google. Note that some of the more customized default emotes such as some of the family emotes will not work in any of the YAGPDB commands. diff --git a/content/reference/kms.gif b/content/reference/kms.gif new file mode 100644 index 0000000..cce020b Binary files /dev/null and b/content/reference/kms.gif differ diff --git a/content/reference/load_windows10.gif b/content/reference/load_windows10.gif new file mode 100644 index 0000000..714263a Binary files /dev/null and b/content/reference/load_windows10.gif differ diff --git a/content/reference/loading1.gif b/content/reference/loading1.gif new file mode 100644 index 0000000..4ab7c42 Binary files /dev/null and b/content/reference/loading1.gif differ diff --git a/content/reference/loading2.gif b/content/reference/loading2.gif new file mode 100644 index 0000000..3c329ff Binary files /dev/null and b/content/reference/loading2.gif differ diff --git a/content/reference/movingeyes.gif b/content/reference/movingeyes.gif new file mode 100644 index 0000000..b0f65c8 Binary files /dev/null and b/content/reference/movingeyes.gif differ diff --git a/content/reference/no_u.gif b/content/reference/no_u.gif new file mode 100644 index 0000000..ef62162 Binary files /dev/null and b/content/reference/no_u.gif differ diff --git a/content/reference/okSpin.gif b/content/reference/okSpin.gif new file mode 100644 index 0000000..8feb060 Binary files /dev/null and b/content/reference/okSpin.gif differ diff --git a/content/reference/oofoof.gif b/content/reference/oofoof.gif new file mode 100644 index 0000000..bb35b60 Binary files /dev/null and b/content/reference/oofoof.gif differ diff --git a/content/reference/oooo.gif b/content/reference/oooo.gif new file mode 100644 index 0000000..7a4aca4 Binary files /dev/null and b/content/reference/oooo.gif differ diff --git a/content/reference/owowhatsthis.gif b/content/reference/owowhatsthis.gif new file mode 100644 index 0000000..54cfb8c Binary files /dev/null and b/content/reference/owowhatsthis.gif differ diff --git a/content/reference/rageping.gif b/content/reference/rageping.gif new file mode 100644 index 0000000..b1fd604 Binary files /dev/null and b/content/reference/rageping.gif differ diff --git a/content/reference/reeeeeeeeeeeeeeeeeeee.gif b/content/reference/reeeeeeeeeeeeeeeeeeee.gif new file mode 100644 index 0000000..418be5c Binary files /dev/null and b/content/reference/reeeeeeeeeeeeeeeeeeee.gif differ diff --git a/content/reference/regex.md b/content/reference/regex.md new file mode 100644 index 0000000..402de46 --- /dev/null +++ b/content/reference/regex.md @@ -0,0 +1,78 @@ ++++ +title = 'Using RegEx' +weight = 5 ++++ + +A quick overview of golang flavored RegEx for your convenience. + + + +Full RE2 syntax reference at > [https://github.com/google/re2/wiki/Syntax](https://github.com/google/re2/wiki/Syntax)\ +Go RegExp doc at > [https://golang.org/pkg/regexp/](https://golang.org/pkg/regexp/)\ +More about general concepts > [https://www.regular-expressions.info/](https://www.regular-expressions.info/) + +## Basic Regex + +### Basics of Regular Expressions + +#### Match + +You can match a word by putting it between two brackets. + +_As example, this will only match the word "Dinosaur":_ `(Dinosaur)` + +#### Don't match + +Using `?:` after opening parenthesis of a capturing group creates a non-capturing group. Useful for example with template function `reFindAllSubmatches`. + +_This will not sub-match the words "red, blue, green":_ \ +``{{ reFindAllSubmatches `(?:color=)(red|blue|green)` "color=red beautiful" }}`` + +To clarify more - it will not show `dateid,` because it's a whole match:\ +``{{ slice (index (reFindAllSubmatches `(?:dateid=)([0-9]{5})` "dateid=12345") 0) 1 }}`` + +#### Match A or B + +You may also want to catch multiple options, for that we use a _"Vertical bar"_ or also known as a _"Pipe"_ between linux users. + +_As example, this will match if either "Cat" or "Dog" is present:_ `(Cat|Dog)` + +To match anything of any length, use `.*`. + +### Character classes + +#### Words + +To match a word, you put it between two brackets. + +Example: `(Banana)` + +#### Characters + +For matching characters there are multiple options: + +##### Matching specific characters + +For matching a specific character, you put them in square brackets. + +This will match A, B and C: `([abc])` + +This will match every character from A-z: `([A-z])` + +This will match every number: `([0-9])` + +#### Special Characters + +Sometimes you have to use special characters but it may cause conflicts. In this case, you will have to use an escape character. + +For example, this is a star that doesn't interfere with other matches `\*`. + +## Understanding Regex + +If you still do not know what Regex are or want to know more. Check out the cheat sheet on the site below. + +[computerhope.com](https://www.computerhope.com/jargon/r/regex.htm) + +## Great tools for writing and testing Regex + +[regex101.com](https://regex101.com) diff --git a/content/reference/shakyshaky.gif b/content/reference/shakyshaky.gif new file mode 100644 index 0000000..50f39e9 Binary files /dev/null and b/content/reference/shakyshaky.gif differ diff --git a/content/reference/simpleembed_example.png b/content/reference/simpleembed_example.png new file mode 100644 index 0000000..74c15f0 Binary files /dev/null and b/content/reference/simpleembed_example.png differ diff --git a/content/reference/spycolor.png b/content/reference/spycolor.png new file mode 100644 index 0000000..2d4fe93 Binary files /dev/null and b/content/reference/spycolor.png differ diff --git a/content/reference/templates/_index.md b/content/reference/templates/_index.md new file mode 100644 index 0000000..d0578ef --- /dev/null +++ b/content/reference/templates/_index.md @@ -0,0 +1,844 @@ ++++ +archetype = "chapter" +title = "Templates" +weight = 1 ++++ + +Library of base data accessible within custom scripting. + + + +> "Go is all about type... Type is life." // William Kennedy + +## Preface + +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/](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. **Functions** are covered +[here](functions). For detailed explanations and syntax guide refer to the [learning +resource](https://learn.yagpdb.xyz/). + +**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 or have +been disabled in main bot, but are in master code branch. + +{{% notice warning %}} + +**Always put curly brackets around the data and "actions you perform" you want to formulate as a template** like +this:`{{.User.Username}}` + +This `{{ ... }}` syntax of having two curly brackets aka braces around context is necessary to form a template's control +structure also known as an action with methods and functions stated below. + +{{% /notice %}} + +{{% notice info %}} + +Templating system uses standard ASCII quotation marks:\ +0x22 > `"` for straight double quotes, 0x27 > `'`for apostrophes and 0x60 `` ` `` for backticks/back quotes; so make +sure no "smart-quotes" are being used. + +The difference between back quotes and double quotes in string literals is covered +[here](https://go.dev/ref/spec#String\_literals). + +{{% /notice %}} + +## 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. \ +\ +For example .User is a Discord User object/structure of current context, meaning the triggering user. To get user object +for other users, functions `getMember`, `userArg` would help. Same meaning of object/struct applies to other **Fields** +with dot prefix. If it is mentioned as a **Method** (for example, .Append for type _cslice_) or as a field on a struct +(for example, .User.Bot) then it can not be used alone in template context and always belongs on a parent value. That +is, `{{.Bot}}` would return `` whereas `{{.User.Bot}}` returns _bool_ true/false. Another good example is +.Reaction.Emoji.MessageFormat, here you can use .MessageFormat every time you get emoji structure of type +_discordgo.Emoji_, either using reaction triggers or for example .Guild.Emojis.\ +\ +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. + +For commenting something inside a template, use this syntax: `{{/* this is a comment */}}`. May contain newlines. +Comments do not nest and they start and end at the delimiters. + +To trim spaces use hyphens after/before curly brackets, for example >`{{- /* this is a multi-line` \ +`comment with whitespace trimmed from preceding and following text */ -}}` \ +Using`{{- ... -}}` is also handy inside`range` actions, because whitespaces and newlines are rendered there as output.\ +\ +`$` has a special significance in templates, it is set to the [starting value of a +dot](https://golang.org/pkg/text/template/#hdr-Variables). 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`, `range`, `etc.`) in which it is +declared, or to the end of custom command if there are no control structures - call it global scope. + +## Pipes + +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 pipeline`randInt` 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`. + +{{% notice warning %}} + +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. + +{{% /notice %}} + +## Context Data + +Context data refers to information accessible via the dot, `{{ . }}`. The accessible data ranges from useful constants +to information regarding the environment in which the custom command was executed, such as the user that ran it, the +channel it was ran in, and so on. + +Fields documented as accessible on specific structures, like the context user `.User`, are usable on all values that +share the same type. That is, given a user `$user`, `$user.ID` is a valid construction that yields the ID of the user. +Similarly, provided a channel `$channel`, `$channel.Name` gives the name of the channel. + +|**Field**| **Description**| +|-| -| +|.BotUser| Returns bot's user object.| +|.CCID| The ID of currently executing custom command in type of _int64_.| +|.CCRunCount| Shows run count of triggered custom command, although this is not going to be 100% accurate as it's cached up to 30 minutes.| +|.CCTrigger| If trigger type has a printable trigger, prints out its name. For example, if trigger type is `regex` and trigger is set to `\A`, it would print `\A`.| +|.DomainRegex| Returns string value of in-built domain-matching regular expression.| +|.IsMessageEdit| Returns boolean true/false if message is edited and edit trigger for custom commands is enabled. Defaults to false.| +|.IsPremium| Returns boolean true/false whether guild is premium of YAGPDB or not.| +|.LinkRegex| Returns string value of in-built link-matching regular expression.| +|.Permissions| Returns all mapped-out permission bits available for Discord in their bitshifted decimal values;
e.g. `{{.Permissions.AddReactions}}` would return `64`, same as `{{bitwiseLeftShift 1 6}}`. More [here](https://discord.com/developers/docs/topics/permissions#permissions).| +|.ServerPrefix| Returns server's command-prefix.| + +### Channel + +|**Field**| **Description**| +|-| -| +|.Channel.AppliedTags| All tags applied to a forum channel post as _\[]int64_ slice of tag IDs.| +|.Channel.AvailableTags| All tags available for forum channel posts as a slice of _\[]discordgo.ForumTag_.| +|.Channel.Bitrate| Bitrate used; only set on voice channels.| +|.Channel.GuildID| Guild ID of the channel.| +|.Channel.ID| The ID of the channel.| +|.Channel.IsForum| Whether the channel is a forum channel.| +|.Channel.IsPrivate| Whether the channel is created for DM.| +|.Channel.IsThread| Whether the channel is a thread.| +|.Channel.Mention| Mentions the channel object.| +|.Channel.Name| The name of the channel.| +|.Channel.NSFW| Outputs whether this channel is NSFW or not.| +|.Channel.OwnerID| The ID of the creator of threads as _int64_. Returns `0` for normal channels.| +|.Channel.ParentID| The ID of the channel's parent (category), returns 0 if none.| +|.Channel.PermissionOverwrites| A slice of [permission overwrite](https://discord.com/developers/docs/resources/channel#overwrite-object) structures applicable to the channel.| +|.Channel.Position| Channel position from top-down.| +|.Channel.Topic| The topic of the channel.| +|.Channel.Type| The type of the channel. [Explained here.](https://discord.com/developers/docs/resources/channel#channel-object-channel-types)| + +[Channel object in Discord documentation](https://discordapp.com/developers/docs/resources/channel#channel-object). + +Channel functions are covered [here](functions#channel). + +### Guild / Server + +|**Field**| **Description**| +|-| -| +|.Guild.AfkChannelID| Outputs the AFK channel ID.| +|.Guild.AfkTimeout| Outputs the time when a user gets moved into the AFK channel while not being active.| +|.Guild.Banner| Returns guild banner hash if available.| +|.Guild.Channels| Outputs a _slice_ of channels in the guild with type _\[]dstate.ChannelState._| +|.Guild.DefaultMessageNotifications| Outputs the default message [notification setting](https://discordapp.com/developers/docs/resources/guild#guild-object-default-message-notification-level) for the guild.| +|.Guild.Emojis| Outputs a list of emojis in the guild with type _discordgo.Emoji._| +|.Guild.ExplicitContentFilter| Outputs the explicit content [filter level](https://discordapp.com/developers/docs/resources/guild#guild-object-explicit-content-filter-level) for the guild.| +|.Guild.Features| The list of enabled guild features of type _\[]string_.| +|.Guild.Icon| Outputs the [icon hash](https://discordapp.com/developers/docs/reference#image-formatting) ID of the guild's icon. Setting full icon URL is explained [here](https://discord.com/developers/docs/reference#image-formatting).| +|.Guild.ID| Outputs the ID of the guild.| +|.Guild.MemberCount| Outputs the number of users on a guild.| +|.Guild.MfaLevel| The required [MFA level](https://discordapp.com/developers/docs/resources/guild#guild-object-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.| +|.Guild.Name| Outputs the name of the guild.| +|.Guild.OwnerID| Outputs the ID of the owner.| +|.Guild.PreferredLocale| The preferred locale of a guild with the "PUBLIC" feature; used in server discovery and notices from Discord; defaults to "en-US"| +|.Guild.Roles| 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](https://discordapp.com/developers/docs/topics/permissions#role-object).| +|.Guild.Splash| Outputs the [splash hash](https://discordapp.com/developers/docs/reference#image-formatting) ID of the guild's splash.| +|.Guild.SystemChannelID| The ID of the channel where guild notices such as welcome messages and boost events are posted.| +|.Guild.Threads| Returns all active threads in the guild as a slice of type _\[]dstate.ChannelState_.| +|.Guild.VerificationLevel| Outputs the required verification level for the guild.| +|.Guild.VoiceStates| Outputs a slice of [voice states](https://discord.com/developers/docs/resources/voice#voice-state-object) (users connected to VCs) with type _\[]discordgo.VoiceState._| +|.Guild.WidgetChannelID| Outputs the channel ID for the server widget.| +|.Guild.WidgetEnabled| Outputs whether or not the server widget is enabled.| + +|**Method**| **Description**| +|-| -| +|`.Guild.BannerURL` "256"| Gives the URL for guild's banner, argument “256” is the size of the picture and increases/decreases twofold (e.g. 512, 1024 or 128, 64 etc.).| +|`.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| Calculates full [permissions](https://discord.com/developers/docs/topics/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 permissions integer the triggering member has in the context/triggering channel.| +|`.Guild.GetRole` id| Gets the [role object](https://discord.com/developers/docs/topics/permissions#role-object) with the integer ID provided, returning a struct of type _\*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}}`| +|`.Guild.IconURL` "size"| Outputs the URL of guild’s avatar/icon. Size argument is the size of the picture and can increase/decrease twofold (e.g. 512, 1024 or 128, 64 etc.).| + +[Guild object in Discord documentation](https://discordapp.com/developers/docs/resources/guild#guild-object). + +### Member + +|**Field**| **Description**| +|-| -| +|.Member.Avatar| Member’s avatar hash, if it is custom per server, then custom avatar hash.| +|.Member.CommunicationDisabledUntil| Returns _time.Time_ when member’s time out expires. Time is in the past or `nil` if the user is not timed out.
NB. was previously called TimeoutExpiresAt.| +|.Member.Flags| [Guild member flags](https://discord.com/developers/docs/resources/guild#guild-member-object-guild-member-flags) represented as a bit set, defaulting to 0.| +|.Member.GuildID| The guild ID on which the member exists.| +|.Member.JoinedAt| When member joined the guild/server of type _discordgo.Timestamp_. Method `.Parse` will convert this to of type _time.Time_.| +|.Member.Nick| The nickname for this member.| +|.Member.Pending| Returns _bool_ true/false, whether member is pending behind Discord's screening process.| +|.Member.PremiumSince| When the user started boosting the guild.| +|.Member.Roles| A _slice_ of role IDs that the member has.| +|.Member.User| Underlying user object on which the member is based on.| + +|**Method**| **Description**| +|-| -| +|`.Member.AvatarURL` "256"| Gives the URL for member’s avatar, argument “256” is the size of the picture and increases/decreases twofold (e.g. 512, 1024 or 128, 64 etc.).| + +[Member object in Discord documentation](https://discordapp.com/developers/docs/resources/guild#guild-member-object). + +Member functions are covered [here](functions#member). + +### Message + +|**Field**| **Description**| +|-| -| +|.Message.Activity| Represents the activity sent with a message, such as a game invite, of type _\*discordgo.MessageActivity_. Sent with Rich Presence-related chat embeds.| +|.Message.Attachments| Attachments of this message (_slice_ of [attachment](https://discord.com/developers/docs/resources/channel#attachment-object) objects).| +|.Message.Author| Author of the message ([User](#user) object).| +|.Message.ChannelID| Channel ID this message is in.| +|.Message.Content| Text content of this message.| +|.Message.ContentWithMentionsReplaced| Replaces all <@ID> mentions with the username of the mention.| +|.Message.EditedTimestamp| 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._| +|.Message.Embeds| Embeds of this message (_slice_ of embed objects).| +|.Message.GuildID| Guild ID in which the message is.| +|.Message.ID| ID of the message.| +|.Message.Link| Discord link to the message. \*| +|.Message.Member| [Member object](#member). \*| +|.Message.MentionEveryone| Whether the message mentions everyone, returns _bool_ true/false.| +|.Message.MentionRoles| The roles mentioned in the message, returned as a slice of type _discordgo.IDSlice._| +|.Message.Mentions| Users this message mentions, returned as a slice of type _\[]\*discordgo.User._| +|.Message.MessageReference| MessageReference contains reference data sent with crossposted or reply messages. Has fields MessageID, ChannelID and GuildID.| +|.Message.Pinned| Whether this message is pinned, returns _bool_ true/false.| +|.Message.Reactions| Reactions on this message, returned as a slice of type _\[]\*discordgo.MessageReactions_. Reaction Object in [Discord documentation](https://discord.com/developers/docs/resources/channel#reaction-object).| +|.Message.Reference| Reference returns MessageReference of given message.| +|.Message.ReferencedMessage| Message object associated by message\_reference, like a message that was replied to.| +|.Message.Timestamp| Timestamp of the message in type _discordgo.Timestamp_ (use .Message.Timestamp.Parse to get type _time.Time_ and .Parse.String method returns type _string_).| +|.Message.TTS| Whether the message is text-to-speech. \*| +|.Message.Type| The [type](https://discordapp.com/developers/docs/resources/channel#message-object-message-types) of the message.| +|.Message.WebhookID| If the message is generated by a webhook, this is the webhook's id| + +|Field| Description| +|-| -| +|.Args| List of everything that is passed to .Message.Content. .Args is a _slice_ of type _string_.| +|.Cmd| .Cmd is of type _string_ and shows all arguments that trigger custom command, part of .Args. Starting from `{{index .Args 0}}`.| +|.CmdArgs| List of all the arguments passed after `.Cmd` (`.Cmd` is the actual trigger) `.CmdArgs` is a _slice_ of type _string_. For example `{{$allArgs := (joinStr " " .CmdArgs)}}` saves all the arguments after trigger to a variable `$allArgs`.| +|.StrippedMsg| "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. + +[Message object in Discord documentation](https://discordapp.com/developers/docs/resources/channel#message-object). + +Message functions are covered [here](functions#message). + +### Reaction + +This is available and part of the dot when reaction trigger type is used. + +|**Field**| **Description**| +|-| -| +|.Reaction| Returns reaction object which has following fields `UserID`, `MessageID`,
`Emoji.(ID/Name/...)`, `ChannelID`, `GuildID`. The 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).| +|.Reaction.Emoji.APIName| Returns type _string_, a correctly formatted API name for use in the MessageReactions endpoints. For custom emojis it is `emojiname:ID`.| +|.Reaction.Emoji.MessageFormat| Returns a correctly formatted emoji for use in Message content and embeds. It's equal to `<:.Reaction.Emoji.APIName>` and `` for animated emojis.| +|.ReactionAdded| Returns a boolean type _bool_ true/false indicating whether reaction was added or removed.| +|.ReactionMessage| Returns the message object reaction was added to. Not all regular .Message fields are filled though e.g. .Member. `{{range .ReactionMessage.Reactions}}`
`{{.Count}} - {{.Emoji.Name}}`
`{{end}}`Returns emoji count and their name.Has an alias `.Message` and it works the same way.| + +[Reaction object in Discord documentation](https://discordapp.com/developers/docs/resources/channel#reaction-object).\ +[Emoji object in Discord documentation.](https://discord.com/developers/docs/resources/emoji) + +### User + +|**Field**| **Description**| +|-| -| +|.User| The user's username together with discriminator.| +|.User.Avatar| The user's avatar [hash](https://discord.com/developers/docs/reference#image-formatting).| +|.User.Bot| Determines whether the target user is a bot - if yes, it will return `true`.| +|.User.Discriminator| The user's discriminator/tag (The four digits after a person's username).| +|.User.Globalname| User's global username from the new naming system.| +|.User.ID| The user's ID.| +|.User.Mention| Mentions user.| +|.User.String| The user's username, with legacy discriminator if available, as _string_ type.| +|.User.Username| The user's username.| +|.UsernameHasInvite| Only works with join and leave messages (not join dms). It will determine does the username contain an invite link.| +|.RealUsername| 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.| + +|**Method**| **Description**| +|-| -| +|`.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.).| + +[User object in Discord documentation](https://discordapp.com/developers/docs/resources/user#user-object). + +User functions are covered [here](functions#user). + +## Actions + +Actions, or elements enclosed in double braces `{{ }}`, are what makes templates dynamic. Without them, templates would +be no more than static text. In this section, we introduce several special kinds of actions which affect the control +flow of the program. For example, iteration actions like `range` and `while` permit statements to be executed multiple +times, while conditional actions like `if` and `with` allow for alteration of what statements are ran or are not ran. + +### If (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`.\ +Learning resources covers conditional branching [more in depth](https://learn.yagpdb.xyz/beginner/control\_flow\_1).\ + +{{% notice style="success" %}} + +`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 behavior is unique +to `eq`. + +{{% /notice %}} + +{{% notice info %}} + +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`. + +{{% /notice %}} + +
CaseExample
**Case****Example**
if`{{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 many`else if`statements as many different conditionals you have.
else`{{if (condition)}} output1 {{else}} output2 {{end}}`
+ +
CaseExample
**Boolean Logic**
and`{{if and (cond1) (cond2) (cond3)}} output {{end}}`
not`{{if not (condition)}} output {{end}}`
or`{{if or (cond1) (cond2) (cond3)}} output {{end}}`
+ +
CaseExample
**Comparison operators**
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 + +`range`iterates over element values in variety of data structures in pipeline - integers, 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. + +{{% notice info %}} + +To skip execution of a single iteration and jump to the next iteration, the `{{continue}}` action may be used. Likewise, +if one wishes to skip all remaining iterations, the `{{break}}` action may be used. These both are usable also inside +`while` action. + +{{% /notice %}} + +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`, `range`is concluded with`{{end}}`action and declared variable scope inside `range` extends to that point.\ + +
{{/* range over an integer */}}
+{{range 2}}{{.}}{{end}}
+{{range $k, $v := toInt64 2}}{{$k}}{{$v}}{{end}}
+**{{/* 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 }}
+`
+ +{{% notice warning %}} + +**Custom command response was longer than 2k (contact an admin on the server...)**\ +or \ +**Failed executing template: response grew too big (>25k)** + +\ +This is quite common error users will get whilst using range. Simple example to reproduce it:\ +_\{{ range seq 0 10000 \}}_\ +_\{{ $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 := . -\}}_ + +{{% /notice %}} + +### Try-catch + +Multiple template functions have the possibility of returning an error upon failure. For example, `dbSet` can return a +short write error if the size of the database entry exceeds some threshold. + +While it is possible to write code that simply ignores the possibility of such issues occurring (letting the error stop +the code completely), there are times at which one may wish to write more robust code that handles such errors +gracefully. The `try`-`catch` construct enables this possibility. + +Similar to an `if` action with an associated `else` branch, the `try`-`catch` construct is composed of two blocks: the +`try` branch and the `catch` branch. First, the code in the `try` branch is ran, and if an error is raised by a function +during execution, the `catch` branch is executed instead with the context (`.`) set to the offending error. + +To check for a specific error, one can compare the result of the `Error` method with a predetermined message. (For +context, all errors have a method `Error` which is specified to return a message describing the reason that the error +was thrown.) For example, the following example has different behavior depending on whether "Reaction blocked" is in the +message of the error caught. + +```go +{{ try }} + {{ addReactions "👋" }} + added reactions successfully +{{ catch }} + {{ if in .Error "Reaction blocked" }} + user blocked YAG :( + {{ else }} + different issue occurred: {{ .Error }} + {{ end }} +{{ end }} +``` + +### While + +`while` iterates as long as the specified condition is true, or more generally evaluates to a non-empty value. The dot +(`.`) is not affected, unlike with the `range` action. Analogous to `range`, `while` introduces a new scope which is +concluded by the `end` action. Within the body of a `while` action, the `break` and `continue` actions can be used to +appropriate effect, like in a `range` action. + +```go +{{/* efficiently search for an element in a sorted slice using binary search */}} +{{ $xs := cslice 1 3 5 6 6 8 10 12 }} +{{ $needle := 8 }} + +{{ $lo := 0 }} +{{ $hi := sub (len $xs) 1 }} +{{ $found := false }} +{{/* it's possible to combine multiple conditions using logical operators */}} +{{ while and (le $lo $hi) (not $found) }} + {{- $mid := div (add $lo $hi) 2 }} + {{- $elem := index $xs $mid }} + {{- if lt $elem $needle }} + {{- $lo = add $mid 1 }} + {{- else if eq $elem $needle }} + {{- print "found at index " $mid }} + {{- $found = true }} + {{- else }} + {{- $hi = sub $mid 1 }} + {{- end -}} +{{ end }} +{{ if not $found }} not found {{ end }} +``` + +### With + +`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 an `else` or `else if` action is used, +execution moves on to those branches instead, similar to the `if` action. \ +\ +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. + +```go +{{/* 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 }} +{{/* using else-if chain is possible */}} +{{ with false }} + not executed +{{ else if eq $x "42" }} + x is 42, dot is unaffected {{ .User.Mention }} +{{ else if eq $x "43" }} + x is not 43, so this is not executed +{{ else }} + branch above already executed, so else branch is not +{{ end }} +``` + +## Associated Templates + +Templates (i.e., custom command programs) may also define additional helper templates that may be invoked from the main +template. Technically speaking, these helper templates are referred to as _associated templates_. Associated templates +can be used to create reusable procedures accepting parameters and outputting values, similar to functions in other +programming languages. + +### Definition + +To define an associated template, use the `define` action. It has the following syntax: + +```go +{{ define "template_name" }} + {{/* associated template body */}} +{{ end }} +``` + +{{% notice warning %}} + +**Warning:** Template definitions must be at the top level of the custom command program; in other words, they cannot be +nested in other actions (for example, an if action.) That is, the following custom command is invalid: + +```go +{{ if $cond }} + {{ define "hi" }} hi! {{ end }} +{{ end }} +``` + +{{% /notice %}} + +The template name can be any string constant; however, it cannot be a variable, even if said variable references a value +of string type. As for the body of the associated template body, it can be anything that is a standalone, syntactically +valid template program. Note that the first criterion precludes using variables defined outside of the associated +template; that is, the following custom command is invalid, as the body of the associated template references a variable +(`$name`) defined in an outer scope: + +```go +{{ $name := "YAG" }} +{{ define "hello" }} + Hello, {{ $name }}! +{{ end }} +``` + +If accessing the value of `$name` is desired, then it needs to be passed as part of the context when executing the +associated template. + +Within the body of an associated template, the variable `$` and the context dot (`.`) both initially refer to the data +passed as context during execution. Consequently, any data on the original context that needs to be accessed must be +explicitly provided as part of the context data. For example, if one wishes to access `.User.Username` in an associated +template body, it is necessary to pass `.User.Username` as part of the context data when executing said template. + +To return a value from an associated template, use the `return` action. Encountering a `return` action will cause +execution of the associated template to end immediately and control to be returned to the caller. For example, below is +an associated template that always returns `1`: + +```go +{{ define "getOne" }} {{ return 1 }} {{ end }} +``` + +Note that it is not necessary for a value to be returned; `{{ return }}` by itself is completely valid. + +{{% notice style="success" %}} + +**Note:** Since all custom commands are themselves templates, using a `return` action at the top level is perfectly +valid, and will result in execution of the custom command being stopped at the point the `return` is encountered. + +```go +{{ if not .CmdArgs }} + no arguments passed + {{ return }} {{/* anything beyond this point is not executed */}} +{{ end }} +{{ $firstArg := index .CmdArgs 0 }} +{{/* safe since .CmdArgs is guaranteed to be non-empty here */}} +``` + +{{% /notice %}} + +### Execution + +To execute a custom command, one of three methods may be used: `template`, `block`, or `execTemplate`. + +#### Template action + +`template` is a function-like action that executes the associated template with the name provided, ignoring its return +value. Note that the name of the template to execute must be a string constant; similar to `define` actions, a variable +referencing a value of string type is invalid. Data to use as the context may optionally be provided following the name. + +{{% notice info %}} + +While `template` is function-like, it is not an actual function, leading to certain quirks; notably, it must be used +alone, not part of another action (like a variable declaration), and the data argument need not be parenthesized. Due to +this, it is recommended that `execTemplate`, which has much more intuitive behavior, be used instead of the `template` +action if at possible. + +{{% /notice %}} + +Below is an example of the `template` action in action: + +```go +{{ define "sayHi" }} + {{- if . -}} + hi there, {{ . }} + {{- else }} + hi there! + {{- end -}} +{{ end }} +{{ template "sayHi" }} {{/* hi there! */}} +{{ template "sayHi" "YAG" }} {{/* hi there, YAG */}} +``` + +Trim markers: `{{- ... -}}`were used in above example because whitespace is considered as part of output for associated +template definitions (and actions in general). + +#### Block action + +`block` has a structure similar to that of a `define` action. It is equivalent to a `define` action followed by a +`template` action: + +```go +{{ $name := "YAG" }} +{{ block "sayHi" $name }} + hi there, {{ . }} +{{ end }} + +{{/* equivalent to above */}} +{{ define "sayHi" }} + hi there, {{ . }} +{{ end }} +{{ template "sayHi" $name }} +``` + +#### execTemplate function + +`execTemplate` is essentially the same as the `template` action, but it provides access to the return value of the +template and may be used as part of another action. Below is an example using `execTemplate`: + +```go +{{ define "factorial" }} + {{- $n := 1 }} + {{- range seq 2 (add . 1) }} + {{- $n = mult $n . }} + {{- end }} + {{- return $n -}} +{{ end }} + +{{ $fac := execTemplate "factorial" 5 }} +2 * 5! = {{ mult $fac 2 }} +``` + +## 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](#time).\ +\ +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.) + +{{% notice warning %}} + +**Reference type-like behavior:** Slices and dictionaries in CCs 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: + +```go +{{ $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 behavior is undesirable, copy the slice/dictionary via `cslice.AppendSlice` or a `range` + `Set` call . + +```go +{{ $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. + +{{% /notice %}} + +### templates.Slice + +`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. + +|**Function**| **Description**| +|-| -| +|`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.| + +|Method| Description| +|-| -| +|.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 _bool_ 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 _[]string_ only if **all** elements are pure _string_, else `<no value>` is returned.Example in this section's [Snippets](#this-sections-snippets).| + +#### This section's snippets + +* To demonstrate .StringSlice `{{(cslice currentTime.Month 42 "YAGPDB").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 `` is returned. + +General example: + +```go +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 }}** +Indexing that position 1: +**{{ index $x 1 }}** +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 + +`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. + +|**Function**| **Description**| +|-| -| +|`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.SDict_ elements' inherent type does not change.| + +|**Method**| **Description**| +|-| -| +|.Del "key"| Deletes given key from _sdict_.| +|.Get "key"| Retrieves given key from _sdict_.| +|.HasKey "key"| Returns _bool_ true/false regarding whether the key is set or not e.g. `{{(sdict "YAGPDB" "is cool").HasKey "YAGPDB"}}` would return `true`.| +|.Set "key" value| Changes/sets given key to a new value or creates new one, if no such key exists in _sdict_.| + +```go +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 }}** +``` + +{{% notice style="success" %}} + +**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. + +{{% /notice %}} + +## Database + +You have access to a basic set of Database functions having return of type _\*customcommands.LightDBEntry_ called here +[DBEntry](#dbentry). \ +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 a `short write` error gets raised. + +You can just pass a `userID`of 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 covers database [more in-depth](https://learn.yagpdb.xyz/intermediate/custom-command-database). + +**Database functions** are covered [here](functions#database). + +[Example here](/reference/custom-command-examples#database-example). + +### DBEntry + +|**Fields**| **Description**| +|-| -| +|.ID| ID of the entry of type _int64_.| +|.GuildID| ID of the server of type _int64_.| +|.UserID| Value of type _int64_ for `userID` argument or ID of the user if for example `.User.ID` was used for `dbSet`.| +|.User| User object of type _discordgo.User_ having only `.ID` field, .Mention is still usable with correct userID field entry.| +|.CreatedAt| When this entry was created, of type _time.Time_.| +|.UpdatedAt| When this entry was last updated, of type _time.Time_.| +|.ExpiresAt| When entry will expire, of type _time.Time_.| +|.Key| The key of the entry, of type _string_.| +|.Value| The value of the entry. All numbers will get returned as _float64_ and other set values are of varying types.| +|.ValueSize| Returns the entry’s value size in bytes.| + +## Tickets + +{{% notice warning %}} + +Ticket functions are limited to 1 call per custom command for both normal and premium guilds and limited to max 10 open +tickets per user. + +{{% /notice %}} + +|**Function**| **Description**| +|-| -| +|`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](#template-ticket) struct on success.| + +### Template Ticket + +|**Field**| **Description**| +|-| -| +|.AuthorID| Author ID of the ticket.| +|.AuthorUsernameDiscrim| The Discord tag of the author of the ticket, formatted like `username#discrim`.| +|.ChannelID| Channel ID of the ticket.| +|.ClosedAt| Time that the ticket was closed, of type _null.Time._ This is, for the most part, useless in custom commands.| +|.CreatedAt| Time that the ticket was created.| +|.GuildID| Guild ID of the ticket.| +|.LocalID| The ticket ID.| +|.LogsID| Log ID of the ticket.| +|.Title| Title of the ticket.| + +## Time + +Time and duration types use Golang's time package library and its methods > +[https://golang.org/pkg/time/#time](https://golang.org/pkg/time/#Time) and also this although slightly different syntax +all applies here > [https://gobyexample.com/time](https://gobyexample.com/time). + +|**Field**| **Description**| +|-| -| +|.DiscordEpoch| Gives you Discord Epoch time in _time.Time._ `{{.DiscordEpoch.Unix}}` would return in seconds > 1420070400.| +|.UnixEpoch| Gives you Unix Epoch time in _time.Time._| +|.TimeHour| Variable of _time.Duration_ type and returns 1 hour > `1h0m0s`.| +|.TimeMinute| Variable of _time.Duration_ type and returns 1 minute > `1m0s`.| +|.TimeSecond| Variable of _time.Duration_ type and returns 1 second > `1s`.| + +Time functions are covered [here](functions#time). diff --git a/content/reference/templates/functions.md b/content/reference/templates/functions.md new file mode 100644 index 0000000..4e50b6e --- /dev/null +++ b/content/reference/templates/functions.md @@ -0,0 +1,396 @@ ++++ +title = 'Functions' +weight = 1 ++++ + +Functions are used to take action within template scripts. Some functions accept arguments, and some functions return +values you can send in your response or use as arguments for other functions. + + + +> Functions are underappreciated. In general, not just in templates. // Rob Pike + +{{% notice info %}} + +Every function having both cases possible for an argument - ID/name, then this name is handled case insensitive, for +example `getRole "yagpdb"` and `getRole "yAgPdB"` would have same responses even if server has both of these roles, so +using IDs is better. + +{{% /notice %}} + +### Channel + +{{% notice info %}} + +The ratelimit for editing a channel is 2 requests per 10 minutes per channel. + +{{% /notice %}} + +|**Function**| **Description**| +|-| -| +|`addThreadMember` thread member| Adds a member to an existing thread. Does nothing if either thread or member arguments are invalid. Argument `thread` can be either ID, "name" or even `nil` for current thread, and `member` can either be ID or @ mention.| +|`createForumPost` channel name content (values)| Creates a new forum post in forum `channel`. Argument `name` represents the title of the post, `content` can be a string, an embed or a complex message. The optional `"values"` argument consists of key-value pairs:`"slowmode" value` allows the user to specify the thread's slowmode in seconds. `"tags" value` allows the user to specify one or more forum tags. Duplicate and invalid tags are ignored, and its `value` can be either a string or cslice for multiple tags. Returns *templates.CtxChannel upon success.

Example: `{{createForumPost <forumChannelID> "Post Title" "content as a string" "slowmode" 60}}`.| +|`createThread` channel messageID name (values)| Creates a new thread in `channel`. `messageID` can point to a valid message (creates a message thread) or nil. `name` is the title of the thread. Optional `values` determine how thread is created and come in following order - `private` of type _bool_ determines whether the thread is private; `auto_archive_duration` must be an integer between 60 and 10080 and it determines when the thread will stop showing in the channel list after given minutes of inactivity, can be set to: 60, 1440, 4320, 10080 ; `invitable` of type _bool_, whether non-moderators can add other non-moderators to a thread, it is only available on private threads. Returns *templates.CtxChannel upon success.
Example: `{{createThread nil nil "YAGPDB is cool" true 60 true}}` will create a thread named "YAGPDB is cool" under current channel without any message reference, thread is private, it will get archived in an hour and non-moderators can add others to thread.| +|`deleteForumPost` post| Deletes existing post in the forum - `post` can be either its ID, post’s name or even `nil` if done inside that post. If supplied argument is not a valid active post then it is ignored. Is an alias of `deleteThread` function.| +|`deleteThread` thread| Deletes existing thread - `thread` can be either its ID, thread’s name or even `nil` if done inside that thread. If supplied argument is not a valid active thread then it is ignored.| +|`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 (print "YAG - " (randInt 1000))}}`| +|`editChannelTopic` channel "newTopic"| Function edits channel's topic/description. `channel` can be either ID, "name" or `nil` if triggered in that channel where 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 _\*templates.CtxChannel_. For example > `{{(getChannel nil).Name}}` returns the name of the channel command was triggered in.| +|`getChannelOrThread` channel| Returns type _\*templates.CtxChannel_ corresponding to [Channel](/reference/templates#channel) object.| +|`getChannelPins` channel| Returns a slice of all pinned message objects in targeted channel.| +|`getPinCount` channel| Returns the count of pinned messages in given channel which can be either its ID, name or `nil` for triggering channel. Can be called 2 times for regular and 4 for premium servers.| +|`getThread` channel| Returns type _\*templates.CtxChannel_ corresponding to [Channel](/reference/templates#channel) object.| +|`removeThreadMember` thread member| Removes a member from an existing thread. Does nothing if either thread or user are invalid. Argument `thread` can be either ID, "name" or even `nil` for current thread, and `member` can either be ID or @ mention.| + +### Database + +|**Function**| **Description**| +|-| -| +|`dbBottomEntries` pattern amount nSkip| Returns `amount (max 100)`top entries of keys determined by the `pattern` from the database, sorted by the numeric value in a ascending order, next by entry ID.| +|`dbCount` (userID\|pattern\|query)| Returns the count of all database entries which are not expired. Optional arguments: if `userID` is given, counts entries for that userID; if `pattern`, only those keys are counted that match the given pattern; and if `query` is provided, it should be an sdict with the following keys:
  • `userID` - only counts entries with that userID, defaults to counting entries with any userID.
  • `pattern` - only counts dbEntry keys 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 an _sdict_ with the following options:
  • `userID` - only deletes entries with the dbEntry field .UserID provided, defaults to deleting entries with any ID.
  • `pattern` - only deletes entry keys 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. Does not fetch member data as user object for .User like `dbGetPattern`, `dbBottom/TopEntries` do.| +|`dbGetPattern` userID pattern amount nSkip| Retrieves up to`amount (max 100)`entries from the database in ascending order.| +|`dbGetPatternReverse` userID pattern amount nSkip| Retrieves`amount (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 `ttl` which is an _int_ and represents seconds.| +|`dbTopEntries` pattern amount nSkip| Returns `amount (max 100)`top entries of keys determined by the `pattern` from the database, sorted by the numeric value in a descending order, next by entry ID.| + +Patterns are basic PostgreSQL patterns, not Regexp: An underscore `(_)` matches any single character; a percent sign +`(%)` matches any sequence of zero or more characters. + +{{% notice info %}} + +**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 +convert the number to type _string_ before saving and later 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)` + +{{% /notice %}} + +### ExecCC + +{{% notice warning %}} + +`execCC` calls are limited to 1 / CC for non-premium users and 10 / CC for premium users. + +{{% /notice %}} + +|**Function**| **Description**| +|-| -| +|`cancelScheduledUniqueCC` ccID key| Cancels a previously scheduled custom command execution using `scheduleUniqueCC`.| +|`execCC` ccID channel delay data| Function that executes another custom command specified by `ccID`. With delay 0 the max recursion depth is 2 (using `.StackDepth` shows the current depth). `execCC` is 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. The`delay` 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](/reference/custom-command-examples#countdown-example-exec-cc) also next snippet which shows you same thing run using the same custom command > [Snippets](#execcc-sections-snippets). `execCC` is also thoroughly covered in this [GitHub gist](https://gist.github.com/l-zeuch/9f10d128184509ad531778f26550ed6d).| +|`scheduleUniqueCC` ccID channel delay key data| Same as `execCC`except there can only be 1 scheduled cc execution per server per key (unique name for the scheduler), 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.| + +#### ExecCC section's snippets + +* To demonstrate execCC and .ExecData using the same CC. + +```go +{{ $yag := "YAGPDB rules! " }} +{{ $ctr := 0 }} {{ $yourCCID := .CCID }} +{{ if .ExecData }} + {{ $ctr = add .ExecData.number 1 }} + {{ $yag = print $yag $ctr }} {{ .ExecData.YAGPDB }} +{{ else }} + So, someone rules. + {{ $ctr = add $ctr 1 }} {{ $yag = print $yag 1 }} +{{ end }} +{{ if lt $ctr 5 }} + {{ execCC $yourCCID nil 3 (sdict "YAGPDB" $yag "number" $ctr) }} +{{ else }} FUN'S OVER! {{ end }} +``` + +### Math + +{{% notice info %}} + +Boolean logic (and, not, or) and comparison operators (eq, gt, lt, etc.) are covered in [conditional +branching](/reference/templates#if-conditional-branching). + +{{% /notice %}} + +|**Function**| **Description**| +|-| -| +|`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`.| +|`bitwiseAnd` x y| The output of bitwise AND is 1 if the corresponding bits of two operands is 1. If either bit of an operand is 0, the result of corresponding bit is evaluated to 0. Example: `{{bitwiseAnd 12 25}}` returns `8`, that in binary 00001100 AND 00011001 is 00001000.| +|`bitwiseAndNot` x y| This function is called bit clear because of AND NOT. For example in the expression z = x AND NOT y, each bit of z is 0 if the corresponding bit of y is 1; otherwise it equals to the corresponding bit of x. `{{bitwiseAndNot 7 12}}` returns `3`, that is 0111 AND NOT 1100 is 11.| +|`bitwiseNot` x| The bitwise NOT operator inverts the bits of the argument. Example: `{{bitwiseNot 7}}` returns `-8`. that in binary 0111 to 1000| +|`bitwiseOr` x y z...| The output of bitwise OR is 1 if at least one corresponding bit of two operands is 1. Example: `{{bitwiseOr 12 25}}` returns `29`, that in binary 00001100 OR 00011001 is 00011101.| +|`bitwiseXor` x y| The result of bitwise XOR operator is 1 if the corresponding bits of two operands are opposite. Example: `{{bitwiseXor 12 25}}` returns `21`, that in binary 00001100 OR 00011001 is 00010101.| +|`bitwiseLeftShift` x y| Left shift operator shifts all bits towards left by a certain number of specified bits. The bit positions that have been vacated by the left shift operator are filled with 0. Example: `{{range seq 0 3}} {{bitwiseLeftShift 212 .}} {{end}}` returns `212 424 848`| +|`bitwiseRightShift` x y| Right shift operator shifts all bits towards right by certain number of specified bits. Example: `{{range seq 0 3}} {{bitwiseRightShift 212 .}} {{end}}` returns `212 106 53`.| +|`cbrt` x| Returns the cube root of given argument in type _float64_ e.g. `{{cbrt 64}}` returns `4`.| +|`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.`{{ log "123" 2 }}` will return `6.94251450533924`.| +|`mathConst` "arg"| Function returns all constants available in golang's math package as _float64_. `"arg"` has to be a case-insensitive _string_ from [math constants list](https://pkg.go.dev/math@go1.18.2#pkg-constants). For example `{{mathConst "sqrtphi"}}` would return `1.272019649514069`.| +|`max` x y| Returns the larger of x or y as type _float64_.| +|`min` x y| Returns the smaller of x or y as type _float64_.| +|`mod` x y| Mod returns the floating-point remainder of the division of x by y. For example, `mod 17 3` returns `2` as type _float64_.

Note that like Go's `[math.Mod](https://pkg.go.dev/math#Mod)` function, `mod` takes the sign of `x`, so `mod -5 3` results in `-2`, not `1`. To ensure a non-negative result, use `mod` twice, like such: `mod (add (mod x y) y) y`.| +|`mult` x y z ...| Multiplication, like `add` or `div`, detects first number's type. `{{mult 3.14 2}}` returns `6.28`| +|`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`. Without arguments, range is 0..10. Example in section's [Snippets](#math-sections-snippets).| +|`round` x| 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](#math-sections-snippets).| +|`roundCeil` x| Returns the least integer value greater than or equal to input or rounds up. `{{roundCeil 1.1}}` returns `2`.| +|`roundEven` x| Returns the nearest integer, rounding ties to even.
`{{roundEven 10.5}}` returns `10 {{roundEven 11.5}}` returns `12`.| +|`roundFloor` x| Returns the greatest integer value less than or equal to input or rounds down. `{{roundFloor 1.9}}` returns `1`.| +|`sqrt` x| Returns the square root of a number as type _float64_.
`{{sqrt 49}}` returns `7, {{sqrt 12.34 \|printf "%.4f"}} returns 3.5128`| +|`sub` x y z ...| Returns x - y -z - ... Works like add, just subtracts.| + +#### Math 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 + +### Member + +|**Function**| **Description**| +|-| -| +|`getTargetPermissionsIn` memberID channelID| Returns target’s permissions in the given channel.| +|`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 and bot can't edit owner's nickname.| +|`hasPermissions` arg| Returns true/false on whether triggering user has the permission bit _int64_ that is also set in .Permissions_._| +|`getMember` mention/userID| Function returns [Member object](/reference/templates/#member) having above methods. `{{(getMember .User.ID).JoinedAt}}`
is the same as `{{.Member.JoinedAt}}`| +|`onlineCount`| Returns the count of online users/members on current server.| +|`targetHasPermissions` memberID arg| Returns true/false on whether targeted member has the permission bit _int64_.| + +Permissions are covered +[here](https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags). For example to get +permission bit for "use application commands" `{{bitwiseLeftShift 1 32}}` would return _int64_ `4294967296`. + +### Mentions + +|**Function**| **Description**| +|-| -| +|`mentionEveryone`| Mentions `@everyone`.| +|`mentionHere`| Mentions `@here`.| +|`mentionRoleID` roleID| Mentions the role found with the provided ID.| +|`mentionRoleName` "rolename"| Mentions the first role found with the provided name (case-insensitive).| + +There is also .Mention method available for channel, role, user structs/objects. + +#### Mentions 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](/reference/how-to-get-ids) to get ID). +* `<#&&&&&&&&&&&>` Mentions the channel that has ID &&&&&& (See [How to get IDs](/reference/how-to-get-ids) to get ID). +* `<@&##########>` Mentions the role with ID ######## ([listroles](/commands/all-commands#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. +* `` Mentions a slash command, and makes it clickable and interactive with proper arguments e.g. + ``. + +### Message + +|**Function**| **Description**| +|-| -| +|`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.Emojis can be presented as a _cslice_. Example in section's [Snippets](#message-sections-snippets).| +|`addReactions` "👍" "👎" ...| Adds each emoji as a reaction to the message that triggered the command (recognizes Unicode emojis and `emojiName:emojiID`). Emojis can be presented as a _cslice_.| +|`addResponseReactions` "👍" "👎" ...| Adds each emoji as a reaction to the response message (recognizes Unicode emojis and `emojiName:emojiID`). Emojis can be presented as a _cslice_.| +|`complexMessage` "allowed\_mentions" arg "content" arg "embed" arg "file" arg "filename" arg "reply" arg "silent" arg| `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:
`"allowed_mentions"` sends out very specific mentions where `arg` is an _sdict_ having keys: `"parse"` that takes a _cslice_ with accepted values for a type to mention - "users", "roles", "everyone"; _sdict_ keys`"users"`, `"roles"` take a _cslice_ of IDs either for users or roles and `"replied_user"` is a _bool_ true/false for mentioning replied user.`"content"` for regular text; `"embed"` for embed arguments created by `cembed` or `sdict`. With `cslice` you can use up to 10 embeds as arguments for `"embed"`. `"file"` for printing out content as a file with default name `attachment_YYYY-MM-DD_HH-MM-SS.txt` (max size 100 000 characters ca 100kB). `"filename"` lets you define custom file name if `"file"` is used with max length of 64 characters, extension name remains `txt`. `"reply"`replies to a message, where `arg`is messageID. If replied message is in another channel, `sendMessage`channel must be also that channel. To "reply-ping", use `sendMessageNoEscape`.`"silent"` suppresses message's push and desktop notifications, `arg` is _bool_ true/false.Example in this section's [Snippets](#message-sections-snippets).| +|`complexMessageEdit` "allowed\_mentions" arg "content" arg "embed" arg "silent" arg| Special case for `editMessage` function - either if `complexMessage` is involved or works even with regular message. Has parameters "allowed\_mentions",`"content", "embed"` and `"silent"` that edit the message and work the same way as for `complexMessage`. Example in this section's [Snippets](#message-sections-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`.| +|`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](functions#message-sections-snippets).| +|`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](/reference/templates#reaction).| +|`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](#message-sections-snippets).| +|`editMessageNoEscape` channel messageID newMessageContent| Edits the message in channel and has same logic in escaping characters as `sendMessageNoEscape`.| +|`getMessage` channel messageID| Returns a [Message](/reference/templates#message) object. `channel` can be either its ID, name or nil for triggering channel.| +|`pinMessage` channel messageID| Pins a message by its ID in given channel. `channel` can be either its ID, name or nil for triggering channel. Can be called 5 times.| +|`publishMessage` channel messageID| Publishes a message by its ID in given announcement channel. `channel` can be either its ID, name or nil for triggering channel. Can be called once. This function will not work for leave or join messages.| +|`publishResponse`| Publishes the response when executed in an announcement channel. This function will not work for leave or join messages.| +|`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. This function will not work for leave messages.| +|`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, reply mentions or @here/@everyone).| +|`sendMessageNoEscapeRetID` channel message| Same as `sendMessageNoEscape`, but also returns messageID to assigned variable for later use.| +|`sendMessageRetID` channel message| Same as `sendMessage`, but also returns messageID to assigned variable for later use. Example in section's [Snippets](#message-sections-snippets).| +|`unpinMessage` channel messageID| Unpins the message by its ID in given channel. `channel` can be either its ID, name or nil for triggering channel. Can be called 5 times.| + +#### Message 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 (cslice "👍" "👎") "`❤️`" }} + {{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 "reply" .Message.ID + "content" "Who rules?" "embed" (cembed "description" "YAGPDB of course!" "color" 0x89aa00) "file" "Here we print + something nice - you all are doing awesome!" "filename" currentTime.Weekday)}}` +* To demonstrate usage of `complexMessageEdit` with `editMessage`.\ + `{{$mID := sendMessageRetID nil (complexMessage "content" "You know what is..." "silent" true "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" "Will delete this message in a sec, goodbye YAG!"`)}}{{deleteMessage nil $mID 3}}` + +### Miscellaneous + +{{% notice info %}} + +`if`, `range`, `try-catch`, `while`, `with` actions are all covered [here](/reference/templates#actions). + +{{% /notice %}} + +
FunctionDescription
**Function****Description**
`adjective`Returns a random adjective.
`cembed` "list of embed values"Function to generate embed inside custom command. [More in-depth here](/reference/custom-embeds).
`createTicket` author topicCreates a new ticket with the author and topic provided. Covered in its own section [here](/reference/templates#tickets).
`cslice`, `sdict`These functions are covered in their own section [here](/reference/templates#custom-types).
`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](/reference/custom-command-examples#dictionary-example). 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, .HasKey and .Set and they function the same way as `sdict` ones discussed [here](/reference/templates#templates.Sdict).
`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 CC. 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 commands with paginated embed returns, like `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.
`execTemplate` "template" dataExecutes the associated template, optionally with data. A more detailed treatment of this function can be found in the [Associated Templates](/reference/templates#associated-templates) section.
`getWarnings` userReturns a _slice_ of warnings of type _[[]*moderation.WarningModel](https://github.com/botlabs-gg/yagpdb/blob/master/moderation/models.go#L121)_ given to user argument which can be its ID or user object.
`hasPrefix` string prefix`hasPrefix` tests whether the given `string` begins with `prefix` and returns _bool_. Example > `{{hasPrefix "YAGPDB" "YAG"}}` returns `true`.
`hasSuffix` string suffixhasSuffix tests whether the given `string` ends with `suffix` and returns _bool_.Example > `{{hasSuffix "YAGPDB" "YAG"}}` returns `false`.
`humanizeThousands` argThis 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 valueReturns _bool_ true/false whether case-sensitive value is in list/_slice_. `{{ in (cslice "YAGPDB" "is cool") "yagpdb" }}` returns `false`.
`index` arg ...keysReturns the result by indexing its first argument with following arguments. Each indexed item must be a _map_, _slice_ or _array._ Indexed _string_ returns value in _uint8._ 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 valueSame 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` argReturns the integer length of its argument. arg can be an _array_, _slice_, _map_, or _string._`{{ len (cslice 1 2 3) }}`returns `3`.
`noun`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`. An example in [Custom Command Examples.](/reference/custom-command-examples.md#parseargs-example)
`sendTemplate` channel templateName (data)Function sends a formulated template to another channel and returns sent response's messageID.
Channel is like always either name, number or nil; and returns messageID. `data` is optional and is meant for additional data for the template.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!"}}.`

Template definitions are discussed [here](https://pkg.go.dev/text/template#hdr-Nested_template_definitions).
`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. This function will not work for leave messages.
`seq` start stopCreates 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.
`shuffle` listReturns a shuffled, randomized version of a list/_slice_.
`sleep` secondsPauses execution of template's action-structure inside custom command for max 60 seconds combined. Argument`seconds`is an integer (whole number). Example in [snippets](#message-sections-snippets).
`sort` slice (...args)Sorts a slice of same type values with optional arguments. These arguments are presented in an `sdict` as keys: `"reverse"` true/false and `"key"` with dictionary/map's key name as value.Example > `{{sort (cslice (sdict "name" "bob") (sdict "name" "alice") (sdict "name" "yagpdb")) (sdict "key" "name" "reverse" true)}}` would return `[map[name:yagpdb] map[name:bob] map[name:alice]]`.Sorting mixed types is not supported. Previous sorting options `"subslices"` and `"emptyslices"` have been removed.
Sort function is limited to 1/3 CC calls regular/premium.
`verb`Returns a random verb.
+ +### Role functions + +{{% notice info %}} + +In all role functions where userID is required as argument to target a user, it can also be full user object. + +{{% /notice %}} + +|**Function**| **Description**| +|-| -| +|`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](https://discord.com/developers/docs/topics/permissions#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 triggering user has the role with the specified ID (use the listroles command for a list of roles).| +|`hasRoleName` "rolename"| Returns true if the triggering 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.| +|`roleAbove` role1 role2| `roleAbove` compares two role objects e.g. `getRole`return and gives`true/false` value is `role1` positioned higher than `role2` or not.| +|`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/targeted 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/targeted user has the role with the specified name (case-insensitive).| + +### String manipulation + +{{% notice info %}} + +All regexp functions are limited to 10 different pattern calls per CC. + +{{% /notice %}} + +|**Function**| **Description**| +|-| -| +|`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](https://golang.org/pkg/fmt/#Sprint), [fmt.Sprintf](https://golang.org/pkg/fmt/#Sprintf) and [fmt.Sprintln](https://golang.org/pkg/fmt/#Sprintln). Formatting is also discussed [here](https://golang.org/pkg/fmt/#hdr-Printing). `printf` cheat sheet [here](https://yourbasic.org/golang/fmt-printf-reference-cheat-sheet/).

`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](functions.md#string-manipulation-sections-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]]`.| +|`reQuoteMeta` "string"| `reQuoteMeta` returns a string that escapes all regular expression metacharacters inside the argument text; the returned string is a regular expression matching the literal text. Example in [package documentation](https://pkg.go.dev/regexp#QuoteMeta).| +|`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).
Inside "string2" dollar-sign, $ with numeric name like $1 or ${1} are interpreted as referrals to the submatches in "regex" pattern, so for instance $1 represents the text of the first submatch. To insert a literal $ in the output, use $$.| +|`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.`| +|`sanitizeText` "string"| Detects accented and confusable characters in input and turns these characters to normal, ISO-Latin ones. `{{ sanitizeText "𝔭𝒶ỿ𝕡𝕒ℓ" }}`would return `paypal`.| +|`slice` arg integer (integer2)| Function's first argument must be of type _string_ or _slice_.Outputs the `arg` 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](#string-manipulation-sections-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](https://golang.org/pkg/text/template/#hdr-Functions) 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](functions.md#string-manipulation-sections-snippets).| +|`title` "string"| Returns the string with the first letter of each word capitalized.| +|`trimSpace` "string"| Returns the string with all leading and trailing white space removed.| +|`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"
There's also predefined template package function `urlquery` which is covered [here](https://pkg.go.dev/text/template#hdr-Functions).| + +{{% notice info %}} + +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". + +{{% /notice %}} + +{{% notice info %}} + +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)}}`` + +{{% /notice %}} + +#### String manipulation 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: {{println "Only" (index $re2 0) "YAGPDB and already" (index $re2 1) "servers captured."}}` + +### Time + +|**Function**| **Description**| +|-| -| +|`currentTime`| Gets the current time, value is of type _time.Time_ which can be used in a custom embed. More info [here](/commands/custom-commands.md#currenttime-template).| +|`formatTime` Time ("layout 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 when user was created. Layout argument is covered [here](https://pkg.go.dev/time#pkg-constants).| +|`humanizeDurationHours`| 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](functions.md#time-sections-snippets).| +|`humanizeDurationMinutes`| Same as `humanizeDurationHours`, this time duration is returned in minutes - e.g. `{{humanizeDurationMinutes 3500000000000}}` would return `58 minutes`.| +|`humanizeDurationSeconds`| Same as both humanize functions above, this time duration is returned in seconds - e.g. `{{humanizeDurationSeconds 3500000000000}}` would return `58 minutes and 20 seconds`.| +|`humanizeTimeSinceDays`| Returns time passed since given argument of type _time.Time_ in human readable format - e.g. `{{humanizeTimeSinceDays currentUserCreated}}.`| +|`loadLocation` "location"| Returns value of type _*time.Location_ which can be used further in other golang's [time](https://pkg.go.dev/time) functions, for example `{{currentTime.In (loadLocation "Asia/Kathmandu")}}` would return current time in Nepal.
`location` is of type _string_ and has to be in [ZONEINFO syntax](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).| +|`newDate` year month day hour minute second (timezone)| Returns type _time.Time_ object in UTC using given syntax (all required 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](#time-sections-snippets). `timezone` is an optional argument of type _string_ which uses golang's [LoadLocation](https://golang.org/pkg/time/#LoadLocation) function and [ZONEINFO syntax](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). For example: `{{newDate 2020 4 20 12 34 56 "Atlantic/Reykjavik"}}` would return that time in GMT+0.| +|`parseTime` value layout (location)| `parseTime` uses golang's [parseInLocation](https://pkg.go.dev/time#ParseInLocation) time function and returns value of type _time.Time_. Argument `value` is the time representation that needs to be parsed and has to be a string which matches and is formatted using `layout` argument. `layout` must be either a slice of strings or a single string and max number of layouts is 50. [Layout reference](https://pkg.go.dev/time#pkg-constants).
`location` is optional, defaulting to UTC; of type _string_ and has to be in [ZONEINFO syntax](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).

For example: `{{parseTime "July 18, 2016 at 12:38am" "January 2, 2006 at 3:04pm" "Asia/Kathmandu"}}` would return that time in +0545.| +|`snowflakeToTime` snowflake| Converts given [snowflake](https://discord.com/developers/docs/reference#snowflakes) to type _time.Time_ e.g. using bot's ID `{{snowflakeToTime .BotUser.ID}}` returns `2016-07-17 15:17:19 +0000 UTC`for YAGPDB.| +|`timestampToTime` arg| Converts UNIX timestamp to _time.Time_. Example: \{{timestampToTime 1420070400\}} would return same time as `.DiscordEpoch`.| +|`weekNumber` time| Returns the week number as _int_ of given argument `time` of type _time.Time_. `{{weekNumber currentTime}}` would return the week number of current time.| + +{{% notice info %}} + +Discord Timestamp Styles referenced +[here](https://discord.com/developers/docs/reference#message-formatting-timestamp-styles) can be done using `print` +function e.g. + +`{{print ""}}` for "Long Date/Time" formatting. + +{{% /notice %}} + +#### Time 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}}` + +### Type conversion + +
FunctionDescription
**Function****Description**
`json` value (flag)Traverses given `value` through MarshalJSON ([more here](https://golang.org/pkg/encoding/json/#Marshal)) 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. `flag` part is a _bool_ and if set as **true** (**false** is optional) returns the value indented through MarshalIndent.
`jsonToSdict` valueReturns `templates.SDict` from given JSON argument, e.g. `{{jsonToSdict `{"yagpdb":"is cool"}` }}` would print `map[yagpdb:is cool]`.
`structToSdict` structFunction 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. Example: `{{$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_.
`toDuration`Converts the argument, number or string to type _time.Duration_ - more duration related methods [here](https://pkg.go.dev/time#Duration). 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](#type-conversion-sections-snippets).
`toFloat`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_.
`toInt`Converts something into an integer of type _int_. Usage: `(toInt x)`. Function will return 0, if type can't be converted to _int._
`toInt64`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_.
`toString`Has alias `str`. Converts some other types into a _string_. Usage: `(toString x)`.
+ +#### Type conversion 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"}}` + +{{% notice style="tip" %}} + +**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.](https://golang.org/pkg/fmt/) Cheat sheet +[here](https://yourbasic.org/golang/fmt-printf-reference-cheat-sheet/). + +{{% /notice %}} + +### User + +|**Function**| **Description**| +|-| -| +|`currentUserAgeHuman`| The account age of the current user in more human readable format.| +|`currentUserAgeMinutes`| The account age of the current user in minutes.| +|`currentUserCreated`| Returns value of type _time.Time_ and shows when the current user was created.| +|\~`pastNicknames` userID offset| Same as `pastUsernames`.| +|\~`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}}` | +|`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](#user-sections-snippets). Previous limit of 5 to this function is no longer there.| + +#### User 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 in [templates section](/reference/templates#guild-server)). The parentheses surrounding them make `userArg` function return +`.User` as [.User object](/reference/templates#user) which is handled further by `.String` method (ref.`.User.String`), giving a result +like > `YAGPDB#8760`. diff --git a/content/reference/thinking.gif b/content/reference/thinking.gif new file mode 100644 index 0000000..8a7cd5c Binary files /dev/null and b/content/reference/thinking.gif differ diff --git a/content/reference/twitch_turnover1.gif b/content/reference/twitch_turnover1.gif new file mode 100644 index 0000000..4d7d7b7 Binary files /dev/null and b/content/reference/twitch_turnover1.gif differ diff --git a/content/reference/youtried.gif b/content/reference/youtried.gif new file mode 100644 index 0000000..3038e9e Binary files /dev/null and b/content/reference/youtried.gif differ