Хочу представить вашему вниманию Телеграм бота, который написан на Mikrotik Script и работает в среде маршрутизатора без использования сторонних сервисов.
Его разработка стала возможной после того, как один из авторов Хабра выложил в общий доступ библиотеку, с помощью которой можно парсить JSON объекты в ассоциативный массив на Микротике. Автора зовут Александр @Chupakabra303. Спасибо ему за это большое. Ещё у автора есть хорошие статьи по исследованию переменных и функций.
Так же по функциям есть отличный материал на всё том же Хабре от уважаемого Сергея Sertik13. Всем кто хочет разобраться в этом вопросе - очень рекомендую.
Ну а я, собрав воедино, в том числе и эти знания, написал бота, который теперь очень помогает мне в работе. Информацию по нему начал выкладывать на своём канале. Всем, кому интересно - добро пожаловать https://t.me/mikRobot_RU
Бот модульный, написан с использованием функций. Первая его версия была почти монолитной, что было очень не удобно с точки зрения отладки и обновления. Функции этот вопрос решают. Так же написаны библиотечные функции для Телеграмма, типа SendMessage, EditMessage и т.д. Их я скоро планирую выложить в общий доступ, как и основную часть кода.
Больше информации на канале. Прошу сильно не пинать, публикую в первый раз. Конструктивная критика приветствуется.
С наличием свободного времени, конечно, не очень. Но буду рад и постараюсь ответить на ваши вопросы, если таковые возникнут.
Код бота опубликован, поэтому - License
В общих чертах про архитектуру приложения.
Главный модуль забирает у Телеграмма сообщения двух типов, Message и Callback. Это текстовые сообщения и нажатия на инлайн-кнопку.
Парсит полученный объект в многомерный ассоциативный массив с помощью библиотеки JParseFunctions.
Код: Выделить всё
:local tgUrl "https://api.telegram.org/$botID/getUpdates\?&allowed_updates=[%22message%22,%22callback_query%22]&offset=$Jtoffset&timeout=5"
:local fetchData [/tool fetch ascii=yes url=$tgUrl as-value output=user]
:if ([:len [:tostr ($fetchData->"data")]] != 0) do={
:set fetchData ($fetchData->"data")
:set JSONIn $fetchData
:set JParseOut [$fJParse]
:if ([:len ($JParseOut->"result")] != 0) do={
:set JParseOut ($JParseOut->"result")
} else={error message="no data..." }
}
Далее определяется тип полученного объекта и проверяются разрешения пользователя на работу с устройством.
Если разрешения есть, то вызываем функцию обработки. Для каждого типа обработчик свой.
Обработчик сообщений получает на вход объект в виде массива, определяет команду и обрабатывает её.
Если было нажатие на кнопку, то вызывается функция обработки Callbackов. У неё одна задача - определить от какого модуля пришло сообщение и вызвать функцию его обработки, в которой находится вся логика и формируются параметры для передачи в функцию построения ответного сообщения.
Она формирует объект для Телеграмма, с текстовой информацией, медиа, кнопками и отправляет его пользователю.
Каждая функция возвращает значение в то место, откуда была вызвана. Оно обрабатывается и передаётся по цепочке в обратном порядке.
Главный модуль увеличивает счетчик сообщений на единицу и ждёт новый объект. Это если вкратце.
Не спеша начну выкладывать код общих функций.
Настройки для них хранятся в массиве и получаются во время работы.
Чтобы адаптировать их для общего пользования, буду переносить такие настройки в параметры самих функций. Итак...
Код: Выделить всё
#---------------------------------------------------teSendMessage--------------------------------------------------------------
# Function sends a message to the recipient.
# Params for this function:
# 1. fChatID - Recipient id
# 2. fText - Message text
# 3. fReplyMarkup - Reply markup (may be empty)
# 4. fBotID - ID for bot
# Function return $messageID or 0 or "lengthError"
#---------------------------------------------------teSendMessage--------------------------------------------------------------
:global teSendMessage
:if (!any $teSendMessage) do={ :global teSendMessage do={
:local botID $fBotID
:local disableWebPagePreview true
:local parseMode "html"
:local tgUrl []; :local result []; :local content []
:if ([:len $fText] >= 4096) do={:return [error message="lengthError"]}
:if ([:len $fReplyMarkup] != 0) do={
:set tgUrl "https://api.telegram.org/$botID/sendmessage\?chat_id=$fChatID&text=$fText&parse_mode=$parseMode&disable_web_page_preview=$disableWebPagePreview&reply_markup=$fReplyMarkup"
}
:if ([:len $fReplyMarkup] = 0) do={
:set tgUrl "https://api.telegram.org/$botID/sendmessage\?chat_id=$fChatID&text=$fText&parse_mode=$parseMode&disable_web_page_preview=$disableWebPagePreview"
}
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
} on-error={ :return 0 }
:if ($content->"status" = "finished") do={
:local tmpStr [:pick ($content->"data") ([:find ($content->"data") "message_id"]) ([:find ($content->"data") "_id"]+20)]
:local messageID [:pick $tmpStr ([:find $tmpStr "message_id"]+12) ([:find $tmpStr ","])]
:set result $messageID
:return $result
} else={ :return 0 }
}
}
Параметры и варианты использования
fChatID - ID чата, куда отправляем
fText - Текст сообщения
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
fReplyMarkup - это клавиатура, которую можно прицепить к сообщению. Она формируется отдельной функцией. Её выложу позже.
Возвращает ID сообщения, если оно успешно доставлено. 0 (ноль), если доставить не получилось,
или ошибку "lengthError", если длина текста >= 4096 (ограничение Telegram на длину сообщения)
В приведенном ниже примере результат возвращается в переменную messageID
Код: Выделить всё
# Сообщение с текстом и клавиатурой
:global teSendMessage
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID [$teSendMessage fBotID=$botID fChatID=$chatID fText=$sendText fReplyMarkup=$replyMarkup]
Код: Выделить всё
# Сообщение только с текстом
:global teSendMessage
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID [$teSendMessage fBotID=$botID fChatID=$chatID fText=$sendText]
Параметры:
fChatID - ID чата, куда отправляем
fPhoto - url изображения или его file_id из Telegram
fText - текст сообщения
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
fReplyMarkup - клавиатура.
Код: Выделить всё
#---------------------------------------------------teSendPhoto--------------------------------------------------------------
# Function sends a message to the recipient.
# Params for this function:
# 1. fChatID - Recipient id
# 2. fPhoto - Message Photo
# 3. fText - Message text
# 4. fReplyMarkup - Reply markup (may be empty)
# 5. fBotID - ID for bot
# Function return $messageID or 0 or "lengthError"
# if the global variable fDBGteSendPhoto=true, then a debug event will be logged
#---------------------------------------------------teSendPhoto--------------------------------------------------------------
:global teSendPhoto
:if (!any $teSendPhoto) do={ :global teSendPhoto do={
:local botID $fBotID
:local disableWebPagePreview true
:local parseMode "html"
:local tgUrl []; :local result []; :local content []
:if ([:len $fText] = 0) do={:set $fText " "}
:if ([:len $fText] >= 1024) do={:return [error message="lengthError"]}
:if ([:len $fReplyMarkup] != 0) do={
:set tgUrl "https://api.telegram.org/$botID/sendphoto\?chat_id=$fChatID&photo=$fPhoto&caption=$fText&parse_mode=$parseMode&reply_markup=$fReplyMarkup"
}
:if ([:len $fReplyMarkup] = 0) do={
:set tgUrl "https://api.telegram.org/$botID/sendphoto\?chat_id=$fChatID&photo=$fPhoto&caption=$fText&parse_mode=$parseMode"
}
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
} on-error={ :return 0 }
:if ($content->"status" = "finished") do={
:local tmpStr [:pick ($content->"data") ([:find ($content->"data") "message_id"]) ([:find ($content->"data") "_id"]+20)]
:local messageID [:pick $tmpStr ([:find $tmpStr "message_id"]+12) ([:find $tmpStr ","])]
:set result $messageID
:return $result
} else={ :return 0 }
}
}
Возвращает ID сообщения, если оно успешно доставлено. 0 (ноль), если доставить не удалось,
или ошибку "lengthError", если длина текста >= 1024. (ограничение Telegram на длину подписи к фотографии)
Варианты использования:
Код: Выделить всё
# Сообщение с текстом фотографией и клавиатурой
:global teSendPhoto
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local image "https://habrastorage.org/webt/kz/uh/xm/kzuhxmsrjq7mrzqin8aznrrhclw.jpeg"
:local messageID [$teSendPhoto fBotID=$botID fChatID=$chatID fPhoto=$imageRoot fText=$sendText fReplyMarkup=$replyMarkup]
Код: Выделить всё
# Сообщение только с текстом и фото
:global teSendPhoto
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local image "https://habrastorage.org/webt/kz/uh/xm/kzuhxmsrjq7mrzqin8aznrrhclw.jpeg"
:local messageID [$teSendPhoto fBotID=$botID fChatID=$chatID fPhoto=$imageRoot fText=$sendText]
Параметры:
fChatID - ID чата, куда отправляем
fMessageID - номер редактируемого сообщения
fText - текст сообщения
fReplyMarkup - клавиатура.
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teEditMessage--------------------------------------------------------------
# Function edits the specified message
# Params for this function:
# 1. fChatID - Recipient id
# 2. fMessageID - id for edited message
# 3. fText - Message text
# 4. fReplyMarkup - Reply markup (may be empty)
# 5. fBotID - ID for bot
# Function return message ID or 0 or "lengthError"
#---------------------------------------------------teEditMessage--------------------------------------------------------------
:global teEditMessage
:if (!any $teEditMessage) do={ :global teEditMessage do={
:local botID $fBotID
:local disableWebPagePreview true
:local parseMode "html"
:local tgUrl []; :local result []; :local content []
:if ([:len $fText] = 0) do={:set $fText " "}
:if ([:len $fText] >= 4096) do={:return [error message="lengthError"]}
:if ([:len $fReplyMarkup] != 0) do={
:set tgUrl "https://api.telegram.org/$botID/editMessageText\?chat_id=$fChatID&message_id=$fMessageID&text=$fText&parse_mode=$parseMode&disable_web_page_preview=$disableWebPagePreview&reply_markup=$fReplyMarkup"
} else={
:set tgUrl "https://api.telegram.org/$botID/editMessageText\?chat_id=$fChatID&message_id=$fMessageID&text=$fText&parse_mode=$parseMode&disable_web_page_preview=$disableWebPagePreview"
}
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
:if ($content->"status" = "finished") do={
:local tmpStr [:pick ($content->"data") ([:find ($content->"data") "message_id"]) ([:find ($content->"data") "_id"]+20)]
:local messageID [:pick $tmpStr ([:find $tmpStr "message_id"]+12) ([:find $tmpStr ","])]
:set result $messageID
:return $result
} else={ :return 0 }
} on-error={ :return 0 }
}
}
Варианты использования:
Код: Выделить всё
# Редактирование сообщения с текстом и клавиатурой
:global teEditMessage
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID 12345
:set messageID [$teEditMessage fBotID=$botID fChatID=$chatID fMessageID=$messageID fText=$sendText fReplyMarkup=$replyMarkup]
Код: Выделить всё
# Редактирование сообщения только с текстом
:global teEditMessage
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID 12345
:set messageID [$teEditMessage fBotID=$botID fChatID=$chatID fMessageID=$messageID fText=$sendText]
или ошибку "lengthError", если длина текста >= 4096. (ограничение Telegram на длину сообщения)
Параметры:
fChatID - ID чата, куда отправляем
fMessageID - номер редактируемого сообщения
fText - текст сообщения
fReplyMarkup - клавиатура (не обязательный параметр)
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teEditCaption--------------------------------------------------------------
# Function edits the specified message caption
# Params for this function:
# 1. fChatID - Recipient id
# 2. fMessageID - id for edited message
# 3. fText - Message text
# 4. fBotID - ID for bot
# Function return message ID or 0 or "lengthError"
#---------------------------------------------------teEditCaption--------------------------------------------------------------
:global teEditCaption
:if (!any $teEditCaption) do={ :global teEditCaption do={
:local botID $fBotID
:local disableWebPagePreview true
:local parseMode "html"
:local tgUrl []; :local result []; :local content []
:if ([:len $fText] = 0) do={:set $fText " "}
:if ([:len $fText] >= 1024) do={:return [error message="lengthCaptionError"]}
:if ([:len $fReplyMarkup] != 0) do={
:set tgUrl "https://api.telegram.org/$botID/editMessageCaption\?chat_id=$fChatID&message_id=$fMessageID&caption=$fText&parse_mode=$parseMode&reply_markup=$fReplyMarkup"
} else={
:set tgUrl "https://api.telegram.org/$botID/editMessageCaption\?chat_id=$fChatID&message_id=$fMessageID&caption=$fText&parse_mode=$parseMode"
}
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
:if ($content->"status" = "finished") do={
:local tmpStr [:pick ($content->"data") ([:find ($content->"data") "message_id"]) ([:find ($content->"data") "_id"]+20)]
:local messageID [:pick $tmpStr ([:find $tmpStr "message_id"]+12) ([:find $tmpStr ","])]
:set result $messageID; :return $result
} else={:return 0 }
} on-error={ :return 0 }
}
}
Варианты использования:
Код: Выделить всё
:global teEditCaption
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID 12345
:set messageID [$teEditCaption fBotID=$botID fChatID=$chatID fMessageID=$messageID fText=$sendText fReplyMarkup=$replyMarkup]
или ошибку "lengthError", если длина текста >= 1024. (ограничение Telegram на длину сообщения)
Параметры:
fChatID - ID чата, куда отправляем
fMessageID - номер редактируемого сообщения
fMediaType - тип медиа сообщения (animation, document, audio, photo, video)
fMediaLink - ссылка на новое медиа
fMediaCaption - текст подписи
fReplyMarkup - клавиатура (не обязательный параметр)
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teEditMedia--------------------------------------------------------------
# Params for this function:
# 1. fChatID - Recipient id
# 2. fMessageID - id for edited message
# 3. fMediaType - This object represents the content of a media message to be edit. It should be one of:
# animation, document, audio, photo, video
# 4. fMediaLink - Message media,
# 5. fMediaCaption - Message caption
# 6. fReplyMarkup - Reply markup (may be empty)
# 7. fBotID - ID for bot
# Function return message ID or 0 or "lengthError"
#---------------------------------------------------teEditMedia--------------------------------------------------------------
:global teEditMedia
:if (!any $teEditMedia) do={ :global teEditMedia do={
:local botID $fBotID
:local disableWebPagePreview true
:local parseMode "html"
:local tgUrl []; :local result []; :local content []
:local newMedia []
:if ([:len $fMediaCaption] >= 1024) do={:return [error message="lengthError"]}
:local inputMediaType "\7B\22type\22:\22$fMediaType"
:local inputMediaLink "\22,\22media\22:\22"
:local inputMediaCaption "\22,\22caption\22:\22"
:local endMedia "\22\7D"
:if ([:len $fMediaCaption] = 0) do={
:set newMedia "$inputMediaType$inputMediaLink$fMediaLink$endMedia"
} else={
:set newMedia "$inputMediaType$inputMediaLink$fMediaLink$inputMediaCaption$fMediaCaption$endMedia"
}
:if ([:len $fReplyMarkup] != 0) do={
:set tgUrl "https://api.telegram.org/$botID/editMessageMedia\?chat_id=$fChatID&message_id=$fMessageID&media=$newMedia&parse_mode=$parseMode&reply_markup=$fReplyMarkup"
} else={
:set tgUrl "https://api.telegram.org/$botID/editMessageMedia\?chat_id=$fChatID&message_id=$fMessageID&media=$newMedia&parse_mode=$parseMode"
}
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
:if ($content->"status" = "finished") do={
:local tmpStr [:pick ($content->"data") ([:find ($content->"data") "message_id"]) ([:find ($content->"data") "_id"]+20)]
:local messageID [:pick $tmpStr ([:find $tmpStr "message_id"]+12) ([:find $tmpStr ","])]
:set result $messageID
:return $result
} else={ :return 0 }
} on-error={ :return 0 }
}
}
Варианты использования:
Код: Выделить всё
:global teEditMedia
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID 12345
:local mediaType "photo"
:local fMediaLink "https://habrastorage.org/webt/kz/uh/xm/kzuhxmsrjq7mrzqin8aznrrhclw.jpeg"
:set messageID [$teEditMedia fBotID=$botID fChatID=$chatID fMessageID=$messageID fMediaType=$mediaType fMediaCaption=$sendText fReplyMarkup=$replyMarkup]
или ошибку "lengthError", если длина текста >= 1024. (ограничение Telegram на длину сообщения)
Параметры:
fChatID - ID чата, куда отправляем
fMessageID - номер редактируемого сообщения
fReplyMarkup - клавиатура
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teEditMessageReplyMarkup--------------------------------------------------------------
# Function edits the specified message reply_markup
# Params for this function:
# 1. fChatID - Recipient id
# 2. fMessageID - id for edited message
# 3. fReplyMarkup - Reply markup
# 4. fBotID - ID for bot
# Function return message ID or 0
#---------------------------------------------------teEditMessageReplyMarkup--------------------------------------------------------------
:global teEditMessageReplyMarkup
:if (!any $teEditMessageReplyMarkup) do={ :global teEditMessageReplyMarkup do={
:local botID $fBotID
:local disableWebPagePreview true
:local parseMode "html"
:local tgUrl []; :local result []; :local content []
:if ([:len $fReplyMarkup] != 0) do={
:set tgUrl "https://api.telegram.org/$botID/editMessageReplyMarkup\?chat_id=$fChatID&message_id=$fMessageID&reply_markup=$fReplyMarkup"
} else={ :return 0 }
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
} on-error={ :return 0 }
:if ($content->"status" = "finished") do={
:local tmpStr [:pick ($content->"data") ([:find ($content->"data") "message_id"]) ([:find ($content->"data") "_id"]+20)]
:local messageID [:pick $tmpStr ([:find $tmpStr "message_id"]+12) ([:find $tmpStr ","])]
:set result $messageID
:return $result
} else={ :return 0 }
}
}
Варианты использования:
Код: Выделить всё
:global teEditMessageReplyMarkup
:global teBuildKeyboard
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local chatID 1234567890
:local messageID 12345
:local replyMarkup [$teBuildKeyboard fButtonsKeyBoard=$lineButtons fReplyKeyboard=false]
:set messageID [$teEditMessageReplyMarkup fBotID=$botID fChatID=$chatID fMessageID=$messageID fReplyMarkup=$replyMarkup]
Это те команды, которые добавляются через botFather и отображаются при вводе символа /
Через API их тоже можно добавлять. Этим и занимается данная функция. Имейте ввиду, что при добавлении новых, старые удаляются.
Параметры:
fCommands - команды, которые надо добавить. Формат - команда;описание. Может быть несколько. Разделяются запятой.
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teSetMyCommands--------------------------------------------------------------
# Params for this function:
# 1. fCommands - commands with descriptions
# 2. fBotID - ID for bot
# command - text of the command; 1-32 characters. Can contain only lowercase English letters, digits and underscores.
# description - description of the command; 1-256 characters
# Usage example:
# $teSetMyCommands fCommands="command;description"
# $teSetMyCommands fCommands="command;description,command;description"
# $teSetMyCommands fCommands="getifaces;Get interfaces list,getusers;Get users list"
# Function return 1 or 0
#---------------------------------------------------teSetMyCommands--------------------------------------------------------------
:global teSetMyCommands
:if (!any $teSetMyCommands) do={ :global teSetMyCommands do={
:local botID $fBotID
:local tgUrl []; :local content []
:local commandsList [:toarray $fCommands]
:local cmdItems []
:local command []
:foreach i in=$commandsList do={
:local command [:pick $i 0 [find $i ";"]]
:local description [:pick $i ([find $i ";"] + 1) [:len $i]]
:local startCommand "\7B\22command\22:\22$command\22"
:local commandDescription ",\22description\22:\22$description\22\7D,"
:set command "$startCommand$commandDescription$endCommand"
:set $cmdItems ($cmdItems . $command)
}
:set cmdItems [:pick $cmdItems 0 ([:len $cmdItems] - 1)]
:local start "\5B"
:local end "\5D"
:set commandsList "$start$cmdItems$end"
:set tgUrl "https://api.telegram.org/$botID/setMyCommands\?commands=$commandsList"
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
:if ($content->"status" = "finished") do={ :return 1 }
} on-error={ :return 0 }
}
}
Варианты использования:
Код: Выделить всё
# будет отправлено три команды с описанием
# interfaces
# users
# chatid
:global teSetMyCommands
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$teSetMyCommands fBotID=$botID fCommands="interfaces;Get interfaces list,users;Get users list,chatid;Get ID of current chat"
Боты могут удалять сообщения не старше 48 часов. Если сообщение старше, то функция попытается его отредактировать, заменив текст на сообщение о том, что удалить не получится и его можно удалить вручную. Если сообщения с таким номером вообще нет, тогда вернет false
Параметры:
fChatID - ID чата
fMessageID - номер удаляемого сообщения
fUserName - имя пользователя Телеграм (не обязательно)
fDeviceName - имя устройства (не обязательно)
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teDeleteMessage--------------------------------------------------------------
# Function delete the specified message
# Params for this function:
# 1. fChatID - Recipient id
# 2. fMessageID - id for edited message
# 3. fUserName - user Name from Telegram
# 4. fBotID - ID for bot
# 5. fDeviceName - name of device
# Function return message true or false
#---------------------------------------------------teDeleteMessage--------------------------------------------------------------
:global teDeleteMessage
:if (!any $teDeleteMessage) do={ :global teDeleteMessage do={
:global teEditMessage
:local botID $fBotID
:local deviceName $fDeviceName
:local oneFeed "%0D%0A"
:local doubleFeed "%0D%0A%0D%0A"
:local tgUrl []; :local content []
:set tgUrl "https://api.telegram.org/$botID/deletemessage\?chat_id=$fChatID&message_id=$fMessageID"
:do {
:set content [:tool fetch url=$tgUrl as-value output=user]
} on-error={
:local headerText "\F0\9F\97\91 <b>$deviceName</b> $oneFeed----------------------------------------------------$doubleFeed"
:local bodyText ("<b>$fUserName,</b> the bot cannot delete messages older than 48 hours. $doubleFeed" )
:local footerText ("<b>The function on the device worked normally.</b> $doubleFeed"."You can delete this message manually.$oneFeed")
:local sendText "$headerText$bodyText$footerText"
:if ([$teEditMessage fChatID=$fChatID fMessageID=$fMessageID fText=$sendText] != 0) do={
:return true
} else={ :return false }
}
:if ($content->"status" = "finished") do={
:return true
} else={ :return false }
}
}
Варианты использования:
Код: Выделить всё
:global teDeleteMessage
:local chatID 1234567890
:local messageID 12345
:local userName "user name"
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local result [$teDeleteMessage fBotID=$botID fChatID=$chatID fMessageID=$messageID fUserName=$userName]
Когда боту прилетает сообщение с типом Callback (нажатие на кнопку), то можно на него ответить. В Телеграмме это будет выглядеть, как всплывающее сообщение.
Параметры:
fQueryID - ID callback-а
fAnswerText - текст ответа
fAlert - тип ответа (не обязательно)
fBotID - ID бота в формате "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
Код: Выделить всё
#---------------------------------------------------teAnswerCallbackQuery--------------------------------------------------------------
# Function sends a response to a button click
# Params for this function:
# 1. fQueryID - query id
# 2. fAnswerText - text for answer
# 3. fAlert - alert type (true or false) may be empty
# 4. fBotID - ID for bot
# Function return message true or false
#---------------------------------------------------teAnswerCallbackQuery--------------------------------------------------------------
:global teAnswerCallbackQuery
:if (!any $teAnswerCallbackQuery) do={ :global teAnswerCallbackQuery do={
:local botID $fBotID
:local tgUrl []; :local content []
:if ([:len $fAlert] = 0) do={ :set $fAlert false }
:set tgUrl "https://api.telegram.org/$botID/answerCallbackQuery\?callback_query_id=$fQueryID&text=$fAnswerText&show_alert=fAlert"
do {
:set content [:tool fetch ascii=yes url=$tgUrl as-value output=user]
} on-error={ :return false }
:if ($content->"status" = "finished") do={
:return true
} else={ :return false }
}
}
Варианты использования:
Код: Выделить всё
:global teAnswerCallbackQuery
:local fQueryID 12345
:local answerText "Answer Text"
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local result [$teAnswerCallbackQuery fBotID=$botID fQueryID=$queryID fAnswerText=$answerText]
К сообщению можно прикрепить клавиатуру, которая состоит из inline кнопок.
Для этого надо сформировать объект InlineButton в формате JSON. Функция строит
такой объект и возвращает в виде строки. Получается кнопка, которая вставляется в клавиатуру.
В качестве параметров в функцию передается fPictButton - это эмоджи в hex формате, fTextButton - текст кнопки,
дальше на выбор:
url - тип строка, любой url адрес;
SwitchCurrentChat - тип строка, вставляет в поле для ввода имя бота и текст, присвоенный этой переменной;
fTextCallBack - тип строка, это текст команды, которая прилетает в ответ на нажатие кнопки.
Код: Выделить всё
#---------------------------------------------------teBuildButton--------------------------------------------------------------
# Function build button and returns it in text format
# Params for this function:
# 1. fPictButton - picture for the button from Emoji Unicode Tables
# 2. fTextButton - text for the button
# 3. fUrlButton - URL for the button
# 4. fSwitchCurrentChat - URL for the button
# 5. fTextCallBack - callback for the button
#---------------------------------------------------teBuildButton--------------------------------------------------------------
:global teBuildButton
:if (!any $teBuildButton) do={ :global teBuildButton do={
:local startButton "\7B\22text\22: \22 "
:local startUrl "\22,\22url\22: \22"
:local startCallBack " \22,\22callback_data\22: \22"
:local startSwitchCurrentChat " \22,\22switch_inline_query_current_chat\22: \22"
:local endButton "\22\7D"
:local button []
:set button "$startButton$fPictButton$fTextButton$startCallBack$fTextCallBack$endButton"
:if ([:len $fUrlButton] != 0) do={
:set button "$startButton$fPictButton$fTextButton$startUrl$fUrlButton$startCallBack$fTextCallBack$endButton"
}
:if ([:len $fSwitchCurrentChat] != 0) do={
:set button "$startButton$fPictButton$fTextButton$startSwitchCurrentChat$fSwitchCurrentChat$endButton"
}
:put "teBuildButton = $button"
:return $button
}
}
Пример использования
Рассмотрим подробнее параметр fTextCallBack. Ему присвоено значение "teCallbackLeaseCard,isBlocked,true"
Код: Выделить всё
:global teBuildButton
:local pictDeny "\E2\9B\94"
:local buttonDenyCallBackText "teCallbackLeaseCard,isBlocked,true"
:local buttonDeny [$teBuildButton fPictButton=$pictDeny fTextButton=" Deny" fTextCallBack=$buttonDenyCallBackText]
teCallbackLeaseCard - это имя функции, которой будет передано управление;
isBlocked - команда, которая будет выполнена этой функцией.
true - значение этой команды
Значения переменной fTextCallBack перечисляются через запятую, так удобно парсить команду в массив.
Их может быть несколько, но не более 64 символов из-за ограничений Телеграм.
В примере выше, функция возвратит результат в переменную buttonDeny в виде строки.
Сформированные функцией teBuildButton кнопки, передаются в функцию teBuildKeyboard.
К сообщению можно прикрепить два вида клавиатур на выбор. Это Inline или Reply клавиатура.
Их различия в том, что Inline прикрепляется к телу сообщения, а Reply выводится внизу взамен
стандартной.
Код: Выделить всё
#---------------------------------------------------teBuildKeyboard--------------------------------------------------------------
# Function builds a keyboard from an array of buttons
# and return keyBoard in text format
# Params for this function:
# 1. fButtonsKeyBoard - an array of buttons formed by a function teBuildButton
# 2. fReplyKeyboard - keyboard type, reply or inline (true or false)
# 3. fReplyOneTime - Requests clients to hide the keyboard as soon as it's been used (true or false)
# 4. fReplyResize - Requests clients to resize the keyboard vertically for optimal fit (true or false)
# 5. fPlaceholder - The placeholder to be shown in the input field when the keyboard is active; 1-64 characters (hint in the input field)
# 6. fReplySelective - Use this parameter if you want to show the keyboard to specific users only (true or false)
#---------------------------------------------------teBuildKeyboard--------------------------------------------------------------
:global teBuildKeyboard
:if (!any $teBuildKeyboard) do={ :global teBuildKeyboard do={
:local keyBoard []
:local startKeyBoard []
:local resizeKeyboard []
:local ontimeKeyboard []
:local inputFieldPlaceholder []
:local selectiveKeyboard []
:if ($fReplyResize = false or [:len $fReplyResize] = 0) do={
:set resizeKeyboard "\5D\5D,\22resize_keyboard\22:false"
} else={
:set resizeKeyboard "\5D\5D,\22resize_keyboard\22:true"
}
:if ($fReplyOneTime = false or [:len $fReplyOneTime] = 0) do={
:set ontimeKeyboard ",\22one_time_keyboard\22:false"
} else={
:set ontimeKeyboard ",\22one_time_keyboard\22:true"
}
:if ($fReplySelective = false or [:len $fReplySelective] = 0) do={
:set selectiveKeyboard ",\22selective\22:false"
} else={
:set selectiveKeyboard ",\22selective\22:true"
}
:if ([:len $fPlaceholder] != 0) do={
:if ([:len $fPlaceholder] > 64) do={:set $fPlaceholder [:pick $fPlaceholder 0 63]}
:set inputFieldPlaceholder ",\22input_field_placeholder\22:\22$fPlaceholder\22"
} else={
:set inputFieldPlaceholder ""
}
:local endReplyKeyBoard "\7D"
:local endKeyBoard "\5D\5D\7D"
:if ($fReplyKeyboard = true) do={
:set startKeyBoard "\7B\22keyboard\22: \5B\5B"
:set keyBoard "$startKeyBoard$fButtonsKeyBoard$resizeKeyboard$ontimeKeyboard$inputFieldPlaceholder$selectiveKeyboard$endReplyKeyBoard"
} else={
:set startKeyBoard "\7B\22inline_keyboard\22: \5B\5B"
:set keyBoard "$startKeyBoard$fButtonsKeyBoard$endKeyBoard"
}
:return $keyBoard
}
}
В примере ниже формируются две кнопки, buttonDeny и buttonAllow .
Они объединяются в одну строку в переменной buttons .
Переменная :local NB "," содержит признак новой кнопки. Если ее вставить между двух кнопок, то они будут располагаться в одной строке.
Переменная :local NL "\5D,\5B" признак новой линии. Если ее вставить между двух кнопок, то они будут располагаться одна под другой.
Между кнопок обязательно должен присутствовать один из этих разделителей.
Код: Выделить всё
:global teBuildButton
:global teBuildKeyboard
:local pictDeny "\E2\9B\94"
:local buttonDenyCallBackText "teCallbackLeaseCard,isBlocked,true"
:local buttonDeny [$teBuildButton fPictButton=$pictDeny fTextButton=" Deny" fTextCallBack=$buttonDenyCallBackText]
:local pictAllow "\E2\9C\85"
:local buttonAllowCallBackText "teCallbackLeaseCard,isBlocked,false"
:local buttonAllow [$teBuildButton fPictButton=$pictAllow fTextButton=" Allow" fTextCallBack=$buttonAllowCallBackText]
:local NB ","
:local NL "\5D,\5B"
:local buttons "$buttonDeny$NB$buttonAllow "
:local replyMarkup [$teBuildKeyboard fButtonsKeyBoard=$buttons fReplyKeyboard=false]
Теперь мы можем прикрепить её к нашему сообщению.
Код: Выделить всё
# Сообщение с текстом и клавиатурой
:global teSendMessage
:local botID "botxxxxxxxxx:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
:local sendText "Any text..."
:local chatID 1234567890
:local messageID [$teSendMessage fBotID=$botID fChatID=$chatID fText=$sendText fReplyMarkup=$replyMarkup]
Как писал выше, клавиатуры бывают двух типов, это Inline или Reply . Для построения последней, нужна
кнопка типа ReplyButton.
Функция teBuildReplyButton строит такую кнопку.
Код: Выделить всё
#---------------------------------------------------teBuildReplyButton--------------------------------------------------------------
# Function build reply button and returns it in text format
# Params for this function:
# 1. fPictButton - picture for the button from Emoji Unicode Tables
# 2. fTextButton - text for the button
#---------------------------------------------------teBuildReplyButton--------------------------------------------------------------
:global teBuildReplyButton
:if (!any $teBuildReplyButton) do={ :global teBuildReplyButton do={
:local startButton "\22"
:local endButton "\22"
:local button "$startButton$fPictButton$fTextButton$endButton"
:return $button
}
}
В параметрах указываются:
fPictButton - тип строка, содержит эмоджи в формате hex
fTextButton - текст кнопки. Этот текст будет отправлен в качестве сообщения, при нажатии на кнопку.
Код: Выделить всё
:global teBuildReplyButton
:global teBuildKeyboard
:local pictStatic "\F0\9F\93\8C"
:local textStaticButton " Make static"
:local buttonStatic [$teBuildReplyButton fPictButton=$pictStatic fTextButton=$textStaticButton ]
:local pictNonStatic "\E2\9A\99"
:local textNonStatiсButton " Non static"
:local buttonNonStatic [$teBuildReplyButton fPictButton=$pictNonStatic fTextButton=$textNonStatiсButton ]
:local NB ","
:local NL "\5D,\5B"
:local buttons "$buttonStatic$NL$buttonNonStatic "
:local replyMarkup [$teBuildKeyboard fButtonsKeyBoard=$buttons fReplyKeyboard=true]
В этом примере строятся две кнопки buttonStatic и buttonNonStatic .
Они объединяются в одну строку в переменной buttons .
Переменная :local NB "," содержит признак новой кнопки. Если ее вставить между двух кнопок, то они будут располагаться в одной строке.
Переменная :local NL "\5D,\5B" признак новой линии. Если ее вставить между двух кнопок, то они будут располагаться одна под другой.
Между кнопок обязательно должен присутствовать один из этих разделителей.
Переменная buttons передается в качестве параметра в функцию teBuildKeyboard.
Для построения Reply клавиатуры параметр fReplyKeyboard должен быть равен true.
Эта функция генерирует рандомную строку, обращаясь к ресурсу www.random.org
С её помощью можно генерировать пароли различной длины и состава.
Код: Выделить всё
#---------------------------------------------------teGenValue--------------------------------------------------------------
# Function generates a value with the specified parameters using the www.random.com website API
# and returns it in text format
# Params for this function:
# 1. fValueLen - value length
# 2. fDigits - on/off digits
# 3. fUpperAlpha - on/off upper case characters
# 3. fLowerAlpha - on/off lowercase characters
# 3. fUnique - on/off uniqueness
#---------------------------------------------------teGenValue--------------------------------------------------------------
:global teGenValue
:if (!any $teGenValue) do={ :global teGenValue do={
:local newValue []
:if ([:len $fValueLen] = 0) do={ :set $fValueLen 8 }
:if ([:len $fDigits] = 0) do={ :set $fDigits "on" }
:if ([:len $fUpperAlpha] = 0) do={ :set $fUpperAlpha "on" }
:if ([:len $fLowerAlpha] = 0) do={ :set $fLowerAlpha "on" }
:if ([:len $fUnique] = 0) do={ :set $fUnique "on" }
:local valueURL "https://www.random.org/strings/?num=1&len=$fValueLen&digits=$fDigits&upperalpha=$fUpperAlpha&loweralpha=$fLowerAlpha&unique=$fUnique&format=plain&rnd=new"
do {
:set newValue [:tool fetch ascii=yes url=$valueURL as-value output=user]
} on-error={ :return false }
:if ($newValue->"status" = "finished") do={
:set newValue ($newValue->"data")
:set newValue ($newValue [:pick $newValue 0 $fValueLen])
} else={
:set newValue false
}
:return $newValue
}
}
Параметры:
fValueLen - желаемая длина строки, тип num
fDigits - использовать цифры on/off
fUpperAlpha - использовать верхний регистр on/off
fLowerAlpha - использовать нижний регистр on/off
fUnique - признак уникальности каждого символа on/off
По-умолчанию длина строки=8, все опции включены.
Варианты использования
Код: Выделить всё
:global teGenValue
:local newSecret [$teGenValue fValueLen=10 fDigits=on fUpperAlpha=on fLowerAlpha=on fUnique=on]
mikrotik,telegram,bot