增加自动翻译skill
This commit is contained in:
parent
40f123d4e4
commit
c3e28bf7de
|
|
@ -3,7 +3,12 @@
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(python:*)",
|
"Bash(python:*)",
|
||||||
"Bash(tree:*)",
|
"Bash(tree:*)",
|
||||||
"Bash(dir:*)"
|
"Bash(dir:*)",
|
||||||
|
"Bash(python3 -m venv venv)",
|
||||||
|
"Bash(source venv/bin/activate)",
|
||||||
|
"Bash(pip install:*)",
|
||||||
|
"Bash(source /mnt/d/code/sync-pd2-wiki/venv/bin/activate)",
|
||||||
|
"Bash(curl -s \"https://wiki.projectdiablo2.cn/w/api.php?action=query&prop=revisions&titles=Filter%20Info&rvprop=ids&format=json\")"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
---
|
||||||
|
name: mediawiki-wikitext
|
||||||
|
description: MediaWiki Wikitext markup language for Wikipedia and wiki-based sites. Use when creating or editing wiki articles, generating wikitext content, working with wiki tables/templates/references, or converting content to wikitext format. Triggers on requests mentioning Wikipedia, MediaWiki, wikitext, wiki markup, or wiki article creation.
|
||||||
|
---
|
||||||
|
|
||||||
|
# MediaWiki Wikitext
|
||||||
|
|
||||||
|
Generate and edit content using MediaWiki's wikitext markup language.
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Text Formatting
|
||||||
|
```wikitext
|
||||||
|
''italic'' '''bold''' '''''bold italic'''''
|
||||||
|
<code>inline code</code> <sub>subscript</sub> <sup>superscript</sup>
|
||||||
|
<s>strikethrough</s> <u>underline</u>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Headings (line start only, avoid level 1)
|
||||||
|
```wikitext
|
||||||
|
== Level 2 ==
|
||||||
|
=== Level 3 ===
|
||||||
|
==== Level 4 ====
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lists
|
||||||
|
```wikitext
|
||||||
|
* Bullet item # Numbered item ; Term
|
||||||
|
** Nested ## Nested : Definition
|
||||||
|
```
|
||||||
|
|
||||||
|
### Links
|
||||||
|
```wikitext
|
||||||
|
[[Page Name]] Internal link
|
||||||
|
[[Page Name|Display Text]] With display text
|
||||||
|
[[Page Name#Section]] Section link
|
||||||
|
[https://url Display Text] External link
|
||||||
|
[[File:image.jpg|thumb|Caption]] Image
|
||||||
|
[[Category:Name]] Category (place at end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Table
|
||||||
|
```wikitext
|
||||||
|
{| class="wikitable"
|
||||||
|
|+ Caption
|
||||||
|
|-
|
||||||
|
! Header 1 !! Header 2
|
||||||
|
|-
|
||||||
|
| Cell 1 || Cell 2
|
||||||
|
|}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Templates & Variables
|
||||||
|
```wikitext
|
||||||
|
{{TemplateName}} Basic call
|
||||||
|
{{TemplateName|arg1|name=value}} With arguments
|
||||||
|
{{{parameter|default}}} Parameter (in template)
|
||||||
|
{{PAGENAME}} {{CURRENTYEAR}} Magic words
|
||||||
|
```
|
||||||
|
|
||||||
|
### References
|
||||||
|
```wikitext
|
||||||
|
Text<ref>Citation here</ref>
|
||||||
|
<ref name="id">Citation</ref> Named reference
|
||||||
|
<ref name="id" /> Reuse reference
|
||||||
|
{{Reflist}} Display footnotes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Tags
|
||||||
|
```wikitext
|
||||||
|
<nowiki>[[escaped]]</nowiki> Disable markup
|
||||||
|
<pre>preformatted block</pre> Preformatted (no markup)
|
||||||
|
<syntaxhighlight lang="python"> Code highlighting
|
||||||
|
code here
|
||||||
|
</syntaxhighlight>
|
||||||
|
<math>x^2 + y^2 = z^2</math> LaTeX math
|
||||||
|
<!-- comment --> Comment (hidden)
|
||||||
|
---- Horizontal rule
|
||||||
|
#REDIRECT [[Target Page]] Redirect (first line only)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Magic Words
|
||||||
|
```wikitext
|
||||||
|
__NOTOC__ Hide table of contents
|
||||||
|
__TOC__ Position TOC here
|
||||||
|
__NOEDITSECTION__ Hide section edit links
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Article Structure
|
||||||
|
```wikitext
|
||||||
|
{{Infobox Type
|
||||||
|
| name = Example
|
||||||
|
| image = Example.jpg
|
||||||
|
}}
|
||||||
|
|
||||||
|
'''Article Title''' is a brief introduction.
|
||||||
|
|
||||||
|
== Section ==
|
||||||
|
Content with citation<ref>Source</ref>.
|
||||||
|
|
||||||
|
=== Subsection ===
|
||||||
|
More content.
|
||||||
|
|
||||||
|
== See also ==
|
||||||
|
* [[Related Article]]
|
||||||
|
|
||||||
|
== References ==
|
||||||
|
{{Reflist}}
|
||||||
|
|
||||||
|
== External links ==
|
||||||
|
* [https://example.com Official site]
|
||||||
|
|
||||||
|
{{DEFAULTSORT:Sort Key}}
|
||||||
|
[[Category:Category Name]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Template Definition
|
||||||
|
```wikitext
|
||||||
|
<noinclude>{{Documentation}}</noinclude><includeonly>
|
||||||
|
{| class="wikitable"
|
||||||
|
! {{{title|Default Title}}}
|
||||||
|
|-
|
||||||
|
| {{{content|No content provided}}}
|
||||||
|
{{#if:{{{footer|}}}|
|
||||||
|
{{!}}-
|
||||||
|
{{!}} {{{footer}}}
|
||||||
|
}}
|
||||||
|
|}
|
||||||
|
</includeonly>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Syntax Rules
|
||||||
|
|
||||||
|
1. **Headings**: Use `==` to `======`; don't use `=` (reserved for page title)
|
||||||
|
2. **Line-start markup**: Lists (`*#;:`), headings, tables (`{|`) must start at line beginning
|
||||||
|
3. **Closing tags**: Close heading equals on same line; no text after closing `==`
|
||||||
|
4. **Blank lines**: Create paragraph breaks; single newlines are ignored
|
||||||
|
5. **Pipes in templates**: Use `{{!}}` for literal `|` inside templates
|
||||||
|
6. **Escaping**: Use `<nowiki>` to escape markup; `&` for `&`, `<` for `<`
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
For detailed syntax, see:
|
||||||
|
- **references/syntax.md**: Complete markup reference with all options
|
||||||
|
- **references/templates.md**: Template and parser function details
|
||||||
|
- **assets/snippets.yaml**: Editor snippets for common patterns
|
||||||
|
|
@ -0,0 +1,236 @@
|
||||||
|
# MediaWiki Wikitext Snippets
|
||||||
|
# For VS Code and compatible editors
|
||||||
|
|
||||||
|
---
|
||||||
|
# Headings
|
||||||
|
h2:
|
||||||
|
prefix: '@h2'
|
||||||
|
body: "== ${1:Heading} ==\n"
|
||||||
|
description: Level 2 heading
|
||||||
|
|
||||||
|
h3:
|
||||||
|
prefix: '@h3'
|
||||||
|
body: "=== ${1:Heading} ===\n"
|
||||||
|
description: Level 3 heading
|
||||||
|
|
||||||
|
h4:
|
||||||
|
prefix: '@h4'
|
||||||
|
body: "==== ${1:Heading} ====\n"
|
||||||
|
description: Level 4 heading
|
||||||
|
|
||||||
|
# Text formatting
|
||||||
|
bold:
|
||||||
|
prefix: '@bold'
|
||||||
|
body: "'''${1:text}'''"
|
||||||
|
description: Bold text
|
||||||
|
|
||||||
|
italic:
|
||||||
|
prefix: '@italic'
|
||||||
|
body: "''${1:text}''"
|
||||||
|
description: Italic text
|
||||||
|
|
||||||
|
# Links
|
||||||
|
link:
|
||||||
|
prefix: '@link'
|
||||||
|
body: '[[${1:Page}]]'
|
||||||
|
description: Internal link
|
||||||
|
|
||||||
|
linkd:
|
||||||
|
prefix: '@linkd'
|
||||||
|
body: '[[${1:Page}|${2:Display}]]'
|
||||||
|
description: Link with display text
|
||||||
|
|
||||||
|
elink:
|
||||||
|
prefix: '@elink'
|
||||||
|
body: '[${1:https://} ${2:Text}]'
|
||||||
|
description: External link
|
||||||
|
|
||||||
|
file:
|
||||||
|
prefix: '@file'
|
||||||
|
body: '[[File:${1:name.jpg}|thumb|${2:Caption}]]'
|
||||||
|
description: Image/file
|
||||||
|
|
||||||
|
cat:
|
||||||
|
prefix: '@cat'
|
||||||
|
body: '[[Category:${1:Name}]]'
|
||||||
|
description: Category
|
||||||
|
|
||||||
|
# Table
|
||||||
|
table:
|
||||||
|
prefix: '@table'
|
||||||
|
body: |
|
||||||
|
{| class="wikitable"
|
||||||
|
|+ ${1:Caption}
|
||||||
|
|-
|
||||||
|
! ${2:Header1} !! ${3:Header2}
|
||||||
|
|-
|
||||||
|
| ${4:Cell1} || ${5:Cell2}
|
||||||
|
|}
|
||||||
|
description: Basic table
|
||||||
|
|
||||||
|
tr:
|
||||||
|
prefix: '@tr'
|
||||||
|
body: |
|
||||||
|
|-
|
||||||
|
| ${1} || ${2}
|
||||||
|
description: Table row
|
||||||
|
|
||||||
|
# References
|
||||||
|
ref:
|
||||||
|
prefix: '@ref'
|
||||||
|
body: '<ref>${1:Citation}</ref>'
|
||||||
|
description: Reference
|
||||||
|
|
||||||
|
refn:
|
||||||
|
prefix: '@refn'
|
||||||
|
body: '<ref name="${1:id}">${2:Citation}</ref>'
|
||||||
|
description: Named reference
|
||||||
|
|
||||||
|
refr:
|
||||||
|
prefix: '@refr'
|
||||||
|
body: '<ref name="${1:id}" />'
|
||||||
|
description: Reference reuse
|
||||||
|
|
||||||
|
reflist:
|
||||||
|
prefix: '@reflist'
|
||||||
|
body: |
|
||||||
|
== References ==
|
||||||
|
{{Reflist}}
|
||||||
|
description: References section
|
||||||
|
|
||||||
|
# Templates
|
||||||
|
tpl:
|
||||||
|
prefix: '@tpl'
|
||||||
|
body: '{{${1:Template}}}'
|
||||||
|
description: Template call
|
||||||
|
|
||||||
|
tplp:
|
||||||
|
prefix: '@tplp'
|
||||||
|
body: '{{${1:Template}|${2:param}=${3:value}}}'
|
||||||
|
description: Template with params
|
||||||
|
|
||||||
|
infobox:
|
||||||
|
prefix: '@infobox'
|
||||||
|
body: |
|
||||||
|
{{Infobox ${1:type}
|
||||||
|
| name = ${2}
|
||||||
|
| image = ${3}
|
||||||
|
}}
|
||||||
|
description: Infobox
|
||||||
|
|
||||||
|
# Code
|
||||||
|
code:
|
||||||
|
prefix: '@code'
|
||||||
|
body: |
|
||||||
|
<syntaxhighlight lang="${1:python}">
|
||||||
|
${2:code}
|
||||||
|
</syntaxhighlight>
|
||||||
|
description: Syntax highlight block
|
||||||
|
|
||||||
|
codei:
|
||||||
|
prefix: '@codei'
|
||||||
|
body: '<code>${1:code}</code>'
|
||||||
|
description: Inline code
|
||||||
|
|
||||||
|
nowiki:
|
||||||
|
prefix: '@nowiki'
|
||||||
|
body: '<nowiki>${1:text}</nowiki>'
|
||||||
|
description: Escape markup
|
||||||
|
|
||||||
|
pre:
|
||||||
|
prefix: '@pre'
|
||||||
|
body: |
|
||||||
|
<pre>
|
||||||
|
${1:text}
|
||||||
|
</pre>
|
||||||
|
description: Preformatted block
|
||||||
|
|
||||||
|
math:
|
||||||
|
prefix: '@math'
|
||||||
|
body: '<math>${1:formula}</math>'
|
||||||
|
description: Math formula
|
||||||
|
|
||||||
|
# Comments
|
||||||
|
comment:
|
||||||
|
prefix: '@comment'
|
||||||
|
body: '<!-- ${1:comment} -->'
|
||||||
|
description: Comment
|
||||||
|
|
||||||
|
todo:
|
||||||
|
prefix: '@todo'
|
||||||
|
body: '<!-- TODO: ${1:task} -->'
|
||||||
|
description: TODO comment
|
||||||
|
|
||||||
|
# Magic words
|
||||||
|
notoc:
|
||||||
|
prefix: '@notoc'
|
||||||
|
body: '__NOTOC__'
|
||||||
|
description: Hide TOC
|
||||||
|
|
||||||
|
toc:
|
||||||
|
prefix: '@toc'
|
||||||
|
body: '__TOC__'
|
||||||
|
description: TOC position
|
||||||
|
|
||||||
|
# Lists
|
||||||
|
ul:
|
||||||
|
prefix: '@ul'
|
||||||
|
body: |
|
||||||
|
* ${1:Item 1}
|
||||||
|
* ${2:Item 2}
|
||||||
|
* ${3:Item 3}
|
||||||
|
description: Bullet list
|
||||||
|
|
||||||
|
ol:
|
||||||
|
prefix: '@ol'
|
||||||
|
body: |
|
||||||
|
# ${1:Item 1}
|
||||||
|
# ${2:Item 2}
|
||||||
|
# ${3:Item 3}
|
||||||
|
description: Numbered list
|
||||||
|
|
||||||
|
dl:
|
||||||
|
prefix: '@dl'
|
||||||
|
body: |
|
||||||
|
; ${1:Term}
|
||||||
|
: ${2:Definition}
|
||||||
|
description: Definition list
|
||||||
|
|
||||||
|
# Structure
|
||||||
|
redirect:
|
||||||
|
prefix: '@redirect'
|
||||||
|
body: '#REDIRECT [[${1:Target}]]'
|
||||||
|
description: Redirect
|
||||||
|
|
||||||
|
article:
|
||||||
|
prefix: '@article'
|
||||||
|
body: |
|
||||||
|
{{Infobox
|
||||||
|
| name = ${1:Name}
|
||||||
|
}}
|
||||||
|
|
||||||
|
'''${2:Title}''' is ${3:description}.
|
||||||
|
|
||||||
|
== Overview ==
|
||||||
|
${4:Content}
|
||||||
|
|
||||||
|
== References ==
|
||||||
|
{{Reflist}}
|
||||||
|
|
||||||
|
[[Category:${5:Category}]]
|
||||||
|
description: Article template
|
||||||
|
|
||||||
|
hr:
|
||||||
|
prefix: '@hr'
|
||||||
|
body: '----'
|
||||||
|
description: Horizontal rule
|
||||||
|
|
||||||
|
br:
|
||||||
|
prefix: '@br'
|
||||||
|
body: '<br />'
|
||||||
|
description: Line break
|
||||||
|
|
||||||
|
sort:
|
||||||
|
prefix: '@sort'
|
||||||
|
body: '{{DEFAULTSORT:${1:Key}}}'
|
||||||
|
description: Default sort key
|
||||||
|
|
@ -0,0 +1,345 @@
|
||||||
|
# MediaWiki Wikitext Syntax Reference
|
||||||
|
|
||||||
|
Complete syntax reference for MediaWiki wikitext markup.
|
||||||
|
|
||||||
|
## Text Formatting (Inline)
|
||||||
|
|
||||||
|
| Syntax | Result | Notes |
|
||||||
|
|--------|--------|-------|
|
||||||
|
| `''text''` | *italic* | Two single quotes |
|
||||||
|
| `'''text'''` | **bold** | Three single quotes |
|
||||||
|
| `'''''text'''''` | ***bold italic*** | Five single quotes |
|
||||||
|
| `<code>text</code>` | `monospace` | Inline code |
|
||||||
|
| `<var>x</var>` | Variable style | |
|
||||||
|
| `<kbd>Ctrl</kbd>` | Keyboard input | |
|
||||||
|
| `<samp>output</samp>` | Sample output | |
|
||||||
|
| `<sub>2</sub>` | Subscript | H₂O |
|
||||||
|
| `<sup>2</sup>` | Superscript | x² |
|
||||||
|
| `<s>text</s>` | ~~strikethrough~~ | |
|
||||||
|
| `<del>text</del>` | Deleted text | Semantic |
|
||||||
|
| `<ins>text</ins>` | Inserted text | Often underlined |
|
||||||
|
| `<u>text</u>` | Underline | |
|
||||||
|
| `<small>text</small>` | Small text | |
|
||||||
|
| `<big>text</big>` | Large text | Deprecated |
|
||||||
|
|
||||||
|
## Headings
|
||||||
|
|
||||||
|
```wikitext
|
||||||
|
= Level 1 = (Don't use - reserved for page title)
|
||||||
|
== Level 2 ==
|
||||||
|
=== Level 3 ===
|
||||||
|
==== Level 4 ====
|
||||||
|
===== Level 5 =====
|
||||||
|
====== Level 6 ======
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rules:**
|
||||||
|
- Must start at line beginning
|
||||||
|
- No text after closing equals on same line
|
||||||
|
- 4+ headings auto-generate TOC (unless `__NOTOC__`)
|
||||||
|
- Spaces around heading text are optional but recommended
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
### Unordered (Bullet) Lists
|
||||||
|
```wikitext
|
||||||
|
* Item 1
|
||||||
|
* Item 2
|
||||||
|
** Nested item 2.1
|
||||||
|
** Nested item 2.2
|
||||||
|
*** Deeper nesting
|
||||||
|
* Item 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ordered (Numbered) Lists
|
||||||
|
```wikitext
|
||||||
|
# First item
|
||||||
|
# Second item
|
||||||
|
## Sub-item 2.1
|
||||||
|
## Sub-item 2.2
|
||||||
|
### Sub-sub-item
|
||||||
|
# Third item
|
||||||
|
```
|
||||||
|
|
||||||
|
### Definition Lists
|
||||||
|
```wikitext
|
||||||
|
; Term 1
|
||||||
|
: Definition 1
|
||||||
|
; Term 2
|
||||||
|
: Definition 2a
|
||||||
|
: Definition 2b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mixed Lists
|
||||||
|
```wikitext
|
||||||
|
# Numbered item
|
||||||
|
#* Bullet under numbered
|
||||||
|
#* Another bullet
|
||||||
|
# Next numbered
|
||||||
|
#: Definition-style continuation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Indentation
|
||||||
|
```wikitext
|
||||||
|
: Single indent
|
||||||
|
:: Double indent
|
||||||
|
::: Triple indent
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** List markers must be at line start. Blank lines end the list.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
|
||||||
|
### Internal Links
|
||||||
|
```wikitext
|
||||||
|
[[Page Name]]
|
||||||
|
[[Page Name|Display Text]]
|
||||||
|
[[Page Name#Section]]
|
||||||
|
[[Page Name#Section|Display Text]]
|
||||||
|
[[Namespace:Page Name]]
|
||||||
|
[[/Subpage]]
|
||||||
|
[[../Sibling Page]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### External Links
|
||||||
|
```wikitext
|
||||||
|
[https://example.com] Numbered link [1]
|
||||||
|
[https://example.com Display Text] Named link
|
||||||
|
https://example.com Auto-linked
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Links
|
||||||
|
```wikitext
|
||||||
|
[[File:Image.jpg]] Embed image
|
||||||
|
[[File:Image.jpg|thumb|Caption]] Thumbnail with caption
|
||||||
|
[[File:Image.jpg|thumb|left|200px|Caption]]
|
||||||
|
[[Media:File.pdf]] Direct file link
|
||||||
|
[[Category:Category Name]] Add to category
|
||||||
|
[[:Category:Category Name]] Link to category (no add)
|
||||||
|
[[Special:RecentChanges]] Special page
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interwiki Links
|
||||||
|
```wikitext
|
||||||
|
[[en:English Article]] Language link
|
||||||
|
[[wikt:word]] Wiktionary
|
||||||
|
[[commons:File:Image.jpg]] Wikimedia Commons
|
||||||
|
```
|
||||||
|
|
||||||
|
## Images
|
||||||
|
|
||||||
|
### Basic Syntax
|
||||||
|
```wikitext
|
||||||
|
[[File:Example.jpg|options|caption]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Image Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `thumb` | Thumbnail (default right-aligned) |
|
||||||
|
| `frame` | Framed, no resize |
|
||||||
|
| `frameless` | Thumbnail without frame |
|
||||||
|
| `border` | Thin border |
|
||||||
|
| `right`, `left`, `center`, `none` | Alignment |
|
||||||
|
| `200px` | Width |
|
||||||
|
| `x100px` | Height |
|
||||||
|
| `200x100px` | Max dimensions |
|
||||||
|
| `upright` | Smart scaling for tall images |
|
||||||
|
| `upright=0.5` | Custom ratio |
|
||||||
|
| `link=Page` | Custom link target |
|
||||||
|
| `link=` | No link |
|
||||||
|
| `alt=Text` | Alt text for accessibility |
|
||||||
|
|
||||||
|
### Gallery
|
||||||
|
```wikitext
|
||||||
|
<gallery>
|
||||||
|
File:Image1.jpg|Caption 1
|
||||||
|
File:Image2.jpg|Caption 2
|
||||||
|
</gallery>
|
||||||
|
|
||||||
|
<gallery mode="packed" heights="150">
|
||||||
|
File:Image1.jpg
|
||||||
|
File:Image2.jpg
|
||||||
|
</gallery>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tables
|
||||||
|
|
||||||
|
### Basic Structure
|
||||||
|
```wikitext
|
||||||
|
{| class="wikitable"
|
||||||
|
|+ Caption
|
||||||
|
|-
|
||||||
|
! Header 1 !! Header 2 !! Header 3
|
||||||
|
|-
|
||||||
|
| Cell 1 || Cell 2 || Cell 3
|
||||||
|
|-
|
||||||
|
| Cell 4 || Cell 5 || Cell 6
|
||||||
|
|}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Table Elements
|
||||||
|
|
||||||
|
| Markup | Location | Meaning |
|
||||||
|
|--------|----------|---------|
|
||||||
|
| `{|` | Start | Table start |
|
||||||
|
| `|}` | End | Table end |
|
||||||
|
| `|+` | After `{|` | Caption |
|
||||||
|
| `|-` | Row | Row separator |
|
||||||
|
| `!` | Cell | Header cell |
|
||||||
|
| `!!` | Cell | Header cell separator (same row) |
|
||||||
|
| `|` | Cell | Data cell |
|
||||||
|
| `||` | Cell | Data cell separator (same row) |
|
||||||
|
|
||||||
|
### Cell Attributes
|
||||||
|
```wikitext
|
||||||
|
| style="background:#fcc" | Red background
|
||||||
|
| colspan="2" | Spans 2 columns
|
||||||
|
| rowspan="3" | Spans 3 rows
|
||||||
|
! scope="col" | Column header
|
||||||
|
! scope="row" | Row header
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sortable Table
|
||||||
|
```wikitext
|
||||||
|
{| class="wikitable sortable"
|
||||||
|
|-
|
||||||
|
! Name !! Value
|
||||||
|
|-
|
||||||
|
| Alpha || 1
|
||||||
|
| Beta || 2
|
||||||
|
|}
|
||||||
|
```
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
### Basic Citation
|
||||||
|
```wikitext
|
||||||
|
Statement<ref>Source information</ref>
|
||||||
|
|
||||||
|
== References ==
|
||||||
|
{{Reflist}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Named References
|
||||||
|
```wikitext
|
||||||
|
First use<ref name="smith2020">Smith, 2020, p. 42</ref>
|
||||||
|
Second use<ref name="smith2020" />
|
||||||
|
```
|
||||||
|
|
||||||
|
### Grouped References
|
||||||
|
```wikitext
|
||||||
|
Note<ref group="note">Explanatory note</ref>
|
||||||
|
Source<ref>Regular citation</ref>
|
||||||
|
|
||||||
|
== Notes ==
|
||||||
|
{{Reflist|group="note"}}
|
||||||
|
|
||||||
|
== References ==
|
||||||
|
{{Reflist}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Special Tags
|
||||||
|
|
||||||
|
### nowiki (Escape Markup)
|
||||||
|
```wikitext
|
||||||
|
<nowiki>[[Not a link]]</nowiki>
|
||||||
|
<<nowiki/>nowiki> Outputs: <nowiki>
|
||||||
|
```
|
||||||
|
|
||||||
|
### pre (Preformatted)
|
||||||
|
```wikitext
|
||||||
|
<pre>
|
||||||
|
Preformatted text
|
||||||
|
Whitespace preserved
|
||||||
|
'''Markup not processed'''
|
||||||
|
</pre>
|
||||||
|
```
|
||||||
|
|
||||||
|
### syntaxhighlight (Code)
|
||||||
|
```wikitext
|
||||||
|
<syntaxhighlight lang="python">
|
||||||
|
def hello():
|
||||||
|
print("Hello")
|
||||||
|
</syntaxhighlight>
|
||||||
|
|
||||||
|
<syntaxhighlight lang="python" line="1" start="10">
|
||||||
|
# Line numbers starting at 10
|
||||||
|
</syntaxhighlight>
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported languages: python, javascript, php, java, c, cpp, csharp, ruby, perl, sql, xml, html, css, json, yaml, bash, etc.
|
||||||
|
|
||||||
|
### math (LaTeX)
|
||||||
|
```wikitext
|
||||||
|
Inline: <math>E = mc^2</math>
|
||||||
|
Block: <math display="block">\sum_{i=1}^n i = \frac{n(n+1)}{2}</math>
|
||||||
|
Chemistry: <chem>H2O</chem>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transclusion Control
|
||||||
|
```wikitext
|
||||||
|
<includeonly>Only when transcluded</includeonly>
|
||||||
|
<noinclude>Only on template page itself</noinclude>
|
||||||
|
<onlyinclude>Only this part is transcluded</onlyinclude>
|
||||||
|
```
|
||||||
|
|
||||||
|
## HTML Entities
|
||||||
|
|
||||||
|
| Entity | Character | Description |
|
||||||
|
|--------|-----------|-------------|
|
||||||
|
| `&` | & | Ampersand |
|
||||||
|
| `<` | < | Less than |
|
||||||
|
| `>` | > | Greater than |
|
||||||
|
| ` ` | (space) | Non-breaking space |
|
||||||
|
| `—` | — | Em dash |
|
||||||
|
| `–` | – | En dash |
|
||||||
|
| `→` | → | Right arrow |
|
||||||
|
| `←` | ← | Left arrow |
|
||||||
|
| `©` | © | Copyright |
|
||||||
|
| `€` | € | Euro |
|
||||||
|
| `:` | : | Colon (in definition lists) |
|
||||||
|
|
||||||
|
## Miscellaneous
|
||||||
|
|
||||||
|
### Horizontal Rule
|
||||||
|
```wikitext
|
||||||
|
----
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
```wikitext
|
||||||
|
<!-- This is a comment -->
|
||||||
|
<!--
|
||||||
|
Multi-line
|
||||||
|
comment
|
||||||
|
-->
|
||||||
|
```
|
||||||
|
|
||||||
|
### Line Breaks
|
||||||
|
```wikitext
|
||||||
|
Line 1<br />Line 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Redirect
|
||||||
|
```wikitext
|
||||||
|
#REDIRECT [[Target Page]]
|
||||||
|
#REDIRECT [[Target Page#Section]]
|
||||||
|
```
|
||||||
|
Must be first line of page.
|
||||||
|
|
||||||
|
### Signatures (Talk pages)
|
||||||
|
```wikitext
|
||||||
|
~~~ Username only
|
||||||
|
~~~~ Username and timestamp
|
||||||
|
~~~~~ Timestamp only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Categories
|
||||||
|
```wikitext
|
||||||
|
[[Category:Category Name]]
|
||||||
|
[[Category:Category Name|Sort Key]]
|
||||||
|
{{DEFAULTSORT:Sort Key}}
|
||||||
|
```
|
||||||
|
Place at end of article.
|
||||||
|
|
@ -0,0 +1,311 @@
|
||||||
|
# MediaWiki Templates and Parser Functions
|
||||||
|
|
||||||
|
## Template Basics
|
||||||
|
|
||||||
|
### Calling Templates
|
||||||
|
```wikitext
|
||||||
|
{{TemplateName}}
|
||||||
|
{{TemplateName|positional arg}}
|
||||||
|
{{TemplateName|param1=value1|param2=value2}}
|
||||||
|
{{TemplateName
|
||||||
|
| param1 = value1
|
||||||
|
| param2 = value2
|
||||||
|
}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Template Parameters (Definition Side)
|
||||||
|
```wikitext
|
||||||
|
{{{1}}} First positional parameter
|
||||||
|
{{{paramName}}} Named parameter
|
||||||
|
{{{1|default}}} With default value
|
||||||
|
{{{paramName|}}} Empty default (vs undefined)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transclusion
|
||||||
|
```wikitext
|
||||||
|
{{:Page Name}} Transclude article (with colon)
|
||||||
|
{{Template Name}} Transclude template
|
||||||
|
{{subst:Template Name}} Substitute (one-time expansion)
|
||||||
|
{{safesubst:Template}} Safe substitution
|
||||||
|
{{msgnw:Template}} Show raw wikitext
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parser Functions
|
||||||
|
|
||||||
|
### Conditionals
|
||||||
|
|
||||||
|
#### #if (empty test)
|
||||||
|
```wikitext
|
||||||
|
{{#if: {{{param|}}} | not empty | empty or undefined }}
|
||||||
|
{{#if: {{{param|}}} | has value }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #ifeq (equality test)
|
||||||
|
```wikitext
|
||||||
|
{{#ifeq: {{{type}}} | book | It's a book | Not a book }}
|
||||||
|
{{#ifeq: {{{1}}} | {{{2}}} | same | different }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #iferror
|
||||||
|
```wikitext
|
||||||
|
{{#iferror: {{#expr: 1/0}} | Division error | OK }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #ifexist (page exists)
|
||||||
|
```wikitext
|
||||||
|
{{#ifexist: Page Name | [[Page Name]] | Page doesn't exist }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #ifexpr (expression test)
|
||||||
|
```wikitext
|
||||||
|
{{#ifexpr: {{{count}}} > 10 | Many | Few }}
|
||||||
|
{{#ifexpr: {{{year}}} mod 4 = 0 | Leap year candidate }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #switch
|
||||||
|
```wikitext
|
||||||
|
{{#switch: {{{type}}}
|
||||||
|
| book = 📚 Book
|
||||||
|
| article = 📄 Article
|
||||||
|
| website = 🌐 Website
|
||||||
|
| #default = 📋 Other
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{#switch: {{{1}}}
|
||||||
|
| A | B | C = First three letters
|
||||||
|
| #default = Something else
|
||||||
|
}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### String Functions
|
||||||
|
|
||||||
|
#### #len
|
||||||
|
```wikitext
|
||||||
|
{{#len: Hello }} Returns: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #pos (find position)
|
||||||
|
```wikitext
|
||||||
|
{{#pos: Hello World | o }} Returns: 4 (first 'o')
|
||||||
|
{{#pos: Hello World | o | 5 }} Returns: 7 (after position 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #sub (substring)
|
||||||
|
```wikitext
|
||||||
|
{{#sub: Hello World | 0 | 5 }} Returns: Hello
|
||||||
|
{{#sub: Hello World | 6 }} Returns: World
|
||||||
|
{{#sub: Hello World | -5 }} Returns: World (from end)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #replace
|
||||||
|
```wikitext
|
||||||
|
{{#replace: Hello World | World | Universe }} Returns: Hello Universe
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #explode (split)
|
||||||
|
```wikitext
|
||||||
|
{{#explode: a,b,c,d | , | 2 }} Returns: c (third element)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #urlencode / #urldecode
|
||||||
|
```wikitext
|
||||||
|
{{#urlencode: Hello World }} Returns: Hello%20World
|
||||||
|
{{#urldecode: Hello%20World }} Returns: Hello World
|
||||||
|
```
|
||||||
|
|
||||||
|
### Math Functions
|
||||||
|
|
||||||
|
#### #expr
|
||||||
|
```wikitext
|
||||||
|
{{#expr: 1 + 2 * 3 }} Returns: 7
|
||||||
|
{{#expr: (1 + 2) * 3 }} Returns: 9
|
||||||
|
{{#expr: 2 ^ 10 }} Returns: 1024
|
||||||
|
{{#expr: 17 mod 5 }} Returns: 2
|
||||||
|
{{#expr: floor(3.7) }} Returns: 3
|
||||||
|
{{#expr: ceil(3.2) }} Returns: 4
|
||||||
|
{{#expr: round(3.567, 2) }} Returns: 3.57
|
||||||
|
{{#expr: abs(-5) }} Returns: 5
|
||||||
|
{{#expr: sqrt(16) }} Returns: 4
|
||||||
|
{{#expr: ln(e) }} Returns: 1
|
||||||
|
{{#expr: sin(pi/2) }} Returns: 1
|
||||||
|
```
|
||||||
|
|
||||||
|
**Operators:** `+`, `-`, `*`, `/`, `^` (power), `mod`, `round`, `floor`, `ceil`, `abs`, `sqrt`, `ln`, `exp`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `pi`, `e`
|
||||||
|
|
||||||
|
**Comparison:** `=`, `<>`, `!=`, `<`, `>`, `<=`, `>=`
|
||||||
|
|
||||||
|
**Logical:** `and`, `or`, `not`
|
||||||
|
|
||||||
|
### Date/Time Functions
|
||||||
|
|
||||||
|
#### #time
|
||||||
|
```wikitext
|
||||||
|
{{#time: Y-m-d }} Current: 2024-01-15
|
||||||
|
{{#time: F j, Y | 2024-01-15 }} January 15, 2024
|
||||||
|
{{#time: Y年n月j日 | 2024-01-15 }} 2024年1月15日
|
||||||
|
{{#time: l | 2024-01-15 }} Monday
|
||||||
|
```
|
||||||
|
|
||||||
|
**Format codes:**
|
||||||
|
| Code | Output | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| Y | 2024 | 4-digit year |
|
||||||
|
| y | 24 | 2-digit year |
|
||||||
|
| n | 1 | Month (no leading zero) |
|
||||||
|
| m | 01 | Month (with leading zero) |
|
||||||
|
| F | January | Full month name |
|
||||||
|
| M | Jan | Abbreviated month |
|
||||||
|
| j | 5 | Day (no leading zero) |
|
||||||
|
| d | 05 | Day (with leading zero) |
|
||||||
|
| l | Monday | Full weekday |
|
||||||
|
| D | Mon | Abbreviated weekday |
|
||||||
|
| H | 14 | Hour (24h, leading zero) |
|
||||||
|
| i | 05 | Minutes (leading zero) |
|
||||||
|
| s | 09 | Seconds (leading zero) |
|
||||||
|
|
||||||
|
#### #timel (local time)
|
||||||
|
```wikitext
|
||||||
|
{{#timel: H:i }} Local time
|
||||||
|
```
|
||||||
|
|
||||||
|
### Formatting Functions
|
||||||
|
|
||||||
|
#### #formatnum
|
||||||
|
```wikitext
|
||||||
|
{{#formatnum: 1234567.89 }} 1,234,567.89
|
||||||
|
{{#formatnum: 1,234.56 | R }} 1234.56 (raw)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #padleft / #padright
|
||||||
|
```wikitext
|
||||||
|
{{#padleft: 7 | 3 | 0 }} 007
|
||||||
|
{{#padright: abc | 6 | . }} abc...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #lc / #uc / #lcfirst / #ucfirst
|
||||||
|
```wikitext
|
||||||
|
{{#lc: HELLO }} hello
|
||||||
|
{{#uc: hello }} HELLO
|
||||||
|
{{#lcfirst: HELLO }} hELLO
|
||||||
|
{{#ucfirst: hello }} Hello
|
||||||
|
{{lc: HELLO }} hello (shortcut)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other Functions
|
||||||
|
|
||||||
|
#### #tag
|
||||||
|
```wikitext
|
||||||
|
{{#tag: ref | Citation text | name=smith }}
|
||||||
|
Equivalent to: <ref name="smith">Citation text</ref>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### #invoke (Lua modules)
|
||||||
|
```wikitext
|
||||||
|
{{#invoke: ModuleName | functionName | arg1 | arg2 }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Magic Words
|
||||||
|
|
||||||
|
### Behavior Switches
|
||||||
|
```wikitext
|
||||||
|
__NOTOC__ No table of contents
|
||||||
|
__FORCETOC__ Force TOC even with <4 headings
|
||||||
|
__TOC__ Place TOC here
|
||||||
|
__NOEDITSECTION__ No section edit links
|
||||||
|
__NEWSECTIONLINK__ Add new section link
|
||||||
|
__NONEWSECTIONLINK__ Remove new section link
|
||||||
|
__NOGALLERY__ No gallery in category
|
||||||
|
__HIDDENCAT__ Hidden category
|
||||||
|
__INDEX__ Index by search engines
|
||||||
|
__NOINDEX__ Don't index
|
||||||
|
__STATICREDIRECT__ Don't update redirect
|
||||||
|
```
|
||||||
|
|
||||||
|
### Page Variables
|
||||||
|
```wikitext
|
||||||
|
{{PAGENAME}} Page title without namespace
|
||||||
|
{{FULLPAGENAME}} Full page title
|
||||||
|
{{BASEPAGENAME}} Parent page name
|
||||||
|
{{SUBPAGENAME}} Subpage name
|
||||||
|
{{ROOTPAGENAME}} Root page name
|
||||||
|
{{TALKPAGENAME}} Associated talk page
|
||||||
|
{{NAMESPACE}} Current namespace
|
||||||
|
{{NAMESPACENUMBER}} Namespace number
|
||||||
|
{{PAGEID}} Page ID
|
||||||
|
{{REVISIONID}} Revision ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### Site Variables
|
||||||
|
```wikitext
|
||||||
|
{{SITENAME}} Wiki name
|
||||||
|
{{SERVER}} Server URL
|
||||||
|
{{SERVERNAME}} Server hostname
|
||||||
|
{{SCRIPTPATH}} Script path
|
||||||
|
```
|
||||||
|
|
||||||
|
### Date/Time Variables
|
||||||
|
```wikitext
|
||||||
|
{{CURRENTYEAR}} 4-digit year
|
||||||
|
{{CURRENTMONTH}} Month (01-12)
|
||||||
|
{{CURRENTMONTHNAME}} Month name
|
||||||
|
{{CURRENTDAY}} Day (1-31)
|
||||||
|
{{CURRENTDAYNAME}} Day name
|
||||||
|
{{CURRENTTIME}} HH:MM
|
||||||
|
{{CURRENTTIMESTAMP}} YYYYMMDDHHmmss
|
||||||
|
```
|
||||||
|
|
||||||
|
### Statistics
|
||||||
|
```wikitext
|
||||||
|
{{NUMBEROFPAGES}} Total pages
|
||||||
|
{{NUMBEROFARTICLES}} Content pages
|
||||||
|
{{NUMBEROFFILES}} Files
|
||||||
|
{{NUMBEROFUSERS}} Registered users
|
||||||
|
{{NUMBEROFACTIVEUSERS}} Active users
|
||||||
|
{{NUMBEROFEDITS}} Total edits
|
||||||
|
{{PAGESINCATEGORY:Name}} Pages in category
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template Examples
|
||||||
|
|
||||||
|
### Simple Infobox
|
||||||
|
```wikitext
|
||||||
|
<noinclude>{{Documentation}}</noinclude><includeonly>
|
||||||
|
{| class="infobox" style="width:22em"
|
||||||
|
|-
|
||||||
|
! colspan="2" style="background:#ccc" | {{{title|{{PAGENAME}}}}}
|
||||||
|
{{#if:{{{image|}}}|
|
||||||
|
{{!}}-
|
||||||
|
{{!}} colspan="2" {{!}} [[File:{{{image}}}|200px|center]]
|
||||||
|
}}
|
||||||
|
|-
|
||||||
|
| '''Type''' || {{{type|Unknown}}}
|
||||||
|
|-
|
||||||
|
| '''Date''' || {{{date|—}}}
|
||||||
|
|}
|
||||||
|
</includeonly>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Navbox Template
|
||||||
|
```wikitext
|
||||||
|
<noinclude>{{Documentation}}</noinclude><includeonly>
|
||||||
|
{| class="navbox" style="width:100%"
|
||||||
|
|-
|
||||||
|
! style="background:#ccf" | {{{title|Navigation}}}
|
||||||
|
|-
|
||||||
|
| {{{content|}}}
|
||||||
|
|}
|
||||||
|
</includeonly>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Citation Template
|
||||||
|
```wikitext
|
||||||
|
<includeonly>{{#if:{{{author|}}}|{{{author}}}. }}{{#if:{{{title|}}}|''{{{title}}}''. }}{{#if:{{{publisher|}}}|{{{publisher}}}{{#if:{{{year|}}}|, }}}}{{{year|}}}.{{#if:{{{url|}}}| [{{{url}}} Link]}}</includeonly>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
1. **Pipe trick**: `[[Help:Contents|]]` displays as "Contents"
|
||||||
|
2. **Escape pipes in templates**: Use `{{!}}` for literal `|`
|
||||||
|
3. **Trim whitespace**: Parameters automatically trim whitespace
|
||||||
|
4. **Check emptiness correctly**: `{{{param|}}}` vs `{{{param}}}` - the former has empty default, latter is undefined if not passed
|
||||||
|
5. **Subst for speed**: Use `{{subst:Template}}` for templates that don't need dynamic updates
|
||||||
|
|
@ -0,0 +1,139 @@
|
||||||
|
---
|
||||||
|
name: wiki-sync-translate
|
||||||
|
description: 同步英文 MediaWiki 页面变更到中文翻译文档。当用户需要更新中文 Wiki页面、同步英文变更、或翻译 Wiki 内容时触发。适用于 Project Diablo 2 Wiki 的中英双语同步维护场景。
|
||||||
|
---
|
||||||
|
|
||||||
|
# Wiki 同步翻译
|
||||||
|
|
||||||
|
同步英文 Wiki 页面变更到中文翻译文档,保持行号一致。
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
- 用户请求更新中文 Wiki 页面
|
||||||
|
- 用户请求同步英文 Wiki 变更
|
||||||
|
- 用户指定某个页面需要进行翻译更新
|
||||||
|
- 用户执行 `/wiki-sync-translate <页面名称>`
|
||||||
|
|
||||||
|
## 工作目录
|
||||||
|
|
||||||
|
脚本位于:`.claude/skills/wiki-sync-translate/scripts/wiki_sync.py`
|
||||||
|
|
||||||
|
## 执行步骤
|
||||||
|
|
||||||
|
### Step 1: 运行同步脚本
|
||||||
|
|
||||||
|
使用 skill 目录下的专用脚本获取变更:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/d/code/sync-pd2-wiki
|
||||||
|
source venv/bin/activate
|
||||||
|
python .claude/skills/wiki-sync-translate/
|
||||||
|
scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run
|
||||||
|
```
|
||||||
|
|
||||||
|
参数说明:
|
||||||
|
- `--title`: 指定要同步的页面名称
|
||||||
|
- `--since`: 起始时间,格式如 `2026-01-02T12:07:05Z`
|
||||||
|
- `--run`: 必须提供此参数才会执行
|
||||||
|
|
||||||
|
### Step 2: 读取输出文件
|
||||||
|
|
||||||
|
脚本会在 `wiki_sync_output/<时间戳>/changed_pages/` 目录下生成:
|
||||||
|
|
||||||
|
| 文件 | 说明 | 用途 |
|
||||||
|
|------|------|------|
|
||||||
|
| `*.comparison.json` | 结构化变更信息 | **AI 读取,包含行号和变更内容** |
|
||||||
|
| `*.full.txt` | 英文最新版本 | 参考对照 |
|
||||||
|
| `*.cn.txt` | 中文原文 | **基于此文件进行修改** |
|
||||||
|
| `*.old.txt` | 英文历史版本 | 参考对照 |
|
||||||
|
|
||||||
|
|
||||||
|
### Step 3: 解析 comparison.json
|
||||||
|
`comparison.json` 格式:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"title": "页面标题",
|
||||||
|
"has_cn_translation": true,
|
||||||
|
"summary": {
|
||||||
|
"total_changes": 1,
|
||||||
|
"replaced": 1,
|
||||||
|
"added": 0,
|
||||||
|
"removed": 0
|
||||||
|
},
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"type": "replaced",
|
||||||
|
"old_line": 66,
|
||||||
|
"new_line": 66,
|
||||||
|
"old_content": "旧内容",
|
||||||
|
"new_content": "新内容"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
变更类型:
|
||||||
|
- `replaced`: 替换,`old_line` 表示需要修改的行号
|
||||||
|
- `added`: 新增,`new_line` 表示插入位置
|
||||||
|
- `removed`: 删除,`old_line` 表示要删除的行
|
||||||
|
|
||||||
|
### Step 4: 更新中文文档
|
||||||
|
|
||||||
|
**核心原则:行号必须完全一致**
|
||||||
|
|
||||||
|
1. 创建 `wiki_sync_output/<时间戳>/result_pages/` 目录(如不存在)
|
||||||
|
2. 复制 `*.cn.txt` 内容到 `result_pages/<页面名>.cn.txt`
|
||||||
|
3. 根据 `changes` 中的行号定位对应行
|
||||||
|
4. 智能更新:
|
||||||
|
- 仅同步变更的内容
|
||||||
|
- 保留中文翻译(如 "赛季 12" 不改为 "Season 12")
|
||||||
|
- 新增的英文内容智能翻译成中文,可以用`grep` 命令在`references/PatchString.txt` 搜索技能或物品的中文名称,搜索不到的则智能翻译按照暗黑破坏神2的常用命名。
|
||||||
|
- 保持 MediaWiki 语法正确,语法可以参照`mediawiki-wikitest` skill.
|
||||||
|
5. 保存到 `wiki_sync_output/<时间戳>/result_pages/<页面名>.cn.txt`
|
||||||
|
|
||||||
|
### Step 5: 输出结果
|
||||||
|
|
||||||
|
将更新后的文档保存到 `wiki_sync_output/<时间戳>/result_pages/<页面名>.cn.txt`,用户可直接复制到 Wiki。
|
||||||
|
|
||||||
|
## 示例
|
||||||
|
|
||||||
|
**输入 - comparison.json:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"changes": [{
|
||||||
|
"type": "replaced",
|
||||||
|
"old_line": 66,
|
||||||
|
"old_content": "| style=\"...\"| 2025-11-25<br>(Season 12)",
|
||||||
|
"new_content": "| style=\"...\"| 2026-01-25<br>(Season 12)"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**中文原文第66行:**
|
||||||
|
```
|
||||||
|
| style="color:#3f6e2d; background-color:#161f0c; border-color:#0d1709"| 2025-11-25<br>(赛季 12)
|
||||||
|
```
|
||||||
|
**更新后第66行:**
|
||||||
|
```
|
||||||
|
| style="color:#3f6e2d; background-color:#161f0c; border-color:#0d1709"| 2026-01-25<br>(赛季 12)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更说明:** 日期 `2025-11-25` → `2026-01-25`,但 `赛季 12` 保持中文不变。
|
||||||
|
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **行号一致性**:确保中英文文档行号完全对应,这是长期维护的基础
|
||||||
|
2. **保留翻译**:只同步变更内容,不替换已有的中文翻译
|
||||||
|
3. **MediaWiki 语法**:
|
||||||
|
- 表格分隔符 `|-` 位置保持一致
|
||||||
|
- 链接格式 `[[页面名|显示文本]]` 不变
|
||||||
|
- 样式属性 `style="..."` 不变
|
||||||
|
4. **特殊字符**:注意 `<br>`、` ` 等 HTML 实体
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
- 如果 `has_cn_translation` 为 false,提示用户该页面无中文翻译
|
||||||
|
- 如果 `is_new_page` 为 true,说明是新页面,需要全新翻译
|
||||||
|
- 如果找不到对应行,可能是中英文版本不同步,需要人工确认
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,417 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
MediaWiki Wiki 同步工具 - AI Agent 版本
|
||||||
|
输出 JSON 格式的对比文件,便于 AI Agent 读取和处理
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import difflib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
# ==================== 配置区 ====================
|
||||||
|
load_dotenv()
|
||||||
|
WIKI_API_URL_EN = os.getenv("WIKI_API_URL_EN", "https://wiki.projectdiablo2.com/w/api.php")
|
||||||
|
WIKI_API_URL_CN = os.getenv("WIKI_API_URL_CN", "https://wiki.projectdiablo2.cn/w/api.php")
|
||||||
|
OUTPUT_DIR = Path("wiki_sync_output")
|
||||||
|
OUTPUT_DIR.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
CURRENT_OUTPUT_DIR = None
|
||||||
|
LAST_TIMESTAMP_FILE = "last_sync_timestamp.txt"
|
||||||
|
|
||||||
|
SESSION_EN = requests.Session()
|
||||||
|
SESSION_EN.headers.update({
|
||||||
|
"User-Agent": "WikiSyncTool/5.0 (AI Agent Version)"
|
||||||
|
})
|
||||||
|
|
||||||
|
SESSION_CN = requests.Session()
|
||||||
|
SESSION_CN.headers.update({
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||||
|
})
|
||||||
|
SESSION_CN.trust_env = False
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
def load_last_timestamp():
|
||||||
|
if os.path.exists(LAST_TIMESTAMP_FILE):
|
||||||
|
with open(LAST_TIMESTAMP_FILE, encoding="utf-8") as f:
|
||||||
|
return f.read().strip()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def save_last_timestamp(ts):
|
||||||
|
with open(LAST_TIMESTAMP_FILE, "w", encoding="utf-8") as f:
|
||||||
|
f.write(ts)
|
||||||
|
|
||||||
|
def get_recent_changes(since):
|
||||||
|
"""获取自 since 时间后每个页面的最新 revid"""
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"list": "recentchanges",
|
||||||
|
"rcprop": "title|ids|timestamp",
|
||||||
|
"rctype": "edit|new",
|
||||||
|
"rcdir": "newer",
|
||||||
|
"rcstart": since,
|
||||||
|
"rclimit": 500,
|
||||||
|
"format": "json"
|
||||||
|
}
|
||||||
|
latest = {}
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
r = SESSION_EN.get(WIKI_API_URL_EN, params=params)
|
||||||
|
r.raise_for_status()
|
||||||
|
data = r.json()
|
||||||
|
if "error" in data:
|
||||||
|
raise Exception(data["error"])
|
||||||
|
for rc in data.get("query", {}).get("recentchanges", []):
|
||||||
|
latest[rc["title"]] = (rc["revid"], rc["timestamp"])
|
||||||
|
if "continue" not in data:
|
||||||
|
break
|
||||||
|
params.update(data["continue"])
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取最近更改时出错: {e}")
|
||||||
|
break
|
||||||
|
return latest
|
||||||
|
|
||||||
|
def get_old_revid(title, end_time):
|
||||||
|
"""获取指定时间前的最后一个 revid"""
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"prop": "revisions",
|
||||||
|
"titles": title,
|
||||||
|
"rvprop": "ids|timestamp",
|
||||||
|
"rvlimit": 1,
|
||||||
|
"rvdir": "older",
|
||||||
|
"rvstart": end_time,
|
||||||
|
"format": "json"
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
r = SESSION_EN.get(WIKI_API_URL_EN, params=params).json()
|
||||||
|
pages = r["query"]["pages"]
|
||||||
|
page = next(iter(pages.values()))
|
||||||
|
if "revisions" in page:
|
||||||
|
return page["revisions"][0]["revid"]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取旧版本ID时出错: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_page_content(wiki_url, session, title, revid=None):
|
||||||
|
"""获取页面完整内容"""
|
||||||
|
params = {
|
||||||
|
"action": "query",
|
||||||
|
"prop": "revisions",
|
||||||
|
"titles": title,
|
||||||
|
"rvprop": "content|timestamp|ids",
|
||||||
|
"rvslots": "main",
|
||||||
|
"format": "json"
|
||||||
|
}
|
||||||
|
if revid:
|
||||||
|
params["rvstartid"] = revid
|
||||||
|
params["rvendid"] = revid
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = session.get(wiki_url, params=params).json()
|
||||||
|
pages = r["query"]["pages"]
|
||||||
|
page = next(iter(pages.values()))
|
||||||
|
if "revisions" in page:
|
||||||
|
rev = page["revisions"][0]
|
||||||
|
return rev["slots"]["main"]["*"], rev["timestamp"], rev["revid"]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取页面内容时出错: {e}")
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
def generate_text_diff(old_text, new_text):
|
||||||
|
"""生成 unified diff 格式"""
|
||||||
|
if not old_text:
|
||||||
|
return "新创建页面"
|
||||||
|
|
||||||
|
old_lines = old_text.splitlines(keepends=True)
|
||||||
|
new_lines = new_text.splitlines(keepends=True)
|
||||||
|
|
||||||
|
return ''.join(difflib.unified_diff(old_lines, new_lines, lineterm='\n'))
|
||||||
|
|
||||||
|
def parse_diff_to_changes(diff_text):
|
||||||
|
"""
|
||||||
|
解析 diff 文本,提取结构化的变更信息
|
||||||
|
返回一个列表,每个元素包含:变更类型、行号、旧内容、新内容
|
||||||
|
"""
|
||||||
|
if not diff_text or diff_text.startswith("新创建页面"):
|
||||||
|
return []
|
||||||
|
|
||||||
|
changes = []
|
||||||
|
current_old_line = 0
|
||||||
|
current_new_line = 0
|
||||||
|
in_hunk = False
|
||||||
|
|
||||||
|
for line in diff_text.splitlines():
|
||||||
|
if line.startswith('@@'):
|
||||||
|
match = re.match(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@', line)
|
||||||
|
if match:
|
||||||
|
current_old_line = int(match.group(1))
|
||||||
|
current_new_line = int(match.group(3))
|
||||||
|
in_hunk = True
|
||||||
|
elif line.startswith('---') or line.startswith('+++'):
|
||||||
|
continue
|
||||||
|
elif in_hunk:
|
||||||
|
if line.startswith('-'):
|
||||||
|
changes.append({
|
||||||
|
"type": "removed",
|
||||||
|
"old_line": current_old_line,
|
||||||
|
"new_line": None,
|
||||||
|
"old_content": line[1:],
|
||||||
|
"new_content": None
|
||||||
|
})
|
||||||
|
current_old_line += 1
|
||||||
|
elif line.startswith('+'):
|
||||||
|
changes.append({
|
||||||
|
"type": "added",
|
||||||
|
"old_line": None,
|
||||||
|
"new_line": current_new_line,
|
||||||
|
"old_content": None,
|
||||||
|
"new_content": line[1:]
|
||||||
|
})
|
||||||
|
current_new_line += 1
|
||||||
|
elif line.startswith(' '):
|
||||||
|
current_old_line += 1
|
||||||
|
current_new_line += 1
|
||||||
|
|
||||||
|
return changes
|
||||||
|
|
||||||
|
def group_changes_by_line(changes):
|
||||||
|
"""
|
||||||
|
将变更按行号分组,将连续的删除和添加合并为替换操作
|
||||||
|
"""
|
||||||
|
# 先收集所有的删除和添加
|
||||||
|
removed_by_line = {} # old_line -> content
|
||||||
|
added_by_line = {} # new_line -> content
|
||||||
|
|
||||||
|
for c in changes:
|
||||||
|
if c["type"] == "removed":
|
||||||
|
removed_by_line[c["old_line"]] = c["old_content"]
|
||||||
|
elif c["type"] == "added":
|
||||||
|
added_by_line[c["new_line"]] = c["new_content"]
|
||||||
|
|
||||||
|
# 尝试将删除和添加配对
|
||||||
|
grouped = []
|
||||||
|
used_added = set()
|
||||||
|
|
||||||
|
for old_line, old_content in sorted(removed_by_line.items()):
|
||||||
|
# 找一个未使用的添加行来配对
|
||||||
|
paired = False
|
||||||
|
for new_line, new_content in sorted(added_by_line.items()):
|
||||||
|
if new_line not in used_added:
|
||||||
|
grouped.append({
|
||||||
|
"type": "replaced",
|
||||||
|
"old_line": old_line,
|
||||||
|
"new_line": new_line,
|
||||||
|
"old_content": old_content,
|
||||||
|
"new_content": new_content
|
||||||
|
})
|
||||||
|
used_added.add(new_line)
|
||||||
|
paired = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not paired:
|
||||||
|
grouped.append({
|
||||||
|
"type": "removed",
|
||||||
|
"old_line": old_line,
|
||||||
|
"new_line": None,
|
||||||
|
"old_content": old_content,
|
||||||
|
"new_content": None
|
||||||
|
})
|
||||||
|
|
||||||
|
# 添加未配对的新增行
|
||||||
|
for new_line, new_content in sorted(added_by_line.items()):
|
||||||
|
if new_line not in used_added:
|
||||||
|
grouped.append({
|
||||||
|
"type": "added",
|
||||||
|
"old_line": None,
|
||||||
|
"new_line": new_line,
|
||||||
|
"old_content": None,
|
||||||
|
"new_content": new_content
|
||||||
|
})
|
||||||
|
|
||||||
|
# 按行号排序
|
||||||
|
grouped.sort(key=lambda x: x["old_line"] or x["new_line"] or 0)
|
||||||
|
|
||||||
|
return grouped
|
||||||
|
|
||||||
|
def create_diff_json(title, en_old_content, en_new_content, cn_content):
|
||||||
|
"""
|
||||||
|
创建结构化的 JSON 对比数据(仅包含英文变更,AI自行匹配中文)
|
||||||
|
"""
|
||||||
|
# 生成英文 diff
|
||||||
|
diff_text = generate_text_diff(en_old_content, en_new_content)
|
||||||
|
|
||||||
|
# 解析变更
|
||||||
|
raw_changes = parse_diff_to_changes(diff_text)
|
||||||
|
grouped_changes = group_changes_by_line(raw_changes)
|
||||||
|
|
||||||
|
# 构建输出结构(精简版,不含中文行内容)
|
||||||
|
result = {
|
||||||
|
"title": title,
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"is_new_page": diff_text == "新创建页面",
|
||||||
|
"has_cn_translation": cn_content is not None,
|
||||||
|
"summary": {
|
||||||
|
"total_changes": len(grouped_changes),
|
||||||
|
"replaced": len([c for c in grouped_changes if c["type"] == "replaced"]),
|
||||||
|
"added": len([c for c in grouped_changes if c["type"] == "added"]),
|
||||||
|
"removed": len([c for c in grouped_changes if c["type"] == "removed"])
|
||||||
|
},
|
||||||
|
"changes": grouped_changes
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def save_files(title, diff_json, en_full_text, cn_content, timestamp, revid=None, old_full_text=None):
|
||||||
|
"""保存文件"""
|
||||||
|
global CURRENT_OUTPUT_DIR
|
||||||
|
|
||||||
|
if CURRENT_OUTPUT_DIR is None:
|
||||||
|
current_time_str = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
CURRENT_OUTPUT_DIR = OUTPUT_DIR / current_time_str
|
||||||
|
CURRENT_OUTPUT_DIR.mkdir(exist_ok=True)
|
||||||
|
(CURRENT_OUTPUT_DIR / "new_pages").mkdir(exist_ok=True)
|
||||||
|
(CURRENT_OUTPUT_DIR / "changed_pages").mkdir(exist_ok=True)
|
||||||
|
print(f"创建输出目录: {CURRENT_OUTPUT_DIR}")
|
||||||
|
|
||||||
|
safe_title = "".join(c if c.isalnum() or c in " -_." else "_" for c in title)
|
||||||
|
time_str = timestamp[:19].replace("-", "").replace(":", "").replace("T", "_")
|
||||||
|
base_filename = f"{safe_title}-{time_str}-{revid}" if revid else f"{safe_title}-{time_str}"
|
||||||
|
|
||||||
|
is_new_page = diff_json["is_new_page"]
|
||||||
|
|
||||||
|
if is_new_page:
|
||||||
|
target_dir = CURRENT_OUTPUT_DIR / "new_pages"
|
||||||
|
print(f" 检测到新页面")
|
||||||
|
|
||||||
|
# 保存英文完整内容
|
||||||
|
full_file = target_dir / f"{base_filename}.full.txt"
|
||||||
|
with open(full_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write(en_full_text)
|
||||||
|
print(f" → 已保存: {full_file.relative_to(OUTPUT_DIR)}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
target_dir = CURRENT_OUTPUT_DIR / "changed_pages"
|
||||||
|
|
||||||
|
# 保存英文完整内容
|
||||||
|
full_file = target_dir / f"{base_filename}.full.txt"
|
||||||
|
with open(full_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write(en_full_text)
|
||||||
|
print(f" → 已保存: {full_file.relative_to(OUTPUT_DIR)}")
|
||||||
|
|
||||||
|
# 保存中文内容
|
||||||
|
if cn_content:
|
||||||
|
cn_file = target_dir / f"{base_filename}.cn.txt"
|
||||||
|
with open(cn_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write(cn_content)
|
||||||
|
print(f" → 已保存: {cn_file.relative_to(OUTPUT_DIR)}")
|
||||||
|
|
||||||
|
# 保存 JSON 对比文件(核心输出)
|
||||||
|
json_file = target_dir / f"{base_filename}.comparison.json"
|
||||||
|
with open(json_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(diff_json, f, ensure_ascii=False, indent=2)
|
||||||
|
print(f" → 已保存: {json_file.relative_to(OUTPUT_DIR)} (AI Agent 对比文件)")
|
||||||
|
|
||||||
|
# 保存历史版本
|
||||||
|
if old_full_text:
|
||||||
|
old_file = target_dir / f"{base_filename}.old.txt"
|
||||||
|
with open(old_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write(old_full_text)
|
||||||
|
print(f" → 已保存: {old_file.relative_to(OUTPUT_DIR)}")
|
||||||
|
|
||||||
|
def process_single_page(title, since_time, update_timestamp=False):
|
||||||
|
"""处理单个页面"""
|
||||||
|
print(f"正在处理页面:{title}")
|
||||||
|
|
||||||
|
# 获取最新内容
|
||||||
|
latest_content, latest_ts, latest_revid = get_page_content(WIKI_API_URL_EN, SESSION_EN, title)
|
||||||
|
if latest_content is None:
|
||||||
|
print("页面不存在或被删除")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取旧版本
|
||||||
|
old_revid = get_old_revid(title, since_time)
|
||||||
|
old_content = None
|
||||||
|
|
||||||
|
if old_revid:
|
||||||
|
old_content, _, _ = get_page_content(WIKI_API_URL_EN, SESSION_EN, title, old_revid)
|
||||||
|
if old_content is None:
|
||||||
|
print(" 无法获取历史版本,视为新页面")
|
||||||
|
|
||||||
|
# 获取中文翻译
|
||||||
|
print(" 搜索中文翻译...")
|
||||||
|
cn_content = None
|
||||||
|
|
||||||
|
# 直接尝试获取同名页面
|
||||||
|
cn_result, _, _ = get_page_content(WIKI_API_URL_CN, SESSION_CN, title)
|
||||||
|
if cn_result:
|
||||||
|
cn_content = cn_result
|
||||||
|
print(f" 找到中文页面 ({len(cn_content)} 字符)")
|
||||||
|
else:
|
||||||
|
print(" 未找到中文翻译")
|
||||||
|
|
||||||
|
# 生成对比 JSON
|
||||||
|
diff_json = create_diff_json(title, old_content, latest_content, cn_content)
|
||||||
|
|
||||||
|
print(f" 变更统计: 替换={diff_json['summary']['replaced']}, "
|
||||||
|
f"新增={diff_json['summary']['added']}, 删除={diff_json['summary']['removed']}")
|
||||||
|
|
||||||
|
# 保存文件
|
||||||
|
save_files(title, diff_json, latest_content, cn_content, latest_ts, latest_revid, old_content)
|
||||||
|
|
||||||
|
if update_timestamp:
|
||||||
|
save_last_timestamp(latest_ts)
|
||||||
|
print(f"已更新时间戳 → {latest_ts}")
|
||||||
|
|
||||||
|
return latest_ts
|
||||||
|
|
||||||
|
def process_all_pages_since(since_time):
|
||||||
|
"""处理所有变更页面"""
|
||||||
|
print("正在获取最近变更列表...")
|
||||||
|
changes = get_recent_changes(since_time)
|
||||||
|
|
||||||
|
if not changes:
|
||||||
|
print("没有发现任何变更")
|
||||||
|
return
|
||||||
|
|
||||||
|
latest_global_ts = since_time
|
||||||
|
for title, (revid, ts) in changes.items():
|
||||||
|
print(f"\n处理:{title}")
|
||||||
|
page_ts = process_single_page(title, since_time)
|
||||||
|
if page_ts and page_ts > latest_global_ts:
|
||||||
|
latest_global_ts = page_ts
|
||||||
|
|
||||||
|
save_last_timestamp(latest_global_ts)
|
||||||
|
print(f"\n同步完成!最新时间戳: {latest_global_ts}")
|
||||||
|
print(f"文件保存在: {CURRENT_OUTPUT_DIR.resolve() if CURRENT_OUTPUT_DIR else OUTPUT_DIR.resolve()}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="MediaWiki 同步工具 - AI Agent 版本")
|
||||||
|
parser.add_argument("--since", type=str, help="起始时间,格式: 2025-11-28T00:00:00Z")
|
||||||
|
parser.add_argument("--title", type=str, help="只处理指定页面")
|
||||||
|
parser.add_argument("--update-timestamp", action="store_true", help="更新全局时间戳")
|
||||||
|
parser.add_argument("--run", action="store_true", help="执行同步")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not args.run:
|
||||||
|
parser.print_help()
|
||||||
|
return
|
||||||
|
|
||||||
|
since_time = args.since or load_last_timestamp()
|
||||||
|
if not since_time:
|
||||||
|
since_time = (datetime.utcnow() - timedelta(days=1)).isoformat(timespec='seconds') + "Z"
|
||||||
|
print(f"起始时间: {since_time}")
|
||||||
|
|
||||||
|
if args.title:
|
||||||
|
process_single_page(args.title.strip(), since_time, args.update_timestamp)
|
||||||
|
else:
|
||||||
|
process_all_pages_since(since_time)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
32
sync.py
32
sync.py
|
|
@ -41,8 +41,9 @@ SESSION_EN.headers.update({
|
||||||
|
|
||||||
SESSION_CN = requests.Session()
|
SESSION_CN = requests.Session()
|
||||||
SESSION_CN.headers.update({
|
SESSION_CN.headers.update({
|
||||||
"User-Agent": "WikiSyncTool/4.0 (your-email@example.com; MediaWiki Sync Bot)"
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||||
})
|
})
|
||||||
|
SESSION_CN.trust_env = False # 禁用环境变量代理
|
||||||
# ================================================
|
# ================================================
|
||||||
|
|
||||||
def load_last_timestamp():
|
def load_last_timestamp():
|
||||||
|
|
@ -264,31 +265,24 @@ def parse_diff_with_line_numbers(diff_text):
|
||||||
|
|
||||||
def search_chinese_page(title):
|
def search_chinese_page(title):
|
||||||
"""在中文wiki中搜索对应的页面"""
|
"""在中文wiki中搜索对应的页面"""
|
||||||
# 首先尝试精确匹配
|
# 首先尝试直接获取页面(因为中文wiki禁用了标题搜索)
|
||||||
params = {
|
params = {
|
||||||
"action": "query",
|
"action": "query",
|
||||||
"list": "search",
|
"prop": "revisions",
|
||||||
"srsearch": f'"{title}"',
|
"titles": title,
|
||||||
"srwhat": "title",
|
"rvprop": "ids",
|
||||||
"srlimit": 5,
|
|
||||||
"format": "json"
|
"format": "json"
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = SESSION_CN.get(WIKI_API_URL_CN, params=params).json()
|
r = SESSION_CN.get(WIKI_API_URL_CN, params=params, timeout=10)
|
||||||
search_results = r.get("query", {}).get("search", [])
|
data = r.json()
|
||||||
|
pages = data.get("query", {}).get("pages", {})
|
||||||
|
|
||||||
if search_results:
|
for page_id, page_info in pages.items():
|
||||||
# 返回第一个匹配的结果
|
# page_id 为负数表示页面不存在
|
||||||
return search_results[0]["title"]
|
if page_id != "-1" and "missing" not in page_info:
|
||||||
|
return page_info.get("title")
|
||||||
# 如果精确匹配没有结果,尝试模糊搜索
|
|
||||||
params["srsearch"] = title.replace(" ", "%20")
|
|
||||||
r = SESSION_CN.get(WIKI_API_URL_CN, params=params).json()
|
|
||||||
search_results = r.get("query", {}).get("search", [])
|
|
||||||
|
|
||||||
if search_results:
|
|
||||||
return search_results[0]["title"]
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"搜索中文页面时出错: {e}")
|
print(f"搜索中文页面时出错: {e}")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue