We write a Telegram-bot on Go to search Wikipedia

Table of contents [/b]
 
Create a bot
 
We write the code
 
Expand the bot
 
Conclusion
 
 
Now telegrams are very popular and writing bots for him has become a kind of hello world of our days, therefore, at the thought of what you can write now, many immediately think of writing telegrams for the bot.
 
As a student, like all students, I often visit Wikipedia in parallel, giving time and a telegram. It was decided to find a way to combine the location in the telegram and the opportunity to find the material I wanted in Wikipedia, in fact, this bot appeared (at the time of writing the bot, I did not even suspect that there is a bot for the same purpose from the telegraph team).
BotFather and create a new bot.
 
We write a Telegram-bot on Go to search Wikipedia
 
Next, we add a command with which we can get the number of users who used the bot.
 

 
We write the code
 
Since our bot will live inside the container docker, we will immediately set the environment variables to set the parameters without entering the container.
 
From non-standard libraries we will need.
 
go get github.com/Syfaro/telegram-bot-api
 
To work with TelegramAPI.
 
go get github.com/lib/pq
 
To work with Postgres DB.
 
Create the structures.
 
type SearchResults struct {
ready bool
Query string
Results[]Result
}
type Result struct {
Name, Description, URL string
}

 
Pay attention to the field
ready bool
, if the structure is empty then the value will be
false
.
 
Then we enter the data into the structures.
 
func (sr * SearchResults) UnmarshalJSON (bs[]byte) error {
array: =[]interface {} {}
if err: = json.Unmarshal (bs, & array); err! = nil {
return err
}
sr.Query = array[0]. (string)
for i: = range array[1]. ([]interface {}) {
sr.Results = append (sr.Results, Result {
? array[1] ([] interface {})[i](string),
array[2] ([].wdw)[i](string ),
? array[3] ([] interface {})[i](string),
})
}
return nil
}

 
Now we need a function that will send and receive data.
 
func wikipediaAPI (request string) (answer[]string) {
//Create a slice for 3 elements
s: = make ([]string, 3)
//Send the request to
if response, err: = http.Get (request); err! = nil {
log.Fatal (err)
} else {
defer response.Body.Close ()
//Read the answer
contents, err: = ioutil.ReadAll (response.Body)
if err! = nil {
log.Fatal (err)
}
//Send the data to the structure
sr: = & SearchResults {}
if err = json.Unmarshal ([]byte (contents), sr); err! = nil {
s[0]= "Something going wrong, try to change your question"
}
//Check if our structure is not empty
if! sr.ready {
s[0]= "Something going wrong, try to change your question"
}
//We pass through our structure and send the data to the slice with the answer
for i: = range sr.Results {
s[i]= sr.Results[i].URL
}
}
return s
}

 
The full code is [/b]
type SearchResults struct {
ready bool
Query string
Results[]Result
}
type Result struct {
Name, Description, URL string
}
func (sr * SearchResults) UnmarshalJSON (bs[]byte) error {
array: =[]interface {} {}
if err: = json.Unmarshal (bs, & array); err! = nil {
return err
}
sr.Query = array[0]. (string)
for i: = range array[1]. ([]interface {}) {
sr.Results = append (sr.Results, Result {
? array[1] ([] interface {})[i](string),
array[2] ([].wdw)[i](string ),
? array[3] ([] interface {})[i](string),
})
}
return nil
}
func wikipediaAPI (request string) (answer[]string) {
//Create a slice for 3 elements
s: = make ([]string, 3)
//Send the request to
if response, err: = http.Get (request); err! = nil {
log.Fatal (err)
} else {
defer response.Body.Close ()
//Read the answer
contents, err: = ioutil.ReadAll (response.Body)
if err! = nil {
log.Fatal (err)
}
//Send the data to the structure
sr: = & SearchResults {}
if err = json.Unmarshal ([]byte (contents), sr); err! = nil {
s[0]= "Something going wrong, try to change your question"
}
//Check if our structure is not empty
if! sr.ready {
s[0]= "Something going wrong, try to change your question"
}
//We pass through our structure and send the data to the slice with the answer
for i: = range sr.Results {
s[i]= sr.Results[i].URL
}
}
return s
}

 
In view of the fact that we send the URL, we need to convert the message from the user to the URL part. Why this is necessary, then that the user can send the bot not one and two words through a space, but we need to replace the space so that it becomes part of the URL. This is done by the function urlEncoded.
 
//Convert the query to use as part of the URL
func urlEncoded (str string) (string, error) {
u, err: = url.Parse (str)
if err! = nil {
return "", err
}
return u.String (), nil
}

 
Now we need to interact with the database. Create variables in which we will store the data of environment variables for connection to the database.
 
var host = os.Getenv ("HOST")
var port = os.Getenv ("PORT")
var user = os.Getenv ("USER")
var password = os.Getenv ("PASSWORD")
var dbname = os.Getenv ("DBNAME")
var sslmode = os.Getenv ("SSLMODE")
var dbInfo = fmt.Sprintf ("host =% s port =% s user =% s password =% s dbname =% sslmode =% s", host, port, user, password, dbname, sslmode)

 
Also we write function which will create the table in our DB.
 
//Create the users table in the database when
is connected to it. func createTable () error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Create the users table
if _, err = db.Exec (`CREATE TABLE users (ID SERIAL PRIMARY KEY, TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP, USERNAME TEXT, CHAT_ID INT, MESSAGE TEXT, ANSWER TEXT); err! = nil {
return err
}
return nil
}

 
We created the table, and we need to enter data into it, this will be done by the following function.
 
//Collect the data received by the bot
func collectData (username string, chatid int6? message string, answer[]string) error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Convert the slice with the answer to line
answ: = strings.Join (answer, ",")
//Create the SQL query
dаta: = INSERT INTO users (username, chat_id, message, answer) VALUES ($ ? $ ? $ ? $ 4),
//Execute our SQL query
if _, err = db.Exec (data, `@` + username, chatid, message, answ); err! = nil {
return err
}
return nil
}

 
Also, let's write a function that will count the number of unique users who wrote to the bot to give this number to users if they send the bot the right command.
 
func getNumberOfUsers () (int6? error) {
var count int64
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return ? err
}
defer db.Close ()
//Send the request to the database to count the number of unique users
row: = db.QueryRow ("SELECT COUNT (DISTINCT username) FROM users;")
err = row.Scan (& count)
if err! = nil {
return ? err
}
return count, nil
}

 
The full code is [/b]
var host = os.Getenv ("HOST")
var port = os.Getenv ("PORT")
var user = os.Getenv ("USER")
var password = os.Getenv ("PASSWORD")
var dbname = os.Getenv ("DBNAME")
var sslmode = os.Getenv ("SSLMODE")
var dbInfo = fmt.Sprintf ("host =% s port =% s user =% s password =% s dbname =% sslmode =% s", host, port, user, password, dbname, sslmode)
//Collect the data received by the bot
func collectData (username string, chatid int6? message string, answer[]string) error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Convert the slice with the answer to line
answ: = strings.Join (answer, ",")
//Create the SQL query
dаta: = INSERT INTO users (username, chat_id, message, answer) VALUES ($ ? $ ? $ ? $ 4),
//Execute our SQL query
if _, err = db.Exec (data, `@` + username, chatid, message, answ); err! = nil {
return err
}
return nil
}
//Create the users table in the database when
is connected to it. func createTable () error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Create the users table
if _, err = db.Exec (`CREATE TABLE users (ID SERIAL PRIMARY KEY, TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP, USERNAME TEXT, CHAT_ID INT, MESSAGE TEXT, ANSWER TEXT); err! = nil {
return err
}
return nil
}
func getNumberOfUsers () (int6? error) {
var count int64
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return ? err
}
defer db.Close ()
//Send the query to the database to count the number of unique users
row: = db.QueryRow ("SELECT COUNT (DISTINCT username) FROM users;")
err = row.Scan (& count)
if err! = nil {
return ? err
}
return count, nil
}

 
We connect all together
 
Create a bot.
 
//Create the bot
bot, err: = tgbotapi.NewBotAPI (os.Getenv ("TOKEN"))
if err! = nil {
panic (err)
}
//Set the update time to
u: = tgbotapi.NewUpdate (0)
u.Timeout = 60
//Get updates from bot
updates, err: = bot.GetUpdatesChan (u)
for update: = range updates {
if update.Message == nil {
continue
}

 
We need to check that it's the text message that comes from the user that our if will do.
 
if reflect.TypeOf (update.Message.Text) .Kind () == reflect.String && update.Message.Text! = ""
 
In the event that a user sends a sticker or video, we will send a message to the user that will indicate to him that only text messages are suitable.
 
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Use the words for search.")
bot.Send (msg)

 
Inside if we put a switch that will catch the commands:
 
/start
 
case "/start":
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Hi, i'm a wikipedia bot, i can search information in wikipedia.")
bot.Send (msg)

 
/number_of_users
 
case "/number_of_users":
if os.Getenv ("DB_SWITCH") == "on" {
//Assign the number of users using the bot to the num variable
num, err: = getNumberOfUsers ()
if err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error.")
bot.Send (msg)
}
//Create a string that contains the number of users using the bot
ans: = fmt.Sprintf ("% d people used me for search information in Wikipedia", num)
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, ans)
bot.Send (msg)
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database not connected, so i can not say you how many people used me.")
bot.Send (msg)
}

 
and default which will interact with wikipedia and bd
 
default:
//Set the language for search in Wikipedia
language: = os.Getenv ("LANGUAGE")
//Create a url to search for
ms, _: = urlEncoded (update.Message.Text)
url: = ms
request: = "https: //" + language + ".wikipedia.org /w /api.php? action = opensearch & search =" + url + "& limit = 3 & origin = * & format = json"
//Sets the slice data with the response to the variable message
message: = wikipediaAPI (request)
if os.Getenv ("DB_SWITCH") == "on" {
//Send username, chat_id, message, answer to DB
if err: = collectData (update.Message.Chat.UserName, update.Message.Chat.ID, update.Message.Text, message); err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error, but bot still working.")
bot.Send (msg)
}
}
//Pass through the slice and send each element to the user
for _, val: = range message {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, val)
bot.Send (msg)
}
}

 
Create the main.
 
func main () {
time.Sleep (1 * time.Minute)
//Create the table
if os.Getenv ("CREATE_TABLE") == "yes" {
if os.Getenv ("DB_SWITCH") == "on" {
if err: = createTable (); err! = nil {
panic (err)
}
}
}
time.Sleep (1 * time.Minute)
//Call the bot
telegramBot ()
}

 
The full code is [/b]
func telegramBot () {
//Create the bot
bot, err: = tgbotapi.NewBotAPI (os.Getenv ("TOKEN"))
if err! = nil {
panic (err)
}
//Set the update time to
u: = tgbotapi.NewUpdate (0)
u.Timeout = 60
//Get updates from bot
updates, err: = bot.GetUpdatesChan (u)
for update: = range updates {
if update.Message == nil {
continue
}
//Verify that the text message
came from the user. if reflect.TypeOf (update.Message.Text) .Kind () == reflect.String && update.Message.Text! = "" {
switch update.Message.Text {
case "/start":
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Hi, i'm a wikipedia bot, i can search information in wikipedia.")
bot.Send (msg)
case "/number_of_users":
if os.Getenv ("DB_SWITCH") == "on" {
//Assign the number of users using the bot to the num variable
num, err: = getNumberOfUsers ()
if err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error.")
bot.Send (msg)
}
//Create a string that contains the number of users using the bot
ans: = fmt.Sprintf ("% d people used me for search information in Wikipedia", num)
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, ans)
bot.Send (msg)
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database not connected, so i can not say you how many people used me.")
bot.Send (msg)
}
default:
//Set the language for search in Wikipedia
language: = os.Getenv ("LANGUAGE")
//Create a url to search for
ms, _: = urlEncoded (update.Message.Text)
url: = ms
request: = "https: //" + language + ".wikipedia.org /w /api.php? action = opensearch & search =" + url + "& limit = 3 & origin = * & format = json"
//Sets the slice data with the response to the variable message
message: = wikipediaAPI (request)
if os.Getenv ("DB_SWITCH") == "on" {
//Send username, chat_id, message, answer to DB
if err: = collectData (update.Message.Chat.UserName, update.Message.Chat.ID, update.Message.Text, message); err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error, but bot still working.")
bot.Send (msg)
}
}
//Pass through the slice and send each element to the user
for _, val: = range message {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, val)
bot.Send (msg)
}
}
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Use the words for search.")
bot.Send (msg)
}
}
}
func main () {
time.Sleep (1 * time.Minute)
//Create the table
if os.Getenv ("CREATE_TABLE") == "yes" {
if os.Getenv ("DB_SWITCH") == "on" {
if err: = createTable (); err! = nil {
panic (err)
}
}
}
time.Sleep (1 * time.Minute)
//Call the bot
telegramBot ()
}

 
The final code is [/b]
package main
import (
"database /sql"
"encoding /json"
"fmt"
"github.com/Syfaro/telegram-bot-api"
_ "github.com/lib/pq"
"io /ioutil"
"log"
"net /http"
"net /url"
"os"
"reflect"
"strings"
"time"
)
type SearchResults struct {
ready bool
Query string
Results[]Result
}
type Result struct {
Name, Description, URL string
}
func (sr * SearchResults) UnmarshalJSON (bs[]byte) error {
array: =[]interface {} {}
if err: = json.Unmarshal (bs, & array); err! = nil {
return err
}
sr.Query = array[0]. (string)
for i: = range array[1]. ([]interface {}) {
sr.Results = append (sr.Results, Result {
? array[1] ([] interface {})[i](string),
array[2] ([].wdw)[i](string ),
? array[3] ([] interface {})[i](string),
})
}
return nil
}
func wikipediaAPI (request string) (answer[]string) {
//Create a slice for 3 elements
s: = make ([]string, 3)
//Send the request to
if response, err: = http.Get (request); err! = nil {
log.Fatal (err)
} else {
defer response.Body.Close ()
//Read the answer
contents, err: = ioutil.ReadAll (response.Body)
if err! = nil {
log.Fatal (err)
}
//Send the data to the structure
sr: = & SearchResults {}
if err = json.Unmarshal ([]byte (contents), sr); err! = nil {
s[0]= "Something going wrong, try to change your question"
}
//Check if our structure is not empty
if! sr.ready {
s[0]= "Something going wrong, try to change your question"
}
//We pass through our structure and send the data to the slice with the answer
for i: = range sr.Results {
s[i]= sr.Results[i].URL
}
}
return s
}
//Convert the query to use as part of the URL
func urlEncoded (str string) (string, error) {
u, err: = url.Parse (str)
if err! = nil {
return "", err
}
return u.String (), nil
}
var host = os.Getenv ("HOST")
var port = os.Getenv ("PORT")
var user = os.Getenv ("USER")
var password = os.Getenv ("PASSWORD")
var dbname = os.Getenv ("DBNAME")
var sslmode = os.Getenv ("SSLMODE")
var dbInfo = fmt.Sprintf ("host =% s port =% s user =% s password =% s dbname =% sslmode =% s", host, port, user, password, dbname, sslmode)
//Collect the data received by the bot
func collectData (username string, chatid int6? message string, answer[]string) error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Convert the slice with the answer to line
answ: = strings.Join (answer, ",")
//Create the SQL query
dаta: = INSERT INTO users (username, chat_id, message, answer) VALUES ($ ? $ ? $ ? $ 4),
//Execute our SQL query
if _, err = db.Exec (data, `@` + username, chatid, message, answ); err! = nil {
return err
}
return nil
}
//Create the users table in the database when
is connected to it. func createTable () error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Create the users table
if _, err = db.Exec (`CREATE TABLE users (ID SERIAL PRIMARY KEY, TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP, USERNAME TEXT, CHAT_ID INT, MESSAGE TEXT, ANSWER TEXT); err! = nil {
return err
}
return nil
}
func getNumberOfUsers () (int6? error) {
var count int64
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return ? err
}
defer db.Close ()
//Send the query to the database to count the number of unique users
row: = db.QueryRow ("SELECT COUNT (DISTINCT username) FROM users;")
err = row.Scan (& count)
if err! = nil {
return ? err
}
return count, nil
}
func telegramBot () {
//Create the bot
bot, err: = tgbotapi.NewBotAPI (os.Getenv ("TOKEN"))
if err! = nil {
panic (err)
}
//Set the update time to
u: = tgbotapi.NewUpdate (0)
u.Timeout = 60
//Get updates from bot
updates, err: = bot.GetUpdatesChan (u)
for update: = range updates {
if update.Message == nil {
continue
}
//Verify that the text message
came from the user. if reflect.TypeOf (update.Message.Text) .Kind () == reflect.String && update.Message.Text! = "" {
switch update.Message.Text {
case "/start":
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Hi, i'm a wikipedia bot, i can search information in wikipedia.")
bot.Send (msg)
case "/number_of_users":
if os.Getenv ("DB_SWITCH") == "on" {
//Assign the number of users using the bot to the num variable
num, err: = getNumberOfUsers ()
if err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error.")
bot.Send (msg)
}
//Create a string that contains the quantity users who used bot
ans: = fmt.Sprintf ("% d people used me for search information in Wikipedia", num)
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, ans)
bot.Send (msg)
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database not connected, so i can not say you how many people used me.")
bot.Send (msg)
}
default:
//Set the language for search in Wikipedia
language: = os.Getenv ("LANGUAGE")
//Create a url to search for
ms, _: = urlEncoded (update.Message.Text)
url: = ms
request: = "https: //" + language + ".wikipedia.org /w /api.php? action = opensearch & search =" + url + "& limit = 3 & origin = * & format = json"
//Sets the slice data with the response to the variable message
message: = wikipediaAPI (request)
if os.Getenv ("DB_SWITCH") == "on" {
//Send username, chat_id, message, answer to DB
if err: = collectData (update.Message.Chat.UserName, update.Message.Chat.ID, update.Message.Text, message); err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error, but bot still working.")
bot.Send (msg)
}
}
//Pass through the slice and send each element to the user
for _, val: = range message {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, val)
bot.Send (msg)
}
}
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Use the words for search.")
bot.Send (msg)
}
}
}
func main () {
time.Sleep (1 * time.Minute)
//Create the table
if os.Getenv ("CREATE_TABLE") == "yes" {
if os.Getenv ("DB_SWITCH") == "on" {
if err: = createTable (); err! = nil {
panic (err)
}
}
}
time.Sleep (1 * time.Minute)
//Call the bot
telegramBot ()
}

 
Let's separate the code into separate files to make it more readable.
 
wikipedia_api.go [/b]
package main
import (
"encoding /json"
"io /ioutil"
"log"
"net /http"
)
type SearchResults struct {
ready bool
Query string
Results[]Result
}
type Result struct {
Name, Description, URL string
}
func (sr * SearchResults) UnmarshalJSON (bs[]byte) error {
array: =[]interface {} {}
if err: = json.Unmarshal (bs, & array); err! = nil {
return err
}
sr.Query = array[0]. (string)
for i: = range array[1]. ([]interface {}) {
sr.Results = append (sr.Results, Result {
? array[1] ([] interface {})[i](string),
array[2] ([].wdw)[i](string ),
? array[3] ([] interface {})[i](string),
})
}
return nil
}
func wikipediaAPI (request string) (answer[]string) {
//Create a slice for 3 elements
s: = make ([]string, 3)
//Send the request to
if response, err: = http.Get (request); err! = nil {
log.Fatal (err)
} else {
defer response.Body.Close ()
//Read the answer
contents, err: = ioutil.ReadAll (response.Body)
if err! = nil {
log.Fatal (err)
}
//Send the data to the structure
sr: = & SearchResults {}
if err = json.Unmarshal ([]byte (contents), sr); err! = nil {
s[0]= "Something going wrong, try to change your question"
}
//Check if our structure is not empty
if! sr.ready {
s[0]= "Something going wrong, try to change your question"
}
//We pass through our structure and send the data to the slice with the answer
for i: = range sr.Results {
s[i]= sr.Results[i].URL
}
}
return s
}

 
url_encoder.go [/b]
package main
import (
"net /url"
)
//Convert the query to use as part of the URL
func urlEncoded (str string) (string, error) {
u, err: = url.Parse (str)
if err! = nil {
return "", err
}
return u.String (), nil
}

 
db.go [/b]
package main
import (
"database /sql"
"fmt"
"github.com/lib/pq"
"os"
"strings"
)
var host = os.Getenv ("HOST")
var port = os.Getenv ("PORT")
var user = os.Getenv ("USER")
var password = os.Getenv ("PASSWORD")
var dbname = os.Getenv ("DBNAME")
var sslmode = os.Getenv ("SSLMODE")
var dbInfo = fmt.Sprintf ("host =% s port =% s user =% s password =% s dbname =% sslmode =% s", host, port, user, password, dbname, sslmode)
//Collect the data received by the bot
func collectData (username string, chatid int6? message string, answer[]string) error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Convert the slice with the answer to line
answ: = strings.Join (answer, ",")
//Create the SQL query
dаta: = INSERT INTO users (username, chat_id, message, answer) VALUES ($ ? $ ? $ ? $ 4),
//Execute our SQL query
if _, err = db.Exec (data, `@` + username, chatid, message, answ); err! = nil {
return err
}
return nil
}
//Create the users table in the database when
is connected to it. func createTable () error {
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return err
}
defer db.Close ()
//Create the users table
if _, err = db.Exec (`CREATE TABLE users (ID SERIAL PRIMARY KEY, TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP, USERNAME TEXT, CHAT_ID INT, MESSAGE TEXT, ANSWER TEXT); err! = nil {
return err
}
return nil
}
func getNumberOfUsers () (int6? error) {
var count int64
//We connect to the database
db, err: = sql.Open ("postgres", dbInfo)
if err! = nil {
return ? err
}
defer db.Close ()
//Send the query to the database to count the number of unique users
row: = db.QueryRow ("SELECT COUNT (DISTINCT username) FROM users;")
err = row.Scan (& count)
if err! = nil {
return ? err
}
return count, nil
}

 
telegrambot.go [/b]
package main
import (
"fmt"
"github.com/Syfaro/telegram-bot-api"
"os"
"reflect"
"time"
)
func telegramBot () {
//Create the bot
bot, err: = tgbotapi.NewBotAPI (os.Getenv ("TOKEN"))
if err! = nil {
panic (err)
}
//Set the update time to
u: = tgbotapi.NewUpdate (0)
u.Timeout = 60
//Get updates from bot
updates, err: = bot.GetUpdatesChan (u)
for update: = range updates {
if update.Message == nil {
continue
}
//Verify that the text message
came from the user.       if reflect.TypeOf (update.Message.Text) .Kind () == reflect.String && update.Message.Text! = "" {
switch update.Message.Text {
case "/start":
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Hi, i'm a wikipedia bot, i can search information in wikipedia.")
bot.Send (msg)
case "/number_of_users":
if os.Getenv ("DB_SWITCH") == "on" {
//Assign the number of users using the bot to the num variable
num, err: = getNumberOfUsers ()
if err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error.")
bot.Send (msg)
}
//Create a string that contains the number of users using the bot
ans: = fmt.Sprintf ("% d people used me for search information in Wikipedia", num)
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, ans)
bot.Send (msg)
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database not connected, so i can not say you how many people used me.")
bot.Send (msg)
}
default:
//Set the language for search in Wikipedia
language: = os.Getenv ("LANGUAGE")
//Create a url to search for
ms, _: = urlEncoded (update.Message.Text)
url: = ms
request: = "https: //" + language + ".wikipedia.org /w /api.php? action = opensearch & search =" + url + "& limit = 3 & origin = * & format = json"
//Sets the slice data with the response to the variable message
message: = wikipediaAPI (request)
if os.Getenv ("DB_SWITCH") == "on" {
//Send username, chat_id, message, answer to DB
if err: = collectData (update.Message.Chat.UserName, update.Message.Chat.ID, update.Message.Text, message); err! = nil {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Database error, but bot still working.")
bot.Send (msg)
}
}
//Pass through the slice and send each element to the user
for _, val: = range message {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, val)
bot.Send (msg)
}
}
} else {
//Send the message
msg: = tgbotapi.NewMessage (update.Message.Chat.ID, "Use the words for search.")
bot.Send (msg)
}
}
}
func main () {
time.Sleep (1 * time.Minute)
//Create the table
if os.Getenv ("CREATE_TABLE") == "yes" {
if os.Getenv ("DB_SWITCH") == "on" {
if err: = createTable (); err! = nil {
panic (err)
}
}
}
time.Sleep (1 * time.Minute)
//Call the bot
telegramBot ()
}

 
Now when we have our bot write a Dockerfile for it.
 
FROM alpine
ENV LANGUAGE = "en"
COPY /code /code.
RUN apk add --no-cache ca-certificates &&
chmod + x code
EXPOSE 80 /tcp
CMD["./code" ]

 

And as docker-compose.yml so that we do not have to raise our hands in the database if the database will be on the same machine as the bot.


 
    version: '3.5'
services:
db:
image: postgres
environment:
POSTGRES_PASSWORD: test
bot:
image: trigun117 /wikipedia-telegram-bot
environment:
CREATE_TABLE: "yes"
DB_SWITCH: "on"
TOKEN:
HOST: db
PORT: 5432
USER: postgres
PASSWORD: test
DBNAME: postgres
SSLMODE: disable

 

Expand the bot


 

We deploy the bot on the local PC, Amazon Web Service: EC2 and RDS database, Google Cloud Platform: GCE and SQL database.


 

Docker-Compose


 

We paste into our docker-compose.yml a token for the bot.


 


 

And run docker-compose.


 


 

We wait about 3 minutes and our bot is ready to work.


 

Google Cloud Platform


 

Go to the SQL section in GCP and create a database.


 


 

In the allowed networks, we indicate that traffic can come from any source.


 


 

Now we have a DB and an IP to connect to it.


 


 

Next, go to the Compute Engine tab and create an instance on which the bot will be deployed.


 


 

Since the Google Compute Engine has such a thing as Automation, which allows us to execute any commands on the instance after its creation without our participation, we will use it for a comfortable sweep.


 

On the Automation tab, we will specify the commands for installing the docker and launching the container with our bot.


 


 
    curl -s https://raw.githubusercontent.com/trigun117/TelegramBot-Go/master/installdocker.sh | bash &&
sudo docker run
-e CREATE_TABLE = yes
-e DB_SWITCH = on
-e TOKEN = 497014514: AAGKayv3tUxNrFWCmqtEIxKAS1TMvhXGdLE
-e HOST = ???.57
-e PORT = 5432
-e USER = postgres
-e PASSWORD = postgres
-e DBNAME = postgres
-e SSLMODE = disable
-d trigun117 /wikipedia-telegram-bot

 

We are waiting about 5 minutes and our bot is ready for use.


 

Amazon Web Service


 

Create a database.


 


 


 


 

We wait while the DB is created and we pass to it for reception endpoint and the further connection to a DB by means of it.


 


 

Next, go to the tab EC? Security Groups and select the group in which our database is located.


 


 

At the bottom of the Inbound tab, click Edit and change our rule so that incoming connections go from any source


 


 

and click Save.


 

On the Instances tab, create a new instance and go through all the creation steps. After the creation, we connect in a convenient way for you and execute the command that we have already seen before.


 


 

We are waiting about 3 minutes and our bot is ready to work.


 
    curl -s https://raw.githubusercontent.com/trigun117/TelegramBot-Go/master/installdocker.sh | bash &&
sudo docker run
-e CREATE_TABLE = yes
-e DB_SWITCH = on
-e TOKEN = 497014514: AAGKayv3tUxNrFWCmqtEIxKAS1TMvhXGdLE
-e HOST = test-habr.c2ugekubflbp.eu-west-2.rds.amazonaws.com
-e PORT = 5432
-e USER = postgres
-e PASSWORD = postgres
-e DBNAME = postgres
-e SSLMODE = disable
-d trigun117 /wikipedia-telegram-bot

 
Conclusion
 
To summarize, I want to say that writing a bot on Go was quite an interesting and cognitive experience. And also gave an opportunity to pull up old and get new knowledge.
 
Repository on GitHub
 
Wikipedia Search RU
 
Wikipedia Search EN
 
Thank you for your attention and invite everyone to share their opinions and thoughts in the comments.
+ 0 -

Add comment