Шаблон за прост бот за телеграм за ученици от 7-9 клас на Powershell

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

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

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

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

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

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

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

Смело пропускамеПрез 2006 година Microsoft пусна PowerShell 1.0 за тогавашния Windows XP, Vista и Windows Server 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 и Телеграм бот за системен администратор

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.", съгласяваме се и запазваме в C:poshtranslate файла от powershell с името 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                                                                                                                                       
----                 ----------     ----------                                                                                                                                       
Добавяне                  <b>Метод         </b>datetime Добавяне(timespan стойност)  
..
DisplayHint          NoteProperty   DisplayHintType DisplayHint=ДатаВреме                                                                                                             
Дата                 <b>Свойство       </b>datetime Дата {get;}                                                                                                                             
Година                 Свойство       int Година {get;}   
..                                                                                                                               
ДатаЧас             ScriptProperty System.Object ДатаЧас {get=if ((&amp; { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq  "Дата")...                                         

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

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

Отладчик

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

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

Разбира се, по темата за взаимодействието с бота, с всички 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"

За съжаление, в предишната статия "Телеграм бот за системен администратор" този низ (да, според $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 = "Дървото и храстът на крушата са вид от род 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 множество преимуществ — читаемость, экспорт всего объекта и отсутствие необходимости в выполнении appending.

Попробуем прочитать файл 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 на първото обновление, което ще бъде върнато. Трябва да е по-голямо с едно от най-високото между идентификаторите на предишно получени обновления. По подразбиране, обновления, започващи с най-ранното
непотвърдено обновление се връщат. Обновление се счита за потвърдено веднага след като getUpdates бъде извикан с offset по-висок от неговия update_id. Отрицателният offset може да бъде указан, за да получи обновления, започващи от -offset обновление от края на опашката с обновления. Всички предишни обновления ще бъдат забравени.

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

$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