Шаблон найпростішого бота для телеграм для школярів 7-9 класу на Powershell

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

Дуже здивувався, відкрив інтернет і поліз читати
Одне із завдань профільної школи – сприяти вихованню нового покоління, що відповідає за своїм рівнем розвитку та способу життя умовам інформаційного суспільства.
Цей курс дозволить закріпити на практиці знання учнів з основних конструкцій мови програмування Паскаль. (З програми якоїсь гімназії за 2017 рік)

У результаті вирішив витратити кілька годин і накидати приклад «як створити простого бота для школярів».

Під катом про те, як написати чергового простого бота на Powershell і змусити його працювати без webhook, білих IP, виділених серверів, віртуалок, що розгортаються, в хмарі та інша — на звичайному домашньому ПК зі звичайною Windows.

TLDR: Ще одна нудна стаття з граматичними та фактичними помилками, читати нічого, гумору немає, картинок немає.

Нічого нового у статті немає, майже все написане раніше вже було на Хабрі, наприклад у статтях Інструкція: Як створювати ботів у Telegram и Телеграм-бот для системного адміністратора.
Більше того, стаття спеціально надмірна, щоб не посилатися щоразу на навчальну літературу. Жодних посилань на банду 4, PowerShell Deep Dives або скажімо The 5

Замість передмови можна пропустити

Сміливо пропускаємоУ 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 наприклад)
  • Для тих, у кого обмежений доступ до телеграми — встановлений і налаштований freegate у браузері, у деяких складних випадках разом із Symple DNS Crypt
  • Наявність працюючого клієнта телеграм на телефоні
  • Розуміння основ — що таке змінна, масив, цикл.

Відкриті та прочитані статті Інструкція: Як створювати ботів у Telegram и Телеграм-бот для системного адміністратора

1. Створимо ще одного тестового робота.

Оскільки це і так усі знають, і вже було, то також можна пропуститиЯк сказано в статті вище - Перш за все, бот для Telegram - це, як і раніше, програма, запущена на вашій стороні і здійснює запити до Telegram Bot API. Причому API зрозуміле - бот звертається на певний URL з параметрами, а Telegram відповідає JSON об'єктом.

Пов'язані з цим проблеми: якщо якимось невідомим чином ви візьмете якийсь код з JSON об'єкта і якось відправте його на виконання (не спеціально) код виконається у вас.

Процес створення описаний у двох статтях вище, але повторюся: у телеграмі відкриваємо контакти, шукаємо @botfather, говоримо йому /newbot, створимо бота Botfortest12344321, назвемо його Mynext1234bot, і отримаємо повідомлення з унікальним ключем виду 1234544311:

Ключ бережіть і не роздавайте!

Далі можна налаштувати бота, наприклад заборонити додавання його в групи, але на перших кроках це не потрібно.

Запитаємо у BotFather /mybot і поправимо налаштування, якщо чогось не подобається.

Знову відкриємо контакти, знайдемо там @Botfortest12344321 (починати пошук з обов'язково), натиснемо «почати» і напишемо боту «/Слава роботам». Знак / обов'язковий, лапки – не потрібні.
Бот, звісно, ​​нічого не відповість.

Перевіримо, що бот створився - відкриємо.

api.telegram.org/bot1234544311:AbcDefNNNNNNNNNNNNNNN/getMe
де 1234544311:AbcDefNNNNNNNNNNNNNN — раніше отриманий ключ,
та отримаємо рядок виду
{«ok»:true,«result»:{«»}}

Перша таємна фраза (токен) у нас є. Тепер нам потрібно дізнатися другу секретну цифру – ID чату з ботом. Кожен чат, група тощо — індивідуальні та мають свій власний номер (іноді з мінусом — для відкритих груп). Для того, щоб дізнатися цей номер, нам потрібно запросити в браузері (насправді зовсім не обов'язково в браузері, але для кращого розуміння можна почати з нього) адреса (де 1234544311:NNNNNNNNN — це ваш токен

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

та отримати відповідь виду

{«ok»:true,«result»:[{«update_id»:…,… чат«:{«id»:123456789

Нам потрібний саме chat_id.

Перевіримо, що ми можемо писати в чат вручну: Викликаємо з браузера адресу

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

Якщо до вас у чат надійшло повідомлення від бота - окей, ви переходите на наступний етап.

Таким чином (через браузер) ви завжди можете перевірити, де проблеми - у вас при генерації посилання, або десь прикрито і не працює.

Що потрібно знати, перед продовженням прочитання

У телеграмі є кілька типів групових чатів (відкриті, закриті). Для даних чатів частина функцій (наприклад, id) відрізняється, що іноді викликає деякі проблеми.

Вважатимемо, що на подвір'ї кінець 2019 року, і навіть герой нашого часу, широковідома Людина-Оркестр (адміністратор, юрист, інфобезпечник, програміст і практично MVP) Євген В. відрізняє змінну $i від масиву, освоїв цикли, дивишся в наступні пару років освоїть Chocolatey, а там і до Parallel processing with PowerShell и ForEach-Object Parallel дійде.

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.

Отримаємо висновок:

Saturday, December 8, 2019 21:00:50 PM (или что-то типа)

Тепер розберемося з цими двома рядками та деякими цікавими моментами, щоб надалі до цього не повертатися.

На відміну від паскаля (і не тільки), повершелл сам намагається визначити, який тип привласнити змінну, докладніше про це написано у статті Лікнеп з типізації в мовах програмування
Тому, заводячи змінну $TimeNow, і присвоюючи їй значення поточної дати та часу (Get-Date), нам не треба особливо турбуватися, який тип даних там буде.

Щоправда, від цього незнання згодом може бути боляче, але це згодом. Нижче у тексті буде приклад.
Побачимо, що в нас вийшло. Виконаємо (у командному рядку)

$TimeNow | Get-member

та отримаємо сторінку незрозумілого тексту

Приклад незрозумілого тексту номер 1

PS C:> $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 ((& { 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. Розбираємось із взаємодією з ботом Телеграм

Звичайно, по взаємодії з ботом, з усіма засобамиспоживання і так далі написано ще більше літератури, проте питання теорії можна розглянути факультативно.

У нашому випадку необхідно:

  • Навчитися щось відправляти в листування
  • Навчитися отримувати щось із листування

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. Подивимося його уважніше.

Спочатку напишемо боту ще пару фраз із web інтерфейсу або з телефону

/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#>

Тепер нам треба визначитися, де брати словник (ну як де - на диску у файлі) і як він виглядатиме.

Звичайно, можна набити величезний словник прямо в тексті скрипта, але це зовсім не справа.
Тому подивимося, з чим вміє штатно працювати потужність шосе.
Взагалі йому все одно з яким файлом працювати, не все одно нам.
На вибір ми маємо: txt (можна, але навіщо), csv, xml.
А можна всіх подивитись Подивимося всіх.
Створимо клас MyVocabClassExample1 та змінну $MyVocabExample1
зауважу що клас пишеться без $

трохи коду #5

write-host "This is part 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 = "An apple is a sweet, edible fruit produced by an apple tree (Malus domestica)"
# $MyVocabExample1.$VocWordID = 1

$MyVocabExample2 = [MyVocabClassExample1]::new()
$MyVocabExample2.Original = "Pear"
$MyVocabExample2.Transcript = "[ pe(ə)r ]"
$MyVocabExample2.Translate = "Груша"
$MyVocabExample2.Example = "The pear (/ˈpɛər/) tree and shrub are a species of genus 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 "This is Part 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: Get a random integer between 0 and 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 "This is part 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
непідтверджений update are returned. У update is considered confirmed as soon as getUpdates is called with an зсув вище than its update_id. Негативний offset може бути спрямований на відновлення updates starting from -offset update from the end of the updates queue. Всі попередні updates будуть 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.

Всі ці завдання прості та легко реалізуються за допомогою читання документації про такі параметри, як
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

Додати коментар або відгук