Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
watzon committed Dec 9, 2021
1 parent 8dfbf1d commit d219ac7
Show file tree
Hide file tree
Showing 29 changed files with 253 additions and 116 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

I will do my best to keep this updated with changes as they happen.

## 0.25.1
- Added `sender_type` method and `SenderType` enum to `Message`, allowing the user to easily figure out what type of user or channel sent the given message.
- Updated docs

## 0.25.0
- Removed `Container` class which was being used to maintain a global instance of `Client`.
- Added `finish_init` method to all models, allowing them to contain an instance of the `Client` that created them.

## 0.24.0
- Added full support for Bot API 5.4 and 5.5
- More, see [the official Bot API changelog](https://core.telegram.org/bots/api#december-7-2021) for a complete list of changes.

## 0.23.0

- Added full support for Bot API 5.1 - 5.3
Expand Down
32 changes: 17 additions & 15 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
* Usage
* [Getting Started](index.md)
* [FAQ](usage/faq.md)
* [Home](index.md)
* [Usage](usage/index.md)
* [Getting Started](usage/getting_started.md)
* Features
* [Handlers](features/handlers.md)
* [Async](features/async.md)
* [Stage](features/stage.md)
* [Formatting](features/format.md)
* [Background Jobs](features/background_jobs.md)
* [Emoji Support](features/emoji_support.md)
* [Kemal Middleware](features/kemal_middleware.md)
* [Multilevel Menus](features/multilevel_menus.md)
* [Paginated Keyboards](features/paginated_keyboards.md)
* [TDLight](features/tdlight.md)
* Resources
* [FAQ](resources/faq.md)
* [Asking Questions](http://www.catb.org/~esr/faqs/smart-questions.html)
* [Changelog](changelog.md)
* Features
* [Handlers](usage/features/handlers.md)
* [Async](usage/features/async.md)
* [Stage](usage/features/stage.md)
* [Formatting](usage/features/format.md)
* [Background Jobs](usage/features/background_jobs.md)
* [Emoji Support](usage/features/emoji_support.md)
* [Kemal Middleware](usage/features/kemal_middleware.md)
* [Multilevel Menus](usage/features/multilevel_menus.md)
* [Paginated Keyboards](usage/features/paginated_keyboards.md)
* [TDLight](usage/features/tdlight.md)
* API Reference
* [Changelog](changelog.md)
* [Tourmaline](api_reference/Tourmaline/)
* [String](api_reference/String/)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion docs/usage/features/stage.md → docs/features/stage.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ end

## Steps

Stages are composed of steps which are created either with [Stage#on][Tourmaline::Stage#on(step,initial,&)] or with the [Step][Tourmaline::Stage::Step] annotation. If a step is tagged with `initial: true` it will be the first step in the series. If no step is tagged with `initial: true` you will have to manually transition to the step you want to start with by using [Stage#transition][Stage#transition(event)].
Stages are composed of steps which are created either with [Stage#on][Tourmaline::Stage#on(step,initial,&)] or with the [Step][Tourmaline::Stage::Step] annotation. If a step is tagged with `initial: true` it will be the first step in the series. If no step is tagged with `initial: true` you will have to manually transition to the step you want to start with by using [Stage#transition][Tourmaline::Stage#transition(event)].

!!! note
The main caviat, and it's a small one, is that you must either transition or exit from a step. If you don't your program will be stuck in a sort of limbo.
Expand Down
File renamed without changes.
134 changes: 64 additions & 70 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,71 @@
# Getting Started with Tourmaline
---
hide:
- navigation
- toc
---

So you want a Telegram bot, you like clean code and powerful frameworks, and you don't want to spend forever combing through complex documentation trying to figure out how to get started. Well you've come to the right place!
<style>
.md-content__button {
display: none;
}

Tourmaline is a Telegram bot framework for those that like things fast, efficient, and beautiful, which is why it's written in [Crystal](https://crystal-lang.org). But enough talk, this is a getting started guide, not a biography.
.md-content .logo {
width: 150px;
display: block;
margin: 0px auto 20px auto;
}

## Installation
.md-content .md-typeset h1,
.md-content .md-typeset h2 {
text-align: center;
margin: 0;
}

First be sure you have Crystal installed. This is a Crystal framework, and won't work otherwise. Unfortunately this means (for now) you Windows people are going to have to run this using WSL or a virtual machine.
.md-content .md-typeset h1 {
text-align: center;
margin-bottom: 0.12em;
}

Initialize your new Crystal project, for the purposes of this example we'll call our project `echo_bot`, but you can call yours whatever you like. To create the new project, run `crystal init app echo_bot` (replacing `echo_bot` with the name you'd like for your project).
.md-content .md-typeset .headerlink {
display: none;
}

Change directories into your new project (using `cd echo_bot` in our case here). If you're unfamiliar with Crystal I highly recommend checking out [the official docs](https://crystal-lang.org/reference/using_the_compiler/index.html#creating-a-crystal-project) to get an idea of the project structure, from now on it will be assumed that you understand the basics of a Crystal project.
.md-content .md-typeset .quickstart {
display: flex;
flex-direction: row;
}

Now to install Tourmaline. Open up `shard.yml` and add the following snippet:
.md-content .md-typeset .quickstart * {
flex: 1 1 0;
}

```yaml
dependencies:
tourmaline:
github: protoncr/tourmaline
```
Then in your terminal run `shards install`. Tourmaline should now be installed!

## Register Your Bot

All bot registration in Telegram is facilitated through [@BotFather](https://t.me/BotFather). Don't confuse _registration_ with _creation_ though. BotFather simply helps you tell Telegram about, and manage your bot. We still need to write some code for the bot to function.

Head on over to [t.me/BotFather](https://t.me/BotFather). Just in case this is your first time using a Telegram bot, let's go over some basics.

1. Bots are {++NOT++} users. The operate by a specific set of rules, most of which don't apply to standard user accounts.

2. Most bots are controlled by using {++bot commands++}. Officially, bot commands begin with a `/` and can contain any of the characters `[a-zA-Z_]`, additionally an `@` can be used to differentiate between bots in a group.

3. Bots cannot send messages to users that did not start a conversation with them first. This is a spam prevention measure.

4. By default bots will be in group privacy mode. This means that they will only be able to see messages that start with a bot command until this is turned off. This only applies to groups, in private they can see everything.
.md-content .md-typeset .quickstart .sample-one {
margin-right: 10px;
}

There are plenty of other things to know, but those are some of the more important ones. Now let's get on to using BotFather to register a bot!
.md-content .md-typeset .quickstart .sample-two {
margin-left: 10px;
}
</style>

First send the `/help` command. Whenever using a new bot it's always a good idea to see if they respond to this command, and what help is available. In our case we can see in the BotFather help that we can run `/newbot` to create (register) a new bot.
<img src="/images/logo.svg" class="logo">

The `/newbot` command will ask for a name. This will be the screen name of your bot, not the username, so feel free to call it whatever you want.
<h1>Tourmaline<h1>
<h2>Crystal Telegram Bot Framework</h2>

Next it will ask for a username. This must be unique, can only contain alphanumeric characters and underscores, and must be at least 5 characters long (3 and 4 character usernames are reserved for Telegram internal use).
<hr />

Once you've entered a valid username it will send you a message containing some instructions and an API token that looks something like this `123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ`. {++KEEP THIS SECRET!++} Anyone with access to this token will be able to control your bot, so don't share it.
<h2>Quickstart</h2>

Now if you send the `/mybots` command you should see your bot listed. Click it to enter the configuration menu for your bot. You can explore the options here if you wish, but we'll be moving on now.
<p style="text-align: center;">Tourmaline uses Crystal to communicate with the Telegram Bot API. Therefore to use Tourmaline you should be familiar with how Crystal works.</p>
<p style="text-align: center;">Once inside of a Crystal project, add Tourmaline to your <code>shard.yml</code> file and run <code>shards install</code> to install it.</p>
<p style="text-align: center;">For more information, see the <a href="usage/getting_started">getting started</a> page.</p>

## Example Project
<div class="quickstart">
<div class="sample-one">
<h3>Using Annotations</h3>

We're going to be making a really simple echo bot for this example, hence the project name `echo_bot`. Go head and open the generated file at `src/echo_bot.cr` and paste in the following code. Don't worry, we'll go over each line in detail afterwards.

!!! tip
This is exactly the same as the [echo bot](https://github.com/protoncr/tourmaline/blob/master/examples/echo_bot.cr) example in the source repo. If anything is broken be sure to check there for an updated code snippet.

```crystal linenums="1"
```crystal
require "tourmaline"
class EchoBot < Tourmaline::Client
Expand All @@ -68,37 +78,21 @@ end
bot = EchoBot.new(bot_token: "YOUR_API_TOKEN")
bot.poll
```

Be sure to replace `YOUR_API_TOKEN` with the token you received from BotFather. Now run the bot with `crystal run ./src/echo_bot.cr`. Now if you visit your bot in Telegram and run `/echo some message` it should relpy to you with the message you sent. Congrats! You just created your first bot.

Now let's break this code down line by line, starting from line 3 since the `require` should be obvious enough.

```crystal
class EchoBot < Tourmaline::Client
# ...
end
```

This is just defining our bot, `EchoBot`, as a sublcass of `Tourmaline::Client`. This gives us access to everything that `Tourmaline::Client` has, as well as allowing us to use the `Command` annotation which we'll take a look at next.

</div>
<div class="sample-two">
<h3>More Procedural</h3>
```crystal
@[Command("echo")]
def echo_command(ctx)
# ...
end
```

`@[Constant()]` is the annotation invokation syntax. Annotations, much like in languages like Python and Typescript, can be used to modify the annotated method, class, variable, etc.. In this case it registers the method as an event handler for commands specifically (see `Tourmaline::Handlers::CommandHandler`). The `ctx` property that gets passed into the command is an instance of `Tourmaline::Handlers::CommandHandler::Context`.
require "tourmaline"
include Tourmaline # To shorten things
```crystal
ctx.message.reply(ctx.text)
```
bot = Client.new(bot_token: "YOUR_API_TOKEN")
This is pretty simple. The [context][Tourmaline::Handlers::CommandHandler::Context] has a `message` property, which is the [message][Tourmaline::Message] that contains the command we're acting on. All we're doing is replying to the message with the text that was in the message (minus the bot command).
echo_handler = Handlers::CommandHandler.new("echo") do |ctx|
ctx.message.reply(ctx.text)
end
```crystal
bot = EchoBot.new(bot_token: "YOUR_API_TOKEN")
bot.add_event_handler(echo_handler)
bot.poll
```

Finally we create a new instance of our `EchoBot` with our API token, and start it in [polling mode][Tourmaline::Client::CoreMethods#poll(delete_webhook)].
</div>
</div>
4 changes: 2 additions & 2 deletions docs/usage/faq.md → docs/resources/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ If the answer to that question is "yes", as I hope it is, then there are a few t

Assuming you have the right token, be sure to check that your bot is actually running and that you have a working internet connection. Try running your bot with the `LOG_LEVEL` environement variable set to `DEBUG` and check the logs. When running in polling mode you should see a whole bunch of calls to `getUpdates`.

If that didn't work, you may need to check if group privacy mode is turned on. Go to BotFather, send the command `/mybots`, select your bot, go to `Bot Settings > Group Privacy > Turn off`. This should only be necessary if your bot is running in groups and you're using non-standard command prefixes, or other handlers like the [HearsHandler][Tourmaline::HearsHandler].
If that didn't work, you may need to check if group privacy mode is turned on. Go to BotFather, send the command `/mybots`, select your bot, go to `Bot Settings > Group Privacy > Turn off`. This should only be necessary if your bot is running in groups and you're using non-standard command prefixes, or other handlers like the [HearsHandler][Tourmaline::Handlers::HearsHandler].

Lastly be sure to check the commands you're trying to send. The standard [CommandHandler][Tourmaline::CommandHandler] will only respond to commands with the `/` prefix. Also be sure to remember that command names should be unprefixed. For example:
Lastly be sure to check the commands you're trying to send. The standard [CommandHandler][Tourmaline::Handlers::CommandHandler] will only respond to commands with the `/` prefix. Also be sure to remember that command names should be unprefixed. For example:

```diff
- @[Command("/echo")]
Expand Down
27 changes: 23 additions & 4 deletions docs/stylesheets/extra.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
[data-md-color-scheme="default"] {
--md-heading-bg-color: #F8F8F8;
--md-including-bg-color: #E2E2E2;
}

[data-md-color-scheme="slate"] {
--md-heading-bg-color: #3E3E46;
--md-including-bg-color: #C4C4C4;
}

.doc-method:target::before {
display: block;
margin-top: -4.1rem;
Expand All @@ -6,17 +16,26 @@
}

.doc-type > .doc-heading {
font-size: 1.3em !important;
font-size: 1.1em !important;
}

.doc-type > .doc-heading > code {
font-size: 1.3em !important;
font-size: 1.1em !important;
vertical-align: baseline !important;
}

.doc-method .doc-heading {
.doc-object .doc-heading {
padding: 8px !important;
background-color: #F8F8F8 !important;
background-color: var(--md-heading-bg-color) !important;
}

.doc-object .doc-contents {
font-size: 0.9em;
}

.doc-contents > .doc-children > h2 + code {
font-size: 1.2em;
background-color: var(--md-including-bg-color) !important;
}

.doc-source-link {
Expand Down
51 changes: 51 additions & 0 deletions docs/usage/getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Getting Started with Tourmaline

Time to create your first bot with Tourmaline!

## Installing Tourmaline

This guide assumes that you have Crystal installed and are at least semi-familiar with the syntax. The first thing we are going to need is a fresh Crystal project, so go ahead and run `crystal init app your_bot_name`, making sure to replace "your_bot_name" with whatever you want to call your bot. I'm going to use the famous echo_bot example. Once it's finished, `cd` into the project directory.

Now, open `shard.yml` and add the following lines anywhere in the file (probably at the end):

```yaml linenums="1"
dependencies:
tourmaline:
github: protoncr/tourmaline
```
Save the file, and run `shards install`. That's it! Tourmaline is now installed.

## Creating Your Bot

Now it's time to write some code. Open `src/echo_bot.cr` or whatever file was generated for you, and paste in the following code. The code is annotated so you can understand what's going on every step of the way.

```crystal linenums="1"
require "tourmaline" # (1)
class EchoBot < Tourmaline::Client # (2)
@[Command("echo")] # (3)
def echo_command(ctx)
ctx.message.reply(ctx.text) # (4)
end
end
bot = EchoBot.new(bot_token: ENV["API_KEY"]) # (5)
bot.poll # (6)
```

1. First we have to import Tourmaline into our code. In Crystal this is done with the `require` statement.
2. Next we extend `Tourmaline::Client` and use it to create our own class.
3. In this example we're going to go with the annotation approach to creating bots. Annotations use the `@[Annotation(params)]` syntax and can decorate classes and methods. In this case, we're annotating the `echo_command` method with the `Command` annotation, which turns the method into a command handler.
4. Within the `echo_command` method we're just going to echo whatever the user said back to them, so we use the `reply` method which exists on the `Message` object and send `ctx.text` (the user's message text minus the command) back to them.
5. Almost done. Now we need to create a new instance of our `EchoBot`. Since we extended `Tourmaline::Client` and didn't provide a custom initializer our bot takes the same arguments, so we can provide it with our API Key as an environment variable.
6. The last step is to start our bot. We can do that by "long polling" using the `poll` method.

And that's really all their is to it. Now we can run our code!

```sh
export API_KEY=YOUR_BOT_API_TOKEN
crystal run ./src/echo_bot.cr
```

You should be greeted with a log message in your console telling you that your bot is running, and if you visit your bot on Telegram and run the `/echo` command with some text (eg `/echo hello world`), you should receive a message in reply.
Loading

0 comments on commit d219ac7

Please sign in to comment.