Skip to content

Commit

Permalink
Merge pull request #41 from botlabs-gg/add-learn
Browse files Browse the repository at this point in the history
learn: add learning resources
  • Loading branch information
jo3-l authored Jul 22, 2024
2 parents 2e8457d + 0587c3e commit df4ea5d
Show file tree
Hide file tree
Showing 21 changed files with 1,813 additions and 9 deletions.
2 changes: 1 addition & 1 deletion config/_default/hugo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
title = "YAGPDB Docs"
title = "YAGPDB Help Center"
copyright = "Copyright (c) 2020-2024 YAGPDB documentation contributors"
# To avoid weirdness with images and whatnot, this is empty and instead set at build time.
# See also https://discourse.gohugo.io/t/solved-what-should-be-used-for-the-value-of-site-baseurl/5896
Expand Down
6 changes: 3 additions & 3 deletions config/_default/menus/menus.en.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[[main]]
name = "Introduction"
name = "Documentation"
url = "/docs/welcome/introduction/"
weight = 10

[[main]]
name = "Getting Started"
url = "/docs/welcome/getting-started/"
name = "Learning Resources"
url = "/learn/welcome/introduction/"
weight = 15

[[social]]
Expand Down
4 changes: 2 additions & 2 deletions config/_default/params.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ mainSections = ["docs"]
bootstrapJavascript = false # false (default) or true

# Nav
sectionNav = ["docs"] # ["docs"] (default) or list of sections (e.g. ["docs", "guides"])
sectionNav = ["docs", "learn"] # ["docs"] (default) or list of sections (e.g. ["docs", "guides"])
toTopButton = false # false (default) or true
breadcrumbTrail = false # false (default) or true
headlineHash = true # true (default) or false
Expand Down Expand Up @@ -85,7 +85,7 @@ mainSections = ["docs"]
[hyas_images]
[hyas_images.defaults]
decoding = "async" # sync, async, or auto (default)
fetchpriority = "auto" # high, low, or auto (default)
fetchpriority = "auto" # high, low, or auto (default)
loading = "lazy" # eager or lazy (default)
widths = [480, 576, 768, 1025, 1200, 1440] # [640, 768, 1024, 1366, 1600, 1920] for example
sizes = "auto" # 100vw (default), 75vw, or auto for example
Expand Down
21 changes: 21 additions & 0 deletions content/learn/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 caubert

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
4 changes: 4 additions & 0 deletions content/learn/beginner/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
+++
title = "Beginner"
weight = 200
+++
204 changes: 204 additions & 0 deletions content/learn/beginner/control-flow-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
+++
title = "Control Flow 1"
weight = 230
+++

Until now, we have just written some linear code that executes from top to bottom. However, in real-world applications,
we often need to execute different code depending on certain states of our program. This is where _control flow_ comes
into play.

You already have an intuitive understanding of control flow. For instance, when you cross the street, you look left and
right to see if any cars are coming. If there are no cars, you cross the street. Otherwise ("else"), you wait until the
incoming cars have passed, then check again. This is a simple example of a decision-making process.

## If Statements

The most basic form of control flow is the `if` statement. An `if` statement checks a condition and executes a block of
code if the condition is true. If the condition is false, the block of code is skipped.

```go
{{ if eq 5 5 }}
{{/* It is conventional, though not required, to consistently indent inner blocks of code with spaces or tabs. */}}
Five is equal to five!
{{ end }}
```

We use the `eq` comparison function to check whether its given arguments are equal; we will enumerate all comparison
functions in a [later section](#comparison-actions) on this page.

We can expand this to execute a different block of code if the condition is false by using an `else` block:

```go
{{ if eq 5 3 }}
Five is equal to three!
{{ else }}
Five is not equal to three!
{{ end }}
```

This can be further expanded to check multiple conditions using `else if`, which are checked sequentially until one of
them is true:

```go
{{ if eq 5 3 }}
Five is equal to three!
{{ else if eq 5 5 }}
Five is equal to five!
{{ else }}
Five is not equal to three or five!
{{ end }}
```

{{< callout context="note" title="Note" icon="outline/info-circle" >}}

The condition of an `if` statement need not be a boolean: in general, an `if` statement will trigger if the condition is
_truthy_: that is, not empty or zero. (Conversely, empty and zero values are _falsy_.)

For example, consider how you would check whether a string `$s` is empty. One way is to explicitly compare the length of
the string to zero using `if gt (len $s) 0`, but since empty strings are falsy, the more idiomatic solution is to simply
write `if $s`, like so:

```go
{{ $s := "" }}
{{ if $s }}
not empty
{{ else }}
empty
{{ end }}
```

{{< /callout >}}

#### Guard Clauses

As your code grows, you may find yourself nesting `if` statements inside each other. This can lead to code that is hard
to read and understand. One way to avoid this is to use _guard clauses_. A guard clause is an `if` statement that checks
for a condition and returns early via the `{{ return }}` action. Therefore, the condition we checked in the earlier
`if`/`else` construct must be negated.

Rewriting the second example to use these guard clauses yields the following code:

```go
{{ if ne 5 3 }}
Five is not equal to three!
{{ return }}
{{ end }}

Five is equal to three!
```

Although this example may be a bit contrived, guard clauses can help you avoid deeply nested code and make your code
easier to read, especially when you come back to it at a later date.

## Comparison Actions

In programming, we can make decisions by comparing values to each other. We can compare numbers, strings, and other data
types. The result of a comparison is a _boolean_ value, which is either `true` or `false`. Normally, comparisons are
binary operators; however, in custom commands, we use functions to compare values.

{{< callout context="caution" title="Comparing across Types" icon="outline/alert-triangle" >}}

Just like you cannot quite compare apples and oranges, you cannot compare values of different types. For instance, you
cannot compare a number to a string. The bot will throw an error if you try to do so, you will have to convert either of
them to the other type first.

{{< /callout >}}

We provide the following comparison functions in custom commands:

- `eq` (equals `==`)
- `ne` (not equals `!=`)
- `lt` (less than `<`)
- `le` (less than or equal to `<=`)
- `gt` (greater than `>`)
- `ge` (greater than or equal to `>=`)

These functions can only compare basic data types as introduced in [Data Types 1](/learn/beginner/datatypes-1). Strings
are compared on a byte-by-byte basis. Please refer to the [functions documentation](/docs/reference/templates/functions)
for the syntax of these functions.

## Blocks And Scope

In [Data Types 1](/learn/beginner/datatypes-1), we introduced the concept of variables. Variables are used to store
values and give them a name. In programming, variables have a _scope_, which defines where in the code the variable can
be accessed. Think of each scope as a "container" for things inside it.

Often, you will want to have a variable available across multiple scopes. In custom commands, the variable is accessible
in the scope in which it was defined, as well as all nested scopes within. Let us assume that we want to assign a
coolness value, which should be true if the user's name is "alice". We can achieve this by defining a variable in
the outer scope and re-assigning, using the `=` operator, its value in the inner scope:

```go
{{ $isCool := false }}

{{ if eq .User.Username "alice" }}
{{ $isCool = true }}
{{ end }}

Are you cool? {{ $isCool }}
```

It is considered good practice to define variables in the smallest scope possible. This makes your code easier to read
and understand, as you do not have to search through the entire codebase to find where a variable was defined.

{{< callout context="caution" title="Definition and Reassignment" icon="outline/alert-triangle" >}}

In custom commands, you use `:=` to define a variable, and `=` to reassign a variable. The bot will not throw an error
if you try to re-define a variable using `:=`. Rather, it will define a new variable, effectively overwriting the
existing variable, but only within that scope---the existing variable is untouched in the outer scope.

{{< /callout >}}

{{< callout context="danger" title="Unexpected EOF" icon="outline/alert-octagon" >}}

The following error appears when you are missing an `{{ end }}` action.

![Responses: template: :XX: unexpected EOF](unexpected-eof.png)

Each control structure must have a corresponding `{{ end }}` action. If you forget to do so, YAGPDB will not know where
your control structure terminates and hence issues the above error.

To fix this error, examine each `{{ if ... }}` in your program and verify that each has a matching `{{ end }}`. (The
same applies to other control structures introduced in future chapters, such as `{{ range ... }}`.)

If you are familiar with C-family programming languages, this error is analogous to forgetting the closing `}` of a
block of code.

{{< /callout >}}

## Exercises

1. Write a Custom Command to determine if the number stored in a variable `$a` is even or odd and print `Number is Even`
or `Number is Odd` depending on the case. Verify the output for the following values of `$a`: 1, 9, 0, 10021, -5.

2. Predict the output of the following code snippets. If there is an error in the snippet, what is the cause of the
error, and how can it be fixed? Also note down potential improvements to the code that make it easier to follow.

```go
{{ $num1 := 10 }}
{{ if $num1 }}
{{ num1 := 6 }}
{{ $num1 }}
{{ end }}
{{ if not (mod $num1 3) }}
{{ $num1 }}
{{ end }}
{{ $num1 }}
```

```go
{{ $name := "John" }}
{{ if eq $name "John" }}
{{ $familyName := "Walters" }}
{{ end }}
My name is: {{ $name }} {{ $familyName }}
```

```go
{{ $mood := "happy" }}
{{ if gt $mood "Sad" }}
Be {{ $mood }}!
{{ else }}
Do not be {{ $mood }}!
{{ end }}
```
128 changes: 128 additions & 0 deletions content/learn/beginner/datatypes-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
+++
title = "Data Types 1"
weight = 220
+++

In this chapter, we will overview how to store data in variables and the basic data types available, which include
[strings](#strings), [numbers](#numbers), and [booleans](#booleans).

## Variables

Before we go over data types, we will cover how to store data and refer to it later. We can do this by using variables.
A variable is a way to store a value and give it a name, to which you can refer back to later.

In Custom Commands, you define a variable by starting with a `$`, its name, and the assignment operator `:=`. To
illustrate this, consider the following example:

```go
{{ $name := "Alice" }}
{{ $age := 42 }}
{{ $isCool := true }}

{{ $name }}, aged {{ $age }}, is cool: {{ $isCool }}
```

Later on, we may wish to reassign a new value to a variable. We can do this by using the re-assignment operator `=`.
When and why this becomes necessary will be discussed in a later chapter.

{{< callout context="tip" title="Tip" icon="outline/rocket" >}}

When debugging your code, you might have to figure out the type of a certain variable. You can do this by using the
[`printf` function](/docs/reference/templates/functions/#string-manipulation) with the `%T` format verb, like so:

```go
{{ $name := "Alice" }}
{{ printf "%T" $name }}
```

{{< /callout >}}

## Data Types

If you're completely new to programming, you might not know what a data type is. You can think of a data type as a way
to distinguish between different kinds of things. As a real-life analogy, you can separate food into several categories,
such as fruits, vegetables, and meat. Each category is in that sense its own data type.

In programming, we have similar categories, such as numbers, strings, and booleans (true / false values).
Each of these categories has its own set of operations that can be performed on them. For instance, you can add two
numbers together, but you cannot add two strings together (at least not in the way you might expect).


### Strings

A string is a sequence of zero or more characters. You can generally think of it as a word or a sentence.
In the bot's template actions, you can create a string by enclosing some text in double quotes (`"`). For instance,
`"Hello, world!"` is a string. We call those *double-quoted strings*.

Now, here we might run into a problem quite quickly: what if we want to include a double quote in our string? We can't
just write `"Peter said "Hello, world!""`, as the bot would think the string ends at the quotes before `Hello` and not
know that we want them included in the string. To solve this, we must escape the double quote by adding a backslash
(`\`) in front of it. This tells the bot that the double quote is not the end of the string. In other words,
`"Peter said \"Hello, world!\""` would yield the expected result.

To insert a newline (you would press `Enter` on your keyboard), you can use the escape sequence `\n`. For example the
string `"Hello\nWorld!"` would result in the following output:

```txt
Hello
World!
```

For a full list of escape sequences, you can refer to the [Go documentation](https://golang.org/ref/spec#Rune_literals).
Please note that not all escape sequences are supported by Discord.

#### Raw String Literals

It should become relatively clear that a lot of new lines and other special characters can make a quoted string quite
hard to read. To make this easier, you can use backticks (`` ` ``) to create a *raw string literal*. A raw string
literal does not attempt to interpret its contents in any way, and will simply contain the text between the opening ``
` `` and closing `` ` `` unmodified---we cannot even escape a backtick to include one in the string, but we will later
cover functions that solve this special case.

```txt
`This is my
cool multiline string!
Look at all the new lines!`
```

### Numbers

Numeric values can be represented in two ways, using integers (whole numbers) and floating-point numbers (numbers with a
decimal point). In the bot's template actions, you can create an integer by simply writing a whole number, such as `5`.
For floating-point numbers, you can add a decimal point, like so: `5.0`.

#### Integers

In the bot's template actions, integers are represented as 64-bit signed integers. This means that you can store numbers
from `-9223372036854775808` to `9223372036854775807`. If you try to store a number outside this range, the bot will
return an error.

The bot accepts several notations for integers:

1. As a base-10 number, such as `42`. This will mean what you think, the number forty-two.
2. As a base-16 number, such as `0x2A`. This is the [hexadecimal representation][hex] of the number forty-two.
3. As a base-8 number, such as `0o52`. This is the [octal representation][oct] of the number forty-two.
4. As a base-2 number, such as `0b101010`. This is the [binary representation][bin] of the number forty-two.

[hex]: https://en.wikipedia.org/wiki/Hexadecimal
[oct]: https://en.wikipedia.org/wiki/Octal
[bin]: https://en.wikipedia.org/wiki/Binary_number

#### Floating-Point Numbers

We represent floating-point numbers as 64-bit IEEE-754 floating-point numbers. This means that you can store numbers
with a precision of about 15 decimal places. If you try to store a number with more precision, the bot will round it to
the nearest representable number.

There are a lot of ways to define a floating-point number, but the most common way is to use the decimal point, such as
`3.14`. For a full list of ways to define a floating-point number, you can refer to the
[Go documentation](https://golang.org/ref/spec#Floating-point_literals).

### Booleans

A boolean is a data type that can only have one of two values: `true` or `false`. Booleans are used to represent the
truth value of an expression. For instance, `5 > 3` would evaluate to `true`, while `5 < 3` would evaluate to `false`.

You can think of it as a light switch: it can either be on (`true`) or off (`false`). Booleans are often used in
conditional statements, such as `if` statements, to determine which branch of the code should be executed.
Binary file added content/learn/beginner/hello_world.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit df4ea5d

Please sign in to comment.