Telegram-bot لإدارة البنية التحتية

صورة

بناءً على المقالة ، أراد برنامج Telegram bot الخاص بمسؤول النظام (المقالة ليس مقالاً ، لقد قرأت للتو) مشاركة تجربة إنشاء روبوت Telegram على PowerShell لإدارة خوادم التطبيقات. سيكون هناك نص ورمز وعدد قليل من الصور. النقد البناء هو موضع ترحيب (الشيء الرئيسي هو عدم القول "لماذا على PowerShell؟ كان ينبغي أن يكون على بيرل").

أعتقد أن هذه المقالة ستكون أكثر ملاءمة للقادمين الجدد إلى PowerShell ، ولكن يمكن للمسؤولين ذوي الخبرة رؤية شيء مفيد هنا.

حاول بناء المقال في أجزاء - من البسيط إلى المعقد. ربما سوف يحدث الانتحال ، كن حذرا!

لذلك ، نحن بحاجة إلى إدارة الخدمات أو التطبيقات على عدة خوادم (إيقاف ، بدء) ، إعادة تشغيل الخادم ، إلقاء نظرة على السجلات وبعض المعلومات الأخرى إذا لزم الأمر. كل هذا أريد القيام به (في الواقع لا) ، في مترو الأنفاق ، في المتجر أو حتى الاستلقاء على الأريكة ، بدون VPN وأجهزة الكمبيوتر المحمولة. من المتطلبات (التي كانت مكتوبة ، بالطبع ، على الركبة).

  • سهلة لإضافة / تغيير المهام في روبوت Telegram
  • تعدد المهام أو التزامن
  • واجهة إدارة بديهية
  • على الأقل بعض الأمن

في مرحلة ما ، تقرر وضع التهيئة في ملف منفصل - في حالتنا xml (هنا يمكن للشخص أن يقول إن كل شيء في json ، لكننا فعلنا ذلك في xml ورضا)
لنبدأ من البداية:

الجزء 1: روبوت برقية بسيط


نحن نبحث عن مجلد bot (وليس دليل) - BotFather (BotFather) في Telegram

BotFather

اكتب / newbot
بعد ذلك ، عليك أن تجد اسمًا للبوت (في حالتي ، اتصلت Haaaabr خصيصًا للمادة) واسم المستخدم ، والتي يجب أن تنتهي بـ "bot" (Haaaabr_bot)

بعد ذلك ، سيصدر BotFather رمزًا مميزًا ، والذي سنستخدمه:

صورة

بعد ذلك يمكنك تحميل صورة للروبوت ، ووضع الوصف ، وإنشاء قائمة من الأوامر ، لكنني كنت كسولًا جدًا.

نحن نصنع روبوتًا بسيطًا يتلقى الرسائل ويستجيب لها.

سأكتب رمز PS في الأجزاء وأدخل الرمز الكامل للمرجع بشكل دوري.

للرجوع إليها ، سنحتاج إلى أوصاف استدعاء API Telegram Bot

سنحتاج إلى طريقتين:

getUpdates - تلقي الرسائل عن طريق الروبوت (البرنامج النصي)
sendMessage - إرسال الرسائل بواسطة الروبوت (البرنامج النصي) إلى المستخدم

هناك ، نرى أن:
تقديم الطلبات
يجب تقديم جميع الاستفسارات إلى Telegram Bot API عبر HTTPS ويجب تقديمها في هذا النموذج: api.telegram.org/bot<token>/METHOD_NAME

الخطوة 1 - تلقي الرسائل
المتغيرات

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

الآن سوف نتحقق من أنها ستعيد مكالمة إلى $ URL_get

 Invoke-RestMethod -Uri $URL_get 

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


ليس سيئا دعنا نكتب شيئا للبوت:

مرحبا

وقراءة:

 # 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=}
}

من الواضح أننا نحتاج إلى نتيجة. يجب أن أقول على الفور أننا مهتمون فقط بالرسالة الأخيرة من المستخدم ، مثل هذا:

 # 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 

الآن نحن بحاجة إلى تأكيد أننا تلقينا الرسالة. يتم كل هذا من خلال طريقة getUpdates مع معلمة الإزاحة :
بشكل افتراضي ، يتم إرجاع التحديثات التي تبدأ بأقرب تحديث غير مؤكد. يعتبر التحديث مؤكدًا بمجرد استدعاء getUpdates بإزاحة أعلى من update_id الخاص به

فعل

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

ونحن نلقي كل ذلك في دورة مع مهلة من 1 ثانية:

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

الآن دعونا نجعل رسالة قراءة وظيفة للخروج منه. لأن نحتاج إلى إرجاع العديد من القيم من الوظيفة - قررنا استخدام HashTable (مجموعة المسمى / النقابي)

رسالة تلقي النصي
 # 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 } 



الخطوة 2 - إرسال البيانات
لإرسال رسالة ، نحتاج إلى أسلوب sendMessage والحقول النصية و chat_id (الباقي عبارة عن https://core.telegram.org/bots/api#sendmessage ) اختياري.

وظيفة الكاش على الفور

 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 } 

الآن عن طريق الاتصال

 sendMessage $URL_set <__id> "123" 

احصل على رسالة في العربة

الخطوة 3 - وضع كل شيء معا

أدناه هو كل رمز لإرسال واستقبال الرسائل

إظهار الرمز
 # 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 } 


يمكن بناء منطق إضافي على أساس $ return.text ، على سبيل المثال ، عبارة التبديل :
 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("", "", "", ""))"} } 

رمز تعبيري:
يتم استخدام الرموز التعبيرية في أمر Get-Random cmdlet ، لكن لا يمكنني تضمينها في الكود في المقالة ، لكن PS يفهمها أصليًا
الحصول عشوائي

الجزء 2: أزرار الحاجة


يوجد في telegram bot خيار لتحديد قائمة بالأوامر (يفتح هنا على هذا الرمز أيقونة )
في البداية ، لقد فعلنا ذلك تمامًا - كان هناك مجموعة من الأوامر ، لقد تجاوزنا أسماء الخوادم أو الخدمات هناك كمعلمات. ثم قررنا أننا نحتاج إلى المضي قدمًا نحو واجهات سهلة الاستخدام وتوصيل وظائف الأزرار.

يستخدم بواسطة sendMessage المكالمات مع معلمة reply_markup

للحصول على وظائفنا ، استخدمنا نوع InlineKeyboardMarkup
https://core.telegram.org/bots/api#inlinekeyboardmarkup .

من الوصف ، يتبع أن الحقل inline_keyboard عبارة عن صفيف من مجموعة من الأزرار
(صفيف من صفيف InlineKeyboardButton)

محاولة إجراء اختبار إرسال الأزرار

 # 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 

حصلنا على خطأ:
Invoke-RestMethod: {"ok": false ، "error_code": 400 ، "description": "طلب غير صحيح: field \" inline_keyboard \ "من InlineKeyboardMarkup يجب أن يكون مصفوفة من المصفوفات"}
على الخط: 21 حرفًا: 1

التحقق من ما يحتوي عليه متغير json $

الاستنتاج:

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

على ما يبدو ، بطريقة ما ، لا يقوم كائن HashTable ("System.Collections.Hashtable System.Collections.Hashtable") الخاص ببرنامج التلغراف بالإرسال. القليل من Google والنتيجة النهائية - عند التحويل إلى Json ، قم بضبط عمق التحويل

 # 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 

نحصل على الأزرار:

أزرار

نحن نجعل وظيفة لإرسال الأزرار ، وسوف نقدم مجموعة من الأزرار كمدخلات

 # 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 

ضعها جميعًا معًا عن طريق تغيير كتلة التبديل قليلاً
 # 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 } 


الآن من أجل "مرحبًا" ، سيرسلنا الروبوت إلى اثنين من الأزرار. يبقى أن نفهم أي زر نقر المستخدم. وظيفة ps الحالية getUpdates لديها الاختيار ل

 if($text)... 

عند النقر فوق الزر ، لا يتم إرجاع أي نص ؛ وبالتالي ، تحتاج إلى تعديل الوظيفة. انقر فوق الزر

PushTheButton

وتشغيل قطعة من الكود للتحقق من محتويات البيانات $

 # 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 

لا توجد رسالة تصل بعد الآن. بدلاً من ذلك ، الآن callback_query . تحرير وظيفة

 # 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 

تقوم الدالة الآن بإرجاع النص إذا كانت هناك رسالة ، أو callback_data إذا كان هناك نقرة زر. في مرحلة الاختبار ، وقعوا خطأ عند الاتصال:

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

Invoke-RestMethod: {"ok": false ، "error_code": 400 ، "description": "طلب غير صالح: لا يمكن تحليل الكيانات: لا يمكن العثور على نهاية الكيان بدءًا من بايت 8"

منذ يتم تعيين parse_mode إلى تخفيض السعر ، والنص المراد إرسالها

 $return.callback_data = “Project1_CD” 

تحتاج إلى تنسيق الرسالة قبل الإرسال ، مزيد من التفاصيل هنا:
https://core.telegram.org/bots/api#formatting-options
أو قم بإزالة الشرطة السفلية "_"

النصي النهائي
 # 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 } 


الجزء 3: هل التكوين

حان الوقت لوضع كل شيء في التكوين. كل شيء بسيط هنا - نفعل 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> 

وصف المهام وتحديد برنامج نصي أو أمر لكل مهمة.
نتحقق من:

 [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 #  } 

ضعها في النص الرئيسي
 [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 } 


الآن ، إذا كتبت "مرحبًا" ، فسيرجع برنامج الروبوت قائمة الأزرار التي تتوافق مع المهام الموضحة في ملفات xml. سيكون هناك أمر أو برنامج نصي في callback_data.

إذا قمت بإجراء تغييرات تجميلية - من المستحسن أن تكون الأزرار 3-4 لكل سطر ، وإلا فلن يتم عرضها بالكامل:

لوحة مفاتيح

سنفعل 3 أزرار في كل سطر (الحد الأقصى).

بشكل تخطيطي ، يجب أن تبدو صفيف لوحة المفاتيح بالشكل التالي:

لوحة المفاتيح

بهذه الطريقة:
زر [i] - صفيف (نقابي) من النموذج

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

السطر [1-3] - هذه صفائف (من أزرار) تخزن صفيفات من الأزرار (هذا مهم)
لوحة المفاتيح - مجموعة من Line'ov.

تعديل وظيفة 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 } 

نتحقق من:

Keyboard_Telegram

النصي النهائي
 [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 } 


الجزء 4: المهام وتعدد المهام


حان الوقت للزر للقيام بالأشياء.

لتعدد المهام ، سوف نستخدم آلية الوظيفة. تحقق من هذا الكود:

 $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 } 

يجب أن يُرجع إخراج $ ipconfig مع مضيف محلي

أضف هذا إلى البرنامج النصي الرئيسي إلى كتلة 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 } 

وهذا أدناه

 # ,  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 } 

تحقق ، خطأ التقاط
Invoke-RestMethod: {"موافق": خطأ ، "error_code": 400 ، "وصف": "طلب غير صحيح: الرسالة طويلة جدًا"}

على الإنترنت ، نجد معلومات لا يمكن أن يتجاوز طول الرسالة 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' # 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 } 


إنتاج

الآن دعنا نضع في أمان قليل ،

أضف سطرًا جديدًا إلى xml config ، واتصل به للمستخدمين ، وأشر إلى هناك chat_id لأولئك الذين يمكنهم التواصل مع bot:

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

في البرنامج النصي ، سنحصل على مجموعة المستخدمين

 $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() # 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 } 


الجزء 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 

في هذه الحالة ، يجب أن يكون للحساب الذي سيتم تشغيل البرنامج النصي لـ telegram bot فيه الامتيازات المناسبة على الجهاز البعيد.

أيضًا ، لم أتطرق إلى وظيفة التسجيل ، ولكن هنا ، أعتقد ، أن كل شيء بسيط ، حسب الرغبة ، يمكن للجميع تحديد ما يريد تسجيله وما لا.

بالتأكيد سيواجه شخص ما مشكلة في إرسال رسالة> 4096 حرفًا ، ولكن يتم حل هذا بواسطة السلسلة الفرعية وحلقة الإرسال.

وأخيرًا - يعد التحكم عن بُعد من أي مكان في العالم (من أي مكان تقريبًا) جيدًا ، ولكن هناك دائمًا خطر حدوث خطأ ما (يمكن لأي شخص سيء التحكم في الروبوت). في هذه الحالة ، أضفنا للتو Exit من البرنامج النصي لكلمة معينة

  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("", "", "", ""))"} } 

لدي كل شيء.

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


All Articles