
Com base no artigo, o
bot do
Telegram para o administrador do sistema (o artigo não é meu, acabei de ler) queria compartilhar a experiência de criar um bot do Telegram no PowerShell para gerenciar servidores de aplicativos. Haverá texto, código e algumas fotos. Críticas construtivas são bem-vindas (o principal não é dizer "por que no PowerShell? Deveria estar em perl").
Acho que este artigo será mais adequado para iniciantes no PowerShell, mas administradores experientes podem ver algo útil aqui.
Ele tentou criar o artigo em partes - do simples ao complexo. Talvez ocorra plágio, tenha cuidado!
Portanto, precisamos gerenciar serviços ou aplicativos em vários servidores (parar, iniciar), reiniciar o servidor, examinar os logs e outras informações, se necessário. Tudo isso que eu quero fazer (na verdade não), estar no metrô, na loja ou mesmo deitado no sofá, sem uma VPN e laptops. Dos requisitos (que foram escritos, é claro, no joelho).
- Fácil de adicionar / alterar tarefas no bot Telegram
- Multitarefa ou simultaneidade
- Interface de gerenciamento intuitiva
- Pelo menos alguma segurança
Em algum momento, foi decidido colocar a configuração em um arquivo separado - no nosso caso xml (aqui alguém pode dizer que vamos todos em json, mas fizemos em xml e ficamos satisfeitos)
Vamos começar do começo:
Parte 1: um simples bot de telegrama
Estamos procurando uma pasta bot (não um diretório) -
BotFather (@BotFather) no Telegram

Write
/ newbotEm seguida, você precisa criar um nome para o bot (no meu caso, chamei o Haaaabr especificamente para o artigo) e um nome de usuário, que deve terminar em "bot" (Haaaabr_bot)
Depois disso, o BotFather emitirá um token, que usaremos:

Então você pode fazer upload de uma foto para o bot, colocar Descrição, criar uma lista de comandos, mas fiquei com preguiça.
Nós fazemos um bot simples que receberá mensagens e responderá a elas.
Escreverei o código PS em partes e periodicamente insiro o código completo para a referência.
Para referência, precisaremos de descrições de chamada da
API da API do Telegram BotVamos precisar de 2 métodos:
getUpdates - recebendo mensagens por bot (script)
sendMessage - enviando mensagens de um bot (script) para um usuário
Lá, vemos que:
Fazendo pedidos
Todas as consultas à API do Telegram Bot devem ser atendidas por HTTPS e precisam ser apresentadas neste formulário: api.telegram.org/bot<token>/METHOD_NAME
Etapa 1 - receber mensagensVariáveis
Agora vamos verificar se ele retorna uma chamada para
$ URL_get Invoke-RestMethod -Uri $URL_get
ok result
-- ------
True {}
Nada mal. Vamos escrever algo para o bot:

E leia:
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
ok result
-- ------
True {@{update_id=635172027; message=}, @{update_id=635172028; message=}
}
Obviamente, precisamos de resultado. Devo dizer imediatamente que estamos interessados apenas na última mensagem do usuário, assim:
Agora precisamos confirmar que recebemos a mensagem. Isso é feito da mesma maneira, através do método
getUpdates com o parâmetro
offset :
Por padrão, as atualizações iniciadas com a atualização não confirmada mais antiga são retornadas. Uma atualização é considerada confirmada assim que getUpdates é chamado com um deslocamento maior que seu update_id
Fazer
Invoke-RestMethod "$($URL_get)?offset=$($($data.update_id)+1)" -Method Get | Out-Null
E jogamos tudo em um ciclo com um tempo limite de 1 segundo:
Agora vamos criar uma função de leitura de mensagem. Porque precisamos retornar vários valores da função - decidimos usar um HashTable (matriz nomeada / associativa)
Script de recebimento de mensagens Etapa 2 - enviando dadosPara enviar uma mensagem, precisamos do método
sendMessage e dos campos
chat_id e
text (o restante é opcional
https://core.telegram.org/bots/api#sendmessage ).
Função de corte imediato
function sendMessage($URL, $chat_id, $text) {
Agora chamando
sendMessage $URL_set <__id> "123"
receba uma mensagem no carrinho.
Etapa 3 - Juntando tudoAbaixo está todo o código para enviar e receber mensagens
Lógica adicional pode ser construída com base em
$ return.text e, por exemplo, na
instrução 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 é usado no cmdlet Get-Random, não pude incorporá-los no código do artigo, mas o PS os entende nativamente

Parte 2: precisa de botões
No bot de telegrama, há uma opção para especificar uma lista de comandos (abre aqui neste ícone

)
Inicialmente, fizemos exatamente isso - havia um conjunto de comandos, passamos os nomes de servidores ou serviços lá como parâmetros. Decidimos então que precisávamos avançar ainda mais em direção às interfaces amigáveis ao usuário e conectamos a funcionalidade dos botões.
Usado pelas
chamadas sendMessage com o parâmetro
reply_markupPara nossa funcionalidade, usamos o tipo
InlineKeyboardMarkuphttps://core.telegram.org/bots/api#inlinekeyboardmarkup .
A partir da descrição, segue-se que o campo
inline_keyboard é uma matriz de uma matriz de botões
(Matriz de matriz de InlineKeyboardButton)
Tentando fazer um teste enviar botões
Temos erro:
Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Bad Request: field \" inline_keyboard \ "do InlineKeyboardMarkup deve ser uma matriz de matrizes"}
Na linha: 21 caracteres: 1Verificando o que a variável
$ json contém
Conclusão:
{ "reply_markup": { "inline_keyboard": [ "System.Collections.Hashtable System.Collections.Hashtable" ] }, "chat_id": **********, "text": "Test Text", "parse_mode": "Markdown" }
Aparentemente, de alguma forma, o objeto HashTable ("System.Collections.Hashtable System.Collections.Hashtable") da API do telegrama não está transmitindo muito. Um pouco do Google e os resultados - ao converter para Json, defina a profundidade da conversão
Temos os botões:

Fazemos uma função para enviar botões, enviaremos uma matriz de botões para a entrada
Juntando tudo, alterando um pouco o bloco do interruptor Agora, para "olá", o bot nos enviará alguns botões. Resta entender em qual botão o usuário clicou. A função ps atual getUpdates tem uma verificação para
if($text)...
Quando você clica no botão, nenhum texto é retornado; portanto, você precisa modificar a função. Clique no botão

E execute um pedaço de código para verificar o conteúdo de
$ data
Nenhuma mensagem chega mais. Em vez disso, agora
callback_query . Editar função
Agora, a função retorna
texto se houver uma mensagem ou
callback_data se houver um clique no botão. Na fase de teste, eles detectaram um erro ao chamar:
sendMessage $URL_set $($return.chat_id) $($return.callback_data)
Invoke-RestMethod: {"ok": false, "error_code": 400, "description": "Solicitação incorreta: não é possível analisar entidades: não é possível encontrar o final da entidade iniciando no deslocamento de bytes 8"}Como
parse_mode está definido como
Markdown , e o texto a ser enviado
$return.callback_data = “Project1_CD”
você precisa formatar a mensagem antes de enviar, mais detalhes aqui:
https://core.telegram.org/bots/api#formatting-optionsou remova o sublinhado "_"
Parte 3: faça a configuração
É hora de colocar tudo na configuração. Tudo é simples aqui - fazemos 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>
Descrevemos tarefas e especificamos um script ou comando para cada tarefa.
Verificamos:
[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
Colocando no script principal [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
Agora, se você escrever “olá”, o bot retornará uma lista de botões que correspondem às tarefas descritas nos arquivos xml. Haverá um comando ou script em callback_data.
Se você fizer alterações cosméticas - é desejável que os botões sejam de 3 a 4 por linha, caso contrário eles não serão totalmente exibidos:

Faremos 3 botões por linha (máximo).
Esquematicamente, a matriz do teclado deve ficar assim:

Desta forma:
Botão [i] - uma matriz (associativa) do formulário
$button = @{ "text" = $task.name; callback_data = $task.script}
Linha [1-3] - são matrizes (de botões) que armazenam matrizes de botões (isso é importante)
Teclado - uma matriz de Line'ov.
Modifique a função
sendKeyboard function sendKeyboard($URL, $buttons, $chat_id, $text) { $keyboard = @{}
Verificamos:

Roteiro final [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
Parte 4: tarefas e multitarefa
É hora do botão fazer as coisas.Para multitarefa, usaremos o mecanismo Job. Verifique este trecho de código: $script = "ipconfig" $script_block = { Param($script) ; Invoke-Expression $script } $job_name = "TestJob" Start-Job -ScriptBlock $script_block -ArgumentList $script -Name $job_name | Out-Null
E após 5 segundos, fazemos: foreach($job in (Get-Job | Where {$_.State -eq "Completed"} )) { $output = Get-Job -ID $job.Id | Receive-Job $output $job | Remove-Job }
$ output deve retornar ipconfig com localhostAdicione isso ao script principal, ao bloco callback_data
E isso está abaixo
Verifique, pegue o erroInvoke-RestMethod: {"ok": false, "error_code": 400, "description": "Solicitação incorreta: a mensagem é muito longa"}Na Internet, encontramos informações de que o tamanho da mensagem não pode exceder 4096 caracteres. Okey ... $output.Length
diz comprimento 39. Pensamospor um longo tempo o que está errado; como resultado, tentamos este trecho de código: $text = $null foreach($string in $output) { $text = "$text`n$string" } sendMessage $URL_set $job.Name $text
Tentamos tudo juntos [xml]$xmlConfig = Get-Content -Path ("c:\Temp\Habr\telegram_bot.xml") $token = $xmlConfig.config.system.token $timeout = $xmlConfig.config.system.timeout.'#text'
Agora vamos estragar um pouco de segurança:adicione uma nova linha à configuração xml, chame-a de users e indique chat_id lá para quem puder se comunicar com o bot: <system> <token>*********************************</token> <timeout desc="bot check timeout in seconds">1</timeout> <users>111111111, 222222222</users> </system>
No script, obteremos a matriz de usuários $users = (($xmlConfig.config.system.users).Split(",")).Trim()
E verifique if($users -contains $return.chat_id) { ... }
Script completo [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()
Parte 5: em conclusão
Verificamos a funcionalidade do bot - adicionamos scripts para fazer algo útil.Paraoperações em servidores remotos, usamos Invoke-Command seguido de 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
Nesse caso, a conta na qual o script bot de telegrama será executado deve ter os privilégios apropriados na máquina remota.Além disso, eu não toquei na funcionalidade de registro, mas aqui, acho, tudo é simples, à vontade, todos podem decidir o que ele deseja registrar e o que não.Certamente alguém terá problemas ao enviar uma mensagem com mais de 4096 caracteres, mas isso é resolvido pelo Substring e pelo ciclo de envio.E finalmente - o controle remoto de qualquer lugar do mundo (de quase qualquer lugar) é bom, mas sempre existe o risco de que algo dê errado (alguém ruim pode obter um controle de bot). Nesse caso, adicionamos Exit do script para uma palavra específica switch -Wildcard ($return["text"]) { "**" {
Eu tenho tudo.