W trakcie rozmowy z przyjacielem nagle dowiedziałem się, że dzieci w klasach 8-10 w ich szkole w ogóle nie uczą się programowania. Tylko Word, Excel i na tym koniec. Ani Logo, ani nawet Pascala, ani VBA dla Excela.
Byłem bardzo zdziwiony, otworzyłem internet i zacząłem czytać —
Jednym z zadań szkół profilowych jest wspieranie wychowania nowego pokolenia, które odpowiada swoim poziomem rozwoju i stylem życia warunkom społeczeństwa informacyjnego.
Ten kurs pozwoli na praktyczne utrwalenie wiedzy uczniów na temat podstawowych konstrukcji języka programowania Pascal. (z programu jakiejś gimnazjum z 2017 roku)
W efekcie postanowiłem poświęcić kilka godzin i zarysować przykład «jak stworzyć prostego bota — dla uczniów».
W artykule dowiesz się, jak napisać kolejnego prostego bota w PowerShell i uruchomić go bez webhooka, białych IP, serwerów dedykowanych, wirtualnych maszyn w chmurze i tym podobnych — na zwykłym domowym PC z systemem Windows.
TLDR: Jeszcze jeden nudny artykuł z błędami gramatycznymi i faktograficznymi, nic ciekawego, brak humoru, brak obrazków.
Nie ma nic nowego w artykule, prawie wszystko, co napisano wcześniej, było już na Habrze, na przykład w artykułach i .
Co więcej, artykuł jest specjalnie rozbudowany, aby nie odnosić się za każdym razem do literatury edukacyjnej. W tekście nie ma żadnych odwołań do grupy 4, PowerShell Deep Dives czy na przykład The 5 Pillars of the AWS Well-Architected Framework.
Zamiast wstępu, możemy to pominąć
Śmiało pomijamyW 2006 roku Microsoft wydał PowerShell 1.0 dla ówczesnych systemów Windows XP, Vista i serwera 2003. W pewnym sensie zastąpił on takie rzeczy jak skrypty cmdbat, skrypty vb, Windows Script Host i JScript.
Nawet teraz PowerShell można postrzegać tylko jako kolejny krok po wersjach Logo, w miejsce być może wciąż używanych Delphi (lub czegoś starszego), mimo że zawiera on pętle, klasy, funkcje, wywołania GUI MS, i tym podobne.
PowerShell jest stosunkowo rzadko używany, można się z nim spotkać jedynie w postaci PowerShell Core, VMware vSphere PowerCLI, Azure PowerShell, MS Exchange, Desired State Configuration, i jeszcze dziesiątek innych rzadko używanych programów i funkcji. Może jego drugie życie pojawi się z wydaniem , ale to niepewne.
PowerShell ma też trzy wielkie zalety:
- Jest stosunkowo prosty, jest wiele literatury i przykładów, w tym również w języku polskim, na przykład artykuł o Foreach — z książki — o różnicy () i {}
- Przechodzi ze edytorem , w zestawie z Windows. Jest nawet jakiś debugger.
- Łatwo wywołuje .
0. Przygotowanie.
Będziemy potrzebować:
- PC z Windows (mam Windows 10)
- Jakikolwiek dostęp do internetu (na przykład przez NAT)
- Dla tych, którzy mają ograniczony dostęp do telegramu — zainstalowany i skonfigurowany freegate w przeglądarce, w niektórych trudnych przypadkach łącznie z Symple DNS Crypt
- Posiadanie działającego klienta telegram na telefonie
- Zrozumienie podstaw — czym jest zmienna, tablica, pętla.
Przeczytane i otwarte artykuły — i
1. Stworzymy kolejnego testowego bota.
Ponieważ wszyscy to już znają, można to również pominąćJak wspomniano w powyższym artykule — Przede wszystkim bot na Telegram — to wciąż aplikacja uruchomiona po waszej stronie, wysyłająca zapytania do Telegram Bot API. API jest zrozumiałe — bot wykonuje zapytania do określonego URL z parametrami, a Telegram odpowiada obiektem JSON.
Związane z tym problemy: jeśli w sposób nieznany weźmiecie jakiś kod z obiektu JSON i jakoś wyślecie go do wykonania (niezamierzony), — kod zostanie uruchomiony.
Proces tworzenia został opisany w dwóch powyższych artykułach, ale powtórzę: w telegramie otwieramy kontakty, szukamy @botfather, mówimy mu /newbot, stworzymy bota Botfortest12344321, nazwiemy go Mynext1234bot, i otrzymamy wiadomość z unikalnym kluczem w postaci 1234544311:AbcDefNNNNNNNNNNNNN
Klucz zachowajcie i nie rozdawajcie!
Potem można skonfigurować bota, na przykład zabronić dodawania go do grup, ale na początku nie jest to konieczne.
Zapytamy BotFather’a „/mybot” i poprawimy ustawienia, jeśli coś nam nie odpowiada.
Znowu otworzymy kontakty, znajdziemy tam @Botfortest12344321 (należy zacząć szukać od @), klikniemy „start” i napiszemy do bota „/Chwała robotom”. Znak / jest obowiązkowy, cudzysłowy — nie są potrzebne.
Bot oczywiście nic nie odpowie.
Sprawdzimy, czy bot został stworzony — otworzymy.
:AbcDefNNNNNNNNNNNNN/getMe
gdzie 1234544311:AbcDefNNNNNNNNNNNNN — wcześniej uzyskany klucz,
i otrzymamy linię w postaci
{„ok”:true,„result”:{„”}}
Pierwsza tajna fraza (token) już mamy. Teraz musimy poznać drugi sekret - ID czatu z botem. Każdy czat, grupa itd. jest unikalny i ma swój własny numer (czasami z minusem - dla otwartych grup). Aby poznać ten numer, musimy w przeglądarce (w rzeczywistości niekoniecznie w przeglądarce, ale dla lepszego zrozumienia można zacząć od niej) wpisać adres (gdzie 1234544311:NNNNNNNNN to Twój token
https://api.telegram.org/bot1234544311:NNNNNNNNN/getUpdates
i otrzymać odpowiedź w formacie
{«ok»:true,«result»:[{«update_id»:…,… czat«:{«id»:123456789
Potrzebujemy właśnie chat_id.
Sprawdźmy, czy możemy pisać na czat ręcznie: wywołamy z przeglądarki adres
https://api.telegram.org/botwashtoken/sendMessage?chat_id=123456789&text="Life is directed motion"Jeśli otrzymasz wiadomość na czacie od bota - w porządku, przechodzisz do następnego etapu.
W ten sposób (przez przeglądarkę) zawsze możesz sprawdzić, gdzie są problemy - czy u Ciebie przy generowaniu linku, czy coś gdzieś jest zablokowane i nie działa.
Co warto wiedzieć, zanim przejdziesz do dalszej lektury
W Telegramie jest kilka typów grupowych czatów (otwarte, zamknięte). W przypadku tych czatów część funkcji (np. id) różni się, co czasami powoduje pewne problemy.
Załóżmy, że mamy koniec 2019 roku, a nawet nasz bohater czasów, powszechnie znany Człowiek-Orkiestra (administrator, prawnik, specjalista ds. bezpieczeństwa informacji, programista i praktycznie MVP) Eugeniusz W. odróżnia zmienną $i od tablicy, opanował pętle i być może w ciągu następnych dwóch lat opanuje Chocolatey, a wtedy do i dojdzie.
1. Myślimy, co nasz bot będzie robił
Nie miałem żadnych pomysłów, musiałem myśleć. Już pisałem bota-notatnika. Nie chciało mi się robić bota „który coś przekazuje gdzieś”. Do podłączenia Azure potrzebna jest karta kredytowa, a skąd ją ma uczeń? Warto zauważyć, że nie jest tak źle: główne chmury oferują jakiś darmowy okres próbny (ale karta kredytowa i tak jest potrzebna - i z niej zostanie pobrana, wydaje mi się, dolar. Nie pamiętam, czy potem wrócił).
Bez AI ML robienie bota-molestownika-wierszopisa nie jest tak interesujące.
Postanowiłem stworzyć bota, który będzie mi (lub nie) przypominał angielskie słowa z słownika.
Słownik, aby nie bawić się z bazą danych, będzie leżał w pliku tekstowym i będzie uzupełniany ręcznie.
W tym przypadku celem jest pokazanie podstaw w pracy, a nie stworzenie przynajmniej częściowo gotowego produktu.
2. Spróbujmy, co i jak po raz pierwszy
Stworzymy folder C:poshtranslate
Na początek zobaczmy, co to jest PowerShell, uruchomimy ISE przez menu uruchamiania
powershell ise
lub znajdziemy PowerShell ISE w zainstalowanych programach.
Po uruchomieniu otworzy się zwykły znany 'jakiś tam edytor', jeśli nie będzie pola tekstowego, zawsze można kliknąć 'File — create new'.
Sprawdźmy wersję PowerShell — napiszemy w polu tekstowym:
get-host
i naciśniemy F5.
PowerShell zaproponuje zapisanie — 'The script you are about to run will be saved.', zgadzamy się i zapisujemy w C:poshtranslate plik z PowerShell o nazwie myfirstbotBT100.
Po uruchomieniu w dolnym oknie tekstowym otrzymamy tabelę danych:
Name : Windows PowerShell ISE Host
Version : 5.1.(i tak dalej)
Mam 5.1 z czymś, to wystarczy. Jeśli masz starszy Windows 7/8, to nic strasznego — chociaż PowerShell należy zaktualizować do wersji 5 — na przykład przez .
Wpiszmy w dolnym wierszu polecenia Get-Date, naciśnij Enter, zobaczmy czas, przejdźmy do katalogu głównego poleceniem
cd
i wyczyśćmy ekran poleceniem cls (nie, nie używaj rm)
Teraz sprawdźmy, co i jak działa — napiszemy nawet nie kod, a dwie linie, i spróbujemy zrozumieć, co one robią. Zakomentujemy linię z get-host znakiem # i trochę dopiszemy.
# Пример шаблона бота
# get-host
<# это пример многострочного комментария #>
$TimeNow = Get-Date
$TimeNow
(Co ciekawe. W menu rozwijanym stylów kodu na Habrze jest dwa tuziny opcji — ale PowerShell tam nie ma. Dos jest. Perl jest.)
I uruchomimy kod, naciskając F5 lub '>' z GUI.
Otrzymamy wynik:
Sobota, 8 grudnia 2019 21:00:50 PM (lub coś podobnego)
Teraz rozwiążmy te dwie linie oraz niektóre interesujące momenty, aby później do tego nie wracać.
W przeciwieństwie do Pascala (i nie tylko), PowerShell sam próbuje określić, jaki typ przypisać zmiennej, więcej o tym napisano w artykule
Dlatego, tworząc zmienną $TimeNow i przypisując jej wartość bieżącej daty i czasu (Get-Date), nie musimy się wyjątkowo martwić, jaki typ danych tam będzie.
Prawda, z tego braku wiedzy później może być boleśnie, ale to później. Poniżej w tekście będzie przykład.
Zobaczmy, co osiągnęliśmy. Wykonajmy (w wierszu poleceń)
$TimeNow | Get-memberi otrzymamy stronę niezrozumiałego tekstu
Przykład niezrozumiałego tekstu numer 1
PS C:> $TimeNow | Get-member
TypeName: System.DateTime
Name MemberType Definition
---- ---------- ----------
Dodaj <b>Metoda </b>datetime Dodaj(timespan wartość)
..
DisplayHint NoteProperty DisplayHintType DisplayHint=DateTime
Data <b>Właściwość </b>datetime Data {get;}
Rok Właściwość int Rok {get;}
..
DataCzas ScriptProperty System.Object DataCzas {get=if ((& { Set-StrictMode -Version 1; $this.DisplayHint }) -ieq "Data")...
Jak widać, stworzono zmienną typu TypeName: System.DateTime z mnóstwem metod (w sensie, co możemy robić z tym obiektem zmiennej) i właściwości.
Wywołamy $TimeNow.DayOfYear — otrzymamy numer dnia w roku.
Wywołamy $TimeNow.DayOfYear | Get-Member — otrzymamy TypeName: System.Int32 i grupę metod.
Wywołamy $TimeNow.ToUniversalTime() — i otrzymamy czas w UTC
Debuger
Czasami jest tak, że trzeba wykonać program do jakiejś linii i zobaczyć, co się dzieje w programie w tym momencie. W tym celu w ISE jest funkcja Debug — toggle break point
Ustaw punkt zatrzymania gdzieś pośrodku, uruchom te dwie linie do wykonania i zobacz, jak wygląda zatrzymanie.
3. Zajmiemy się interakcją z botem Telegrama
Oczywiście na temat interakcji z botem, z wszystkimi getpush i tak dalej napisano jeszcze więcej literatury, jednak teorię można rozważyć fakultatywnie.
W naszym przypadku musimy:
- Nauczyć się coś wysyłać w rozmowę
- Nauczyć się odbierać coś z rozmowy
3.1 Uczymy się wysyłać coś w rozmowę i odbierać z niej
Trochę kodu — część 3
Write-output "To jest część 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 = "Życie to ruch kierunkowy - " + $TimeNow
$URL4SEND = "https://api.telegram.org/bot$MyToken/sendMessage?chat_id=$MyChatID&text=$MyText01"
Invoke-WebRequest -Uri $URL4SEND
i w Rosji w tym miejscu mamy błąd Unable to connect to the remote server.
Lub nie możemy — to zależy od operatora sieci i tego, czy proxy jest skonfigurowane i działa.
Cóż — pozostaje dodać proxy. Uważaj na to — użycie niezaszyfrowanego i w ogóle nieznanego proxy jest ekstremalnie niebezpieczne dla twojego zdrowia.
Zadanie znalezienia działającego proxy nie jest zbyt trudne — większość publikowanych http proxy działa. U mnie działało chyba piąte z kolei.
Składnia użycia proxy:
Invoke-WebRequest -Uri $URL4SEND -Proxy $MyProxy
Jeśli otrzymałeś wiadomość w swoim czacie z botem — to świetnie, można iść dalej. Jeśli nie — kontynuuj debugowanie.
Możesz zobaczyć, w co przekształca się twój ciąg $URL4SEND i spróbować zażądać go w przeglądarce, na przykład tak:
$URL4SEND2 = '"'+$URL4SEND+'"'
start chrome $URL4SEND2
3.2. Nauczyliśmy się pisać „coś” w czacie, teraz spróbujemy czytać
Dodajemy jeszcze 4 linie i zobaczymy, co tam jest poprzez | 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
Najbardziej interesujące informacje dostarcza nam
Content Property string Content {get;}
ParsedHtml Property mshtml.IHTMLDocument2 ParsedHtml {get;}
RawContent Property string RawContent {get;set;}Zobaczmy, co w nich jest:
Write-Host "ParsedHtml"
$MyMessageGet.ParsedHtml # tutaj jest coś ciekawego
Write-Host "RawContent"
$MyMessageGet.RawContent # i tutaj jest coś ciekawego, a do tego jeszcze czytelne.
Write-Host "Content"
$MyMessageGet.Content
Jeśli wszystko działa, otrzymasz długi ciąg o następującym formacie:
{"ok":true,"result":[{"update_id":12345678,
"message":{"message_id":3,"from":{"id"Na szczęście w wcześniej opublikowanym artykule o bocie Telegram dla administratorów systemu ten ciąg (tak, zgodnie z $MyMessageGet.RawContent | get-member — jest to System.String), już został omówiony.
4. Obróbka otrzymanego (wysyłanie czegokolwiek już potrafimy)
Jak już napisano , najważniejsze znajduje się w content. Przyjrzyjmy się temu bliżej.
Najpierw napiszemy botowi jeszcze kilka zwrotów z interfejsu webowego lub telefonu
/message1
/message2
/message3i przyjrzymy się przez przeglądarkę adresowi, który utworzył się w zmiennej $URLGET.
Zobaczymy coś w rodzaju:
{"ok":true,"result":[{"update_id":NNNNNNN,
"message":{"message_id":10, .. "text":"\/message1"
"message":{"message_id":11, .. "text":"\/message2
"message":{"message_id":12, .. "text":"\/message3 Co to takiego? Jakiś skomplikowany obiekt z tablic obiektów, zawierający unikalny identyfikator wiadomości, identyfikator czatu, identyfikator nadawcy oraz wiele innych informacji.
Jednak nie musimy analizować „co to za obiekt” — część pracy została już wykonana za nas. Sprawdźmy, co tam jest w środku:
Czytamy otrzymane wiadomości lub część 4
Write-Host "To jest część 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. Co teraz z tym zrobimy
Zapiszemy otrzymany plik pod nazwą myfirstbotBT105 lub w dowolny inny sposób, zmienimy tytuł i zakomentujemy cały wcześniej napisany kod za pomocą
<#start comment 105 end comment 105#>
Teraz musimy zdecydować, skąd wziąć słownik (no jak skąd — z dysku w pliku) i jak on będzie wyglądał.
Oczywiście, można wypełnić ogromny słownik bezpośrednio w tekście skryptu, ale to nie jest najlepsze rozwiązanie.
Dlatego zobaczmy, z czym standardowo potrafi pracować PowerShell.
W zasadzie mu wszystko jedno, z jakim plikiem pracować, ale nam nie jest obojętne.
Do wyboru mamy: txt (można, ale po co), csv, xml.
A można ich wszystkich zobaczyć. Przyjrzyjmy się wszystkim.
Utworzymy klasę MyVocabClassExample1 oraz zmienną $MyVocabExample1
zauważam, że klasę pisze się bez $
trochę kodu #5
write-host "To jest część 5"
class MyVocabClassExample1 {
[string]$Original # słowo
[string]$Transcript
[string]$Translate
[string]$Example
[int]$VocWordID # bardzo interesujący moment. Użycie int z jego ograniczeniami może czasami prowadzić do dzikich konsekwencji, na przykład - niedawny przypadek z SSD HPE. Na początku nie dodałem tego elementu, potem jednak dopisałem i skomentowałem.
}
$MyVocabExample1 = [MyVocabClassExample1]::new()
$MyVocabExample1.Original = "Jabłko"
$MyVocabExample1.Transcript = "[ ˈapəl ]"
$MyVocabExample1.Translate = "Jabłko"
$MyVocabExample1.Example = "Jabłko to słodki, jadalny owoc produkowany przez jabłoń (Malus domestica)"
# $MyVocabExample1.$VocWordID = 1
$MyVocabExample2 = [MyVocabClassExample1]::new()
$MyVocabExample2.Original = "Gruszka"
$MyVocabExample2.Transcript = "[ pe(ə)r ]"
$MyVocabExample2.Translate = "Gruszka"
$MyVocabExample2.Example = "Gruszka (/FˈpɛərF/) to gatunek drzewa i krzewu z rodzaju Pyrus"
# $MyVocabExample1.$VocWordID = 2
Spróbujmy zapisać to w plikach według .
Trochę kodu #5.1
Write-Host $ScriptDir # mam nadzieję, że nie skomentowaliście $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
— i otrzymamy błąd w linii Out-File -FilePath $MyFilenameExample01 -InputObject -Append $MyVocabExample2.
Nie chce dodać, oj jak to frustrujące.
$MyVocabExample3AsArray = @($MyVocabExample1,$MyVocabExample2)
Out-File -FilePath $MyFilenameExample02 -InputObject $MyVocabExample3AsArray
notepad $MyFilenameExample02
Zobaczmy, co wyszło. Świetny widok tekstowy — ale jak można to z powrotem wyeksportować? Wprowadzić jakieś separatory tekstu, na przykład przecinki?
A w rezultacie uzyskać "plik z separatorami — przecinkami, comma-separated values (CSV) A .
#
$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
Jak łatwo zauważyć, MS nie popisało się logiką, w przypadku podobnej procedury w jednym przypadku używa -FilePath, w innym -Path.
Ponadto w trzecim pliku zniknął język rosyjski, w czwartym pliku wyszło… no wyszło coś. #TYPE System.Object[] 00
# „Count“,»Length»,«LongLength»,«Rank»,«SyncRoot»,«IsReadOnly»,«IsFixedSize»,«IsSynchronized»
#
Trochę przepiszemy:
Export-Csv -Path $MyFilenameExample03 -InputObject $MyVocabExample1 -Encoding Unicode
Export-Csv -Path $MyFilenameExample03 -InputObject $MyVocabExample2 -Append -Encoding Unicode
notepad $MyFilenameExample03
notepad $MyFilenameExample04
Wydaje się, że pomogło — ale format wciąż mi się nie podoba.
Szczególnie nie podobają mi się, że nie mogę umieścić linii z obiektu bezpośrednio do pliku.
A propos, since we started writing to files — should we start maintaining a launch log? We have time as a variable, and we know how to define a filename.
There's nothing to write for now, but we can think about how to improve log rotation.
For now, let's try xml.
A bit of 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
Exporting to xml has many advantages — readability, exporting the entire object, and no need to perform an append.
Let's try .
A bit of reading from xml
$MyFilenameExample06 = $ScriptDir + "Example06.xml"
$MyVocabExample4AsArray = Import-Clixml -Path $MyFilenameExample06
# $MyVocabExample4AsArray
# $MyVocabExample4AsArray[0]
# and a little about some rather non-obvious nuances. Powershell behaves from time to time in ways that one might not expect.
# for example, I have these two outputs differing
# Write-Output $MyVocabExample4AsArray
# write-host $MyVocabExample4AsArray
Let's get back to the task. We have recorded a test file, read it, the storage format is clear, and if necessary, we can write a separate small file editor for adding and deleting lines.
Let me remind you that the task was to create a small training bot.
Work format: I send the command "example" to the bot, the bot sends me a randomly selected word and transcription, and after 10 seconds, sends the translation and comment. We know how to read commands, it would be good to learn how to automatically select and check proxies, and reset message counters to oblivion.
We'll uncomment everything previously commented out as unnecessary, comment out the now unnecessary examples with txt and csv, and save the file as version B106.
Ah, yes. Let's send something to the bot again.
6. Sending from functions and beyond
Before processing the reception, we need to create a function to send "something", apart from the test message.
Of course, in our example, there will be only one send and from one processing, but what if we need to do the same thing multiple times?
It's easier to write a function. So, we have a variable of type object $MyVocabExample4AsArray, read from the file, as an array with two elements.
At the same time, we'll figure out the time, we'll need it later (in this example, it actually won't be necessary 🙂
A bit of code #6.1
Write-Output "To jest część 6"
$Timezone = (Get-TimeZone)
IF($Timezone.SupportsDaylightSavingTime -eq $True){
$TimeAdjust = ($Timezone.BaseUtcOffset.TotalSeconds + 3600) } # czas letni
ELSE{$TimeAdjust = ($Timezone.BaseUtcOffset.TotalSeconds)
}
function MyFirstFunction($SomeExampleForFunction1){
$TimeNow = Get-Date
$TimeNow.ToUniversalTime()
# $MyText02 = $TimeNow + " " + $SomeExampleForFunction1 # w tym miejscu wystąpi błąd
$MyText02 = $SomeExampleForFunction1 + " " + $TimeNow # a tutaj nie, kto się domyśli, dlaczego - ten jest sprytny.
$URL4SendFromFunction = "https://api.telegram.org/bot$MyToken/sendMessage?chat_id=$MyChatID&text=$MyText02"
Invoke-WebRequest -Uri $URL4SendFromFunction -Proxy $MyProxy
}
Jak łatwo zauważyć, w funkcji wywoływane są $MyToken i $MyChatID, które zostały wcześniej przypisane na stałe.
Tak nie powinno się robić, a jeśli $MyToken jest jeden dla każdego bota, to $MyChatID będzie się zmieniać w zależności od czatu.
Jednakże, ponieważ jest to przykład, na razie to zignorujemy.
Ponieważ $MyVocabExample4AsArray nie jest tablicą, chociaż bardzo do niej podobna, to nie można po prostu wziąć i zapytac o jej długość.
Będzie trzeba po raz kolejny zrobić coś, czego nie powinno się robić — wpaść niezgodnie z zasadami — wziąć i policzyć
Trochę kodu #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)
# lub w jednej linijce
# MyFirstFunction($MyVocabExample4AsArray[Get-Random -Minimum 0 -Maximum ($MaxRandomExample -1)].Example)
# Zgadnij sam, który przykład jest łatwiejszy do zrozumienia dla obcych ludzi.
Losowy . Załóżmy, że chcemy otrzymać 0 lub 1 (mamy tylko dwa elementy w tablicy). Przy ustaleniu granic 0..1 — czy dostaniemy „1”?
nie — nie dostaniemy, specjalnie podano przykład Example 2: Get a random integer between 0 and 99 Get-Random -Maximum 100
Dlatego dla 0..1 będziemy musieli ustalić rozmiar 0..2, przy czym maksymalny numer elementu = 1.
7. Przetwarzanie przychodzących wiadomości i maksymalna długość kolejki
Na czym się wcześniej zatrzymaliśmy? mamy otrzymaną zmienną $MyMessageGet
i uzyskaną z niej $Content4Pars01, z których interesują nas elementy tablicy Content4Pars01.result
$Content4Pars01.result[0].update_id
$Content4Pars01.result[0].message
$Content4Pars01.result[0].message.text Wyślemy do bota /message10, /message11, /message12, /word i jeszcze raz /word i /cześć.
Zobaczmy, co otrzymaliśmy:
$Content4Pars01.result[0].message.text
$Content4Pars01.result[2].message.text Przejrzymy wszystko, co otrzymaliśmy i wyślemy odpowiedź, jeśli wiadomość to /word
konstruowanie case of, to, co niektórzy opisują jako if-elseif, w PowerShell wywołuje się W kodzie poniżej użyto klucza -wildcard, co wcale nie jest konieczne, a nawet może być szkodliwe.
Nieco kodu #7.1
Write-Output "To jest część 7"
Foreach ($Result in $Content4Pars01.result) # Tak, można to zrobić szybciej
{
switch -wildcard ($Result.message.text)
{
"\/word" {MyFirstFunction($TextForExample1)}
}
}
Wykonamy skrypt kilka razy. Otrzymamy dwa razy to samo słowo przy każdej próbie, szczególnie jeśli popełniliśmy błąd w implementacji random.
Ale chwileczkę. Nie wysłaliśmy jeszcze raz \/word, dlaczego więc wiadomość jest przetwarzana ponownie?
Kolejka do wysyłania wiadomości do bota ma ograniczoną długość (wydaje się, że 100 lub 200 wiadomości) i trzeba ją ręcznie opróżniać.
Oczywiście jest to opisane w dokumentacji, ale trzeba ją przeczytać!
W tym przypadku potrzebny nam jest parametr ?chat_id, a &timeout, &limit, &parse_mode=HTML oraz &disable_web_page_preview=true na razie nie są potrzebne.
Dokumentacja dotycząca W białym angielskim tam jest napisane:
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. Negatywne przesunięcie można określić, aby pobrać aktualizacje zaczynając od -offset aktualizacji z końca kolejki aktualizacji. Wszystkie poprzednie aktualizacje zostaną zapomniane.
Zobaczmy na:
$Content4Pars01.result[0].update_id
$Content4Pars01.result[1].update_id
$Content4Pars01.result | select -last 1
($Content4Pars01.result | select -last 1).update_idI zresetujmy – trochę przepiszemy funkcję. Mamy dwa warianty - przekazać funkcji całą wiadomość i przetwarzać ją w całości w funkcji, lub oddać tylko ID wiadomości i również ją zresetować. Dla przykładu, drugie wydaje się prostsze.
Kiedyś nasz ciąg zapytania "wszystkich wiadomości" wyglądał jak
$URLGET = "https:\/\/api.telegram.org\/bot$MyToken\/getUpdates"a będzie wyglądał jak
$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
Nikt nie zabrania najpierw pobrać wszystkich wiadomości, następnie je przetworzyć, a dopiero po pomyślnej obróbce zażądać unconfirmed -> confirmed.
Dlaczego ma sens wywoływać potwierdzenie po zakończeniu wszystkich obróbek? Może wystąpić awaria w trakcie wykonywania, a jeśli na przykład w przypadku darmowego czatu bota pominięcie jednej wiadomości to jeszcze nic specjalnego, to jeśli przetwarzasz czyjąś wypłatę lub transakcję kartą, to wynik może być gorszy.
Jeszcze kilka linii kodu
$LastMessageId = ($Content4Pars01.result | select -last 1).update_id #błąd w tym miejscu ma być poprawiony samodzielnie.
$URLGET1 = "https://api.telegram.org/bot$mytoken/getUpdates?offset=$LastMessageId&limit=100"
Invoke-WebRequest -Uri $URLGET1 -Method Get -Proxy $MyProxy
8. Zamiast zakończenia
Główne funkcje — czytanie wiadomości, resetowanie kolejki, czytanie z pliku i zapisywanie do pliku zostały zrealizowane i pokazane.
Pozostały do zrobienia tylko cztery rzeczy:
- wysyłanie poprawnej odpowiedzi na zapytanie w czacie
- wysyłanie odpowiedzi do DOWOLNEGO czatu, do którego dodano bota
- wykonywanie kodu w pętli
- uruchamianie bota z harmonogramu Windows.
Wszystkie te zadania są proste i łatwe do zrealizowania poprzez zapoznanie się z dokumentacją na temat takich parametrów, jak
Set-ExecutionPolicy Unrestricted i -ExecutionPolicy Bypass
pętli typu
$TimeToSleep = 3 # zapytanie co 3 sekundy
$TimeToWork = 10 # minut
$HowManyTimes = $TimeToWork*60/$TimeToSleep # licznik dla pętli
$MainCounter = 0
for ($MainCounter=0; $MainCounter -le $HowManyTimes) {
sleep $TimeToSleep
$MainCounter ++
Dziękuję wszystkim, którzy dotrwali do końca.
Źródło: habr.com
