diff --git a/.cache/plugin/social/manifest.json b/.cache/plugin/social/manifest.json
new file mode 100644
index 000000000..9e26dfeeb
--- /dev/null
+++ b/.cache/plugin/social/manifest.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/add-environment-variables.png b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/add-environment-variables.png
new file mode 100644
index 000000000..e4aae9be9
Binary files /dev/null and b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/add-environment-variables.png differ
diff --git a/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/telegram-bot-cover.jpg b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/telegram-bot-cover.jpg
new file mode 100644
index 000000000..0f6e9393e
Binary files /dev/null and b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/telegram-bot-cover.jpg differ
diff --git a/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/update-git-branch.png b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/update-git-branch.png
new file mode 100644
index 000000000..f9c464540
Binary files /dev/null and b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/update-git-branch.png differ
diff --git a/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/working-telegram-bot.png b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/working-telegram-bot.png
new file mode 100644
index 000000000..7a8dbe6a9
Binary files /dev/null and b/docs/assets/tutorials/create-and-host-go-ai-telegram-bot/working-telegram-bot.png differ
diff --git a/docs/tutorials/create-and-host-go-ai-telegram-bot.md b/docs/tutorials/create-and-host-go-ai-telegram-bot.md
new file mode 100644
index 000000000..772c2b6f1
--- /dev/null
+++ b/docs/tutorials/create-and-host-go-ai-telegram-bot.md
@@ -0,0 +1,502 @@
+---
+title: Create a Go Telegram Bot
+description: Learn how to build a Telegram bot that messages you with AI backed responses.
+image: assets/tutorials/create-and-host-go-ai-telegram-bot/telegram-bot-cover.jpg
+---
+
+
+
+*This guide uses Go. You can find the NodeJS version [here](./create-nodejs-telegram-bot.md), or the Python version [here](./create-and-host-telegram-bot.md)*
+
+# How to Create and Host an AI backed Telegram Bot on Code Capsules
+
+In this tutorial we'll cover creating a [Telegram bot]() from scratch using [Go]() and hosting it in a [New Capsule](https://app.codecapsules.io/new/capsule?capsuleType=backend). We will give it the basic functionality of checking the current air temperature of a specified city. Then we will extend to provide AI functionality using Google AI Studio.
+
+### Getting started
+For this project we will use the following stack:
+- Our frontend will be our Telegram bot that will accept specific commands from our users.
+- Our backend will be an application written in Go and supported by various third-party APIs.
+- The rest of the stack will be handled by our backend Capsule.
+
+#### Create a telegram account
+Go to [Telegram](https://telegram.org/), download it for your desired platform and create an account.
+
+As we are doing development, we recommend to download the version that will run on your development environment. But, it will work fine on your phone.
+
+#### Installing Go
+Start the project by creating a new directory for our local development.
+
+`mkdir go-bot && cd go-bot`
+
+Install Go.
+
+If you have Homebrew configured, you can run the following to install it in a single step:
+
+`brew install go`
+
+Alternatively, You can find the appropriate instructions [here](https://go.dev/doc/install) for your OS.
+
+Ensure that it is installed correctly by checking that the version is displayed when running `go version`.
+
+#### Third-party APIs
+We will make use of two free-to-use APIs.
+
+Weatherstack provides weather data with 100 free requests per month.
+
+Obtaining the Weatherstack API key is similar:
+
+- Create a free account [here](https://weatherstack.com/product).
+- Log in and take note of the API key presented in the control panel.
+
+Google AI Studio provides access to Generative AI models with a free tier for API calls.
+
+Setting up Google AI Studio:
+
+- Go to [Google AI Studio](https://aistudio.google.com/).
+- Sign in with your Google account (create one if you don't already have one).
+- Click on "Get API key" and select "Create API key in new project".
+- Copy the generated API key and store it securely.
+
+#### Code Capsules
+Create a Code Capsules account [here](https://codecapsules.io/) if you don't already have one.
+
+We will host our code on GitHub, so make sure you have a [GitHub account](https://github.com/signup) and are familiar with [Git](https://git-scm.com/). You can find a guide to getting started with Git [here](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control).
+
+### Registering a Bot Account and Talking to the BotFather
+When you've signed in to Telegram, search for "BotFather" (a bot for managing all other Telegram bots) and start a new chat with it. Follow the steps below to register a new bot with the BotFather:
+
+1. Type `/start` and press send.
+2. Type `/newbot` and press send.
+3. Choose a name for your bot.
+4. Choose a username for your bot that ends in `bot`.
+
+The BotFather will respond with a message containing an access token for your newly created bot. This access token will allow our application to access the Telegram API and tell our bot what to do when receiving different messages from users.
+
+To confirm that your bot was created successfully, search for the bot's username. You should be able to see it and start a conversation with it, although it won't respond as we haven't written the bot's logic yet.
+
+### Installing the Required Packages
+
+Our bot requires four third-party Go packages to handle Telegram communication, HTTP requests, environment variables, and AI integration. We'll install these using Go's package manager.
+
+Before installing packages, initialize a Go module for your project by running the following command in your `go-bot` directory:
+
+```bash
+go mod init
+```
+
+Now, install each of the required packages:
+
+```bash
+go get github.com/go-telegram-bot-api/telegram-bot-api/v5
+
+go get github.com/go-resty/resty/v2
+
+go get github.com/joho/godotenv
+
+go get google.golang.org/genai
+```
+
+After running all four commands, Go will automatically download and install each package, and your `go.mod` file will be updated with the dependencies.
+
+### Setting up the Environment
+
+Create a `.env` file in your `go-bot` directory to store your API keys securely.
+
+```bash
+touch .env
+```
+
+Add the following environment variables Replace the placeholder values with your actual API keys:
+
+```
+BOT_TOKEN=YOUR_TELEGRAM_BOT_TOKEN
+WEATHER_API_KEY=YOUR_WEATHERSTACK_API_KEY
+GEMINI_API_KEY=YOUR_GOOGLE_GEMINI_API_KEY
+```
+
+The godotenv package will automatically load these variables when the bot starts, making them available through `os.Getenv()`.
+
+### Creating the Bot
+
+Create a new file `bot.go`. This will contain all the code for our bot's backend.
+
+Go requires a package and a function named `main` in order to execute. Specify that the new bot.go file is in our main package.
+
+```go
+package main
+```
+
+Now we can add imports for all our third-party packages. We will also need the functionality in some of the default packages that are shipped with Go.
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+
+ "github.com/go-resty/resty/v2"
+ tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
+ "github.com/joho/godotenv"
+ "google.golang.org/genai"
+)
+```
+Before we build out the `main()` function, which will control most of the logic to handle the bot commands, we will build two helper functions that will make the calls to our third-party APIs.
+
+The first helper function will fetch temperature data from the Weatherstack API.
+
+First, we define a struct that maps the JSON response from Weatherstack:
+
+```go
+type TemperatureResponse struct {
+ Current struct {
+ Temperature int `json:"temperature"`
+ } `json:"current"`
+ Location struct {
+ Name string `json:"name"`
+ Country string `json:"country"`
+ } `json:"location"`
+}
+```
+
+This struct tells Go how to parse the API's JSON response, matching the fields we need.
+
+Now we'll create the `getTemperature` function. This function takes a city name, queries the Weatherstack API.
+
+```go
+func getTemperature(city string) (string, error) {
+ apiKey := os.Getenv("WEATHER_API_KEY")
+ if apiKey == "" {
+ return "", fmt.Errorf("WEATHER_API_KEY environment variable not set")
+ }
+
+ client := resty.New()
+
+ url := fmt.Sprintf("http://api.weatherstack.com/current?access_key=%s&query=%s", apiKey, city)
+ resp, err := client.R().SetResult(&TemperatureResponse{}).Get(url)
+ if err != nil {
+ return "", err
+ }
+
+ if resp.StatusCode() != 200 {
+ return "", fmt.Errorf("API returned status %d", resp.StatusCode())
+ }
+
+ result := resp.Result().(*TemperatureResponse)
+
+ if result.Location.Name == "" {
+ return "", fmt.Errorf("could not find location data in API response")
+ }
+
+ return fmt.Sprintf("Temperature in %s, %s: %d°C", result.Location.Name, result.Location.Country, result.Current.Temperature), nil
+}
+```
+
+The function starts by retrieving the API key from our environment variables. We create a new Resty client to make HTTP requests, then build the API URL with our key and the requested city. The `SetResult()` method tells Resty to parse the response into our `TemperatureResponse` struct. We can then check that the request succeeded, verify the location data exists, and finally return a formatted string with the temperature information.
+
+For a more interesting example, the second helper function will send user questions to Google's Gemini API and return responses in the style of Goku from Dragon Ball Z.
+
+```go
+func askGoku(question string) (string, error) {
+
+ const prompt = "You are Goku from DragonballZ. Give a very brief reply with no fluff. Always speak in the style of Goku."
+```
+To get the LLM to respond in the style of Goku, we define a [System Prompt](https://www.promptlayer.com/glossary/system-prompt) that we tack on to the end of the user's message so it will always be the final instruction to the LLM.
+
+Then, all we have to do is send our question up to our Google AI Studio API instance and return the response to the user.
+```go
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ if apiKey == "" {
+ return "", fmt.Errorf("GEMINI_API_KEY environment variable not set")
+ }
+
+ ctx := context.Background()
+
+ client, clientErr := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if clientErr != nil {
+ return "", fmt.Errorf("failed to create Gemini client: %v", clientErr)
+ }
+
+ result, err := client.Models.GenerateContent(
+ ctx,
+ "gemini-2.5-flash",
+ genai.Text(question+" "+prompt), // add the system prompt
+ nil,
+ )
+ if err != nil {
+ return "", fmt.Errorf("failed to generate content: %v", err)
+ }
+
+ if result == nil {
+ return "", fmt.Errorf("received nil response from Gemini API")
+ }
+
+ if result.Text() == "" {
+ return "", fmt.Errorf("received empty response from Gemini API")
+ }
+
+ response := result.Text()
+ if len(response) > 4096 {
+ response = response[:4093] + "..."
+ }
+
+ return response, nil
+}
+```
+
+ For some basic error handling, we validate that we received a non-empty response and then check if it exceeds Telegram's message length limit (4096 characters).
+
+We can now begin to build our `main()` function.
+
+We need our code to be aware of the environment variables that we set up in our .env file. We're using this file for convenience so that we can run the bot locally. Once our code is deployed, we will configure the environment variables on our server instead.
+
+```go
+func main() {
+
+ if err := godotenv.Load(); err != nil {
+ log.Printf("Warning: Could not load .env file: %v", err)
+ }
+```
+
+Now that the variables are loaded into our environment, we can access them with the `os.Getenv()` function. We use this to get our `BOT_TOKEN`, and then we can create a new instance of the Telegram BotAPI.
+
+
+```go
+token := os.Getenv("BOT_TOKEN")
+ if token == "" {
+ log.Fatal("BOT_TOKEN environment variable not set")
+ }
+
+ bot, err := tgbotapi.NewBotAPI(token)
+ if err != nil {
+ log.Panic(err)
+ }
+```
+
+The next step is to create a way for our backend to be ready to respond whenever a user sends a command. Luckily, the Telegram Bot API package has several ready solutions for us. Using polling is the simplest approach, it involves our backend code constantly querying the bot API checking for new messages.
+
+```go
+u := tgbotapi.NewUpdate(0)
+u.Timeout = 60
+
+// Get the updates channel from the bot
+updates := bot.GetUpdatesChan(u)
+```
+
+The above code snippet sets this up. It uses the `GetUpdatesChan(u)` function to launch a background [goroutine](https://go.dev/tour/concurrency/1) that will poll the Telegram API for any new message requests. As soon as a message comes in from a user it will be assigned to the updates variable and the code will continue. This is not the most efficient approach from a bandwidth perspective as it requires constant communication from our backend code with the server. The alternative is to use Webhooks which we will discuss a bit later in this guide.
+
+Now that we have our polling, we can set up the main loop of our program that will process the user commands when they are available.
+
+```go
+for update := range updates {
+```
+
+Go handles the above code quite efficiently as the loop will sleep until a value is available in the updates channel; so it does not use constant CPU cycles.
+
+Next, we add a check to skip any kind of update we receive that doesn't contain an actual message string. Once we are sure there is a message we can extract the `Message` object.
+
+```go
+if update.Message == nil {
+ continue
+}
+
+msg := update.Message
+
+```
+Now that we know we have a message from the user, we can add the logic for the commands that we want to support. To keep the code easier to follow, we will keep all the logic in the main class. If you are coding a more complex bot, with support for a large number of commands, you would probably want to consider improving the extendibility of your code with some Object Oriented principles. For example, this code would be suited to the [Factory Pattern](https://refactoring.guru/design-patterns/factory-method) which could provide some layers of abstraction that could make adding new commands more streamlined.
+
+First, we will look at the temperature command `/temperature [city]`. This will make a call to the Weatherstack API, which we made an account for earlier, to get the live temperature for the provided city.
+
+```go
+if strings.HasPrefix(msg.Text, "/temperature") {
+ city := "Cape+Town"
+ parts := strings.Fields(msg.Text)
+ if len(parts) > 1 {
+ city = strings.Join(parts[1:], "+")
+ }
+```
+
+We extract the actual text string from the `Message` object and then we check if it starts with our supported command. If there is any more text after the command string, we assume all of it is the provided city name. The Weatherstack API expects city names that have multiple words to be separated by a `+` symbol. So we split the rest of the string into parts and join them back together with pluses. Now that our city name is in the correct format expected by our `getTemperature(city)` helper method, we simply call it and return the result to the user.
+
+```go
+ temperature, err := getTemperature(city)
+ if err != nil {
+ log.Print(err)
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Sorry, I couldn't fetch the temperature for "+city)
+ bot.Send(reply)
+ continue
+ }
+
+ reply := tgbotapi.NewMessage(msg.Chat.ID, temperature)
+ bot.Send(reply)
+```
+
+One thing to note is our error handling. It is generally good UX and security practice not to show the raw error message to the user. Instead, we log the real error for ourselves and return a generic error to the user.
+
+Next, we handle the `/askGoku` command.
+
+```go
+} else if strings.HasPrefix(msg.Text, "/askGoku") {
+ parts := strings.SplitN(msg.Text, " ", 2)
+ if len(parts) < 2 {
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Please provide a question. Usage: /askGoku [question]")
+ bot.Send(reply)
+ continue
+ }
+```
+We start by checking if the message begins with `/askGoku`. We then use `strings.SplitN()` to split the message into at most two parts. This ensures that questions with multiple words are kept together. If the user didn't provide a question after the command, we send them an error message with usage instructions and `continue` to the next update.
+
+Now we just need to call our `askGoku()` helper function. If the request succeeds, we create a new message with the AI response and send it back to the user.
+
+```go
+response, err := askGoku(parts[1])
+ if err != nil {
+ log.Print(err)
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Sorry, I couldn't process your question. Please try again.")
+ bot.Send(reply)
+ continue
+ }
+
+ reply := tgbotapi.NewMessage(msg.Chat.ID, response)
+ bot.Send(reply)
+```
+
+The last thing we need to do is add a default response if the user sends anything other than the commands that we support.
+```go
+ } else {
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Welcome to my Bot!\n\nIt can help you get the current temperature for any city or else you can ask Goku a question.\n\nAvailable commands:\n/temperature [city] - Get the current temperature for a city (defaults to Cape Town)\n/askGoku [question] - Ask a question and see what Goku has to say!")
+ bot.Send(reply)
+ }
+ }
+}
+```
+We can use this default case to show the expected usages for our supported commands.
+
+### Testing the Bot Locally
+
+Before deploying to Code Capsules, you should test that your bot works correctly. You can run it locally using the polling approach.
+
+```bash
+go run bot.go
+```
+
+You should see output similar to:
+
+```
+Bot starting...
+Authorized on account your_bot_username
+```
+
+Now open Telegram, find your bot by username, and test the commands:
+
+- Send `/temperature Cape Town` to check the current temperature
+- Send `/askGoku What is your favorite food?` to get a response from Goku
+- Send any other text to see the welcome message
+
+
+
+Check your terminal for any error logs if something doesn't work. Once you've verified all commands work as expected, you're ready to deploy.
+
+### Deploying the Bot
+
+We will now deploy the bot on Code Capsules to allow us to make it more efficient by using webhooks instead of polling.
+
+Follow [this quick guide](https://docs.codecapsules.io/backend/go) to set up your Code Capsules account and create a new capsule.
+
+Once it is up and running, navigate to the "Config" tab of our capsule and add a `BOT_TOKEN` environment variable giving it the value of your bot's access token.
+Similarly, add your `WEATHER_API_KEY` and `GEMINI_API_KEY`
+
+
+
+Make sure your code is committed and pushed to your GitHub repository. Then In the Deploy tab, find "Capsule Branch" and press "Edit". Select your new repository from the drop down (if it doesn't show up press "Configure Git for Code Capsules" and follow the steps) and press "save". This will trigger a new build of the project and the Bot will go live.
+
+
+
+We can then run the same tests as we ran locally (ensure the local instance is stopped) and ensure that our bot is still working as intended.
+
+### Polling Versus Webhooks for a Telegram bot using Go
+
+The polling approach we used above works well for development and small bots, but it can be inefficient.
+
+Webhooks work in the reverse manner. Instead of your bot repeatedly asking Telegram for updates, Telegram will send updates directly to your bot's HTTP server whenever a user sends a message.
+
+#### Converting to Webhooks
+
+The main architectural change is replacing the polling loop with an HTTP server. Instead of this:
+
+```go
+u := tgbotapi.NewUpdate(0)
+u.Timeout = 60
+updates := bot.GetUpdatesChan(u)
+
+for update := range updates {
+ // process update
+}
+```
+
+We create an HTTP server that listens for POST requests from Telegram:
+
+```go
+http.HandleFunc("/", handleWebhook)
+log.Fatal(http.ListenAndServe(":"+port, nil))
+```
+
+#### Setting up the Webhook
+
+When your bot starts, you need to tell Telegram where to send updates. This is done with the `NewWebhook()` function:
+
+```go
+webhookURL := os.Getenv("APP_URL")
+if webhookURL == "" {
+ log.Fatal("APP_URL environment variable not set")
+}
+
+wh, err := tgbotapi.NewWebhook(webhookURL)
+if err != nil {
+ log.Panic(err)
+}
+if _, err := bot.Request(wh); err != nil {
+ log.Panic(err)
+}
+```
+
+The `APP_URL` is the public URL where Telegram can reach your bot. This will be automatically configured in your capsule.
+
+#### Receiving and Processing Webhook Updates
+
+The HTTP handler receives JSON from Telegram and unmarshals it into an Update struct:
+
+```go
+func handleWebhook(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ log.Printf("Error reading request body: %v", err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ var update tgbotapi.Update
+ if err = json.Unmarshal(body, &update); err != nil {
+ log.Printf("Error unmarshaling update: %v", err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ go processUpdate(update)
+ w.WriteHeader(http.StatusOK)
+}
+```
+
+The handler immediately returns an HTTP 200 OK response to Telegram. The actual processing of the update happens asynchronously in a goroutine with `go processUpdate(update)`. This ensures that Telegram doesn't timeout waiting for your bot to process the command.
+
+The `processUpdate()` function contains the same command handling logic as before (temperature, askGoku, etc.), but it's now called for each webhook update instead of being in a polling loop.
+
+To deploy these changes, simply push the code to your GitHub repo and your Code Capsules instance will automatically rebuild and deploy the changes to production.
\ No newline at end of file
diff --git a/get-started/.gitbook/assets/url-env-variable.png b/get-started/.gitbook/assets/url-env-variable.png
index 6d07b33a9..c8a9bb6bf 100644
Binary files a/get-started/.gitbook/assets/url-env-variable.png and b/get-started/.gitbook/assets/url-env-variable.png differ
diff --git a/get-started/SUMMARY.md b/get-started/SUMMARY.md
index 876b0a125..7a9d38cec 100644
--- a/get-started/SUMMARY.md
+++ b/get-started/SUMMARY.md
@@ -17,17 +17,19 @@
* [Django](backend/python/django.md)
* [Flask](backend/python/flask.md)
* [Python Discord Bot](backend/python/python-discord-bot.md)
- * [Python Telegram Bot](backend/python/python-telegram-bot.md)
+ * [Deploy a Python Telegram Bot in 5 Minutes](backend/python/python-telegram-bot.md)
* [Polling Python Telegram Bot](backend/python/polling-python-telegram-bot.md)
- * [WhatsApp Bot](backend/python/whatsapp-bot.md)
+ * [Whatsapp Bot](backend/python/whatsapp-bot.md)
* [Node.js](backend/node.js/README.md)
* [Express.js](backend/node.js/express.js.md)
* [Node.js Discord Bot](backend/node.js/node.js-discord-bot.md)
- * [Node.js Telegram Bot](backend/node.js/node.js-telegram-bot.md)
+ * [Deploy a Node.js Telegram Bot in 5 Minutes](backend/node.js/node.js-telegram-bot.md)
* [Polling Node Telegram Bot](backend/node.js/polling-node-telegram-bot.md)
* [Slack Bot](backend/node.js/slack-bot.md)
* [Java](backend/java.md)
- * [Go](backend/go.md)
+ * [Go](backend/go/README.md)
+ * [Go](backend/go/go.md)
+ * [Deploy a Go Telegram Bot in 5 Minutes](backend/go/go-telegram-bot.md)
* [Docker](backend/docker/README.md)
* [Caddy Docker Site](backend/docker/caddy-docker-site.md)
* [Docker Laravel App](backend/docker/docker-laravel-app.md)
@@ -52,5 +54,5 @@
* [Flask + HTMX](full-stack/flask-+-htmx.md)
* [MEAN Stack](full-stack/mean-stack.md)
* [MERN Stack](full-stack/mern-stack.md)
-* [Persistent Storage](peristent-storage.md)
+* [Peristent Storage](peristent-storage.md)
* [WordPress](wordpress.md)
diff --git a/get-started/backend/go/README.md b/get-started/backend/go/README.md
new file mode 100644
index 000000000..66fda561d
--- /dev/null
+++ b/get-started/backend/go/README.md
@@ -0,0 +1,2 @@
+# Go
+
diff --git a/get-started/backend/go/go-telegram-bot.md b/get-started/backend/go/go-telegram-bot.md
new file mode 100644
index 000000000..357bd2ed5
--- /dev/null
+++ b/get-started/backend/go/go-telegram-bot.md
@@ -0,0 +1,101 @@
+---
+description: Learn how to deploy a Go Telegram Bot from GitHub.
+---
+
+# Go Telegram Bot
+
+Deploy a Go Telegram Bot and learn how to host backend code on Code Capsules.
+
+## Register the Bot
+
+Before you can create a Telegram bot, you need a Telegram user account. Open Telegram and create an account if you don't already have one.
+
+When you've signed in to Telegram, search for `BotFather` (a bot for managing all other Telegram bots) and start a new chat with it.
+
+Follow the steps below to register a new bot with the BotFather:
+
+1. Type `/start` and press send.
+2. Type `/newbot` and press send.
+3. Choose a name for your bot.
+4. Choose a username for your bot that ends in `bot`.
+
+The BotFather will respond with a message containing an access token for your newly created bot. This access token allows our application to:
+
+- Access the Telegram API.
+- Tell our bot what to do when receiving different messages from users.
+
+To confirm that your bot has been created successfully, search for the bot's username. You should be able to see it and start a conversation with it. However, it won't respond because we haven't written the bot's logic yet.
+
+## Setup
+
+Code Capsules connects to GitHub repositories to deploy applications. To follow this guide, you need a [Code Capsules](https://codecapsules.io/) account and a [GitHub](https://github.com/) account.
+
+To demonstrate how to deploy a Go Telegram Bot with Code Capsules, we've provided an example bot in the [Code Capsules GitHub repository](https://github.com/codecapsules-io/go-telegram-echobot).
+
+Sign in to GitHub, and fork the example bot repository by clicking **Fork** at the top right of your screen and selecting your GitHub account as the destination.
+
+## Create a Space for Your Bot
+
+Log in to your Code Capsules account and navigate to the **Spaces** tab. Then, click the yellow **+** icon at the bottom left of the screen to add a new Space.
+
+Follow the prompts to choose your region and give your Space a name, then click **Create Space**.
+
+.png>)
+
+Example instructions to go with numbered annotations:
+
+1. Choose a Team. You can use a default "personal" Team if you're the only person working on this project, or a named Team if you're collaborating with others.
+2. The name should remind you of the project, for example, `customer-api` or `notetaking-app`.
+3. Choose a country close to where most of your users reside.
+
+## Create the Capsule
+
+A [Capsule](https://docs.codecapsules.io/platform/capsules/what-is-a-capsule) provides the server for hosting an application on Code Capsules.
+
+To create a new Capsule for your Space, follow the instructions below:
+
+1. Click the add Capsule **+** button in your Space.
+2. Choose **Backend** for the Capsule type, and select your Team and Space, if not already populated.
+3. Choose your payment plan and click **Next**.
+4. Click the **Configure Git for Code Capsules** button and grant access to your forked **`go-telegram-echobot`** repository.
+5. Press **Next**.
+6. Leave the **Run Command** blank.
+7. Click **Create Capsule**.
+
+Code Capsules automatically builds your application when you've finished creating the Capsule.
+
+Once your application is live, you can view the build logs by selecting the **Deploy** tab and clicking the **View build log** link in the **Builds** section.
+
+.png>)
+
+## Add Environment Variables
+
+Once the build is complete, we need to add the `BOT_TOKEN` and `URL` environment variables on the **Config** tab, under the **Environment Variables** section.
+
+First, name the `BOT_TOKEN` variable and enter your Telegram access token (which you received from the BotFather when you registered the bot) as its **Value**.
+
+
+
+Name the `URL` variable and set your bot's domain as its **Value**.
+
+- Get the domain by clicking the Capsule's **Details** tab and copying the URL in the **Public URL** section.
+- Paste the URL in the **Value** field.
+- Make sure the URL ends with a `/` or the webhook will not be valid.
+
+
+
+Confirm your changes by clicking on **Save**, then restart your Capsule by toggling the radio button in the top-right corner off and on again.
+
+## Set Up Webhook
+
+The next step is to set up a webhook for your bot:
+
+- Click the **Public URL** link in your Capsule's **Details** tab.
+- In the new tab that opens, add `/setwebhook` to the URL and press `enter`/`return` to visit the URL.
+- If you see `webhook setup ok`, then your bot is ready to chat!
+
+## Chat with the Bot
+
+The bot can now respond to messages! Search for your bot on Telegram using the username you assigned it, and start a chat with it. The bot is programmed to respond to `/start` and echo any messages you send it.
+
+If you'd like to deploy another application in a different language or framework, take a look at our other [deployment guides](https://docs.codecapsules.io/backend).
diff --git a/get-started/backend/go.md b/get-started/backend/go/go.md
similarity index 83%
rename from get-started/backend/go.md
rename to get-started/backend/go/go.md
index 7fe87a5b2..40835a72f 100644
--- a/get-started/backend/go.md
+++ b/get-started/backend/go/go.md
@@ -6,7 +6,7 @@ description: A guide to deploying a Go application from GitHub.
Deploy a Go application and learn how to host backend code on Code Capsules.
-## Setup
+## Set up
Code Capsules connects to GitHub repositories to deploy applications. To follow this guide, you'll need a [Code Capsules](https://codecapsules.io/) account and a [GitHub](https://github.com/) account.
@@ -22,15 +22,15 @@ If you've just signed up for an account, you'll be directed to a welcome page on
Alternatively, if you're signing in again, click on the "Spaces" tab.
-Code Capsules gives every account a Personal Team by default. A Team is an environment for you to manage your Spaces and Capsules. For a better understanding of Teams, Spaces, and Capsules, take a look at [our explanation](https://app.gitbook.com/s/gIlxo9gU7Lotj1cdGRh6/platform).
+Code Capsules gives every account a Personal Team by default. A Team is an environment for you to manage your Spaces and Capsules. For a better understanding of Teams, Spaces, and Capsules, take a look at [our explanation](../../platform/platform.md).
-## Create a Space for Your App
+## Create a Space for your App
Log in to your Code Capsules account and navigate to the "Spaces" tab. Once there, click the yellow `+` icon on the bottom left of the screen to add a new Space.
Follow the prompts, choosing your region and giving your Space a name, then click "Create Space".
-.png>)
+.png>)
Example instructions to go with numbered annotations
@@ -56,13 +56,7 @@ Code Capsules will automatically build your application when you've finished cre
Once your application is live, you can view the build log by selecting the "Deploy" tab and clicking the "View build log" link in the "Builds" section.
-
-
-## Binding the Capsules
-
-Now you need to bind the two capsules together. Navigate to the "Config" tab of the Backend Capsule you've just created. Scroll to the "Bind Data Capsule" section and click on the "Bind". This enables the capsule to use the Persistent database in the Data Capsule.
-
-
+.png>)
## View Application
@@ -70,6 +64,6 @@ Restart your capsule by toggling the radio button in the top right off and on ag
Once the build is complete, click the "URL" link in the "Details" tab, and you should see your deployed application.
-
+
-If you’d like to deploy another application in a different language or framework, take a look at our other [deployment guides](../).
+If you’d like to deploy another application in a different language or framework, take a look at our other [deployment guides](../../).
diff --git a/get-started/backend/java.md b/get-started/backend/java.md
index ba26c3a8f..9ec419824 100644
--- a/get-started/backend/java.md
+++ b/get-started/backend/java.md
@@ -6,7 +6,7 @@ description: A guide on how to launch a Java application from GitHub.
Deploy a Java application and learn how to host backend code on Code Capsules.
-## Setup
+## Set Up
Code Capsules connects to GitHub repositories to deploy applications. To follow this guide, you'll need a [Code Capsules](https://codecapsules.io/) account and a [GitHub](https://github.com/) account.
@@ -24,13 +24,13 @@ Alternatively, if you're signing in again, click on the "Spaces" tab.
Code Capsules gives every account a Personal Team by default. A Team is an environment for you to manage your Spaces and Capsules. For a better understanding of Teams, Spaces, and Capsules, take a look at [our explanation](https://app.gitbook.com/s/gIlxo9gU7Lotj1cdGRh6/platform).
-## Create a Space for Your App
+## Create a Space for your App
Log in to your Code Capsules account and navigate to the "Spaces" tab. Once there, click the yellow `+` icon on the bottom left of the screen to add a new Space.
Follow the prompts, choosing your region and giving your Space a name, then click "Create Space".
-.png>)
+.png>)
Example instructions to go with numbered annotations
@@ -56,13 +56,7 @@ Code Capsules will automatically build your application when you've finished cre
Once your application is live, you can view the build log by selecting the "Deploy" tab and clicking the "View build log" link in the "Builds" section.
-
-
-## Binding the Capsules
-
-Now you need to bind the two capsules together. Navigate to the "Config" tab of the Backend Capsule you've just created. Scroll to the "Data capsules" section and click on "Bind". This enables the capsule to use the Persistent database in the Data Capsule.
-
-
+.png>)
## View Application
diff --git a/tutorials/.gitbook/assets/CleanShot 2025-06-02 at 15.09.05@2x.png b/tutorials/.gitbook/assets/CleanShot 2025-06-02 at 15.09.05@2x.png
deleted file mode 100644
index 8ebd174cd..000000000
Binary files a/tutorials/.gitbook/assets/CleanShot 2025-06-02 at 15.09.05@2x.png and /dev/null differ
diff --git a/tutorials/.gitbook/assets/add-environment-variables.png b/tutorials/.gitbook/assets/add-environment-variables.png
new file mode 100644
index 000000000..3738504af
Binary files /dev/null and b/tutorials/.gitbook/assets/add-environment-variables.png differ
diff --git a/tutorials/.gitbook/assets/bot-api-key-env.png b/tutorials/.gitbook/assets/bot-api-key-env.png
index 3b948029a..5af7b8cff 100644
Binary files a/tutorials/.gitbook/assets/bot-api-key-env.png and b/tutorials/.gitbook/assets/bot-api-key-env.png differ
diff --git a/tutorials/.gitbook/assets/telegram-bot-chat.png b/tutorials/.gitbook/assets/telegram-bot-chat.png
new file mode 100644
index 000000000..aeb6b94f7
Binary files /dev/null and b/tutorials/.gitbook/assets/telegram-bot-chat.png differ
diff --git a/tutorials/.gitbook/assets/telegram-bot-cover-go-2.png b/tutorials/.gitbook/assets/telegram-bot-cover-go-2.png
new file mode 100644
index 000000000..c79a3dbc0
Binary files /dev/null and b/tutorials/.gitbook/assets/telegram-bot-cover-go-2.png differ
diff --git a/tutorials/.gitbook/assets/update-git-branch.png b/tutorials/.gitbook/assets/update-git-branch.png
new file mode 100644
index 000000000..04e64fbcb
Binary files /dev/null and b/tutorials/.gitbook/assets/update-git-branch.png differ
diff --git a/tutorials/.gitbook/assets/working-telegram-bot.png b/tutorials/.gitbook/assets/working-telegram-bot.png
new file mode 100644
index 000000000..5adcf1940
Binary files /dev/null and b/tutorials/.gitbook/assets/working-telegram-bot.png differ
diff --git a/tutorials/README.md b/tutorials/README.md
index 8ae167814..23bab221a 100644
--- a/tutorials/README.md
+++ b/tutorials/README.md
@@ -6,4 +6,60 @@ coverY: 0
# Tutorials Overview
-
A decentralized application (dApp) is a software application that runs on a decentralized network, often…
diff --git a/tutorials/SUMMARY.md b/tutorials/SUMMARY.md
index d73789090..08c221aa4 100644
--- a/tutorials/SUMMARY.md
+++ b/tutorials/SUMMARY.md
@@ -1,14 +1,16 @@
# Table of contents
* [Tutorials Overview](README.md)
+* [How to Create and Host an AI-Backed Telegram Bot with Go on Code Capsules](create-and-host-go-ai-telegram-bot.md)
* [How to Create and Host a Telegram Bot on Code Capsules](how-to-create-and-host-a-telegram-bot-on-code-capsules.md)
+* [Create and Host a Telegram Bot with Node.js on Code Capsules](create-and-host-a-telegram-bot-with-node.js-on-code-capsules.md)
+* [How to Deploy a React Token dApp to Code Capsules](how-to-deploy-a-react-token-dapp-to-code-capsules.md)
* [Nuxt3 and Nitro](nuxt3-and-nitro.md)
* [How to Deploy a React Token dApp to Code Capsules](how-to-deploy-a-react-token-dapp-to-code-capsules.md)
* [Optimizing Performance in MERN Stack: Tips and Techniques](optimizing-performance-in-mern-stack-tips-and-techniques.md)
* [Creating and Hosting an API With Flask](creating-and-hosting-an-api-with-flask/README.md)
* [API Reference \[Sample\]](creating-and-hosting-an-api-with-flask/api-reference-sample.md)
* [Building a Full Stack Application with Flask and HTMx](building-a-full-stack-application-with-flask-and-htmx.md)
-* [Create and Host a Telegram Bot with Node.js on Code Capsules](create-and-host-a-telegram-bot-with-node.js-on-code-capsules.md)
* [Build a Slackbot with Node.js to Monitor your Applications](build-a-slackbot-with-node.js-to-monitor-your-applications.md)
* [Video Guides](video-guides.md)
* [Building a Full Stack Application With Express and HTMx](building-a-full-stack-application-with-express-and-htmx.md)
diff --git a/tutorials/create-and-host-go-ai-telegram-bot.md b/tutorials/create-and-host-go-ai-telegram-bot.md
new file mode 100644
index 000000000..c9ec8f1ba
--- /dev/null
+++ b/tutorials/create-and-host-go-ai-telegram-bot.md
@@ -0,0 +1,532 @@
+---
+description: >-
+ Build a Telegram bot that uses AI to respond to users with a custom personality.
+cover: .gitbook/assets/telegram-bot-cover-go-2.png
+coverY: 0
+layout:
+ cover:
+ visible: true
+ size: hero
+ title:
+ visible: true
+ description:
+ visible: true
+ tableOfContents:
+ visible: true
+ outline:
+ visible: true
+ pagination:
+ visible: true
+---
+
+
+
+*This guide uses Go. You can find the Node.js version [here](https://docs.codecapsules.io/tutorials/create-and-host-a-telegram-bot-with-node.js-on-code-capsules) or the Python version [here](https://docs.codecapsules.io/tutorials/how-to-create-and-host-a-telegram-bot-on-code-capsules)*.
+
+# How to Create and Host an AI-Backed Telegram Bot on Code Capsules
+
+In this tutorial we'll create a [Telegram bot](https://core.telegram.org/bots/) from scratch using [Go](https://go.dev/) and host it in a [new Capsule](https://app.codecapsules.io/new/capsule?capsuleType=backend). The bot will start with the basic functionality of checking the current air temperature of a given city. Then, we'll extend its functionality by adding AI capabilities using Google AI Studio.
+
+### Getting Started
+
+For this project we will use the following tech stack:
+
+- **Frontend**: A Telegram bot that accepts specific commands from users.
+- **Backend**: An application written in Go and supported by various third-party APIs.
+
+The rest of the stack will be handled by our Backend Capsule.
+
+Create a Code Capsules account [here](https://codecapsules.io/) if you don't already have one.
+
+We will host our code on GitHub, so make sure you have a [GitHub account](https://github.com/signup) and are familiar with [Git](https://git-scm.com/). You can find a guide to getting started with Git [here](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control).
+
+
+#### Create a Telegram Account
+
+Go to [Telegram](https://telegram.org/), download the app for your desired platform, and create an account.
+
+For development purposes, we recommend downloading the version suited to your development environment, but it will work fine on your phone.
+
+#### Install Go
+
+If you have Homebrew configured, you can run the following code to install Go in a single step:
+
+```bash
+brew install go
+```
+
+Alternatively, you can find the appropriate instructions for your OS [here](https://go.dev/doc/install).
+
+Ensure that Go is installed correctly by checking that the version is displayed when you run `go version`.
+
+Start a project by creating a new directory for local development:
+
+```bash
+mkdir go-bot && cd go-bot
+```
+
+#### Set Up Third-Party APIs
+
+We will make use of APIs from Weatherstack and Google AI Studio.
+
+Weatherstack provides weather data with 100 free requests per month. To obtain a Weatherstack API key:
+
+- Create a free [account](https://weatherstack.com/product).
+- Log in and take note of the API key presented in the control panel.
+
+Google AI Studio provides access to generative AI models with a free tier for API calls. To obtain a Google AI Studio API key:
+
+- Go to [Google AI Studio](https://aistudio.google.com/).
+- Sign in with your Google account.
+- Click on **Get API key** and select **Create API key in new project**.
+- Copy the generated API key and store it securely.
+
+### Register a Bot Account and Talk to the BotFather
+
+Once you've signed in to Telegram, search for "BotFather" (a bot for managing all other Telegram bots) and start a new chat. Follow the steps below to register a new bot with the BotFather:
+
+1. Type `/start` and press send.
+2. Type `/newbot` and press send.
+3. Choose a name for your bot.
+4. Choose a username for your bot that ends in `bot`.
+
+The BotFather will respond with a message containing an access token for your newly created bot. This access token will allow your application to access the Telegram Bot API and tell your bot what to do when receiving different messages from users.
+
+To confirm that your bot was created successfully, search for the bot's username. You should be able to see it and start a conversation with it, although it won't respond at this stage as we haven't written the bot's logic yet.
+
+### Install Required Packages
+
+Our bot requires four third-party Go packages to handle Telegram communication, HTTP requests, environment variables, and AI integration. We'll install these using Go's package manager.
+
+First, initialize a Go module for your project by running the following command in your `go-bot` directory:
+
+```bash
+go mod init example.com/go-bot
+```
+
+Then install each of the required packages:
+
+```bash
+go get github.com/go-telegram-bot-api/telegram-bot-api/v5
+
+go get github.com/go-resty/resty/v2
+
+go get github.com/joho/godotenv
+
+go get google.golang.org/genai
+```
+
+After running all four commands, Go will automatically download and install each package, and your `go.mod` file will be updated with the dependencies.
+
+### Set Up the Environment
+
+Create a `.env` file in your `go-bot` directory to store your API keys securely.
+
+```bash
+touch .env
+```
+
+Add the following environment variables and replace the placeholder values with your actual API keys:
+
+```
+BOT_TOKEN=YOUR_TELEGRAM_BOT_TOKEN
+WEATHER_API_KEY=YOUR_WEATHERSTACK_API_KEY
+GEMINI_API_KEY=YOUR_GOOGLE_GEMINI_API_KEY
+```
+
+The godotenv package will automatically load these variables when the bot starts, making them available through `os.Getenv()`.
+
+### Create the Bot
+
+Create a new file `bot.go`. This will contain all the code for your bot's backend.
+
+Go requires a package and a function named `main` in order to execute. Specify that the new `bot.go` file is your main package.
+
+```go
+package main
+```
+
+#### Imports
+
+Now we can add imports for the third-party packages. We'll also need the functionality in some of the default packages that are shipped with Go.
+
+```go
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+
+ "github.com/go-resty/resty/v2"
+ tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
+ "github.com/joho/godotenv"
+ "google.golang.org/genai"
+)
+```
+
+#### Helper Functions
+
+Before we build out the `main()` function, which will control most of the logic to handle the bot commands, we need to build two helper functions that will make the calls to the third-party APIs.
+
+The first helper function retrieves temperature data from the Weatherstack API.
+
+First, we define a struct that maps the JSON response from Weatherstack:
+
+```go
+type TemperatureResponse struct {
+ Current struct {
+ Temperature int `json:"temperature"`
+ } `json:"current"`
+ Location struct {
+ Name string `json:"name"`
+ Country string `json:"country"`
+ } `json:"location"`
+}
+```
+
+This struct tells Go how to parse the API's JSON response by matching the fields we need.
+
+Then we create the `getTemperature` function, which takes a city name and queries the Weatherstack API:
+
+```go
+func getTemperature(city string) (string, error) {
+ apiKey := os.Getenv("WEATHER_API_KEY")
+ if apiKey == "" {
+ return "", fmt.Errorf("WEATHER_API_KEY environment variable not set")
+ }
+
+ client := resty.New()
+
+ url := fmt.Sprintf("http://api.weatherstack.com/current?access_key=%s&query=%s", apiKey, city)
+ resp, err := client.R().SetResult(&TemperatureResponse{}).Get(url)
+ if err != nil {
+ return "", err
+ }
+
+ if resp.StatusCode() != 200 {
+ return "", fmt.Errorf("API returned status %d", resp.StatusCode())
+ }
+
+ result := resp.Result().(*TemperatureResponse)
+
+ if result.Location.Name == "" {
+ return "", fmt.Errorf("could not find location data in API response")
+ }
+
+ return fmt.Sprintf("Temperature in %s, %s: %d°C", result.Location.Name, result.Location.Country, result.Current.Temperature), nil
+}
+```
+
+The function starts by retrieving the API key from our environment variables. We create a new Resty client to make HTTP requests, and then build the API URL with our key and the requested city. The `SetResult()` method tells Resty to parse the response into our `TemperatureResponse` struct. We can then check that the request succeeded, verify that the location data exists, and return a formatted string with the temperature information.
+
+For a more interesting example, the second helper function sends user questions to Google's Gemini API and return responses in whichever style we prompt it to use. In this case, we'll use Goku from Dragon Ball Z.
+
+```go
+func askGoku(question string) (string, error) {
+
+ const prompt = "You are Goku from DragonballZ. Give a very brief reply with no fluff. Always speak in the style of Goku."
+```
+
+To make the LLM respond in the style of Goku, we define a [system prompt](https://www.promptlayer.com/glossary/system-prompt) and attach it to the end of every user message so it will always be the final instruction to the LLM.
+
+From there, all we have to do is send our question to our Google AI Studio API instance and return the generated response to the user.
+
+```go
+ apiKey := os.Getenv("GEMINI_API_KEY")
+ if apiKey == "" {
+ return "", fmt.Errorf("GEMINI_API_KEY environment variable not set")
+ }
+
+ ctx := context.Background()
+
+ client, clientErr := genai.NewClient(ctx, &genai.ClientConfig{
+ APIKey: apiKey,
+ })
+ if clientErr != nil {
+ return "", fmt.Errorf("failed to create Gemini client: %v", clientErr)
+ }
+
+ result, err := client.Models.GenerateContent(
+ ctx,
+ "gemini-2.5-flash",
+ genai.Text(question+" "+prompt), // add the system prompt
+ nil,
+ )
+ if err != nil {
+ return "", fmt.Errorf("failed to generate content: %v", err)
+ }
+
+ if result == nil {
+ return "", fmt.Errorf("received nil response from Gemini API")
+ }
+
+ if result.Text() == "" {
+ return "", fmt.Errorf("received empty response from Gemini API")
+ }
+
+ response := result.Text()
+ if len(response) > 4096 {
+ response = response[:4093] + "..."
+ }
+
+ return response, nil
+}
+```
+
+For some basic error handling, we validate that we received a non-empty response and then check if it exceeds Telegram's message length limit (4096 characters).
+
+#### Main Function
+
+We can now begin to build our `main()` function. We need our code to be aware of the environment variables that we set up in our `.env` file. We're using this file for convenience so that we can run the bot locally. Once our code is deployed, we'll configure the environment variables on our server instead.
+
+```go
+func main() {
+
+ if err := godotenv.Load(); err != nil {
+ log.Printf("Warning: Could not load .env file: %v", err)
+ }
+```
+
+Now that the variables are loaded into our environment, we can access them with the `os.Getenv()` function to get our `BOT_TOKEN`, and then we can create a new instance of the Telegram BotAPI.
+
+
+```go
+ token := os.Getenv("BOT_TOKEN")
+ if token == "" {
+ log.Fatal("BOT_TOKEN environment variable not set")
+ }
+
+ bot, err := tgbotapi.NewBotAPI(token)
+ if err != nil {
+ log.Panic(err)
+ }
+```
+
+##### Get Messages
+
+The next step is to enable our backend to respond whenever a user sends a command. Luckily, the Telegram Bot API package offers several ready solutions. The simplest approach is to use polling, in which the backend code continuously queries the bot API to check for new messages.
+
+```go
+ u := tgbotapi.NewUpdate(0)
+ u.Timeout = 60
+
+ // Get the updates channel from the bot
+ updates := bot.GetUpdatesChan(u)
+```
+
+The above code handles this setup. It uses the `GetUpdatesChan()` function to launch a background [goroutine](https://go.dev/tour/concurrency/1) that polls the Telegram Bot API for new message requests. As soon as a message arrives, it is assigned to the `updates` variable and the rest of the code continues to run.
+
+Now that we have polling inplace, we can set up the main loop of our program that will process the user commands when they are available.
+
+```go
+ for update := range updates {
+```
+
+Go handles the above code quite efficiently, as the loop will sleep until a value is available on the updates channel, instead of running continuouslt and using CPU cycles.
+
+Next, we add a check to skip any kind of update we receive that doesn't contain an actual message string. Once we are sure there is a message, we can extract the `Message` object.
+
+```go
+ if update.Message == nil {
+ continue
+ }
+
+ msg := update.Message
+```
+
+##### Process Commands
+
+Now that we know we have a message from the user, we can add the logic for the commands that we want to support. To make the code easier to follow, we'll keep all the logic in the main class. If you are coding a more complex bot, with support for a large number of commands, you would probably want to consider improving the extendibility of your code with some object-oriented principles. For example, this code would be suited to the [Factory Method pattern](https://refactoring.guru/design-patterns/factory-method), which could provide some layers of abstraction that make adding new commands more streamlined.
+
+First, let's look at the temperature command `/temperature [city]`. This will make a call to the Weatherstack API to get the live temperature for the provided city.
+
+```go
+ if strings.HasPrefix(msg.Text, "/temperature") {
+ city := "Cape+Town"
+ parts := strings.Fields(msg.Text)
+ if len(parts) > 1 {
+ city = strings.Join(parts[1:], "+")
+ }
+```
+
+We extract the actual text string from the `Message` object and then check if it starts with our supported command.
+
+If any more text appears after the command string, we treat all of it as the city name. Since the Weatherstack API requires multi-word city names to be separated by `+`, we split the rest of the string into parts and then join them back together using `+` symbols.
+
+Now that our city name is in the correct format required by the `getTemperature(city)` helper method, we simply call it and return the result to the user.
+
+```go
+ temperature, err := getTemperature(city)
+ if err != nil {
+ log.Print(err)
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Sorry, I couldn't fetch the temperature for "+city)
+ bot.Send(reply)
+ continue
+ }
+
+ reply := tgbotapi.NewMessage(msg.Chat.ID, temperature)
+ bot.Send(reply)
+```
+
+One thing to note is error handling. For good UX and security best practices, it is generally best not to send raw error messages to users. Instead, log the real error internally and return a generic error message to the user.
+
+Next, we'll work on the `/askGoku` command.
+
+```go
+ } else if strings.HasPrefix(msg.Text, "/askGoku") {
+ parts := strings.SplitN(msg.Text, " ", 2)
+ if len(parts) < 2 {
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Please provide a question. Usage: /askGoku [question]")
+ bot.Send(reply)
+ continue
+ }
+```
+
+tart by checking if the message begins with `/askGoku`. Then, use `strings.SplitN()` to split the message into at most two parts. This ensures that questions with multiple words are kept together. If the user did not provide a question after the command, we send them an error message with usage instructions and `continue` to the next update.
+
+Now we can call our `askGoku()` helper function. If the request succeeds, we create a new message with the AI response and send it back to the user.
+
+```go
+ response, err := askGoku(parts[1])
+ if err != nil {
+ log.Print(err)
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Sorry, I couldn't process your question. Please try again.")
+ bot.Send(reply)
+ continue
+ }
+
+ reply := tgbotapi.NewMessage(msg.Chat.ID, response)
+ bot.Send(reply)
+```
+
+##### Default Response
+
+The last thing we need to do is add a default response if the user sends anything other than the commands that we support.
+
+```go
+ } else {
+ reply := tgbotapi.NewMessage(msg.Chat.ID, "Welcome to my Bot!\n\nIt can help you get the current temperature for any city or else you can ask Goku a question.\n\nAvailable commands:\n/temperature [city] - Get the current temperature for a city (defaults to Cape Town)\n/askGoku [question] - Ask a question and see what Goku has to say!")
+ bot.Send(reply)
+ }
+ }
+}
+```
+
+We can use this default case to show the expected usage for our supported commands.
+
+## Test the Bot Locally
+
+Before deploying to Code Capsules, test that your bot works correctly. You can run it locally using the polling approach.
+
+```bash
+go run bot.go
+```
+
+Open Telegram, find your bot by username, and test the commands:
+
+- Send `/temperature Cape Town` to check the current temperature.
+- Send `/askGoku What is your favorite food?` to get a response from Goku.
+- Send any other text to see the welcome message.
+
+
+
+Check your terminal for any error logs if something doesn't work. Once you've verified that all commands work as expected, you're ready to deploy.
+
+### Deploy the Bot
+
+We will now deploy the bot on Code Capsules and make it more efficient by using webhooks instead of polling.
+
+Follow [this quick guide](https://docs.codecapsules.io/backend/go) to set up your Code Capsules account and create a new Capsule.
+
+Once it is up and running, navigate to the **Config** tab of your Capsule and add a `BOT_TOKEN` environment variable, giving it the value of your bot's access token. Similarly, add your `WEATHER_API_KEY` and `GEMINI_API_KEY`.
+
+
+
+Commit your bot code and push to your GitHub repository. Then, in the **Deploy** tab, find **Capsule Branch** and click **Edit**. Select your new repository from the drop down menu (if it doesn't show up, select **Configure Git for Code Capsules** and follow the steps) and click **Save**. This will trigger a new build of the project and the bot will go live.
+
+
+
+We can then run the same tests as we ran locally (ensure the local instance is stopped) to check that the bot is still working as intended.
+
+### Polling vs Webhooks
+
+The polling approach we used earlier works well for development and small bots. However, polling is not the most bandwidth-efficient method because it requires constant communication between the backend code and the server.
+
+A more efficient alternative is webhooks, which work in the reverse manner. Instead of the bot repeatedly asking Telegram for updates, Telegram will send updates directly to the bot's HTTP server whenever a user sends a message.
+
+#### How to Convert to Webhooks
+
+The main architectural change is replacing the polling loop with an HTTP server. Instead of this:
+
+```go
+u := tgbotapi.NewUpdate(0)
+u.Timeout = 60
+updates := bot.GetUpdatesChan(u)
+
+for update := range updates {
+ // process update
+}
+```
+
+Create an HTTP server that listens for POST requests from Telegram:
+
+```go
+http.HandleFunc("/", handleWebhook)
+log.Fatal(http.ListenAndServe(":"+port, nil))
+```
+
+When the bot starts, we need to tell Telegram where to send updates. This is done with the `NewWebhook()` function:
+
+```go
+webhookURL := os.Getenv("APP_URL")
+if webhookURL == "" {
+ log.Fatal("APP_URL environment variable not set")
+}
+
+wh, err := tgbotapi.NewWebhook(webhookURL)
+if err != nil {
+ log.Panic(err)
+}
+if _, err := bot.Request(wh); err != nil {
+ log.Panic(err)
+}
+```
+
+The `APP_URL` is the public URL where Telegram can reach our bot. This will be automatically configured in the Capsule.
+
+#### Receiving and Processing Webhook Updates
+
+The HTTP handler receives JSON from Telegram and unmarshals it into an `Update` struct:
+
+```go
+func handleWebhook(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+ log.Printf("Error reading request body: %v", err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ var update tgbotapi.Update
+ if err = json.Unmarshal(body, &update); err != nil {
+ log.Printf("Error unmarshaling update: %v", err)
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ go processUpdate(update)
+ w.WriteHeader(http.StatusOK)
+}
+```
+
+The handler immediately returns an HTTP 200 OK response to Telegram. The actual processing of the update happens asynchronously in a goroutine with `go processUpdate(update)`. This ensures that Telegram doesn't timeout waiting for the bot to process the command.
+
+The `processUpdate()` function contains the same command handling logic as before (temperature, askGoku, etc.), but it's now called for each webhook update instead of being in a polling loop.
+
+Now you can simply push the code to your GitHub repo, and your Code Capsules instance will automatically rebuild and deploy the changes to production.
diff --git a/tutorials/deploy-a-node.js-discord-bot-to-production-in-5-minutes.md b/tutorials/deploy-a-node.js-discord-bot-to-production-in-5-minutes.md
deleted file mode 100644
index d501c826a..000000000
--- a/tutorials/deploy-a-node.js-discord-bot-to-production-in-5-minutes.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Deploy a Node.js Discord Bot to Production in 5 Minutes
-
-### Setup
-
-Code Capsules connects to GitHub repositories to deploy applications. To follow this guide, you’ll need a [Code Capsules](https://codecapsules.io/) account and a [GitHub](https://github.com/) account.
-
-To demonstrate how to deploy a Node.js Discord Bot with Code Capsules, we’ve provided an example bot, which you can find on the [Code Capsules GitHub repository](https://github.com/codecapsules-io/nodejs-discord-echobot).
-
-Sign in to GitHub, and fork the example bot repository by clicking “Fork” at the top-right of your screen and selecting your GitHub account as the destination.
-
-### Register the Bot
-
-You’ll need a Discord user account before you can create a Discord bot. Head over to Discord and create an account if you don’t already have one.
-
-When you’ve signed in to Discord, follow the steps below:
-
-* Click on the “+” icon in the left toolbar to create a server to contain your channels.
-
-
-
-* Navigate to the [Application Page](https://discord.com/developers/applications).
-* Click on the “New Application” button.
-* Give the application a name and click “Create”.
-* Go to the “Bot” tab and click “Add Bot”. Confirm your decision by clicking, “Yes, do it!”
-
-
-
-* Click the “Copy” button under the “TOKEN” section to copy your bot’s token.
-
-
-
-* Go to the “OAuth2/URL Generator” tab and select the “bot” option under the “Scopes” section.
-
-
-
-* Select all the text permission options under the “Bot Permissions” section.
-
-
-
-* Click the “Copy” button under the, “Generated URL” section
-
-
-
-* Paste the URL you copied in the previous step in another browser tab and add the bot to the server you created in the first step. Click “Continue” to confirm your changes.
-
-After actioning these steps, your bot will now have access to all the channels in the server you added it to.
-
-### Create a Space for Your App
-
-Log in to your Code Capsules account and navigate to the “Spaces” tab. Once there, click the yellow `+` icon on the top right of the screen to add a new Space.
-
-Follow the prompts, choosing your region and giving your Space a name, then click “Create Space”.
-
-
-
-Example instructions to go with numbered annotations, 1. Choose a team — you can use a default “personal” team if you’re the only person working on this project, or a named team if you’re collaborating with others 2. This should remind you of the project, for example “customer-api” or “notetaking-app” 3. Choose a country close to where most of your users will be 4. If you’re already using a specific cloud, you can choose that here, otherwise pick anyone.
-
-### Create the Capsule
-
-A [Capsule](https://app.gitbook.com/s/gIlxo9gU7Lotj1cdGRh6/capsules/what-is-a-capsule) provides the server for hosting an application on Code Capsules.
-
-To create a new Capsule for your space, follow the instructions below:
-
-1. Choose “Backend Capsule”, your Team and Space.
-2. Choose your payment plan.
-3. Click the GitHub button and provide access to the repository you forked at the start of the tutorial.
-4. Choose the GitHub repository you forked.
-5. Press “Next”.
-6. Leave “Run Command” blank.
-7. Click “Create Capsule”.
-
-Code Capsules will automatically build your application when you’ve finished creating the Capsule.
-
-Once your application is live, you can view the build log by selecting the “Deploy” tab and clicking the “View build log” link in the “Builds” section.
-
-
-
-### Add a `TOKEN` Environment Variable
-
-Once the build is complete, you have to add a `TOKEN` environment variable on the “Config” tab under the “Environment Variables” section. Assign it the value of the token you copied in step 6 of the Register the Bot section above.
-
-
-
-Confirm your changes by clicking on “Save”, then restart your Capsule by toggling the radio button in the top right off and on again.
-
-### Chat with the Bot
-
-The bot will be able to respond to messages after Code Capsules finishes building it. When this is done, you can send messages in the general channel of your Discord server and the bot will echo them.
-
-If you’d like to deploy another application in a different language or framework, take a look at our other [deployment guides](https://app.gitbook.com/o/zJVrvoPm6oe6islWnPll/s/xjp0G5hHSJs8nyv5Z5g7/).
diff --git a/tutorials/deploy-a-node.js-telegram-bot-webhook-method-to-production-in-5-minutes.md b/tutorials/deploy-a-node.js-telegram-bot-webhook-method-to-production-in-5-minutes.md
deleted file mode 100644
index 1af9b54e7..000000000
--- a/tutorials/deploy-a-node.js-telegram-bot-webhook-method-to-production-in-5-minutes.md
+++ /dev/null
@@ -1,78 +0,0 @@
-# Deploy a Node.js Telegram Bot (Webhook Method) to Production in 5 Minutes
-
-### Register the Bot
-
-You’ll need a Telegram user account before you can create a Telegram bot. Head over to Telegram and create an account if you don’t already have one.
-
-When you’ve signed in to Telegram, search for “BotFather” (a bot for managing all other Telegram bots) and start a new chat with it. Follow the steps below to register a new bot with the BotFather:
-
-1. Type `/start` and press send.
-2. Type `/newbot` and press send.
-3. Choose a name for your bot.
-4. Choose a username for your bot that ends in “bot”.
-
-The BotFather will respond with a message containing an access token for your newly created bot. This access token will allow our application to access the Telegram API and tell our bot what to do when receiving different messages from users.
-
-To confirm that your bot was created successfully, search for the bot’s username. You should be able to see it and start a conversation with it, although it won’t respond, as we haven’t written the bot’s logic yet.
-
-### Setup
-
-Code Capsules connects to GitHub repositories to deploy applications. To follow this guide, you’ll need a [Code Capsules](https://codecapsules.io/) account and a [GitHub](https://github.com/) account.
-
-To demonstrate how to deploy a Node.js Telegram Bot with Code Capsules, we’ve provided an example bot, which you can find on the [Code Capsules GitHub repository](https://github.com/codecapsules-io/nodejs-telegram-echobot).
-
-Sign in to GitHub, and fork the example bot repository by clicking “Fork” at the top-right of your screen and selecting your GitHub account as the destination.
-
-### Create a Space for Your Bot
-
-Log in to your Code Capsules account and navigate to the “Spaces” tab. Once there, click the yellow `+` icon on the top right of the screen to add a new Space.
-
-Follow the prompts, choosing your region and giving your Space a name, then click “Create Space”.
-
-
-
-Example instructions to go with numbered annotations, 1. Choose a team — you can use a default “personal” team if you’re the only person working on this project, or a named team if you’re collaborating with others 2. This should remind you of the project, for example “customer-api” or “notetaking-app” 3. Choose a country close to where most of your users will be 4. If you’re already using a specific cloud, you can choose that here, otherwise pick anyone.
-
-### Create the Capsule
-
-A [Capsule](https://app.gitbook.com/s/gIlxo9gU7Lotj1cdGRh6/capsules/what-is-a-capsule) provides the server for hosting an application on Code Capsules.
-
-To create a new Capsule for your space, follow the instructions below:
-
-1. Choose “Backend Capsule”, your Team and Space.
-2. Choose your payment plan.
-3. Click the GitHub button and provide access to the repository you forked at the start of the tutorial.
-4. Choose the GitHub repository you forked.
-5. Press “Next”.
-6. Leave “Run Command” blank.
-7. Click “Create Capsule”.
-
-Code Capsules will automatically build your application when you’ve finished creating the Capsule.
-
-Once your application is live, you can view the build log by selecting the “Deploy” tab and clicking the “View build log” link in the “Builds” section.
-
-
-
-### Add Environment Variables
-
-Once the build is complete, you have to add `BOT_TOKEN` and `URL` environment variables on the “Config” tab under the “Environment Variables” section.
-
-#### `BOT_TOKEN`
-
-Assign the `BOT_TOKEN` variable the value of the access token you were given by the BotFather when you registered the bot.
-
-
-
-#### `URL`
-
-For the `URL` variable, set it to the value of your bot’s domain. You can get it by clicking the “Live Website” link to the left of the capsule’s toggle button and copying the URL in the new tab that opens. Paste the URL you copied in the value field for the `URL` environment variable (make sure the URL ends with a `/` or the webhook will not be valid).
-
-
-
-Confirm your changes by clicking on “Save”, then restart your Capsule by toggling the radio button in the top right off and on again.
-
-### Chat with the Bot
-
-The bot will be able to respond to messages after Code Capsules finishes building it. When this is done, search for your bot on Telegram using the username you assigned it and start a chat with it. The bot has been programmed to respond to `/start` and echo any messages you send it.
-
-If you’d like to deploy another application in a different language or framework, take a look at our other [deployment guides](https://app.gitbook.com/o/zJVrvoPm6oe6islWnPll/s/xjp0G5hHSJs8nyv5Z5g7/).
diff --git a/tutorials/deploy-a-python-telegram-bot-webhook-method-to-production-in-5-minutes.md b/tutorials/deploy-a-python-telegram-bot-webhook-method-to-production-in-5-minutes.md
deleted file mode 100644
index c0a09ec32..000000000
--- a/tutorials/deploy-a-python-telegram-bot-webhook-method-to-production-in-5-minutes.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# Deploy a Python Telegram Bot (Webhook Method) to Production in 5 Minutes
-
-### Register the Bot
-
-You’ll need a Telegram user account before you can create a Telegram bot. Head over to Telegram and create an account if you don’t already have one.
-
-When you’ve signed in to Telegram, search for “BotFather” (a bot for managing all other Telegram bots) and start a new chat with it. Follow the steps below to register a new bot with the BotFather:
-
-1. Type `/start` and press send.
-2. Type `/newbot` and press send.
-3. Choose a name for your bot.
-4. Choose a username for your bot that ends in “bot”.
-
-The BotFather will respond with a message containing an access token for your newly created bot. This access token will allow our application to access the Telegram API and tell our bot what to do when receiving different messages from users.
-
-To confirm that your bot was created successfully, search for the bot’s username. You should be able to see it and start a conversation with it, although it won’t respond, as we haven’t written the bot’s logic yet.
-
-### Setup
-
-Code Capsules connects to GitHub repositories to deploy applications. To follow this guide, you’ll need a [Code Capsules](https://codecapsules.io/) account and a [GitHub](https://github.com/) account.
-
-To demonstrate how to deploy a Python Telegram Bot with Code Capsules, we’ve provided an example bot, which you can find on the [Code Capsules GitHub repository](https://github.com/codecapsules-io/python-telegram-echobot).
-
-Sign in to GitHub, and fork the example bot repository by clicking “Fork” at the top-right of your screen and selecting your GitHub account as the destination.
-
-### Create a Space for Your Bot
-
-Log in to your Code Capsules account and navigate to the “Spaces” tab. Once there, click the yellow `+` icon on the top right of the screen to add a new Space.
-
-Follow the prompts, choosing your region and giving your Space a name, then click “Create Space”.
-
-
-
-Example instructions to go with numbered annotations, 1. Choose a team — you can use a default “personal” team if you’re the only person working on this project, or a named team if you’re collaborating with others 2. This should remind you of the project, for example “customer-api” or “notetaking-app” 3. Choose a country close to where most of your users will be 4. If you’re already using a specific cloud, you can choose that here, otherwise pick anyone.
-
-### Create the Capsule
-
-A [Capsule](https://app.gitbook.com/s/gIlxo9gU7Lotj1cdGRh6/capsules/what-is-a-capsule) provides the server for hosting an application on Code Capsules.
-
-To create a new Capsule for your space, follow the instructions below:
-
-1. Choose “Backend Capsule”, your Team and Space.
-2. Choose your payment plan.
-3. Click the GitHub button and provide access to the repository you forked at the start of the tutorial.
-4. Choose the GitHub repository you forked.
-5. Press “Next”.
-6. Leave “Run Command” blank.
-7. Click “Create Capsule”.
-
-Code Capsules will automatically build your application when you’ve finished creating the Capsule.
-
-Once your application is live, you can view the build log by selecting the “Deploy” tab and clicking the “View build log” link in the “Builds” section.
-
-
-
-### Add Environment Variables
-
-Once the build is complete, you have to add `BOT_TOKEN` and `URL` environment variables on the “Config” tab under the “Environment Variables” section.
-
-#### `BOT_TOKEN`
-
-Assign the `BOT_TOKEN` variable the value of the access token you were given by the BotFather when you registered the bot.
-
-
-
-#### `URL`
-
-For the `URL` variable, set it to the value of your bot’s domain. You can get it by clicking the “Live Website” link to the left of the capsule’s toggle button and copying the URL in the new tab that opens. Paste the URL you copied in the value field for the `URL` environment variable (make sure the URL ends with a `/` or the webhook will not be valid).
-
-
-
-Confirm your changes by clicking on “Save”, then restart your Capsule by toggling the radio button in the top right off and on again.
-
-### Set Up Webhook
-
-The next step is to set up a webhook for your bot. Do this by clicking the “Live Website” link at the top of the capsule’s page. On the new tab that opens, add `/setwebhook` to the URL and press enter/return to visit the URL. If you see `webhook setup ok` then your bot is ready to chat!
-
-### Chat with the Bot
-
-The bot will be able to respond to messages after actioning the above steps. When this is done, search for your bot on Telegram using the username you assigned it and start a chat with it. The bot has been programmed to respond to `/start` and echo any messages you send it.
-
-If you’d like to deploy another application in a different language or framework, take a look at our other [deployment guides](https://app.gitbook.com/o/zJVrvoPm6oe6islWnPll/s/xjp0G5hHSJs8nyv5Z5g7/).
diff --git a/tutorials/how-to-create-and-host-a-telegram-bot-on-code-capsules.md b/tutorials/how-to-create-and-host-a-telegram-bot-on-code-capsules.md
index 9c7a2b461..a5e066b14 100644
--- a/tutorials/how-to-create-and-host-a-telegram-bot-on-code-capsules.md
+++ b/tutorials/how-to-create-and-host-a-telegram-bot-on-code-capsules.md
@@ -20,16 +20,14 @@ layout:
visible: true
---
-# How to Create and Host a Telegram Bot on Code Capsules
+# How To Create and Host a Telegram Bot on Code Capsules
-_This guide uses Python. You can find the NodeJS version_ [_here_](create-and-host-a-telegram-bot-with-node.js-on-code-capsules.md)_._
+_This guide uses Python. You can find the Node.js version_ [_here_](https://docs.codecapsules.io/tutorials/create-and-host-a-telegram-bot-with-node.js-on-code-capsules)_, and the Go version [_here_](https://docs.codecapsules.io/tutorials/create-and-host-go-ai-telegram-bot)._
-In [another tutorial](creating-and-hosting-an-api-with-flask/), we created and hosted an API on Code Capsules. In this tutorial, we'll create a client for this API in the form of a Telegram bot. This will allow us to pull temperature, weather and exchange rate data on the go by messaging our bot in the Telegram app.
+In this tutorial, we'll create a Telegram bot that will allow us to pull temperature, weather, and exchange rate data on the go by messaging our bot in the Telegram app.
We'll also learn how to host this bot on [Code Capsules](https://codecapsules.io/) so it can be used by others. Along the way, we'll learn some key concepts about hosting bots securely and efficiently.
-Let's get started!
-
### Requirements
To create a Telegram bot, we'll need:
@@ -37,13 +35,11 @@ To create a Telegram bot, we'll need:
* [Python](https://www.python.org/) 3.9+ installed.
* A [GitHub account](https://github.com/) and [Git](https://git-scm.com/) installed.
* [Virtualenv](https://pypi.org/project/virtualenv/) installed.
-* A [Telegram](https://telegram.org/) account.
* A [Code Capsules](https://codecapsules.io/) account.
-* An API on Code Capsules, created using [the Personal API tutorial](creating-and-hosting-an-api-with-flask/).
### About Telegram Bots
-Telegram bots appear as contacts on the Telegram interface. Users interact with Telegram bots by messaging them with commands – these are words preceded by a forward slash, e.g. `/weather`, or `/currency`. Commands sent to the bot's account on Telegram will be passed to the bot's backend code (in our case, this will be the code we host on Code Capsules).
+Telegram bots appear as contacts on the Telegram interface. Users interact with Telegram bots by messaging them with commands – these are words preceded by a forward slash, such as `/weather` or `/currency`. Commands sent to the bot's account on Telegram are passed to the bot's backend code (in our case, this will be the code we host on Code Capsules).
For example, when we send the command `/weather` to our bot later in this article, the bot will reply with the weather data from our personal API.
@@ -51,110 +47,116 @@ Let's create a Telegram bot.
### Registering a Bot Account and Talking to the BotFather
-To create a Telegram bot, we need to download [Telegram](https://telegram.org/) and create a user account. You can use Telegram from either your PC or your phone, or both.
+To create a Telegram bot, we need to download [Telegram](https://telegram.org/) and create a user account. You can use Telegram on your PC, your phone, or both.
-Once you have a Telegram account, you can register a new bot by sending a message to BotFather, a bot managed by Telegram themselves. Search for "BotFather" and initiate a chat. From the chat interface, follow these steps:
+Once you have a Telegram account, you can register a new bot by sending a message to BotFather, a bot managed by Telegram itself. Search for `BotFather` and initiate a chat. From the chat interface, follow these steps:
-1. Press "start".
+1. Click **Start**.
2. Type `/newbot`.
3. Choose a name for your bot.
-4. Choose a username for your bot (must end in "bot").
+4. Choose a username for your bot (must end in `bot`).
-Once you've chosen a username, the BotFather will reply with an _authorization token_. This is a string that enables your bot to send requests to the Telegram Bot API, similar to the authorisation tokens we used to retrieve weather and exchange rate data in the personal API tutorial. Make sure to save this token somewhere safe and private.
+Once you've chosen a username, the BotFather will reply with an _authorization token_. This is a string that enables your bot to send requests to the Telegram Bot API. Make sure to save this token somewhere safe and private.
-To see if your bot was successfully created, search for the bot's username. You should see the bot and be able to start a conversation with it. Right now, our bot won't reply to anything you send it, as it doesn't have any backend code yet. Let's change that.
+To check whether your bot was successfully created, search for the bot's username. You should see the bot and be able to start a conversation with it. Right now, your bot won't reply to anything you send it, as it doesn't have any backend code yet. Let's change that.
### Planning and Setup
We're going to implement two commands for our bot.
-* When we send the command `/weather`, our bot will reply with the weather data from the API we created.
-* When we send the command `/currency`, our bot will reply with the exchange rates from USD to CAD, EUR, and ZAR.
+* When we send the command `/weather`, our bot will reply with the weather data from the weather API.
+* When we send the command `/currency`, our bot will reply with the exchange rates for converting USD to CAD, EUR, and ZAR.
-#### Creating a virtual environment and installing requirements
+#### Creating a Virtual Environment and Installing Requirements
-First, we need to create a local directory. Give it the same name as our bot. Then, from this directory, open a terminal and create a Python [virtual environment](https://docs.python.org/3/tutorial/venv.html) by entering the following command:
+First, we need to create a local directory. Give it the same name as the bot. Then, from this directory, open a terminal and create a Python [virtual environment](https://docs.python.org/3/tutorial/venv.html) by entering the following command:
```bash
-virtualenv env
+python -m venv venv
```
-Enter the virtual environment using the appropriate command for your system:
+Activate the virtual environment using the appropriate command for your system:
-* **Linux/MacOSX**: `source env/bin/activate`
-* **Windows**: `env\Scripts\activate.bat`
+* **Linux/macOS**: `source venv/bin/activate`
+* **Windows**: `venv\Scripts\activate.bat`
-The virtual environment will help manage our dependencies for when we host the bot on Code Capsules.
+The virtual environment will help manage our dependencies when we host the bot on Code Capsules.
-To interact with the Telegram Bot API, we need to install the [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) library, a Python wrapper for the [Telegram Bot API](https://core.telegram.org/bots/api). We'll also use the Python library `requests` to retrieve data from the weather and currency exchange rate API. To install these requirements, enter the following in your terminal:
+To interact with the Telegram Bot API, we need to install the [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) library, a Python wrapper for the [Telegram Bot API](https://core.telegram.org/bots/api). We'll also use the Python library `requests` to retrieve data from the weather and currency exchange rate API. Finally, we will install the `python-dotenv` library for convenience, so that we can store our sensitive API keys in a file while we test locally. To install these requirements, enter the following in your terminal:
```bash
-pip install python-telegram-bot requests
+pip install python-telegram-bot requests python-dotenv
```
-### Retrieving Data from the API
+### Registering Accounts on OpenExchangeRates and Weatherstack
-Now we can start coding. Create a file named `bot.py` in the same directory where we activated the virtual environment. In this file, enter the following code, replacing `YOUR-URL-HERE` with the URL pointing to the weather and exchange rate API hosted on Code Capsules.
+Our bot will return the current temperature of a chosen city and the USD exchange rates for three currencies. We'll create our response by combining data from two APIs: Weatherstack and OpenExchangeRates.
-```python
-import requests
+#### Getting Our API Keys
-url = 'YOUR-URL-HERE/GET'
-data = requests.get(url) # requests data from API
-data = data.json() # converts return data to json
+In the directory where we'll create our bot (the same directory where we activated our virtual environment), create a file called `.env`. We'll use this to store our API keys temporarily while we code and test.
-# Retrieve values from API
-curr_temp = data['curr_temp']
-cad_rate = data['usd_rates']['CAD']
-eur_rate = data['usd_rates']['EUR']
-zar_rate = data['usd_rates']['ZAR']
+First, let's register an account on OpenExchangeRates. Navigate [to the site](https://openexchangerates.org/) and:
+1. Sign up and log in.
+2. On the dashboard, click **App IDs**.
+3. Take note of your **App ID** (API key), and paste it in the `.env` file: `EXCHANGE_API_KEY=`.
-def return_weather():
- print('Hello. The current temperature in Cape Town is: '+str(curr_temp)+" celsius.")
+Obtaining the Weatherstack API key is similar:
+1. Create a free account on [Weatherstack](https://weatherstack.com/)
+2. Log in, copy the API key displayed in the control panel, and paste it into the `.env` file: `WEATHER_API_KEY=`.
-def return_rates():
- print("Hello. Today, USD conversion rates are as follows: USD->CAD = "+str(cad_rate)+
- ", USD->EUR = "+str(eur_rate)+", USD->ZAR = "+str(zar_rate))
+At this point, we can also add the _authorization token_ for the Telegram bot we created earlier to the `.env` file: `BOT_TOKEN=`.
+### Creating the Bot
-return_weather()
+Now we can start coding. Create a file named `bot.py` in the same directory as your `.env` file. We'll set up helper functions to fetch data from the weather and exchange rate APIs. In this file, enter the following code:
-return_rates()
-```
+```python
+import os
+import requests
+from dotenv import load_dotenv
-Here we request the currency and weather data from the API and parse the temperature and conversion rates. Then we print out the data using `return_weather()` and `return_rates()`.
+load_dotenv()
-Try it out! Run the program to ensure everything works, then continue.
+EXCHANGE_API_KEY = os.getenv('EXCHANGE_API_KEY')
+WEATHER_API_KEY = os.getenv('WEATHER_API_KEY')
-### Creating the Bot
+EXCHANGE_URL = f'https://openexchangerates.org/api/latest.json?app_id={EXCHANGE_API_KEY}'
+EXCHANGE_PARAMS = {'symbols': 'ZAR,EUR,CAD'}
-Now we can get to creating the actual bot. At the top of the `bot.py` file, add this line:
+WEATHER_URL = f'http://api.weatherstack.com/current?access_key={WEATHER_API_KEY}'
+WEATHER_PARAMS = {'query': 'Cape Town'}
-```python
-from telegram.ext import Application, CommandHandler
-```
+def get_weather():
+ response = requests.get(WEATHER_URL, params=WEATHER_PARAMS)
+ curr_temp = response.json()['current']['temperature']
+ return f'Hello. The current temperature in Cape Town is: {curr_temp} celsius.'
-From the `python-telegram-bot` library, we import two classes: `Application` and `CommandHandler`. We'll talk about these classes soon.
+def get_exchange_rates():
+ response = requests.get(EXCHANGE_URL, params=EXCHANGE_PARAMS)
+ rates = response.json()['rates']
+ cad_rate, eur_rate, zar_rate = rates['CAD'], rates['EUR'], rates['ZAR']
+ return f'Hello. Today, USD conversion rates are as follows: USD->CAD = {cad_rate}, USD->EUR = {eur_rate}, USD->ZAR = {zar_rate}'
+```
-We don't need to print our data anymore – instead, we'll return a string to our bot, so the bot can display it on Telegram. Replace `def return_weather()` and `def return_rates()` with the following:
-
-```python
-def return_weather():
- return 'Hello. The current temperature in Cape Town is: '+str(curr_temp)+" celsius."
+We've created two helper functions: `get_weather()` and `get_exchange_rates()`. These functions make requests to the third-party APIs and then format the JSON response into a string that the bot can send back to users. The `load_dotenv()` call above loads the variables from our `.env` as environment variables.
+### Creating the Bot
-def return_rates():
- return "Hello. Today, USD conversion rates are as follows: USD->CAD = "+str(cad_rate)+", USD->EUR = "+str(eur_rate)+", USD->ZAR = "+str(zar_rate)
+Now we can get to creating the actual bot. At the top of the `bot.py` file, add this line:
+```python
+from telegram.ext import Application, CommandHandler
```
-Now, replace the `return_weather()` and `return_rates()` function calls with the code below:
+From the `python-telegram-bot` library, we import two classes: `Application` and `CommandHandler`. Now we can create our `main()` method like this:
+
```python
def main():
- TOKEN = "YOUR-BOT-TOKEN-HERE"
+ TOKEN = os.getenv('BOT_TOKEN')
application = Application.builder().token(TOKEN).build()
weather_handler = CommandHandler("weather", weather)
@@ -169,114 +171,124 @@ def main():
if __name__ == '__main__':
main()
-
```
At the top of our new `main` method, which will be called when this file is run, we instantiate `application`, an instance of the Telegram library's [`Application`](https://docs.python-telegram-bot.org/en/stable/telegram.ext.application.html) class. This object will retrieve commands sent to our bot and handle them using the appropriate handlers we define.
-Next, we create three different `CommandHandler` classes, one for each command that can be sent to our bot: `/start`, `/weather` and `/currency`. We pass two arguments into each instantiation: the command text (without the preceding `/`), and a function to call. For example, when a user enters the command `/weather`, the `weather()` function will be called.
+Next, we create three different `CommandHandler` classes, one for each command that can be sent to our bot: `/start`, `/weather`, and `/currency`. We pass two arguments into each instantiation: the command text (without the preceding `/`) and a function to call. For example, when a user enters the command `/weather`, the `weather()` function will be called.
-Let's define that function and the other two. Just above `def main()`, enter the following three function definitions:
+Let's define these functions. Just above `def main()`, enter the following three function definitions:
```python
async def weather(update, context):
- await context.bot.send_message(chat_id=update.effective_chat.id, text=return_weather())
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=get_weather())
async def currency(update, context):
- await context.bot.send_message(chat_id=update.effective_chat.id, text=return_rates())
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=get_exchange_rates())
async def start(update, context):
await context.bot.send_message(chat_id=update.effective_chat.id, text="Hi! I respond to /weather and /currency. Try them!")
```
-Each of these functions calls the `python-telegram-bot` function `send_message()` with the ID of the current chat and the appropriate text, either returned from one of our other functions or specified as a string. Note that these functions are now `async` and use `await` when calling bot methods - this is required in the newer version of the library. The `update` and `context` arguments are supplied automatically by the application.
+Each function calls the `python-telegram-bot` function `send_message()` with the ID of the current chat and the appropriate text, either returned from one of our other functions or specified as a string. Note that these functions are now `async` and use `await` when calling bot methods - this is required in the newer version of the library. The `update` and `context` arguments are supplied automatically by the application.
Back in our `main()` function, we use `application.add_handler` to add all three handlers to our application.
-Finally, `application.run_polling()` will begin [_polling_](https://en.wikipedia.org/wiki/Polling_\(computer_science\)) for updates from Telegram. This means our code will regularly ask Telegram's servers if any commands have been sent to it. Upon receiving commands, the appropriate handler will be invoked.
+Finally, `application.run_polling()` will begin [_polling_](https://en.wikipedia.org/wiki/Polling_\(computer_science\)) for updates from Telegram. This means our code will regularly ask Telegram's servers whether any commands have been sent to it. Upon receiving commands, the appropriate handler will be invoked.
-The code `bot.py` file should now look like the code below. Once again, make sure to replace `YOUR-URL-HERE` with the URL of the API you created in the API tutorial.
+The code `bot.py` file should now look like the code below. Make sure you add your API keys to the `.env` file in the same directory.
```python
-from telegram.ext import Application, CommandHandler
+import os
import requests
+from telegram.ext import Application, CommandHandler
+from dotenv import load_dotenv
+
+load_dotenv()
+EXCHANGE_API_KEY = os.getenv('EXCHANGE_API_KEY')
+WEATHER_API_KEY = os.getenv('WEATHER_API_KEY')
-url = 'YOUR-URL-HERE/get'
-data = requests.get(url) # requests data from API
-data = data.json() # converts return data to json
+EXCHANGE_URL = f'https://openexchangerates.org/api/latest.json?app_id={EXCHANGE_API_KEY}'
+EXCHANGE_PARAMS = {'symbols': 'ZAR,EUR,CAD'}
-# Retrieve values from API
-curr_temp = data['curr_temp']
-cad_rate = data['usd_rates']['CAD']
-eur_rate = data['usd_rates']['EUR']
-zar_rate = data['usd_rates']['ZAR']
+WEATHER_URL = f'http://api.weatherstack.com/current?access_key={WEATHER_API_KEY}'
+WEATHER_PARAMS = {'query': 'Cape Town'}
-def return_weather():
- return'Hello. The current temperature in Cape Town is: '+str(curr_temp)+" celsius."
+def get_weather():
+ """Fetch current weather from external API"""
+ response = requests.get(WEATHER_URL, params=WEATHER_PARAMS)
+ curr_temp = response.json()['current']['temperature']
+ return f'Hello. The current temperature in Cape Town is: {curr_temp} celsius.'
+
+
+def get_exchange_rates():
+ """Fetch exchange rates from external API"""
+ response = requests.get(EXCHANGE_URL, params=EXCHANGE_PARAMS)
+ rates = response.json()['rates']
+ cad_rate, eur_rate, zar_rate = rates['CAD'], rates['EUR'], rates['ZAR']
+ return f'Hello. Today, USD conversion rates are as follows: USD->CAD = {cad_rate}, USD->EUR = {eur_rate}, USD->ZAR = {zar_rate}'
-def return_rates():
- return "Hello. Today, USD conversion rates are as follows: USD->CAD = "+str(cad_rate)+ ", USD->EUR = "+str(eur_rate)+", USD->ZAR = "+str(zar_rate)
async def weather(update, context):
- await context.bot.send_message(chat_id=update.effective_chat.id, text=return_weather())
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=get_weather())
+
async def currency(update, context):
- await context.bot.send_message(chat_id=update.effective_chat.id, text=return_rates())
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=get_exchange_rates())
+
async def start(update, context):
- await context.bot.send_message(chat_id=update.effective_chat.id, text='Hi! I respond to /weather and /currency. Try these!')
+ await context.bot.send_message(chat_id=update.effective_chat.id, text='Hi! I respond to /weather and /currency. Try them!')
+
def main():
- TOKEN = "YOUR-BOT-TOKEN-HERE"
+ TOKEN = os.getenv('BOT_TOKEN')
application = Application.builder().token(TOKEN).build()
-
+
weather_handler = CommandHandler('weather', weather)
- currency_handler = CommandHandler('currency',currency)
- start_handler = CommandHandler('start',start)
-
+ currency_handler = CommandHandler('currency', currency)
+ start_handler = CommandHandler('start', start)
+
application.add_handler(weather_handler)
application.add_handler(currency_handler)
application.add_handler(start_handler)
-
+
application.run_polling()
+
if __name__ == '__main__':
main()
```
Below is a conversation with a bot created using this program. Run `bot.py` and try it out yourself.
-
Telegram Bot Conversation
+
Telegram Bot Conversation
We won't be able to send messages to our bot if this program isn't running, so hosting it on Code Capsules will allow us to interact with the bot without having to keep this code permanently running on our development PC.
While we could deploy our bot to Code Capsules in its current state, there is a downside to our current implementation that we should remedy first.
-### Polling versus Webhooks
+### Polling Versus Webhooks
-There are two ways for our `bot.py` file to receive commands sent to it on Telegram. Currently, the code polls Telegram constantly, regardless of whether the bot is in use. If we hosted this current version on Code Capsules, we would be wasting bandwidth, as the vast majority of polls would return nothing.
+There are two ways for our `bot.py` file to receive commands that have been sent to it on Telegram. Currently, the code polls Telegram constantly, regardless of whether the bot is in use. If we hosted this current version on Code Capsules, we would waste bandwidth, because the vast majority of polls would return nothing.
Instead of polling Telegram for changes, we can create a [_webhook_](https://en.wikipedia.org/wiki/Webhook). This will allow us to receive commands as they are sent by Telegram users, without having to continuously ask Telegram servers for them.
We'll set up a webhook by telling Telegram to send commands sent to our bot account to our bot's Code Capsules URL. Our application will then process the command using the appropriate handler and send back the requested information.
-#### Creating a webhook
+#### Creating a Webhook
-To set up the webhook, replace the line `application.run_polling()` in the `main` function with the code below:
+To set up the webhook, replace the line `application.run_polling()` in the `main` function with the following code:
```python
PORT = int(os.environ.get('PORT', '443'))
- HOOK_URL = 'YOUR-CODECAPSULES-URL-HERE' + '/' + TOKEN
+ HOOK_URL = f"https://{os.getenv('APP_URL')}/{TOKEN}"
application.run_webhook(listen='0.0.0.0', port=PORT, url_path=TOKEN, webhook_url=HOOK_URL)
```
-Make sure to add this line to the top of the file:
-
-```python
-import os
-```
+The `APP_URL` environment variable will be automatically set when the Capsule finishes building on Code Capsules.
And install Telegram webhooks:
@@ -287,35 +299,14 @@ pip install "python-telegram-bot[webhooks]"
Here we start a webhook that will listen on our Code Capsules URL at TCP port 443 and with the path of our token. Thus, Telegram will relay commands sent to our bot to the following URL:
```
-https://YOUR-CODECAPSULES-SUBDOMAIN.codecapsules.io:443/TOKEN
+https://YOUR-CAPSULE-PUBLIC-URL/TOKEN
```
If you've completed some of our other backend tutorials, you will be familiar with setting up web servers that receive `GET` and `POST` requests to different routes. You can think of a webhook as a very simple HTTP server that is intended to be used by bots and automated services rather than humans.
-### Preparing For Deployment
-
-Before we push our code to GitHub and deploy it on Code Capsules, we need to make one small code change and create some files.
-
-#### Creating an API key environment variable
-
-Because we'll push our code to GitHub, we need to hide our bot's authentication key. If we don't, anyone could use our authentication key and take control of our bot.
-
-Replace this line
+### Preparing for Deployment
-```python
-TOKEN = "YOUR-BOT-TOKEN-HERE"
-```
-
-with the below
-
-```python
-import os
-TOKEN = os.getenv('BOTAPIKEY')
-```
-
-`os.getenv('BOTAPIKEY')` will look for an [environment variable](https://medium.com/chingu/an-introduction-to-environment-variables-and-how-to-use-them-f602f66d15fa) with the name "BOTAPIKEY". When we host our bot on Code Capsules, we'll set this environment variable to the key we received from the BotFather.
-
-With that done, we must now create some files before we can push our code to GitHub and deploy it on Code Capsules.
+Before we push our code to GitHub and deploy it on Code Capsules, we need to create some files.
#### Creating a Procfile and requirements.txt
@@ -323,42 +314,40 @@ Code Capsules requires a couple of files to deploy our application: `Procfile` a
To create the `Procfile`:
-1. Navigate to the directory containing the `bot.py` file and enter the virtual environment.
+1. Navigate to the directory containing the `bot.py` file and activate the virtual environment.
2. Create a file named `Procfile` (with no file extension).
3. Open `Procfile`, enter `web: python3 bot.py`, and save the file.
In the same directory, open a terminal and activate the virtual environment. Then enter `pip3 freeze > requirements.txt` to generate a list of requirements for our Code Capsules server.
-Now we can push our code to GitHub. Create a GitHub repository and send the `requirements.txt`, `Procfile`, and `bot.py` files to the repository.
+Now we can push our code to GitHub. Create a GitHub repository, then commit and push the `requirements.txt`, `Procfile`, and `bot.py` files to the repository. Make sure **not** to include the virtual environment files and the `.env` file, because these contain your sensitive keys. You can do so by creating a `.gitignore` file with the following contents:
+
+```
+venv/
+.env
+```
### Deploying the Bot to Code Capsules
-With all of the files sent to GitHub, let's deploy the bot to Code Capsules:
+With all of the necessary files pushed to GitHub, let's deploy the bot to Code Capsules. Log in to Code Capsules and create a Team and Space as necessary.
-1. Log in to Code Capsules and create a Team and Space as necessary.
-2. Link Code Capsules to the GitHub repository created previously.
-3. Enter your Code Capsules Space.
-4. Create a new Capsule, selecting the "Backend" capsule type.
-5. Select the GitHub repository containing the bot – leave "Repo subpath" empty and click "Next".
-6. Leave the "Run Command" blank and click "Create Capsule".
+1. Click the add Capsule **+** button in your space.
+2. Choose **Backend** for the Capsule type, select your Team, and Space if not already populated.
+3. Choose your payment plan and click **Next**.
+4. Click the **Configure Git for Code Capsules** button and give access to the repository you created for the bot.
+5. Click **Next**.
+6. Leave the **Run Command** blank.
+7. Click **Create Capsule**.
-We haven't supplied our webhook a URL yet, and we still need to create an environment variable for our bot's authorisation token. To create an environment variable:
+We still need to create environment variables for our bot's authorization and API tokens. To create the environment variables:
1. Navigate to your Capsule.
-2. Click the "Config" tab.
-3. Add an environment variable with the name "BOTAPIKEY" and give it your bot's API key as a value. Make sure to hit the "Update Capsule" button after adding the variable.
+2. Click the **Config** tab.
+3. Add environment variables in the same form as our `.env` file. Add three environment variables: `BOT_TOKEN`, `WEATHER_API_KEY`, and `EXCHANGE_API_KEY`.
Bot API Key Environment Variable
-Next, let's supply our webhook with the correct domain.
-
-1. Navigate to the "Details" tab.
-2. Copy the domain found under "Domains".
-3. Open the `bot.py` file and find the line `HOOK_URL = 'YOUR-CODECAPSULES-URL-HERE' + '/' + TOKEN`.
-4. Replace "YOUR-CODECAPSULES\_URL" with the domain just copied.
-5. Commit and push these changes to GitHub.
-
-After pushing these changes, the Capsule will rebuild. Once this is done, the bot is ready. Give it a try!
+Once this is done, the Capsule will restart and the bot is ready. Give it a try!
### Further Reading