Модуль:FetchData: відмінності між версіями
Admin (обговорення | внесок) Немає опису редагування |
Admin (обговорення | внесок) Немає опису редагування Мітка: Ручний відкіт |
||
| (Не показані 2 проміжні версії цього користувача) | |||
| Рядок 2: | Рядок 2: | ||
-- ================================================ | -- ================================================ | ||
-- КЕШУВАННЯ | -- КЕШУВАННЯ | ||
-- ================================================ | -- ================================================ | ||
| Рядок 12: | Рядок 12: | ||
return page_cache[page_name] | return page_cache[page_name] | ||
end | end | ||
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 | ||
| Рядок 18: | Рядок 18: | ||
return nil | return nil | ||
end | end | ||
local content = title:getContent() | local content = title:getContent() | ||
page_cache[page_name] = content | page_cache[page_name] = content | ||
| Рядок 28: | Рядок 28: | ||
return table_cache[page_name] | return table_cache[page_name] | ||
end | end | ||
local content = get_page_content(page_name) | local content = get_page_content(page_name) | ||
if not content then | if not content then | ||
table_cache[page_name] = nil | table_cache[page_name] = nil | ||
return nil | 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 | table_cache[page_name] = nil | ||
return nil | 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 | table_cache[page_name] = table_content | ||
| Рядок 49: | Рядок 49: | ||
-- ================================================ | -- ================================================ | ||
-- УТИЛІТИ | -- УТИЛІТИ | ||
-- ================================================ | -- ================================================ | ||
local function error_output(context, error_details) | local function error_output(context, error_details) | ||
local safe_details = error_details or "N/A" | local safe_details = error_details or "N/A" | ||
return string.format("<span style='color:red; font-weight:bold;'>[Error: %s. Details: %s]</span>", | return string.format("<span style='color:red; font-weight:bold;'>[Error: %s. Details: %s]</span>", | ||
context or "Unknown", safe_details) | context or "Unknown", safe_details) | ||
end | end | ||
-- ШВИДКА ВЕРСІЯ: патерни тільки з ASCII-символів, тому string.gsub безпечний | |||
local function clean_wikilinks(text) | local function clean_wikilinks(text) | ||
if not text then return nil end | if not text then return nil end | ||
text = | text = string.gsub(text, "%[%[([^%]|]+)|([^%]]+)%]%]", "%2") | ||
text = | text = string.gsub(text, "%[%[([^%]]+)%]%]", "%1") | ||
return mw.text.trim(text) | return mw.text.trim(text) | ||
end | end | ||
| Рядок 86: | Рядок 87: | ||
local function parse_row_cells(row) | local function parse_row_cells(row) | ||
local cells = {} | local cells = {} | ||
for line in mw.ustring.gmatch(row, "[^\n]+") do | for line in mw.ustring.gmatch(row, "[^\n]+") do | ||
line = mw.text.trim(line) | line = mw.text.trim(line) | ||
if line ~= "|-" and line ~= "" then | if line ~= "|-" and line ~= "" then | ||
line = mw.ustring.gsub(line, "^|%s*", "") | line = mw.ustring.gsub(line, "^|%s*", "") | ||
if line ~= "" then | if line ~= "" then | ||
table.insert(cells, line) | table.insert(cells, line) | ||
| Рядок 98: | Рядок 99: | ||
end | end | ||
end | end | ||
return cells | return cells | ||
end | end | ||
| Рядок 104: | Рядок 105: | ||
local function find_player_in_table(table_content, player_name, column_index) | local function find_player_in_table(table_content, player_name, column_index) | ||
if not table_content then return nil end | if not table_content then return nil end | ||
local escaped = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") | local escaped = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") | ||
local player_pattern = "%[%[" .. escaped .. "%]%]" | local player_pattern = "%[%[" .. escaped .. "%]%]" | ||
local rows = parse_table_rows(table_content) | 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 | ||
| Рядок 119: | Рядок 120: | ||
end | end | ||
end | end | ||
return nil | return nil | ||
end | end | ||
-- ================================================ | -- ================================================ | ||
-- | -- SEASON RESULT | ||
-- ================================================ | -- ================================================ | ||
| Рядок 130: | Рядок 131: | ||
local season = frame.args.season | local season = frame.args.season | ||
local player = frame.args.player | local player = frame.args.player | ||
local season_titles = { | local season_titles = { | ||
"Перший сезон", "Другий сезон", "Третій сезон", | "Перший сезон", "Другий сезон", "Третій сезон", | ||
"Четвертий сезон", "П'ятий сезон", "Шостий сезон", | "Четвертий сезон", "П'ятий сезон", "Шостий сезон", | ||
"Сьомий сезон", "Восьмий сезон", "Дев'ятий сезон" | "Сьомий сезон", "Восьмий сезон", "Дев'ятий сезон" | ||
} | } | ||
local season_title = season_titles[tonumber(season)] | local season_title = season_titles[tonumber(season)] | ||
if not season_title then return "??" end | if not season_title then return "??" end | ||
local content = get_page_content(season_title) | local content = get_page_content(season_title) | ||
if not content then return "??" end | if not content then return "??" end | ||
local rating_section = mw.ustring.match(content, "==%s*Рейтинг%s*==.-{|%s*class%s*=%s*\"wikitable sortable\"(.-)|}") | local rating_section = mw.ustring.match(content, "==%s*Рейтинг%s*==.-{|%s*class%s*=%s*\"wikitable sortable\"(.-)|}") | ||
if not rating_section then return "--" end | if not rating_section then return "--" end | ||
local pattern = "|%s*(%d+)%s*|%s*%[%[" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%]%]" | local pattern = "|%s*(%d+)%s*|%s*%[%[" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%]%]" | ||
local direct_pattern = "|%s*(%d+)%s*|%s*" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%s*" | local direct_pattern = "|%s*(%d+)%s*|%s*" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%s*" | ||
local rank = mw.ustring.match(rating_section, pattern) or mw.ustring.match(rating_section, direct_pattern) | local rank = mw.ustring.match(rating_section, pattern) or mw.ustring.match(rating_section, direct_pattern) | ||
return rank or "--" | return rank or "--" | ||
end | end | ||
-- ================================================ | -- ================================================ | ||
-- | -- БАЗОВА СТАТИСТИКА | ||
-- ================================================ | -- ================================================ | ||
| Рядок 161: | Рядок 162: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0" end | if not player_name or player_name == "" then return "0" end | ||
local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 2) | local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 2) | ||
return result or "0" | return result or "0" | ||
| Рядок 169: | Рядок 170: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0" end | if not player_name or player_name == "" then return "0" end | ||
local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 3) | local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 3) | ||
return result or "0" | return result or "0" | ||
| Рядок 177: | Рядок 178: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0" end | if not player_name or player_name == "" then return "0" end | ||
local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 4) | local result = find_player_in_table(get_table_from_page("Статистика"), player_name, 4) | ||
return result or "0" | return result or "0" | ||
| Рядок 185: | Рядок 186: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0%" end | 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) | local win_rate = find_player_in_table(get_table_from_page("Статистика"), player_name, 5) | ||
if not win_rate then return "0%" end | if not win_rate then return "0%" end | ||
local rate_num = tonumber(mw.ustring.match(win_rate, "([%d%.]+)")) | local rate_num = tonumber(mw.ustring.match(win_rate, "([%d%.]+)")) | ||
if not rate_num then return win_rate end | if not rate_num then return win_rate end | ||
local color = "#4caf50" | local color = "#4caf50" | ||
if rate_num < 40 then | if rate_num < 40 then | ||
| Рядок 198: | Рядок 199: | ||
color = "orange" | color = "orange" | ||
end | end | ||
if not mw.ustring.find(win_rate, "%%") then | if not mw.ustring.find(win_rate, "%%") then | ||
win_rate = win_rate .. "%" | win_rate = win_rate .. "%" | ||
end | end | ||
return string.format('<span style="color:%s;">%s</span>', color, win_rate) | return string.format('<span style="color:%s;">%s</span>', color, win_rate) | ||
end | end | ||
| Рядок 209: | Рядок 210: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "Не вказано" end | if not player_name or player_name == "" then return "Не вказано" 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 "Не вказано" end | ||
local cells = find_player_in_table(table_content, player_name) | local cells = find_player_in_table(table_content, player_name) | ||
if not cells or #cells < 2 then return "Не вказано" end | if not cells or #cells < 2 then return "Не вказано" end | ||
local raw = mw.text.trim(cells[2]) | local raw = mw.text.trim(cells[2]) | ||
if raw == "Відсутній" or raw == "-" or raw == "" then | if raw == "Відсутній" or raw == "-" or raw == "" then | ||
return "Не вказано" | return "Не вказано" | ||
end | end | ||
return raw | return raw | ||
end | end | ||
| Рядок 227: | Рядок 228: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "Лише Бог знає" end | if not player_name or player_name == "" then return "Лише Бог знає" end | ||
local raw = find_player_in_table(get_table_from_page("Гравці"), player_name, 3) | local raw = find_player_in_table(get_table_from_page("Гравці"), player_name, 3) | ||
if not raw or raw == "" or raw == "-" or raw == "Відсутній" then | if not raw or raw == "" or raw == "-" or raw == "Відсутній" then | ||
return "Лише Бог знає" | return "Лише Бог знає" | ||
end | end | ||
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}) | local end_date = os.time({year=2024, month=12, day=1}) | ||
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) | ||
return string.format("%s (%d днів)", raw, days_diff) | return string.format("%s (%d днів)", raw, days_diff) | ||
end | end | ||
return "Лише Бог знає" | return "Лише Бог знає" | ||
end | end | ||
| Рядок 250: | Рядок 251: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0₴" end | if not player_name or player_name == "" then return "0₴" end | ||
local raw = find_player_in_table(get_table_from_page("Фундація"), player_name, 2) | local raw = find_player_in_table(get_table_from_page("Фундація"), player_name, 2) | ||
if not raw or raw == "" or raw == "-" then return "0₴" end | if not raw or raw == "" or raw == "-" then return "0₴" end | ||
raw = mw.ustring.gsub(raw, "[^%d%s]", "") | raw = mw.ustring.gsub(raw, "[^%d%s]", "") | ||
raw = mw.text.trim(raw) | raw = mw.text.trim(raw) | ||
return (raw or "0") .. " ₴" | return (raw or "0") .. " ₴" | ||
end | end | ||
| Рядок 264: | Рядок 265: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0₴" end | if not player_name or player_name == "" then return "0₴" end | ||
local raw = find_player_in_table(get_table_from_page("Призовий_фонд"), player_name, 2) | local raw = find_player_in_table(get_table_from_page("Призовий_фонд"), player_name, 2) | ||
if not raw or raw == "" or raw == "-" then return "0₴" end | if not raw or raw == "" or raw == "-" then return "0₴" end | ||
raw = mw.ustring.gsub(raw, "[^%d%s]", "") | raw = mw.ustring.gsub(raw, "[^%d%s]", "") | ||
raw = mw.text.trim(raw) | raw = mw.text.trim(raw) | ||
return (raw or "0") .. " ₴" | return (raw or "0") .. " ₴" | ||
end | end | ||
| Рядок 278: | Рядок 279: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0/9" end | 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) | 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 | if not raw or raw == "" or raw == "-" then return "0/9" end | ||
local number_only = mw.ustring.gsub(raw, "[^%d]", "") | local number_only = mw.ustring.gsub(raw, "[^%d]", "") | ||
local count = tonumber(number_only) | local count = tonumber(number_only) | ||
if count then | if count then | ||
return string.format("%d/9", count) | return string.format("%d/9", count) | ||
end | end | ||
return "0/9" | return "0/9" | ||
end | end | ||
| Рядок 296: | Рядок 297: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "—" end | if not player_name or player_name == "" then return "—" 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==") | ||
if not foty_section then | if not foty_section then | ||
foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.*)") | foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.*)") | ||
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 cells = find_player_in_table(table_content, player_name) | ||
if not cells or #cells < 3 then return "—" end | if not cells or #cells < 3 then return "—" end | ||
local place = strip_html_tags(cells[1]) or "—" | local place = strip_html_tags(cells[1]) or "—" | ||
local rating = strip_html_tags(cells[3]) or "—" | local rating = strip_html_tags(cells[3]) or "—" | ||
rating = mw.ustring.gsub(rating, "[^%d%-]", "") | rating = mw.ustring.gsub(rating, "[^%d%-]", "") | ||
rating = mw.text.trim(rating) | rating = mw.text.trim(rating) | ||
if rating and rating ~= "" then | if rating and rating ~= "" then | ||
return rating .. " (" .. place .. " місце)" | return rating .. " (" .. place .. " місце)" | ||
end | end | ||
return "—" | return "—" | ||
end | end | ||
-- ================================================ | -- ================================================ | ||
-- | -- ТИТУЛИ ТА НАГОРОДИ | ||
-- ================================================ | -- ================================================ | ||
| Рядок 379: | Рядок 380: | ||
if not value or value == "" or value == " " then return nil end | if not value or value == "" or value == " " then return nil end | ||
value = mw.text.trim(value) | value = mw.text.trim(value) | ||
if value == "" then return nil | if value == "" then return nil | ||
elseif value == "1" then return "[[Файл:Gold.png|20px|link=]]" | elseif value == "1" then return "[[Файл:Gold.png|20px|link=]]" | ||
| Рядок 400: | Рядок 401: | ||
season_num = mw.ustring.match(header_name, "([А-Яа-яЁёІіЇїЄєҐґ']+) сезон %(рейтинг%)") | season_num = mw.ustring.match(header_name, "([А-Яа-яЁёІіЇїЄєҐґ']+) сезон %(рейтинг%)") | ||
end | end | ||
if season_num then | if season_num then | ||
local season_map = { | local season_map = { | ||
| Рядок 407: | Рядок 408: | ||
["Сьомий"] = 7, ["Восьмий"] = 8, ["Дев'ятий"] = 9 | ["Сьомий"] = 7, ["Восьмий"] = 8, ["Дев'ятий"] = 9 | ||
} | } | ||
local season_number = season_map[season_num] | local season_number = season_map[season_num] | ||
if season_number then | if season_number then | ||
| Рядок 415: | Рядок 416: | ||
clean_name = mw.ustring.gsub(clean_name, "%s*%(рейтинг%)%s*", "") | clean_name = mw.ustring.gsub(clean_name, "%s*%(рейтинг%)%s*", "") | ||
clean_name = mw.text.trim(clean_name) | clean_name = mw.text.trim(clean_name) | ||
return string.format("[[%s|%s сезон. %s]]", clean_name, roman, type_text) | return string.format("[[%s|%s сезон. %s]]", clean_name, roman, type_text) | ||
end | end | ||
end | end | ||
return string.format("[[%s]]", header_name) | return string.format("[[%s]]", header_name) | ||
end | end | ||
| Рядок 426: | Рядок 427: | ||
local table_content = get_table_from_page(page_title) | local table_content = get_table_from_page(page_title) | ||
if not table_content then return nil end | if not table_content then return nil end | ||
local first_row = mw.ustring.match(table_content, "{|[^\n]*\n!(.-)[\n]") | local first_row = mw.ustring.match(table_content, "{|[^\n]*\n!(.-)[\n]") | ||
local headers = {} | local headers = {} | ||
if first_row then | if first_row then | ||
for header in mw.ustring.gmatch(first_row, "([^|]+)") do | for header in mw.ustring.gmatch(first_row, "([^|]+)") do | ||
| Рядок 438: | Рядок 439: | ||
end | end | ||
end | end | ||
if #headers == 0 then return nil end | if #headers == 0 then return nil end | ||
local cells = find_player_in_table(table_content, player_name) | local cells = find_player_in_table(table_content, player_name) | ||
if not cells then return nil end | if not cells then return nil end | ||
return {headers = headers, cells = cells} | return {headers = headers, cells = cells} | ||
end | end | ||
function p.player_image(frame) | function p.player_image(frame) | ||
| Рядок 455: | Рядок 452: | ||
local player_file = "Player_" .. nickname .. ".png" | local player_file = "Player_" .. nickname .. ".png" | ||
local placeholder = "MCC_Placeholder.png" | local placeholder = "MCC_Placeholder.png" | ||
local title = mw.title.new("File:" .. player_file) | local title = mw.title.new("File:" .. player_file) | ||
local file_to_use | local file_to_use | ||
if title and title.exists then | if title and title.exists then | ||
| Рядок 464: | Рядок 461: | ||
file_to_use = placeholder | file_to_use = placeholder | ||
end | end | ||
return string.format("[[File:%s|none|alt=Фотографія гравця|center|360px]]", file_to_use) | return string.format("[[File:%s|none|alt=Фотографія гравця|center|360px]]", file_to_use) | ||
end | end | ||
| Рядок 471: | Рядок 468: | ||
local name = frame.args.player | local name = frame.args.player | ||
if not name or name == "" then return "''Відсутні''" end | if not name or name == "" then return "''Відсутні''" end | ||
local titles_data = get_player_row("Титули", name) | local titles_data = get_player_row("Титули", name) | ||
if not titles_data then return "''Відсутні''" end | if not titles_data then return "''Відсутні''" end | ||
local prize_data = get_player_row("Призові", name) | local prize_data = get_player_row("Призові", name) | ||
if not prize_data then return error_output("Prize Data Missing", name) end | if not prize_data then return error_output("Prize Data Missing", name) end | ||
local results = {} | local results = {} | ||
for i = 2, #titles_data.cells do | for i = 2, #titles_data.cells do | ||
local cell_value = titles_data.cells[i] | local cell_value = titles_data.cells[i] | ||
local header = titles_data.headers[i] | local header = titles_data.headers[i] | ||
local prize_value = prize_data.cells[i] | local prize_value = prize_data.cells[i] | ||
if cell_value and cell_value ~= "" and cell_value ~= "-" and header then | if cell_value and cell_value ~= "" and cell_value ~= "-" and header then | ||
local medal = get_medal_icon(cell_value) | local medal = get_medal_icon(cell_value) | ||
if medal then | if medal then | ||
local prize_amount = "0 ₴" | local prize_amount = "0 ₴" | ||
if prize_value and prize_value ~= "" and prize_value ~= "-" then | if prize_value and prize_value ~= "" and prize_value ~= "-" then | ||
prize_value = mw.ustring.gsub(prize_value, "₴", "") | prize_value = mw.ustring.gsub(prize_value, "₴", "") | ||
| Рядок 498: | Рядок 495: | ||
end | end | ||
end | end | ||
local date = tournament_dates[header] or "01.01.2023" | local date = tournament_dates[header] or "01.01.2023" | ||
local tournament_link = get_tournament_link(header) | local tournament_link = get_tournament_link(header) | ||
table.insert(results, { | table.insert(results, { | ||
date = date, | date = date, | ||
| Рядок 511: | Рядок 508: | ||
end | end | ||
end | end | ||
if #results == 0 then return "''Відсутні''" end | if #results == 0 then return "''Відсутні''" end | ||
local table_html = { | local table_html = { | ||
'{| class="wikitable sortable" style="font-size: 14.5px;"', | '{| class="wikitable sortable" style="font-size: 14.5px;"', | ||
| Рядок 521: | Рядок 518: | ||
'! style="text-align: left;" | Приз' | '! style="text-align: left;" | Приз' | ||
} | } | ||
for _, result in ipairs(results) do | for _, result in ipairs(results) do | ||
table.insert(table_html, '|-') | table.insert(table_html, '|-') | ||
| Рядок 529: | Рядок 526: | ||
)) | )) | ||
end | end | ||
table.insert(table_html, '|}') | table.insert(table_html, '|}') | ||
return table.concat(table_html, '\n') | return table.concat(table_html, '\n') | ||
| Рядок 535: | Рядок 532: | ||
-- ================================================ | -- ================================================ | ||
-- | -- ЦІКАВІ ФАКТИ | ||
-- ================================================ | -- ================================================ | ||
| Рядок 554: | Рядок 551: | ||
local content = get_page_content(page_title) | local content = get_page_content(page_title) | ||
if not content then return nil end | if not content then return nil end | ||
local section_start = mw.ustring.find(content, "==%s*[Цц]ікаві%s*[Фф]акти%s*==") | local section_start = mw.ustring.find(content, "==%s*[Цц]ікаві%s*[Фф]акти%s*==") | ||
if not section_start then return nil end | if not section_start then return nil end | ||
local section_end = mw.ustring.find(content, "\n==[^=]", section_start + 1) | local section_end = mw.ustring.find(content, "\n==[^=]", section_start + 1) | ||
local section_content | local section_content | ||
if section_end then | if section_end then | ||
| Рядок 566: | Рядок 563: | ||
section_content = mw.ustring.sub(content, section_start) | section_content = mw.ustring.sub(content, section_start) | ||
end | end | ||
return section_content | return section_content | ||
end | end | ||
| Рядок 574: | Рядок 571: | ||
local pattern1 = "%[%[" .. escaped_name .. "%]%]" | local pattern1 = "%[%[" .. escaped_name .. "%]%]" | ||
local pattern2 = "%[%[" .. escaped_name .. "|[^%]]+%]%]" | local pattern2 = "%[%[" .. escaped_name .. "|[^%]]+%]%]" | ||
return mw.ustring.find(fact, pattern1) or mw.ustring.find(fact, pattern2) | return mw.ustring.find(fact, pattern1) or mw.ustring.find(fact, pattern2) | ||
end | end | ||
| Рядок 588: | Рядок 585: | ||
function p.facts(frame) | function p.facts(frame) | ||
local player_name = frame.args.player or frame.args[1] or mw.title.getCurrentTitle().text | local player_name = frame.args.player or frame.args[1] or mw.title.getCurrentTitle().text | ||
local result_parts = {} | local result_parts = {} | ||
for _, page_title in ipairs(pages_to_search) do | for _, page_title in ipairs(pages_to_search) do | ||
local section = get_facts_section(page_title) | local section = get_facts_section(page_title) | ||
if section then | if section then | ||
local facts = extract_facts(section) | local facts = extract_facts(section) | ||
local player_facts = {} | local player_facts = {} | ||
for _, fact in ipairs(facts) do | for _, fact in ipairs(facts) do | ||
if fact_contains_player(fact, player_name) then | if fact_contains_player(fact, player_name) then | ||
| Рядок 603: | Рядок 600: | ||
end | end | ||
end | end | ||
if #player_facts > 0 then | if #player_facts > 0 then | ||
local block = string.format("==== %s ====\n", page_title) | local block = string.format("==== %s ====\n", page_title) | ||
| Рядок 613: | Рядок 610: | ||
end | end | ||
end | end | ||
if #result_parts == 0 then return "" end | if #result_parts == 0 then return "" end | ||
return table.concat(result_parts, "\n") | return table.concat(result_parts, "\n") | ||
end | end | ||
-- ================================================ | -- ================================================ | ||
-- | -- ЗАПИСИ ІГОР | ||
-- ================================================ | -- ================================================ | ||
| Рядок 627: | Рядок 624: | ||
return "Невідомо" | return "Невідомо" | ||
end | end | ||
if short_name == "Фанова гра" then | if short_name == "Фанова гра" then | ||
return "Фанова гра" | return "Фанова гра" | ||
end | end | ||
if full_name then | if full_name then | ||
full_name = clean_wikilinks(full_name) | full_name = clean_wikilinks(full_name) | ||
end | end | ||
if full_name and full_name ~= "" and full_name ~= short_name then | if full_name and full_name ~= "" and full_name ~= short_name then | ||
return "[[" .. full_name .. "|" .. short_name .. "]]" | return "[[" .. full_name .. "|" .. short_name .. "]]" | ||
| Рядок 643: | Рядок 640: | ||
end | end | ||
local function | local function parse_games_row_cells(row) | ||
local cells = {} | |||
row = string.gsub(row, "^%s*|%s*", "") | |||
for cell in string.gmatch(row .. "||", "(.-)%s*||%s*") do | |||
table.insert(cells, mw.text.trim(cell)) | |||
end | |||
return cells | |||
end | |||
-- Кеш: таблиця розбирається ОДИН раз на сторінку | |||
local games_rows_cache = nil | |||
local player_games_cache = {} | |||
local function get_games_rows() | |||
if games_rows_cache then return games_rows_cache end | |||
local table_content = get_table_from_page("Ігри") | |||
if not table_content then return nil end | |||
local rows = {} | local rows = {} | ||
for row in string.gmatch(table_content, "|-\n([^\n]+)") do | |||
for row in | if not string.match(row, "^%s*!") then | ||
if not | local cells = parse_games_row_cells(row) | ||
table.insert(rows, | if #cells >= 17 then | ||
local names = {} | |||
for i = 6, 15 do | |||
names[i] = clean_wikilinks(cells[i]) | |||
end | |||
table.insert(rows, { cells = cells, names = names }) | |||
end | |||
end | end | ||
end | end | ||
games_rows_cache = rows | |||
return rows | return rows | ||
end | end | ||
| Рядок 671: | Рядок 680: | ||
local res = cells[16] | local res = cells[16] | ||
if not res or mw.text.trim(res) == "" then return true end | if not res or mw.text.trim(res) == "" then return true end | ||
return | return string.find(res, "?", 1, true) ~= nil | ||
end | end | ||
local function get_player_role(player_name, | local function get_player_role(player_name, entry) | ||
if has_unknown_roles(cells) then return "—" end | if has_unknown_roles(entry.cells) then return "—" end | ||
local names = entry.names | |||
for i = 6, 11 do | for i = 6, 11 do | ||
if | if names[i] == player_name then return "Мир" end | ||
end | end | ||
if names[12] == player_name then return "Шер" end | |||
if | |||
for i = 13, 14 do | for i = 13, 14 do | ||
if | if names[i] == player_name then return "Маф" end | ||
end | end | ||
if names[15] == player_name then return "Дон" end | |||
if | |||
return "—" | return "—" | ||
end | end | ||
| Рядок 708: | Рядок 704: | ||
game_result = mw.text.trim(game_result) | game_result = mw.text.trim(game_result) | ||
if string.find(game_result, "?", 1, true) then return "—" end | |||
if | |||
if game_result == "Місто" or game_result == "Мирні" then | if game_result == "Місто" or game_result == "Мирні" then | ||
| Рядок 723: | Рядок 718: | ||
local function get_player_games_list(player_name) | local function get_player_games_list(player_name) | ||
local | if player_games_cache[player_name] then | ||
if not | return player_games_cache[player_name] | ||
end | |||
local rows = get_games_rows() | |||
if not rows then return nil end | |||
local games = {} | local games = {} | ||
for _, | for _, entry in ipairs(rows) do | ||
local | local found = false | ||
for i = 6, 15 do | |||
if entry.names[i] == player_name then | |||
found = true | |||
break | |||
end | end | ||
end | |||
if found then | |||
local cells = entry.cells | |||
local role = get_player_role(player_name, entry) | |||
local result = get_player_result(role, cells[16]) | |||
local time_raw = cells[5] or "" | |||
local time_formatted = time_raw | |||
local minutes, seconds = string.match(time_raw, "(%d+)%s*хв%s*(%d+)%s*с") | |||
if minutes and seconds then | |||
time_formatted = minutes .. ":" .. string.format("%02d", tonumber(seconds)) | |||
end | end | ||
table.insert(games, { | |||
short = cells[3] or "", | |||
tournament = cells[4] or "", | |||
time = time_formatted, | |||
role = role, | |||
result = result, | |||
link = cells[17] or "" | |||
}) | |||
end | end | ||
end | end | ||
player_games_cache[player_name] = games | |||
return games | return games | ||
end | end | ||
| Рядок 773: | Рядок 768: | ||
return error_output("No Player Name", "") | return error_output("No Player Name", "") | ||
end | end | ||
local games = get_player_games_list(player_name) | local games = get_player_games_list(player_name) | ||
if not games then | if not games then | ||
return error_output("Cannot Load Games", "Ігри") | return error_output("Cannot Load Games", "Ігри") | ||
end | end | ||
if #games == 0 then | if #games == 0 then | ||
return "''Ігор не знайдено''" | return "''Ігор не знайдено''" | ||
end | end | ||
local htmlTable = mw.html.create('table') | local htmlTable = mw.html.create('table') | ||
:addClass('wikitable sortable') | :addClass('wikitable sortable') | ||
:css('width', '100%') | :css('width', '100%') | ||
:css('font-size', '14.5px') | :css('font-size', '14.5px') | ||
local headerRow = htmlTable:tag('tr') | local headerRow = htmlTable:tag('tr') | ||
headerRow:tag('th'):wikitext('Подія') | headerRow:tag('th'):wikitext('Подія') | ||
| Рядок 795: | Рядок 790: | ||
headerRow:tag('th'):wikitext('Запис') | headerRow:tag('th'):wikitext('Запис') | ||
for _, game in ipairs(games) do | for _, game in ipairs(games) do | ||
local row = htmlTable:tag('tr') | local row = htmlTable:tag('tr') | ||
local unknown = (game.result == "—") | |||
-- Турнір | -- Турнір | ||
row:tag('td') | row:tag('td') | ||
| Рядок 806: | Рядок 799: | ||
:css('padding', '8px') | :css('padding', '8px') | ||
:wikitext(get_tournament_link_games(game.short, game.tournament)) | :wikitext(get_tournament_link_games(game.short, game.tournament)) | ||
-- Роль | -- Роль | ||
row:tag('td') | |||
:css('text-align', 'center') | :css('text-align', 'center') | ||
:css('padding', '8px') | :css('padding', '8px') | ||
:tag('span') | |||
:css('color', unknown and '#777' or '#fff') | |||
:wikitext(game.role) | |||
-- Час | -- Час | ||
| Рядок 837: | Рядок 828: | ||
:wikitext('П') | :wikitext('П') | ||
else | else | ||
resultCell:tag('span'):css('color', '#777'):wikitext('—') | resultCell:tag('span') | ||
:css('color', '#777') | |||
:wikitext('—') | |||
end | end | ||
-- | -- Запис | ||
local linkCell = row:tag('td') | local linkCell = row:tag('td') | ||
:css('text-align', 'center') | :css('text-align', 'center') | ||
:css('padding', '8px') | :css('padding', '8px') | ||
if game.link and game.link ~= "" then | if game.link and game.link ~= "" then | ||
linkCell:wikitext('<span class="game-record-btn">[' .. game.link .. ' ▶︎]</span>') | linkCell:wikitext('<span class="game-record-btn">[' .. game.link .. ' ▶︎]</span>') | ||
end | end | ||
end | end | ||
return tostring(htmlTable) | return tostring(htmlTable) | ||
end | end | ||
| Рядок 856: | Рядок 849: | ||
local player_name = frame.args.player | local player_name = frame.args.player | ||
if not player_name or player_name == "" then return "0" end | if not player_name or player_name == "" then return "0" end | ||
local games = get_player_games_list(player_name) | local games = get_player_games_list(player_name) | ||
return games and tostring(#games) or "0" | return games and tostring(#games) or "0" | ||
| Рядок 862: | Рядок 855: | ||
-- ================================================ | -- ================================================ | ||
-- | -- ДОСЯГНЕННЯ В СЕЗОНАХ | ||
-- ================================================ | -- ================================================ | ||
| Рядок 871: | Рядок 864: | ||
[7] = "Сьомий сезон", [8] = "Восьмий сезон", [9] = "Дев'ятий сезон" | [7] = "Сьомий сезон", [8] = "Восьмий сезон", [9] = "Дев'ятий сезон" | ||
} | } | ||
local season_name = season_names[season_number] | local season_name = season_names[season_number] | ||
if not season_name then return nil end | if not season_name then return nil end | ||
local content = get_page_content(season_name) | local content = get_page_content(season_name) | ||
if not content then return nil end | if not content then return nil end | ||
local rating_section = mw.ustring.match(content, "==+%s*Рейтинг%s*==+(.-)\n==") | local rating_section = mw.ustring.match(content, "==+%s*Рейтинг%s*==+(.-)\n==") | ||
if not rating_section then | if not rating_section then | ||
| Рядок 883: | Рядок 876: | ||
end | end | ||
if not rating_section then return nil end | if not rating_section then return nil end | ||
local table_start = mw.ustring.find(rating_section, "{|") | local table_start = mw.ustring.find(rating_section, "{|") | ||
local table_end = mw.ustring.find(rating_section, "|}", table_start) | local table_end = mw.ustring.find(rating_section, "|}", table_start) | ||
if not table_start or not table_end then return nil end | 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 table_content = mw.ustring.sub(rating_section, table_start, table_end + 1) | ||
local current_row = {} | local current_row = {} | ||
local all_rows = {} | local all_rows = {} | ||
for line in mw.ustring.gmatch(table_content .. "\n", "([^\n]*)\n") do | for line in mw.ustring.gmatch(table_content .. "\n", "([^\n]*)\n") do | ||
line = mw.text.trim(line) | line = mw.text.trim(line) | ||
if line == "|-" then | if line == "|-" then | ||
if #current_row > 0 then | if #current_row > 0 then | ||
| Рядок 906: | Рядок 899: | ||
end | end | ||
end | end | ||
if #current_row > 0 then | if #current_row > 0 then | ||
table.insert(all_rows, current_row) | table.insert(all_rows, current_row) | ||
end | end | ||
for _, row in ipairs(all_rows) do | for _, row in ipairs(all_rows) do | ||
if #row >= 5 then | if #row >= 5 then | ||
local player_cell = clean_wikilinks(row[2]) | local player_cell = clean_wikilinks(row[2]) | ||
if player_cell == player_name then | if player_cell == player_name then | ||
return { | return { | ||
| Рядок 925: | Рядок 918: | ||
end | end | ||
end | end | ||
return nil | return nil | ||
end | end | ||
| Рядок 934: | Рядок 927: | ||
return "<span style='color:red;'>Ім'я гравця не вказано</span>" | return "<span style='color:red;'>Ім'я гравця не вказано</span>" | ||
end | end | ||
local htmlTable = mw.html.create('table') | local htmlTable = mw.html.create('table') | ||
:addClass('wikitable sortable') | :addClass('wikitable sortable') | ||
:css('width', '100%') | :css('width', '100%') | ||
:css('font-size', '14.5px') | :css('font-size', '14.5px') | ||
local headerRow = htmlTable:tag('tr') | local headerRow = htmlTable:tag('tr') | ||
headerRow:tag('th'):wikitext('Сезон') | headerRow:tag('th'):wikitext('Сезон') | ||
| Рядок 946: | Рядок 939: | ||
headerRow:tag('th'):wikitext('% перемог') | headerRow:tag('th'):wikitext('% перемог') | ||
headerRow:tag('th'):wikitext('Ігор') | headerRow:tag('th'):wikitext('Ігор') | ||
local season_links = { | local season_links = { | ||
[1] = "[[Перший сезон#Рейтинг|01 сезон]]", | [1] = "[[Перший сезон#Рейтинг|01 сезон]]", | ||
| Рядок 958: | Рядок 951: | ||
[9] = "[[Дев'ятий сезон#Рейтинг|09 сезон]]" | [9] = "[[Дев'ятий сезон#Рейтинг|09 сезон]]" | ||
} | } | ||
for season = 1, 9 do | for season = 1, 9 do | ||
local data = get_season_data(season, player_name) | local data = get_season_data(season, player_name) | ||
local row = htmlTable:tag('tr') | local row = htmlTable:tag('tr') | ||
row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(season_links[season]) | row:tag('td'):css('text-align', 'center'):css('padding', '8px'):wikitext(season_links[season]) | ||
if data then | 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.place) | ||
| Рядок 976: | Рядок 969: | ||
end | end | ||
end | end | ||
return tostring(htmlTable) | return tostring(htmlTable) | ||
end | end | ||
return p | return p | ||
Поточна версія на 17:08, 9 серпня 2026
Документацію для цього модуля можна створити у Модуль:FetchData/документація
local p = {}
-- ================================================
-- КЕШУВАННЯ
-- ================================================
local page_cache = {}
local table_cache = {}
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
page_cache[page_name] = nil
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
table_cache[page_name] = nil
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
table_cache[page_name] = nil
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 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
-- ШВИДКА ВЕРСІЯ: патерни тільки з ASCII-символів, тому string.gsub безпечний
local function clean_wikilinks(text)
if not text then return nil end
text = string.gsub(text, "%[%[([^%]|]+)|([^%]]+)%]%]", "%2")
text = string.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 = {}
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 = {}
for line in mw.ustring.gmatch(row, "[^\n]+") do
line = mw.text.trim(line)
if line ~= "|-" and line ~= "" then
line = mw.ustring.gsub(line, "^|%s*", "")
if line ~= "" then
table.insert(cells, line)
end
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
if mw.ustring.find(row, player_pattern) then
local cells = parse_row_cells(row)
if column_index and column_index <= #cells then
return cells[column_index]
end
return cells
end
end
return nil
end
-- ================================================
-- SEASON RESULT
-- ================================================
function p.season_result(frame)
local season = frame.args.season
local player = frame.args.player
local season_titles = {
"Перший сезон", "Другий сезон", "Третій сезон",
"Четвертий сезон", "П'ятий сезон", "Шостий сезон",
"Сьомий сезон", "Восьмий сезон", "Дев'ятий сезон"
}
local season_title = season_titles[tonumber(season)]
if not season_title then return "??" end
local content = get_page_content(season_title)
if not content then return "??" end
local rating_section = mw.ustring.match(content, "==%s*Рейтинг%s*==.-{|%s*class%s*=%s*\"wikitable sortable\"(.-)|}")
if not rating_section then return "--" end
local pattern = "|%s*(%d+)%s*|%s*%[%[" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%]%]"
local direct_pattern = "|%s*(%d+)%s*|%s*" .. mw.ustring.gsub(player, "([%(%)%.%-%+%[%]])", "%%%1") .. "%s*"
local rank = mw.ustring.match(rating_section, pattern) or mw.ustring.match(rating_section, direct_pattern)
return rank or "--"
end
-- ================================================
-- БАЗОВА СТАТИСТИКА
-- ================================================
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
if not mw.ustring.find(win_rate, "%%") then
win_rate = win_rate .. "%"
end
return string.format('<span style="color:%s;">%s</span>', color, win_rate)
end
function p.recruiter(frame)
local player_name = frame.args.player
if not player_name or player_name == "" then return "Не вказано" end
local table_content = get_table_from_page("Гравці")
if not table_content then return "Не вказано" end
local cells = find_player_in_table(table_content, player_name)
if not cells or #cells < 2 then return "Не вказано" end
local raw = mw.text.trim(cells[2])
if raw == "Відсутній" or raw == "-" or raw == "" then
return "Не вказано"
end
return raw
end
function p.date_added(frame)
local player_name = frame.args.player
if not player_name or player_name == "" then return "Лише Бог знає" end
local raw = find_player_in_table(get_table_from_page("Гравці"), player_name, 3)
if not raw or raw == "" or raw == "-" or raw == "Відсутній" then
return "Лише Бог знає"
end
local day, month, year = mw.ustring.match(raw, "(%d+)%.(%d+)%.(%d+)")
if day and month and year then
local end_date = os.time({year=2024, month=12, day=1})
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.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
function p.prize_pool(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
function p.finalist(frame)
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)
local player_name = frame.args.player
if not player_name or player_name == "" then return "—" 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 cells = find_player_in_table(table_content, player_name)
if not cells or #cells < 3 then return "—" end
local place = strip_html_tags(cells[1]) or "—"
local rating = strip_html_tags(cells[3]) or "—"
rating = mw.ustring.gsub(rating, "[^%d%-]", "")
rating = mw.text.trim(rating)
if rating and rating ~= "" then
return rating .. " (" .. place .. " місце)"
end
return "—"
end
-- ================================================
-- ТИТУЛИ ТА НАГОРОДИ
-- ================================================
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
if season_num then
local season_map = {
["Перший"] = 1, ["Другий"] = 2, ["Третій"] = 3,
["Четвертий"] = 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
return string.format("[[%s]]", header_name)
end
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
for header in mw.ustring.gmatch(first_row, "([^|]+)") do
local trimmed = mw.text.trim(header)
if trimmed ~= "" then
table.insert(headers, trimmed)
end
end
end
if #headers == 0 then return nil end
local cells = find_player_in_table(table_content, player_name)
if not cells then return nil end
return {headers = headers, cells = cells}
end
function p.player_image(frame)
local nickname = frame.args.nickname or frame.args[1] or mw.title.getCurrentTitle().text
local player_file = "Player_" .. nickname .. ".png"
local placeholder = "MCC_Placeholder.png"
local title = mw.title.new("File:" .. player_file)
local file_to_use
if title and title.exists then
file_to_use = player_file
else
file_to_use = placeholder
end
return string.format("[[File:%s|none|alt=Фотографія гравця|center|360px]]", file_to_use)
end
function p.titles(frame)
local name = frame.args.player
if not name or name == "" then return "''Відсутні''" end
local titles_data = get_player_row("Титули", name)
if not titles_data then return "''Відсутні''" end
local prize_data = get_player_row("Призові", name)
if not prize_data then return error_output("Prize Data Missing", name) end
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
if #results == 0 then return "''Відсутні''" end
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
table.insert(table_html, '|}')
return table.concat(table_html, '\n')
end
-- ================================================
-- ЦІКАВІ ФАКТИ
-- ================================================
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 not section_start then return nil end
local section_end = mw.ustring.find(content, "\n==[^=]", section_start + 1)
local section_content
if section_end then
section_content = mw.ustring.sub(content, section_start, section_end - 1)
else
section_content = mw.ustring.sub(content, section_start)
end
return section_content
end
local function fact_contains_player(fact, player_name)
local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
local pattern1 = "%[%[" .. escaped_name .. "%]%]"
local pattern2 = "%[%[" .. escaped_name .. "|[^%]]+%]%]"
return mw.ustring.find(fact, pattern1) or mw.ustring.find(fact, pattern2)
end
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 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
if #result_parts == 0 then return "" end
return table.concat(result_parts, "\n")
end
-- ================================================
-- ЗАПИСИ ІГОР
-- ================================================
local function get_tournament_link_games(short_name, full_name)
if not short_name or short_name == "" then
return "Невідомо"
end
if short_name == "Фанова гра" then
return "Фанова гра"
end
if full_name then
full_name = clean_wikilinks(full_name)
end
if full_name and full_name ~= "" and full_name ~= short_name then
return "[[" .. full_name .. "|" .. short_name .. "]]"
else
return "[[" .. short_name .. "]]"
end
end
local function parse_games_row_cells(row)
local cells = {}
row = string.gsub(row, "^%s*|%s*", "")
for cell in string.gmatch(row .. "||", "(.-)%s*||%s*") do
table.insert(cells, mw.text.trim(cell))
end
return cells
end
-- Кеш: таблиця розбирається ОДИН раз на сторінку
local games_rows_cache = nil
local player_games_cache = {}
local function get_games_rows()
if games_rows_cache then return games_rows_cache end
local table_content = get_table_from_page("Ігри")
if not table_content then return nil end
local rows = {}
for row in string.gmatch(table_content, "|-\n([^\n]+)") do
if not string.match(row, "^%s*!") then
local cells = parse_games_row_cells(row)
if #cells >= 17 then
local names = {}
for i = 6, 15 do
names[i] = clean_wikilinks(cells[i])
end
table.insert(rows, { cells = cells, names = names })
end
end
end
games_rows_cache = rows
return rows
end
local function has_unknown_roles(cells)
local res = cells[16]
if not res or mw.text.trim(res) == "" then return true end
return string.find(res, "?", 1, true) ~= nil
end
local function get_player_role(player_name, entry)
if has_unknown_roles(entry.cells) then return "—" end
local names = entry.names
for i = 6, 11 do
if names[i] == player_name then return "Мир" end
end
if names[12] == player_name then return "Шер" end
for i = 13, 14 do
if names[i] == player_name then return "Маф" end
end
if names[15] == player_name then return "Дон" end
return "—"
end
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 string.find(game_result, "?", 1, true) then return "—" end
if game_result == "Місто" or game_result == "Мирні" then
return (role == "Мир" or role == "Шер") and "Перемога" or "Поразка"
end
if game_result == "Мафія" then
return (role == "Маф" or role == "Дон") and "Перемога" or "Поразка"
end
return "—"
end
local function get_player_games_list(player_name)
if player_games_cache[player_name] then
return player_games_cache[player_name]
end
local rows = get_games_rows()
if not rows then return nil end
local games = {}
for _, entry in ipairs(rows) do
local found = false
for i = 6, 15 do
if entry.names[i] == player_name then
found = true
break
end
end
if found then
local cells = entry.cells
local role = get_player_role(player_name, entry)
local result = get_player_result(role, cells[16])
local time_raw = cells[5] or ""
local time_formatted = time_raw
local minutes, seconds = string.match(time_raw, "(%d+)%s*хв%s*(%d+)%s*с")
if minutes and seconds then
time_formatted = minutes .. ":" .. string.format("%02d", tonumber(seconds))
end
table.insert(games, {
short = cells[3] or "",
tournament = cells[4] or "",
time = time_formatted,
role = role,
result = result,
link = cells[17] or ""
})
end
end
player_games_cache[player_name] = games
return games
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('🏆')
headerRow:tag('th'):wikitext('Запис')
for _, game in ipairs(games) do
local row = htmlTable:tag('tr')
local unknown = (game.result == "—")
-- Турнір
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')
:tag('span')
:css('color', unknown and '#777' or '#fff')
:wikitext(game.role)
-- Час
row:tag('td')
:css('text-align', 'center')
:css('padding', '8px')
:wikitext(game.time)
-- Результат
local resultCell = row:tag('td')
:css('text-align', 'center')
:css('padding', '8px')
if game.result == "Перемога" then
resultCell:tag('span')
:css('color', '#4caf50')
:wikitext('В')
elseif game.result == "Поразка" then
resultCell:tag('span')
:css('color', 'indianred')
:wikitext('П')
else
resultCell:tag('span')
:css('color', '#777')
:wikitext('—')
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
-- ================================================
-- ДОСЯГНЕННЯ В СЕЗОНАХ
-- ================================================
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
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
line = mw.text.trim(line)
if line == "|-" then
if #current_row > 0 then
table.insert(all_rows, current_row)
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
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
return nil
end
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
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('% перемог')
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
return tostring(htmlTable)
end
return p