Шаблон за най-простия бот за Telegram, предназначен за ученици от 7-9 клас на Powershell

В хода на разговори с приятел ми стана ясно, че в тяхната школа не учат децата в 8-10 клас на програмиране. Word, Excel и толкова. Нито Logo, нито дори Pascal, нито дори VBA за Excel.

Много се учудих, отворих интернет и започнах да чета —
Една от задачите на профилното училище е да допринася за възпитанието на ново поколение, което да отговаря на условията на информационното общество според своето ниво на развитие и начин на живот.
Този курс ще позволи на учениците да затвърдят на практика знанията си за основните конструкции на езика за програмиране Pascal. (от програма на някаква гимназия за 2017 година)

В крайна сметка реших да отделя няколко часа и да наброскам пример "как да се създаде прост бот — за ученици".

Под кат има информация как да напишете поредния прост бот на PowerShell и да го накарате да работи без webhook, бели IP адреси, отделни сървъри, виртуални машини в облака и подобни неща — на обикновен домашен компютър с обикновен Windows.

TLDR: Още една скучна статия с граматически и фактически грешки, няма какво да се чете, няма хумор, няма картинки.

Няма нищо ново в статията, почти всичко написано преди вече е било на Хабра, например в статиите Инструкция: Как да създавате ботове в Telegram и Telegram бот за системен администратор.
Повече от това, статията е специално излишна, за да не се налага да се позоваваме всеки път на учебната литература. Няма позовавания на банда 4, PowerShell Deep Dives или например The 5 Pillars of the AWS Well-Architected Framework в текста.

Вместо предисловие, можем да пропуснем

Спокойно пропускамеПрез 2006 година Microsoft пусна PowerShell 1.0 за тогавашния Windows XP, Vista и 2003 сървър. В някои отношения той замени такива неща като скриптове cmdbat, vb скриптове, Windows Script Host и JScript.

Дори сега PowerShell може да се разглежда само като следващата стъпка след вариантите Logo, вместо някъде, вероятно все още използвани Delphi (или нещо по-старо), въпреки наличието на цикли, класове, функции, извикване на MS GUI, интеграция с Git и така нататък.

PowerShell се използва сравнително рядко, може да се срещне само под формата на PowerShell Core, VMware vSphere PowerCLI, Azure PowerShell, MS Exchange, Desired State Configuration, PowerShell Web Access и още десетина рядко използвани програми и функции. Възможно е, второто дишане да му се появи с излизането на WSL2, но това не е сигурно.

PowerShell има и три големи предимства:

  1. Той е сравнително прост, има много литература и примери за него, дори на руски, например статия за Foreach — от книга PowerShell in depth — за разликата между () и {}
  2. Той идва с редактора ISE, включен в Windows. Там дори има и отладчик.
  3. Лесно се извикват компоненти за изграждане на графичен интерфейс.

0. Подготовка.

Ще ни трябва:

  • ПК с Windows (имам Windows 10)
  • Някакъв достъп до интернет (например през NAT)
  • За тези, които имат ограничен достъп до Telegram — инсталиран и конфигуриран freegate в браузъра, в някои сложни случаи съвместно с Symple DNS Crypt.
  • Рабещ клиент на Telegram на телефона
  • Разбиране на основите — какво е променлива, масив, цикъл.

Прочетени и открити статии — Инструкция: Как да създавате ботове в Telegram и Telegram бот за системен администратор

1. Ще създадем следващия тестови бот.

Тъй като това е нещо, което всички знаят и вече е било, може да се пропусне.Както е споменато в по-горната статия — Първо, ботът за Telegram — остава приложение, стартирано на ваша страна и извършващо заявки към Telegram Bot API. API-то е разбираемо — ботът се обръща на определен URL с параметри, а Telegram отговаря с JSON обект.

Свързаните с това проблеми: ако по някакъв начин вземете код от JSON обект и по някакъв начин го изпратите за изпълнение (не нарочно) — кодът ще се изпълни у вас.

Процесът на създаване е описан в две горни статии, но ще повторя: в Telegram отваряме контактите, търсим @botfather, казваме му /newbot, ще създадем бота Botfortest12344321, ще го наречем Mynext1234bot и ще получим съобщение с уникален ключ изглеждащ 1234544311:AbcDefNNNNNNNNNNNNN.

Запазете ключа и не го раздавайте!

След това можем да конфигурираме бота, например да забраним добавянето му в групи, но на първоначалните етапи това не е необходимо.

Ще попитаме BotFather „/mybot“ и ще коригираме настройките, ако нещо не ни харесва.

Отново ще отворим контактите, ще намерим @Botfortest12344321 (задължително започнете търсенето с @), ще натиснем „старт“ и ще напишем на бота „/Слава роботам“. Знакът / е задължителен, кавичките — не са нужни.
Ботът, разбира се, няма да отговори.

Ще проверим дали ботът е създаден — ще отворим.

api.telegram.org/bot1234544311:AbcDefNNNNNNNNNNNNN/getMe
където 1234544311:AbcDefNNNNNNNNNNNNN — предишно получен ключ,
и ще получим ред от вида
{"ok":true,"result":{}}

Първата секретна фраза (токен) вече е налична. Сега ни трябва да разберем втората секретна цифра — ID на чата с бота. Всеки чат, група и т.н. са индивидуални и имат свой собствен номер (понякога с минус — за отворени групи). За да разберем този номер, трябва да поискате в браузъра (всъщност не е задължително да бъде в браузър, но за по-добро разбиране можете да започнете с него) адреса (където 1234544311:NNNNNNNNN — това е вашият токен)

https://api.telegram.org/bot1234544311:NNNNNNNNN/getUpdates

и да получите отговор от вида

{«ok»:true,«result»:[{«update_id»:…,… chat«:{«id»:123456789

Трябва ни именно chat_id.

Да проверим дали можем да пишем в чата ръчно: ще извикаме от браузъра адреса

https://api.telegram.org/botваштокен/sendMessage?chat_id=123456789&text="Life is directed motion"

Ако сте получили съобщение от бота в чата — отлично, преминавате към следващия етап.

По този начин (чрез браузър) винаги можете да проверите къде е проблемът — при генерирането на линка, или нещо е закрито и не работи.

Какво трябва да знаете, преди да продължите да четете

В Telegram има няколко типа групови чатове (отворени, затворени). За тези чатове част от функциите (например, id) се различават, което понякога предизвиква известни проблеми.

Нека приемем, че е края на 2019 година, и дори герой на нашето време, широкоизвестният Човек-Оркестър (администратор, юрист, инфобезопасник, програмист и практически MVP) Евгений В. различава променливата $i от масив и е овладял цикли, и в следващите години може да овладее Chocolatey, а след това и Паралелна обработка с PowerShell и ForEach-Object Паралелно ще стигне.

1. Мислим, какво точно ще прави нашият бот

Нямах идеи, трябваше да мисля. Вече бях създавал бот-бележник. Не исках да правя бот, който просто ‘препраща нещо някъде’. За свързване с Azure е необходима кредитна карта, а от къде да я вземе ученикът? Трябва да отбележа, че всичко не е толкова лошо: основните облачни услуги предлагат някакъв период на проба безплатно (но все пак е необходима кредитна карта — и от мен ще се удържи, изглежда, долар. Не помня дали се върна после.)

Без AI ML правенето на бот с не особено интересни функции не е толкова вълнуващо.

Реших да направя бот, който ще ми (или не ми) напомня английски думи от речника.
Речникът, за да не се мъча с БД, ще бъде в текстов файл и ще се попълва ръчно.
В този случай целта е да се покажат основите в работата, а не да се направи поне частично завършен продукт.

2. Изпробваме какво и как за първи път

Ще създадем папка C:poshtranslate
Първо да видим какво имаме за powershell, ще стартираме ISE от пуск-стартиране
powershell ise
или ще намерим Powershell ISE в инсталираните програми.
След стартиране ще се отвори обикновен познат "някакъв редактор", ако текстовото поле не се покаже, винаги можем да натиснем "File — create new".

Нека да видим версията на powershell — ще напишем в текстовото поле:

get-host 

и ще натиснем F5.

Powershell ще предложи да се запази — "The script you are about to run will be saved.", съгласяваме се и ще запазим файла от powershell в C:poshtranslate с името myfirstbotBT100.

След стартиране, в долния текстов прозорец ще получим таблица с данни:

Name             : Windows PowerShell ISE Host
Version          : 5.1.(и така нататък)

Имам 5.1 с нещо, това е достатъчно. Ако имате стара Windows 7/8, не е страшно — въпреки че PowerShell трябва да се обнови до версия 5 — например, по инструкции.

Напишете в командния ред отдолу Get-Date, натиснете Enter, да видим времето, да преминем в основната папка с командата
cd
и да изчистим екрана с командата cls (не, не трябва да използвате rm)

Сега да проверим какво работи — напишем дори не код, а две реда и да видим какво правят. Ще коментираме реда с get-host със символа # и леко ще допишем.

# Пример шаблона бота 
# get-host
<# это пример многострочного комментария #>
$TimeNow = Get-Date
$TimeNow

(Интересно е. В падащия списък с оформления на кода в Хабр има двайсетина варианта — но powershell там няма. Има Dos. Има Perl.)

И ще стартираме кода, натискайки F5 или ">" от GUI.

Ще получим изход:

събота, 8 декември 2019 21:00:50 (или нещо подобно)

Сега да разберем тези два реда и някои интересни моменти, за да не се връщаме към това по-късно.

В отличие от Паскал (и не само), PowerShell сам се опитва да определи какъв тип да присвои на променливата, повече за това е написано в статията Ликбез по типизация в езиците за програмиране
Затова, когато създаваме променливата $TimeNow и ѝ присвояваме стойността на текущата дата и време (Get-Date), не трябва да се притесняваме особено какъв тип данни ще бъде там.

Истина е, че незнанието може да причини проблеми по-късно, но това е после. По-долу в текста ще има пример.
Нека видим какво сме получили. Ще изпълним (в командния ред)

$TimeNow | Get-member

и ще получим страница с неразбираем текст

Пример за неразбираем текст номер 1

PS C:&gt; $TimeNow | Get-member
   TypeName: System.DateTime
Name                 MemberType     Definition                                                                                                                                       
----                 ----------     ----------                                                                                                                                       
Add                  <b>Method         </b>datetime Add(timespan value)  
..
DisplayHint          NoteProperty   DisplayHintType DisplayHint=DateTime                                                                                                             
Date                 <b>Property       </b>datetime Date {get;}                                                                                                                             
Year                 Property       int Year {get;}   
..                                                                                                                               
DateTime             ScriptProperty System.Object DateTime {get=if ((&amp; { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq  "Date")...                                         

Както се вижда, е създадена променлива от тип TypeName: System.DateTime с куп методи (в смисъл, това, което можем да правим с този обект-променлива) и свойства.

Ще извикаме $TimeNow.DayOfYear — ще получим номер на деня в годината.
Ще извикаме $TimeNow.DayOfYear | Get-Member — ще получим TypeName: System.Int32 и група от методи.
Ще извикаме $TimeNow.ToUniversalTime() — и ще получим времето в UTC

Отладчик

Понякога е необходимо да изпълните програма до определена редица и да видите състоянието на програмата в този момент. За това в ISE има функция Debug — toggle break point
Сложете точка за спиране някъде по средата, стартирайте тези две реда за изпълнение и вижте как изглежда спирането.

3. Разглеждаме взаимодействието с бота Телеграм

Разбира се, за взаимодействието с бота, с всички getpush и подобни, има много литература, но въпросът с теорията може да бъде разгледан като допълнителен.

В нашия случай е необходимо:

  • Да се научим да изпращаме нещо в чата
  • Да се научим да получаваме нещо от чата

3.1 Учим се да изпращаме нещо в чата и да получаваме от него

Малко код — част 3

Write-output "This is part 3"
$MyToken = "1234544311:AbcDefNNNNNNNNNNNNN"
$MyChatID = "123456789"
$MyProxy = "http://1.2.3.4:5678" 

$TimeNow = Get-Date
$TimeNow.ToUniversalTime()
$ScriptDir = Split-Path $script:MyInvocation.MyCommand.Path
$BotVersion = "BT102"

$MyText01 = "Life is directed motion - " + $TimeNow

$URL4SEND = "https://api.telegram.org/bot$MyToken/sendMessage?chat_id=$MyChatID&text=$MyText01"

Invoke-WebRequest -Uri $URL4SEND

и в РСФ на това място получаваме грешка Unable to connect to the remote server.

Или не получаваме — зависи от оператора на връзката и от това дали проксито е настроено и работи.
Какво да правим — остава да добавим прокси. Внимавайте — използването на нешифровано и произволно прокси е изключително опасно за вашето здраве.

Задачата за намиране на работещо прокси не е особено трудна — повечето публикувани http проксита работят. За мен сработи вероятно петото по ред.

Синтаксисът с използване на прокси:

Invoke-WebRequest -Uri $URL4SEND -Proxy $MyProxy

Ако сте получили съобщение в чата си с бота — значи всичко е отлично, можете да продължите напред. Ако не — продължете с отстраняването на проблема.

Можете да видите какво се получава от вашия низ $URL4SEND и да опитате да го поискате в браузъра, например така:

$URL4SEND2 = '"'+$URL4SEND+'"'
start chrome $URL4SEND2 

3.2. Научихме се да пишем "нещо" в чата, сега ще опитаме да четем

Добавим още 4 реда и ще видим какво има вътре чрез | get-member

$URLGET = "https://api.telegram.org/bot$MyToken/getUpdates"
$MyMessageGet = Invoke-WebRequest -Uri $URLGET -Method Get -Proxy $MyProxy
Write-Host "Get-Member"
$MyMessageGet | Get-Member

Най-интересното ни предоставя

Content           Property   string Content {get;}  
ParsedHtml        Property   mshtml.IHTMLDocument2 ParsedHtml {get;}                                    
RawContent        Property   string RawContent {get;set;}

Нека видим какво има в тях:

Write-Host "ParsedHtml"
$MyMessageGet.ParsedHtml # тук е интересно
Write-Host "RawContent"
$MyMessageGet.RawContent # и тук е интересно, но също така и четимо.
Write-Host "Content"
$MyMessageGet.Content

Ако всичко работи, ще получите дълга строка в следния вид:

{"ok":true,"result":[{"update_id":12345678,
"message":{"message_id":3,"from":{"id"

За съжаление, в предишната статия за Telegram-бота за системни администратори този ред (да, според $MyMessageGet.RawContent | get-member — това е System.String), вече беше разгледан.

4. Обработете полученото (вече знаем как да изпращаме нещо)

Както вече е написано тук, най-важното се намира в content. Нека го разгледаме по-внимателно.

Първо, нека да изпратим на бота още няколко фрази от уеб интерфейса или от телефона

/message1
/message2
/message3

и да погледнем в браузъра на адреса, който се получи в променливата $URLGET.

Ще видим нещо подобно:

{"ok":true,"result":[{"update_id":NNNNNNN,
"message":{"message_id":10, .. "text":"/message1"
"message":{"message_id":11, .. "text":"/message2 
"message":{"message_id":12, .. "text":"/message3 

Какво е това? Някакъв сложен обект от масиви, съдържащи уникален идентификатор на съобщението, идентификатор на чата, идентификатор на изпращача и още куп информация.

Впрочем, не е нужно да разглеждаме „какъв е този обект“ — част от работата вече е извършена за нас. Нека да разгледаме какво има вътре:

Четем получените съобщения или част 4

Write-Host "This is part 4" 

$Content4Pars01 = ConvertFrom-Json $MyMessageGet.Content
$Content4Pars01 | Get-Member
$Content4Pars01.result
$Content4Pars01.result[0]
$Content4Pars01.result[0] | Get-Member
$Content4Pars01.result[0].update_id
$Content4Pars01.result[0].message
$Content4Pars01.result[0].message.text
$Content4Pars01.result[1].message.text
$Content4Pars01.result[2].message.text

5. Какво ще правим с това сега

Ще запазим полученото файл под името myfirstbotBT105 или каквото ви харесва повече, ще променим заглавието и ще коментираме целия написан код чрез

<#start comment 105 end comment 105#>

Сега трябва да решим откъде да вземем речника (тоест, разбира се, от диска в файл) и как ще изглежда той.

Разбира се, можем да създадем огромен речник директно в текста на скрипта, но това не е редно.
Затова да видим с какво стандартно работи PowerShell.
Всъщност, без значение с какъв файл работи, но за нас не е все едно.
На разположение имаме: txt (може, но защо), csv, xml.
И можем да видим всички. Нека да погледнем всички.
Да създадем клас MyVocabClassExample1 и променлива $MyVocabExample1
забелязвам, че класът се записва без $

малко код #5

write-host "Това е част 5"
class MyVocabClassExample1 {
    [string]$Original  # дума
    [string]$Transcript
    [string]$Translate
    [string]$Example
    [int]$VocWordID # много интересен момент. Използването на int с неговите ограничения понякога може да доведе до странни последици, за пример - неотдавнашен случай с SSD HPE. Първоначално не добавих този елемент, след това все пак го дописах и коментирах.
    }

$MyVocabExample1 = [MyVocabClassExample1]::new()
$MyVocabExample1.Original = "Apple"
$MyVocabExample1.Transcript = "[ ˈapəl ]"
$MyVocabExample1.Translate = "Ябълка"
$MyVocabExample1.Example = "Ябълката е сладък, годен за консумация плод, произведен от ябълково дърво (Malus domestica)"
# $MyVocabExample1.$VocWordID = 1

$MyVocabExample2 = [MyVocabClassExample1]::new()
$MyVocabExample2.Original = "Pear"
$MyVocabExample2.Transcript = "[ pe(ə)r ]"
$MyVocabExample2.Translate = "Круша"
$MyVocabExample2.Example = "Крушовото (ˈpɛər) дърво и храст са вид от род Pyrus"
# $MyVocabExample1.$VocWordID = 2

Нека опитаме да го запишем в файлове по шаблон.

Малко код #5.1

Write-Host $ScriptDir # надявам се $ScriptDir да не сте коментирали 
$MyFilenameExample01 = $ScriptDir + "Example01.txt"
$MyFilenameExample02 = $ScriptDir + "Example02.txt"
Write-Host $MyFilenameExample01
Out-File  -FilePath $MyFilenameExample01 -InputObject $MyVocabExample1

Out-File  -FilePath $MyFilenameExample01 -InputObject -Append $MyVocabExample2
notepad $MyFilenameExample01

— и ще получим грешка на реда Out-File -FilePath $MyFilenameExample01 -InputObject -Append $MyVocabExample2.

Не иска да добавя, ох каква досада.

$MyVocabExample3AsArray = @($MyVocabExample1,$MyVocabExample2)
Out-File  -FilePath $MyFilenameExample02 -InputObject $MyVocabExample3AsArray
notepad $MyFilenameExample02

Ще видим какво е излязло. Прекрасен текстов вид — а как да експортирваме обратно? Да въведем някакви разделители на текста, например запетаи?

И накрая да получим „файл с разделители — запетаи, comma-separated values (CSV) А СТОП ПОГОДИТЕ.
#

$MyFilenameExample03 = $ScriptDir + "Example03.csv"
$MyFilenameExample04 = $ScriptDir + "Example04.csv"
Export-Csv  -Path $MyFilenameExample03 -InputObject $MyVocabExample1 
Export-Csv  -Path $MyFilenameExample03 -InputObject $MyVocabExample2 -Append 
Export-Csv  -Path $MyFilenameExample04 -InputObject $MyVocabExample3AsArray 

Как е лесно да видим, че MS не се е отличило логически, за подобна процедура в един случай се използва -FilePath, за друг -Path.

Освен това в третия файл изчезна руският език, в четвъртия файл се получи… добре нещо се получи. #TYPE System.Object[] 00
# „Count“,»Length»,«LongLength»,«Rank»,«SyncRoot»,«IsReadOnly»,«IsFixedSize»,«IsSynchronized»
#
Нека малко пренапишем:

Export-Csv  -Path $MyFilenameExample03 -InputObject $MyVocabExample1 -Encoding Unicode
Export-Csv  -Path $MyFilenameExample03 -InputObject $MyVocabExample2 -Append -Encoding Unicode
notepad $MyFilenameExample03
notepad $MyFilenameExample04

Изглежда помогна — но форматът все пак не ми харесва.

Особено не ми харесва, че не мога да поставя редове от обекта директно във файла.
Между прочим, раз уж начали писать в файлы, возможно, стоит начать вести лог запусков? У нас есть время как переменная, имя файла тоже можем задавать.

Пока что писать, в общем, нечего, но можно подумать, как лучше реализовать ротацию логов.
Пока попробуем xml.

Немного xml

$MyFilenameExample05 = $ScriptDir + "Example05.xml"
$MyFilenameExample06 = $ScriptDir + "Example06.xml"
Export-Clixml  -Path $MyFilenameExample05 -InputObject $MyVocabExample1 
Export-Clixml  -Path $MyFilenameExample05 -InputObject $MyVocabExample2 -Append -Encoding Unicode
Export-Clixml  -Path $MyFilenameExample06 -InputObject $MyVocabExample3AsArray
notepad $MyFilenameExample05
notepad $MyFilenameExample06

В экспорте в xml одни преимущества — читаемость, экспорт всего объекта, и не нужно выполнять uppend.

Попробуем прочитать файл xml.

Немного чтения из xml

$MyFilenameExample06 = $ScriptDir + "Example06.xml"
$MyVocabExample4AsArray = Import-Clixml -Path $MyFilenameExample06
# $MyVocabExample4AsArray 
# $MyVocabExample4AsArray[0]
# и немного о совершенно неочевидных нюансах. Powershell временами ведёт себя не так, как можно было бы ожидать.
# например, у меня эти два вывода отличаются
# Write-Output $MyVocabExample4AsArray 
# write-host $MyVocabExample4AsArray 

Вернемся к задаче. Мы записали пробный файл, прочитали его, формат хранения понятен, при необходимости можно написать небольшой редактор для добавления и удаления строк.

Напомню, что задача была сделать небольшого обучающего бота.

Формат работы: я отправляю боту команду «пример», бот присылает мне случайно выбранное слово и транскрипцию, а через 10 секунд шлет перевод и комментарий. Мы умеем читать команды, было бы неплохо научиться автоматически подбирать и проверять прокси, а также сбрасывать счетчики сообщений.

Раскомментируем все ранее ненужные строки, закомментируем примеры с txt и csv, и сохраним файл как версию B106.

А, да. Отправим что-то боту еще раз.

6. Отправка из функций и не только

Перед тем как обрабатывать прием, необходимо создать функцию для отправки «чего-то», кроме тестового сообщения.

В нашем примере будет всего одна отправка и всего из одной обработки, а что если надо будет несколько раз делать одно и то же?

Проще написать функцию. Итак, у нас есть переменная типа объект $MyVocabExample4AsArray, считанная из файла, в виде массива на два элемента.
Поехали читать.

Одновременно разберемся с часами, это нам потом понадобится (хотя на самом деле в этом примере — не понадобится 🙂

Немного кода #6.1

Write-Output "Това е част 6"
$Timezone = (Get-TimeZone)
IF($Timezone.SupportsDaylightSavingTime -eq $True){
    $TimeAdjust =  ($Timezone.BaseUtcOffset.TotalSeconds + 3600) } # коригирано време
    ELSE{$TimeAdjust = ($Timezone.BaseUtcOffset.TotalSeconds) 
    }
    
function MyFirstFunction($SomeExampleForFunction1){
$TimeNow = Get-Date
$TimeNow.ToUniversalTime()
# $MyText02 = $TimeNow + " " + $SomeExampleForFunction1 # и тук ще получим грешка
$MyText02 = $SomeExampleForFunction1 + " " + $TimeNow # а тук не, който познае защо - той е молодец.

$URL4SendFromFunction = "https://api.telegram.org/bot$MyToken/sendMessage?chat_id=$MyChatID&text=$MyText02"
Invoke-WebRequest -Uri $URL4SendFromFunction -Proxy $MyProxy
}

Как лесно да видите, че в функцията се извикват $MyToken и $MyChatID, които са зададени предварително.

Така не трябва да се прави, и, ако $MyToken е един за всеки бот, то $MyChatID ще се променя в зависимост от чата.

Въпреки това, тъй като това е пример, засега ще го игнорираме.

Тъй като $MyVocabExample4AsArray не е масив, макар и много да прилича на такъв, то не може просто да попитате за неговата дължина.

Ще се наложи отново да направим нещо, което не трябва — да се отбием без кодекса — да вземем и да преброим

Леко код #6.2

$MaxRandomExample = 0 
foreach ($Obj in $MyVocabExample4AsArray) {
$MaxRandomExample ++
}
Write-Output $MaxRandomExample
$RandomExample = Get-Random -Minimum 0 -Maximum ($MaxRandomExample)
$TextForExample1 = $MyVocabExample4AsArray[$RandomExample].Original
# MyFirstFunction($TextForExample1)
# или в един ред
# MyFirstFunction($MyVocabExample4AsArray[Get-Random -Minimum 0 -Maximum ($MaxRandomExample -1)].Example)
# Познайте сами, кой пример е по-лесен за четене от непознати.

Случайно интригуваща функция. Да предположим, че искаме да получим 0 или 1 (имаме само два елемента в масива). При задаване на граници 0..1 — ще получим ли "1"?
не — не можем, специално сме показали пример Example 2: Вземете случайно цяло число между 0 и 99 Get-Random -Maximum 100
Затова за 0..1 ще трябва да зададем размер 0..2, при това максималният номер на елемента = 1.

7. Обработка на получени съобщения и максимална дължина на опашката

На какво се спряхме досега? имаме получената променлива $MyMessageGet
и получената от нея $Content4Pars01, от които ни интересуват елементите на масива Content4Pars01.result

$Content4Pars01.result[0].update_id
$Content4Pars01.result[0].message
$Content4Pars01.result[0].message.text

Ще изпратим на бота /message10, /message11, /message12, /word и още веднъж /word и /привет.
Да видим какво получихме:

$Content4Pars01.result[0].message.text
$Content4Pars01.result[2].message.text

Ще преминем през всичко, което получихме, и ще изпратим отговор, ако съобщението е било /word
конструкция case of, това, което някои описват като if-elseif, в PowerShell се извиква чрез switch. В кода по-долу се използва ключ -wildcard, което не е задължително и дори може да навреди.

Няколко реда код #7.1

Write-Output "Това е част 7"
Foreach ($Result in $Content4Pars01.result) # Да, може да стане по-бързо 
 { 
    switch -wildcard ($Result.message.text) 
            {
            "\/word" {MyFirstFunction($TextForExample1)}
            }
}

Ще изпълним скрипта няколко пъти. Ще получим два пъти същата дума при всяка попытка на изпълнение, особено ако сме допуснали грешка в изпълнението на random.

Но чакай. Ние не сме изпратили \/word отново, защо тогава съобщението се обработва повторно?

Опашката за изпращане на съобщения до бота има крайна дължина (изглежда, 100 или 200 съобщения) и трябва да се изчиства ръчно.

В документацията това е описано, но трябва да се чете!

В този случай ни е необходим параметър ?chat_id, а &timeout, &limit, &parse_mode=HTML и &disable_web_page_preview=true засега не са нужни.

Документацията за telegram api се намира тук
На английски е написано:
Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest
unconfirmed update are returned. An update is considered confirmed as soon as getUpdates is called with an offset higher than its update_id. The negative offset can be specified to retrieve updates starting from -offset update from the end of the updates queue. All previous updates will forgotten.

Нека погледнем на:

$Content4Pars01.result[0].update_id
$Content4Pars01.result[1].update_id 
$Content4Pars01.result | select -last 1
($Content4Pars01.result | select -last 1).update_id

Също така ще пренапишем — малко ще променим функцията. Имаме два варианта — да предадем на функцията цялото съобщение и да го обработим изцяло в функцията, или само да предадем ID на съобщението и да го изтрием. За пример второто изглежда по-просто.

По-рано нашият ред за заявка „всички съобщения“ изглеждаше като

$URLGET = "https:\/\/api.telegram.org\/bot$MyToken\/getUpdates"

а сега ще изглежда като

$LastMessageId = ($Content4Pars01.result | select -last 1).update_id
$URLGET1 = "https:\/\/api.telegram.org\/bot$mytoken\/getUpdates?offset=$LastMessageId&limit=100" 
$MyMessageGet = Invoke-WebRequest -Uri $URLGET1 -Method Get -Proxy $MyProxy 

Никой не забранява първо да получим всички съобщения, да ги обработим и само след успешна обработка да поискаме unconfirmed -> confirmed.

Защо е полезно да се извика потвърждение след завършване на всички обработки? Възможен е провал в средата на изпълнението, и ако за пример свободен чат-бот пропускането на някое единствено съобщение не е голямо бедствие, то ако обработвате нечия заплата или трансакция с карта, резултатът може да бъде по-лош.

Още няколко реда код

$LastMessageId = ($Content4Pars01.result | select -last 1).update_id  #грешката на това място се предполагае, че ще бъде поправена самостоятелно. 
$URLGET1 = "https://api.telegram.org/bot$mytoken/getUpdates?offset=$LastMessageId&limit=100" 
Invoke-WebRequest -Uri $URLGET1 -Method Get -Proxy $MyProxy

8. Вместо заключение

Основните функции — четене на съобщения, нулиране на опашката, четене от файл и запис в файл са изпълнени и показани.

Остатъка от четири неща:

  • изпращане на правилния отговор на запитване в чата
  • изпращане на отговор в ЛЮБОЙ чат, в който е добавен ботът
  • изпълнение на кода в цикъл
  • стартиране на бота от Windows Task Scheduler.

Всички тези задачи са простички и лесно реализируеми с помощта на четене на документацията относно параметри като
Set-ExecutionPolicy Unrestricted и -ExecutionPolicy Bypass
цикъл от вида

$TimeToSleep = 3 # опит през 3 секунди
$TimeToWork = 10 # минути
$HowManyTimes = $TimeToWork*60/$TimeToSleep # брояч за цикъла
$MainCounter = 0
for ($MainCounter=0; $MainCounter -le $HowManyTimes) {
sleep $TimeToSleep
$MainCounter ++

На всички, които стигнаха до края, благодаря.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster