Шаблон найпростага робата для тэлеграм для школьнікаў 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 і 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:AbcDefNNNNNNNNNNNNN/getMe
дзе 1234544311:AbcDefNNNNNNNNNNNNN — раней атрыманы ключ,
і атрымаем радок выгляду
{«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 прапануе захавацца - "Паказваемся, і захаваем у 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. Разбіраемся з узаемадзеяннем з ботам Тэлеграм

Вядома па ўзаемадзеянні з ботам, з усімі 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

і ў РФ на гэтым месцы мы атрымліваем памылку Немагчыма падключыцца да 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#>

Цяпер нам трэба вызначыцца, дзе браць слоўнік (ну як дзе - на дыску ў файле) і як ён будзе выглядаць.

Вядома, можна набіць велізарны слоўнік прама ў тэксце скрыпту, але гэта ж зусім не справа.
Таму паглядзім, з чым умее штатна працаваць powershell.
Наогул то яму ўсё роўна з якім файлам працаваць, не ўсё роўна нам.
На выбар у нас есць: 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-Random -Maximum 0
Таму для 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. Muse be greater by one than 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 можа быць 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.

Усе гэтыя задачы простыя і лёгка рэалізуюцца з дапамогай чытання дакументацыі пра такія параметры, як
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

Дадаць каментар