-- объявляем прототипы экземпляров
local utils
local telegram
local Utils = {} -- создаем класс
function Utils:new() -- функция-конструктор класса
    -- прототипы модификаторов доступа
    local public
    local private
    private = {}
        
    public = {}
        -- методы класса
        function public:formattedNumber(num)
            return tostring(num):reverse():gsub("(%d%d%d)","%1."):gsub("%.$",""):reverse()
        end     
        function public:encodeUrl(str)
            for c in str:gmatch("[%c%p%s]") do
                if c ~= "%" then
                    local find = str:find(c, 1, true)
                    if find then
                        wait(0)
                        local char = str:sub(find, find)
                        str = str:gsub(string.format("%%%s", char), ("%%%%%02X"):format(char:byte()))
                    end
                end
            end
            return u8(str)
        end
        function public:err()
            rep = false
            packet = {}
            counter = 1
            print("an error has occured while writing data")
        end
    
    setmetatable(public, self)
    self.__index = self; return public
end
local Telegram = {}
function Telegram:new(token, chatId)
    local public
    local private
    private = {}
        -- поля класса
        private.token = token
        private.chatId = chatId
        private.updateId = nil   
        private.commands = {}       
    
    public = {}
        function public:sendMessage(text) -- отправка сообщения
            text = text:gsub("{......}", "")
            text = text:gsub("_", " ")
            local params = {
                chat_id = private.chatId,
                text = utils:encodeUrl(text)
            }
            local url = ("https://api.telegram.org/bot%s/sendMessage"):format(private.token)
            local status, result = pcall(requests.get, {url = url, params = params})
            print(("%s telegram message"):format((status and (result.status_code == 200)) and
                "successfully sended" or "error while sending"
            ))           
        end
        function public:registerCommand(command, callback) -- регистрация новой команды
            print(("a new command has been registered: \"%s\". Adress: %s"):format(command, tostring(callback):gsub("function: ", "")))
            table.insert(private.commands, {command = command, callback = callback})       
        end
        function public:processingTelegramMessages(result) -- проверка текста принимаемого сообщения
            if result then               
                local table = json.decode(result.text)
                if table.ok then
                    if #table.result > 0 then
                        local res_table = table.result[1]
                        if res_table then
                            if res_table.update_id ~= private.updateId then
                                private.updateId = res_table.update_id
                                local message_from_user = res_table.message.text
                                if message_from_user then                                                                   
                                    local text = ("%s "):format(u8:decode(message_from_user))
                                    for _, data in ipairs(private.commands) do                                                                               
                                        if text:find(data.command) then                                                                                   
                                            data.callback(text:match(data.command))
                                            break                                       
                                        end
                                    end                                   
                                end
                            end
                        end
                    end
                end
            end
        end
        function public:getTelegramUpdates() -- получение сообщения от юзера
            while not private.updateId do wait(1) end       
            while true do wait(0)
                local url = ("https://api.telegram.org/bot%s/getUpdates?chat_id=%s&offset=-1"):format(private.token, private.chatId)
                local status, result = pcall(requests.get, {url = url, params = nil})
                if status and (result.status_code == 200) then
                    self:processingTelegramMessages(result)                                           
                end
            end
        end
        function public:getLastUpdate() -- получение последнего ID сообщения
            local url = ("https://api.telegram.org/bot%s/getUpdates?chat_id=%s&offset=-1"):format(private.token, private.chatId)
            local status, result = pcall(requests.get, {url = url, params = nil})
            if status and (result.status_code == 200) then               
                local table = json.decode(result.text)               
                if table.ok then   
                    if #table.result > 0 then
                        local res_table = table.result[1]
                        if res_table then
                            private.updateId = res_table.update_id                       
                        end
                    else
                        private.updateId = 1
                    end
                end   
            end           
        end       
    
    setmetatable(public, self)
    self.__index = self; return public
end
-- создание экземпляров классов
utils = Utils:new()
telegram = Telegram:new("token", "chatId")
newTask(function() -- поток, вызываемый при запуске скрипта   
    telegram:registerCommand("^!send (.+)$", function(text) -- вызываем метод registerCommand, у которого первйы параметр - паттерн команды, а второй функция-каллбек, которая вызывается при получение команды из первого параметра       
        sendInput(text)
        telegram:sendMessage(("Сообщение успешно отправлено: %s"):format(text))       
    end)
    telegram:registerCommand("^!stats $", function() -- знак $ (окончание строки) надо ставить через пробел, т.к. скрипт видит команду как "/stats ", т.е. с пробелом
        telegram:sendMessage((Моя крутая статистика: %s"):format("..."))   
    end)
    telegram:getLastUpdate()
    newTask(telegram:getTelegramUpdates())
end)