Немає опису редагування
Немає опису редагування
Мітка: Скасовано
Рядок 2: Рядок 2:


-- ================================================
-- ================================================
-- КЕШУВАННЯ (найважливіша оптимізація)
-- ГЛОБАЛЬНЕ КЕШУВАННЯ
-- ================================================
-- ================================================


local page_cache = {}
local page_cache = {}
local table_cache = {}
local table_cache = {}
-- ================================================
-- УТИЛІТИ
-- ================================================
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 error_output(message)
    return '<span style="color:red; font-weight:bold;">Помилка: ' .. message .. '</span>'
end


local function get_page_content(page_name)
local function get_page_content(page_name)
Рядок 15: Рядок 30:
     local title = mw.title.new(page_name)
     local title = mw.title.new(page_name)
     if not title or not title.exists then
     if not title or not title.exists then
        page_cache[page_name] = nil
         return nil
         return nil
     end
     end
Рядок 30: Рядок 44:
      
      
     local content = get_page_content(page_name)
     local content = get_page_content(page_name)
     if not content then  
     if not content then return nil end
        table_cache[page_name] = nil
        return nil  
    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 return nil end
        table_cache[page_name] = nil
        return nil
    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
     table_cache[page_name] = table_content
     return table_content
     return table_content
end
-- ================================================
-- УТИЛІТИ (спільні для всіх функцій)
-- ================================================
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
end


Рядок 87: Рядок 71:
     local cells = {}
     local cells = {}
     row = mw.ustring.gsub(row, "^%s*|%s*", "")
     row = mw.ustring.gsub(row, "^%s*|%s*", "")
     for cell in mw.ustring.gmatch(row, "([^|]+)") do
   
     for cell in mw.ustring.gmatch(row, "|([^|]+)") do
         local trimmed = mw.text.trim(cell)
         local trimmed = mw.text.trim(cell)
         if trimmed ~= "" then
         if trimmed ~= "" then
Рядок 93: Рядок 78:
         end
         end
     end
     end
   
     return cells
     return cells
end
end


local function find_player_in_table(table_content, player_name, column_index)
local function find_player_data(table_content, player_name)
     if not table_content then return nil end
     if not table_content then return nil end
      
      
Рядок 106: Рядок 92:
     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)
             return parse_row_cells(row)
            if column_index and column_index <= #cells then
                return cells[column_index]
            end
            return cells
         end
         end
     end
     end
      
      
     return nil
     return nil
end
-- Конвертація часу (якщо потрібно)
local function format_time(time_str)
    if not time_str or time_str == "" then return "" end
   
    time_str = mw.text.trim(time_str)
   
    -- Якщо вже в правильному форматі
    if mw.ustring.find(time_str, "хв") then
        return time_str
    end
   
    -- Парсимо формат HH:MM:SS або MM:SS
    local hours, minutes, seconds = mw.ustring.match(time_str, "(%d+):(%d+):(%d+)")
   
    if not hours then
        minutes, seconds = mw.ustring.match(time_str, "(%d+):(%d+)")
        hours = "0"
    end
   
    if minutes and seconds then
        hours = tonumber(hours) or 0
        minutes = tonumber(minutes) or 0
        seconds = tonumber(seconds) or 0
       
        if hours > 0 then
            return string.format("%d год %02d хв %02d с", hours, minutes, seconds)
        else
            return string.format("%d хв %02d с", minutes, seconds)
        end
    end
   
    return time_str
end
end


-- ================================================
-- ================================================
-- FETCHDATA (season_result)
-- ФУНКЦІЇ З FETCHDATA
-- ================================================
-- ================================================


function p.season_result(frame)
function p.season_result(frame)
     local season = frame.args.season
     local player_name = frame.args.player or frame.args[1]
     local player = frame.args.player
     local season_number = frame.args.season or frame.args[2]
      
      
     local season_titles = {
    if not player_name or player_name == "" then
         "Перший сезон", "Другий сезон", "Третій сезон",
        return error_output("Не вказано ім'я гравця")
         "Четвертий сезон", "П'ятий сезон", "Шостий сезон",  
    end
         "Сьомий сезон", "Восьмий сезон", "Дев'ятий сезон"
   
    if not season_number or season_number == "" then
        return error_output("Не вказано номер сезону")
    end
   
    local season_page = nil
     local season_names = {
         ["1"] = "Перший сезон",
        ["2"] = "Другий сезон",
        ["3"] = "Третій сезон",
         ["4"] = "Четвертий сезон",
        ["5"] = "П'ятий сезон",
        ["6"] = "Шостий сезон",
         ["7"] = "Сьомий сезон",
        ["8"] = "Восьмий сезон",
        ["9"] = "Дев'ятий сезон"
     }
     }
      
      
     local season_title = season_titles[tonumber(season)]
    season_page = season_names[season_number]
     if not season_title then return "??" end
   
    if not season_page then
        return error_output("Невірний номер сезону")
    end
   
     local content = get_page_content(season_page)
    if not content then
        return error_output("Не вдалося завантажити сторінку сезону")
    end
   
    local final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.-)\n==")
    if not final_section then
        final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.*)")
    end
   
     if not final_section then
        return error_output("Не знайдено секцію Фінал")
    end
      
      
     local content = get_page_content(season_title)
     local table_start = mw.ustring.find(final_section, "{|")
     if not content then return "??" end
     local table_end = mw.ustring.find(final_section, "|}", table_start)
      
      
     local rating_section = mw.ustring.match(content, "==%s*Рейтинг%s*==.-{|%s*class%s*=%s*\"wikitable sortable\"(.-)|}")
     if not table_start or not table_end then
     if not rating_section then return "--" end
        return error_output("Не знайдено таблицю у секції Фінал")
     end
      
      
     local pattern = "|%s*(%d+)%s*|%s*%[%[" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%]%]"
     local table_content = mw.ustring.sub(final_section, table_start, table_end + 1)
     local direct_pattern = "|%s*(%d+)%s*|%s*" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%s*"
     local player_data = find_player_data(table_content, player_name)
      
      
     local rank = mw.ustring.match(rating_section, pattern) or mw.ustring.match(rating_section, direct_pattern)
     if not player_data or #player_data < 2 then
        return "—"
    end
      
      
     return rank or "--"
    local place = player_data[1]
     return place or ""
end
end


-- ================================================
-- ================================================
-- FETCHDATA2 (базова статистика)
-- ФУНКЦІЇ З FETCHDATA2
-- ================================================
-- ================================================


function p.games_count(frame)
function p.games_count(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
     if not player_name or player_name == "" then return "0" end
   
     if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
   
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
   
    local data = find_player_data(table_content, player_name)
   
    if not data or #data < 3 then
        return "0"
    end
      
      
    local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 2)
     return data[3] or "0"
     return result or "0"
end
end


function p.wins_count(frame)
function p.wins_count(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
     if not player_name or player_name == "" then return "0" end
   
     if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
   
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 3)
     local data = find_player_data(table_content, player_name)
     return result or "0"
   
    if not data or #data < 4 then
        return "0"
    end
   
     return data[4] or "0"
end
end


function p.losses_count(frame)
function p.losses_count(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
    if not player_name or player_name == "" then return "0" end
      
      
     local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 4)
    if not player_name or player_name == "" then
     return result or "0"
        return error_output("Не вказано ім'я гравця")
    end
   
     local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
   
    local data = find_player_data(table_content, player_name)
   
    if not data or #data < 5 then
        return "0"
    end
   
     return data[5] or "0"
end
end


function p.win_rate_colored(frame)
function p.win_rate_colored(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
    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 player_name or player_name == "" then
     if not win_rate then return "0%" end
        return error_output("Не вказано ім'я гравця")
     end
      
      
     local rate_num = tonumber(mw.ustring.match(win_rate, "([%d%.]+)"))
     local table_content = get_table_from_page("Статистика")
     if not rate_num then return win_rate end
     if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     local color = "#4caf50"
     local data = find_player_data(table_content, player_name)
     if rate_num < 40 then
   
         color = "indianred"
     if not data or #data < 6 then
    elseif rate_num < 50 then
         return '<span style="color:indianred;">0%</span>'
        color = "orange"
     end
     end
      
      
     if not mw.ustring.find(win_rate, "%%") then
     local winrate = data[6]
        win_rate = win_rate .. "%"
    winrate = mw.ustring.gsub(winrate, "%%", "")
    end
    winrate = mw.text.trim(winrate)
   
    local rate = tonumber(winrate) or 0
    local color = rate < 40 and "indianred" or "#4caf50"
      
      
     return string.format('<span style="color:%s;">%s</span>', color, win_rate)
     return '<span style="color:' .. color .. ';">' .. winrate .. '%</span>'
end
end


function p.recruiter(frame)
function p.recruiter(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
     if not player_name or player_name == "" then return "Не вказано" end
   
     if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
      
      
     local table_content = get_table_from_page("Гравці")
     local table_content = get_table_from_page("Гравці")
     if not table_content then return "Не вказано" end
     if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     local cells = find_player_in_table(table_content, player_name)
     local data = find_player_data(table_content, player_name)
    if not cells or #cells < 2 then return "Не вказано" end
      
      
    local raw = mw.text.trim(cells[2])
     if not data or #data < 4 then
     if raw == "Відсутній" or raw == "-" or raw == "" then  
         return "Не вказано"
         return "Не вказано"  
     end
     end
      
      
     return raw
     local recruiter = mw.text.trim(data[4])
   
    if recruiter == "Відсутній" or recruiter == "-" or recruiter == "" then
        return "Не вказано"
    end
   
    return recruiter
end
end


function p.date_added(frame)
function p.date_added(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
     if not player_name or player_name == "" then return "Лише Бог знає" end
   
     if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
   
    local table_content = get_table_from_page("Гравці")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     local raw = find_player_in_table(get_table_from_page("Гравці"), player_name, 3)
     local data = find_player_data(table_content, player_name)
      
      
     if not raw or raw == "" or raw == "-" or raw == "Відсутній" then
     if not data or #data < 3 then
         return "Лише Бог знає"
         return "Лише Бог знає"
     end
     end
      
      
     local day, month, year = mw.ustring.match(raw, "(%d+)%.(%d+)%.(%d+)")
     local raw_date = data[3]
      
      
     if day and month and year then
     if not raw_date or raw_date == "" or raw_date == "-" then
        local end_date = os.time({year=2024, month=12, day=1})
         return "Лише Бог знає"
         local start_date = os.time({year=tonumber(year), month=tonumber(month), day=tonumber(day)})
    end
        local days_diff = math.floor((end_date - start_date) / 86400)
   
       
    local day, month, year = mw.ustring.match(raw_date, "(%d+)%.(%d+)%.(%d+)")
        return string.format("%s (%d днів)", raw, days_diff)
   
    if not day or not month or not year then
        return raw_date
     end
     end
      
      
     return "Лише Бог знає"
    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_date, days_diff)
end
end


function p.foundation(frame)
function p.foundation(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
    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 player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
      
      
     if not raw or raw == "" or raw == "-" then return "0₴" end
     local table_content = get_table_from_page("Фундація")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     raw = mw.ustring.gsub(raw, "[^%d%s]", "")
     local data = find_player_data(table_content, player_name)
    raw = mw.text.trim(raw)
      
      
     return (raw or "0") .. " "
     if not data or #data < 3 then
        return "0"
    end
   
    local amount = mw.ustring.gsub(data[3], "[^%d]", "")
    return amount
end
end


function p.prize_pool(frame)
function p.prize_pool(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
    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 player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
      
      
     if not raw or raw == "" or raw == "-" then return "0₴" end
     local table_content = get_table_from_page("Призовий_фонд")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     raw = mw.ustring.gsub(raw, "[^%d%s]", "")
     local data = find_player_data(table_content, player_name)
    raw = mw.text.trim(raw)
      
      
     return (raw or "0") .. " "
     if not data or #data < 2 then
        return "0"
    end
   
    local amount = mw.ustring.gsub(data[2], "[^%d]", "")
    return amount
end
end


function p.finalist(frame)
function p.finalist(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
    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 player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
      
      
     if not raw or raw == "" or raw == "-" then return "0/9" end
     local table_content = get_table_from_page("Фіналіст")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
      
      
     local number_only = mw.ustring.gsub(raw, "[^%d]", "")
     local data = find_player_data(table_content, player_name)
    local count = tonumber(number_only)
      
      
     if count then
     if not data or #data < 3 then
         return string.format("%d/9", count)
         return "0"
     end
     end
      
      
     return "0/9"
     local count = mw.ustring.gsub(data[3], "[^%d]", "")
    return count
end
end


function p.foty_rating(frame)
function p.foty_rating(frame)
     local player_name = frame.args.player
     local player_name = frame.args.player or frame.args[1]
     if not player_name or player_name == "" then return "" end
   
     if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
      
      
     local content = get_page_content("Період")
     local content = get_page_content("Період")
     if not content then return "—" end
     if not content then
        return "—"
    end
      
      
     local foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.-)\n==")
     local foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.-)\n==")
Рядок 299: Рядок 442:
     end
     end
      
      
     if not foty_section then return "—" end
     if not foty_section then
        return "—"
    end
      
      
     local table_start = mw.ustring.find(foty_section, "{|")
     local table_start = mw.ustring.find(foty_section, "{|")
     local table_end = mw.ustring.find(foty_section, "|}", table_start)
     local table_end = mw.ustring.find(foty_section, "|}", table_start)
      
      
     if not table_start or not table_end then return "—" end
     if not table_start or not table_end then
        return "—"
    end
      
      
     local table_content = mw.ustring.sub(foty_section, table_start, table_end + 1)
     local table_content = mw.ustring.sub(foty_section, table_start, table_end + 1)
     local cells = find_player_in_table(table_content, player_name)
     local data = find_player_data(table_content, player_name)
      
      
     if not cells or #cells < 3 then return "—" end
     if not data or #data < 3 then
        return "—"
    end
      
      
     local place = strip_html_tags(cells[1]) or "—"
     local place = mw.text.trim(data[1])
     local rating = strip_html_tags(cells[3]) or "—"
     local rating = mw.text.trim(data[3])
      
      
    rating = mw.ustring.gsub(rating, "<span[^>]*>", "")
    rating = mw.ustring.gsub(rating, "</span>", "")
     rating = mw.ustring.gsub(rating, "[^%d%-]", "")
     rating = mw.ustring.gsub(rating, "[^%d%-]", "")
     rating = mw.text.trim(rating)
     rating = mw.text.trim(rating)
Рядок 325: Рядок 476:


-- ================================================
-- ================================================
-- FETCHDATA3 (титули та нагороди)
-- ФУНКЦІЇ З FETCHDATA5 (player_games)
-- ================================================
-- ================================================


local tournament_dates = {
function p.player_games(frame)
    ["Women's Closed Cup I"] = "15.03.2023",
     local player_name = frame.args.player or frame.args[1]
    ["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
     if not player_name or player_name == "" then
    elseif value == "1" then return "[[Файл:Gold.png|20px|link=]]"
         return error_output("Не вказано ім'я гравця")
    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 page_content = get_page_content("Записи_ігор")
        local season_map = {
    if not page_content then
            ["Перший"] = 1, ["Другий"] = 2, ["Третій"] = 3,
        return error_output("Не вдалося завантажити сторінку Записи_ігор")
            ["Четвертий"] = 4, ["П'ятий"] = 5, ["Шостий"] = 6,
            ["Сьомий"] = 7, ["Восьмий"] = 8, ["Дев'ятий"] = 9
        }
       
        local season_number = season_map[season_num]
        if season_number then
            local roman = to_roman(season_number)
            local type_text = mw.ustring.find(header_name, "фінал") and "Фінал." or "Рейтинг."
            local clean_name = mw.ustring.gsub(header_name, "%s*%(фінал%)%s*", "")
            clean_name = mw.ustring.gsub(clean_name, "%s*%(рейтинг%)%s*", "")
            clean_name = mw.text.trim(clean_name)
           
            return string.format("[[%s|%s сезон. %s]]", clean_name, roman, type_text)
        end
     end
     end
      
      
     return string.format("[[%s]]", header_name)
     local table_start = mw.ustring.find(page_content, "{|")
end
     local table_end = mw.ustring.find(page_content, "|}", table_start)
 
local function get_player_row(page_title, player_name)
    local table_content = get_table_from_page(page_title)
    if not table_content then return nil end
   
     local first_row = mw.ustring.match(table_content, "{|[^\n]*\n!(.-)[\n]")
    local headers = {}
      
      
     if first_row then
     if not table_start or not table_end then
         for header in mw.ustring.gmatch(first_row, "([^|]+)") do
         return error_output("Не знайдено таблицю")
            local trimmed = mw.text.trim(header)
            if trimmed ~= "" then
                table.insert(headers, trimmed)
            end
        end
     end
     end
      
      
     if #headers == 0 then return nil end
     local table_content = mw.ustring.sub(page_content, table_start, table_end + 1)
    local rows = parse_table_rows(table_content)
      
      
     local cells = find_player_in_table(table_content, player_name)
     if #rows < 1 then
     if not cells then return nil end
        return error_output("Таблиця порожня")
     end
      
      
     return {headers = headers, cells = cells}
     local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
end
     local player_pattern = "%[%[" .. escaped_name .. "%]%]"
 
function p.titles(frame)
     local name = frame.args.player
    if not name or name == "" then return "''Відсутні''" end
      
      
     local titles_data = get_player_row("Титули", name)
     local player_games = {}
    if not titles_data then return "''Відсутні''" end
      
      
     local prize_data = get_player_row("Призові", name)
     for _, row in ipairs(rows) do
    if not prize_data then return error_output("Prize Data Missing", name) end
         if mw.ustring.find(row, player_pattern) then
   
             local cells = parse_row_cells(row)
    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
             if #cells >= 17 then
                 local prize_amount = "0 ₴"
                 local game_type = cells[2]
                local tournament = cells[4]
                local time = cells[5]
                local result = cells[16]
                local link = cells[17]
                  
                  
                 if prize_value and prize_value ~= "" and prize_value ~= "-" then
                 local position = nil
                    prize_value = mw.ustring.gsub(prize_value, "₴", "")
                local role = nil
                    prize_value = mw.text.trim(prize_value)
               
                    if prize_value ~= "0" then
                for j = 6, 15 do
                        prize_amount = prize_value .. " "
                    local cell_player = clean_wikilinks(cells[j])
                    if cell_player == player_name then
                        position = j - 5
                       
                        if j >= 6 and j <= 11 then
                            role = "Мирний"
                        elseif j == 12 then
                            role = "Шериф"
                        elseif j >= 13 and j <= 14 then
                            role = "Мафія"
                        elseif j == 15 then
                            role = "Дон"
                        end
                        break
                     end
                     end
                 end
                 end
                  
                  
                 local date = tournament_dates[header] or "01.01.2023"
                 if position and role then
                local tournament_link = get_tournament_link(header)
                    table.insert(player_games, {
               
                        type = game_type,
                table.insert(results, {
                        tournament = tournament,
                    date = date,
                        time = time,
                    medal = medal,
                        position = position,
                    tournament = tournament_link,
                        role = role,
                    prize = prize_amount
                        result = result,
                })
                        link = link
                    })
                end
             end
             end
         end
         end
     end
     end
      
      
     if #results == 0 then return "''Відсутні''" end
     if #player_games == 0 then
   
         return "''Записів ігор не знайдено''"
    local table_html = {
         '{| class="wikitable sortable" style="font-size: 14.5px;"',
        '|-',
        '! style="" | Дата',
        '! style="text-align: left;" | Місце і турнір',
        '! style="text-align: left;" | Приз'
    }
   
    for _, result in ipairs(results) do
        table.insert(table_html, '|-')
        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, '|}')
    local output = {}
     return table.concat(table_html, '\n')
     table.insert(output, '{| class="wikitable sortable" style="font-size: 14px;"')
end
     table.insert(output, '|-')
 
    table.insert(output, '! №')
-- ================================================
     table.insert(output, '! Тип')
-- FETCHDATA4 (цікаві факти)
     table.insert(output, '! Турнір')
-- ================================================
     table.insert(output, '! Час')
 
     table.insert(output, '! Позиція')
local pages_to_search = {
     table.insert(output, '! Роль')
     "Перший сезон", "Другий сезон", "Третій сезон", "Четвертий сезон",
     table.insert(output, '! Результат')
     "П'ятий сезон", "Шостий сезон", "Сьомий сезон", "Восьмий сезон",
     table.insert(output, '! Відео')
    "Дев'ятий сезон", "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 not section_start then return nil end
      
      
     local section_end = mw.ustring.find(content, "\n==[^=]", section_start + 1)
     for i, game in ipairs(player_games) do
   
        table.insert(output, '|-')
    local section_content
         table.insert(output, '| style="text-align:center;" | ' .. i)
    if section_end then
         table.insert(output, '| ' .. game.type)
         section_content = mw.ustring.sub(content, section_start, section_end - 1)
        table.insert(output, '| ' .. game.tournament)
    else
        table.insert(output, '| style="text-align:center;" | ' .. game.time)
         section_content = mw.ustring.sub(content, section_start)
        table.insert(output, '| style="text-align:center;" | ' .. game.position)
    end
       
   
        local role_color = ""
    return section_content
        if game.role == "Мирний" then
end
            role_color = "#90EE90"
 
        elseif game.role == "Шериф" then
local function fact_contains_player(fact, player_name)
            role_color = "#4169E1"
    local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
        elseif game.role == "Мафія" then
    local pattern1 = "%[%[" .. escaped_name .. "%]%]"
            role_color = "#DC143C"
    local pattern2 = "%[%[" .. escaped_name .. "|[^%]]+%]%]"
         elseif game.role == "Дон" then
   
            role_color = "#8B0000"
    return mw.ustring.find(fact, pattern1) or mw.ustring.find(fact, pattern2)
        end
end
       
 
        table.insert(output, '| style="background-color:' .. role_color .. '; text-align:center;" | ' .. game.role)
local function extract_facts(section_content)
    local facts = {}
    for fact in mw.ustring.gmatch(section_content, "%*[^\n]+") do
         table.insert(facts, fact)
    end
    return facts
end
 
function p.facts(frame)
    local player_name = frame.args.player or frame.args[1] or mw.title.getCurrentTitle().text
   
    local result_parts = {}
   
    for _, page_title in ipairs(pages_to_search) do
        local section = get_facts_section(page_title)
          
          
         if section then
         local result_style = ""
            local facts = extract_facts(section)
        if game.result == "Місто" or game.result == "Мирні" then
            local player_facts = {}
            result_style = 'style="color:#4caf50; font-weight:bold;"'
           
        elseif game.result == "Мафія" then
            for _, fact in ipairs(facts) do
            result_style = 'style="color:#f44336; font-weight:bold;"'
                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
       
        table.insert(output, '| ' .. result_style .. ' | ' .. game.result)
        table.insert(output, '| [' .. game.link .. ' YouTube]')
     end
     end
      
      
     if #result_parts == 0 then return "" end
     table.insert(output, '|}')
      
      
     return table.concat(result_parts, "\n")
     return table.concat(output, '\n')
end
end


-- ================================================
function p.games_count_records(frame)
-- FETCHDATA5 (записи ігор)
     local player_name = frame.args.player or frame.args[1]
-- ================================================
 
local function get_tournament_link_games(short_name, full_name)
     if short_name == "Фанова гра" then
        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
 
local function get_player_role(player_name, cells)
    for i = 5, 10 do
        if cells[i] and clean_wikilinks(cells[i]) == player_name then
            return "Мирний"
        end
    end
      
      
     if cells[11] and clean_wikilinks(cells[11]) == player_name then
     if not player_name or player_name == "" then
         return "Шериф"
         return "0"
     end
     end
      
      
     for i = 12, 13 do
     local page_content = get_page_content("Записи_ігор")
        if cells[i] and clean_wikilinks(cells[i]) == player_name then
    if not page_content then
            return "Мафія"
        return "0"
        end
     end
     end
      
      
     if cells[14] and clean_wikilinks(cells[14]) == player_name then
     local table_start = mw.ustring.find(page_content, "{|")
        return "Дон"
    local table_end = mw.ustring.find(page_content, "|}", table_start)
    end
      
      
    return "Невідомо"
     if not table_start or not table_end then
end
         return "0"
 
local function get_player_result(role, game_result)
     if not game_result or game_result == "" then return "Невідомо" end
   
    game_result = mw.text.trim(game_result)
   
    if game_result == "Місто" or game_result == "Мирні" then
         return (role == "Мирний" or role == "Шериф") and "Перемога" or "Поразка"
     end
     end
      
      
     if game_result == "Мафія" then
     local table_content = mw.ustring.sub(page_content, table_start, table_end + 1)
        return (role == "Мафія" or role == "Дон") and "Перемога" or "Поразка"
     local rows = parse_table_rows(table_content)
     end
      
      
     return "Невідомо"
     local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
end
     local player_pattern = "%[%[" .. escaped_name .. "%]%]"
 
local function get_player_games_list(player_name)
    local table_content = get_table_from_page("Ігри")
    if not table_content then return nil end
   
     local rows = parse_table_rows(table_content)
    local games = {}
      
      
    local count = 0
     for _, row in ipairs(rows) do
     for _, row in ipairs(rows) do
         local cells = parse_row_cells(row)
         if mw.ustring.find(row, player_pattern) then
       
             count = count + 1
        local player_in_game = false
        for i = 5, 14 do
            if cells[i] and clean_wikilinks(cells[i]) == player_name then
                player_in_game = true
                break
            end
        end
       
        if player_in_game and #cells >= 15 then
            local role = get_player_role(player_name, cells)
             local result = get_player_result(role, cells[15])
           
            table.insert(games, {
                tournament = cells[3] or "",
                short = cells[4] or "",
                role = role,
                result = result,
                link = cells[16] or ""
            })
         end
         end
     end
     end
      
      
     return games
     return tostring(count)
end
 
function p.player_games(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then
        return error_output("No Player Name", "")
    end
   
    local games = get_player_games_list(player_name)
    if not games then
        return error_output("Cannot Load Games", "Ігри")
    end
   
    if #games == 0 then
        return "''Ігор не знайдено''"
    end
   
    local htmlTable = mw.html.create('table')
        :addClass('wikitable sortable')
        :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('Запис')
   
    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
   
    return tostring(htmlTable)
end
 
function p.games_count_records(frame)
    local player_name = frame.args.player
    if not player_name or player_name == "" then return "0" end
   
    local games = get_player_games_list(player_name)
    return games and tostring(#games) or "0"
end
end


-- ================================================
-- ================================================
-- FETCHDATA6 (досягнення в сезонах)
-- ФУНКЦІЇ З FETCHDATA6 (season_achievements)
-- ================================================
-- ================================================


local function get_season_data(season_number, player_name)
function p.season_achievements(frame)
     local season_names = {
     local player_name = frame.args.player or frame.args[1]
        [1] = "Перший сезон", [2] = "Другий сезон", [3] = "Третій сезон",
        [4] = "Четвертий сезон", [5] = "П'ятий сезон", [6] = "Шостий сезон",
        [7] = "Сьомий сезон", [8] = "Восьмий сезон", [9] = "Дев'ятий сезон"
    }
      
      
    local season_name = season_names[season_number]
     if not player_name or player_name == "" then
     if not season_name then return nil end
         return error_output("Не вказано ім'я гравця")
   
    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 season_data = {}
     local table_end = mw.ustring.find(rating_section, "|}", table_start)
     local season_names = {
     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)
     for i, season_name in ipairs(season_names) do
   
         local content = get_page_content(season_name)
    local current_row = {}
    local all_rows = {}
   
    for line in mw.ustring.gmatch(table_content .. "\n", "([^\n]*)\n") do
         line = mw.text.trim(line)
          
          
         if line == "|-" then
         if content then
             if #current_row > 0 then
            local final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.-)\n==")
                 table.insert(all_rows, current_row)
             if not final_section then
                 final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.*)")
             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
   
    if #current_row > 0 then
        table.insert(all_rows, current_row)
    end
   
    for _, row in ipairs(all_rows) do
        if #row >= 5 then
            local player_cell = clean_wikilinks(row[2])
              
              
             if player_cell == player_name then
             if final_section then
                 return {
                 local table_start = mw.ustring.find(final_section, "{|")
                    place = mw.text.trim(row[1]),
                local table_end = mw.ustring.find(final_section, "|}", table_start)
                    points = mw.text.trim(row[3]),
               
                     games = mw.text.trim(row[4]),
                if table_start and table_end then
                     winrate = mw.text.trim(row[5])
                     local table_content = mw.ustring.sub(final_section, table_start, table_end + 1)
                 }
                    local player_data = find_player_data(table_content, player_name)
                      
                    if player_data and #player_data >= 1 then
                        table.insert(season_data, {
                            season = i,
                            place = player_data[1] or "—"
                        })
                    end
                 end
             end
             end
         end
         end
     end
     end
      
      
    return nil
     if #season_data == 0 then
end
         return "''Гравець не брав участі в жодному сезоні''"
 
function p.season_achievements(frame)
    local player_name = frame.args.player
     if not player_name or player_name == "" then
         return "<span style='color:red;'>Ім'я гравця не вказано</span>"
     end
     end
      
      
     local htmlTable = mw.html.create('table')
     local output = {}
        :addClass('wikitable sortable')
    table.insert(output, '{| class="wikitable" style="text-align:center; font-size: 14px;"')
        :css('width', '100%')
    table.insert(output, '|-')
        :css('font-size', '14.5px')
      
      
     local headerRow = htmlTable:tag('tr')
     for _, data in ipairs(season_data) do
    headerRow:tag('th'):wikitext('Сезон')
        table.insert(output, '! [[' .. season_names[data.season] .. '|' .. data.season .. ']]')
    headerRow:tag('th'):wikitext('Місце')
     end
     headerRow:tag('th'):wikitext('Бали')
    headerRow:tag('th'):wikitext('% перемог')
    headerRow:tag('th'):wikitext('Ігор')
      
      
     local season_links = {
     table.insert(output, '|-')
        [1] = "[[Перший сезон#Рейтинг|01 сезон]]",
        [2] = "[[Другий сезон#Рейтинг|02 сезон]]",
        [3] = "[[Третій сезон#Рейтинг|03 сезон]]",
        [4] = "[[Четвертий сезон#Рейтинг|04 сезон]]",
        [5] = "[[П'ятий сезон#Рейтинг|05 сезон]]",
        [6] = "[[Шостий сезон#Рейтинг|06 сезон]]",
        [7] = "[[Сьомий сезон#Рейтинг|07 сезон]]",
        [8] = "[[Восьмий сезон#Рейтинг|08 сезон]]",
        [9] = "[[Дев'ятий сезон#Рейтинг|09 сезон]]"
    }
      
      
     for season = 1, 9 do
     for _, data in ipairs(season_data) do
        local data = get_season_data(season, player_name)
         table.insert(output, '| ' .. data.place)
         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)
    table.insert(output, '|}')
   
     return table.concat(output, '\n')
end
end


return p
return p

Версія за 12:05, 2 грудня 2025

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

local p = {}

-- ================================================
-- ГЛОБАЛЬНЕ КЕШУВАННЯ
-- ================================================

local page_cache = {}
local table_cache = {}

-- ================================================
-- УТИЛІТИ
-- ================================================

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 error_output(message)
    return '<span style="color:red; font-weight:bold;">Помилка: ' .. message .. '</span>'
end

local function get_page_content(page_name)
    if page_cache[page_name] then
        return page_cache[page_name]
    end
    
    local title = mw.title.new(page_name)
    if not title or not title.exists then
        return nil
    end
    
    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 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)
    table_cache[page_name] = table_content
    return table_content
end

local function parse_table_rows(table_content)
    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
    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_data(table_content, player_name)
    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
        if mw.ustring.find(row, player_pattern) then
            return parse_row_cells(row)
        end
    end
    
    return nil
end

-- Конвертація часу (якщо потрібно)
local function format_time(time_str)
    if not time_str or time_str == "" then return "" end
    
    time_str = mw.text.trim(time_str)
    
    -- Якщо вже в правильному форматі
    if mw.ustring.find(time_str, "хв") then
        return time_str
    end
    
    -- Парсимо формат HH:MM:SS або MM:SS
    local hours, minutes, seconds = mw.ustring.match(time_str, "(%d+):(%d+):(%d+)")
    
    if not hours then
        minutes, seconds = mw.ustring.match(time_str, "(%d+):(%d+)")
        hours = "0"
    end
    
    if minutes and seconds then
        hours = tonumber(hours) or 0
        minutes = tonumber(minutes) or 0
        seconds = tonumber(seconds) or 0
        
        if hours > 0 then
            return string.format("%d год %02d хв %02d с", hours, minutes, seconds)
        else
            return string.format("%d хв %02d с", minutes, seconds)
        end
    end
    
    return time_str
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA
-- ================================================

function p.season_result(frame)
    local player_name = frame.args.player or frame.args[1]
    local season_number = frame.args.season or frame.args[2]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    if not season_number or season_number == "" then
        return error_output("Не вказано номер сезону")
    end
    
    local season_page = nil
    local season_names = {
        ["1"] = "Перший сезон",
        ["2"] = "Другий сезон",
        ["3"] = "Третій сезон",
        ["4"] = "Четвертий сезон",
        ["5"] = "П'ятий сезон",
        ["6"] = "Шостий сезон",
        ["7"] = "Сьомий сезон",
        ["8"] = "Восьмий сезон",
        ["9"] = "Дев'ятий сезон"
    }
    
    season_page = season_names[season_number]
    
    if not season_page then
        return error_output("Невірний номер сезону")
    end
    
    local content = get_page_content(season_page)
    if not content then
        return error_output("Не вдалося завантажити сторінку сезону")
    end
    
    local final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.-)\n==")
    if not final_section then
        final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.*)")
    end
    
    if not final_section then
        return error_output("Не знайдено секцію Фінал")
    end
    
    local table_start = mw.ustring.find(final_section, "{|")
    local table_end = mw.ustring.find(final_section, "|}", table_start)
    
    if not table_start or not table_end then
        return error_output("Не знайдено таблицю у секції Фінал")
    end
    
    local table_content = mw.ustring.sub(final_section, table_start, table_end + 1)
    local player_data = find_player_data(table_content, player_name)
    
    if not player_data or #player_data < 2 then
        return "—"
    end
    
    local place = player_data[1]
    return place or "—"
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA2
-- ================================================

function p.games_count(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "0"
    end
    
    return data[3] or "0"
end

function p.wins_count(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 4 then
        return "0"
    end
    
    return data[4] or "0"
end

function p.losses_count(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 5 then
        return "0"
    end
    
    return data[5] or "0"
end

function p.win_rate_colored(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 6 then
        return '<span style="color:indianred;">0%</span>'
    end
    
    local winrate = data[6]
    winrate = mw.ustring.gsub(winrate, "%%", "")
    winrate = mw.text.trim(winrate)
    
    local rate = tonumber(winrate) or 0
    local color = rate < 40 and "indianred" or "#4caf50"
    
    return '<span style="color:' .. color .. ';">' .. winrate .. '%</span>'
end

function p.recruiter(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Гравці")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 4 then
        return "Не вказано"
    end
    
    local recruiter = mw.text.trim(data[4])
    
    if recruiter == "Відсутній" or recruiter == "-" or recruiter == "" then
        return "Не вказано"
    end
    
    return recruiter
end

function p.date_added(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Гравці")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "Лише Бог знає"
    end
    
    local raw_date = data[3]
    
    if not raw_date or raw_date == "" or raw_date == "-" then
        return "Лише Бог знає"
    end
    
    local day, month, year = mw.ustring.match(raw_date, "(%d+)%.(%d+)%.(%d+)")
    
    if not day or not month or not year then
        return raw_date
    end
    
    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_date, days_diff)
end

function p.foundation(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Фундація")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "0"
    end
    
    local amount = mw.ustring.gsub(data[3], "[^%d]", "")
    return amount
end

function p.prize_pool(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Призовий_фонд")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 2 then
        return "0"
    end
    
    local amount = mw.ustring.gsub(data[2], "[^%d]", "")
    return amount
end

function p.finalist(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Фіналіст")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "0"
    end
    
    local count = mw.ustring.gsub(data[3], "[^%d]", "")
    return count
end

function p.foty_rating(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local content = get_page_content("Період")
    if not content then
        return "—"
    end
    
    local foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.-)\n==")
    if not foty_section then
        foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.*)")
    end
    
    if not foty_section then
        return "—"
    end
    
    local table_start = mw.ustring.find(foty_section, "{|")
    local table_end = mw.ustring.find(foty_section, "|}", table_start)
    
    if not table_start or not table_end then
        return "—"
    end
    
    local table_content = mw.ustring.sub(foty_section, table_start, table_end + 1)
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "—"
    end
    
    local place = mw.text.trim(data[1])
    local rating = mw.text.trim(data[3])
    
    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 rating .. " (" .. place .. " місце)"
    end
    
    return "—"
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA5 (player_games)
-- ================================================

function p.player_games(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local page_content = get_page_content("Записи_ігор")
    if not page_content then
        return error_output("Не вдалося завантажити сторінку Записи_ігор")
    end
    
    local table_start = mw.ustring.find(page_content, "{|")
    local table_end = mw.ustring.find(page_content, "|}", table_start)
    
    if not table_start or not table_end then
        return error_output("Не знайдено таблицю")
    end
    
    local table_content = mw.ustring.sub(page_content, table_start, table_end + 1)
    local rows = parse_table_rows(table_content)
    
    if #rows < 1 then
        return error_output("Таблиця порожня")
    end
    
    local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped_name .. "%]%]"
    
    local player_games = {}
    
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            local cells = parse_row_cells(row)
            
            if #cells >= 17 then
                local game_type = cells[2]
                local tournament = cells[4]
                local time = cells[5]
                local result = cells[16]
                local link = cells[17]
                
                local position = nil
                local role = nil
                
                for j = 6, 15 do
                    local cell_player = clean_wikilinks(cells[j])
                    if cell_player == player_name then
                        position = j - 5
                        
                        if j >= 6 and j <= 11 then
                            role = "Мирний"
                        elseif j == 12 then
                            role = "Шериф"
                        elseif j >= 13 and j <= 14 then
                            role = "Мафія"
                        elseif j == 15 then
                            role = "Дон"
                        end
                        break
                    end
                end
                
                if position and role then
                    table.insert(player_games, {
                        type = game_type,
                        tournament = tournament,
                        time = time,
                        position = position,
                        role = role,
                        result = result,
                        link = link
                    })
                end
            end
        end
    end
    
    if #player_games == 0 then
        return "''Записів ігор не знайдено''"
    end
    
    local output = {}
    table.insert(output, '{| class="wikitable sortable" style="font-size: 14px;"')
    table.insert(output, '|-')
    table.insert(output, '! №')
    table.insert(output, '! Тип')
    table.insert(output, '! Турнір')
    table.insert(output, '! Час')
    table.insert(output, '! Позиція')
    table.insert(output, '! Роль')
    table.insert(output, '! Результат')
    table.insert(output, '! Відео')
    
    for i, game in ipairs(player_games) do
        table.insert(output, '|-')
        table.insert(output, '| style="text-align:center;" | ' .. i)
        table.insert(output, '| ' .. game.type)
        table.insert(output, '| ' .. game.tournament)
        table.insert(output, '| style="text-align:center;" | ' .. game.time)
        table.insert(output, '| style="text-align:center;" | ' .. game.position)
        
        local role_color = ""
        if game.role == "Мирний" then
            role_color = "#90EE90"
        elseif game.role == "Шериф" then
            role_color = "#4169E1"
        elseif game.role == "Мафія" then
            role_color = "#DC143C"
        elseif game.role == "Дон" then
            role_color = "#8B0000"
        end
        
        table.insert(output, '| style="background-color:' .. role_color .. '; text-align:center;" | ' .. game.role)
        
        local result_style = ""
        if game.result == "Місто" or game.result == "Мирні" then
            result_style = 'style="color:#4caf50; font-weight:bold;"'
        elseif game.result == "Мафія" then
            result_style = 'style="color:#f44336; font-weight:bold;"'
        end
        
        table.insert(output, '| ' .. result_style .. ' | ' .. game.result)
        table.insert(output, '| [' .. game.link .. ' YouTube]')
    end
    
    table.insert(output, '|}')
    
    return table.concat(output, '\n')
end

function p.games_count_records(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return "0"
    end
    
    local page_content = get_page_content("Записи_ігор")
    if not page_content then
        return "0"
    end
    
    local table_start = mw.ustring.find(page_content, "{|")
    local table_end = mw.ustring.find(page_content, "|}", table_start)
    
    if not table_start or not table_end then
        return "0"
    end
    
    local table_content = mw.ustring.sub(page_content, table_start, table_end + 1)
    local rows = parse_table_rows(table_content)
    
    local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped_name .. "%]%]"
    
    local count = 0
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            count = count + 1
        end
    end
    
    return tostring(count)
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA6 (season_achievements)
-- ================================================

function p.season_achievements(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local season_data = {}
    local season_names = {
        "Перший сезон",
        "Другий сезон",
        "Третій сезон",
        "Четвертий сезон",
        "П'ятий сезон",
        "Шостий сезон",
        "Сьомий сезон",
        "Восьмий сезон",
        "Дев'ятий сезон"
    }
    
    for i, season_name in ipairs(season_names) do
        local content = get_page_content(season_name)
        
        if content then
            local final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.-)\n==")
            if not final_section then
                final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.*)")
            end
            
            if final_section then
                local table_start = mw.ustring.find(final_section, "{|")
                local table_end = mw.ustring.find(final_section, "|}", table_start)
                
                if table_start and table_end then
                    local table_content = mw.ustring.sub(final_section, table_start, table_end + 1)
                    local player_data = find_player_data(table_content, player_name)
                    
                    if player_data and #player_data >= 1 then
                        table.insert(season_data, {
                            season = i,
                            place = player_data[1] or "—"
                        })
                    end
                end
            end
        end
    end
    
    if #season_data == 0 then
        return "''Гравець не брав участі в жодному сезоні''"
    end
    
    local output = {}
    table.insert(output, '{| class="wikitable" style="text-align:center; font-size: 14px;"')
    table.insert(output, '|-')
    
    for _, data in ipairs(season_data) do
        table.insert(output, '! [[' .. season_names[data.season] .. '|' .. data.season .. ']]')
    end
    
    table.insert(output, '|-')
    
    for _, data in ipairs(season_data) do
        table.insert(output, '| ' .. data.place)
    end
    
    table.insert(output, '|}')
    
    return table.concat(output, '\n')
end

return p