Automating Telegram Messages with Powershell

Automating Telegram Messages with Powershell

In this post I will go through automating Telegram Messages with Powershell, including a full script as an example.

Truth to be told, I’ve installed, and first used, Telegram about 3 hours before writing this post, but I saw so much potentials that I couldn’t wait to publish this. Consider also that I was actually after something similar for WhatsApp, but there’s no official API from them yet.

So because I’m such a noob here, I will actually go through the steps I’ve followed to get a Bot configured to work. Note that a Bot is an easier way to handle this sort automation, but if you’re an advanced user, you could look directly into Telegram’s API which will be way more flexible.

What can I actually use this for?

Well, of course you can just do it for fun and be able to send a message via powershell. But that’d be wasting this great potential. If I look out of the box, I see a possibility to build a (cheap) notification system and/or a (cheap) runbook system.

For example, imagine that you’ve got a script running that right now sends you an email once done just to tell you that the script has finished in 30 minutes. Why an email? Isn’t it handier having an actual push-notification on your phone telling you that?

Let’s think bigger, you’re deploying a new Virtual Machine with an automated script and, besides sending you a report via email, you want to know when it’s done so that you perhaps can go and work on the VM you just deployed, without continuously checking the status of the deployment. Or imagine adding a simple Message after an SCCM Task Sequence has been completed or even just use it to alert in case of a low disk space etc.

Now, what I like the most, what if Powershell can read what we’re writing into that conversation and based on that take actions? Like a runbook. Example: you write “Restart-Computer”. The PS script  could have a part of the code that checks every X amount of time if somebody wrote something and if they did, it checks the message. If the message equals to “Restart-Computer” then go and restart the computer. This is a very basic example, but it contain the core of what this can be used for.

Based on this idea, I actually build a very simple runbook automation script to leverage a Telegram message. Check it out here: Building a runbook with Powershell and Telegram

In the example at the end of this article I will be showing you how to send a message to a Telegram group and a possible action take after somebody replies with a specific keyword.

Set a Bot up

I suggest you to use a computer after step 1 as it’s going to be a bit faster in my opinion. This first bit is super simple.

  1. The first thing you want to do is register with Telegram if you haven’t done it yet. To do that, just go and download the app from the store (depending on what smartphone you’ve got).
  2. Launch the BotFather by opening this link: https://telegram.me/botfather
    • Start a conversation with the BotFather by typing /newbot – This will start the Bot creation wizard.
    • At this point, you will be asked to provide a Friendly name and a username. Once that is done, you will be provided with the token to be used in our scripts.
    • Telegram_botfather_create-bot

Time to intercept the Chat ID and run a quick test

So, in order for us to leverage the Bot to send a message, we will need to get the Chat ID of the conversation we want the Bot to talk/listen in.

  1. Go ahead and click on the link provided by the BotFather.
    • Telegram_botfather_create-bot-link
  2. You should now click Start to start a conversation with the Bot. The conversation will appear in the Telegram interface as if you were speaking to a friend.
    • Telegram_bot-web-interface
  3. Type some text in that conversation and after that, open this link in the browser (or in powershell, but we will see how to view the content in details later on):
    • https://api.telegram.org/bot$MyToken/getUpdates
      • Replace $MyToken with the token that the BotFather posted to you after the Bot was created.
    • Here’s an example:
      • Telegram_bot-send-message-to-it-from-user
    • Now we open the web page based on the link above (where you need to replace $MyToken with your Bot’s token) and you’ll see this:
      • Telegram_bot-chat-id-private
      • The Chat ID will be after “chat”:{“id:
  4. We have everything we need, time to run the test
$MyToken = "ADD YOUR TOKEN HERE"
$chatID = 123456789
$Message = "Hey, this is a test from the Bot!"
$Response = Invoke-RestMethod -Uri "https://api.telegram.org/bot$($MyToken)/sendMessage?chat_id=$($chatID)&text=$($Message)"

Try that out and you’ll see a nice message coming in both your phone and the web interface (if you were using a computer).

Important

Something important before we continue: we can’t send a message to a user that hasn’t start a conversation with the Bot first (you can try once you know the user ID, however, you’ll get an error code 403 – Forbidden: bot can’t initiate conversation with a user). However, a workaround (which I’m using) is to create a group with your own account, add the Bot and whoever friend who needs to be receiving the notifications. When using a group, the chat ID (so far I noticed this) seems to be always a negative number, something like -123456789.

If this is not good enough and you only want a private communication, you can send the link to your Bot to your friend(s): https://telegram.me/userbot >> userbot is the username of the bot we set up with the BotFather in the beginning of the process.

Using a Group – Ability for the Bot to read Messages [Setprivacy]

Important when using a group: By default, you won’t be able to read messages sent in the group from the API. This has to do with the Privacy Mode but it’s very easy to change that.

  1. Open the conversation with the BotFather again (https://telegram.me/botfather)
  2. Type /setprivacy
  3. You’ll be asked to input the Bot username, remember to add an @ before hand, so something like @mybot.
  4. You will be shown the current privacy status and the instructions to change it. In the screenshot below, we’re Disabling the privacy so that the bot will receive all messages that people send to the group.
    • Telegram_botfather_setprivacy
  5. Now you can try to type anything in the group chat and if you reload, (https://api.telegram.org/bot$MyToken/getUpdates) you’ll see the messages and most importantly the Chat ID if you missed that.

Read a message sent to the Bot

This will work either if it is a Group Chat or not. It took me a little bit to work with this json but it wasn’t too difficult. The main problem of the content presented is that fact that it doesn’t directly go into an Array, so once that’s taken care of (see the example below), it’s pretty simple to play with the data.

$MyToken = "YOUR TOKEN HERE"
$MyBotUpdates = Invoke-WebRequest -Uri "https://api.telegram.org/bot$($MyToken)/getUpdates"
#Convert the result from json and put them in an array
$jsonresult = [array]($MyBotUpdates | ConvertFrom-Json).result

#Display all Texts (FROM ANY CHAT!)
$jsonresult.message.text

Let’s make this a bit more interesting and let’s only get the latest message from a specific conversation.

$MyToken = "YOUR TOKEN HERE"
$ChatID = 123456789
$MyBotUpdates = Invoke-WebRequest -Uri "https://api.telegram.org/bot$($MyToken)/getUpdates"
#Convert the result from json and put them in an array
$jsonresult = [array]($MyBotUpdates | ConvertFrom-Json).result

$LastMessage = ""
Foreach ($Result in $jsonresult)  {
  If ($Result.message.chat.id -eq $ChatID)  {
    $LastMessage = $Result.message.text
  }
}

Write-Host "The Last Message sent from others to this conversation is $($LastMessage)"

Understanding the date of a message

The date of a message is super important when you have scripts a bit more complicated. If you’re in a Time Zone that uses daylight saving, you’ll end up having some issues with +- 1h.

You can easily sort that as the date provided ($Result.message.date) is Unix based (Epoch), so basically the seconds passed since 1/1/1970.

Here’s what I’m talking about:Telegram_Bot-Date-Not-Matching-Due-To-Daylight-Saving

So, to fix that all we need to do is get the current Time Zone and then add the Bias to the result (testing again with $jsonresult[5] just as an example)

$TimeZone = Get-CimInstance win32_timezone
$epoch = [datetime]"1/1/1970"
$epoch.AddSeconds($jsonresult[5].message.date).AddMinutes($TimeZone.Bias)

And now it looks good:

Telegram_bot-DayLight-Savings-Fix-Proof

 The Script we were waiting for!

This script will need your Token and Chat ID as always. It has an array of $Messages so that you can customize what to write right away without searching through the script. You can see that in the messages I’m using “fake variables” (%NAMEOFTHEHEROHERE% for example) which are basically placeholders for the real variables and I will be replacing them throughout the script.

There’s a Magic Word that is set to “Done”. The magic word is our keyword to perform an action.

In short, the script will send a message to the $ChatID (the first one in the array) and will do again after $SleepTime (10 minutes, message number 3, or $Messages[2]) until somebody sends a message with the Magic Word “Done”.

In that case, the script will perform a few actions:

  • Get the name of the hero who wrote the message Done and customize the message ($ExitMessage)
  • Set a variable to Exit the While cycle
  • Send a message back to the chat thanking the “Hero” 🙂

I tried adding as many comments as I could, I hope it makes enough sense without going through each of the lines:

$BotToken = "YOUR TOKEN HERE"
$ChatID = 123456789

#Array of Messages, they'll be used depending on the
$Messages = @(
	"Hello, please let me know once you've finished writing the script!"
	"Thank you for finishing the script, %NAMEOFTHEHERO%!"
	"Hello, can you please go and finish the script? This is the %NTIME% time I'm asking you!"
)

#Magic Word to stop the cycle
$MagicWord = "Done"

#Time to wait before sending a new reminder (in seconds - This is an approximate time and it'll depend on how quick the Invoke-WebRequest is)
$SleepTime = 600

#Time to sleep for each loop before checking if a message with the magic word was received
$LoopSleep = 3

#Send initial message
$InitialMessageDate = [Int] (get-date -UFormat %s) #Unix Format (seconds since 1/1/1970), needed as the bot will also work with this format
$InitialRestResponse = Invoke-RestMethod -Uri "https://api.telegram.org/bot$($BotToken)/sendMessage?chat_id=$($ChatID)&text=$($Messages[0])"

#Get the Time Zone: This is needed to make sure the InitialMessageDate isn't ahead or behind Telegram's Date (due to daylight saving etc).
$TimeZone = Get-CimInstance win32_timezone
#TimeZone.Bias will have the amount of MINUTES to be subtracted from InitialMessageDate, which is in seconds, that's why we're multiplying by 60
$TimeZoneBiasInSeconds = ($TimeZone.Bias)*60
#Finally, $InitialMessageDate_NoDaylightSavings will be in the same timezone as Telegram's updates
$InitialMessageDate_NoDaylightSavings = $InitialMessageDate - $TimeZoneBiasInSeconds


#Read the responses in a while cycle until the "MagicWord is matched"
$DoNotExit = 1
$Counter = 1 #This will count the amount of time the Bot is asking!
$SleepStartTime = [Int] (get-date -UFormat %s) #This will be used to check if the $SleepTime has passed yet before sending a new notification out
While ($DoNotExit)  {

  Sleep -Seconds $LoopSleep
  
  #Get the current Bot Updates and store them in an array format to make it easier
  $BotUpdates = Invoke-WebRequest -Uri "https://api.telegram.org/bot$($BotToken)/getUpdates"
  $BotUpdatesResults = [array]($BotUpdates | ConvertFrom-Json).result
  
  
  ForEach ($BotUpdate in $BotUpdatesResults)  {
    If (($BotUpdate.Message.Chat.ID -eq $ChatID) -AND ($BotUpdate.Message.Date -gt $InitialMessageDate_NoDaylightSavings)) {
	  #So, if ChatID matches and also the time of this message we're checking is greater than when the Bot sent the original message,
	  #we can now check if the Message contains the Magic Word!
	  If ($BotUpdate.Message.Text -eq $MagicWord)  {
	    $DoNotExit = 0
		#Get the User who's sent the magic word
	    $HeroOfTheMagicWord = $BotUpdate.message.from.first_name
		
		$ExitMessage = $Messages[1] -Replace ("%NAMEOFTHEHERO%",$HeroOfTheMagicWord)
		$ExitRestResponse = Invoke-RestMethod -Uri "https://api.telegram.org/bot$($BotToken)/sendMessage?chat_id=$($ChatID)&text=$($ExitMessage)"
		
		#Exit the foreach
		break
	  }
	}
  }
  
  
  
  #If the MagicWord hasn't been received, and the SleepTime has been reached
  $CurrentTime = [Int] (get-date -UFormat %s)
  If ($DoNotExit -AND ($CurrentTime -gt ($SleepStartTime + $SleepTime)))  {
    $Counter++
	$NextMessage = $Messages[2] -Replace ("%NTIME%",$Counter)
    $NextRestResponse = Invoke-RestMethod -Uri "https://api.telegram.org/bot$($BotToken)/sendMessage?chat_id=$($ChatID)&text=$($NextMessage)"
	$SleepStartTime = [Int] (get-date -UFormat %s) #Reset the SleepStartTime with the current time
  }
}

Using HTML Tags

Not many tags are currently supported, but for instance, you can send links, italic/bold text etc. To do this, we can leverage an extra option in the URL we’re using to send the message: parse_mode.

$message = "This is a <b>test</b>!"
Invoke-RestMethod -Uri "https://api.telegram.org/bot$($MyBotToken)/sendMessage?chat_id=$($chatID)&text=$($Message)&parse_mode=html"

Sending Multiple lines in one text

This is possible by leveraging `n.

$message = "This is a <b>test</b>! `nAnd this is a new line."
Invoke-RestMethod -Uri "https://api.telegram.org/bot$($MyBotToken)/sendMessage?chat_id=$($chatID)&text=$($Message)&parse_mode=html"

Telegram documentation

I suggest you to go through Telegram’s documentation at https://core.telegram.org/bots/api#sendmessage to see other possible options.

This should explain all steps for Automating Telegram Messages with Powershell!

IT Droplets

IT Droplets