跳至內容

「模組:LumineRelations」:修訂間差異

出自微國家百科
Naritaroad留言 | 貢獻
建立內容為「--[[ ================================================================================ Module:LumineRelations 盧米涅企業共和國 外交關係統計模組 ================================================================================ 【功能說明】 本模組從「盧米涅企業共和國」頁面的三張外交表格中自動讀取資料, 計算各項外交統計數值,供 infobox 或正文引用,避免手動維護數字。 【依賴的目…」的新頁面
 
Naritaroad留言 | 貢獻
無編輯摘要
第6行: 第6行:


【功能說明】
【功能說明】
本模組從「盧米涅企業共和國」頁面的三張外交表格中自動讀取資料,
本模組從「盧米涅企業共和國」頁面的外交表格中自動讀取資料,
計算各項外交統計數值,供 infobox 或正文引用,避免手動維護數字。
計算各項外交統計數值,供 infobox 或正文引用,避免手動維護數字。


【依賴的目標頁面】
【關鍵設計說明】
   TARGET_PAGE = "盧米涅企業共和國"
   每張 AutonumTable 的 id(|id=XXX|class=...}})位於表格「結尾」。
   該頁面須包含以下三張 AutonumTable 表格:
  因此某張表格的列內容,位於「前一個表格的 id」到「本表格的 id」之間。
     id=luminebang   正式邦交國(大使/公使/代辦/不適用)
  本模組使用 countBetween(prevId, thisId) 的方式準確計算每張表格的列數。
    id=luminejing   設立經濟文化代表處之非邦交對象
 
    id=lumineorg    加入之國際組織
   頁面中表格的實際順序(必須與頁面一致,否則計算錯誤):
     (開頭) → luminebang → luminejing → lumineunilateral → lumineorg
          → luminedc → luminetreaty → luminehistbang → luminehistorg (結尾)


【可用函式一覽】
【可用函式一覽】
   {{#invoke:LumineRelations|bangTotal}}     → 邦交國總數
  --- 正式邦交國 ---
   {{#invoke:LumineRelations|bangAmbassador}} → 互派大使級邦交國數
   {{#invoke:LumineRelations|bangTotal}}       → 邦交國總數
   {{#invoke:LumineRelations|bangMinister}}   互派公使級邦交國數
   {{#invoke:LumineRelations|bangAmbassador}}   大使級邦交國數
   {{#invoke:LumineRelations|bangCharge}}     互派代辦級邦交國數
   {{#invoke:LumineRelations|bangMinister}}     公使級邦交國數
   {{#invoke:LumineRelations|bangNA}}         → 不適用(特殊外交安排)邦交國數
   {{#invoke:LumineRelations|bangCharge}}       代辦級邦交國數
   {{#invoke:LumineRelations|jingTotal}}      → 設立經濟文化代表處總數
   {{#invoke:LumineRelations|bangNA}}           → 不適用(特殊外交安排)邦交國數
  {{#invoke:LumineRelations|orgTotal}}      → 加入國際組織總數
   {{#invoke:LumineRelations|bangFormal}}      → 大使+公使+代辦加總(驗算用)
  {{#invoke:LumineRelations|allTotal}}      → 邦交國+代表處+組織總計


   --- 歷史邦交國(已斷交)---
   --- 經濟文化代表處 ---
   {{#invoke:LumineRelations|histTotal}}     歷史邦交國總數(曾建交後斷交)
   {{#invoke:LumineRelations|jingTotal}}       設立經濟文化代表處總數
  {{#invoke:LumineRelations|histExtinct}}    → 因亡國而斷交的數量
  {{#invoke:LumineRelations|histMerged}}    → 因併入他國而斷交的數量
  {{#invoke:LumineRelations|histOther}}      → 其他原因斷交的數量
  {{#invoke:LumineRelations|everTotal}}      → 曾建交總數(現有邦交國+歷史邦交國)


   --- 單方承認主權 ---
   --- 單方承認主權 ---
   {{#invoke:LumineRelations|uniTotal}}       → 單方承認主權國家總數
   {{#invoke:LumineRelations|uniTotal}}         → 單方承認主權國家總數
 
  --- 加入國際組織 ---
  {{#invoke:LumineRelations|orgTotal}}        → 加入國際組織總數


   --- 歷史參與國際組織(已退出)---
   --- 深度合作Discord組織 ---
   {{#invoke:LumineRelations|histOrgTotal}}   → 曾加入但已退出的組織總數
   {{#invoke:LumineRelations|dcTotal}}         深度合作Discord組織總數
  {{#invoke:LumineRelations|histOrgDissolved}} → 因組織解散而退出的數量
  {{#invoke:LumineRelations|histOrgLeft}}    主動退出的數量


   --- 同盟及合作協議 ---
   --- 同盟及合作協議 ---
   {{#invoke:LumineRelations|treatyTotal}}   → 締結條約總數
   {{#invoke:LumineRelations|treatyTotal}}     → 締結條約總數
   {{#invoke:LumineRelations|treatyMilitary}} → 軍事合作條約數
   {{#invoke:LumineRelations|treatyMilitary}}   含軍事性質條約數
   {{#invoke:LumineRelations|treatyOther}}   非軍事合作條約數
   {{#invoke:LumineRelations|treatyOther}}     非軍事性質條約數


   --- 深度合作Discord組織 ---
   --- 歷史邦交國(已斷交)---
   {{#invoke:LumineRelations|dcTotal}}        → 深度合作Discord組織總數
   {{#invoke:LumineRelations|histTotal}}        → 歷史邦交國總數
  {{#invoke:LumineRelations|histExtinct}}      → 因亡國斷交的數量
  {{#invoke:LumineRelations|histMerged}}      → 因併入他國斷交的數量
  {{#invoke:LumineRelations|histOther}}        → 其他原因斷交的數量
 
  --- 歷史參與國際組織(已退出)---
  {{#invoke:LumineRelations|histOrgTotal}}    → 曾加入但已退出的組織總數
  {{#invoke:LumineRelations|histOrgDissolved}} → 因組織解散退出的數量
  {{#invoke:LumineRelations|histOrgLeft}}      → 主動退出的數量
 
  --- 綜合統計 ---
  {{#invoke:LumineRelations|allTotal}}        → 邦交國+代表處+組織總計
  {{#invoke:LumineRelations|everTotal}}        → 曾建交總數(現有+歷史邦交國)


【正文使用範例】
【正文使用範例】
   截至目前,盧米涅企業共和國共與{{#invoke:LumineRelations|bangTotal}}個國家或地區建立正式外交關係,
   截至目前,盧米涅共與{{#invoke:LumineRelations|bangTotal}}個國家建立正式外交關係,
   其中大使級{{#invoke:LumineRelations|bangAmbassador}}個、
   其中大使級{{#invoke:LumineRelations|bangAmbassador}}個、
   公使級{{#invoke:LumineRelations|bangMinister}}個、
   公使級{{#invoke:LumineRelations|bangMinister}}個、
第56行: 第66行:
   另設有{{#invoke:LumineRelations|jingTotal}}處經濟文化代表處,
   另設有{{#invoke:LumineRelations|jingTotal}}處經濟文化代表處,
   並加入{{#invoke:LumineRelations|orgTotal}}個國際組織。
   並加入{{#invoke:LumineRelations|orgTotal}}個國際組織。
【Infobox 使用範例】
  | 邦交國數 = {{#invoke:LumineRelations|bangTotal}}
  | 國際組織 = {{#invoke:LumineRelations|orgTotal}}


【維護說明】
【維護說明】
   - 每次新增或移除邦交國/代表處/組織,只需更新「盧米涅企業共和國」頁面的表格列,
   - 新增或移除表格列後,只需儲存「盧米涅企業共和國」頁面,數字自動更新。
    所有引用本模組的數字會在頁面重新渲染時自動更新。
   - 若頁面名稱更改,修改 TARGET_PAGE 常數。
   - 若頁面名稱更改,修改下方 TARGET_PAGE 常數即可。
   - 若表格 id 或順序更改,必須同步修改下方常數。
   - 若表格 id 更改,同步修改 ID_BANG / ID_JING / ID_ORG 常數。


================================================================================
================================================================================
第73行: 第78行:


-- ============================================================
-- ============================================================
-- 常數設定:目標頁面與表格 id
-- 常數設定
-- ============================================================
-- ============================================================


-- 統計資料所在的 wiki 頁面名稱
local TARGET_PAGE = "盧米涅企業共和國"
local TARGET_PAGE = "盧米涅企業共和國"


-- 三張表格各自的 id(對應 AutonumTable 的 |id=XXX 參數)
-- 頁面中各表格的 id(順序必須與頁面實際順序一致)
local ID_BANG = "luminebang"   -- 邦交國表格
local ID_BANG   = "luminebang"       -- 正式邦交國
local ID_JING = "luminejing"   -- 經濟文化代表處表格
local ID_JING   = "luminejing"       -- 經濟文化代表處
local ID_ORG  = "lumineorg"   -- 國際組織表格
local ID_UNI    = "lumineunilateral" -- 單方承認主權
local ID_HIST = "luminehistbang" -- 歷史邦交國表格(已斷交)
local ID_ORG    = "lumineorg"       -- 加入國際組織
local ID_UNI     = "lumineunilateral" -- 單方承認主權表格
local ID_DC     = "luminedc"         -- 深度合作Discord組織
local ID_HISTORG = "luminehistorg"    -- 歷史參與國際組織表格(已退出)
local ID_TREATY = "luminetreaty"    -- 同盟及合作協議
local ID_TREATY  = "luminetreaty"    -- 同盟及合作協議表格
local ID_HIST    = "luminehistbang"   -- 歷史邦交國(已斷交)
local ID_DC      = "luminedc"         -- 深度合作Discord組織表格
local ID_HISTORG = "luminehistorg"   -- 歷史參與國際組織(已退出)
local ID_HIST = "luminehistbang" -- 歷史邦交國表格(已斷交)


-- ============================================================
-- ============================================================
第94行: 第97行:
-- ============================================================
-- ============================================================


-- 讀取目標頁面的原始 wikitext
local function getContent()
local function getContent()
     return mw.title.new(TARGET_PAGE):getContent() or ""
     return mw.title.new(TARGET_PAGE):getContent() or ""
end
end


-- 計算字串 s 中 AutonumTable/index 出現的次數(即表格列數)
-- 計算字串中 AutonumTable/index 的出現次數
local function countIndex(s)
local function countIndex(s)
     local n = 0
     local n = 0
第106行: 第108行:
end
end


-- 擷取從某個表格 id 開始、到下一個表格 id 為止的區段
-- 核心函式:計算從 prevId 到 thisId 之間的 index 數
-- 用法:getSection(content, "luminebang", "luminejing")
-- 因為 id 在表格結尾,所以 prevId~thisId 之間的內容正好是 thisId 對應表格的所有列
-- 若 endId 為 nil,則擷取到頁面結尾
-- prevId=nil 表示從頁面開頭,thisId=nil 表示到頁面結尾
local function getSection(content, startId, endId)
local function countBetween(content, prevId, thisId)
     local startPos = content:find("id=" .. startId)
     local startPos = 1
    if not startPos then return "" end
     if prevId then
    -- 往前找到此區段的 AutonumTable 開頭
         startPos = content:find("id=" .. prevId)
    local sectionStart = content:sub(1, startPos):match(".*AutonumTable%|content=()")
         if not startPos then return 0 end
     if not sectionStart then
         sectionStart = content:sub(1, startPos):find("AutonumTable|content")
         if not sectionStart then return "" end
     end
     end
    -- 往後找終止點
     local endPos = #content
     local endPos
     if thisId then
     if endId then
         endPos = content:find("id=" .. thisId, startPos)
         endPos = content:find("id=" .. endId, startPos)
         if not endPos then return 0 end
    else
         endPos = #content
     end
     end
     return content:sub(sectionStart or 1, endPos or #content)
     return countIndex(content:sub(startPos, endPos))
end
end


-- 計算區段內特定外交等級的出現次數
-- 擷取 prevId 到 thisId 之間的字串(用於進一步比對欄位值)
-- rank:要搜尋的字串,例如 "大使""公使""代辦""不適用"
local function getSection(content, prevId, thisId)
local function countRank(section, rank)
    local startPos = 1
    if prevId then
        startPos = content:find("id=" .. prevId)
        if not startPos then return "" end
    end
    local endPos = #content
    if thisId then
        endPos = content:find("id=" .. thisId, startPos)
        if not endPos then return "" end
    end
    return content:sub(startPos, endPos)
end
 
-- 計算區段內特定欄位值的出現次數(用 {{!}}{{!}} 包夾精確比對)
local function countField(section, value)
     local n = 0
     local n = 0
    -- 逐行掃描,確保比對的是「外交代表機構」欄位的值
     local pattern = "%{%{!%}%}%{%{!%}%}%s*" .. value .. "%s*%{%{!%}%}%{%{!%}%}"
    -- 每列格式為:{{!}}{{!}} 日期 {{!}}{{!}} 等級 {{!}}{{!}} 詳見
    -- 用 {{!}}{{!}} 包夾來確保精確比對,避免把備註欄的文字也算進去
     local pattern = "%{%{!%}%}%{%{!%}%}%s*" .. rank .. "%s*%{%{!%}%}%{%{!%}%}"
     for _ in section:gmatch(pattern) do n = n + 1 end
     for _ in section:gmatch(pattern) do n = n + 1 end
     return n
     return n
第141行: 第149行:


-- ============================================================
-- ============================================================
-- 邦交國相關函式
-- 正式邦交國(前一表格:無/頁面開頭,本表格 id=luminebang)
-- 欄位:序號 | 國旗及國名 | 建立外交關係時間 | 外交代表機構 | 詳見
-- ============================================================
-- ============================================================


-- 邦交國總數
function p.bangTotal(frame)
function p.bangTotal(frame)
     local c = getContent()
     return countBetween(getContent(), nil, ID_BANG)
    local section = getSection(c, ID_BANG, ID_JING)
    return countIndex(section)
end
end


-- 大使級邦交國數
function p.bangAmbassador(frame)
function p.bangAmbassador(frame)
     local c = getContent()
     return countField(getSection(getContent(), nil, ID_BANG), "大使")
    local section = getSection(c, ID_BANG, ID_JING)
    return countRank(section, "大使")
end
end


-- 公使級邦交國數
function p.bangMinister(frame)
function p.bangMinister(frame)
     local c = getContent()
     return countField(getSection(getContent(), nil, ID_BANG), "公使")
    local section = getSection(c, ID_BANG, ID_JING)
    return countRank(section, "公使")
end
end


-- 代辦級邦交國數
function p.bangCharge(frame)
function p.bangCharge(frame)
     local c = getContent()
     return countField(getSection(getContent(), nil, ID_BANG), "代辦")
    local section = getSection(c, ID_BANG, ID_JING)
    return countRank(section, "代辦")
end
end


-- 不適用(特殊外交安排,如盧米涅-迷途鄉)邦交國數
function p.bangNA(frame)
function p.bangNA(frame)
     local c = getContent()
     return countField(getSection(getContent(), nil, ID_BANG), "不適用")
     local section = getSection(c, ID_BANG, ID_JING)
end
     return countRank(section, "不適用")
 
function p.bangFormal(frame)
     local s = getSection(getContent(), nil, ID_BANG)
     return countField(s, "大使") + countField(s, "公使") + countField(s, "代辦")
end
end


-- ============================================================
-- ============================================================
-- 經濟文化代表處相關函式
-- 經濟文化代表處(前:luminebang,本:luminejing)
-- 欄位:序號 | 國旗及國名 | 建立關係時間 | 外交代表機構 | 詳見
-- ============================================================
-- ============================================================


-- 設立經濟文化代表處總數
function p.jingTotal(frame)
function p.jingTotal(frame)
     local c = getContent()
     return countBetween(getContent(), ID_BANG, ID_JING)
    local section = getSection(c, ID_JING, ID_ORG)
end
     return countIndex(section)
 
-- ============================================================
-- 單方承認主權(前:luminejing,本:lumineunilateral)
-- 欄位:序號 | 國旗及國名 | 建立關係時間 | 外交代表機構 | 詳見
-- ============================================================
 
function p.uniTotal(frame)
     return countBetween(getContent(), ID_JING, ID_UNI)
end
end


-- ============================================================
-- ============================================================
-- 國際組織相關函式
-- 加入國際組織(前:lumineunilateral,本:lumineorg)
-- 欄位:序號 | 組織徽章及名稱 | 加入時間 | 組織內關係 | 詳見
-- ============================================================
-- ============================================================


-- 加入國際組織總數
function p.orgTotal(frame)
function p.orgTotal(frame)
     local c = getContent()
     return countBetween(getContent(), ID_UNI, ID_ORG)
    local section = getSection(c, ID_ORG, nil)
    -- 限定在 id=lumineorg 區段內,避免計入後續其他內容
    local bounded = c:match("id=" .. ID_ORG .. "(.-)}}", c:find("id=" .. ID_ORG) or 1)
    if bounded then
        return countIndex(bounded)
    end
    return countIndex(section)
end
end


-- ============================================================
-- ============================================================
-- 綜合統計函式
-- 深度合作Discord組織(前:lumineorg,本:luminedc)
-- 欄位:序號 | 標誌及名稱 | 合作起始時間 | 詳見
-- ============================================================
-- ============================================================


-- 邦交國+代表處+組織的總計
function p.dcTotal(frame)
function p.allTotal(frame)
     return countBetween(getContent(), ID_ORG, ID_DC)
     local c = getContent()
    local bang = countIndex(getSection(c, ID_BANG, ID_JING))
    local jing = countIndex(getSection(c, ID_JING, ID_ORG))
    local org_bounded = c:match("id=" .. ID_ORG .. "(.-)}}", c:find("id=" .. ID_ORG) or 1)
    local org = org_bounded and countIndex(org_bounded) or 0
    return bang + jing + org
end
end


-- 邦交國中,大使+公使+代辦的加總(驗證用,應等於 bangTotal 減去不適用)
-- ============================================================
function p.bangFormal(frame)
-- 同盟及合作協議(前:luminedc,本:luminetreaty)
     local c = getContent()
-- 欄位:序號 | 締結條約 | 類型 | 締約成員 | 締約時間
    local section = getSection(c, ID_BANG, ID_JING)
-- ============================================================
    local amb = countRank(section, "大使")
 
    local min = countRank(section, "公使")
function p.treatyTotal(frame)
    local chg = countRank(section, "代辦")
     return countBetween(getContent(), ID_DC, ID_TREATY)
    return amb + min + chg
end
end


-- 含軍事性質的條約數(類型欄包含「軍事」)
function p.treatyMilitary(frame)
    local section = getSection(getContent(), ID_DC, ID_TREATY)
    local n = 0
    for _ in section:gmatch("%{%{!%}%}%{%{!%}%}[^%{]*軍事[^%{]*%{%{!%}%}%{%{!%}%}") do n = n + 1 end
    return n
end
-- 非軍事性質條約數
function p.treatyOther(frame)
    return (tonumber(p.treatyTotal(frame)) or 0) - (tonumber(p.treatyMilitary(frame)) or 0)
end


-- ============================================================
-- ============================================================
-- 歷史邦交國相關函式(id=luminehistbang)
-- 歷史邦交國(前:luminetreaty,本:luminehistbang)
-- 歷史邦交國表格欄位順序:
-- 欄位:序號 | 國旗及國名 | 建立外交關係時間 | 外交代表機構 | 斷交時間 | 斷交原因 | 詳見
--  序號 | 國旗及國名 | 建立外交關係時間 | 外交代表機構 | 斷交時間 | 斷交原因 | 詳見
-- 斷交原因:「亡國」/ 「併入[[XXX]]」/ 其他
-- ============================================================
-- ============================================================


-- 歷史邦交國總數(曾建交但已斷交的國家總計)
function p.histTotal(frame)
function p.histTotal(frame)
     local c = getContent()
     return countBetween(getContent(), ID_TREATY, ID_HIST)
    -- 從 id=luminehistbang 往後到頁面結尾,但只取該 AutonumTable 區塊內
    local bounded = c:match("id=" .. ID_HIST .. "(.-)}}", c:find("id=" .. ID_HIST) or 1)
    -- 上方 match 只能抓到第一個 }},AutonumTable 結尾是 }},需要更寬的範圍
    -- 改用:從表格開頭標記到 id=luminehistbang 之後的第一個表格結束標記
    local startPos = c:find("id=" .. ID_HIST)
    if not startPos then return 0 end
    -- 往前找到此表格的 AutonumTable|content 開頭
    local tableStart = 1
    for pos in c:gmatch("()AutonumTable|content") do
        if pos < startPos then tableStart = pos end
    end
    local section = c:sub(tableStart, startPos + #ID_HIST + 50)
    return countIndex(section)
end
end


-- 因亡國而斷交的數量
-- 比對「斷交原因」欄位包含「亡國」的列
function p.histExtinct(frame)
function p.histExtinct(frame)
     local c = getContent()
     return countField(getSection(getContent(), ID_TREATY, ID_HIST), "亡國")
    local startPos = c:find("id=" .. ID_HIST)
    if not startPos then return 0 end
    local tableStart = 1
    for pos in c:gmatch("()AutonumTable|content") do
        if pos < startPos then tableStart = pos end
    end
    local section = c:sub(tableStart, startPos + #ID_HIST + 200)
    -- 完整擷取整個歷史表格:從 AutonumTable/content 到 id=luminehistbang
    local fullSection = c:match("AutonumTable|content=!序號 %{%{!!%}%} 國旗及國名 %{%{!!%}%} 建立外交關係時間 %{%{!!%}%} 外交代表機構 %{%{!!%}%} 斷交時間 %{%{!!%}%} 斷交原因(.-)id=" .. ID_HIST)
    if not fullSection then return 0 end
    local n = 0
    -- 每列格式:... {{!}}{{!}} 斷交時間 {{!}}{{!}} 亡國 {{!}}{{!}} [[詳見]]
    for _ in fullSection:gmatch("%{%{!%}%}%{%{!%}%}%s*亡國%s*%{%{!%}%}%{%{!%}%}") do n = n + 1 end
    return n
end
end


-- 因併入他國而斷交的數量
function p.histMerged(frame)
function p.histMerged(frame)
     local c = getContent()
     local section = getSection(getContent(), ID_TREATY, ID_HIST)
    local fullSection = c:match("AutonumTable|content=!序號 %{%{!!%}%} 國旗及國名 %{%{!!%}%} 建立外交關係時間 %{%{!!%}%} 外交代表機構 %{%{!!%}%} 斷交時間 %{%{!!%}%} 斷交原因(.-)id=" .. ID_HIST)
    if not fullSection then return 0 end
     local n = 0
     local n = 0
    -- 「併入」開頭的斷交原因
     for _ in section:gmatch("%{%{!%}%}%{%{!%}%}%s*併入") do n = n + 1 end
     for _ in fullSection:gmatch("%{%{!%}%}%{%{!%}%}%s*併入") do n = n + 1 end
     return n
     return n
end
end


-- 其他原因斷交的數量(非亡國、非併入)
function p.histOther(frame)
function p.histOther(frame)
     local c = getContent()
     return (tonumber(p.histTotal(frame)) or 0)
    local total_n = tonumber(p.histTotal(frame)) or 0
        - (tonumber(p.histExtinct(frame)) or 0)
    local extinct_n = tonumber(p.histExtinct(frame)) or 0
        - (tonumber(p.histMerged(frame)) or 0)
    local merged_n = tonumber(p.histMerged(frame)) or 0
    return total_n - extinct_n - merged_n
end
end
-- 曾建交總數:現有正式邦交國 + 歷史邦交國
function p.everTotal(frame)
    local bang_n = tonumber(p.bangTotal(frame)) or 0
    local hist_n = tonumber(p.histTotal(frame)) or 0
    return bang_n + hist_n
end


-- ============================================================
-- ============================================================
-- 單方承認主權(id=lumineunilateral)
-- 歷史參與國際組織(前:luminehistbang,本:luminehistorg)
-- 欄位:序號 | 國旗及國名 | 建立關係時間 | 外交代表機構 | 詳見
-- ============================================================
 
-- 單方承認主權國家總數
function p.uniTotal(frame)
    local c = getContent()
    local startPos = c:find("id=" .. ID_UNI)
    if not startPos then return 0 end
    local tableStart = 1
    for pos in c:gmatch("()AutonumTable|content") do
        if pos < startPos then tableStart = pos end
    end
    return countIndex(c:sub(tableStart, startPos + 50))
end
 
-- ============================================================
-- 歷史參與國際組織(id=luminehistorg)
-- 欄位:序號 | 組織徽章及名稱 | 加入時間 | 退出時間 | 退出原因 | 組織內關係 | 詳見
-- 欄位:序號 | 組織徽章及名稱 | 加入時間 | 退出時間 | 退出原因 | 組織內關係 | 詳見
-- 退出原因主要分為:「組織解散」或主動退出(含事件退出)
-- 退出原因:「組織解散」/ 其他(主動退出)
-- ============================================================
-- ============================================================


-- 曾加入但已退出的組織總數
function p.histOrgTotal(frame)
function p.histOrgTotal(frame)
     local c = getContent()
     return countBetween(getContent(), ID_HIST, ID_HISTORG)
    local startPos = c:find("id=" .. ID_HISTORG)
    if not startPos then return 0 end
    local tableStart = 1
    for pos in c:gmatch("()AutonumTable|content") do
        if pos < startPos then tableStart = pos end
    end
    return countIndex(c:sub(tableStart, startPos + 50))
end
end


-- 因組織解散而退出的數量
function p.histOrgDissolved(frame)
function p.histOrgDissolved(frame)
     local c = getContent()
     return countField(getSection(getContent(), ID_HIST, ID_HISTORG), "組織解散")
    -- 擷取歷史組織表格的完整區段(表頭到 id=luminehistorg)
    local section = c:match(
        "AutonumTable|content=!序號 %{%{!!%}%} 組織徽章及名稱 %{%{!!%}%} 加入時間 %{%{!!%}%} 退出時間 %{%{!!%}%} 退出原因(.-)id=" .. ID_HISTORG
    )
    if not section then return 0 end
    local n = 0
    -- 比對退出原因欄位為「組織解散」
    for _ in section:gmatch("%{%{!%}%}%{%{!%}%}%s*組織解散%s*%{%{!%}%}%{%{!%}%}") do n = n + 1 end
    return n
end
end


-- 主動退出的數量(非解散,例如因事件退出)
function p.histOrgLeft(frame)
function p.histOrgLeft(frame)
     local total = tonumber(p.histOrgTotal(frame)) or 0
     return (tonumber(p.histOrgTotal(frame)) or 0) - (tonumber(p.histOrgDissolved(frame)) or 0)
    local dissolved = tonumber(p.histOrgDissolved(frame)) or 0
    return total - dissolved
end
end


-- ============================================================
-- ============================================================
-- 同盟及合作協議(id=luminetreaty)
-- 綜合統計
-- 欄位:序號 | 締結條約 | 類型 | 締約成員 | 締約時間
-- 類型分為:軍事合作、馬娘文化合作、軍事及永續發展合作 等
-- ============================================================
-- ============================================================


-- 締結條約總數
-- 邦交國+代表處+組織總計
function p.treatyTotal(frame)
function p.allTotal(frame)
     local c = getContent()
     return (tonumber(p.bangTotal(frame)) or 0)
    local startPos = c:find("id=" .. ID_TREATY)
        + (tonumber(p.jingTotal(frame)) or 0)
    if not startPos then return 0 end
        + (tonumber(p.orgTotal(frame)) or 0)
    local tableStart = 1
    for pos in c:gmatch("()AutonumTable|content") do
        if pos < startPos then tableStart = pos end
    end
    return countIndex(c:sub(tableStart, startPos + 50))
end
end


-- 含軍事合作性質的條約數(類型欄包含「軍事」)
-- 曾建交總數(現有正式邦交國+歷史邦交國)
function p.treatyMilitary(frame)
function p.everTotal(frame)
    local c = getContent()
     return (tonumber(p.bangTotal(frame)) or 0)
    local section = c:match(
        + (tonumber(p.histTotal(frame)) or 0)
        "AutonumTable|content=!序號 %{%{!!%}%} 締結條約 %{%{!!%}%} 類型 %{%{!!%}%} 締約成員 %{%{!!%}%} 締約時間(.-)id=" .. ID_TREATY
    )
    if not section then return 0 end
    local n = 0
    -- 比對類型欄包含「軍事」的列
    for _ in section:gmatch("%{%{!%}%}%{%{!%}%}[^%{]*軍事[^%{]*%{%{!%}%}%{%{!%}%}") do n = n + 1 end
     return n
end
 
-- 非軍事性質條約數(文化、經濟等)
function p.treatyOther(frame)
    local total = tonumber(p.treatyTotal(frame)) or 0
    local military = tonumber(p.treatyMilitary(frame)) or 0
    return total - military
end
 
-- ============================================================
-- 深度合作Discord組織(id=luminedc)
-- 欄位:序號 | 標誌及名稱 | 合作起始時間 | 詳見
-- ============================================================
 
-- 深度合作Discord組織總數
function p.dcTotal(frame)
    local c = getContent()
    local startPos = c:find("id=" .. ID_DC)
    if not startPos then return 0 end
    local tableStart = 1
    for pos in c:gmatch("()AutonumTable|content") do
        if pos < startPos then tableStart = pos end
    end
    return countIndex(c:sub(tableStart, startPos + 50))
end
end


return p
return p

於 2026年6月12日 (五) 02:39 的修訂

可在模組:LumineRelations/doc建立此模組的說明文件

--[[
================================================================================
Module:LumineRelations
盧米涅企業共和國 外交關係統計模組
================================================================================

【功能說明】
本模組從「盧米涅企業共和國」頁面的外交表格中自動讀取資料,
計算各項外交統計數值,供 infobox 或正文引用,避免手動維護數字。

【關鍵設計說明】
  每張 AutonumTable 的 id(|id=XXX|class=...}})位於表格「結尾」。
  因此某張表格的列內容,位於「前一個表格的 id」到「本表格的 id」之間。
  本模組使用 countBetween(prevId, thisId) 的方式準確計算每張表格的列數。

  頁面中表格的實際順序(必須與頁面一致,否則計算錯誤):
    (開頭) → luminebang → luminejing → lumineunilateral → lumineorg
           → luminedc → luminetreaty → luminehistbang → luminehistorg → (結尾)

【可用函式一覽】
  --- 正式邦交國 ---
  {{#invoke:LumineRelations|bangTotal}}        → 邦交國總數
  {{#invoke:LumineRelations|bangAmbassador}}   → 大使級邦交國數
  {{#invoke:LumineRelations|bangMinister}}     → 公使級邦交國數
  {{#invoke:LumineRelations|bangCharge}}       → 代辦級邦交國數
  {{#invoke:LumineRelations|bangNA}}           → 不適用(特殊外交安排)邦交國數
  {{#invoke:LumineRelations|bangFormal}}       → 大使+公使+代辦加總(驗算用)

  --- 經濟文化代表處 ---
  {{#invoke:LumineRelations|jingTotal}}        → 設立經濟文化代表處總數

  --- 單方承認主權 ---
  {{#invoke:LumineRelations|uniTotal}}         → 單方承認主權國家總數

  --- 加入國際組織 ---
  {{#invoke:LumineRelations|orgTotal}}         → 加入國際組織總數

  --- 深度合作Discord組織 ---
  {{#invoke:LumineRelations|dcTotal}}          → 深度合作Discord組織總數

  --- 同盟及合作協議 ---
  {{#invoke:LumineRelations|treatyTotal}}      → 締結條約總數
  {{#invoke:LumineRelations|treatyMilitary}}   → 含軍事性質條約數
  {{#invoke:LumineRelations|treatyOther}}      → 非軍事性質條約數

  --- 歷史邦交國(已斷交)---
  {{#invoke:LumineRelations|histTotal}}        → 歷史邦交國總數
  {{#invoke:LumineRelations|histExtinct}}      → 因亡國斷交的數量
  {{#invoke:LumineRelations|histMerged}}       → 因併入他國斷交的數量
  {{#invoke:LumineRelations|histOther}}        → 其他原因斷交的數量

  --- 歷史參與國際組織(已退出)---
  {{#invoke:LumineRelations|histOrgTotal}}     → 曾加入但已退出的組織總數
  {{#invoke:LumineRelations|histOrgDissolved}} → 因組織解散退出的數量
  {{#invoke:LumineRelations|histOrgLeft}}      → 主動退出的數量

  --- 綜合統計 ---
  {{#invoke:LumineRelations|allTotal}}         → 邦交國+代表處+組織總計
  {{#invoke:LumineRelations|everTotal}}        → 曾建交總數(現有+歷史邦交國)

【正文使用範例】
  截至目前,盧米涅共與{{#invoke:LumineRelations|bangTotal}}個國家建立正式外交關係,
  其中大使級{{#invoke:LumineRelations|bangAmbassador}}個、
  公使級{{#invoke:LumineRelations|bangMinister}}個、
  代辦級{{#invoke:LumineRelations|bangCharge}}個。
  另設有{{#invoke:LumineRelations|jingTotal}}處經濟文化代表處,
  並加入{{#invoke:LumineRelations|orgTotal}}個國際組織。

【維護說明】
  - 新增或移除表格列後,只需儲存「盧米涅企業共和國」頁面,數字自動更新。
  - 若頁面名稱更改,修改 TARGET_PAGE 常數。
  - 若表格 id 或順序更改,必須同步修改下方常數。

================================================================================
]]

local p = {}

-- ============================================================
-- 常數設定
-- ============================================================

local TARGET_PAGE = "盧米涅企業共和國"

-- 頁面中各表格的 id(順序必須與頁面實際順序一致)
local ID_BANG    = "luminebang"       -- 正式邦交國
local ID_JING    = "luminejing"       -- 經濟文化代表處
local ID_UNI     = "lumineunilateral" -- 單方承認主權
local ID_ORG     = "lumineorg"        -- 加入國際組織
local ID_DC      = "luminedc"         -- 深度合作Discord組織
local ID_TREATY  = "luminetreaty"     -- 同盟及合作協議
local ID_HIST    = "luminehistbang"   -- 歷史邦交國(已斷交)
local ID_HISTORG = "luminehistorg"    -- 歷史參與國際組織(已退出)

-- ============================================================
-- 內部工具函式
-- ============================================================

local function getContent()
    return mw.title.new(TARGET_PAGE):getContent() or ""
end

-- 計算字串中 AutonumTable/index 的出現次數
local function countIndex(s)
    local n = 0
    for _ in s:gmatch("AutonumTable/index") do n = n + 1 end
    return n
end

-- 核心函式:計算從 prevId 到 thisId 之間的 index 數
-- 因為 id 在表格結尾,所以 prevId~thisId 之間的內容正好是 thisId 對應表格的所有列
-- prevId=nil 表示從頁面開頭,thisId=nil 表示到頁面結尾
local function countBetween(content, prevId, thisId)
    local startPos = 1
    if prevId then
        startPos = content:find("id=" .. prevId)
        if not startPos then return 0 end
    end
    local endPos = #content
    if thisId then
        endPos = content:find("id=" .. thisId, startPos)
        if not endPos then return 0 end
    end
    return countIndex(content:sub(startPos, endPos))
end

-- 擷取 prevId 到 thisId 之間的字串(用於進一步比對欄位值)
local function getSection(content, prevId, thisId)
    local startPos = 1
    if prevId then
        startPos = content:find("id=" .. prevId)
        if not startPos then return "" end
    end
    local endPos = #content
    if thisId then
        endPos = content:find("id=" .. thisId, startPos)
        if not endPos then return "" end
    end
    return content:sub(startPos, endPos)
end

-- 計算區段內特定欄位值的出現次數(用 {{!}}{{!}} 包夾精確比對)
local function countField(section, value)
    local n = 0
    local pattern = "%{%{!%}%}%{%{!%}%}%s*" .. value .. "%s*%{%{!%}%}%{%{!%}%}"
    for _ in section:gmatch(pattern) do n = n + 1 end
    return n
end

-- ============================================================
-- 正式邦交國(前一表格:無/頁面開頭,本表格 id=luminebang)
-- 欄位:序號 | 國旗及國名 | 建立外交關係時間 | 外交代表機構 | 詳見
-- ============================================================

function p.bangTotal(frame)
    return countBetween(getContent(), nil, ID_BANG)
end

function p.bangAmbassador(frame)
    return countField(getSection(getContent(), nil, ID_BANG), "大使")
end

function p.bangMinister(frame)
    return countField(getSection(getContent(), nil, ID_BANG), "公使")
end

function p.bangCharge(frame)
    return countField(getSection(getContent(), nil, ID_BANG), "代辦")
end

function p.bangNA(frame)
    return countField(getSection(getContent(), nil, ID_BANG), "不適用")
end

function p.bangFormal(frame)
    local s = getSection(getContent(), nil, ID_BANG)
    return countField(s, "大使") + countField(s, "公使") + countField(s, "代辦")
end

-- ============================================================
-- 經濟文化代表處(前:luminebang,本:luminejing)
-- 欄位:序號 | 國旗及國名 | 建立關係時間 | 外交代表機構 | 詳見
-- ============================================================

function p.jingTotal(frame)
    return countBetween(getContent(), ID_BANG, ID_JING)
end

-- ============================================================
-- 單方承認主權(前:luminejing,本:lumineunilateral)
-- 欄位:序號 | 國旗及國名 | 建立關係時間 | 外交代表機構 | 詳見
-- ============================================================

function p.uniTotal(frame)
    return countBetween(getContent(), ID_JING, ID_UNI)
end

-- ============================================================
-- 加入國際組織(前:lumineunilateral,本:lumineorg)
-- 欄位:序號 | 組織徽章及名稱 | 加入時間 | 組織內關係 | 詳見
-- ============================================================

function p.orgTotal(frame)
    return countBetween(getContent(), ID_UNI, ID_ORG)
end

-- ============================================================
-- 深度合作Discord組織(前:lumineorg,本:luminedc)
-- 欄位:序號 | 標誌及名稱 | 合作起始時間 | 詳見
-- ============================================================

function p.dcTotal(frame)
    return countBetween(getContent(), ID_ORG, ID_DC)
end

-- ============================================================
-- 同盟及合作協議(前:luminedc,本:luminetreaty)
-- 欄位:序號 | 締結條約 | 類型 | 締約成員 | 締約時間
-- ============================================================

function p.treatyTotal(frame)
    return countBetween(getContent(), ID_DC, ID_TREATY)
end

-- 含軍事性質的條約數(類型欄包含「軍事」)
function p.treatyMilitary(frame)
    local section = getSection(getContent(), ID_DC, ID_TREATY)
    local n = 0
    for _ in section:gmatch("%{%{!%}%}%{%{!%}%}[^%{]*軍事[^%{]*%{%{!%}%}%{%{!%}%}") do n = n + 1 end
    return n
end

-- 非軍事性質條約數
function p.treatyOther(frame)
    return (tonumber(p.treatyTotal(frame)) or 0) - (tonumber(p.treatyMilitary(frame)) or 0)
end

-- ============================================================
-- 歷史邦交國(前:luminetreaty,本:luminehistbang)
-- 欄位:序號 | 國旗及國名 | 建立外交關係時間 | 外交代表機構 | 斷交時間 | 斷交原因 | 詳見
-- 斷交原因:「亡國」/ 「併入[[XXX]]」/ 其他
-- ============================================================

function p.histTotal(frame)
    return countBetween(getContent(), ID_TREATY, ID_HIST)
end

function p.histExtinct(frame)
    return countField(getSection(getContent(), ID_TREATY, ID_HIST), "亡國")
end

function p.histMerged(frame)
    local section = getSection(getContent(), ID_TREATY, ID_HIST)
    local n = 0
    for _ in section:gmatch("%{%{!%}%}%{%{!%}%}%s*併入") do n = n + 1 end
    return n
end

function p.histOther(frame)
    return (tonumber(p.histTotal(frame)) or 0)
         - (tonumber(p.histExtinct(frame)) or 0)
         - (tonumber(p.histMerged(frame)) or 0)
end

-- ============================================================
-- 歷史參與國際組織(前:luminehistbang,本:luminehistorg)
-- 欄位:序號 | 組織徽章及名稱 | 加入時間 | 退出時間 | 退出原因 | 組織內關係 | 詳見
-- 退出原因:「組織解散」/ 其他(主動退出)
-- ============================================================

function p.histOrgTotal(frame)
    return countBetween(getContent(), ID_HIST, ID_HISTORG)
end

function p.histOrgDissolved(frame)
    return countField(getSection(getContent(), ID_HIST, ID_HISTORG), "組織解散")
end

function p.histOrgLeft(frame)
    return (tonumber(p.histOrgTotal(frame)) or 0) - (tonumber(p.histOrgDissolved(frame)) or 0)
end

-- ============================================================
-- 綜合統計
-- ============================================================

-- 邦交國+代表處+組織總計
function p.allTotal(frame)
    return (tonumber(p.bangTotal(frame)) or 0)
         + (tonumber(p.jingTotal(frame)) or 0)
         + (tonumber(p.orgTotal(frame)) or 0)
end

-- 曾建交總數(現有正式邦交國+歷史邦交國)
function p.everTotal(frame)
    return (tonumber(p.bangTotal(frame)) or 0)
         + (tonumber(p.histTotal(frame)) or 0)
end

return p