Usage
Setting up telegram token and chat_id
In all examples of this section, it is assumed for simplicity that you set telegram token and chat_id in TELEGRAM_BOT_TOKEN and TELEGRAM_BOT_CHAT_ID environment variables correspondingly. Recommended way to do it, by using ConfigEnv.jl. You should create file .env
# .env
TELEGRAM_BOT_TOKEN = 123456:ababababababbabababababbababaab
TELEGRAM_BOT_CHAT_ID = 1234567And at the beginning of the application, populate ENV with the dotenv function. Telegram.jl methods will use these variables automatically. Alternatively (but less secure) you can provide token and chat_id via TelegramClient constructor.
In order to get token itself, you should follow this instruction. Just talk to BotFather and after few simple questions you will receive the token.
Easiest way to obtain chat_id is through running simple print bot.
using Telegram
using ConfigEnv
dotenv()
run_bot() do msg
    println(msg)
endAfter running this script just open chat with your newly created bot and send it any message. You will receive something like this
{
   "update_id": 87654321,
     "message": {
                   "message_id": 6789,
                         "from": {
                                               "id": 123456789,
                                           "is_bot": false,
                                    "language_code": "en"
                                 },
                         "chat": {
                                            "id": 123456789,
                                          "type": "private"
                                 },
                         "date": 1592767594,
                         "text": "Hello"
                }
}In this example, field message.chat.id = 123456789 is necessary chat_id which shoud be stored in TELEGRAM_BOT_CHAT_ID variable.
Initializing TelegramClient
If you set ENV variables TELEGRAM_BOT_TOKEN and TELEGRAM_BOT_CHAT_ID, then telegram client use them to run all commands. Alternatively you can initialize client by passing required token parameter.
using Telegram
tg = TelegramClient("TELEGRAM BOT TOKEN")Since Telegram.jl was built with the first-class support of the Telegram as a notification system, you can pass chat_id variable, which will be used then by default in every function related to messaging
using Telegram
tg = TelegramClient("TELEGRAM BOT TOKEN"; chat_id = "DEFAULT TELEGRAM CHAT ID")
Telegram.sendMessage(tg, text = "Hello world") # will send "Hello world" message 
                                               # to chat defined in `tg.chat_id`Also, by default new TelegramClient is used globally in all API Reference related functions, so you can run commands like
using Telegram
using ConfigEnv
dotenv()
Telegram.sendMessage(text = "Hello world")which will send "Hello world" message to the chat defined by ENV["TELEGRAM_BOT_CHAT_ID"] variable with the bot defined by ENV["TELEGRAM_BOT_TOKEN"] variable.
In order to override this behaviour you can set use_globally argument of TelegramClient function. To set previously defined client as a global, you should use useglobally!.
Using Telegram Bot API
Due to the rather large number of functions defined in API Reference, they are hidden behind module declaration, so by default they should be prefixed with Telegram.
using Telegram
using ConfigEnv
dotenv()
Telegram.getMe() # returns information about botIf this is inconvenient for some reason, you can either introduce new and short constant name, like this
using Telegram
using ConfigEnv
const TG = Telegram
dotenv()
TG.getMe()or you can import all telegram Bot API by using Telegram.API, in this scenario you do not need to use any prefixes
using Telegram
using Telegram.API
using ConfigEnv
dotenv()
getMe()In what follows we will use latter approach.
Sending messages
If you set telegram client globally with chat_id as it is described in previous sections, then you can use message related function from API Reference, omitting chat_id argument. For example, this is how you can use most basic sendMessage function
using Telegram, Telegram.API
using ConfigEnv
dotenv()
sendMessage(text = "Hello world")Of course if you have more than one client or writing a bot which should communicate in multiple chats, you can add this parameters to function calls and they will override default values, for example
# .env
TG_TOKEN = 123456:asdasd
TG_TOKEN2 = 546789:zxczxc
TG_CHAT_ID = 1234using Telegram, Telegram.API
using ConfigEnv
dotenv()
tg1 = TelegramClient(ENV["TG_TOKEN"]; chat_id = ENV["TG_CHAT_ID"])
tg2 = TelegramClient(ENV["TG_TOKEN2"])
sendMessage(tg1, text = "I am bot number 1", chat_id = 12345)
sendMessage(tg2, text = "I am bot number 2", chat_id = 54321)will send messages from two different telegram bots to two different chats. It is useful for example, when you have telegram bot communicating with users and at the same time error logs of this bot is being sent to another chat by error reporting bot.
In addition to text messages you can also send any sort of IO objects: images, audio, documents and so on. For example to send picture you can do something like this
using Telegram, Telegram.API
using ConfigEnv
dotenv()
open("picture.jpg", "r") do io
    sendPhoto(photo = io)
end
# or if you want it quick and dirty
sendPhoto(photo = open("picture.jpg", "r"))Data sending is not limited by files only, you can send memory objects as well, in this case you should give them name in the form of Pair
using Telegram, Telegram.API
using ConfigEnv
dotenv()
io = IOBuffer()
print(io, "Hello world!")
sendDocument(document = "hello.txt" => io)Logging
You can also use Telegram.jl as a logging system, for this you are provided with special TelegramLogger structure. It accepts TelegramClient object which must have initialized chat_id parameter
using Telegram
using Logging
using ConfigEnv
dotenv()
tg = TelegramClient()
tg_logger = TelegramLogger(tg; async = false)
with_logger(tg_logger) do
    @info "Hello from telegram logger!"
endBut even better it is used together with LoggingExtras.jl package, which can demux log messages and send critical messages to telegram backend without interrupting normal logging flow
using Telegram
using Logging, LoggingExtras
using ConfigEnv
dotenv()
tg = TelegramClient()
tg_logger = TelegramLogger(tg; async = false)
demux_logger = TeeLogger(
    MinLevelLogger(tg_logger, Logging.Error),
    ConsoleLogger()
)
global_logger(demux_logger)
@warn "It is bad"        # goes to console
@info "normal stuff"     # goes to console
@error "THE WORSE THING" # goes to console and telegram
@debug "it is chill"     # goes to consoleThis way, just by adding few configuration lines, you can have unchanged logging system with telegram instant messaging if anything critically important happened.
Also, take notion of async argument of TelegramLogger. There are two modes of operating, usual and asynchronous. Second is useful if you have long running program and you do not want it to pause and send telegram message, so usual scenario is like this
while true
    try
        # do some stuff
    catch err
        @error err
        # process error
    end
end    But asynchronous mode has it's drawbacks, consider this for example
try
    sqrt(:a)
catch err
    @error err
endif you run this snippet (with proper logger initialization) from command line, main thread  stops before asynchronous message to telegram is sent. In such cases, it make sense to set async = false
Bots
Echo bot
With the help of run_bot method it's quite simple to set up simple telegram bots.
using Telegram, Telegram.API
using ConfigEnv
dotenv()
# Echo bot
run_bot() do msg
    sendMessage(text = msg.message.text, chat_id = msg.message.chat.id)
endTurtle graphics bot
In this example we build more advanced bot, which is generating turtle graphics with the help of Luxor.jl package.
In addition to previous echo bot, this can do the following
- Generate and send images in memory, without storing them in file system
- Generate virtual keyboard, which can be used by users to make input easier
using Telegram, Telegram.API
using ConfigEnv
using Luxor
dotenv()
"""
    draw_turtle(angles::AbstractVector)
    
Draw turtle graphics, where turtle is moving in spiral, on each step rotating
on next angle from `angles` vector. Vector `angles` is repeated cyclically.
"""
function draw_turtle(angles)
    d = Drawing(600, 400, :png)
    origin()
    background("midnightblue")
    ๐ข = Turtle() # you can type the turtle emoji with \:turtle:
    Pencolor(๐ข, "cyan")
    Penwidth(๐ข, 1.5)
    n = 5.0
    dn = 1.0/length(angles)*0.7
    for i in 1:400
        for angle in angles
            Forward(๐ข, n)
            Turn(๐ข, angle)
            n += dn
        end
        HueShift(๐ข)
    end
    finish()
    return d
end
"""
    build_keyboard()
    
Generates [telegram keyboard](https://core.telegram.org/bots#keyboards) in the
form of 3x3 grid of buttons.
"""
function build_keyboard()
    keyboard = Vector{Vector{String}}()
    for x in 1:3
        row = String[]
        for y in 1:3
            s = join(string.(Int.(round.(rand(rand(1:4)) * 360))), " ")
            push!(row, s)
        end
        push!(keyboard, row)
    end
    return Dict(:keyboard => keyboard, :one_time_keyboard => true)
end
run_bot() do msg
    message = get(msg, :message, nothing)
    message === nothing && return nothing
    text = get(message, :text, "")
    chat = get(message, :chat, nothing)
    chat === nothing && return nothing
    chat_id = get(chat, :id, nothing)
    chat_id === nothing && return nothing
    
    if match(r"^[0-9 \.]+$", text) !== nothing
        angles = parse.(Float64, split(text, " "))
        turtle = draw_turtle(angles)
        sendPhoto(photo = "turtle.png" => turtle.buffer, reply_markup = build_keyboard(), chat_id = chat_id)
    else
        sendMessage(text = "Unknown command, please provide turtle instructions in the form `angle1 angle2` or use keyboard", reply_markup = build_keyboard(), chat_id = chat_id, parse_mode = "MarkdownV2")
    end
end