Немає опису редагування
Немає опису редагування
Мітка: Скасовано
Рядок 1: Рядок 1:
local p = {}
local p = {}


-- ================================================
--- Функція для виведення помилки на сторінку
-- КЕШУВАННЯ (найважливіша оптимізація)
local function error_output(context, error_details)
-- ================================================
    local safe_details = error_details or "N/A"
    return string.format("<span style='color:red; font-weight:bold;'>[FD2 Error: %s. Details: %s]</span>",
        context or "Unknown Context", safe_details)
end


local page_cache = {}
--- Видаляє вікі-посилання [[link|text]] і [[link]]
local table_cache = {}
local function clean_wikilinks(text)
    if not text then return nil end
    -- [[link|text]] -> text
    text = mw.ustring.gsub(text, "%[%[([^%]|]+)|([^%]]+)%]%]", "%2")
    -- [[link]] -> link
    text = mw.ustring.gsub(text, "%[%[([^%]]+)%]%]", "%1")
    return mw.text.trim(text)
end


local function get_page_content(page_name)
----------------------------------------------------------------------
     if page_cache[page_name] then
-- ГОЛОВНИЙ ПАРСЕР ДЛЯ ГОРИЗОНТАЛЬНИХ ТАБЛИЦЬ
         return page_cache[page_name]
----------------------------------------------------------------------
local function fetch_from_table(page_title, player_name, column_index)
   
     if not player_name or player_name == "" then
         return error_output("No Player Name", page_title)
     end
     end
      
      
     local title = mw.title.new(page_name)
     local title = mw.title.new(page_title)
     if not title or not title.exists then
     if not title or not title.exists then  
        page_cache[page_name] = nil
         return error_output("Page Missing", page_title)
         return nil
     end
     end
      
      
     local content = title:getContent()
     local content = title:getContent()
    page_cache[page_name] = content
    return content
end
local function get_table_from_page(page_name)
    if table_cache[page_name] then
        return table_cache[page_name]
    end
   
    local content = get_page_content(page_name)
     if not content then  
     if not content then  
        table_cache[page_name] = nil
         return error_output("Content Read Fail", page_title)
         return nil
     end
     end
      
      
    -- Знаходимо таблицю
     local table_start = mw.ustring.find(content, "{|")
     local table_start = mw.ustring.find(content, "{|")
     local table_end = mw.ustring.find(content, "|}", table_start)
     local table_end = mw.ustring.find(content, "|}", table_start)
      
      
     if not table_start or not table_end then
     if not table_start or not table_end then
        table_cache[page_name] = nil
         return error_output("Table Missing", page_title)
         return nil
     end
     end
      
      
     local table_content = mw.ustring.sub(content, table_start, table_end + 1)
     local table_content = mw.ustring.sub(content, table_start, table_end + 1)
     table_cache[page_name] = table_content
      
     return table_content
     -- Екрануємо спеціальні символи в імені гравця
end
     local escaped = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
 
     local player_pattern = "%[%[" .. escaped .. "%]%]"
-- ================================================
      
-- УТИЛІТИ (спільні для всіх функцій)
    -- Шукаємо рядки, які починаються з |-
-- ================================================
 
local function error_output(context, error_details)
    local safe_details = error_details or "N/A"
     return string.format("<span style='color:red; font-weight:bold;'>[Error: %s. Details: %s]</span>",
        context or "Unknown", safe_details)
end
 
local function clean_wikilinks(text)
    if not text then return nil end
    text = mw.ustring.gsub(text, "%[%[([^%]|]+)|([^%]]+)%]%]", "%2")
    text = mw.ustring.gsub(text, "%[%[([^%]]+)%]%]", "%1")
     return mw.text.trim(text)
end
 
local function strip_html_tags(text)
    if not text then return text end
    text = mw.ustring.gsub(text, "<span[^>]*>", "")
    text = mw.ustring.gsub(text, "</span>", "")
     return mw.text.trim(text)
end
 
local function parse_table_rows(table_content)
     local rows = {}
     local rows = {}
     for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
     for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
         table.insert(rows, row)
         table.insert(rows, row)
     end
     end
   
    -- Останній рядок (до |})
     local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
     local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
     if last_row then
     if last_row then
         table.insert(rows, last_row)
         table.insert(rows, last_row)
     end
     end
    return rows
end
local function parse_row_cells(row)
    local cells = {}
    row = mw.ustring.gsub(row, "^%s*|%s*", "")
    for cell in mw.ustring.gmatch(row, "([^|]+)") do
        local trimmed = mw.text.trim(cell)
        if trimmed ~= "" then
            table.insert(cells, trimmed)
        end
    end
    return cells
end
local function find_player_in_table(table_content, player_name, column_index)
    if not table_content then return nil end
   
    local escaped = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped .. "%]%]"
   
    local rows = parse_table_rows(table_content)
      
      
    -- Обробляємо кожен рядок
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
         if mw.ustring.find(row, player_pattern) then
         if mw.ustring.find(row, player_pattern) then
             local cells = parse_row_cells(row)
            -- Розбиваємо рядок на комірки
             if column_index and column_index <= #cells then
             local cells = {}
                 return cells[column_index]
           
            -- Видаляємо початковий |
            row = mw.ustring.gsub(row, "^%s*|%s*", "")
           
            -- Розбиваємо по ||
            for cell in mw.ustring.gmatch(row, "([^|]+)") do
                -- Пропускаємо порожні комірки від подвійних ||
                local trimmed = mw.text.trim(cell)
                if trimmed ~= "" then
                    table.insert(cells, trimmed)
                end
            end
           
            -- Повертаємо потрібну колонку
             if column_index <= #cells then
                 return clean_wikilinks(cells[column_index])
            else
                return error_output("Column Out of Range",
                    string.format("Requested col %d, found %d cells", column_index, #cells))
             end
             end
            return cells
         end
         end
     end
     end
      
      
     return nil
     return error_output("Player Not Found", player_name)
end
end


-- ================================================
----------------------------------------------------------------------
-- FETCHDATA (season_result)
-- ФУНКЦІЇ, ЩО ВИКЛИКАЮТЬСЯ З ШАБЛОНУ
-- ================================================
----------------------------------------------------------------------
 
function p.recruiter(frame)
function p.season_result(frame)
     local name = frame.args.player
     local season = frame.args.season
    -- Don't clean wikilinks - keep them for clickable links
     local player = frame.args.player
     local title = mw.title.new("Гравці")
    if not title or not title.exists then
        return error_output("Page Missing", "Гравці")
    end
      
      
     local season_titles = {
     local content = title:getContent()
        "Перший сезон", "Другий сезон", "Третій сезон",
    if not content then
         "Четвертий сезон", "П'ятий сезон", "Шостий сезон",
         return error_output("Content Read Fail", "Гравці")
        "Сьомий сезон", "Восьмий сезон", "Дев'ятий сезон"
     end
     }
      
      
     local season_title = season_titles[tonumber(season)]
     local table_start = mw.ustring.find(content, "{|")
     if not season_title then return "??" end
     local table_end = mw.ustring.find(content, "|}", table_start)
      
      
    local content = get_page_content(season_title)
     if not table_start or not table_end then
     if not content then return "??" end
        return error_output("Table Missing", "Гравці")
    end
      
      
     local rating_section = mw.ustring.match(content, "==%s*Рейтинг%s*==.-{|%s*class%s*=%s*\"wikitable sortable\"(.-)|}")
     local table_content = mw.ustring.sub(content, table_start, table_end + 1)
     if not rating_section then return "--" end
    local escaped = mw.ustring.gsub(name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
     local player_pattern = "%[%[" .. escaped .. "%]%]"
      
      
     local pattern = "|%s*(%d+)%s*|%s*%[%[" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%]%]"
     local rows = {}
     local direct_pattern = "|%s*(%d+)%s*|%s*" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%s*"
     for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
   
        table.insert(rows, row)
    local rank = mw.ustring.match(rating_section, pattern) or mw.ustring.match(rating_section, direct_pattern)
   
    return rank or "--"
end
 
-- ================================================
-- FETCHDATA2 (базова статистика)
-- ================================================
 
function p.games_count(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0" end
   
    local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 2)
    return result or "0"
end
 
function p.wins_count(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0" end
   
    local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 3)
    return result or "0"
end
 
function p.losses_count(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0" end
   
    local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 4)
    return result or "0"
end
 
function p.win_rate_colored(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0%" end
   
    local win_rate = find_player_in_table(get_table_from_page("Статистика"), player_name, 5)
    if not win_rate then return "0%" end
   
    local rate_num = tonumber(mw.ustring.match(win_rate, "([%d%.]+)"))
    if not rate_num then return win_rate end
   
    local color = "#4caf50"
    if rate_num < 40 then
        color = "indianred"
    elseif rate_num < 50 then
        color = "orange"
     end
     end
      
      
     if not mw.ustring.find(win_rate, "%%") then
     local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
         win_rate = win_rate .. "%"
    if last_row then
         table.insert(rows, last_row)
     end
     end
      
      
     return string.format('<span style="color:%s;">%s</span>', color, win_rate)
     for _, row in ipairs(rows) do
end
        if mw.ustring.find(row, player_pattern) then
 
            local cells = {}
function p.recruiter(frame)
            row = mw.ustring.gsub(row, "^%s*|%s*", "")
    local player_name = frame.args.player
           
    if not player_name or player_name == "" then return "Не вказано" end
            for cell in mw.ustring.gmatch(row, "([^|]+)") do
   
                local trimmed = mw.text.trim(cell)
    local table_content = get_table_from_page("Гравці")
                if trimmed ~= "" then
    if not table_content then return "Не вказано" end
                    table.insert(cells, trimmed)
   
                end
    local cells = find_player_in_table(table_content, player_name)
            end
    if not cells or #cells < 2 then return "Не вказано" end
           
   
            if 4 <= #cells then
    local raw = mw.text.trim(cells[2])
                local raw = mw.text.trim(cells[4])
    if raw == "Відсутній" or raw == "-" or raw == "" then  
                if raw == "Відсутній" or raw == "-" then  
        return "Не вказано"  
                    return "Не вказано"  
                end
                -- Return with wikilinks intact
                return raw
            end
        end
     end
     end
      
      
     return raw
     return "Не вказано"
end
end


function p.date_added(frame)
function p.date_added(frame)
     local player_name = frame.args.player
     local name = frame.args.player
     if not player_name or player_name == "" then return "Лише Бог знає" end
     local raw = fetch_from_table("Гравці", name, 3)
      
      
     local raw = find_player_in_table(get_table_from_page("Гравці"), player_name, 3)
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
        return "Лише Бог знає"
    end
      
      
     if not raw or raw == "" or raw == "-" or raw == "Відсутній" then
     if not raw or raw == "" or raw == "-" or raw == "Відсутній" then
Рядок 228: Рядок 169:
     end
     end
      
      
    -- Парсимо дату у форматі DD.MM.YYYY
     local day, month, year = mw.ustring.match(raw, "(%d+)%.(%d+)%.(%d+)")
     local day, month, year = mw.ustring.match(raw, "(%d+)%.(%d+)%.(%d+)")
      
      
     if day and month and year then
     if day and month and year then
         local end_date = os.time({year=2024, month=12, day=1})
        -- Кінцева дата: 26.10.2024
         local end_date = os.time({year=2024, month=10, day=26})
         local start_date = os.time({year=tonumber(year), month=tonumber(month), day=tonumber(day)})
         local start_date = os.time({year=tonumber(year), month=tonumber(month), day=tonumber(day)})
       
         local days_diff = math.floor((end_date - start_date) / 86400)
         local days_diff = math.floor((end_date - start_date) / 86400)
          
          
Рядок 239: Рядок 183:
      
      
     return "Лише Бог знає"
     return "Лише Бог знає"
end
function p.foundation(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0₴" end
   
    local raw = find_player_in_table(get_table_from_page("Фундація"), player_name, 2)
   
    if not raw or raw == "" or raw == "-" then return "0₴" end
   
    raw = mw.ustring.gsub(raw, "[^%d%s]", "")
    raw = mw.text.trim(raw)
   
    return (raw or "0") .. " ₴"
end
end


function p.prize_pool(frame)
function p.prize_pool(frame)
     local player_name = frame.args.player
     local name = frame.args.player
     if not player_name or player_name == "" then return "0₴" end
     local raw = fetch_from_table("Призовий_фонд", name, 2)
      
      
     local raw = find_player_in_table(get_table_from_page("Призовий_фонд"), player_name, 2)
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
        return "0 ₴"
    end
      
      
     if not raw or raw == "" or raw == "-" then return "0₴" end
     if raw then  
   
        raw = mw.ustring.gsub(raw, "[^%d%s]", "")
    raw = mw.ustring.gsub(raw, "[^%d%s]", "")
        raw = mw.text.trim(raw)
    raw = mw.text.trim(raw)
    end
      
      
     return (raw or "0") .. " ₴"
     return (raw or "0") .. " ₴"
end
end


function p.finalist(frame)
-- Додай цю функцію до FetchData2 (модуль:FetchData2)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0/9" end
   
    local raw = find_player_in_table(get_table_from_page("Фіналіст"), player_name, 2)
   
    if not raw or raw == "" or raw == "-" then return "0/9" end
   
    local number_only = mw.ustring.gsub(raw, "[^%d]", "")
    local count = tonumber(number_only)
   
    if count then
        return string.format("%d/9", count)
    end
   
    return "0/9"
end


function p.foty_rating(frame)
function p.foty_rating(frame)
     local player_name = frame.args.player
     local name = frame.args.player
    if not player_name or player_name == "" then return "—" end
      
      
     local content = get_page_content("Період")
     if not name or name == "" then
    if not content then return "" end
        return "0"
    end
      
      
     local foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.-)\n==")
     local title = mw.title.new("Період")
     if not foty_section then
     if not title or not title.exists then  
         foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.*)")
         return "0"
     end
     end
      
      
     if not foty_section then return "" end
    local content = title:getContent()
     if not content then  
        return "0"
    end
      
      
     local table_start = mw.ustring.find(foty_section, "{|")
    -- Шукаємо секцію "Фінал Року"
     local table_end = mw.ustring.find(foty_section, "|}", table_start)
     local section_start = mw.ustring.find(content, "== Фінал Року ==")
     if not section_start then
        return "0"
    end
      
      
     if not table_start or not table_end then return "" end
    -- Знаходимо таблицю після секції
    local table_start = mw.ustring.find(content, "{|", section_start)
     if not table_start then
        return "0"
    end
      
      
     local table_content = mw.ustring.sub(foty_section, table_start, table_end + 1)
     local table_end = mw.ustring.find(content, "|}", table_start)
     local cells = find_player_in_table(table_content, player_name)
    if not table_end then
        return "0"
     end
      
      
     if not cells or #cells < 3 then return "—" end
     local table_content = mw.ustring.sub(content, table_start, table_end + 1)
      
      
     local place = strip_html_tags(cells[1]) or ""
    -- Екрануємо спеціальні символи в імені гравця
     local rating = strip_html_tags(cells[3]) or "—"
     local escaped = mw.ustring.gsub(name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
     local player_pattern = "%[%[" .. escaped .. "%]%]"
      
      
     rating = mw.ustring.gsub(rating, "[^%d%-]", "")
     -- Збираємо всі рядки
    rating = mw.text.trim(rating)
    local rows = {}
   
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
    if rating and rating ~= "" then
         table.insert(rows, row)
         return rating .. " (" .. place .. " місце)"
     end
     end
      
      
     return "—"
     local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
end
     if last_row then
 
         table.insert(rows, last_row)
-- ================================================
-- FETCHDATA3 (титули та нагороди)
-- ================================================
 
local tournament_dates = {
    ["Women's Closed Cup I"] = "15.03.2023",
    ["Men's Closed Cup I"] = "05.04.2023",
    ["Combined Closed Cup I"] = "21.04.2023",
    ["Score Open Battle I"] = "07.05.2023",
    ["Women's Closed Cup II"] = "11.05.2023",
    ["Men's Closed Cup II"] = "27.05.2023",
    ["Combined Closed Cup II"] = "02.06.2023",
    ["Score Open Battle II"] = "17.06.2023",
    ["Mafia Closed Cup I"] = "12.11.2023",
    ["Mafia Closed Cup I Online"] = "28.01.2024",
    ["My Closest Circle I"] = "01.01.2023",
    ["Get Names 01"] = "08.10.2023",
    ["Get Names 02"] = "03.12.2023",
    ["Get Names 03"] = "10.03.2024",
    ["Get Names 04"] = "27.04.2024",
    ["Get Names 05"] = "14.07.2024",
    ["Get Names 06"] = "21.07.2024",
    ["Get Names 07"] = "08.09.2024",
    ["Get Names 08"] = "19.09.2024",
    ["Get Names 09"] = "29.09.2024",
    ["Перший сезон (фінал)"] = "09.09.2023",
    ["Перший сезон (рейтинг)"] = "09.09.2023",
    ["Другий сезон (фінал)"] = "28.10.2023",
    ["Другий сезон (рейтинг)"] = "28.10.2023",
    ["Третій сезон (фінал)"] = "23.12.2023",
    ["Третій сезон (рейтинг)"] = "23.12.2023",
    ["Четвертий сезон (фінал)"] = "10.02.2024",
    ["Четвертий сезон (рейтинг)"] = "10.02.2024",
    ["П'ятий сезон (фінал)"] = "06.04.2024",
    ["П'ятий сезон (рейтинг)"] = "06.04.2024",
    ["Шостий сезон (фінал)"] = "11.05.2024",
    ["Шостий сезон (рейтинг)"] = "11.05.2024",
    ["Сьомий сезон (фінал)"] = "06.07.2024",
    ["Сьомий сезон (рейтинг)"] = "06.07.2024",
    ["Восьмий сезон (фінал)"] = "10.08.2024",
    ["Восьмий сезон (рейтинг)"] = "10.08.2024",
    ["Дев'ятий сезон (фінал)"] = "28.09.2024",
    ["Дев'ятий сезон (рейтинг)"] = "28.09.2024",
    ["Фінал Року"] = "13.10.2024"
}
 
local function get_medal_icon(value)
    if not value or value == "" or value == " " then return nil end
    value = mw.text.trim(value)
   
    if value == "" then return nil
    elseif value == "1" then return "[[Файл:Gold.png|20px|link=]]"
    elseif value == "2" then return "[[Файл:Silver.png|20px|link=]]"
    elseif value == "3" then return "[[Файл:Bronze.png|20px|link=]]"
    elseif value == "4" then return "[[Файл:Finalist.png|20px|link=]]"
    elseif value == "S" or mw.ustring.match(value, "^S%d") then return "[[Файл:Star.png|20px|link=]]"
    else return nil
    end
end
 
local function to_roman(num)
    local romans = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}
    return romans[tonumber(num)] or num
end
 
local function get_tournament_link(header_name)
    local season_num = mw.ustring.match(header_name, "([А-Яа-яЁёІіЏїЄєҐґ']+) сезон %(фінал%)")
     if not season_num then
         season_num = mw.ustring.match(header_name, "([А-Яа-яЁёІіЇїЄєҐґ']+) сезон %(рейтинг%)")
     end
     end
      
      
     if season_num then
     -- Обробляємо кожен рядок
        local season_map = {
    for _, row in ipairs(rows) do
             ["Перший"] = 1, ["Другий"] = 2, ["Третій"] = 3,
        if mw.ustring.find(row, player_pattern) then
             ["Четвертий"] = 4, ["П'ятий"] = 5, ["Шостий"] = 6,
            local cells = {}
            ["Сьомий"] = 7, ["Восьмий"] = 8, ["Дев'ятий"] = 9
             row = mw.ustring.gsub(row, "^%s*|%s*", "")
        }
           
       
             for cell in mw.ustring.gmatch(row, "([^|]+)") do
        local season_number = season_map[season_num]
                local trimmed = mw.text.trim(cell)
        if season_number then
                if trimmed ~= "" then
            local roman = to_roman(season_number)
                    table.insert(cells, trimmed)
            local type_text = mw.ustring.find(header_name, "фінал") and "Фінал." or "Рейтинг."
                end
            local clean_name = mw.ustring.gsub(header_name, "%s*%(фінал%)%s*", "")
            end
            clean_name = mw.ustring.gsub(clean_name, "%s*%(рейтинг%)%s*", "")
           
            clean_name = mw.text.trim(clean_name)
            -- Колонка 1 - це місце (№)
            -- Колонка 3 - це рейтинг (Σ)
            if #cells >= 3 then
                local place = mw.text.trim(cells[1])
                local rating = cells[3]
               
                -- Видаляємо HTML теги з рейтингу
                rating = mw.ustring.gsub(rating, "<span[^>]*>", "")
                rating = mw.ustring.gsub(rating, "</span>", "")
               
                -- Видаляємо всі нецифрові символи окрім мінуса
                rating = mw.ustring.gsub(rating, "[^%d%-]", "")
                rating = mw.text.trim(rating)
               
                if rating and rating ~= "" then
                    return string.format("%s (%s місце)", rating, place)
                end
            end
              
              
             return string.format("[[%s|%s сезон. %s]]", clean_name, roman, type_text)
             return "0"
         end
         end
     end
     end
      
      
     return string.format("[[%s]]", header_name)
     return "0"
end
end


local function get_player_row(page_title, player_name)
function p.foundation(frame)
     local table_content = get_table_from_page(page_title)
     local name = frame.args.player
    if not table_content then return nil end
    local raw = fetch_from_table("Фундація", name, 3)
      
      
     local first_row = mw.ustring.match(table_content, "{|[^\n]*\n!(.-)[\n]")
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
     local headers = {}
        return "0 ₴"
     end
      
      
     if first_row then
     if raw then  
         for header in mw.ustring.gmatch(first_row, "([^|]+)") do
         raw = mw.ustring.gsub(raw, "[^%d%s]", "")
            local trimmed = mw.text.trim(header)
        raw = mw.text.trim(raw)
            if trimmed ~= "" then
                table.insert(headers, trimmed)
            end
        end
     end
     end
      
      
     if #headers == 0 then return nil end
     return (raw or "0") .. " ₴"
   
    local cells = find_player_in_table(table_content, player_name)
    if not cells then return nil end
   
    return {headers = headers, cells = cells}
end
end


function p.titles(frame)
function p.finalist(frame)
     local name = frame.args.player
     local name = frame.args.player
     if not name or name == "" then return "''Відсутні''" end
     local raw = fetch_from_table("Фіналіст", name, 3)
      
      
     local titles_data = get_player_row("Титули", name)
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
    if not titles_data then return "''Відсутні''" end
        return "0/9"
    end
      
      
    local prize_data = get_player_row("Призові", name)
     if not raw or raw == "" or raw == "-" then
     if not prize_data then return error_output("Prize Data Missing", name) end
        return "0/9"
   
    local results = {}
   
    for i = 2, #titles_data.cells do
        local cell_value = titles_data.cells[i]
        local header = titles_data.headers[i]
        local prize_value = prize_data.cells[i]
       
        if cell_value and cell_value ~= "" and cell_value ~= "-" and header then
            local medal = get_medal_icon(cell_value)
           
            if medal then
                local prize_amount = "0 ₴"
               
                if prize_value and prize_value ~= "" and prize_value ~= "-" then
                    prize_value = mw.ustring.gsub(prize_value, "₴", "")
                    prize_value = mw.text.trim(prize_value)
                    if prize_value ~= "0" then
                        prize_amount = prize_value .. " ₴"
                    end
                end
               
                local date = tournament_dates[header] or "01.01.2023"
                local tournament_link = get_tournament_link(header)
               
                table.insert(results, {
                    date = date,
                    medal = medal,
                    tournament = tournament_link,
                    prize = prize_amount
                })
            end
        end
     end
     end
      
      
     if #results == 0 then return "''Відсутні''" end
     -- Видаляємо всі нецифрові символи
    local number_only = mw.ustring.gsub(raw, "[^%d]", "")
      
      
     local table_html = {
     -- Перетворюємо в число
        '{| class="wikitable sortable" style="font-size: 14.5px;"',
    local count = tonumber(number_only)
        '|-',
        '! style="" | Дата',
        '! style="text-align: left;" | Місце і турнір',
        '! style="text-align: left;" | Приз'
    }
      
      
     for _, result in ipairs(results) do
     if count then
        table.insert(table_html, '|-')
         return string.format("%d/9", count)
         table.insert(table_html, string.format(
            '| style="width:80px; text-align:center;" | %s || style="text-align:left; padding-left:10px;" | %s %s || style="text-align:right;" | %s',
            result.date, result.medal, result.tournament, result.prize
        ))
     end
     end
      
      
    table.insert(table_html, '|}')
     return "0/9"
     return table.concat(table_html, '\n')
end
end


-- ================================================
function p.games_count(frame)
-- FETCHDATA4 (цікаві факти)
    local name = frame.args.player
-- ================================================
    local raw = fetch_from_table("Статистика", name, 3)
 
local pages_to_search = {
    "Перший сезон", "Другий сезон", "Третій сезон", "Четвертий сезон",
    "П'ятий сезон", "Шостий сезон", "Сьомий сезон", "Восьмий сезон",
    "Дев'ятий сезон", "Mafia Closed Circle", "Ten's Games", "Alternatywa",
    "Майстерня Стратегічного Саморозвитку", "Get Names 01", "Get Names 02",
    "Get Names 03", "Get Names 04", "Get Names 05", "Get Names 06",
    "Get Names 07", "Get Names 08", "Get Names 09", "Women's Closed Cup I",
    "Women's Closed Cup II", "Men's Closed Cup I", "Men's Closed Cup II",
    "Combined Closed Cup I", "Combined Closed Cup II", "Score Open Battle I",
    "Score Open Battle II", "Mafia Closed Cup I", "Mafia Closed Cup I Online",
    "My Closest Circle I", "Фінал Року"
}
 
local function get_facts_section(page_title)
    local content = get_page_content(page_title)
    if not content then return nil end
      
      
     local section_start = mw.ustring.find(content, "==%s*[Цц]ікаві%s*[Фф]акти%s*==")
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
     if not section_start then return nil end
        return "0"
     end
      
      
     local section_end = mw.ustring.find(content, "\n==[^=]", section_start + 1)
    return raw or "0"
end
 
function p.wins_count(frame)
     local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 4)
      
      
    local section_content
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
     if section_end then
         return "0"
        section_content = mw.ustring.sub(content, section_start, section_end - 1)
    else
         section_content = mw.ustring.sub(content, section_start)
     end
     end
      
      
     return section_content
     return raw or "0"
end
end


local function fact_contains_player(fact, player_name)
function p.losses_count(frame)
    local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
     local name = frame.args.player
     local pattern1 = "%[%[" .. escaped_name .. "%]%]"
     local raw = fetch_from_table("Статистика", name, 5)
     local pattern2 = "%[%[" .. escaped_name .. "|[^%]]+%]%]"
      
      
     return mw.ustring.find(fact, pattern1) or mw.ustring.find(fact, pattern2)
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
end
         return "0"
 
local function extract_facts(section_content)
    local facts = {}
    for fact in mw.ustring.gmatch(section_content, "%*[^\n]+") do
         table.insert(facts, fact)
     end
     end
     return facts
   
     return raw or "0"
end
end


function p.facts(frame)
function p.win_rate(frame)
     local player_name = frame.args.player or frame.args[1] or mw.title.getCurrentTitle().text
     local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 6)
      
      
     local result_parts = {}
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
        return "0%"
    end
      
      
     for _, page_title in ipairs(pages_to_search) do
     if raw then
        local section = get_facts_section(page_title)
        -- Якщо немає знаку %, додаємо його
       
        if not mw.ustring.find(raw, "%%") then
        if section then
            return raw .. "%"
            local facts = extract_facts(section)
            local player_facts = {}
           
            for _, fact in ipairs(facts) do
                if fact_contains_player(fact, player_name) then
                    table.insert(player_facts, fact)
                end
            end
           
            if #player_facts > 0 then
                local block = string.format("==== %s ====\n", page_title)
                for _, fact in ipairs(player_facts) do
                    block = block .. fact .. "\n"
                end
                table.insert(result_parts, string.format('<div style="margin-bottom: 20px;">\n%s</div>', block))
            end
         end
         end
        return raw
     end
     end
      
      
     if #result_parts == 0 then return "" end
     return "0%"
   
    return table.concat(result_parts, "\n")
end
end


-- ================================================
function p.win_rate_colored(frame)
-- FETCHDATA5 (записи ігор)
    local name = frame.args.player
-- ================================================
    local raw = fetch_from_table("Статистика", name, 6)
 
      
local function get_tournament_link_games(short_name, full_name)
     if type(raw) == "string" and mw.ustring.find(raw, "Error") then
     if short_name == "Фанова гра" then
         return "0%"
        return "Фанова гра"
     elseif short_name == "Score Battle" then
        return "[[Score Open Battle I|Score Battle]]"
    elseif short_name and short_name ~= "" and full_name and full_name ~= "" then
        return "[[" .. full_name .. "|" .. short_name .. "]]"
    else
         return short_name or full_name or "Невідомо"
     end
     end
end
   
 
     if not raw or raw == "" then
local function get_player_role(player_name, cells)
        return "0%"
     for i = 5, 10 do
        if cells[i] and clean_wikilinks(cells[i]) == player_name then
            return "Мирний"
        end
     end
     end
      
      
     if cells[11] and clean_wikilinks(cells[11]) == player_name then
     -- Видаляємо % та конвертуємо в число
         return "Шериф"
    local percent_str = mw.ustring.gsub(raw, "%%", "")
    local percent_num = tonumber(percent_str)
   
    if not percent_num then
         return raw
     end
     end
      
      
     for i = 12, 13 do
     -- Визначаємо колір
         if cells[i] and clean_wikilinks(cells[i]) == player_name then
    local color = ""
            return "Мафія"
    if percent_num < 50 then
         end
         color = "indianred"
    elseif percent_num > 50 then
        color = "gold"
    else
         -- Рівно 50% - білий
        color = "white"
     end
     end
      
      
     if cells[14] and clean_wikilinks(cells[14]) == player_name then
    -- Додаємо % якщо його немає
         return "Дон"
    local display_text = raw
     if not mw.ustring.find(raw, "%%") then
         display_text = raw .. "%"
     end
     end
      
      
     return "Невідомо"
     return string.format("<span style='color:%s;'>%s</span>", color, display_text)
end
end


local function get_player_result(role, game_result)
--- Отримує всіх гравців з таблиці "Гравці" в порядку дати приєднання
     if not game_result or game_result == "" then return "Невідомо" end
local function get_all_players()
    local title = mw.title.new("Гравці")
     if not title or not title.exists then
        return nil
    end
   
    local content = title:getContent()
    if not content then  
        return nil
    end
      
      
     game_result = mw.text.trim(game_result)
     local table_start = mw.ustring.find(content, "{|")
    local table_end = mw.ustring.find(content, "|}", table_start)
      
      
     if game_result == "Місто" or game_result == "Мирні" then
     if not table_start or not table_end then
         return (role == "Мирний" or role == "Шериф") and "Перемога" or "Поразка"
         return nil
     end
     end
      
      
     if game_result == "Мафія" then
     local table_content = mw.ustring.sub(content, table_start, table_end + 1)
         return (role == "Мафія" or role == "Дон") and "Перемога" or "Поразка"
   
    local players = {}
    local rows = {}
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
         table.insert(rows, row)
     end
     end
      
      
     return "Невідомо"
     local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
end
     if last_row then
 
        table.insert(rows, last_row)
local function get_player_games_list(player_name)
     end
    local table_content = get_table_from_page("Ігри")
     if not table_content then return nil end
   
    local rows = parse_table_rows(table_content)
     local games = {}
      
      
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
         local cells = parse_row_cells(row)
         local cells = {}
        row = mw.ustring.gsub(row, "^%s*|%s*", "")
          
          
        local player_in_game = false
         for cell in mw.ustring.gmatch(row, "([^|]+)") do
         for i = 5, 14 do
             local trimmed = mw.text.trim(cell)
             if cells[i] and clean_wikilinks(cells[i]) == player_name then
            if trimmed ~= "" then
                 player_in_game = true
                 table.insert(cells, trimmed)
                break
             end
             end
         end
         end
          
          
         if player_in_game and #cells >= 15 then
        -- Колонка 2 - ім'я гравця
             local role = get_player_role(player_name, cells)
         if #cells >= 2 then
            local result = get_player_result(role, cells[15])
             local player_name = clean_wikilinks(cells[2])
              
             if player_name then
            table.insert(games, {
                table.insert(players, player_name)
                tournament = cells[3] or "",
             end
                short = cells[4] or "",
                role = role,
                result = result,
                link = cells[16] or ""
             })
         end
         end
     end
     end
      
      
     return games
     return players
end
end


function p.player_games(frame)
function p.prev_player(frame)
     local player_name = frame.args.player
     local name = frame.args.player
     if not player_name or player_name == "" then
     local players = get_all_players()
        return error_output("No Player Name", "")
    end
      
      
    local games = get_player_games_list(player_name)
     if not players then
     if not games then
         return ""
         return error_output("Cannot Load Games", "Ігри")
     end
     end
      
      
    if #games == 0 then
     for i, player in ipairs(players) do
        return "''Ігор не знайдено''"
         if player == name then
    end
             if i > 1 then
   
                return players[i - 1]
    local htmlTable = mw.html.create('table')
            else
        :addClass('wikitable sortable')
                return "" -- Перший гравець, немає попереднього
        :css('width', '100%')
             end
        :css('font-size', '14.5px')
   
    local headerRow = htmlTable:tag('tr')
    headerRow:tag('th'):wikitext('Турнір')
    headerRow:tag('th'):wikitext('Роль')
    headerRow:tag('th'):wikitext('Результат')
    headerRow:tag('th'):wikitext('Запис')
   
     for _, game in ipairs(games) do
        local row = htmlTable:tag('tr')
       
        row:tag('td'):css('text-align', 'center'):css('padding', '8px')
            :wikitext(get_tournament_link_games(game.short, game.tournament))
       
        row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(game.role)
       
        local resultCell = row:tag('td'):css('text-align', 'center'):css('padding', '8px')
         if game.result == "Перемога" then
             resultCell:tag('span'):css('color', '#4caf50'):css('font-weight', 'normal'):wikitext('Перемога')
        elseif game.result == "Поразка" then
            resultCell:tag('span'):css('color', 'indianred'):css('font-weight', 'normal'):wikitext('Поразка')
        else
            resultCell:wikitext(game.result)
        end
       
        local linkCell = row:tag('td'):css('text-align', 'center'):css('padding', '8px')
        if game.link and game.link ~= "" then
             linkCell:wikitext('<span class="game-record-btn">[' .. game.link .. ' Запис]</span>')
         end
         end
     end
     end
      
      
     return tostring(htmlTable)
     return ""
end
end


function p.games_count_records(frame)
function p.next_player(frame)
     local player_name = frame.args.player
     local name = frame.args.player
     if not player_name or player_name == "" then return "0" end
     local players = get_all_players()
      
      
    local games = get_player_games_list(player_name)
     if not players then
    return games and tostring(#games) or "0"
        return ""
end
 
-- ================================================
-- FETCHDATA6 (досягнення в сезонах)
-- ================================================
 
local function get_season_data(season_number, player_name)
    local season_names = {
        [1] = "Перший сезон", [2] = "Другий сезон", [3] = "Третій сезон",
        [4] = "Четвертий сезон", [5] = "П'ятий сезон", [6] = "Шостий сезон",
        [7] = "Сьомий сезон", [8] = "Восьмий сезон", [9] = "Дев'ятий сезон"
    }
   
    local season_name = season_names[season_number]
     if not season_name then return nil end
   
    local content = get_page_content(season_name)
    if not content then return nil end
   
    local rating_section = mw.ustring.match(content, "==+%s*Рейтинг%s*==+(.-)\n==")
    if not rating_section then
        rating_section = mw.ustring.match(content, "==+%s*Рейтинг%s*==+(.*)")
     end
     end
    if not rating_section then return nil end
   
    local table_start = mw.ustring.find(rating_section, "{|")
    local table_end = mw.ustring.find(rating_section, "|}", table_start)
    if not table_start or not table_end then return nil end
   
    local table_content = mw.ustring.sub(rating_section, table_start, table_end + 1)
   
    local current_row = {}
    local all_rows = {}
      
      
     for line in mw.ustring.gmatch(table_content .. "\n", "([^\n]*)\n") do
     for i, player in ipairs(players) do
        line = mw.text.trim(line)
         if player == name then
       
             if i < #players then
         if line == "|-" then
                 return players[i + 1]
             if #current_row > 0 then
            else
                 table.insert(all_rows, current_row)
                return "" -- Останній гравець, немає наступного
             end
             end
            current_row = {}
        elseif mw.ustring.match(line, "^|[^-}]") then
            local cell_content = mw.ustring.gsub(line, "^|%s*", "")
            table.insert(current_row, cell_content)
         end
         end
     end
     end
      
      
     if #current_row > 0 then
     return ""
        table.insert(all_rows, current_row)
end
     end
 
function p.prev_player_link(frame)
     local name = frame.args.player
    local prev = p.prev_player(frame)
      
      
     for _, row in ipairs(all_rows) do
     if prev == "" or not prev then
        if #row >= 5 then
        return ""
            local player_cell = clean_wikilinks(row[2])
           
            if player_cell == player_name then
                return {
                    place = mw.text.trim(row[1]),
                    points = mw.text.trim(row[3]),
                    games = mw.text.trim(row[4]),
                    winrate = mw.text.trim(row[5])
                }
            end
        end
     end
     end
      
      
     return nil
     return string.format(
        '<a href="/index.php/%s" style="width:48px; height:48px; display:flex; align-items:center; justify-content:center; text-decoration:none; border:2px solid #333; border-radius:8px;"><span style="color:gold; font-size:24px; font-weight:bold;">←</span></a>',
        mw.uri.encode(prev, "WIKI")
    )
end
end


function p.season_achievements(frame)
function p.next_player_link(frame)
     local player_name = frame.args.player
     local name = frame.args.player
     if not player_name or player_name == "" then
     local next = p.next_player(frame)
        return "<span style='color:red;'>Ім'я гравця не вказано</span>"
    end
      
      
     local htmlTable = mw.html.create('table')
     if next == "" or not next then
        :addClass('wikitable sortable')
         return ""
        :css('width', '100%')
        :css('font-size', '14.5px')
   
    local headerRow = htmlTable:tag('tr')
    headerRow:tag('th'):wikitext('Сезон')
    headerRow:tag('th'):wikitext('Місце')
    headerRow:tag('th'):wikitext('Бали')
    headerRow:tag('th'):wikitext('% перемог')
    headerRow:tag('th'):wikitext('Ігор')
   
    local season_links = {
        [1] = "[[Перший сезон#Рейтинг|01 сезон]]",
         [2] = "[[Другий сезон#Рейтинг|02 сезон]]",
        [3] = "[[Третій сезон#Рейтинг|03 сезон]]",
        [4] = "[[Четвертий сезон#Рейтинг|04 сезон]]",
        [5] = "[[П'ятий сезон#Рейтинг|05 сезон]]",
        [6] = "[[Шостий сезон#Рейтинг|06 сезон]]",
        [7] = "[[Сьомий сезон#Рейтинг|07 сезон]]",
        [8] = "[[Восьмий сезон#Рейтинг|08 сезон]]",
        [9] = "[[Дев'ятий сезон#Рейтинг|09 сезон]]"
    }
   
    for season = 1, 9 do
        local data = get_season_data(season, player_name)
        local row = htmlTable:tag('tr')
       
        row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(season_links[season])
       
        if data then
            row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(data.place)
            row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(data.points)
            row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(data.winrate)
            row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(data.games)
        else
            for i = 1, 4 do
                row:tag('td'):css('text-align', 'center'):css('padding', '8px'):css('color', '#666'):wikitext('-')
            end
        end
     end
     end
      
      
     return tostring(htmlTable)
     return string.format(
        '<a href="/index.php/%s" style="width:48px; height:48px; display:flex; align-items:center; justify-content:center; text-decoration:none; border:2px solid #333; border-radius:8px;"><span style="color:gold; font-size:24px; font-weight:bold;">→</span></a>',
        mw.uri.encode(next, "WIKI")
    )
end
end


return p
return p

Версія за 10:39, 1 грудня 2025

Документацію для цього модуля можна створити у Модуль:FetchData/документація

local p = {}

--- Функція для виведення помилки на сторінку
local function error_output(context, error_details)
    local safe_details = error_details or "N/A" 
    return string.format("<span style='color:red; font-weight:bold;'>[FD2 Error: %s. Details: %s]</span>", 
        context or "Unknown Context", safe_details)
end

--- Видаляє вікі-посилання [[link|text]] і [[link]]
local function clean_wikilinks(text)
    if not text then return nil end
    -- [[link|text]] -> text
    text = mw.ustring.gsub(text, "%[%[([^%]|]+)|([^%]]+)%]%]", "%2")
    -- [[link]] -> link
    text = mw.ustring.gsub(text, "%[%[([^%]]+)%]%]", "%1")
    return mw.text.trim(text)
end

----------------------------------------------------------------------
-- ГОЛОВНИЙ ПАРСЕР ДЛЯ ГОРИЗОНТАЛЬНИХ ТАБЛИЦЬ
----------------------------------------------------------------------
local function fetch_from_table(page_title, player_name, column_index)
    
    if not player_name or player_name == "" then
        return error_output("No Player Name", page_title) 
    end
    
    local title = mw.title.new(page_title)
    if not title or not title.exists then 
        return error_output("Page Missing", page_title)
    end
    
    local content = title:getContent()
    if not content then 
        return error_output("Content Read Fail", page_title) 
    end
    
    -- Знаходимо таблицю
    local table_start = mw.ustring.find(content, "{|")
    local table_end = mw.ustring.find(content, "|}", table_start)
    
    if not table_start or not table_end then
        return error_output("Table Missing", page_title)
    end
    
    local table_content = mw.ustring.sub(content, table_start, table_end + 1)
    
    -- Екрануємо спеціальні символи в імені гравця
    local escaped = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped .. "%]%]"
    
    -- Шукаємо рядки, які починаються з |-
    local rows = {}
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
        table.insert(rows, row)
    end
    
    -- Останній рядок (до |})
    local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
    if last_row then
        table.insert(rows, last_row)
    end
    
    -- Обробляємо кожен рядок
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            -- Розбиваємо рядок на комірки
            local cells = {}
            
            -- Видаляємо початковий |
            row = mw.ustring.gsub(row, "^%s*|%s*", "")
            
            -- Розбиваємо по ||
            for cell in mw.ustring.gmatch(row, "([^|]+)") do
                -- Пропускаємо порожні комірки від подвійних ||
                local trimmed = mw.text.trim(cell)
                if trimmed ~= "" then
                    table.insert(cells, trimmed)
                end
            end
            
            -- Повертаємо потрібну колонку
            if column_index <= #cells then
                return clean_wikilinks(cells[column_index])
            else
                return error_output("Column Out of Range", 
                    string.format("Requested col %d, found %d cells", column_index, #cells))
            end
        end
    end
    
    return error_output("Player Not Found", player_name)
end

----------------------------------------------------------------------
-- ФУНКЦІЇ, ЩО ВИКЛИКАЮТЬСЯ З ШАБЛОНУ
----------------------------------------------------------------------
function p.recruiter(frame)
    local name = frame.args.player
    -- Don't clean wikilinks - keep them for clickable links
    local title = mw.title.new("Гравці")
    if not title or not title.exists then 
        return error_output("Page Missing", "Гравці")
    end
    
    local content = title:getContent()
    if not content then 
        return error_output("Content Read Fail", "Гравці") 
    end
    
    local table_start = mw.ustring.find(content, "{|")
    local table_end = mw.ustring.find(content, "|}", table_start)
    
    if not table_start or not table_end then
        return error_output("Table Missing", "Гравці")
    end
    
    local table_content = mw.ustring.sub(content, table_start, table_end + 1)
    local escaped = mw.ustring.gsub(name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped .. "%]%]"
    
    local rows = {}
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
        table.insert(rows, row)
    end
    
    local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
    if last_row then
        table.insert(rows, last_row)
    end
    
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            local cells = {}
            row = mw.ustring.gsub(row, "^%s*|%s*", "")
            
            for cell in mw.ustring.gmatch(row, "([^|]+)") do
                local trimmed = mw.text.trim(cell)
                if trimmed ~= "" then
                    table.insert(cells, trimmed)
                end
            end
            
            if 4 <= #cells then
                local raw = mw.text.trim(cells[4])
                if raw == "Відсутній" or raw == "-" then 
                    return "Не вказано" 
                end
                -- Return with wikilinks intact
                return raw
            end
        end
    end
    
    return "Не вказано"
end

function p.date_added(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Гравці", name, 3) 
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "Лише Бог знає"
    end
    
    if not raw or raw == "" or raw == "-" or raw == "Відсутній" then
        return "Лише Бог знає"
    end
    
    -- Парсимо дату у форматі DD.MM.YYYY
    local day, month, year = mw.ustring.match(raw, "(%d+)%.(%d+)%.(%d+)")
    
    if day and month and year then
        -- Кінцева дата: 26.10.2024
        local end_date = os.time({year=2024, month=10, day=26})
        local start_date = os.time({year=tonumber(year), month=tonumber(month), day=tonumber(day)})
        
        local days_diff = math.floor((end_date - start_date) / 86400)
        
        return string.format("%s (%d днів)", raw, days_diff)
    end
    
    return "Лише Бог знає"
end

function p.prize_pool(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Призовий_фонд", name, 2) 
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0 ₴"
    end
    
    if raw then 
        raw = mw.ustring.gsub(raw, "[^%d%s]", "")
        raw = mw.text.trim(raw)
    end
    
    return (raw or "0") .. " ₴"
end

-- Додай цю функцію до FetchData2 (модуль:FetchData2)

function p.foty_rating(frame)
    local name = frame.args.player
    
    if not name or name == "" then
        return "0"
    end
    
    local title = mw.title.new("Період")
    if not title or not title.exists then 
        return "0"
    end
    
    local content = title:getContent()
    if not content then 
        return "0"
    end
    
    -- Шукаємо секцію "Фінал Року"
    local section_start = mw.ustring.find(content, "== Фінал Року ==")
    if not section_start then
        return "0"
    end
    
    -- Знаходимо таблицю після секції
    local table_start = mw.ustring.find(content, "{|", section_start)
    if not table_start then
        return "0"
    end
    
    local table_end = mw.ustring.find(content, "|}", table_start)
    if not table_end then
        return "0"
    end
    
    local table_content = mw.ustring.sub(content, table_start, table_end + 1)
    
    -- Екрануємо спеціальні символи в імені гравця
    local escaped = mw.ustring.gsub(name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped .. "%]%]"
    
    -- Збираємо всі рядки
    local rows = {}
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
        table.insert(rows, row)
    end
    
    local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
    if last_row then
        table.insert(rows, last_row)
    end
    
    -- Обробляємо кожен рядок
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            local cells = {}
            row = mw.ustring.gsub(row, "^%s*|%s*", "")
            
            for cell in mw.ustring.gmatch(row, "([^|]+)") do
                local trimmed = mw.text.trim(cell)
                if trimmed ~= "" then
                    table.insert(cells, trimmed)
                end
            end
            
            -- Колонка 1 - це місце (№)
            -- Колонка 3 - це рейтинг (Σ)
            if #cells >= 3 then
                local place = mw.text.trim(cells[1])
                local rating = cells[3]
                
                -- Видаляємо HTML теги з рейтингу
                rating = mw.ustring.gsub(rating, "<span[^>]*>", "")
                rating = mw.ustring.gsub(rating, "</span>", "")
                
                -- Видаляємо всі нецифрові символи окрім мінуса
                rating = mw.ustring.gsub(rating, "[^%d%-]", "")
                rating = mw.text.trim(rating)
                
                if rating and rating ~= "" then
                    return string.format("%s (%s місце)", rating, place)
                end
            end
            
            return "0"
        end
    end
    
    return "0"
end

function p.foundation(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Фундація", name, 3)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0 ₴"
    end
    
    if raw then 
        raw = mw.ustring.gsub(raw, "[^%d%s]", "")
        raw = mw.text.trim(raw)
    end
    
    return (raw or "0") .. " ₴"
end

function p.finalist(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Фіналіст", name, 3)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0/9"
    end
    
    if not raw or raw == "" or raw == "-" then
        return "0/9"
    end
    
    -- Видаляємо всі нецифрові символи
    local number_only = mw.ustring.gsub(raw, "[^%d]", "")
    
    -- Перетворюємо в число
    local count = tonumber(number_only)
    
    if count then
        return string.format("%d/9", count)
    end
    
    return "0/9"
end

function p.games_count(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 3)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0"
    end
    
    return raw or "0"
end

function p.wins_count(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 4)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0"
    end
    
    return raw or "0"
end

function p.losses_count(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 5)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0"
    end
    
    return raw or "0"
end

function p.win_rate(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 6)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0%"
    end
    
    if raw then
        -- Якщо немає знаку %, додаємо його
        if not mw.ustring.find(raw, "%%") then
            return raw .. "%"
        end
        return raw
    end
    
    return "0%"
end

function p.win_rate_colored(frame)
    local name = frame.args.player
    local raw = fetch_from_table("Статистика", name, 6)
    
    if type(raw) == "string" and mw.ustring.find(raw, "Error") then 
        return "0%"
    end
    
    if not raw or raw == "" then
        return "0%"
    end
    
    -- Видаляємо % та конвертуємо в число
    local percent_str = mw.ustring.gsub(raw, "%%", "")
    local percent_num = tonumber(percent_str)
    
    if not percent_num then
        return raw
    end
    
    -- Визначаємо колір
    local color = ""
    if percent_num < 50 then
        color = "indianred"
    elseif percent_num > 50 then
        color = "gold"
    else
        -- Рівно 50% - білий
        color = "white"
    end
    
    -- Додаємо % якщо його немає
    local display_text = raw
    if not mw.ustring.find(raw, "%%") then
        display_text = raw .. "%"
    end
    
    return string.format("<span style='color:%s;'>%s</span>", color, display_text)
end

--- Отримує всіх гравців з таблиці "Гравці" в порядку дати приєднання
local function get_all_players()
    local title = mw.title.new("Гравці")
    if not title or not title.exists then 
        return nil
    end
    
    local content = title:getContent()
    if not content then 
        return nil
    end
    
    local table_start = mw.ustring.find(content, "{|")
    local table_end = mw.ustring.find(content, "|}", table_start)
    
    if not table_start or not table_end then
        return nil
    end
    
    local table_content = mw.ustring.sub(content, table_start, table_end + 1)
    
    local players = {}
    local rows = {}
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
        table.insert(rows, row)
    end
    
    local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
    if last_row then
        table.insert(rows, last_row)
    end
    
    for _, row in ipairs(rows) do
        local cells = {}
        row = mw.ustring.gsub(row, "^%s*|%s*", "")
        
        for cell in mw.ustring.gmatch(row, "([^|]+)") do
            local trimmed = mw.text.trim(cell)
            if trimmed ~= "" then
                table.insert(cells, trimmed)
            end
        end
        
        -- Колонка 2 - ім'я гравця
        if #cells >= 2 then
            local player_name = clean_wikilinks(cells[2])
            if player_name then
                table.insert(players, player_name)
            end
        end
    end
    
    return players
end

function p.prev_player(frame)
    local name = frame.args.player
    local players = get_all_players()
    
    if not players then
        return ""
    end
    
    for i, player in ipairs(players) do
        if player == name then
            if i > 1 then
                return players[i - 1]
            else
                return "" -- Перший гравець, немає попереднього
            end
        end
    end
    
    return ""
end

function p.next_player(frame)
    local name = frame.args.player
    local players = get_all_players()
    
    if not players then
        return ""
    end
    
    for i, player in ipairs(players) do
        if player == name then
            if i < #players then
                return players[i + 1]
            else
                return "" -- Останній гравець, немає наступного
            end
        end
    end
    
    return ""
end

function p.prev_player_link(frame)
    local name = frame.args.player
    local prev = p.prev_player(frame)
    
    if prev == "" or not prev then
        return ""
    end
    
    return string.format(
        '<a href="/index.php/%s" style="width:48px; height:48px; display:flex; align-items:center; justify-content:center; text-decoration:none; border:2px solid #333; border-radius:8px;"><span style="color:gold; font-size:24px; font-weight:bold;">←</span></a>',
        mw.uri.encode(prev, "WIKI")
    )
end

function p.next_player_link(frame)
    local name = frame.args.player
    local next = p.next_player(frame)
    
    if next == "" or not next then
        return ""
    end
    
    return string.format(
        '<a href="/index.php/%s" style="width:48px; height:48px; display:flex; align-items:center; justify-content:center; text-decoration:none; border:2px solid #333; border-radius:8px;"><span style="color:gold; font-size:24px; font-weight:bold;">→</span></a>',
        mw.uri.encode(next, "WIKI")
    )
end

return p