用于基础架构管理的电报机器人

图片

根据文章, 系统管理员Telegram机器人 (本文不是我的,我只是读过)想要分享在PowerShell上创建Telegram bot来管理应用程序服务器的经验。 将有文本,代码和一些图片。 欢迎进行建设性的批评(主要的意思不是说“为什么要在PowerShell上使用?应该在perl上使用”)。

我认为本文将更适合PowerShell的新手,但是经验丰富的管理员可以在这里看到有用的信息。

他尝试将文章从简单到复杂分为几部分。 也许会发生窃,请小心!

因此,我们需要管理多个服务器(停止,启动)上的服务或应用程序,重新启动服务器,必要时查看日志和其他一些信息。 我想做的所有事情(实际上不是),是在地铁里,在商店里还是躺在沙发上,而没有VPN和笔记本电脑。 要求中的(当然是写在膝盖上的)。

  • 易于在Telegram机器人中添加/更改任务
  • 多任务或并发
  • 直观的管理界面
  • 至少有些安全

在某个时候,决定将配置放在一个单独的文件中-在我们的例子中是xml(在这里我们可以说让我们全部使用json,但是我们在xml中进行了操作并感到满意)
让我们从头开始:

第1部分:一个简单的电报机器人


我们正在寻找bot文件夹(而非目录) -Telegram中的BotFather(@BotFather)

Botfather

/ newbot
接下来,您需要为机器人命名(在我的情况下,我专门为文章称呼Haaaabr)和用户名,用户名应以“ bot”结尾(Haaaabr_bot)

之后,BotFather将发行一个令牌,我们将使用它:

图片

然后,您可以为机器人上载图片,放入Description(描述),创建命令列表,但是我太懒了。

我们制作了一个简单的机器人,可以接收消息并做出响应。

我将分部分编写PS代码,并定期插入完整代码以供参考。

作为参考,我们将需要Telegram Bot API API调用说明

我们将需要2种方法:

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 

现在,我们需要确认我们已收到消息。 通过使用带有offset参数的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_idtext字段(其余均为可选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语句的基础上构建进一步的逻辑:
 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部分:需要按钮


在电报机器人中,有一个选项可以指定命令列表(在此图标上打开) 图示
最初,我们只是这样做-有一组命令,我们将服务器或服务的名称作为参数传递。 然后,我们决定需要进一步朝着用户友好的界面发展,并连接按钮功能。

由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”:否,“ error_code”:400,“ description”:“错误的请求:InlineKeyboardMarkup的字段\” inline_keyboard \“应该是一个数组数组”}
在线:21个字符:1

检查$ json变量包含什么

结论:

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

显然,以某种方式,电报api的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)... 

当按下按钮时,不返回任何文本;因此,需要修改该功能。 点击按钮

按钮

并运行一段代码来检查$数据的内容

 # 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”:否,“ error_code”:400,“ description”:“错误的请求:无法解析实体:找不到以字节偏移量8开始的实体结尾”}

由于parse_mode设置为Markdown ,并且要发送的文本

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


现在,如果您编写“ hello”,则机器人将返回与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 } 

我们检查:

键盘电报

最终脚本
 [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部分:任务和多任务


现在该按钮执行操作了。

对于多任务,我们将使用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 } 

$输出应返回localhost的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:{“确定”:false,“错误代码”:400,“描述”:“错误的请求:消息太长”}

在Internet上,我们发现信息,消息长度不能超过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配置中添加新行,将其命名为users,并在其中指示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 } 


第五部分:总结


我们检查机器人的功能-在其中添加有用的脚本
对于远程服务器上的操作,我们使用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和send循环解决。

最后,从世界任何地方(几乎在任何地方)进行远程控制都是不错的选择,但是总有可能出问题(不好的人可以得到机器人控制)。对于这种情况,我们只为特定的单词添加了退出脚本

  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/zh-CN483660/


All Articles