Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Special pages
example
Search
Search
English
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Module:Lorebook/Debug
Module
Discussion
English
Read
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit source
View history
Refresh
General
What links here
Related changes
Page information
In other projects
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
local p = {} -- 调试模式标志 local DEBUG = true local function debug_log(msg) if DEBUG then return "<!-- DEBUG: " .. msg .. " -->\n" end return "" end local function splitCSV(s) if not s or s == '' then return {} end local t = {} for token in mw.text.gsplit(s, ',', true) do token = mw.text.trim(token) if token ~= '' then table.insert(t, token:lower()) end end return t end local function containsAny(haystack, needles) local s = haystack:lower() for _, n in ipairs(needles) do if n ~= '' then local needle = n:lower() -- Escape special pattern characters local escaped = needle:gsub('[%^%$%(%)%%%.%[%]%*%+%-%?]', '%%%1') -- Try multiple matching strategies -- 1. Exact match if s == needle then return true end -- 2. Word boundary patterns local patterns = { '^' .. escaped .. '%W', -- At start followed by non-word '%W' .. escaped .. '%W', -- Surrounded by non-word chars '%W' .. escaped .. '$', -- Preceded by non-word at end '^' .. escaped .. '$' -- Exact match (redundant but safe) } for _, pattern in ipairs(patterns) do if s:find(pattern) then return true end end -- 3. Simple substring match as last resort if s:find(escaped, 1, true) then return true end end end return false end local function anyMatch(haystack, primary, _secondary) return containsAny(haystack, primary) end local function andMatch(haystack, primary, secondary) return containsAny(haystack, primary) and containsAny(haystack, secondary) end local function notMatch(haystack, primary, secondary) return containsAny(haystack, primary) and not containsAny(haystack, secondary) end local logicDispatch = { ANY = anyMatch, AND = andMatch, NOT = notMatch, } local function normalizeValue(val) if type(val) == 'table' then return val[1] or '' end return val or '' end local function getAllConcepts(world) local query = { '[[Category:Concept]][[Belongs to world::' .. world .. ']]', '?Plist=plist', '?AliChat=alichat', '?Primary keys=primary', '?Secondary keys=secondary', '?Logic=logic', '?Key mode=mode', '?Parent concept=parent', '?Non-recursable=nonrec', '?Placement=placement', limit = 999 } local res = mw.smw.ask(query) if not res then return {} end local normalized = {} for i, row in ipairs(res) do -- Debug: log raw row structure for first result if DEBUG and i == 1 then local debug_info = "Raw row[1]: " .. tostring(row[1]) .. " (type: " .. type(row[1]) .. ")\n" debug_info = debug_info .. "Raw row.primary: " .. tostring(row.primary) .. " (type: " .. type(row.primary) .. ")\n" debug_info = debug_info .. "Raw row['Primary keys']: " .. tostring(row['Primary keys']) .. " (type: " .. type(row['Primary keys']) .. ")\n" -- List all keys in the row table debug_info = debug_info .. "\nAll keys in row:\n" for k, v in pairs(row) do debug_info = debug_info .. " [" .. tostring(k) .. "] = " .. tostring(v) .. " (type: " .. type(v) .. ")\n" end -- Store debug info in a global to access later _G._debug_row_info = debug_info end normalized[i] = { page = normalizeValue(row[1]), -- First element is the page title plist = normalizeValue(row.plist), alichat = normalizeValue(row.alichat), primary = normalizeValue(row.primary), secondary = normalizeValue(row.secondary), logic = normalizeValue(row.logic), mode = normalizeValue(row.mode), parent = normalizeValue(row.parent), nonrec = normalizeValue(row.nonrec), placement = normalizeValue(row.placement) } end return normalized end local function indexByTitle(rows) local idx = {} for _, r in ipairs(rows) do if r.page and r.page ~= '' then idx[r.page] = r end end return idx end local function shouldTrigger(row, haystack) local mode = (row.mode or 'conditional'):upper() if mode == 'DISABLED' then return false end if mode == 'CONSTANT' then return true end local logic = (row.logic or 'ANY'):upper() local f = logicDispatch[logic] or anyMatch local primary = splitCSV(row.primary) local secondary = splitCSV(row.secondary) return f(haystack, primary, secondary) end local function walkRecursion(rows, idx, startPages) local children = {} for _, r in ipairs(rows) do local parent = r.parent if parent and parent ~= '' then children[parent] = children[parent] or {} table.insert(children[parent], r.page) end end local visited = {} local stack = {} for _, ptitle in ipairs(startPages) do table.insert(stack, ptitle) end local ordered = {} while #stack > 0 do local cur = table.remove(stack) if not visited[cur] then visited[cur] = true table.insert(ordered, cur) local row = idx[cur] if row and (row.nonrec or ''):lower() ~= 'yes' then for _, c in ipairs(children[cur] or {}) do table.insert(stack, c) end end end end return ordered end local function buildInjection(rows, triggered) local plistChunks, chatChunks = {}, {} for _, title in ipairs(triggered) do local r for _, row in ipairs(rows) do if row.page == title then r = row break end end if r then if r.plist and r.plist ~= '' then table.insert(plistChunks, r.plist) end if r.alichat and r.alichat ~= '' then table.insert(chatChunks, r.alichat) end end end local text = '' if #plistChunks > 0 then text = text .. table.concat(plistChunks, '\n') .. '\n' end if #chatChunks > 0 then if #plistChunks > 0 then text = text .. '\n' -- Add extra newline between plist and chat sections end text = text .. table.concat(chatChunks, '\n') end return text end function p.inject(frame) local world = frame.args.world or 'Farlandia' local haystack = frame.args.context or '' local output = debug_log("Starting inject function") output = output .. debug_log("World: " .. world) output = output .. debug_log("Context: " .. haystack) local rows = getAllConcepts(world) output = output .. debug_log("Found " .. #rows .. " concepts") -- Show raw row debug info if _G._debug_row_info then output = output .. "\n'''Raw SMW data (first row):'''\n<pre>\n" .. _G._debug_row_info .. "</pre>\n" _G._debug_row_info = nil -- Clear after use end if #rows == 0 then output = output .. "\n'''⚠️ No concepts found for world: " .. world .. "'''\n\n" output = output .. "Possible issues:\n" output = output .. "* Concepts not created yet\n" output = output .. "* Concepts not using {{Concept}} template\n" output = output .. "* SMW properties not set correctly\n" output = output .. "* SMW data cache needs refresh (re-save concept pages)\n\n" output = output .. "Debug query: <code><nowiki>{{#ask: [[Category:Concept]] [[Belongs to world::" .. world .. "]] }}</nowiki></code>\n" return output end -- 显示找到的概念列表(调试) if DEBUG then output = output .. "\n'''Found concepts:'''\n" for i, r in ipairs(rows) do local primary_debug = "none" if r.primary then primary_debug = tostring(r.primary) .. " (type: " .. type(r.primary) .. ")" end output = output .. "* " .. (r.page or "unnamed") .. " (mode: " .. (r.mode or "conditional") .. ", primary: " .. primary_debug .. ")\n" end output = output .. "\n" end local idx = indexByTitle(rows) local start = {} for _, r in ipairs(rows) do if shouldTrigger(r, haystack) then table.insert(start, r.page) output = output .. debug_log("Triggered: " .. r.page) end end output = output .. debug_log("Triggered " .. #start .. " concepts") if #start == 0 then output = output .. "\n'''ℹ️ No concepts triggered for context: " .. haystack .. "'''\n\n" output = output .. "Available trigger keys:\n" for _, r in ipairs(rows) do if r.primary and r.primary ~= '' then output = output .. "* " .. r.page .. ": <code>" .. r.primary .. "</code>\n" end end return output end local triggered = walkRecursion(rows, idx, start) output = output .. debug_log("After recursion: " .. #triggered .. " concepts") local result = buildInjection(rows, triggered) if DEBUG then output = output .. "\n'''Injected concepts:'''\n" for _, t in ipairs(triggered) do output = output .. "* " .. t .. "\n" end output = output .. "\n'''=== Output ==='''\n<pre>\n" end -- Escape curly braces and use <pre> tag to preserve formatting if result and result ~= '' then result = result:gsub('{', '{'):gsub('}', '}') output = output .. result .. '\n</pre>\n' else output = output .. "(empty output)\n</pre>\n" end return output end function p.export(frame) local world = frame.args.world or 'Farlandia' local rows = getAllConcepts(world) local entries = {} for _, r in ipairs(rows) do table.insert(entries, { title = r.page, plist = r.plist or '', alichat = r.alichat or '', primary = r.primary or '', secondary = r.secondary or '', logic = (r.logic or 'ANY'), mode = (r.mode or 'conditional'), parent = r.parent or '', nonrecursable = r.nonrec or '', placement = r.placement or '' }) end return mw.text.jsonEncode(entries) end return p
Summary:
Please note that all contributions to example are considered to be released under the 知识共享署名-相同方式共享 (see
example:Copyrights
for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:Lorebook/Debug/doc
(
edit
)
Search
Search
Editing
Module:Lorebook/Debug
Add languages
Add topic