
Basierend auf dem Artikel wollte der
Telegramm-Bot für den Systemadministrator (der Artikel gehört nicht mir, ich habe ihn nur gelesen) die Erfahrungen beim Erstellen eines Telegramm-Bot in PowerShell für die Verwaltung von Anwendungsservern teilen. Es wird Text, Code und ein paar Bilder geben. Konstruktive Kritik ist willkommen (Hauptsache nicht "Warum auf PowerShell? Es sollte auf Perl sein").
Ich denke, dass dieser Artikel eher für PowerShell-Neulinge geeignet ist, aber erfahrene Administratoren können hier etwas Nützliches sehen.
Er versuchte, den Artikel in Teilen aufzubauen - von einfach bis komplex. Vielleicht kommt es zu Plagiaten, seien Sie vorsichtig!
Wir müssen also Dienste oder Anwendungen auf mehreren Servern verwalten (stoppen, starten), den Server neu starten, die Protokolle und ggf. einige andere Informationen einsehen. All das möchte ich tun (eigentlich nicht), in der U-Bahn, im Laden oder sogar auf der Couch liegen, ohne VPN und Laptops. Von den Anforderungen (die natürlich am Knie geschrieben wurden).
- Einfaches Hinzufügen / Ändern von Aufgaben im Telegramm-Bot
- Multitasking oder Parallelität
- Intuitive Verwaltungsoberfläche
- Zumindest etwas Sicherheit
Irgendwann wurde beschlossen, die Konfiguration in einer separaten Datei abzulegen - in unserem Fall in xml (hier kann jemand sagen, dass wir alle in json sind, aber wir haben es in xml gemacht und waren zufrieden).
Fangen wir von vorne an:
Teil 1: Ein einfacher Telegramm-Bot
Wir suchen einen Bot-Ordner (kein Verzeichnis) -
BotFather (@BotFather) in Telegram

Write
/ NewbotAls nächstes müssen Sie einen Namen für den Bot (in meinem Fall habe ich Haaaabr speziell für den Artikel genannt) und einen Benutzernamen eingeben, der auf "bot" (Haaaabr_bot) enden sollte.
Danach stellt BotFather einen Token aus, den wir verwenden werden:

Dann kannst du ein Bild für den Bot hochladen, Beschreibung eingeben, eine Befehlsliste erstellen, aber ich war zu faul.
Wir machen einen einfachen Bot, der Nachrichten empfängt und darauf reagiert.
Ich werde den PS-Code in Teilen schreiben und in regelmäßigen Abständen den vollständigen Code als Referenz einfügen.
Als Referenz benötigen wir die
API- Aufrufbeschreibungen der
Telegramm-Bot-APIWir brauchen 2 Methoden:
getUpdates - Empfangen von Nachrichten per Bot (Skript)
sendMessage - Senden von Nachrichten von einem Bot (Skript) an einen Benutzer
Dort sehen wir, dass:
Anfragen stellen
Alle Abfragen an die Telegramm-Bot-API müssen über HTTPS erfolgen und in der folgenden Form erfolgen: api.telegram.org/bot<token>/METHOD_NAME
Schritt 1 - Nachrichten empfangenVariablen
Jetzt werden wir überprüfen, ob ein Aufruf von
$ URL_get zurückgegeben wird Invoke-RestMethod -Uri $URL_get
ok result
-- ------
True {}
Nicht schlecht Schreiben wir etwas an den Bot:

Und lies:
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
}
Offensichtlich brauchen wir Ergebnis. Ich muss gleich sagen, dass wir nur an der letzten Nachricht des Benutzers interessiert sind, also so:
Jetzt müssen wir bestätigen, dass wir die Nachricht erhalten haben. Dies geschieht jedoch mit der Methode
getUpdates mit dem Parameter
offset :
Standardmäßig werden Updates zurückgegeben, die mit dem frühesten nicht bestätigten Update beginnen. Ein Update gilt als bestätigt, sobald getUpdates mit einem Offset höher als dessen update_id aufgerufen wird
Tun
Invoke-RestMethod "$($URL_get)?offset=$($($data.update_id)+1)" -Method Get | Out-Null
Und wir werfen alles in einen Zyklus mit einem Timeout von 1 Sekunde:
Lassen Sie uns nun eine Nachrichtenlesefunktion daraus machen. Weil Wir müssen mehrere Werte von der Funktion zurückgeben - wir haben uns für die Verwendung einer HashTable (named / associative array) entschieden
Skript zum Empfangen von Nachrichten Schritt 2 - Daten sendenZum Senden einer Nachricht benötigen wir die
sendMessage- Methode sowie die Felder
chat_id und
text (der Rest ist optional
https://core.telegram.org/bots/api#sendmessage ).
Sofort Schnittfunktion
function sendMessage($URL, $chat_id, $text) {
Jetzt telefonisch
sendMessage $URL_set <__id> "123"
bekomme eine nachricht in den warenkorb
Schritt 3 - Alles zusammenfügenNachfolgend finden Sie den gesamten Code zum Senden und Empfangen von Nachrichten
Weitere Logik kann auf der Basis von
$ return.text und zum Beispiel der
switch-Anweisung erstellt werden :
switch -Wildcard ($return["text"]) { "**" { sendMessage $URL_set $return.chat_id ", $($return["f_name"])" } "* ?*" { sendMessage $URL_set $return.chat_id "" } default {sendMessage $URL_set $return.chat_id "$(Get-Random("", "", "", ""))"} }
Emoji:emoji wird im Cmdlet "Get-Random" verwendet. Ich konnte sie nicht in den Code des Artikels einbetten, aber PS versteht sie nativ

Teil 2: brauche Knöpfe
Im Telegramm-Bot gibt es die Möglichkeit, eine Liste von Befehlen anzugeben (wird hier über dieses Symbol geöffnet)

)
Am Anfang haben wir genau das getan - es gab eine Reihe von Befehlen, wir haben die Namen von Servern oder Diensten als Parameter übergeben. Dann haben wir beschlossen, dass wir uns weiter in Richtung benutzerfreundlicher Oberflächen bewegen und die Tastenfunktionen miteinander verbinden müssen.
Wird von sendMessage-
Aufrufen mit dem
reply_markup- Parameter verwendet
Für unsere Funktionalität haben wir den Typ
InlineKeyboardMarkup verwendethttps://core.telegram.org/bots/api#inlinekeyboardmarkup .
Aus der Beschreibung folgt, dass das Feld
inline_keyboard ein Array aus einem Array von Schaltflächen ist
(Array von Array von InlineKeyboardButton)
Versucht, einen Test zu senden
Wir bekommen Fehler:
Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Bad Request: Feld" inline_keyboard "des InlineKeyboardMarkup sollte ein Array von Arrays sein"}
In der Zeile: 21 Zeichen: 1Überprüfen, was die Variable
$ json enthält
Fazit:
{ "reply_markup": { "inline_keyboard": [ "System.Collections.Hashtable System.Collections.Hashtable" ] }, "chat_id": **********, "text": "Test Text", "parse_mode": "Markdown" }
Anscheinend überträgt das HashTable-Objekt ("System.Collections.Hashtable System.Collections.Hashtable") für die Telegramm-API nicht sehr viel. Ein bisschen Google und das Endergebnis - beim Konvertieren in Json legen Sie die Konvertierungstiefe fest
Wir bekommen die Knöpfe:

Wir machen eine Funktion zum Senden von Buttons, wir werden ein Array von Buttons als Eingabe übergeben
Alles zusammenfügen, indem Sie den Schalterblock ein wenig ändern Jetzt schickt uns der Bot für "Hallo" ein paar Knöpfe. Es bleibt zu verstehen, auf welche Schaltfläche der Benutzer geklickt hat. Die aktuelle ps-Funktion getUpdates hat eine Prüfung auf
if($text)...
Wenn Sie auf die Schaltfläche klicken, wird kein Text zurückgegeben und Sie müssen die Funktion entsprechend ändern. Klicken Sie auf die Schaltfläche

Führen Sie einen Code aus, um den Inhalt von
$ data zu überprüfen
Es kommt keine Nachricht mehr an. Stattdessen jetzt
callback_query . Bearbeitungsfunktion
Jetzt gibt die Funktion
Text zurück, wenn eine Nachricht
vorliegt , oder
callback_data, wenn auf eine Schaltfläche geklickt wurde. In der Testphase haben sie beim Aufrufen einen Fehler festgestellt:
sendMessage $URL_set $($return.chat_id) $($return.callback_data)
Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Ungültige Anforderung: Entitäten können nicht analysiert werden: Ende der Entität ab Byte-Offset 8 kann nicht gefunden werden"}Da
parse_mode auf
Markdown gesetzt ist , soll auch der Text gesendet werden
$return.callback_data = “Project1_CD”
Sie müssen die Nachricht vor dem Senden formatieren. Weitere Informationen finden Sie hier:
https://core.telegram.org/bots/api#formatting-optionsoder entfernen Sie den Unterstrich "_"
Teil 3: Mach die Konfiguration
Es ist Zeit, alles in die Konfiguration zu setzen. Hier ist alles einfach - wir machen xml:
<config> <system> <token>***********************</token> <timeout desc="bot check timeout in seconds">1</timeout> </system> <tasks> <task name=" " script="c:\Temp\Habr\reboot_all.ps1"></task> <task name=" " script="c:\Temp\Habr\status.ps1"></task> <task name="ipconfig1" script="ipconfig"></task> <task name="ipconfig2" script="ipconfig"></task> <task name="ipconfig3" script="ipconfig"></task> <task name="ipconfig4" script="ipconfig"></task> <task name="ipconfig5" script="ipconfig"></task> </tasks> </config>
Wir beschreiben Aufgaben und geben für jede Aufgabe ein Skript oder einen Befehl an.
Wir prüfen:
[xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text' foreach($task in $xmlConfig.config.tasks.task) { $task.name
Putting es in das Hauptskript [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
Wenn Sie nun "Hallo" schreiben, gibt der Bot eine Liste von Schaltflächen zurück, die den in XML-Dateien beschriebenen Aufgaben entsprechen. In callback_data gibt es einen Befehl oder ein Skript.
Wenn Sie kosmetische Änderungen vornehmen, ist es wünschenswert, dass die Schaltflächen 3-4 pro Zeile waren, andernfalls werden sie nicht vollständig angezeigt:

Wir werden maximal 3 Buttons pro Zeile machen.
Schematisch sollte das Tastatur-Array so aussehen:

Auf diese Weise:
Button [i] - ein Array (assoziativ) des Formulars
$button = @{ "text" = $task.name; callback_data = $task.script}
Zeile [1-3] - das sind Arrays (von Buttons), die Arrays von Buttons speichern (das ist wichtig)
Keyboard - eine Reihe von Line'ov.
Ändern Sie die
sendKeyboard- Funktion
function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{}
Wir prüfen:

Letztes Drehbuch [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
4:
.
Job'. :
$script = "ipconfig" $script_block = { Param($script) ; Invoke-Expression $script } $job_name = "TestJob" Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null
5 :
foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job $output $job | Remove-Job }
$output ipconfig localhost
, callback_data
, error
Invoke-RestMethod: {«ok»:false,«error_code»:400,«description»:«Bad Request: message is too long»}, 4096 . …
$output.Length
39
, :
$text = $null foreach($string in $output) { $text = "$text`n$string" } sendMessage $URL_set $job.Name $text
[xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'

« »
xml ,
users chat_id , :
<system> <token>*********************************</token> <timeout desc="bot check timeout in seconds">1</timeout> <users>111111111, 222222222</users> </system>
users $users = (($xmlConfig.config.system.users).Split(",")).Trim()
if($users -contains $return.chat_id) { ... }
[xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text' $users = (($xmlConfig.config.system.users).Split(",")).Trim()
5:
– , -
Invoke-Command Write-Output
$hostname = "hostname" $service = "MSSQLSERVER" $output = Invoke-Command -ComputerName $hostname -ScriptBlock{param($service); (Get-Service -Name $service).Status} -ArgumentList $service write-output $output.Value
, , - - .
, , , , , , .
- > 4096 , Substring .
– ( ) , , - ( - ). Exit
switch -Wildcard ($return["text"]) { "**" {
.