Telegram-bot untuk manajemen infrastruktur

gambar

Berdasarkan artikel tersebut, bot Telegram untuk administrator sistem (artikel ini bukan milik saya, saya baru saja membacanya) ingin berbagi pengalaman membuat bot Telegram di PowerShell untuk mengelola server aplikasi. Akan ada teks, kode, dan beberapa gambar. Kritik konstruktif diterima (yang utama bukan untuk mengatakan "mengapa di PowerShell? Seharusnya ada di perl").

Saya pikir artikel ini akan lebih cocok untuk pendatang baru di PowerShell, tetapi administrator yang berpengalaman dapat melihat sesuatu yang bermanfaat di sini.

Dia mencoba membangun artikel dalam beberapa bagian - dari yang sederhana hingga yang kompleks. Mungkin plagiarisme akan terjadi, hati-hati!

Jadi, kami memiliki kebutuhan untuk mengelola layanan atau aplikasi pada beberapa server (berhenti, mulai), restart server, lihat log dan beberapa informasi lain jika perlu. Semua ini ingin saya lakukan (sebenarnya tidak), berada di kereta bawah tanah, di toko atau bahkan berbaring di sofa, tanpa VPN dan laptop. Dari persyaratan (yang ditulis, tentu saja, di lutut).

  • Mudah untuk menambah / mengubah tugas di bot Telegram
  • Multitasking atau konkurensi
  • Antarmuka manajemen yang intuitif
  • Setidaknya keamanan

Pada titik tertentu, diputuskan untuk meletakkan konfigurasi dalam file terpisah - dalam xml kasus kami (di sini seseorang dapat mengatakan bahwa mari kita semua di json, tapi kami melakukannya dalam xml dan puas)
Mari kita mulai dari awal:

Bagian 1: bot telegram sederhana


Kami mencari folder bot (bukan direktori) - BotFather (@BotFather) di Telegram

Botfather

Tulis / newbot
Selanjutnya, Anda perlu mencari nama untuk bot (dalam kasus saya, saya memanggil Haaaabr khusus untuk artikel) dan nama pengguna, yang harus diakhiri dengan "bot" (Haaaabr_bot)

Setelah itu, BotFather akan mengeluarkan token, yang akan kami gunakan:

gambar

Kemudian Anda dapat mengunggah gambar untuk bot, letakkan Deskripsi, buat daftar perintah, tapi saya terlalu malas.

Kami membuat bot sederhana yang akan menerima pesan dan meresponsnya.

Saya akan menulis kode PS di bagian dan secara berkala memasukkan kode lengkap untuk referensi.

Untuk referensi, kita akan memerlukan deskripsi panggilan API Telegram Bot API

Kami membutuhkan 2 metode:

getUpdates - menerima pesan dengan bot (skrip)
sendMessage - mengirim pesan dengan bot (skrip) ke pengguna

Di sana, kita melihat bahwa:
Membuat permintaan
Semua pertanyaan ke Telegram Bot API harus dilayani lebih dari HTTPS dan perlu disajikan dalam formulir ini: api.telegram.org/bot<token>/METHOD_NAME

Langkah 1 - terima pesan
Variabel

# Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" 

Sekarang kita akan memeriksa apakah ia mengembalikan panggilan ke $ URL_get

 Invoke-RestMethod -Uri $URL_get 

ok result
-- ------
True {}


Tidak buruk. Mari kita menulis sesuatu ke bot:

Halo

Dan baca:

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" Invoke-RestMethod -Uri $URL_get 


ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
}

Jelas, kami butuh hasil. Saya harus segera mengatakan bahwa kami hanya tertarik pada pesan terakhir dari pengguna, jadi seperti ini:

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" $json = Invoke-RestMethod -Uri $URL_get $data = $json.result | Select-Object -Last 1 $data.update_id $data.message.chat.id $data.message.text $data.message.chat.first_name $data.message.chat.last_name $data.message.chat.type $data.message.chat.username 

Sekarang kita perlu mengkonfirmasi bahwa kami menerima pesan. Semua ini dilakukan dengan cara yang sama, melalui metode getUpdates dengan parameter ofset :
Secara default, pembaruan yang dimulai dengan pembaruan paling awal yang belum dikonfirmasi dikembalikan. Pembaruan dianggap dikonfirmasi segera setelah getUpdates dipanggil dengan offset yang lebih tinggi daripada pembaruan_idnya

Lakukan

 Invoke-RestMethod "$($URL_get)?offset=$($($data.update_id)+1)" -Method Get | Out-Null 

Dan kami membuang semuanya ke dalam siklus dengan batas waktu 1 detik:

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 while($true) #   { $json = Invoke-RestMethod -Uri $URL_get $data = $json.result | Select-Object -Last 1 $data.update_id $data.message.chat.id $data.message.text $data.message.chat.first_name $data.message.chat.last_name $data.message.chat.type $data.message.chat.username Invoke-RestMethod "$($URL_get)?offset=$($($data.update_id)+1)" -Method Get | Out-Null Start-Sleep -s $timeout } 

Sekarang mari kita buat fungsi pembacaan pesan darinya. Karena kita perlu mengembalikan beberapa nilai dari fungsi - kita memutuskan untuk menggunakan HashTable (bernama / array asosiatif)

Skrip Penerimaan Pesan
 # Token $token = "***********************"# Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #$data.update_id $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username #   text  if($text) { # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null # HashTable $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username return $ht } } while($true) #   { #   getUpdates $URL_get Start-Sleep -s $timeout } 



Langkah 2 - mengirim data
Untuk mengirim pesan, kita memerlukan metode sendMessage dan chat_id dan bidang teks (sisanya opsional https://core.telegram.org/bots/api#sendmessage ).

Segera fungsi gash

 function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } 

Sekarang dengan menelepon

 sendMessage $URL_set <__id> "123" 

dapatkan pesan di kereta.

Langkah 3 - Menyatukan Semuanya

Di bawah ini adalah semua kode untuk mengirim dan menerima pesan

Tampilkan kode
 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #$data.update_id $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username #   text  if($text) { # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null # HashTable $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username return $ht } } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } while($true) #   { $return = getUpdates $URL_get if($return) { # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) } Start-Sleep -s $timeout } 


Logika lebih lanjut dapat dibangun berdasarkan $ return.text dan, misalnya, pernyataan switch :
 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 digunakan dalam cmdlet Get-Random, saya tidak bisa menanamkannya dalam kode di artikel, tetapi PS mengerti mereka secara asli
Dapatkan secara acak

Bagian 2: perlu tombol


Di bot telegram ada opsi untuk menentukan daftar perintah (buka di sini di ikon ini Ikon )
Awalnya, kami melakukan hal itu - ada seperangkat perintah, kami memberikan nama server atau layanan di sana sebagai parameter. Kemudian kami memutuskan bahwa kami perlu bergerak lebih jauh menuju antarmuka yang ramah pengguna dan menghubungkan fungsi tombol.

Digunakan oleh panggilan sendMessage dengan parameter reply_markup

Untuk fungsionalitas kami, kami menggunakan tipe InlineKeyboardMarkup
https://core.telegram.org/bots/api#inlinekeyboardmarkup .

Dari uraian tersebut, bidang inline_keyboard adalah larik array tombol
(Array Array of InlineKeyboardButton)

Mencoba membuat tombol kirim tes

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" #   callback_data  ,     $button1 = @{ "text" = "Test1"; callback_data = "Test1_CD"} $button2 = @{ "text" = "Test2"; callback_data = "Test2_CD"} $keyboard = @{"inline_keyboard" = @(,@($button1, $button2))} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = ******** #     Telegram ID text = "Test Text" } $json = $ht | ConvertTo-Json Invoke-RestMethod $URL_set -Method Post -ContentType 'application/json; charset=utf-8' -Body $json 

Kami mendapatkan Galat:
Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Permintaan Buruk: bidang \" inline_keyboard \ "dari InlineKeyboardMarkup harus menjadi Array of Arays"}
Pada baris: 21 char: 1

Memeriksa isi variabel $ json

Kesimpulan:

 { "reply_markup": { "inline_keyboard": [ "System.Collections.Hashtable System.Collections.Hashtable" ] }, "chat_id": **********, "text": "Test Text", "parse_mode": "Markdown" } 

Rupanya, entah bagaimana, objek HashTable ("System.Collections.Hashtable System.Collections.Hashtable") untuk api telegram tidak terlalu mentransmisikan. Sedikit Google dan intinya - ketika mengkonversi ke Json, atur kedalaman konversi

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" #   callback_data  ,     $button1 = @{ "text" = "Test1"; callback_data = "Test1_CD"} $button2 = @{ "text" = "Test2"; callback_data = "Test2_CD"} $keyboard = @{"inline_keyboard" = @(,@($button1, $button2))} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = ******** text = "Test Text" } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL_set -Method Post -ContentType 'application/json; charset=utf-8' -Body $json 

Kami mendapatkan tombol:

Tombol

Kami membuat fungsi untuk mengirim tombol, kami akan mengirimkan berbagai tombol ke input

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" #   callback_data  ,     $button1 = @{ "text" = "Test1"; callback_data = "Test1_CD"} $button2 = @{ "text" = "Test2"; callback_data = "Test2_CD"} $buttons = ($button1, $button2) function sendKeyboard($URL, $buttons) { $keyboard = @{"inline_keyboard" = @(,$buttons)} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = ******** text = "Test Text" } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL_set -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } sendKeyboard $URL_set $buttons 

Menyatukan semuanya dengan mengubah sedikit saklar blok
 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #$data.update_id $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username #   text  if($text) { # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null # HashTable $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username return $ht } } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{"inline_keyboard" = @(,$buttons)} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } while($true) #   { $return = getUpdates $URL_get if($return) { # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons #sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) write-host "$($return["chat_id"])" switch -Wildcard ($return["text"]) { "**" { $button1 = @{ "text" = "Project1"; callback_data = "Project1_CD"} $button2 = @{ "text" = "Project2"; callback_data = "Project2_CD"} $buttons = ($button1, $button2) $text = "Available projects:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $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("", "", "", ""))"} } } Start-Sleep -s $timeout } 


Sekarang untuk "halo" bot akan mengirimi kami beberapa tombol. Masih memahami tombol mana yang diklik pengguna. Fungsi ps saat ini getUpdates telah memeriksa

 if($text)... 

Ketika Anda mengklik tombol, tidak ada teks yang dikembalikan, karena itu, Anda perlu memodifikasi fungsinya. Klik pada tombol

Tombol tekan

Dan jalankan sepotong kode untuk memeriksa isi $ data

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 $data <# $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username #   text  if($text) { # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null # HashTable $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username return $ht } #> } getUpdates $URL_get 

Tidak ada pesan lagi. Sebagai gantinya, sekarang callback_query . Edit fungsi

 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #    if($data.callback_query) { $callback_data = $data.callback_query.data $chat_id = $data.callback_query.from.id $f_name = $data.callback_query.from.first_name $l_name = $data.callback_query.from.last_name $username = $data.callback_query.from.username } #   elseif($data.message) { $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username } $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username $ht["callback_data"] = $callback_data # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null return $ht } getUpdates $URL_get 

Sekarang fungsi mengembalikan teks jika ada pesan, atau callback_data jika ada klik tombol. Pada tahap pengujian, mereka menemukan kesalahan saat memanggil:

 sendMessage $URL_set $($return.chat_id) $($return.callback_data) 

Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Permintaan Buruk: tidak dapat menguraikan entitas: Tidak dapat menemukan akhir entitas mulai dari byte offset 8"}

Karena parse_mode diatur ke Markdown , dan teks yang akan dikirim

 $return.callback_data = β€œProject1_CD” 

Anda perlu memformat pesan sebelum mengirim, lebih detail di sini:
https://core.telegram.org/bots/api#formatting-options
atau hapus garis bawah "_"

Naskah akhir
 # Token $token = "***********************" # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" # timeout sec $timeout = 1 function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #   $text = $null $callback_data = $null #    if($data.callback_query) { $callback_data = $data.callback_query.data $chat_id = $data.callback_query.from.id $f_name = $data.callback_query.from.first_name $l_name = $data.callback_query.from.last_name $username = $data.callback_query.from.username } #   elseif($data.message) { $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username } $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username $ht["callback_data"] = $callback_data # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null return $ht } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{"inline_keyboard" = @(,$buttons)} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } while($true) #   { $return = getUpdates $URL_get #$return #    if($return.text) { # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons #sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) write-host "$($return["chat_id"])" switch -Wildcard ($return["text"]) { "**" { $button1 = @{ "text" = "Project1"; callback_data = "Project1CD"} $button2 = @{ "text" = "Project2"; callback_data = "Project2CD"} $buttons = ($button1, $button2) $text = "Available projects:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $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("", "", "", ""))"} } } #      elseif($return.callback_data) { sendMessage $URL_set $($return.chat_id) $($return.callback_data) } Start-Sleep -s $timeout } 


Bagian 3: lakukan konfigurasi

Saatnya untuk meletakkan semuanya di konfigurasi. Semuanya sederhana di sini - kami melakukannya 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> 

Kami menjelaskan tugas dan menentukan skrip atau perintah untuk setiap tugas.
Kami memeriksa:

 [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 #   $task.script #  } 

Menempatkannya di skrip utama
 [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text' # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #   $text = $null $callback_data = $null #    if($data.callback_query) { $callback_data = $data.callback_query.data $chat_id = $data.callback_query.from.id $f_name = $data.callback_query.from.first_name $l_name = $data.callback_query.from.last_name $username = $data.callback_query.from.username } #   elseif($data.message) { $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username } $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username $ht["callback_data"] = $callback_data # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null return $ht } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{"inline_keyboard" = @(,$buttons)} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } while($true) #   { $return = getUpdates $URL_get #    if($return.text) { # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons #sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) write-host "$($return["chat_id"])" switch -Wildcard ($return["text"]) { "**" { #   $buttons = @() foreach($task in $xmlConfig.config.tasks.task) { $button = @{ "text" = $task.name; callback_data = $task.script} $buttons += $button } $text = "Available tasks:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $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("", "", "", ""))"} } } #      elseif($return.callback_data) { sendMessage $URL_set $($return.chat_id) $($return.callback_data) } Start-Sleep -s $timeout } 


Sekarang, jika Anda menulis "halo", bot akan mengembalikan daftar tombol yang sesuai dengan tugas yang dijelaskan dalam file xml. Akan ada perintah atau skrip di callback_data.

Jika Anda melakukan perubahan kosmetik - diinginkan bahwa tombolnya 3-4 per baris, jika tidak mereka tidak sepenuhnya ditampilkan:

Keyboard

Kami akan melakukan 3 tombol per baris (maksimum).

Secara skematis, susunan keyboard akan terlihat seperti ini:

Keyboard

Dengan cara ini:
Tombol [i] - array (asosiatif) dari formulir

 $button = @{ "text" = $task.name; callback_data = $task.script} 

Baris [1-3] - ini adalah susunan (dari tombol) yang menyimpan susunan tombol (ini penting)
Keyboard - larik Line'ov.

Ubah fungsi sendKeyboard

 function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{} #    ArrayList, .       -   $lines = 3 $buttons_line = New-Object System.Collections.ArrayList for($i=0; $i -lt $buttons.Count; $i++) { #     (line).    3 -  line  keyboard $buttons_line.Add($buttons[$i]) | Out-Null #   -      0 if( ($i + 1 )%$lines -eq 0 ) { #     keyboard $keyboard["inline_keyboard"] += @(,@($buttons_line)) $buttons_line.Clear() } } #     $keyboard["inline_keyboard"] += @(,@($buttons_line)) #$keyboard = @{"inline_keyboard" = @(,$buttons)} $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } 

Kami memeriksa:

Keyboard_Telegram

Naskah akhir
 [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text' # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #   $text = $null $callback_data = $null #    if($data.callback_query) { $callback_data = $data.callback_query.data $chat_id = $data.callback_query.from.id $f_name = $data.callback_query.from.first_name $l_name = $data.callback_query.from.last_name $username = $data.callback_query.from.username } #   elseif($data.message) { $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username } $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username $ht["callback_data"] = $callback_data # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null return $ht } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{} #    ArrayList, .       -   $lines = 3 $buttons_line = New-Object System.Collections.ArrayList for($i=0; $i -lt $buttons.Count; $i++) { #     (line).    3 -  line  keyboard $buttons_line.Add($buttons[$i]) | Out-Null #   -      0 if( ($i + 1 )%$lines -eq 0 ) { #     keyboard $keyboard["inline_keyboard"] += @(,@($buttons_line)) $buttons_line.Clear() } } #     $keyboard["inline_keyboard"] += @(,@($buttons_line)) $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } while($true) #   { $return = getUpdates $URL_get #$return.text = "" #    if($return.text) { # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons #sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) switch -Wildcard ($return["text"]) { "**" { #   $buttons = @() foreach($task in $xmlConfig.config.tasks.task) { $i++ $button = @{ "text" = $task.name; callback_data = $task.script} $buttons += $button } $text = "Available tasks:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $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("", "", "", ""))"} } } #      elseif($return.callback_data) { #sendMessage $URL_set $($return.chat_id) $($return.callback_data) write-host "$($return.chat_id) $($return.callback_data)" } Start-Sleep -s $timeout } 


Bagian 4: penugasan dan multitasking


Saatnya tombol untuk melakukan sesuatu.

Untuk multitasking, kami akan menggunakan mekanisme Pekerjaan. Periksa bagian kode ini:

 $script = "ipconfig" $script_block = { Param($script) ; Invoke-Expression $script } $job_name = "TestJob" Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null 

Dan setelah 5 detik kami melakukannya:
 foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job $output $job | Remove-Job } 

$ output akan mengembalikan ipconfig dengan localhost

Tambahkan ini ke skrip utama, ke blok callback_data

 #      elseif($return.callback_data) { $script = $($return.callback_data) $job_name = $($return.chat_id) $script_block = { Param($script) ; Invoke-Expression $script } # Job Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null } 

Dan ini di bawah

 # ,  job'   foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job #   ,   job sendMessage $URL_set $job.Name $output $job | Remove-Job #     $text = "Available tasks:" sendKeyboard $URL_set $buttons $job.Name $text } 

Periksa, tangkap kesalahan
Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Permintaan Buruk: pesan terlalu panjang"}

Di Internet, kami menemukan informasi bahwa panjang pesan tidak boleh melebihi 4096 karakter. Oke ...

 $output.Length 
mengatakan panjang 39. Kami
berpikir untuk waktu yang lama apa yang salah, sebagai hasilnya, kami mencoba potongan kode ini:

 $text = $null foreach($string in $output) { $text = "$text`n$string" } sendMessage $URL_set $job.Name $text 

Kami mencoba semuanya bersama
 [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text' # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #   $text = $null $callback_data = $null #    if($data.callback_query) { $callback_data = $data.callback_query.data $chat_id = $data.callback_query.from.id $f_name = $data.callback_query.from.first_name $l_name = $data.callback_query.from.last_name $username = $data.callback_query.from.username } #   elseif($data.message) { $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username } $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username $ht["callback_data"] = $callback_data # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null return $ht } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{} $lines = 3 #    ArrayList, .       -   $buttons_line = New-Object System.Collections.ArrayList for($i=0; $i -lt $buttons.Count; $i++) { #     (line).    3 -  line  keyboard $buttons_line.Add($buttons[$i]) | Out-Null #   -      0 if( ($i + 1 )%$lines -eq 0 ) { #     keyboard $keyboard["inline_keyboard"] += @(,@($buttons_line)) $buttons_line.Clear() } } #     $keyboard["inline_keyboard"] += @(,@($buttons_line)) $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } while($true) #   { $return = getUpdates $URL_get #$return.text = "" #    if($return.text) { # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons #sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) switch -Wildcard ($return["text"]) { "**" { #   $buttons = @() foreach($task in $xmlConfig.config.tasks.task) { $i++ $button = @{ "text" = $task.name; callback_data = $task.script} $buttons += $button } $text = "Available tasks:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $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("", "", "", ""))"} } } #      elseif($return.callback_data) { $script = $($return.callback_data) $job_name = $($return.chat_id) write-host "$script $job_name" $script_block = { Param($script) ; Invoke-Expression $script } # Job Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null } # ,  job'   foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job $text = $null foreach($string in $output) { $text = "$text`n$string" } #   ,   job sendMessage $URL_set $job.Name $text $job | Remove-Job #     $text = "Available tasks:" sendKeyboard $URL_set $buttons $job.Name $text } Start-Sleep -s $timeout } 


Keluaran

Sekarang mari kita sedikit keamanan.

Tambahkan baris baru ke konfigurasi xml, sebut saja pengguna dan tunjukkan ada chat_id dari mereka yang dapat berkomunikasi dengan bot:

  <system> <token>*********************************</token> <timeout desc="bot check timeout in seconds">1</timeout> <users>111111111, 222222222</users> </system> 

Dalam skrip kita akan mendapatkan array pengguna

 $users = (($xmlConfig.config.system.users).Split(",")).Trim() 

Dan periksa

  if($users -contains $return.chat_id) { ... } 

Skrip lengkap
 [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() # Telegram URLs $URL_get = "https://api.telegram.org/bot$token/getUpdates" $URL_set = "https://api.telegram.org/bot$token/sendMessage" function getUpdates($URL) { $json = Invoke-RestMethod -Uri $URL $data = $json.result | Select-Object -Last 1 #   $text = $null $callback_data = $null #    if($data.callback_query) { $callback_data = $data.callback_query.data $chat_id = $data.callback_query.from.id $f_name = $data.callback_query.from.first_name $l_name = $data.callback_query.from.last_name $username = $data.callback_query.from.username } #   elseif($data.message) { $chat_id = $data.message.chat.id $text = $data.message.text $f_name = $data.message.chat.first_name $l_name = $data.message.chat.last_name $type = $data.message.chat.type $username = $data.message.chat.username } $ht = @{} $ht["chat_id"] = $chat_id $ht["text"] = $text $ht["f_name"] = $f_name $ht["l_name"] = $l_name $ht["username"] = $username $ht["callback_data"] = $callback_data # confirm Invoke-RestMethod "$($URL)?offset=$($($data.update_id)+1)" -Method Get | Out-Null return $ht } function sendMessage($URL, $chat_id, $text) { #  HashTable,       $ht = @{ text = $text #    Markdown parse_mode = "Markdown" chat_id = $chat_id } #      json $json = $ht | ConvertTo-Json #   Invoke-RestMethod,        Invoke-WebRequest # Method Post - ..  ,   Get Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json | Out-Null } function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{} $lines = 3 #    ArrayList, .       -   $buttons_line = New-Object System.Collections.ArrayList for($i=0; $i -lt $buttons.Count; $i++) { #     (line).    3 -  line  keyboard $buttons_line.Add($buttons[$i]) | Out-Null #   -      0 if( ($i + 1 )%$lines -eq 0 ) { #     keyboard $keyboard["inline_keyboard"] += @(,@($buttons_line)) $buttons_line.Clear() } } #     $keyboard["inline_keyboard"] += @(,@($buttons_line)) $ht = @{ parse_mode = "Markdown" reply_markup = $keyboard chat_id = $chat_id text = $text } $json = $ht | ConvertTo-Json -Depth 5 Invoke-RestMethod $URL -Method Post -ContentType 'application/json; charset=utf-8' -Body $json } while($true) #   { $return = getUpdates $URL_get if($users -contains $return.chat_id) { #    if($return.text) { #write-host $return.chat_id # http://apps.timwhitlock.info/emoji/tables/unicode#block-1-emoticons #sendMessage $URL_set $return.chat_id (Get-Random("", "", "", "")) switch -Wildcard ($return["text"]) { "**" { #   $buttons = @() foreach($task in $xmlConfig.config.tasks.task) { $i++ $button = @{ "text" = $task.name; callback_data = $task.script} $buttons += $button } $text = "Available tasks:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $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("", "", "", ""))"} } } #      elseif($return.callback_data) { $script = $($return.callback_data) $job_name = $($return.chat_id) write-host "$script $job_name" $script_block = { Param($script) ; Invoke-Expression $script } # Job Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null } # ,  job'   foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job $text = $null foreach($string in $output) { $text = "$text`n$string" } #   ,   job sendMessage $URL_set $job.Name $text $job | Remove-Job #     $text = "Available tasks:" sendKeyboard $URL_set $buttons $job.Name $text } } else { if($return.text) { sendMessage $URL_set $return.chat_id "  ?    !" } } Start-Sleep -s $timeout } 


Bagian 5: sebagai kesimpulan


Kami memeriksa fungsionalitas bot - tambahkan skrip di sana yang akan melakukan sesuatu yang berguna.
Untuk operasi di server jauh, kami menggunakan Invoke-Command diikuti oleh 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 

Dalam hal ini, akun yang menjalankan skrip bot telegram harus memiliki hak yang sesuai pada mesin jarak jauh.

Juga, saya tidak menyentuh fungsionalitas logging, tetapi di sini, saya pikir, semuanya sederhana, sesuka hati, semua orang dapat memutuskan apa yang ingin dia login dan apa yang tidak.

Tentunya seseorang akan mengalami masalah dalam mengirim pesan> 4096 karakter, tetapi ini diselesaikan oleh Substring dan siklus pengiriman.

Dan akhirnya - kendali jarak jauh dari mana saja di dunia (hampir dari mana saja) itu baik, tetapi selalu ada risiko bahwa ada sesuatu yang salah (seseorang yang buruk bisa mendapatkan kendali bot). Untuk kasus ini, kami baru saja menambahkan Keluar dari skrip untuk kata tertentu

  switch -Wildcard ($return["text"]) { "**" { #   $buttons = @() foreach($task in $xmlConfig.config.tasks.task) { $i++ $button = @{ "text" = $task.name; callback_data = $task.script} $buttons += $button } $text = "Available tasks:" $chat_id = $return.chat_id sendKeyboard $URL_set $buttons $chat_id $text #sendMessage $URL_set $return.chat_id ", $($return["f_name"])" } "* ?*" { sendMessage $URL_set $return.chat_id "" } "!" {sendMessage $URL_set $return.chat_id "bb" ; Exit} default {sendMessage $URL_set $return.chat_id "$(Get-Random("", "", "", ""))"} } 

Saya memiliki semuanya.

Source: https://habr.com/ru/post/id483660/


All Articles