
根据文章,
系统管理员的
Telegram机器人 (本文不是我的,我只是读过)想要分享在PowerShell上创建Telegram bot来管理应用程序服务器的经验。 将有文本,代码和一些图片。 欢迎进行建设性的批评(主要的意思不是说“为什么要在PowerShell上使用?应该在perl上使用”)。
我认为本文将更适合PowerShell的新手,但是经验丰富的管理员可以在这里看到有用的信息。
他尝试将文章从简单到复杂分为几部分。 也许会发生窃,请小心!
因此,我们需要管理多个服务器(停止,启动)上的服务或应用程序,重新启动服务器,必要时查看日志和其他一些信息。 我想做的所有事情(实际上不是),是在地铁里,在商店里还是躺在沙发上,而没有VPN和笔记本电脑。 要求中的(当然是写在膝盖上的)。
- 易于在Telegram机器人中添加/更改任务
- 多任务或并发
- 直观的管理界面
- 至少有些安全
在某个时候,决定将配置放在一个单独的文件中-在我们的例子中是xml(在这里我们可以说让我们全部使用json,但是我们在xml中进行了操作并感到满意)
让我们从头开始:
第1部分:一个简单的电报机器人
我们正在寻找bot文件夹(而非目录)
-Telegram中的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步-接收消息变数
现在,我们将检查它是否返回对
$ URL_get的调用
Invoke-RestMethod -Uri $URL_get
ok result
-- ------
True {}
还不错 让我们为机器人写一些东西:

并阅读:
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
}
显然,我们需要结果。 我必须马上说,我们只对来自用户的最后一条消息感兴趣,所以像这样:
现在,我们需要确认我们已收到消息。 通过使用带有
offset参数的
getUpdates方法可以完全相同:
默认情况下,将返回以最早的未经确认的更新开始的更新。 一旦调用getUpdates且偏移量高于其update_id,就认为更新已确认
做
Invoke-RestMethod "$($URL_get)?offset=$($($data.update_id)+1)" -Method Get | Out-Null
我们将所有内容放入一个周期为1秒的循环中:
现在,让我们利用它来实现消息读取功能。 因为 我们需要从函数中返回几个值-我们决定使用HashTable(命名为/关联数组)
第2步-发送数据要发送消息,我们需要
sendMessage方法以及
chat_id和
text字段(其余均为可选
https://core.telegram.org/bots/api#sendmessage )。
立即下气功能
function sendMessage($URL, $chat_id, $text) {
现在通过致电
sendMessage $URL_set <__id> "123"
在购物车中收到消息。
步骤3-全部放在一起以下是用于发送和接收消息的所有代码
可以在
$ 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的数组的数组)
尝试进行测试发送按钮
我们得到错误:
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时,设置转换深度
我们得到的按钮:

我们创建了一个用于发送按钮的函数,我们将向输入提交按钮数组
现在,为了“你好”,该机器人将向我们发送几个按钮。 仍然需要了解用户单击了哪个按钮。 当前的ps函数getUpdates会检查
if($text)...
当按下按钮时,不返回任何文本;因此,需要修改该功能。 点击按钮

并运行一段代码来检查
$数据的内容
不再有消息到达。 相反,现在是
callback_query 。 编辑功能
现在,如果有消息,该函数将返回
文本;如果有按钮单击,则该函数将返回
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或删除下划线“ _”
第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
将其放在主脚本中 [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
现在,如果您编写“ 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 = @{}
我们检查:

最终脚本 [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
第4部分:任务和多任务
现在该按钮执行操作了。对于多任务,我们将使用Job机制。检查这段代码: $script = "ipconfig" $script_block = { Param($script) ; Invoke-Expression $script } $job_name = "TestJob" Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null
5秒后,我们执行以下操作: foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job $output $job | Remove-Job }
$输出应返回localhost的ipconfig将此添加到主脚本中,到callback_data块中
这是下面
检查并捕获错误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'
现在让我们提高一点安全性,在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()
第五部分:总结
我们检查机器人的功能-在其中添加有用的脚本对于远程服务器上的操作,我们使用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"]) { "**" {
我都拥有