Wiki L'encyclopédie Moga
Advertisement
Wiki L'encyclopédie Moga
19 617
pages

Ce blog n'a pas réellement d'intérêt à être partagé et sert surtout sert à stocker mes scripts qui fonctionnent quelque part. Ils peuvent cependant être réutilisés tout ou partie, attention cependant, il est conseillé de me consulter avant sur leur fonctionnement.

AutoWikiBrowser (C#)[]

Scripts utilisés pour la modification de masse, avec un module C# et AutoWikiBrowser.

Page splitter[]

Utilisé pour séparer les pages d'objet. Si le booléen "split" est faux, le script détecte les erreurs sur les pages d'objet. Si il est vrai, il va en plus séparer et enregistrer la page sur l'ordinateur de l'utilisateur. Est issu de la fusion des scripts "error finder" et de l'ancien "page splitter".

Le contenu du fichier "Table.txt" se trouve sur la page Réorganisation des pages d'objet/Table.txt.

Page splitter
public string ProcessArticle(string ArticleText, string title, int wikiNamespace, out string Summary, out bool Skip)
{
	// true on sépare et enregistre la page, false on ne fait que vérifier les erreurs
	bool split = true;
    // Tout le contenu de la page http://fr.mogapedia.wikia.com/wiki/R%C3%A9organisation_des_pages_d%27objet#Le_tableau, et du texte additionnel
    string allTable = System.IO.File.ReadAllText(@"./Object reorganisation/Table.txt", Encoding.Default),
	text = ArticleText.Trim(),
    // title sert à sauvegarder le titre original de la page
    	ArticleTitle = title,
	foo, nName, opus, table;
	// name contient les noms trouvés dans la page. Contient uniquement les titres 3U, 4U et Gen
	string[] name = {"/", "/", "/"};
	Regex r = new Regex(@"\|Catégorie\s*=\s*Com");
	Match m;
	int pos = 0;
	Skip = false;
	Summary = "Test.";
	// Si la page n'est pas dans le nom de domaine principal on ne s'en occupe pas, de même s'il s'agit d'un composant de monstre
	if (wikiNamespace != 0 || r.Match(text).Success) {
        	Skip = true;
		return ArticleText;
	}
	// On génère un message d'erreur si le texte "{{Objet" n'est pas présent.
	if (!text.Contains("{{Objet") )
		return MsgBox("Modèle objet introuvable", "Erreur : modèle objet introuvable ou mal écrit. Texte \"{{Objet\" introuvable.", text);
	
    // On remplace "{{#tag:tabber" par "<tabber>". Attention, il restera un "}}" et pas de "</tabber>"
    r = new Regex(@"\{\{\s*#tag\s*:\s*tabber", RegexOptions.IgnoreCase);
    text = r.Replace(text, "<tabber>");
    // On remplace "{{!}}-{{!}}" par "|-|"
    r = new Regex(@"\{\{\s*!\s*\}\}\s*-\s*\{\{\s*!\s*\}\}");
    text = r.Replace(text, "|-|");
    if (text.Contains("<tabber>"))
    {
        // begin une variable qui stocke le texte présent avant le tabber
        string begin = "";
        if (text.IndexOf("<tabber>") > 0)
            begin = text.Substring(0, text.IndexOf("<tabber>"));
        r = new Regex(@"(\|-\||<tabber>)[\s\r\n]*(\w+)\s*=");
        m = r.Match(text);
	// On commence par trouver chaque titre, sans chercher d'erreur
        while (m.Success)
        {
            opus = m.Value;
            // On supprime tout ce qui n'est pas accepté en nom de fichier
            r = new Regex(@"(\|-\||<tabber>|\s|\r|\n|=)*");
            opus = r.Replace(opus, "");
            // On s'assure de découper la bonne zone, on commence par aller jusqu'en bas du texte
            pos = text.Length;
            // On remonte au tabber s'il est assez bas
            if (text.IndexOf("</tabber>", m.Index) > m.Index)
                pos = text.IndexOf("</tabber>", m.Index);
            // On remonte au "|-|"
            if (text.IndexOf("|-|", m.Index + 3) > m.Index)
                pos = text.IndexOf("|-|", m.Index + 3);
            //On supprime tout ce qui est "|-| MH3U =" par exemple
            r = new Regex(@"(\|-\||</?tabber>)([\s\n\r]*\w+\s*=)?");
            string curText = text.Substring(m.Index, pos - m.Index);
            curText = r.Replace(curText, "");
            // On trouve le nom de l'objet dans l'onglet présent
            r = new Regex(@"\|Nom\s*=\s*[^\|]+\|");
            	if (curText.Contains("{{Objet"))
            	{
                	title = r.Match(curText).Value;
            	}
            	else if (begin.Contains("{{Objet"))
                	title = r.Match(begin).Value;
            
		title = title.Replace("Nom", "").Replace("|", "").Replace("=", "").Trim();
		
		// On sauvegarde le titre pour l'opus.
		switch (opus) {
			case "MH3U":
				name[0] = title + "/MH3U";
				break;
			case "MH4U":
				name[1] = title + "/MH4U";
				break;
			case "MHGen":
				name[2] = title + "/MHGen";
				break;
			default:
				// Si l'opus est inconnu, on l'enregistre tel quel, avec un tag "{{orphan}}"
				CreatePage(title + "/" + opus, "{{orphan}}\n" + text.Substring( 0, text.IndexOf("<tabber>") ) + GetPortion(text, opus) + text.Substring( text.LastIndexOf("</tabber>"), text.Length - text.LastIndexOf("</tabber>") ).Replace("</tabber>", ""), ArticleTitle );
				break;
		}
            m = m.NextMatch();
	}
    }
    else
    {
        // Si la page ne contient pas de tabber, on vérifie l'opus par les catégories
        r = new Regex(@"\[\[Catégorie:MH\w+\s*-");
        m = r.Match(text);
        // Si on ne trouve pas d'opus, on le rajoute si l'objet n'est là qu'une fois, sinon on propose le choix entre entre opus
        if (!m.Success) {
		// On vérifie que l'objet est référencé
		if (allTable.Contains("|-| [[" + ArticleTitle + "]]") ) {
			pos = allTable.IndexOf("|-| [[" + ArticleTitle + "]]") + 2;
			table = allTable.Substring(pos, allTable.IndexOf("|-", pos) - pos );
			r = new Regex(@"\[\[[^\]/]+/MH[^\]]+\]\]");
			m = r.Match(table);
			// on trouve l'opus
			r = new Regex(@"\[\[[^/]+/|\]");
			opus = r.Replace(m.Value, "");
			text = text.Replace("[[Catégorie:Objets]]", ""); 
			// On vérifie s'il n'y a qu'un seul opus possible
			if (!m.NextMatch().Success) {
				Summary = "Ajout de la catégorie " + opus + " - Objet";
				return MsgBox("Pas d'opus !", "Etrange, l'article ne semble pas contenir de catégorie indiquant un opus. Seul l'opus " + opus + " semble convenir, la catégorie a été ajoutée.", text + "\n[[Catégorie:" + opus + " - Objet]]");
			}
			using (System.Windows.Forms.Form form = new System.Windows.Forms.Form())
			{
				using (System.Windows.Forms.Label msg = new System.Windows.Forms.Label()) {
					using (System.Windows.Forms.ComboBox comboBox = new System.Windows.Forms.ComboBox()) {
						while (m.Success) {
							comboBox.Items.Add(r.Replace(m.Value, ""));
							m = m.NextMatch();
						}
					form.Text = "Pas d'opus !";
					
					msg.Text = "Etrange, l'article ne semble pas contenir de catégorie indiquant un opus. Différents opus peuvent convenir, vous pouvez en choisir un dans la liste ci-dessous.";
					msg.Width = form.Width = 800;
					msg.Height = 100;
				
					// Régler la hauteur du texte pour apparaître plus bas
					comboBox.Top = msg.Height;
				
					// +50 sinon on s'arrêterait en haut du dernier bloc
					form.Height = comboBox.Height + msg.Height + 50;
					form.Controls.Add(msg);
					form.Controls.Add(comboBox);
					form.ShowDialog();
					text += "[[Catégorie:" + comboBox.SelectedItem + " - Objet]]";
					Summary = "Ajout de la catégorie \"" + comboBox.SelectedItem + " - Objet\"";
					}
				}
			}
		}
		return text;
	}
	
        opus = m.Value.Replace("[[Catégorie:", "").Replace("-", "").Trim();
        while (m.Success)
        {
            // Si deux opus différents sont indiqués, on ne peut rien faire. On vérifie de plus qu'il s'agit d'un opus à traiter.
            if (opus != m.Value.Replace("[[Catégorie:", "").Replace("-", "").Trim() && "MH3UMH4UMHGen".Contains(opus))
                return MsgBox("Doublon d'opus !", "Deux opus trouvés : " + opus + " et " + m.Value.Replace("[[Catégorie:", "").Replace("-", ""), text);
            
            opus = m.Value.Replace("[[Catégorie:", "").Replace("-", "").Trim();
            m = m.NextMatch();
        }
	
        // On trouve le nom de l'objet d'après le modèle objet
        r = new Regex(@"\|Nom\s*=\s*[^\|]+\|");
        title = r.Match(text).Value.Replace("Nom", "").Replace("|", "").Replace("=", "").Trim();
	// On rempli le tableau name
	switch (opus) {
		case "MH3U":
			name[0] = title + "/MH3U";
			break;
		case "MH4U":
			name[1] = title + "/MH4U";
			break;
		case "MHGen":
			name[2] = title + "/MHGen";
			break;
		default :
			// S'il ne s'agit pas d'un opus commun, on enregistre directement, avec le tag orphan
			CreatePage(title + "/" + opus, "{{orphan}}\n" + text, ArticleTitle);
			return ArticleText;
	}
    }
	// Tous les titres étant enregistrés, on vérifie les erreurs
	if (!allTable.Contains("|-| [[" + ArticleTitle + "]]")) {
		Summary = "Uniformisation";
		foo = "| [[" + ArticleTitle + "]]| [[";
	
		// On trouve le dernier opus d'apparition de l'objet
		for (int i = 2; i >= 0; i--) {
			if (name[i] != "/") {
				foo += name[i].Substring(0, name[i].IndexOf("/")) + "]]";
				break;
			}
		}
		
		// On dresse ensuite le tableau, avec ou sans crochets
		foreach (string i in name) 
			foo += "| " + (i == "/" ? i : "[[" + i + "]]");
		SelectableBox("Article inconnu", "Cet article ne figure pas dans le tableau. Vous pouvez l'ajouter avec le code figurant ci-dessous.", foo + "|-");
		return text;
	}
	// Maintenant que l'on sait que le titre est dedans, on sélectionne la partie interressante du tableau
	pos = allTable.IndexOf("|-| [[" + ArticleTitle + "]]") + 2;
	table = allTable.Substring(pos, allTable.IndexOf("|-", pos) - pos );
	
	foo = table.Substring(0, table.IndexOf("| ", table.IndexOf("| ", 3) + 1 ) );
	foreach (string i in name) {
		// Dans le cas où le nom de l'objet a été remplacé par un "(?)", on le rétablit en faisant confiance au tableau.
		if (i.Contains("(?)")) {
			opus = i.Substring( i.IndexOf("/") + 1);
			pos = table.LastIndexOf("[[", table.IndexOf(opus) ) + 2;
			nName = table.Substring(pos, table.IndexOf(opus) - pos - 1);
			Summary = "Changement de nom d'objet";
			return MsgBox("Nom suspect", "Un objet a été nommé \"(?)\". Voici la correction poposée :\n" + nName + "\nVersion visée : " + opus + "\n(attention ! différents opus peuvent avoir été impactés)", text.Replace("(?)", nName));
		}
		foo += "| " + (i == "/" ? i : "[[" + i + "]]"); 
	}
	// On vérifie que le tableau donné par la page correspond au tableau réel
	if (foo == table) {
		foo = "";
	} else {
		// On vérfie si les différences sont dues au fait que l'opus n'a pas été ajouté à la page. On va chercher sur les deux chaînes sont les mêmes sans les opus manquant
		r = new Regex(@"\| [^\|]+");
		m = r.Match(foo);
		Match m1 = r.Match(table);
		for (int i = 0; i < 5; i++) {
			if (m.Value != m1.Value && m.Value != "| /")
				// Si les deux portions sont différentes, on s'arrête
				break;
			m = m.NextMatch();
			m1 = m1.NextMatch();
		}
		if (!m.NextMatch().Success)
			// S'il n'y a plus aucun texte trouvé, donc que l'on est allé jusqu'à la fin de la boucle, c'est qu'il n'y a pas eu d'erreur
			foo = "";
	}
	
	// S'il n'y a pas eu d'erreur
	if (foo == "") {
		if (text == ArticleText) {
			// Début de l'enregistrement de la page
				if (split == true) {
					// Stocke le texte pour chaque opus
					string[] game = {"", "", ""};
					string[] supText = {"", ""};
					string[] ver = {"3U", "4U", "Gen"};
					if (text.Contains("<tabber>")) {
						supText[0] = text.Substring(0, text.IndexOf("<tabber>")).Trim();
						supText[1] = text.Substring(text.IndexOf("</tabber>") + 9).Trim();
						for (int i = 0; i < 3; i++) {
							r = new Regex("MH" + ver[i] + @"\s?=");
							if (r.Match(text).Success) {
								pos = text.IndexOf(r.Match(text).Value);
								int min = text.IndexOf("|-|", pos);
								if (text.IndexOf("</tabber>", pos) < min || min == -1)
									min = text.IndexOf("</tabber>", pos);
								game[i] = supText[0] + "\n" + text.Substring(pos, min - pos).Replace(r.Match(text).Value, "").Trim() + "\n" + supText[1];
								game[i] = game[i].Replace("</tabber>", "").Replace("|-|", "");
							}
						}
					} else
						for (int i = 0; i < 3; i++)
							if (text.Contains("[[Catégorie:MH" + ver[i] + " - Objet]]") )
								game[i] = text.Trim();
						
					// Maintenant qu'on a tout le texte, on génère la page
					CreatePages(table, game);
				}
				Skip = true;
		}
		Summary = "Uniformisation.";
		return text;
	}
	// On travaille maintenant dans le cas où il y a une erreur quelconque
	foreach (string i in name) {
		// On remplace les mots avec erreur de casse. nName pour new name (nom de l'objet après correction). 
		nName = CheckCase(table, i);
                if (nName != i)
                {
                    	r = new Regex(@"\|Nom\s*=\s*" + i.Substring(0, i.IndexOf("/") ) );
                    	Summary = "Changement de casse de nom d'objet.";
                    	text = r.Replace(text, "|Nom            = " + nName.Substring(0, nName.IndexOf("/") ) );
			return MsgBox("Changement de casse", "L'objet " + i + " est présent sous le nom " + nName + ". Appuyez sur save pour accepter son remplacement dans la page.", text);
                }
	}
	
	// On renvoie l'article avec un message d'erreur adapté
	SelectableBox("Erreur : objet(s) introuvable(s)", "Certains objets sont introuvables. Voici une version corrigée du tableau :\n\n" + table + "\n\n → \n", foo);
	return text;
}
public string CheckCase(string table, string word) {
	if (word == "/")
		return word;
	// +2 pour ne pas inclure les crochets
	int begin = table.ToLower().IndexOf("[[" + word.ToLower() + "]]") + 2;
	// Si on contient, avec changement de casse le mot, on le renvoie.
	if (begin != 1) 
		return table.Substring(begin, table.IndexOf("]]", begin) - begin);
	// Sinon on renvoie le mot.
	return word;
}
public string GetPortion(string text, string portion) {
	// Sélectionne une portion de tabber, et la renvoie. portion vaut par exemple "MHGen"
	Regex begin = new Regex(@"(<tabber>|\|-\|)[\n\s]*" + portion + @"\s*="),
	end = new Regex(@"\|-\||</tabber>");
	if (!begin.Match(text).Success) return "";
	int pos = text.IndexOf(begin.Match(text).Value);
	string sect = text.Substring(pos, text.IndexOf(end.Match(text, pos + 1).Value, pos + 1) - pos);
	return sect.Replace(begin.Match(text).Value, "").Replace(end.Match(text).Value, "");
}
public string CreateTable(string table, string[] name) {
	// On crée un tableau au cas où l'objet n'apparaît pas. D'abord on vérifie la casse, puis la présence. table est la portion de tableau intéressante.
	// table est une chaîne du type "| [[Potion]]| [[Potion]]| [[Potion/MH3U]]| [[Potion/MH4U]]| [[Potion/MHGen]] "
	string text = table.Substring(0, table.IndexOf("| ", table.IndexOf("| ", 3) + 1 ) );
	foreach (string i in name) {
		text += "| " + (i == "/" ? i : "[[" + i + "]]");
	}
	return text + "|-" + table.Substring(0, table.IndexOf("| ", table.IndexOf("| ", 3) + 1 ) );
}
public void CreatePages(string table, string[] text) {
	// S'occupe de générer toutes les pages reçues, page principale et sous-pages
	string[] sep = new string[] { "| " };
	Regex r = new Regex(@"[\n\[\]]");
	string[] names = r.Replace(table, "").Split(sep, StringSplitOptions.RemoveEmptyEntries);
	string inner = "{{Info modif objet";
	// names vaut "{"Objet (nom de la page)", "Objet (nouveau nom)", "Objet/MH3U", "Objet/MH4U", "Objet/MHGen"}"
	for (int i = 2; i < names.Length; i++) {
		if (names[i].Trim() == "/") 
			continue;
		inner += "\n| " + names[i].Substring( names[i].IndexOf("/MH") + 1 ) + " = ";
		inner += (names[i].Substring(0, names[i].IndexOf("/MH") ) == names[1]) ? "Y" : names[i].Substring(0, names[i].IndexOf("/MH") );
		
		if (text[i - 2] != "")
			CreatePage(names[i], text[ i - 2 ], names[0] );
	}
	CreatePage(names[1], inner + "\n}}");
}
public void CreatePage(string title, string text)
{
    CreatePage(title, text, "");
}
public void CreatePage(string title, string text, string origin)
{
    // On supprime toutes les catégories ressemblant à "MHx - " pour garder "ébauche"
    Regex r = new Regex(@"\[\[Catégorie:MH\w+ - .+\]\]");
    text = r.Replace(text, "").Trim();
	string ori = (origin != "") ? "\nPage d'origine : [[" + origin + "]]" : "";
    // Si les informations ont été correctement extraites de la page, on vérifie que tout est correct et on enregistre la page. On indique de plus la page d'origine
    System.IO.File.WriteAllText(@".\Object reorganisation\Pages\" + title.Replace(":", "003A").Replace("/", "002F") + ".txt", text + ori, Encoding.Unicode);
	//Après avoir créé le fichier, on indique qu'il existe en écrivant son nom dans un autre fichier (qui sera utilisé lors de la génération des pages)
	using (System.IO.StreamWriter file = 
            new System.IO.StreamWriter(@".\Object reorganisation\Pages\log.txt", true, Encoding.Unicode))
        {
            file.WriteLine(title.Trim() + "\n");
        }
}
public string MsgBox(string title, string msg, string text) {
	System.Windows.Forms.MessageBox.Show(msg, title);
	return text;
}
public void SelectableBox(string title, string text, string selectable) {
	// Crée un formulaire avec du texte pouvant être sélectionné
	using (System.Windows.Forms.Form form = new System.Windows.Forms.Form()) {
		using (System.Windows.Forms.TextBox txt = new System.Windows.Forms.TextBox()) {
			using (System.Windows.Forms.Label msg = new System.Windows.Forms.Label()) {
    				form.Text = title;
				msg.Text = text;
				msg.Width = txt.Width = form.Width = 800;
				msg.Height = 100;
				
				// Régler la hauteur du texte pour apparaître plus bas
				txt.Top = msg.Height;
				txt.Text = selectable;
				txt.Height = 300;
				
				// +50 sinon on s'arrêterait en haut du dernier bloc
				form.Height = txt.Height + msg.Height + 50;
				form.Controls.Add(msg);
				form.Controls.Add(txt);
				form.ShowDialog();
			}
		}
	}
}

Page uploader[]

Fonctionne avec le page splitter. Une fois que les pages ont bien été séparées et importées, son rôle est simplement de les mettre en ligne.

Page uploader
public string ProcessArticle(string ArticleText, string ArticleTitle, int wikiNamespace, out string Summary, out bool Skip)
        {
	Skip = false;
	Summary = "";
	string file; // Texte du fichier
	Regex r = new Regex(@"\nPage d'origine : \[\[.+\]\]");
	Match m;
	Summary = "page créée avec succès.";
	try
        	{
			file = System.IO.File.ReadAllText(@".\Object reorganisation\Pages\" + ArticleTitle.Replace("/", "002F").Replace(":", "003A") + ".txt");
			// Si la page contient "\nPage d'origine : [[Ancienne page]]", on ajoute ce texte au résumé
			m = r.Match(file);
			if (m.Success) {
				file = file.Replace(m.Value, "");
				Summary += " " + m.Value;
			}
        	    	return "{{page traitée}}\n" + file;
        	}
        catch (Exception e)
        	{
			Summary = "Erreur.";
			System.Windows.Forms.MessageBox.Show("Le fichier n'a pas pu être lu. "+ e.Message + "\n\nSi le fichier n'a pas été trouvé, la page possède peut-être une redirection.", "Erreur");
			return ArticleText; 
        	}
	}

Objects links[]

Code créé pour simplifier la génération de liens interwiki pour les objets. Ces pages n'existant que sur les wikis français et anglais, le script ne fonctionne qu'avec ces deux wikis. Il génère les liens interlangue sur le wiki anglais, et enregistre les équivalences.

Objects links / en to fr
public string ProcessArticle(string ArticleText, string ArticleTitle, int wikiNamespace, out string Summary, out bool Skip)
        {
            Skip = false;
            Summary = "";
            ArticleText = ArticleText;
		Regex re = new Regex(@"\(.+\)");
		string title = re.Replace(ArticleTitle, "").Trim();
		string opus = re.Match(ArticleTitle).Value.Replace("(", "").Replace(")", "");
		string frName;
		using (System.Windows.Forms.Form form = new System.Windows.Forms.Form()) {
			using (System.Windows.Forms.TextBox txt = new System.Windows.Forms.TextBox()) {
				using (System.Windows.Forms.Label msg = new System.Windows.Forms.Label()) {
					using (System.Windows.Forms.ComboBox cb = new System.Windows.Forms.ComboBox()) {
						using (System.Windows.Forms.CheckBox box = new System.Windows.Forms.CheckBox()) {
    							form.Text = "Choissisez un objet pour " + title;
							msg.Text = "Quel est l'objet correspondant à " + ArticleTitle + " ?";
							msg.Width = form.Width = 500;
							msg.Top = txt.Top = box.Top = cb.Top = msg.Left = txt.Left = cb.Left = box.Left = 10;
 		
							// Régler la hauteur du texte pour apparaître plus bas
							txt.Top += msg.Height;
							txt.Width = msg.Width / 2;
							txt.Height = 20;
							if (!ArticleTitle.Contains(" of "))
								txt.Text = title;
							
							cb.Top += txt.Top + txt.Height;
							foreach (string i in new string[] {"Page principale", "MH3U", "MH4U", "MHGen"})
								cb.Items.Add(i);
							cb.SelectedItem = "Page principale";
							if (opus != "Item") 
								cb.SelectedItem = opus;
							
							box.Top += cb.Height + cb.Top;
							box.Text = "Valider";					
	 
							// +50 sinon on s'arrêterait en haut du dernier bloc
							form.Height = txt.Height + msg.Height + cb.Height + box.Height + 80;
							form.Controls.Add(msg);
							form.Controls.Add(txt);
							form.Controls.Add(cb);
							form.Controls.Add(box);
							form.ShowDialog();
							
							if (!box.Checked)
								return ArticleText;							
							frName = title + ("/" + cb.SelectedItem).Replace("/Page principale", "");
							ArticleText += "\n[[fr:" + frName + "]]";
						}
					}
				}
			}
		}
		using (System.IO.StreamWriter file = new System.IO.StreamWriter(@".\Objects links\List.txt", true, Encoding.Unicode)) {
			file.WriteLine(ArticleTitle + "," + frName + "\n");
		}
		return ArticleText;
        }

JavaScript[]

Scripts lancés depuis une page internet à l'aide d'un code JavaScript.

Les composants d'armes[]

Crée un tableau pour les composants nécessaires à la création d'armes MH4U, utilisé sur MH4U : Epée longue - Liste des composants.

Récupération d'une page
if (document.querySelector('title').innerText == 'Utilisateur:Houmgaor/Sandbox - Wiki L\'encyclopédie Moga - Wikia') {
		window.addEventListener('load', function() {
            armes = [];
            var ici = document.getElementById('zonedecreation'),
                weapon = new XMLHttpRequest(),
                i = 0,
                a = 0,
                subst = '', 
                arme = '',
                description = 'Longue épée créée avec des techniques orientales. Solide, mais à entretenir.%Longue épée créée avec des techniques orientales. Solide, mais à entretenir.%Longue épée créée avec des techniques orientales. Solide, mais à entretenir.%Longue épée créée avec des techniques orientales. Solide, mais à entretenir.%Une superbe lame forgée par un voyageur venu d\'Orient qui a ensuite disparu.%Une superbe lame forgée par un voyageur venu d\'Orient qui a ensuite disparu.%Une épée de regret et d\'espoir. Son épéiste assoiffé de pouvoir a été tué par son disciple.%Une épée de regret et d\'espoir. Son épéiste assoiffé de pouvoir a été tué par son disciple.%Lieu de repos de l\'âme d\'un guerrier assoiffé de vengeance après la trahison de son élève.%Lieu de repos de l\'âme d\'un guerrier assoiffé de vengeance après la trahison de son élève.%Épée noire maudite qui envoie l\'ennemi en enfer en un clin d\'œil.%Épée noire maudite qui envoie l\'ennemi en enfer en un clin d\'œil.%Épée noire maudite qui envoie l\'ennemi en enfer en un clin d\'œil.%Épée bénie irisée qui ôte la vie de ses ennemis d\'un geste d\'une grâce infinie.%Épée bénie irisée qui ôte la vie de ses ennemis d\'un geste d\'une grâce infinie.%Épée bénie irisée qui ôte la vie de ses ennemis d\'un geste d\'une grâce infinie.%Splendide faux à base de griffe de Daimyo qui coupe cuirs et chairs.%Splendide faux à base de griffe de Daimyo qui coupe cuirs et chairs.%Splendide faux à base de griffe de Daimyo qui coupe cuirs et chairs.%Faux mortelle en coquille de Daimyo. Tire sa force d\'avancées en fabrication.%À base de la corne rouge sang du Monoblos. Ne se brise jamais deux fois.%À base de la corne rouge sang du Monoblos. Ne se brise jamais deux fois.%À base de la corne rouge sang du Monoblos. Ne se brise jamais deux fois.%Faux blanche à base de corne argentée. Elle scintille telle la nouvelle lune.%Faux blanche à base de corne argentée. Elle scintille telle la nouvelle lune.%Une longue épée forgée par des techniques orientales. Elle émet une brume argentée glaciale.%Une longue épée forgée par des techniques orientales. Elle émet une brume argentée glaciale.%Une longue épée forgée par des techniques orientales. Elle émet une brume argentée glaciale.%Une longue épée forgée par des techniques orientales. Elle émet une brume argentée glaciale.%Épée longue exotique aussi légère qu\'un flocon. Si vous observez sa lame, vous y verrez l\'hiver.%Épée longue exotique aussi légère qu\'un flocon. Si vous observez sa lame, vous y verrez l\'hiver.%Aussi noire que la nuit. Une lame supplémentaire surgit si on a du courage.%Aussi noire que la nuit. Une lame supplémentaire surgit si on a du courage.%Aussi noire que la nuit. Une lame supplémentaire surgit si on a du courage.%Quatrième des Sept Étoiles. Le destin de celui qui la voit est déjà décidé.%Longue épée de style oriental privilégiant la solidité sur le tranchant.%Longue épée de style oriental privilégiant la solidité sur le tranchant.%Longue épée de style oriental privilégiant la solidité sur le tranchant.%Lame solide dont la poignée et le fourreau sont composés exclusivement de Tetsucabra.%Lame solide dont la poignée et le fourreau sont composés exclusivement de Tetsucabra.%Épée longue qui accompagna jadis un chef respecté. Ses matériaux sont d\'une qualité exceptionnelle.%#N/A%Réplique d\'une épée conçue durant la genèse de la Guilde. Preuve de dignité.%Réplique d\'une épée conçue durant la genèse de la Guilde. Preuve de dignité.%Une longue épée forgée d\'après  d\'anciens textes trouvés dans  des ruines immergées.%Une longue épée forgée d\'après  d\'anciens textes trouvés dans  des ruines immergées.%Une longue épée forgée de deux sœurs wyvernes. Ses lames fendent les cieux.%Lame se livrant à de sombres rites après s\'être abreuvée du crépuscule vermillon.%Lame se livrant à de sombres rites après s\'être abreuvée du crépuscule vermillon.%Une longue épée forgée de deux sœurs wyvernes. Ses lames fendent les cieux.%Une longue épée forgée de deux sœurs wyvernes. Ses lames fendent les cieux.%Une longue épée forgée de deux sœurs wyvernes. Ses lames fendent les cieux.%La lame écarlate de cette épée longue libère un baptême par le feu.%La lame écarlate de cette épée longue libère un baptême par le feu.%La lame écarlate de cette épée longue libère un baptême par le feu.%Personnification de l\'esprit de combat du Brachydios. Perce et explose les proies.%Personnification de l\'esprit de combat du Brachydios. Perce et explose les proies.%Faite de Brachydios. Sa lame brûlante fait exploser et disparaître les ennemis.%Faite de Brachydios. Sa lame brûlante fait exploser et disparaître les ennemis.%Longue épée dont la lame rougeoyante embrase ce qui l\'entoure quand elle est dégainée.%Un os normal taillé pour faire une épée longue. Bonne arme d\'entraînement.%Un os normal taillé pour faire une épée longue. Bonne arme d\'entraînement.%Une longue épée en os. Facile à manier et pratique pour les améliorations en os.%Une longue épée en os. Facile à manier et pratique pour les améliorations en os.%Une longue épée en os. Sa lame polie est d\'une qualité supérieure à l\'os.%Longue épée à base de Tigrex. Ses crocs infligent des blessures irréversibles.%Longue épée à base de Tigrex. Ses crocs infligent des blessures irréversibles.%Longue épée à base de Tigrex berserk. Ses crocs forment un sourire cruel.%À base de Tigrex magma. Sa lame rouge précipitera la fin du monde.%À base de Tigrex magma. Sa lame rouge précipitera la fin du monde.%À base de Tigrex magma. Sa lame rouge précipitera la fin du monde.%Personnification de l\'esprit de combat du Brachydios. Perce et explose les proies.%Personnification de l\'esprit de combat du Brachydios. Perce et explose les proies.%Faite de Brachydios. Sa lame brûlante fait exploser et disparaître les ennemis.%Faite de Brachydios. Sa lame brûlante fait exploser et disparaître les ennemis.%Longue épée dont la lame rougeoyante embrase ce qui l\'entoure quand elle est dégainée.%L\'incarnation d\'un roi féroce. Si les dieux existent, cette épée peut les vaincre.%L\'incarnation d\'un roi féroce. Si les dieux existent, cette épée peut les vaincre.%L\'incarnation d\'un roi féroce. Si les dieux existent, cette épée peut les vaincre.%L\'incarnation d\'un roi féroce. Si les dieux existent, cette épée peut les vaincre.%Lame royale inégalée émettant une lueur pâle. Son hurlement fait trembler les dieux.%Incarnation d\'un violent empereur des enfers. Peut asservir le paradis.%Incarnation d\'un violent empereur des enfers. Peut asservir le paradis.%Cette arme démoniaque gronde en direction des cieux. En un éclair, tout est réduit en cendres.%Longue épée unique en son genre. Sa lame se confond avec le Najarala.%Longue épée unique en son genre. Sa lame se confond avec le Najarala.%Variante du modèle original doté d\'un fil plus affûté et d\'une étrange beauté.%Variante du modèle original doté d\'un fil plus affûté et d\'une étrange beauté.%Variante du modèle original doté d\'un fil plus affûté et d\'une étrange beauté.%Variante du modèle original doté d\'un fil plus affûté et d\'une étrange beauté.%Telle l\'eau d\'un raz-de-marée, cette longue épée à base de Najarala du déluge engloutit ses proies.%Telle l\'eau d\'un raz-de-marée, cette longue épée à base de Najarala du déluge engloutit ses proies.%Variante du modèle original doté d\'un fil plus affûté et d\'une étrange beauté.%Épée fabriquée avec une griffe incurvée de Kecha Wacha. Déchiquette la chair.%Épée fabriquée avec une griffe incurvée de Kecha Wacha. Déchiquette la chair.%Épée fabriquée avec une griffe incurvée de Kecha Wacha. Déchiquette la chair.%Variante puissante dotée de deux lames terribles qui pourfendent leurs proies.%Variante puissante dotée de deux lames terribles qui pourfendent leurs proies.%Variante puissante dotée de deux lames terribles qui pourfendent leurs proies.%Longue épée à base de Kecha Wacha blanc. Elle ne relâche son étreinte qu\'une fois sa proie achevée.%Longue épée à base de Kecha Wacha blanc. Elle ne relâche son étreinte qu\'une fois sa proie achevée.%On dit que l\'épéiste était seul par force, mais l\'épée était seule par perte.%On dit que l\'épéiste était seul par force, mais l\'épée était seule par perte.%A survécu à des générations de rois et triomphé d\'innombrables menaces.%A survécu à des générations de rois et triomphé d\'innombrables menaces.%A survécu à des générations de rois et triomphé d\'innombrables menaces.%Conçue d\'après les secrets des moines orientaux. Abrite l\'âme d\'un guerrier.%Conçue d\'après les secrets des moines orientaux. Abrite l\'âme d\'un guerrier.%Conçue d\'après les secrets des moines orientaux. Abrite l\'âme d\'un guerrier.%Longue épée imbue des prières mystérieuses d\'un moine oriental. L\'esprit du Rajang brûle en elle.%Épée affûtée aussi élancée qu\'une patte de Nerscylla. Furtive et mortelle.%Épée affûtée aussi élancée qu\'une patte de Nerscylla. Furtive et mortelle.%Fabriquée avec des pattes de Nerscylla. Sa lame acérée est aussi vive qu\'un baiser.%Fabriquée avec des pattes de Nerscylla. Sa lame acérée est aussi vive qu\'un baiser.%Fabriquée avec des pattes de Nerscylla. Sa lame acérée est aussi vive qu\'un baiser.%Longue épée emplie de la noirceur de la Nerscylla spectrale. La mort de sa proie sera silencieuse.%Longue épée emplie de la noirceur de la Nerscylla spectrale. La mort de sa proie sera silencieuse.%Longue épée remarquable faite en couperet. Fut très difficile à forger.%Longue épée remarquable faite en couperet. Fut très difficile à forger.%Longue épée remarquable faite en couperet. Fut très difficile à forger.%Longue épée remarquable faite en couperet. Fut très difficile à forger.%Lame offerte à un chef de guerre oriental. Le soleil ondule sur sa pointe.%Lame offerte à un chef de guerre oriental. Le soleil ondule sur sa pointe.%Lame offerte à un chef de guerre oriental. Le soleil ondule sur sa pointe.%Lame offerte à un chef de guerre oriental. Le soleil ondule sur sa pointe.%Longue épée née du souvenir d\'une wyverne qui, jadis, avait pris son envol. Très rapide.%Un fragment perdu ? Un souvenir ? Une énigme envoyée pour nous hanter ?%Un fragment perdu ? Un souvenir ? Une énigme envoyée pour nous hanter ?%Un fragment perdu ? Un souvenir ? Une énigme envoyée pour nous hanter ?%Lame vivante faite en viande et en os de Deviljho, ayant une piqûre impitoyable.%Lame de folie. Personne n\'échappe à sa soif de sang insatiable.%Épée folle qui trouble ses proies et son porteur et n\'existe que pour tuer.%Épée folle qui trouble ses proies et son porteur et n\'existe que pour tuer.%Lame de maître forgée dans un pays lointain et testée dans de nombreux combats.%Une des "trois grandes lames". Jaillit de son étui comme un requin affamé.%Une des "trois grandes lames". Jaillit de son étui comme un requin affamé.%Longue épée à base de lames de Seregios. Restaurez son tranchant en esquivant, arme dégainée.%Longue épée à base de lames de Seregios. Restaurez son tranchant en esquivant, arme dégainée.%Cette longue épée ne s\'émousse jamais. Restaurez son tranchant en esquivant, arme dégainée.%Cette longue épée ne s\'émousse jamais. Restaurez son tranchant en esquivant, arme dégainée.%Héros, que la fierté panse ton courage, réel et feint, face au serpent.%Héros, que la fierté panse ton courage, réel et feint, face au serpent.%Héros, que la fierté panse ton courage, réel et feint, face au serpent.%Lorsque le soleil parut pour la deuxième fois, les rayons du Dalamadur Shah créèrent les champs.%Lorsque le soleil parut pour la deuxième fois, les rayons du Dalamadur Shah créèrent les champs.%Cette arme en Boisdragon a hérité de ses nombreux pouvoirs. Purifie les âmes.%Cette arme en Boisdragon a hérité de ses nombreux pouvoirs. Purifie les âmes.%Épée translucide faite d\'ancien Boisdragon. Semble couper sans toucher.%Épée longue imbue du pouvoir d\'une déité noire. Qui la manie est détaché du monde.%Épée longue imbue du pouvoir d\'une déité noire. Qui la manie est détaché du monde.%Incarne le 4e pouvoir du dieu blanc, le "rugissement". Un coup sépare les océans.%Incarne le 4e pouvoir du dieu blanc, le "rugissement". Un coup sépare les océans.%Faux géante et fatale. Des blessures inguérissables torturent sur des siècles.%Faux géante et fatale. Des blessures inguérissables torturent sur des siècles.%Lame faite selon la méthode orientale. Des éclairs bleutés la recouvrent.%Lame faite selon la méthode orientale. Des éclairs bleutés la recouvrent.%Lame mystique dont l\'affinité change quand son porteur triomphe du virus de la furie.%Présent honorifique de la guilde, sa lame trancherait la queue de Lao-Shan.%Lame ancienne remise par les anciens de la guilde. Recèle l\'âme d\'un tueur de dragon.%Épée invisible de type Chameleos. Bonne pour tuer. Aval de la guilde requis.%Longue épée à base de Kushala Daora de cuivre. Elle dévorera sa proie dans le silence de l\'hiver.%Tapie dans les ténèbres, cette faucheuse monstrueuse dévore les âmes avant leur réincarnation.%Tapie dans les ténèbres, cette faucheuse monstrueuse dévore les âmes avant leur réincarnation.%Longue épée à base de Kushala Daora de cuivre. Elle dévorera sa proie dans le silence de l\'hiver.%Cette faux a l\'éclat d\'argent d\'un Fatalis ancien. Même le sang qu\'elle fait couler craint son toucher.%Longue épée usée au potentiel jamais utilisé. En la restaurant...%Longue épée usée au potentiel jamais utilisé. En la restaurant...%Lame oubliée par l\'histoire d\'une qualité inégalable de nos jours.%Lame oubliée par l\'histoire d\'une qualité inégalable de nos jours.%Lame oubliée par l\'histoire d\'une qualité inégalable de nos jours.%Longue épée glacée. La lame renferme tant de neige qu\'elle ne laisse nul vivant.%Longue épée glacée. La lame renferme tant de neige qu\'elle ne laisse nul vivant.%Longue épée glacée. La lame renferme tant de neige qu\'elle ne laisse nul vivant.%Ce sabre rouge est aussi puissant que son nom le laisse entendre. Il brûle et saigne ses victimes.%Ce sabre rouge est aussi puissant que son nom le laisse entendre. Il brûle et saigne ses victimes.%Lame pâle destinée à révéler une grande aventure à travers l\'espace.%Lame pâle destinée à révéler une grande aventure à travers l\'espace.%Nanigata à base de Dah\'ren Mohran. Un savoir-faire perdu a accru sa puissance.%Nanigata à base de Dah\'ren Mohran. Un savoir-faire perdu a accru sa puissance.%L\'avatar d\'une aile divine toute puissante. Un battement peut calmer toutes les calamités.%Longue épée mécanique créée par un artisan hardi. Possède une lame rotative.'.split('%'),
                pame = 'N/A,N/A,650,850,3650,6350,16000,30000,60000,85000,8000,12000,30000,60000,90000,130000,20000,35000,60000,85000,30000,40000,100000,90000,110000,3650,6350,30000,40000,70000,110000,35000,60000,90000,130000,1750,4900,7150,20000,30000,60000,100000,20000,35000,60000,100000,8900,20000,30000,65000,90000,130000,40000,90000,120000,16000,30000,55000,95000,130000,N/A,850,1100,1400,3100,8900,30000,40000,70000,95000,130000,16000,30000,55000,95000,130000,8900,30000,45000,90000,110000,55000,95000,120000,3650,16000,30000,45000,70000,110000,70000,95000,120000,1400,4900,7150,20000,35000,80000,60000,85000,6350,20000,55000,90000,120000,70000,85000,90000,120000,1750,6350,20000,45000,70000,90000,120000,N/A,5600,25000,85000,N/A,65000,100000,130000,N/A,40000,60000,100000,N/A,70000,85000,120000,N/A,60000,110000,N/A,60000,90000,130000,N/A,90000,100000,120000,130000,N/A,60000,120000,N/A,130000,N/A,120000,N/A,166666,N/A,120000,N/A,N/A,130000,N/A,N/A,N/A,130000,N/A,N/A,N/A,5000,55555,60000,100000,70000,80000,120000,40000,120000,,,N/A,45000,,,N/A,65000,,N/A,N/A'.split(','),
                pach = '750,750,N/A,N/A,N/A,N/A,N/A,N/A,90000,N/A,12000,N/A,N/A,N/A,N/A,N/A,30000,N/A,N/A,N/A,45000,N/A,N/A,N/A,N/A,5475,N/A,N/A,N/A,105000,N/A,52500,N/A,N/A,N/A,2625,N/A,N/A,N/A,N/A,90000,N/A,30000,N/A,N/A,N/A,13350,N/A,N/A,97500,N/A,N/A,N/A,N/A,N/A,24000,N/A,N/A,N/A,N/A,975,N/A,N/A,2100,N/A,N/A,N/A,N/A,N/A,N/A,N/A,24000,N/A,N/A,N/A,N/A,13350,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,105000,N/A,N/A,2100,N/A,N/A,N/A,N/A,N/A,90000,N/A,9525,N/A,N/A,N/A,N/A,105000,N/A,N/A,N/A,N/A,N/A,N/A,N/A,105000,N/A,N/A,6375,N/A,N/A,N/A,30000,N/A,N/A,N/A,30000,N/A,N/A,N/A,66666,N/A,N/A,N/A,45000,N/A,N/A,40000,N/A,N/A,N/A,75000,N/A,N/A,N/A,N/A,60000,N/A,N/A,99999,N/A,40000,N/A,105000,N/A,85000,N/A,120000,120000,N/A,120000,130000,120000,N/A,166666,166666,NaN,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,N/A,,,4500,N/A,,,30000,N/A,,166666,100000'.split(','),
                ame = 'N/A%N/A%Minerai de fer x5<br />Os de wyverne (P) x2%Minerai de fer x8<br />Griffe Grand Jaggi x2<br />Machalite x5%Machalite x15<br />Cristal de terre x5<br />Poche électrique x4%Dragonite x5<br />Machalite x20<br />Fulgurinsecte x5%Carbalite x10<br />Cristal de terre x10<br />Peau élastique + x3%Carbalite x15<br />Fucium x5<br />Poche de foudre x5<br />Viande albinos x1%Eltalite x15<br />Pierre de lave x6<br />Peau fine élastique + x6%Eltalite x20<br />Meldium x5<br />Poche d\'orage x6<br />Steak Khezu x2%Ecaille maculée x2<br />Ecaille Gore Magala x3<br />Aile Gore Magala x4<br />Antenne Gore Magala x3%Queue Gore Magala x2<br />Ecaille Shagaru Maga x4<br />Corne Shagaru Magala x2<br />Ecaille pure x3%Ecaille Gore Magala + x5<br />Aile Gore Magala + x3<br />Antenne Gore Magala + x3<br />Cuirasse Gore Magala x2%Ecaille Shagaru M + x3<br />Aile prisme Shaga M + x2<br />Corne Shagaru Maga + x3<br />Gemme phospho Shag M x1%Eclat Gore Magala x5<br />Eclipse Gore Magala x3<br />Capteur Gore Magala x3<br />Cortex Gore Magala x2%Eclat Shagaru Magala x3<br />Ailette Shagaru Mag x2<br />Fouet Shagaru Magala x2<br />Mante Shagaru Magala x1%Griffe Hermitaur + x4<br />Cuirasse Hermitaur x6<br />Carapace Hermit (P) x8%Griffe Hermitaur + x4<br />Scalp de wyverne x1<br />Perle noire x1<br />Cuirasse Gravios noir x6%Serre Hermitaur x2<br />Cortex Hermitaur x4<br />Griffe perforante x6%Griffe Hermit prune x3<br />Carapace Hermit prune x4<br />Perle noire parfaite x2<br />Papiluit x4%Bouillon de monstre x5<br />Corne écarlate + x3<br />Poche infernale x6%Thorax Monoblos x4<br />Cuirasse Monoblos x6<br />Corne Teostra + x1<br />Coeur Monoblos x1%Grande corne écarlate x2<br />Echine Monoblos x3<br />Cortex Monoblos x5<br />Massue Diablos x3%Grande corne d\'argent x2<br />Poche cryogène x3<br />Essence de monstre x5%Grande corne d\'argent x1<br />Echine Monoblos ivoire x5<br />Cortex Monoblos ivoire x3<br />Gemme de wyverne (G) x1%Dragonite x8<br />Cristal de lumière x5<br />Poche de givre x3%Dragonite x8<br />Ethérocite x2<br />Cristal de lumière x1<br />Huile de batracien x2%Griffe gelée Lagombi x3<br />Toison Lagombi x5<br />Poche de glace x3<br />Cuir charnu + x5%Fucium x7<br />Cristal nova x5<br />Nageoire Zamtrios + x4<br />Cuirasse Gravios noir x3%Eltalite x5<br />Toison Lagombi + x8<br />Auricule Lagombi x6<br />Poche cryogène x8%Meldium x5<br />Cristal de sérénité x3<br />Nageoire fine Zamtrios x4<br />Cortex Gravios noir x3%Corne Zinogre styg + x2<br />Alfange Nargacuga x6<br />Moelle Nargacuga x2<br />Cuiras Zinogre styg x5%Moelle Nargacuga x2<br />Rale de Tetsucabra x3<br />Serre Tetsucabra x4%Rasoir Nargacuga + x4<br />Fourrure Nargacuga x5<br />Cuir spécial Remobra x4<br />Mante Nargacuga x1%Rasoir Narga sélénite x4<br />Cristal de sérénité x4<br />Artefact de dragon x6<br />Eclat de lune brumeuse x1%Défense Tetsucabra x3<br />Griffe Tetsucabra x3<br />Crane majestueux x2%Crane majestueux x2<br />Ecaille Tetsucabra x7<br />Os de wyverne (G) x4%Griffe Tetsucabra x5<br />Huile de batracien x4<br />Pierre de feu x5%Défenses Tetsucabra + x4<br />Huile de batracien + x3<br />Carbalite x4%Cuirasse Tetsucabra x5<br />Griffe Tetsucabra + x3<br />Os résistos x3<br />Griffe Reine Seltas + x2%Rale Tetsucabra fér. x6<br />Serre Tetsucab féroce x4<br />Visage haineux x2%Cortex Tetsuca féroce x5<br />Serre Tetsucab féroce x3<br />Os massif x3<br />Serre Reine Seltas x2%Carbalite x5<br />Cristal nova x2<br />Cristal de lumière x15%Fucium x10<br />Cristal de terre x30<br />Larme Basarios x2%Cristal de sérénité x2<br />Cristal nova x10<br />Eltalite x5%Meldium x10<br />Cristal céleste x15<br />Cortex Reine Seltas x4%Ecaille Rathalos x3<br />Carapace Rathalos x2<br />Moelle Rath x4%Carapace Rathalos x4<br />Carapace Rathian x4<br />Poche infernale x2<br />Plaque Rathalos x1%Ecaille Rathalos + x8<br />Ecaille Rathian + x8<br />Rubis Rathalos x1%Lueur Rath x3<br />Cuirasse Rathalos arg x10<br />Queue Rathalos arg x3<br />Lueur Rath x4%Cortex Rathalos x5<br />Fouet Rathalos x2<br />Poche explosive x4<br />Gemme de wyverne (G) x1%Cortex Rathalos arg x5<br />Fouet Rathalos arg x2<br />Amas de feu luisant x3<br />Mante Rathalos x1%Ecaille Ratha azur + x8<br />Ecaille Rathian sak + x8<br />Rubis Rathian x1<br />Rubis Rathalos x1%Eclat Rathalos azur x5<br />Eclat Rathian sakura x8<br />Fouet Rathalos azur x2<br />Robustos monstre x6%Déchireur Rathalos x5<br />Mante Rathian x1<br />Mante Rathalos x1<br />Gros os dragon ancien x6%Carapace Brachydios x5<br />Coque noire Brachy x2<br />Poisse étrange x5%Marteau Brachydios x2<br />Cuirasse Gravios x3<br />Moelle Brachydios x1<br />Pierre de lave x4%Cuirasse Brachydios x5<br />Thorax noir Brachy x4<br />Scalp Brachydios + x2<br />Gemme Brachydios x1%Cortex Brachydios x5<br />Echine noire Brachy x4<br />Couronne Brachydios x2<br />Pallium Brachydios x1%Ogive de Brachydios x2<br />Echine noire Brachy x4<br />Poisse critique x4<br />Réacteur immortel x1%N/A%Os de wyverne (P) x5<br />Cuir Jaggi x1%Os de wyverne (M) x3<br />Cristal de terre x5<br />Cuir Grand Jaggi x3%Peau Gendrome x3<br />Cuir Grand Jaggi x4<br />Griffe Grand Jaggi x2%Croc Congalala x4<br />Griffe acérée x5<br />Os de brute x3%Croc Tigrex x5<br />Ecaille Tigrex x3<br />Griffe Tigrex x1%Croc Tigrex + x5<br />Ecaille Tigrex + x8<br />Griffe Tigrex + x2%Cuirasse Tigrex noir x5<br />Griffe Tigrex noir + x4<br />Croc Tigrex noir + x4<br />Crane Tigrex x2%Cuirasse Tigrex magma x5<br />Croc Tigrex magma + x7<br />Machoire Tigrex x1<br />Coeur palpitant x1%Croc mass Tigrex noir x5<br />Eclat Tigrex noir x8<br />Serre Tigrex noir x2%Cortex Tigrex magma x5<br />Croc massif Tigrex m x7<br />Mante Tigrex x1<br />Coeur fulminant x1%Carapace Brachydios x5<br />Coque noire Brachy x2<br />Poisse étrange x5%Marteau Brachydios x2<br />Cuirasse Gravios x3<br />Moelle Brachydios x1<br />Pierre de lave x4%Cuirasse Brachydios x5<br />Thorax noir Brachy x4<br />Scalp Brachydios + x2<br />Gemme Brachydios x1%Cortex Brachydios x5<br />Echine noire Brachy x4<br />Couronne Brachydios x2<br />Pallium Brachydios x1%Ogive de Brachydios x2<br />Echine noire Brachy x4<br />Poisse critique x4<br />Réacteur immortel x1%Corne Zinogre x1<br />Carapace Zinogre x4<br />Griffe Zinogre x6%Carap élect Zinogre + x4<br />Fourr élect Zinogre x5<br />Griffe Zinogre + x3<br />Jaspe Zinogre x1%Corne Zinogre + x2<br />Fourr élect Zinogre x4<br />Griffe Zinogre + x6<br />Saphir Lagiacrus x1%Carap volt Zinogre x4<br />Cortex Zinogre x2<br />Fouet Zinogre x1<br />Poche d\'orage x6%Vouge Zinogre x2<br />Fourr élect Zinogre + x4<br />Serre Zinogre x6<br />Célémeraude Zinogre x1%Corne Zinogre styg + x2<br />Cuiras Zinogre styg x5<br />Griffe Zinogre styg + x6<br />Pénomb Zinogre styg x1%Sternum Zinogre styg x4<br />Crinière Zinogre styg x2<br />Fouet Zinogre stygien x1<br />Artefact de dragon x6%Vouge Zinogre styg x2<br />Serre Zinogre stygien x6<br />Déchireur Deviljho x3<br />Célémer Zinogre styg x1%Carapace Najarala x4<br />Croc Narajala x2<br />Os de wyverne (G) x3%Sondeur Najarala x3<br />Croc Narajala x5<br />Moelle Najarala x1<br />Ecaille Dah\'ren Mohr x4%Cuirasse Najarala x6<br />Sondeur Najarala + x3<br />Os acéros x3<br />Poche de paralysie x4%Cuir Najarala + x8<br />Bec effroyable x5<br />Medulla Najarala x1<br />Cuirasse Gravios noir x2%Cortex Najarala x6<br />Sondeur Najarala x3<br />Robustos monstre x3<br />Poche d\'omniplégie x4%Peau de Najarala x8<br />Bec sidérant x2<br />Gemme de wyverne (G) x1<br />Cortex Gravios noir x2%Poche de déluge x4<br />Peau Najarala tigré x6<br />Jet Najarala tigré x4<br />Bec sidérant x1%Peau Najarala tigré x4<br />Bec sidérant x2<br />Gemme de wyverne (G) x1<br />Scutum R Seltas dés + x2%Cortex Najarala tig. x6<br />Jet Najarala tigré x3<br />Essence R Seltas dés x1<br />Gros os dragon ancien x5%Poche d\'eau x1<br />Serre Kecha x4<br />Peau Kecha x6<br />Queue Kecha x2%Os long Kecha x4<br />Oreille Kecha x2<br />Os de wyverne (G) x6%Queue Kecha x2<br />Os long Kecha x4<br />Ethérocite x3<br />Os jumbos x10%Oreille Kecha + x2<br />Serre Kecha x3<br />Os résistos x4<br />Poche de torrent x2%Grand os Kecha x4<br />Toison Kecha x5<br />Queue Kecha x2<br />Ecaille Zamtrios + x2%Large oreille Kecha x2<br />Déchireur Kecha x3<br />Os massif x4<br />Poche de déluge x2%Poche explosive x2<br />Déchireur Kecha blanc x4<br />Toison Kecha blanc + x6<br />Fouet Kecha blanc x2%Déchireur Kecha blanc x4<br />Toison Kecha blanc + x5<br />Fouet Kecha blanc x2<br />Eclat Rathian x4%Aile Garuga x4<br />Carapace Garuga x6<br />Queue Garuga x2%Bec aiguisé x1<br />Oreille Garuga x2<br />Crinière Garuga x4<br />Cristal nova x1%Crinière Garuga x8<br />Ecaille Garuga + x4<br />Cuirasse Garuga x8<br />Fucium x10%Cortex Garuga x4<br />Peau argentée Garuga x2<br />Fouet Garuga x2<br />Cristal de sérénité x1%Peau argentée Garuga x4<br />Eclat Garuga x4<br />Cortex Garuga x8<br />Gros os dragon ancien x4%Croc Rajang + x4<br />Peau noire Rajang x5<br />Manteau d\'or Rajang x1%Griffe Rajang + x4<br />Queue Rajang x4<br />Nerf Rajang x1<br />Scarabée rare x10%Croc lourd Rajang x5<br />Peau noire Rajang + x6<br />Vouge Rajang x2%Serre Rajang x4<br />Dard d\'or étrange x2<br />Coeur de Rajang x1<br />Sauteur empereur x10%Carapace Nerscylla x3<br />Griffe Nerscylla x3<br />Poche de somnifère x5%Carapace Nerscylla x5<br />Griffe Reine Seltas x4<br />Ethérocite x3%Cuirasse Nerscylla x5<br />Griffe Nerscylla + x3<br />Bouillon de monstre x2%Cuirasse Nerscylla x7<br />Machoire Reine Seltas x4<br />Yoldium x4<br />Crane Gravios noir x2%Serre Nerscylla x5<br />Cornicule Nerscylla x7<br />Eltalite x12%Cortex Nerscylla spec x5<br />Serre Nerscylla spec x3<br />Essence de monstre x2<br />Ecail corail Cephalos x2%Aiguil Nerscylla spec x4<br />Fouet Chameleos x2<br />Poche de coma x5<br />Carap céles Gravios n x1%N/A%Couperet bleu x5<br />Mandragore x2<br />Poche d\'eau x3<br />Cuir charnu x2%Couperet bleu x10<br />Queue acérée Zamtrios x2<br />Poche de torrent x3<br />Ecaille de requin x2%Nageoire fine Zamtrios x2<br />Nage raffi Plesi émer x3<br />Nageoire fine Cephalos x3<br />Alevin Cathangée x1%N/A%Ecaille Dah\'ren M + x5<br />Corne brutale + x5<br />Sang dragon ancien x3<br />Pénomb Zinogre styg x1%Vouge brutale x2<br />Cortex Dah\'ren Mohran x3<br />Procoxa Dah\'ren Mohr x4<br />Gemme-dragon Terre x1%Kératine Dah\'ren M x5<br />Vouge brutale x5<br />Sphère-dragon Terre x1<br />Sceau conquérant x3%N/A%Yoldium x3<br />Carbalite x10<br />Os acéros x3<br />Scarabée rare x6%Eltalite x2<br />Cristal céleste x10<br />Essence de monstre x3<br />Robustos monstre x4%Minerai cosmicite x3<br />Eltalite x10<br />Artefact de dragon x3<br />Papiluit x6%N/A%Cuir Deviljho x4<br />Croc Deviljho x5<br />Salive Deviljho x6<br />Sang dragon ancien x2%Cuir maculé x6<br />Sang noir x5<br />Gemme Deviljho x1<br />Distinction x4%Croc lourd Deviljho x5<br />Cuirébène de sang x3<br />Eclat noir Deviljho x6<br />Crochet Deviljho x1%N/A%Récompense G x3<br />Cristal nova x5<br />Sang dragon ancien x3<br />Gemme-dragon Terre x1%Massue Diablos x3<br />Vouge Diablos x1<br />Cristal de sérénité x5<br />Gemme de wyverne (G) x1%N/A%Ecarteleur Seregios x2<br />Croc Tigrex magma + x4<br />Queue Tigrex magma x1<br />Ecraseur Seregios x1%Trancheur Seregios + x6<br />Déchireur Rathalos x4<br />Aérolame Seregios + x6<br />Robustos monstre x6%Ecarteleur Seregios + x2<br />Trancheur Seregios + x6<br />Oeil Seregios x1<br />Sceau conquérant x3%N/A%Glaive Dalamadur x8<br />Lame éventail Dalama x2<br />Présage brisé x3<br />Gemme-dragon Ciel x1%Observateur Dalamadur x2<br />Serre Dalamadur x4<br />Fouet Chameleos x2<br />Gros os dragon ancien x4%Glaive Dalamad Shah + x8<br />Lame évent Dala Shah + x2<br />Acier Dalamad Shah + x4<br />Observ Dalamadur Shah x2%Acier Dalamadur + x8<br />Lame éventail Dalama + x2<br />Observ Dalamadur Shah x4<br />Sphère-dragon Ciel x1%N/A%Bois-dragon céleste x2<br />Elytre Seltas désert x4<br />Elytre Bnahabra x8<br />Poche d\'omniplégie x4%Bois dragon ancien x3<br />Mousse dragon ancien x4<br />Filet Yama Tsukami x6<br />Joyau dragon ancien x1%N/A%Javelot Akantor x3<br />Cortex Akantor x6<br />Fléau Akantor x1<br />Sphère de conquête x1%N/A%Nageoire dure Ukanlos x4<br />Griffe Ukanlos + x6<br />Machoire Ukanlos + x2<br />Pierre Ukanlos x1%N/A%Cortex Fatalis x8<br />Egide Fatalis x3<br />Oeil maléfique Fatalis x2<br />Joyau dragon ancien x2%N/A%Ticket Argosy x4<br />Eclat Lagiacrus x5<br />Foudre mortelle x3<br />Croc lourd Rajang x2%N/A%N/A%Sceau conquérant G x3<br />Serre Teostra x5%N/A%N/A%N/A%Balafreur Gogmazios x4<br />Forteresse Gogmazios x3<br />Carap céleste Gravios x1<br />Sphère-dragon Feu x1%N/A%N/A%N/A%Cristal de terre x30<br />Bouillon de monstre x5<br />Récompense G x2%Cristal de terre x70<br />Relique de dragon x5<br />Sang dragon ancien x3%Cristal céleste x10<br />Artefact de dragon x5<br />Eltalite x15<br />Cristal de sérénité x3%Cristal céleste x15<br />Pierre ardente x3<br />Minerai cosmicite x5<br />Meldium x8%Griffe Daora + x5<br />Queue Daora x3<br />Os dragon ancien x5<br />Récompense G x2%Queue Daora x2<br />Ecaille Daora x6<br />Corne Daora + x2<br />Joyau Daora x1%Fouet Daora x2<br />Ecaille épaisse Daora x6<br />Vouge Daora x2<br />Joyau dragon ancien x1%Griffe Teostra + x5<br />Queue Teostra x3<br />Sang dragon ancien x5<br />Récompense G x2%Fouet Teostra x2<br />Eclat Teostra x6<br />Vouge Teostra x2<br />Joyau dragon ancien x1%N/A%Ticket de Guilde ARG x3<br />Cuiras Rathalos azur x6<br />Medulla Rath x2<br />Ticket Studio x2%N/A%Ticket de Guilde ARG x3<br />Coxa Dah\'ren Mohr + x4<br />Pierre Dah\'ren x3<br />Gemme-dragon Ciel x1%N/A%N/A%'.replace(/Poche hurlante/g, 'Hurleur').replace(/Extrait d'albinos/g, 'Extrait Pâle').split('%'),
                ach = 'Minerai de fer x3<br />Machalite x1%Minerai de fer x3<br />Machalite x1%N/A%N/A%N/A%N/A%N/A%N/A%Eltalite x10<br />Yoldium x4<br />Peau fine élastique + x3<br />Eltalite x15%N/A%Ecaille Gore Magala x5<br />Aile Gore Magala x2<br />Antenne Gore Magala x3<br />Ecaille maculée x2%N/A%N/A%N/A%N/A%N/A%Griffe Hermitaur + x2<br />Cuirasse Hermitaur x3<br />Carapace Hermit (P) x4<br />Griffe Hermitaur + x4%N/A%N/A%N/A%Corne écarlate + x1<br />Poche infernale x3<br />Bouillon de monstre x5%N/A%N/A%N/A%N/A%Cristal de glace x10<br />Dragonite x4<br />Minerai de fer x15<br />Dragonite x8%N/A%N/A%N/A%Toison Lagombi + x5<br />Auricule Lagombi x2<br />Os massif x3<br />Eltalite x5%N/A%Alfange Nargacuga x5<br />Peau Nargacuga x3<br />Cuiras Zinogre styg x3<br />Corne Zinogre styg + x2%N/A%N/A%N/A%Défense Tetsucabra x2<br />Carapace Tetsucabra x3<br />Machalite x2<br />Défense Tetsucabra x3%N/A%N/A%N/A%N/A%Rale Tetsucabra fér. x4<br />Crème de batracien x3<br />Eltalite x4<br />Rale Tetsucabra fér. x6%N/A%Cristal nova x2<br />Cristal de lumière x10<br />Carbalite x5%N/A%N/A%N/A%Carapace Rathalos x3<br />Carapace Rathian x3<br />Poche de flamme x3<br />Ecaille Rathalos x3%N/A%N/A%Cuirasse Rathalos arg x5<br />Queue Rathalos arg x2<br />Griffe Rath x10<br />Lueur Rath x3%N/A%N/A%N/A%N/A%N/A%Coque noire Brachy x4<br />Scalp Brachydios x3<br />Queue Brachydios x3<br />Carapace Brachydios x5%N/A%N/A%N/A%N/A%Os de wyverne (P) x2<br />Cuir Jaggi x1%N/A%N/A%Peau Gendrome x2<br />Griffe Grand Jaggi x1<br />Minerai de fer x8<br />Peau Gendrome x3%N/A%N/A%N/A%N/A%N/A%N/A%N/A%Coque noire Brachy x4<br />Scalp Brachydios x3<br />Queue Brachydios x3<br />Carapace Brachydios x5%N/A%N/A%N/A%N/A%Corne Zinogre x1<br />Carapace Zinogre x3<br />Griffe Zinogre x3<br />Corne Zinogre x1%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%Cortex Najarala tig. x6<br />Jet Najarala tigré x3<br />Robustos monstre x3<br />Poche de déluge x4%N/A%N/A%Serre Kecha x2<br />Peau Kecha x5<br />Queue Kecha x1<br />Poche d\'eau x1%N/A%N/A%N/A%N/A%N/A%Oreillette Kecha blanc x2<br />Déchireur Kecha blanc x3<br />Os massif x4<br />Poche explosive x2%N/A%Carapace Garuga x4<br />Crinière Garuga x2<br />Queue Garuga x2<br />Aile Garuga x4%N/A%N/A%N/A%N/A%Croc Rajang + x3<br />Peau noire Rajang x4<br />Fucium x8<br />Croc Rajang + x4%N/A%N/A%N/A%N/A%N/A%N/A%N/A%Cortex Nerscylla x3<br />Serre Nerscylla x3<br />Peau fine élastique + x5<br />Serre Nerscylla x5%N/A%N/A%Couperet bleu x3<br />Carapace Seltas x3<br />Poissaiguiseur x5<br />Huile de batracien x2%N/A%N/A%N/A%Corne brutale x2<br />Carap Dah\'ren Mohran x3<br />Pierre Dah\'ren x3%N/A%N/A%N/A%Carbalite x2<br />Cristal de terre x10<br />Bouillon de monstre x3<br />Os de monstre + x4%N/A%N/A%N/A%Scalp Deviljho x2<br />Croc Deviljho x5<br />Cuir Deviljho x3%N/A%N/A%N/A%Ticket de Caravane x3<br />Ecaille de requin x7<br />Fucium x5%N/A%N/A%Trancheur Seregios x5<br />Broyeur Seregios x4<br />Aérolame Seregios x6<br />Os duros x4%N/A%N/A%N/A%Glaive Dalamadur x6<br />Acier Dalamadur x4<br />Plexus Dalamadur x2<br />Os vortex x6%N/A%N/A%N/A%N/A%Bois-dragon céleste x3<br />Mousse-dragon céleste x4<br />Fluide Yama Tsukami x6<br />Foudrinsecte x15%N/A%N/A%Pique Akantor x3<br />Cuirasse Akantor x6<br />Queue Akantor x1<br />Gemme Akantor x1%N/A%Queue Ukanlos x3<br />Nageoire Ukanlos + x4<br />Machoire Ukanlos x2<br />Gemme Ukanlos x1%N/A%Carapace Fatalis x7<br />Palmure Fatalis x3<br />Joyau Teostra x1%N/A%Ticket Argosy x2<br />Electricarapace + x5<br />Cuir de perle Khezu x4<br />Eltalite x10%N/A%Ecaille du chaos x3<br />Aile antinomique x3<br />Corne diamétrale x3<br />Ecaille contradictoire x1%Sceau conquérant x3<br />Gros os dragon ancien x3<br />Pierre ardente x3%N/A%Vouge Chameleos x4<br />Cuir fin Chameleos + x6<br />Meldium x6<br />Joyau dragon ancien x1%Aile maelstrom x2<br />Ecaille oxydée x6<br />Corona du pic-argent x2<br />Joyau dragon ancien x1%Lacéreur Gogmazios x4<br />Membrane Gogmazios x3<br />Barbelé Gogmazios x2<br />Huile dragon épaisse x3%N/A%Carap ardente Fatalis x8<br />Aile ardente Fatalis x4<br />Oeil démon Fatalis + x3<br />Joyau dragon ancien x1%Cortex Fatalis ancien x8<br />Egide Fatalis ancien x4<br />Oeil Fatalis ancien x3<br />Joyau dragon ancien x1%Débris ancien x1%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%N/A%Ticket Studio x3<br />Carapace Rathalos x6<br />Moelle Rath x3<br />Ticket de Guilde BRO x4%N/A%Ticket Dah\'ren Mohran x3<br />Coxa Dah\'ren Mohr x4<br />Corne brutale x4<br />Ticket de Guilde BRO x1%N/A%Mélodie de la déesse x3<br />Peau noire Rajang + x10<br />Vouge Rajang x3<br />Joyau dragon ancien x1%Ticket Instructeur x5<br />Pierre Dah\'ren x10<br />Meldium x10<br />Poche d\'orage x3%'.replace(/Poche hurlante/g, 'Hurleur').replace(/Extrait d'albinos/g, 'Extrait Pâle').split('%');
            ici.innerHTML = '<table style="text-align: center;"><caption>MH4U : Epée longue - Liste des composants</caption><thead  style="border: solid 1px black;"><tr><th>Nom de l\'arme</th><th>Prix ach</th><th>Composants pour créer :</th><th>Prix amé</th><th>Composants pour améliorer :</th><th>Description :</th></thead></tr><tbody></tbody></table><form name="resultat"><textarea name="wiki" cols="100" rows="100" onclick="this.select();"></textarea><textarea name="dep" hidden></textarea><textarea name="share" hidden></textarea><textarea name="objet" hidden></textarea></form>';
            weapon.open('GET', 'http://fr.mogapedia.wikia.com/wiki/MH4U_:_Ep%C3%A9e_longue?action=edit');
            weapon.send(null);
            /***************************************/
            // Récupération de la liste des composants de monstre MH4U
            var xhrdep = new XMLHttpRequest();
            xhrdep.open('GET', 'http://fr.mogapedia.wikia.com/wiki/Liste_d%C3%A9pe%C3%A7ages_des_monstres_(MH4U)?action=edit');
            xhrdep.addEventListener('readystatechange', function() { 
                if (xhrdep.readyState === XMLHttpRequest.DONE && xhrdep.status === 200) {
                    var hrdep = xhrdep.responseText;
                    document.resultat.dep.value = hrdep.match(/\[\[.+\]\]/g).toString();
                }
            });
            xhrdep.send(null);
            // Récupération de la liste des échanges d'objets
            var xhrshare = new XMLHttpRequest();
            xhrshare.open('GET', 'http://fr.mogapedia.wikia.com/wiki/MH4U_-_Echanges_de_Composants?action=edit');
            xhrshare.addEventListener('readystatechange', function() { 
                if (xhrshare.readyState === XMLHttpRequest.DONE && xhrshare.status === 200) {
                    var hrshare = xhrshare.responseText;
                    //Effacement de tout ce qui n'est pas nom de l'objet ou image
                    document.resultat.share.value = hrshare.match(/\[\[.+\]\]/g).toString();
                }
            });
            xhrshare.send(null);
            // Récupération de la liste des objets MH4U
            var xhrobj = new XMLHttpRequest();
            xhrobj.open('GET', 'http://fr.mogapedia.wikia.com/wiki/MH4U_Liste_des_Objets?action=edit');
            xhrobj.addEventListener('readystatechange', function() { 
                if (xhrobj.readyState === XMLHttpRequest.DONE && xhrobj.status === 200) {
                    var hrobj = xhrobj.responseText;
                    //Tri des caractères pour ne garder que les images et les noms d'objet
                    document.resultat.objet.value = hrobj.match(/Icon\s*=\s*\[\[.+\]\]|EN Name\s*=\s*\[\[.+\]\]/gi).toString().replace(/Icon\s*=\s*|EN Name\s*=\s*/g, '');
                }
            });
            xhrobj.send(null);
            /***************************************/
            weapon.addEventListener('readystatechange', function() {
                if (weapon.readyState === XMLHttpRequest.DONE && weapon.status === 200) {
                    var weap = weapon.responseText.substring(weapon.responseText.indexOf('id="wpTextbox1"'), weapon.responseText.indexOf('</textarea>'));
                    while (i < weap.lastIndexOf('{{MH4UWL|Epée Longue|')) {
                        arm = weap.substring(weap.indexOf('{{MH4UWL|Epée Longue|', i), weap.indexOf('}', weap.lastIndexOf('|', weap.indexOf('}}', weap.indexOf('{{MH4UWL|', i) ) ) ) + 1 );
                        arme = weap.substring(weap.indexOf('{{MH4UWL|Epée Longue|', i) + 21, weap.lastIndexOf('|', weap.indexOf('}}', weap.indexOf('{{MH4UWL|', i) ) ) );
                        //Ajout des icônes de composant
                        if (ach[a].length > 3) {
                            subst = ach[a].replace(/\d|<br \/>/g, '').split(' x');
                            for (var c = 0; c < subst.length - 1; c++) {
                                if (document.resultat.dep.value.includes(subst[c])) {
                                    subst[c] = document.resultat.dep.value.substring(document.resultat.dep.value.toLowerCase().lastIndexOf('file:', document.resultat.dep.value.indexOf(subst[c]) ) - 2, document.resultat.dep.value.lastIndexOf('.png', document.resultat.dep.value.indexOf(subst[c]) ) + 4 ) + '|20px]] - [[' + document.resultat.dep.value.substring(document.resultat.dep.value.lastIndexOf('[[', document.resultat.dep.value.indexOf(subst[c]) ), document.resultat.dep.value.indexOf(']]', document.resultat.dep.value.indexOf(subst[c]) ) ) + ']] x' + ach[a].match(/\d+/g)[c] + '<br />';
                                } else if (document.resultat.share.value.includes(subst[c])) {
                                    subst[c] = document.resultat.share.value.substring(document.resultat.share.value.toLowerCase().lastIndexOf('file:', document.resultat.share.value.indexOf(subst[c]) ) - 2, document.resultat.share.value.lastIndexOf('.png', document.resultat.share.value.indexOf(subst[c]) ) + 4 ) + '|20px]] - [[' + document.resultat.share.value.substring(document.resultat.share.value.lastIndexOf('[[', document.resultat.share.value.indexOf(subst[c]) ), document.resultat.share.value.indexOf(']]', document.resultat.share.value.indexOf(subst[c]) ) ) + ']] x' + ach[a].match(/\d+/g)[c] + '<br />';
                                } else if (document.resultat.objet.value.includes(subst[c])) {
                                    subst[c] = document.resultat.objet.value.substring(document.resultat.objet.value.toLowerCase().lastIndexOf('file:', document.resultat.objet.value.indexOf(subst[c]) ) - 2, document.resultat.objet.value.lastIndexOf('.png', document.resultat.objet.value.indexOf(subst[c]) ) + 4 ) + '|20px]] - [[' + document.resultat.objet.value.substring(document.resultat.objet.value.lastIndexOf('[[', document.resultat.objet.value.indexOf(subst[c]) ), document.resultat.objet.value.indexOf(']]', document.resultat.objet.value.indexOf(subst[c]) ) ) + ']] x' + ach[a].match(/\d+/g)[c] + '<br />';
                                }
                            }
                            ach[a] = subst.toString().replace(/,/g, '').replace(/<br \/>$/, '');
                        }
                        if (ame[a].length > 3) {
                            subst = ame[a].replace(/[0-9]|<br \/>/g, '').split(' x');
                            for (c = 0; c < subst.length - 1; c++) {
                                if (document.resultat.dep.value.includes(subst[c])) {
                                    subst[c] = document.resultat.dep.value.substring(document.resultat.dep.value.toLowerCase().lastIndexOf('file:', document.resultat.dep.value.indexOf(subst[c]) ) - 2, document.resultat.dep.value.lastIndexOf('.png', document.resultat.dep.value.indexOf(subst[c]) ) + 4 ) + '|20px]] - [[' + subst[c] + ']] x' + ame[a].match(/\d+/g)[c] + '<br />';
                                } else if (document.resultat.share.value.includes(subst[c])) {
                                   subst[c] = document.resultat.share.value.substring(document.resultat.share.value.toLowerCase().lastIndexOf('file:', document.resultat.share.value.indexOf(subst[c]) ) - 2, document.resultat.share.value.lastIndexOf('.png', document.resultat.share.value.indexOf(subst[c]) ) + 4 ) + '|20px]] - [[' + subst[c] + ']] x' + ame[a].match(/\d+/g)[c] + '<br />'; 
                                } else if (document.resultat.objet.value.includes(subst[c])) {
                                    subst[c] = document.resultat.objet.value.substring(document.resultat.objet.value.toLowerCase().lastIndexOf('file:', document.resultat.objet.value.indexOf(subst[c]) ) - 2, document.resultat.objet.value.lastIndexOf('.png', document.resultat.objet.value.indexOf(subst[c]) ) + 4 ) + '|20px]] - [[' + subst[c] + ']] x' + ame[a].match(/\d+/g)[c] + '<br />';
                                }
                            }
                            ame[a] = subst.toString().replace(/,/g, '').replace(/<br \/>$/, '');
                        }
                        armes.push(arme.replace(/ /g, '-').toLowerCase().replace(/[âäà]/g, 'a').replace(/[éèëê]/g, 'e').replace(/[îï]/g, 'i').replace(/'/g, '').replace(/"/g, '') );
                        ici.querySelector('tbody').innerHTML += '<tr><td>' + arme +'</td><td>' + (pach[a]+'').replace(/(\d{3})$/, ' ' + RegExp.$1) + 'z</td><td>' + ach[a] + '</td><td>' + (pame[a]+'').replace(/(\d{3})$/, ' ' + RegExp.$1) + 'z</td><td>' + ame[a] + '</td><td>' + description[a] + '</td></tr>';
                        //no est la variable du nowiki
                        no = '';
                        no2 = (no == '<nowiki>') ? '</nowiki>' : '';
                        document.resultat.wiki.value += '\n| '+ no + arm +no2+'\n| ' + pach[a] + '\n| ' + no + ach[a] + no2 + '\n| ' + pame[a] + '\n| ' + no  + ame[a] + no2 + '\n| ' + description[a] + '\n|-';
                        i = weap.indexOf('{{MH4UWL|Epée Longue|', i+1);
                        a++;
                    }
                        console.log(armes.toString());
                }
            });
		});
		}

Génération de la liste des monstres[]

Un vieux script personnel, que j'avais utilisé pour mettre à jour la liste des monstres. Ce script ne devrait plus être utilisé tel quel, mais peut être utile un jour. Page originale : MediaWiki:Liste des Monstres/script.js.

Génération de la liste des monstres
/*  Attention! En cas d'activation, ce script sera chargé par tous les visiteurs, sur la page 'A effacer' ce qui nuira fortement à la fluidité de leur navigateur, merci de désactiver ce script après utilisation 
    
    Cette page est utile à la modification de la page Liste des Monstres,
ce script n'est à utiliser qu'en  dernier recours, il permet de récupérer tous les noms de monstre présents sur la page [[Liste des Monstres]], puis d'aller chercher les informations disponibles sur les pages desdits monstres.

Auteur: Houmgaor;
Failles constatées: 
    *ne fonctionne pas bien avec les monstres dont les infobox sont linéaires (pas de retour à la ligne)
    *ne fonctionne pas avec les pages qui n'ont pas le modèle {{Monster Infobox}}
Activation: créez une page 'A effacer' sur le wikia, écrivez dedans ce texte '<div id="ici"></div>', puis dans le code ci-dessous, supprimez le '/ *' (ligne 16) et le '* /' (ligne 70), publiez, puis pressez le bouton 'Activer le mode test'. Retournez ensuite sur la page 'A effacer', un message doit vous confirmer que vous êtes bien en mode test. Attendez environ 3 minutes et du texte va s'afficher dans la zone prévue à cet effet. Copiez/collez ce texte et vérifiez qu'il n'y a pas de bug! Puis n'oubliez pas de remettre les sécurités que vous aviez supprimé ( les '/ *' et '* /' (les deux caractères '*' et '/' doivent être collés) ), le texte doit alors redevenir gris, si des erreurs s'affichent, alors vous avez mal écris; 
*/

// Le scipt commence à partir du 'if'  


if (document.querySelector('title').innerText == 'A effacer - Wiki L\'encyclopédie Moga - Wikia') {
		window.addEventListener('load', function() {
		var ici = document.getElementById('ici');
		ici.innerHTML = '<form name="entre"><textarea name="entre" rows="5" cols="80" style="visibility: hidden;"></textarea></form><form name="res"><textarea cols="80" rows="100" name="exit" onclick="select()"></textarea></form>';
		var input = new XMLHttpRequest();
		input.open('GET', 'http://fr.mogapedia.wikia.com/wiki/Liste_des_Monstres?action=edit');
		input.send(null);
		input.addEventListener('readystatechange', function() {
		if (input.readyState === XMLHttpRequest.DONE && input.status === 200) {
            document.entre.entre.value = input.responseText.substring(input.responseText.indexOf('id="wpTextbox1"'), input.responseText.indexOf('id="CategorySelect"'));
            var entre = document.entre.entre.value,
                monster = {value: ''},
                exit = document.res.exit,
                a = 0,
                b = 0,
                ended = 0,
                myArray = [''];
                run = function() {
				if (a < entre.lastIndexOf('|}') && ended < 2) {
					a = (~entre.indexOf('[[', a)) ? entre.indexOf('[[', a)+2 : entre.lastIndexOf('|}');
					b = entre.indexOf(']]', a);
					if (entre.substring(a, b).length > 30 || /&|[0-9]|Poisson|variante|’|File:|Image:|Shakalaka|Toundra|Plaine|Montagne|Mer de glace|Pics brumeux|forêt et collines|forêt ancestrale|Second Gen|MH|Vallon|Vallée|Canyon|Venteuse|Steppe|Marais|Jungle|Forêt|Château|Toundra|Sommet Tour|Dunes|Frontières Jurassiques|Unknown|Caeserber Fantôme|Highland|Solitude|Mountain|Snowy/i.test(entre.substring(a, b))) {
                        run();
					}
					if (/Apceros|Teppeki Shen Goaren/.test(entre.substring(a, b))) {
                        ended++;
					}
					var article = new XMLHttpRequest();
					article.open('GET', 'http://fr.mogapedia.wikia.com/wiki/' + encodeURIComponent(entre.substring(a, b)) + '?action=edit', false);
					article.send(null);
							if (article.readyState === XMLHttpRequest.DONE && article.status === 200) {
								myArray = entre.substring(a, Math.min(entre.indexOf('|-', b), entre.indexOf('|}', b) ) ).split('| ');
								monster.value = article.responseText.substring(article.responseText.indexOf('id="wpTextbox1"'), article.responseText.indexOf('id="CategorySelect"'));
								myArray[1] = (monster.value.includes('|Nom Japonais')) ? monster.value.substring(monster.value.indexOf('= ', monster.value.indexOf('|Nom Japonais'))+1, monster.value.indexOf('|', monster.value.indexOf('|Nom Japonais') + 1)).trim() : '/' ;
								myArray[1] = (monster.value.includes('|Nom Chinois')) ? myArray[1] + '<br />' + monster.value.substring(monster.value.indexOf('= ', monster.value.indexOf('|Nom Chinois') + 1)+1, monster.value.indexOf('|', monster.value.indexOf('|Nom Chinois') + 1)).trim() : ( (myArray[1] == '/') ? '/' : myArray[1] ) ;
								myArray[2] = (monster.value.includes('|Titre Japonais')) ? monster.value.substring(monster.value.indexOf('= ', monster.value.indexOf('|Titre Japonais') + 1)+1, monster.value.indexOf('|', monster.value.indexOf('|Titre Japonais') + 1)).trim() : '/' ;
								myArray[2] = (monster.value.includes('|Titre Chinois')) ? myArray[2] + '<br />' + monster.value.substring(monster.value.indexOf('= ', monster.value.indexOf('|Titre Chinois') )+1, monster.value.indexOf('|', monster.value.indexOf('|Titre Chinois') + 1)).trim() : ( (myArray[2] == '/') ? '/' : myArray[2] ) ;
								myArray[3] = monster.value.substring(monster.value.indexOf('=', monster.value.indexOf('|Elément')) + 1, monster.value.indexOf('\n', monster.value.indexOf('|Elément') + 1)).trim();
								myArray[4] = monster.value.substring(monster.value.indexOf('=', monster.value.indexOf('|Statut')) + 1, monster.value.indexOf('\n', monster.value.indexOf('|Statut') + 1)).trim();
								myArray[5] = monster.value.substring(monster.value.indexOf('=', monster.value.indexOf('|Faible contre')) + 1, monster.value.indexOf('\n', monster.value.indexOf('|Faible contre') + 1)).trim();
								entre = entre.substring(0, a) + myArray[0] + '| ' + myArray[1] + '\n| ' + myArray[2] + '\n| ' + myArray[3] + '\n| ' + myArray[4] + '\n| '+ myArray[5] + '\n' + entre.substring(Math.min(entre.indexOf('|-', b), entre.indexOf('|}', b) ), entre.length);
                                run();
							} 
                    } else {
                            exit.value = entre.replace(/&lt;/g, '<');
                            alert('Fini!');
                            a = Number.POSITIVE_INFINITY;
                            setTimeout(run, 1000000);
                            }
				};
                run();
			}});
		});
		}

Exemples Lua[]

Exemples de scripts pouvant être utilisés avec des modules en Lua.

Récupération d'une page[]

Récupère tout le texte d'une page et le renvoi.

Récupération d'une page
local p = {}

function p.main(a)
    local titleobject = mw.title.new( 'Rathian' )
    --Doesn't use a colon
    local existspage = titleobject.exists
    local pagecontent
    if existspage then
        -- Method uses a colon
        pagecontent = titleobject:getContent()
    end
    return pagecontent
end
    
return p

Python[]

Scripts utilisés avec un client Python (3.6), et l'API de MediaWiki.

Réorganisation des composants de monstre[]

Enregistre et vérifie les pages de composant de monstre du wiki. Utilise des bases de données sous format texte et XML, tout en UTF-8.

component_converter.py
import xml.etree.ElementTree as ET
import re
from media_moga import moga_session, parse_template
import webbrowser as wb
from os import system
from time import sleep

games = ('MH3U', 'MH4U', 'MHGen')
m = moga_session()
url = 'https://mogapedia.fandom.com/fr/wiki/'

def say(s):
    """ Reads the string """
    system('spd-say "' + str(s).replace('"', "'") + '" -l fr -r -50')

def begin():
    """ Create the xml file with previous name, new name, name for MH3U, 4U
    and Gen for each component."""
    f = {'xml': ET.parse('Cdatabase.xml')}

    for i in games:
        f[i] = open('k' + i + '.txt', 'r')

    f['xml']

def component(obj):
    """ Returns 0 if the object is a not component, 1 if it carries category
    or component's template and 2 if it carries both. """
    if type(obj) == str:
        obj = {'title': obj}
    if not m.exist(obj['title']):
        print('Page inexistante')
        return 0
    else:
        obj['exist'] = True
    if m.is_redirect(obj['title'])[0][1]:
        return 0
    if not 'text' in obj:
        obj['title'], obj['text'] = m.get_text(obj['title'])[0]
        obj['cat'] = m.get_categories(obj['title'])[0][1]

    if '/Dépeçages' in obj['title']:
        return 0
    if re.compile('\{\{\s*Info modif objet').search(obj['text']) or re.search(
    "/MH\S*", obj['title']):
        print(obj['title'] + ' est une page principale')
        return 0
    if re.search('\{\{\s*page traitée\s*\}\}', obj['text'], re.I):
        print(obj['title'] + " : ne pas traiter")
        return 0
    marker = 0
    p = re.compile('\{\{\s*Objet[^\}]*\|\s*Catégorie\s*=\s*[Cc]om')
    if p.search(obj['text']):
        marker += 1
    p = re.compile('Catégorie:(MH\S{,10} - )?Composant de monstre')
    if obj['cat']:
        for i in obj['cat']:
            if p.search(i['title']):
                marker += 1
                break
    if marker == 1:
        print("Impossible d'évaluer la pertinence de la catégorie de "
        "{}".format(obj['title']))
    return marker

def proceed_components(maxi=5000, correct=False, publish=False):
    """ List components and check their conformity. Corrige les noms si
    l'argument correct est vrai, publie les sous-pages si l'argument publish
    est vrai et qu'il n'y a pas d'erreurs."""
    print("Récupération des fichiers...")
    # HACK : à effacer (débogage)
    global data
    files = [open(i, 'r', encoding='UTF-8') for i in [
    'k' + j + '.txt' for j in games]]
    data = [i.read() for i in files]
    data.insert(0, '\n'.join(data))
    for i in files:
        i.close()
    #print("Résultat :", replace_and_find("Cortex Tigrex", data[3]))
    if correct and 'anon' in m.logged():
        m.login()

    print("Obtention de la liste des pages...")
    # We create the dict of pages with name, text and categories
    pages = {}
    for i in m.get_pages_with_category_recursive(
    'Catégorie:Composant de monstre', maxi, 'page', n=3):
        pages[i['title']] = {
            'title': i['title'],
            'exist': True,
        }
    #Then categories, we ask for all category in once because requests are
    # grouped, so it is faster
    print('Obtention des catégories... ')
    foo = m.get_categories(list(pages.keys()))
    for i in foo:
        pages[i[0]]['cat'] = i[1]

    print('Début du traitement des informations...')
    for i in sorted(pages, key=lambda x: x.lower()):
        if i.lower() <= "Fluide éclatant".lower():
            continue
        title = i
        # We add text to the page
        text = pages[i]['text'] = m.get_text(title)[0][1]
        if component(pages[i]) == 2:
            print(title)
            # First we check if it exists, then the individual files
            for j, datum in enumerate(data):
                s = j
                # Checks alternative case
                esc = re.escape(title)
                r = re.compile('^(' + esc +')$', re.M|re.I)
                rep = replace_and_find(title, data[0])
                # If the component page is properly entitled
                if title in datum:
                    # Then we proceed text, if asked
                    split = split_page(pages[i])
                    # HACK : condition sur j pour ne faire ça qu'une fois par tour
                    if correct and not j:
                        correct_page(pages[i], split, data)
                    # If the component appears in a specific version
                    # HACK : on n'agit qu'au dernier tour de datum
                    if j == max((games.index(i) for i in split[0])) + 1:
                        r_titles = set()
                        for k, v in enumerate(data[1:]):
                            if r.search(v):
                                r_titles.update(set([(r.search(v).group(1),
                                                          games[k])]))
                            for l in replace_and_find(title, v):
                                r_titles.update(set([(l, games[k])]))
                            for l in re.findall(esc.replace(' ', ' ?'), v):
                                r_titles.update(set([(l, games[k])]))
                        # HACK : à corriger
                        #if len(r_titles) < len(split[0]):
                        #    continue
                        for l in split[0]:
                            if l not in games:
                                # On ajoute les titres pour les objets
                                # pas dans 3U, 4U et Gen
                                foo = re.search("\|\s*Nom\s*=(.+)\s*",
                                                split[0][l]).group(1).strip()
                                print("Données manquantes pour l'objet",
                                      foo, "dans " + l)
                                if input("Confirmer ce titre ? (oui/non) \
") == "oui":
                                    r_titles.update(set([(foo, l)]))
                            else:
                                # On cherche les titres liés dans la page
                                r = re.compile("\|\s*Nom\s*=(.+)\s*")
                                for k in games:
                                    if k not in split[0]:
                                        continue
                                    if not r.search(split[0][k]):
                                        continue
                                    game_title = r.search(split[0][k]
                                    ).group(1).strip()
                                    if k in (i[1] for i in r_titles):
                                        if game_title != next(filter(lambda x: x[1] == k, r_titles))[0]:
                                            say("Noms incompatibles")
                                            raise Exception ("Erreur : \
aberration de noms " + game_title + " != \
" + next(filter(lambda x: x[1] == k, r_titles))[0])
                                    elif "\n" + game_title + "\n" in data[games.index(k)+1]:
                                             r_titles.update(set([(game_title, k)]))
                        if len(r_titles) > 1:
                            print(title + ' lié à ' + str(r_titles))
                        save_c(title, j - 1, r_titles = r_titles)
                        if publish:
                            send_pages(title, split, r_titles)
                    s += 1
                elif title in data[-1]:
                    # We do not change component's name if it has the last
                    # opus name
                    pass
                elif r.search(datum):
                    n_title = r.search(datum).group(1)
                    if len(n_title) > 100:
                        print("Problème avec " + title + ' : '
                        + n_title[:100] + '...')
                        return
                    if j < 2 and title not in data[2] or j >= 2:
                        print(title + ' nommé ' + n_title + (' ('
                        + games[j - 1] + ')' if j else ''))
                        say(title + " dans " + games[j - 1])
                        if change_title(title, n_title):
                            break
                    s += 1
                elif len(replace_and_find(title, datum)):
                    if (len(rep) == 1
                        and rep[0] != title
                        and input('Valider le titre ' + rep[0] + ' ? ') == 'oui'):
                        for k in range(3, 0, -1):
                            if len(replace_and_find(title, data[k])):
                                print(title + ' nommé ' + rep[0] + (' ('
                                + games[k - 1] + ')' if j else '' ))
                                n_title = rep[0]
                                say("Meilleur nom trouvé pour le composant.")
                                break
                    else:
                        print(title + " correspond à  de multiples titres : ")
                        for k, v in enumerate(rep):
                            print("{} : {}".format(k, v))
                        say("C'est ambigüe")
                        n_title = 'voir'
                        while n_title == 'voir':
                            n_title = input("Votre choix ? (nombre/"
                            "voir/passer/autre) ")
                            if n_title == "voir":
                                wb.open(url + title)
                            elif n_title == "passer":
                                n_title = title
                            elif n_title.isdecimal() and n_title not in rep:
                                if 0 <= int(n_title) < len(rep):
                                    n_title = rep[int(n_title)]
                                else:
                                    print("Nombre invalide : " + n_title)
                                    n_title = 'voir'
                            elif n_title.isdecimal() and n_title in rep:
                                if input("Entrée ambigüe, s'agit-il du titre"
                                " ? (oui/non)") != 'oui':
                                    if 0 <= int(n_title) < len(rep):
                                        n_title = rep[int(n_title)]
                                    else:
                                        print("Nombre invalide : " + n_title)
                                        n_title = 'voir'
                            elif n_title == 'autre':
                                n_title = input('Nouveau titre (non pour '
                                'annuler)')
                                if n_title == 'non':
                                    n_title = "voir"
                            elif n_title not in rep:
                                print("Réponse non reconnue")
                                n_title = 'voir'
                    if change_title(title, n_title):
                        break
                    s += 1
                elif re.search(esc.replace(' ', ' ?'), datum):
                    n_title = re.search("(" + esc.replace(' ',' ?') + ")",
                                        datum).group(1)
                    print(title + ' nommé ' + n_title )
                    say("bip")
                    if change_title(title, n_title):
                        break
                elif title.replace(' ', '') in datum.replace(' ', ''):
                    say("Problème d'espacement.")
                    print(title + " : problème d'espacement")
                    if input("Changer de nom ? (oui/non) ") == 'oui':
                        if change_title(title):
                            break
                    s += 1
                # s == 0 only if it is the first turn with all the data
                if not s:
                    # HACK: solution temporaire, comportement indésirable sur
                    # HACK: le long terme
                    if 'MHW' in text or 'MHST' in text or 'Frontier' in text:
                        print(title + " : non traité \
(objet " + ("MHW" if "MHW" in text else "MH(ST|Frontier)") + ")")
                        break
                    print(title + ' : nom introuvable, ', end='')
                    say("Ce nom est inconnu.")
                    l = re.search('^' + '[\w\.]*\s*'.join([
                    re.escape(k[:3]) for k in title.split(' ')]) + '[\w\.]*$'
                    , datum, re.I|re.M)
                    if l:
                        if change_title(title, l.group(0)):
                            break
                        print('Changer', end='')
                    else:
                        print('changer', end='')
                    if input(' de nom ? (oui/non) ') == 'oui':
                        change_title(title)
                    break

def encode(s):
    r = s
    for i in ((' ', '0020'), ("'", '0027'), ('+', '002B'), ('.', '002E'),
              ('(', '0028'), (')', '0029') ):
                  r = r.replace(*i)
    return r


def save_c(title, game, comp=None, r_titles=set()):
    """ Save in the XML file the component and his game, unless it has already
    been registered. If the tag is not found in the root, then it will search
    for it in each root. If it is find, the program can adapt names.
    - title : name of the component
    - game : from 0 (MH3U) to 2 (MHGen)
    - r_tiles : a list of all other possible tiltes. Useful for research"""
    t = ET.parse('Cdatabase.xml')
    root = t.getroot()

    if game == - 1: print("game == -1, est-ce normal ?")
    ver = games[game]
    if comp is None:
        # First we look for the object in each node
        for obj in root:
            for i in obj:
                if i.text == title or i.text in (x[0] for x in r_titles):
                    # If we have found the object
                    if ver in obj and obj.find(ver).text != title:
                        print("Aberration de noms :\n"
                        "- Ancien : " + obj.find(ver).text +
                        "\n- Nouveau : " + title + "\n pour " + ver)
                    else:
                        save_c(title, game, obj, r_titles)
                    return t.write('Cdatabase.xml', 'UTF-8', True)

        # If nothing was find, we add the object
        obj = ET.SubElement(root, 'page')

        for i in ('prev', 'new'):
            j = ET.SubElement(obj, i)
            coco = (min if i=='prev' else max)(filter(lambda x:x[1] in games,
                   r_titles),
                   key=lambda x:x[1])
            j.text = coco[0]
            j.set('game', coco[1])
        for r_tit, r_ver in filter(lambda x:x[1] in games, r_titles):
            coco = ET.SubElement(obj, r_ver)
            coco.text = r_tit
        t.write('Cdatabase.xml', 'UTF-8', True)
    else:
        # on modifie comp, il n'y a rien à renvoyer
        for r_tit, r_ver in filter(lambda x:x[1] in games, r_titles):
            # If the new name of the object is overdated
            if comp.find('new').attrib['game'] < r_ver:
                comp.find('new').set('game', r_ver)
            if comp.find('prev').attrib['game'] > r_ver:
                comp.find('prev').set('game', r_ver)
            if comp.find(r_ver) is None:
                coco = ET.Element(r_ver)
                coco.text = r_tit
                # If we can insert an element at a specific location
                if len(comp) - 2 > games.index(r_ver):
                    comp.insert(games.index(r_ver) + 2, coco)
                else:
                    comp.append(coco)

def replace_and_find(string, data, rec=False, n=1):
    """ Essaie de plusieurs remplacements dans la chaîne pour trouver le même
    mot dans les données. Peut faire jusqu'à quatres remplacements en même
    temps.

    Par défaut, on renvoie autant de remplaçants sérieux que l'on trouve. La
    méthode est d'utilisé un certain nombre de remplacements usuels, par
    exemple : œ et oe, puis on exploite les abbréviations de mots courrament
    utilisées dans le jeu, mots tronqués, suivis ou non d'un point"""


    if "\n" + string + "\n" in data:
        return [string]
    l = []
    if not rec:
        # Tries specific replacements
        patterns = [('E', 'É'), ('+', ' +'), ('oe', 'œ'), ('Oe', 'Œ'),
                    ('de ', ''), ('des ', '')]
        pat = tuple(filter(lambda x: x[0] in string, patterns))
        # we reverse all the patterns
        pat += tuple((i[::-1] for i in pat))
        pat = (('', ''),) + pat
        # On s'occupe d'abord des remplacements considérés fiables
        for i in range(len(pat)):
            # We append 0 in order to do no replacement
            for j in (('', ''),) + pat[i:]:
                s = string.replace(*pat[i]).replace(*j)
                for k in replace_and_find(s, data, True):
                    if k not in l:
                        l.append(k)

    initials = re.findall(r"\w+\.?|\+", string)

    if n <= max(map(len, initials)):
        s = "^"
        for i in range(len(initials)):
            s += re.escape(initials[i][0])
            for j in range(1, min(n + 1, len(initials[i]))):
                if j == 1:
                    s += "(?:" + re.escape(initials[i][j])
                else:
                    s += "(?:" + re.escape(initials[i][j])
            s += (")?" * min(n, len(initials[i]) - 1))
            s += r"\w{0," + str(len(initials[i]) - n - 1) + r"}(?:\.\s?|\s)"
        s += "?$"
        # Tous les caractères sont optionnels sauf le premier
        #s = "^" + r"\w*(?:\.\s?|\s)".join(
        #        (re.sub("(?<=.)(.)", r"(?=(?:\1|\.))?", re.escape(j[:n])) for j in initials))
        #s += r"\w*\.?$"
        print("s :", s)
        # On effectue une recherche par début de mot
        m = [j.strip() for j in re.findall(s, data, re.I|re.M)]
        print("m :", m)
        for j in m:
            pat = r"\w*\s".join(re.findall(r"\w+", j)) + r"\w*"
            print("j :", j, "pattern : ", pat, "search :",
                  bool(re.search(pat, string, re.I)),
                  " join :", r"\w*\s?".join(re.findall(r"\w+", j)) + r"\w*")
            if not re.search(pat, string, re.I):
                print("Suppression : ", j)
                m.remove(j)
        print("Restant : ", m)
        if 0 < len(m):
            step = replace_and_find(string, data, True, n + 1)
            print("step :", step)
            if step:
                m = step
            elif n < 3:
                return []
        if rec:
            return m
        else:
            l.extend([j for j in m if j not in l])
    print("Final :", l)
    return l

def split_page(page) :
    """ Returns a tuple containig three elements :
            * a dict of subpages in an article.
            * the previous text that does not belong to any section
            * the appendix text that does not belong to any section
    The function plits the page using a tabber. If the tabber doesnt exist,
    uses categories on page.

    The dict keys are game names where values are the text specific to a game.
    Could be empty.

    Le text non contenu dans les tabbers est renvoyé dans les arguments 1 et
    2 du tuple de sortie
    """
    if re.search(r'<tabber\s*>', page['text'], re.M):
        # If there is a tabber
        r = re.compile(r'</?tabber>|\|-\|')
        l = r.split(page['text'])
        r = re.compile(r'\s*(MH[\w\d]+)\s*=\s*(.+)\s*', re.S)
        # Parts which sould be kept
        rep = re.compile(r'^\s*(\{\{\s*Objet[^\}]+\}\})\s*$', re.S|re.I|re.M)
        out = {}
        # Texte à ajouter au début et à la fin
        pre_text, app_text = '', ''
        for i in l:
            # HACK : solution temporaire
            if rep.search(i) and not i.replace(rep.search(i).group(1), "").strip():
                pre_text += rep.search(i).group(1) + '\n'
                if '[[Catégorie:Ébauche]]' in page['text']:
                    app_text += '\n[[Catégorie:Ébauche]]'
            elif r.search(i):
                out[r.search(i).group(1)] = r.search(i).group(2)
            elif '[[Catégorie:' in i or not i.strip():
                continue
            else :
                print("Texte inclassable : " + encode(i))
        return out, pre_text, app_text
    else:
        if 'cat' not in page :
            page['cat'] = m.get_categories(page['title'])[0][1]
        r = re.compile(r'(MH[\w\d]{,10}) - (?:Objet|Composant de monstre)')
        game = ''
        for i in page['cat']:
            if r.search(i['title']):
                if game == '':
                    game = r.search(i['title']).group(1)
                elif game != r.search(i['title']).group(1) :
                    say("Versions multiples.")
                    print('Deux versions présentes : %s et %s' %(game,
                                            r.search(i['title']).group(1)))
                    if input('Continuer (oui/non) ? ') != 'oui':
                        return {}
        if game == '':
            print(page['title'] + ' version inidentifiable')
            say(page['title'] + '. bip bip !')
        return {game: page['text']}, '', ''

def change_title(title, n_title=''):
    """ Move the page from "title" to "n_title" with a textual interface.
    Return True if name was changed, False if not. """
    if n_title == '':
        n_title = input('Nouveau nom : ')
    if m.exist(n_title)[0][1] and not m.is_redirect(n_title)[0][1]:
        print("La page " + n_title + " existe déjà !")
    egg = ''
    while egg != 'oui' and egg != 'non':
        egg = input("Renommer ? (oui/non/voir/modifier/autre nom) ")
        if egg == 'oui':
            m.move_page(title, n_title, "Mise à jour des noms de composant. \
Renommage approuvé par [[Utilisateur:Houmgaor|Houmgaor]].")
            sleep(5)
            return True
        elif egg == 'voir':
            wb.open(url + title)
        elif egg == 'modifier':
            wb.open(url + title + '?action=edit')
        elif egg == 'autre nom':
            return change_title(title)
    return False

def create_table():
    """ Creates the file containing the table of all elements in the xml
    tree """
    r = ET.parse('Cdatabase.xml').getroot()
    text = """{| class="mogatable sortable"
|+ Tableau des composants de monstre
|-
! Ancien nom
! Nouveau nom
! Nom MH3U
! Nom MH4U
! Nom MHGen
"""
    t = ('prev', 'new', 'MH3U', 'MH4U', 'MHGen')
    for i in r:
        text += "|-\n"
        for j in t:
            k = i.find(j)
            text += '| '
            if k is None:
                text += '/'
            else:
                text += '[[' + k.text
                if j in t[2:]:
                    text += '/' + j
                text += ']]'
            text += '\n'

    text += "|}"
    with open('Exchange file.txt', 'w') as f:
        f.write(text)

def rename_descriptions(title='Template:Monster Description', lim=10000):
    """ N'a rien a voir, changement de "Double Cross" à Generations pour
    Monster Description"""
    for i in m.get_transclusions(title, lim, redir='nonredirects', ns='0') :
        i['text'] = m.get_text(i['title'])[0][1]
        print(i['title'])
        pre_tem = parse_template('Monster Description', i['text'], True)
        if "Monster Hunter Double Cross" not in pre_tem :
            print(pre_tem[-200:])
            continue
        n_tem = pre_tem.replace("Monster Hunter Double Cross",
                          "Monster Hunter Generations Ultimate", 1)
        #if input("Proceed (ok) ? ") == 'ok' :
        r = m.send_page(i['title'], i['text'].replace(pre_tem, n_tem, 1),
                    summary="Remplacement de Double Cross par Generations " +
                    "Ultimate dans le modèle [[Modèle:Monster Description]].",
                    minor=True)
        if r:
            print("Succès")
            sleep(5)
        else :
            print("Error")
            break

def correct_page(page, split, data):
    """ Vérifie et corrige la page (si possible). S'occupe notamment du
    nom du composant dans le modèle principal, remplace les ====<u> par =="""
    if split == {}:
        return
    summa = ""
    text = page['text']
    h3 = bool(re.match("^===(?!=\=)", text, flags=re.M))
    for game, game_text in split[0].items():
        if (not game_text.strip()):
            if (input("Section {} vide, souhaitez-vous l'effacer ? (oui/non)\
 ".format(game)) == 'oui'):
                text = re.sub("(?:\|-\|)?\s*" + game + "\s*=\s*", "", text)
                summa += "Suppression de la section " + game + ". "
        elif (text.count(game_text) != 1):
            print("Texte :", game_text)
            raise Warning("Le texte de l'opus {} apparaît plusieurs fois ({} \
fois) dans la page, est-ce normal ?\
".format(game, text.count(game_text)))
        sect = text[text.index(game_text):text.index(game_text)+len(game_text)]
        p_sect = sect
        if game not in games:
            if game.upper() in games:
                sect = sect.replace(game, game.upper())
                summa += "{}{}. ".format(game, game.upper())
                game = game.upper()
            else:
                continue
        datum = data[games.index(game) + 1]
        p = re.compile(r'\|(\s*)Nom(\s*)=\s*([^\|]+)', re.M)
        if p.search(sect):
            # L'infobox se trouve dans la section
            title = p.search(sect).group(3).strip()
        elif p.search(split[1]):
            # L'infobox se trouve hors du tabber (ou de la section)
            title = p.search(split[1]).group(3).strip()
            if len(split[0]) > 1:
                # title appears in previous text, however they are multiple
                # games, we check if the object has the same name in each game
                ok = True
                for i in split[0]:
                    ok = title in data[games.index(i) + 1]
                    if not ok:
                        print("Plusieurs sections peuvent correspondre au \
titre, arrêt. ({} pas dans les données {})".format(title, i))
                        return
                if not ok:
                    break
            # TODO : ligne très bizarre, à vérifier
            #p_sect = sect = split[1]
            #game = 'principale'
            #datum = data[games.index(tuple(split[0].keys())[0]) + 1]
        else:
            raise Warning("Titre introuvable !")
            continue
        if title not in datum:
            rep = replace_and_find(title, datum)
            esc = re.escape(title)
            r = re.compile('^(' + esc +')$', re.M|re.I)
            if len(rep) == 1:
                sect = p.sub(r'|\1Nom\2= ' + rep[0] + '\n', sect)
                summa += "{}{} (dans l'infobox principale, section {}). \
                ".format(title, rep[0], game)
            elif len(rep) > 1:
                print("Plusieurs remplacements possibles : ", rep)
                ans = "autre"
                while ans == "autre":
                    # TODO : has to be done
                    print("PAS encore implémenté !!!!!!!!!!!")
                    ans = input('Lequel choisisez vous (entrez son numéro, ou '
                    '"autre/passer") ? ')
                    if ans.isdigit():
                        ans = rep[int(ans)]
                    elif ans == 'passer':
                        return
                    elif ans == 'autre':
                        ans = input("Nouveau nom (annuler) : ")
                        ans = "autre "if ans == 'annuler' else ans
                summa += " {}{} (dans l'infobox \
                principale, section {}). ".format(title, r, game)
                sect = p.sub(r'|\1Nom\2= ' + rep[0] + '\n', sect)
            elif r.search(datum):
                sect = p.sub(r'|\1Nom\2= ' + r.search(datum).group(0) + '\n',
                             sect)
                summa += "{}{} (dans l'infobox principale, section {}). \
                ".format(title, r.search(datum).group(0), game)
        text = text.replace(p_sect, sect)
    foo = re.sub(r"^===?=?\s*(?:<u>)?\s*([^<:?]+?)(?:(?:</u>)?|\s*|([?:]?))*===?=?$",
                 lambda s: "== " + s.group(1) +
                 (" " + s.group(2) if s.group(2) is not None else "") + " ==",
                 text, flags=re.M)
    if foo != text:
        text = foo
        summa += "Changement des niveaux de titre. "
    if text != page['text']:
        if h3:
            print("Ancien texte :\n\n", page['text'],
                  "\n\nNouveau texte :\n\n", text)
            print("ATTENTION : il peut y avoir des titres niveau 3 !")
            say("Niveau 3 détecté !")
            if input("Valider ? (oui/non) ") != "oui":
                return
        assert m.send_page(page['title'], text, summa), "Erreur dans la \
correction"
        say("La page " + page['title'] + " a été corrigée.")
        sleep(5)


def send_pages(title, split, r_titles):
    """ Envoie les sous-pages, grâce à l'argument split, puis envoie la page
    principale """
    # Envoie des sous-pages
    for game in order_games(split[0].keys()):
        subpage = ''
        foo = tuple(filter(lambda x: x[1] == game, r_titles))
        if foo and foo[0][0] != title:
            subpage += "{{orphan|" + title + "}}\n"
        elif len(foo) == 0:
            raise Warning("Le jeu " + game + " ne figure pas dans les titres \
liés !")
        subpage = "{{page traitée}}\n"
        if split[1]:
            subpage += split[1] + "\n"
        subpage += split[0][game]
        if split[2]:
            subpage += "\n" + split[2]
        assert m.send_page(foo[0][0] + "/" + game, subpage,
                    "Réorganisation des composants de monstre. "), "\
Erreur dans l'envoie"
        sleep(5)
    # Envoie de la page principale
    main = "{{page traitée}}\n{{Info modif objet\n"
    n = max((len(i[1]) for i in r_titles))
    for i in order_games(tuple(x[1] for x in r_titles)):
        name, game = next(filter(lambda x: x[1] == i, r_titles))
        main += "| " + game.ljust(n) + " = "
        main += (name if name != title else "Y") + "\n"
    main += "}}\n[[Catégorie:Composant de monstre]]"
    assert m.send_page(title, main, "Réorganisation des composants de monstre.\
"), "Erreur dans l'envoie"
    say("Les pages " + title + " ont été envoyées.")
    sleep(10)

def order_games(ite):
    """ Returns the iterable with the element sorted in appropriate order """
    order = ("MH1", "MHF", "MHF2", "MHFU", "MHTri", "MH3U", "MH4U", "MHGen",
             "MHF-Z", "MHST", "MHW", "MHGU")
    for i in order:
        if i in ite:
            yield i
    for i in ite:
        if i not in order:
            say("Jeu inconnu : " + i + " !")
            print("Jeu inconnu " + i + " le tri peut être inefficace")
            yield i

m.login('HoumgaBot')
#rename_descriptions()
proceed_components(10000, correct=True, publish=True)

Communication avec l'API MediaWiki[]

Effectue des actions générales avec MediaWiki : identification, vérification de l'existence d'une page, modification d'une page, récupération du texte d'une page, etc...

media_moga.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A tool for creating Python bots interacting with MediaWiki API.

@author: Houmgaor
"""
import requests
# During file upload, it seems MediaWiki does not support no alphanumercial
# file names
from urllib.parse import quote

""" Help on MediaWiki at https://www.mediawiki.org/wiki/API:Main_page """


class moga_session:
    """A session on mogapedia."""

    def __init__(self, name=None, passw=None,
                 url='https://mogapedia.fandom.com/fr/api.php'):
        """Create a new session, as an anonymous user."""
        self.url = url
        self.s = requests.Session()
        self.bot = False
        # We get MediaWiki version
        self.siteinfo = self.query_meta('siteinfo', prop='general')['general']
        # We get tokens
        logged_in = False
        if passw and name:
            logged_in = self.login(name, passw)
        if not logged_in:
            # Default crsf token for unregistrated users should be "+\\"
            self.token = self.get_token('edit')
            r = self.query_meta('userinfo')['userinfo']
            self.userid = r['id']
            self.name = r['name']

    def __del__(self):
        """End session."""
        self.logout()

    def __check_request__(self, r, print_error=True, print_warning=True,
                          require_batchcomplete=False):
        """
        Print any problem in the request.

        Return False if the request contains errors.
        """
        if 'error' in r:
            if print_error:
                print("Error: ", r['error'])
            return False
        elif 'warning' in r and print_warning:
            raise Warning(r['warning'])
        elif 'warnings' in r and print_warning:
            raise Warning(r['warnings'])
        if require_batchcomplete and 'batchcomplete' not in r:
            return False
        return True

    def __split__(obj, wiki=True):
        """
        Join or split iter or str in a wiki friendly way.

        Join an iter into a str if wiki is True, and splits an str into
        an iter if wiki is False.
        """
        if wiki:
            if type(obj) != str:
                return '|'.join(obj)
        else:
            if type(obj) == str:
                return obj.split('|')
        return obj

    def __dict_len__(self, d, interval_weight=2):
        """
        Return the total lenght of a dictionary.

        The length is the total number of characters with dictionnary's keys
        and values. Adds an interval_weight for each key.
        """
        return (interval_weight * len(d.keys()) + sum(map(len, d.values()))
                + sum(map(len, d.keys())))

    def __fill_url__(self, dic, key, to_add):
        """
        Add at most 2**11 characters in the key "key" of dic.

        Modify the key "key" of the dict, in order to stuff an url with
        at most 2 ** 11 characters. The previous text is also used.
        to_add must be an iterable, or a string with values separated by |.

        Return the elements that could not be added, it may be an empty
        list.
        """
        split = to_add.split('|') if type(to_add) == str else tuple(to_add)
        dic[key] = ''
        index = len(self.url) + self.__dict_len__(dic)
        # We avoid overflow in url size (approximatively 2 ** 11 characters)
        for i, j in enumerate(split):
            if index + len(j) >= 2 ** 11:
                break
            dic[key] += j + '|'
            index += len(j) + 1
        # We delete the last '|' character
        dic[key] = dic[key][:-1]
        return split[i + 1:]

    def __list__(self, gen_name, gen_short, maxi=700, **kargs):
        """Create a generator with standard syntax.

        Uses following arguments (some could be useless):
            - gen_name: full name of the generator (backlinks, embeddedin...)
            - gen_short: shorten nam of the generator (bl, ei...)
            - title: title to be use (may be optionnal)
            - maxi: maximum numbre of pages to be retriviewed
            - namespace: filter results by namespace, default : all
            - filterredir: filter redirects (all, redirects, nonredirects)
        """
        gen_list = {
            'allcategories': '', 'alldeletedrevisions': '',
            'allfileusages': '', 'allimages': 'ai', 'alllinks': '',
            'allpages': 'ap', 'allredirects': '', 'allrevisions': '',
            'allusers': '', 'blocks': '', 'categorymembers': '',
            'deletedrevs': '', 'embeddedin': '', 'exturlusage': '',
            'filearchive': '', 'imageusage': '', 'iwbacklinks': '',
            'langbacklinks': '', 'logevents': '', 'pagepropnames': '',
            'pageswithprop': '', 'prefixsearch': '', 'protectedtitles': '',
            'querypage': '', 'random': '', 'recentchanges': '', 'search': '',
            'tags': '', 'usercontribs': '', 'users': '', 'watchlist': '',
            'watchlistraw': ''
            }
        # Maybe we should just check errors in message from the server
        if gen_name not in gen_list:
            raise Warning('Unknown generator: ' + gen_name + ". The generator"
                          + " may not work properly ")
        s = gen_short
        p = {
            'action': 'query',
            'list': gen_name,
            s + 'limit': min(maxi, 5000 if self.bot else 500),
            'format': 'json'
        }
        for i in kargs:
            p[s + i] = kargs[i]
        r = self.s.get(self.url, params=p).json()
        if 'error' in r:
            print("Erreur dans la liste :", r['error'])
            return False
        if 'warning' in r:
            print("Avertissement :", r['warning'])
        if 'warnings' in r:
            print("Avertissement :", r['warnings'])
        out = r['query'][gen_name]
        maxi -= len(out)
        while 'query-continue' in r and maxi > 0:
            if s + 'continue' in r['query-continue'][gen_name]:
                p[s + 'continue'] = r['query-continue'][gen_name][s
                                                                  + 'continue']
            elif s + 'from' in r['query-continue'][gen_name]:
                p[s + 'from'] = r['query-continue'][gen_name][s + 'from']
            else:
                print('Query continue:', r['query-continue'])
                raise Warning('Unable to load results.')
            p[s + 'limit'] = min(maxi, 5000 if self.bot else 500)
            r = self.s.get(self.url, params=p).json()
            out += r['query'][gen_name]
            maxi -= len(r['query'][gen_name])
        # No idea why it is sometimes different
        while 'continue' in r and maxi > 0:
            if s + 'continue' in r['continue']:
                p[s + 'from'] = r['continue'][s + 'continue']
            else:
                print('Query continue:', r['query-continue'])
                raise Warning('Unable to load results.')
            p[s + 'limit'] = min(maxi, 5000 if self.bot else 500)
            r = self.s.get(self.url, params=p).json()
            out += r['query'][gen_name]
            maxi -= len(r['query'][gen_name])
        return out

    def query_meta(self, meta_name, **kwargs):
        """
        Return meta data about this wiki.

        meta_name should a string for a single meta.
        kwargs should be a dictionnary of optional arguments.
        You can add a "meta_short" argument to override default mate short
        name
        """
        all_meta = {
            'allmessages': 'am', 'authmanagerinfo': 'ami', 'babel': 'bab',
            'featureusage': 'afu', 'filerepoinfo': 'fri',
            'globalpreferences': 'gpr', 'globalrenamestatus': 'grs',
            'globaluserinfo': 'gui', 'languageinfo': 'li',
            'languagestats': 'ls', 'linterstats': 'li',
            'managemessagegroups': 'mmg', 'messagegroups': 'mg',
            'messagegroupstats': 'mgs', 'messagetranslations': 'mt',
            'notifications': 'not', 'siteinfo': 'si', 'siteviews': 'pvis',
            'tokens': '', 'unreadnotificationpages': 'unp', 'userinfo': 'ui',
            'wikibase': 'wb', 'oath': 'oauth', 'readinglists': 'rl'
            }
        if meta_name not in all_meta:
            raise Warning(
                'Meta data name not recognized "' + meta_name + '"'
                'by this Python API. Correct values are:\n' + all_meta
                )
        if 'meta_short' in kwargs:
            all_meta[meta_name] = kwargs['meta_short']
        p = {
            'action': 'query',
            'meta': meta_name,
            'format': 'json',
            }
        for i in kwargs:
            if i != 'meta_short':
                p[all_meta[meta_name] + i] = kwargs[i]
        r = self.s.get(self.url, params=p).json()
        if 'error' in r:
            print("An error occured:", r['error'])
            return False
        if 'warnings' in r:
            print("Warning:", r['warnings'])
        if 'batchcomplete' not in r:
            print("Something unexpected occured during the request.")
        return r['query']

    def query_prop(self, prop_name, prop_short=None, titles=None,
                   pageids=None, revids=None, maxi=500, **kargs):
        """Create standard query using a prop key."""
        mapping = {
                'categories': 'cl', 'categoryinfo': 'ci',
                'cirrusbuilddoc': 'cb', 'cirruscompsuggestbuilddoc': 'csb',
                'cirrusdoc': 'cd', 'contributors': 'pc',
                'deletedrevisions': 'drv', 'duplicatefiles': 'df',
                'extlinks': 'el', 'extracts': 'ex', 'fileusage': 'fu',
                'globalusage': 'gu', 'imageinfo': 'ii', 'images': 'im',
                'info': 'in', 'iwlinks': 'iw', 'langlinks': 'll',
                'links': 'pl', 'linkshere': 'lh', 'pageimages': 'pi',
                'pageprops': 'pp', 'pageterms': 'wbpt', 'pageviews': 'pvip',
                'redirects': 'rd', 'revisions': 'rv', 'stashimageinfo': 'sii',
                'templates': 'tl', 'transcludedin': 'ti',
                'transcodestatus': '', 'videoinfo': 'vi',
                'wbentityusage': 'wveu'
        }
        # Maybe we should just check errors in message from the server
        if prop_name not in mapping and not prop_short:
            raise Exception('Unknown generator : ' + prop_name + ". You " +
                            "must specify his prop_short ")
        s = prop_short or mapping[prop_name]
        p = {
            'action': 'query',
            'prop': prop_name,
            s + 'limit': str(min(int(maxi), 5000 if self.bot else 500)),
            'format': 'json'
        }
        for i in kargs:
            p[s + i] = kargs[i]

        out = ()
        if maxi <= 0:
            return out
        # We fill the url, and ask the function recursively if titles were not
        # added
        if titles:
            spam = self.__fill_url__(p, 'titles', titles)
            if spam:
                out += self.query_prop(prop_name, prop_short, titles=spam,
                                       maxi=maxi, **kargs)
        elif pageids:
            spam = self.__fill_url__(p, 'pageids', pageids)
            if spam:
                out += self.query_prop(prop_name, prop_short, pageids=spam,
                                       maxi=maxi, **kargs)
        elif revids:
            spam = self.__fill_url__(p, 'revids', revids)
            if spam:
                out += self.query_prop(prop_name, prop_short, revids=spam,
                                       maxi=maxi, **kargs)
        else:
            raise Exception("You must specify either titles, pageids or " +
                            "revids !")
        maxi -= len(out)
        p[s + 'limit'] = str(min(int(maxi), 5000 if self.bot else 500))
        r = self.s.get(self.url, params=p).json()
        out += tuple(r['query']['pages'].values())
        maxi -= len(out)
        if 'query-continue' in r and maxi > 0:
            # New arguments to be used
            if s + 'continue' in r['query-continue'][prop_name]:
                kargs['continue'] = r['query-continue'][prop_name][s +
                                                                   'continue']
            elif s + 'from' in r['query-continue'][prop_name]:
                kargs['from'] = r['query-continue'][prop_name][s + 'from']
            else:
                print('Query continue :', r['query-sontinue'])
                raise Warning('Unable to load results.')
            if titles:
                kargs['titles'] = titles
            elif pageids:
                kargs['pagesids'] = pageids
            else:
                kargs['revids'] = revids
            spam = self.query_prop(prop_name, s, maxi=maxi, **kargs)
            out += spam
            maxi -= len(spam)
        return out

    def query_generator(self, gen_name, gen_short=None, titles=None,
                        pageids=None, revids=None, redirects=False, **kargs):
        """Create a standard interface for generators."""
        mapping = {
                'allcategories': 'ac', 'alldeletedrevisions': 'adr',
                'allfileusages': 'af', 'allimages': 'ai', 'alllinks': 'al',
                'allpages': 'ap', 'allredirects': 'ar', 'allrevisions': 'arv',
                'alltransclusions': 'at', 'backlinks': 'bl',
                'categories': 'cl', 'categorymembers': 'cm',
                'deletedrevisions': 'drv', 'duplicatefiles': 'df',
                'embeddedin': 'ei', 'exturlusage': 'eu', 'fileusage': 'fu',
                'images': 'im', 'imageusage': 'iu', 'iwbacklinks': 'iwbl',
                'langbacklinks': 'lbl', 'links': 'pl', 'linkshere': 'lh',
                'messagecollection': 'mc', 'mostviewed': 'pvim',
                'pageswithprop': 'pwp', 'prefixsearch': 'ps',
                'protectedtitles': 'pt', 'querypage': 'qp', 'random': 'rn',
                'recentchanges': 'rc', 'redirects': 'rd', 'revisions': 'rv',
                'search': 'sr', 'templates': 'tl', 'transcludedin': 'ti',
                'watchlist': 'wl', 'watchlistraw': 'wr',
                'wblistentityusage': 'wbeu'
                }
        # Maybe we should just check errors in message from the server
        if gen_name not in mapping and not gen_short:
            raise Exception('Unknown generator : ' + gen_name + ". You " +
                            "must specify his gen_short ")
        s = gen_short or 'g' + mapping[gen_name]
        p = {
            'action': 'query',
            'generator': gen_name,
            'format': 'json'
        }
        kargs['limit'] = str(kargs['limit'])
        if redirects:
            p['redirects'] = ''
        for i in kargs:
            if i == 'limit':
                p[s + i] = str(min(int(kargs[i]), 5000 if self.bot else 500))
            else:
                p[s + i] = kargs[i]

        out = {}
        if int(kargs['limit']) <= 0:
            return out
        # We fill the url, and ask the function recursively if titles were not
        # added
        if titles:
            spam = self.__fill_url__(p, 'titles', titles)
            if spam:
                out.update(self.query_prop(gen_name, gen_short, titles=spam,
                                           **kargs))
        elif pageids:
            spam = self.__fill_url__(p, 'pageids', pageids)
            if spam:
                out.update(self.query_prop(gen_name, gen_short, pageids=spam,
                                           **kargs))
        elif revids:
            spam = self.__fill_url__(p, 'revids', revids)
            if spam:
                out.update(self.query_prop(gen_name, gen_short, revids=spam,
                                           **kargs))
        kargs['limit'] = str(int(kargs['limit']) - len(out))
        p[s + 'limit'] = str(min(int(kargs['limit']),
                                 5000 if self.bot else 500))
        r = self.s.get(self.url, params=p).json()
        assert 'error' not in r, "Erreur : " + str(r['error'])
        out.update(r['query']['pages'])
        kargs['limit'] = str(int(kargs['limit']) - len(out))
        if 'query-continue' in r and int(kargs['limit']) > 0:
            # New arguments to be used
            if s + 'continue' in r['query-continue'][gen_name]:
                kargs['continue'] = r['query-continue'][gen_name][s
                                                                  + 'continue']
            elif s + 'from' in r['query-continue'][gen_name]:
                kargs['from'] = r['query-continue'][gen_name][s + 'from']
            else:
                print('Query continue :', r['query-sontinue'])
                raise Warning('Unable to load results.')
            if titles:
                kargs['titles'] = titles
            elif pageids:
                kargs['pagesids'] = pageids
            elif revids:
                kargs['revids'] = revids
            spam = self.query_prop(gen_name, s, **kargs)
            out.update(spam)
            kargs['limit'] = str(int(kargs['limit']) - len(out))
        return out

    def login(self, name='', passw='', bot=True):
        """Log in."""
        if not name:
            name = input("Entrez votre nom d'utilisateur : ")
        if not passw:
            # from getpass import getpass
            # passw = getpass("Entrez votre mot de passe : ")
            passw = input("Entrez votre mot de passe : ")

        if self.siteinfo['generator'] < 'MediaWiki 1.27.0':
            # Old way to get the session token
            r = self.s.post(self.url,
                            data={
                                'action': 'login',
                                'format': 'json',
                                'lgname': str(name),
                                'lgpassword': str(passw),
                                }
                            ).json()
            egg = r['login']['token']
        else:
            # New way to get session token
            r = self.get_token('login')
            egg = r['logintoken']
        # Logs in
        r = self.s.post(self.url,
                        data={
                             'action': 'login',
                             'lgname': name,
                             'lgpassword': passw,
                             'lgtoken': egg,
                             'format': 'json',
                             },
                        ).json()
        egg = r['login']['result']
        if egg == 'Success':
            print('Enregistrement effectué avec succès')
            self.name = name
            self.userid = r['login']['lguserid']
            # We check messages, rights, sanctions and groups
            r = self.query_meta('userinfo',
                                prop='groups|hasmsg|blockinfo|rights'
                                )['userinfo']
            b = 'bot' in r['rights'] and 'bot' in r['groups']
            if bot and not b:
                print("Le compte n'est pas reconnu comme robot")
            self.bot = b and bot
            # Then we get the edit token
            if self.siteinfo['generator'] < 'MediaWiki 1.24.0':
                # Obsolete method
                rand_page = str(self.get_all_pages(1)[0]['pageid'])
                r = self.query_prop('info', 'in', None, rand_page,
                                    token='edit')
                if 'edittoken' in r['pages'][rand_page]:
                    self.token = r['pages'][rand_page]['edittoken']
                else:
                    return r
            else:
                r = self.query_meta('tokens', type='csrf')
                if 'csrftoken' in r['tokens']:
                    self.token = r['tokens']['csrftoken']
                else:
                    return r
            return True
        elif egg == 'WrongPass':
            print('Mot de passe incorrect')
        elif egg == 'Throttled':
            print('Veuillez attendre : %s (Throttled)' % (r['login']['wait']))
        elif egg == 'Failed':
            print("Échec de la connexion : " + r['login']['reason'])
        else:
            print('Impossible de se connecter.\n' + str(r))
        return False

    def logged(self):
        """Check wether you are logged in."""
        u = self.query_meta('userinfo')['userinfo']
        print("Enregistré comme %s" % (u['name']), end='')
        if 'anon' in u:
            print(' (utilisateur anonyme)')
        else:
            print('')
        out = {'name': u['name'],
               'token': self.token,
               'userid': u['id'],
               }
        if 'anon' in u:
            out['anon'] = ''
        return out

    def logout(self):
        """Log out."""
        if 'anon' in self.logged():
            print("Vous n'êtes pas enregistré !")
            return
        # Will be empty if done
        data = {
            'token': self.token,
            'action': 'logout',
            'format': 'json'
            }
        r = self.s.post(self.url, data=data).json()
        if self.__check_request__(r):
            print("Déconnexion effectuée avec succès")
        return r

    def get_token(self, action, page=None):
        """
        Get the token to perform a specific action.

        Prior to MediaWiki 1.24:
            Could be edit, delete, protect, move, block, unblock, email,
            import, watch.

            page argument is required.

            See: https://www.mediawiki.org/wiki/API:Tokens_(action)

        After MediaWiki 1.24:
            createaccount, csrf, deleteglobalaccount, login, patrol, rollback,
            setglobalaccountstatus, userrights, watch

            page argument won't be used

            See: https://www.mediawiki.org/wiki/API:Tokens
        """
        if self.siteinfo['generator'] < 'MediaWiki 1.24.0':
            out = self.query_prop('info', 'in', page, token=action)
            return out[0][action + 'token']
        else:
            # Replaces obsolete values in action by new ones
            new_action = action
            for i in action.split('|'):
                # TODO : change more actions
                if i in ('edit', 'delete', 'protect', 'move'):
                    new_action = new_action.replace(i, 'csrf')
            return self.query_meta('tokens', **{'type': new_action})['tokens']

    def send_page(self, title, text, summary='Automatic edit', append=False,
                  prepend=False, minor=False, **kwargs):
        """Send the article, ignoring the previous version."""
        data = {
             'action':  'edit',
             'title':   title,
             'summary': summary,
             'format': 'json',
             }
        if self.bot:
            data['bot'] = ''
        if minor:
            data['minor'] = ''
        if append:
            data['appendtext'] = text
        elif prepend:
            data['prependtext'] = text
        else:
            data['text'] = text
        data.update(kwargs)
        # edit token should always be the last parameter
        # TODO : verify this code
        if self.siteinfo['generator'] < 'MediaWiki 1.24.0':
            data['token'] = self.get_token(title, 'edit')
        else:
            data['token'] = self.token
        r = self.s.post(self.url, data=data).json()
        if 'error' in r:
            print("Erreur dans l'envoi de la page, code : %s, information : %s"
                  % (r['error']['code'], r['error']['info']))
            return
        try:
            resp = r['edit']
        except Exception:
            print("Erreur :", r)
            return
        if resp['result'] == 'success':
            if 'nochange' in resp['result']:
                print("Aucune modification effectuée, pas d'erreur detectée")
        elif resp['result'] == 'error':
            if resp['code'] == 'badtoken':
                print("Token invalide : " + self.token)
            else:
                print('Erreur :', resp.text)
            return False
        return resp['result'] == 'Success'

    def move_page(self, from_page, to, reason='Automatic rennaming',
                  move_subpages=True, redirect=True):
        """Move the page "from_page" using its title to the page 'to'."""
        data = {
             'action':  'move',
             'from':    from_page,
             'to':      to,
             'reason':  reason,
             'token':   self.token,
             'format':  'json',
             'movetalk': '',
             }
        if not redirect:
            data['noredirect'] = ''
        if move_subpages:
            data['movesubpages'] = ''
        r = self.s.post(self.url, data).json()
        if not self.__check_request__(r):
            return False
        print(r)
        return True

    def upload(self, filename, file=None, **kargs):
        """
        Upload a file to the wiki.

        Main kargs:

        * filename
        * comment
        * tags
        * text: Initial page text for new files.
        * file: File contents. Must be posted as a file upload using
        multipart/form-data.
        * url: URL to fetch the file from.

        See https://www.mediawiki.org/wiki/API:Upload for an exhaustive list.
        """
        d = {
                'action': 'upload',
                'filename': filename,
                'token': self.get_token('File:' + filename, 'edit'),
                'format': 'json'
        }
        d.update(kargs)
        if file:
            f = {'file': (quote(filename), open(file, 'rb'),
                          'multipart/form-data')}
            r = self.s.post(self.url, data=d, files=f).json()
        else:
            r = self.s.post(self.url, data=d).json()
        if 'error' in r:
            print("Erreur :", r['error'])
            return False
        if 'warning' in r:
            print("Avertissement :", r['warning'])
        if 'result' not in r['upload'] or r['upload']['result'] != 'Success':
            print(r)
            return False
        return True

    def exist(self, titles):
        """
        Return couples of (title, boolean).

        The boolean corresponding to the page "Title" is True if page exists,
        False if it doesn't exists.
        """
        out = self.query_prop('info', 'in', titles)
        return [(i['title'],
                 'missing' not in i and 'invalid' not in i) for i in out]
        s_titles = titles.split('|') if type(titles) == str else tuple(titles)
        if type(titles) != str:
            titles = '|'.join(titles)
        # @todo: check this new code
        p = {
            'action': 'query',
            'prop': 'info',
            'titles': '',
            'format': 'json',
        }
        # We avoid overflow in url size (approximatively 2 ** 11 characters)
        for i in range(len(s_titles)):
            if (
                    len(self.url) + self.__dict_len__(p)
                    + sum(map(len, s_titles[i]))
                    ) >= 2 ** 11:
                break
            p['titles'] += s_titles[i] + '|'
        # We delete the last '|' character
        p['titles'] = p['titles'][:-1]
        r = self.s.get(self.url, params=p).json()
        q = r['query']['pages']
        out = [(q[i]['title'],
                'missing' not in q[i] and 'invalid' not in q[i]) for i in q]
        if i + 1 < len(s_titles):
            out += self.exist(s_titles[i + 1:])
        return out

        # ----------------------------------------------------------
        if len(titles.split('|')) > 50:
            out = []
            for i in range(0, len(titles.split('|')), 50):
                out += self.exist(titles.split('|')[i:i + 50])
            return out
        else:
            r = self.s.get(self.url,
                           params={
                               'action': 'query',
                               'titles': titles,
                               'prop': 'info',
                               'format': 'json',
                               }
                           ).json()
            p = r['query']['pages']
        return [(p[i]['title'],
                 'missing' not in p[i] and 'invalid' not in p[i]) for i in p]

    def is_redirect(self, titles):
        """
        Check if a page is a redirection.

        Return False if the page does not exist.
        """
        out = self.query_prop('info', None, titles)
        return [(i['title'], 'redirect' in i) for i in out]
        if type(titles) == list:
            titles = '|'.join(titles)
        if len(titles.split('|')) > 50:
            out = []
            for i in range(0, len(titles.split('|')), 50):
                out += self.is_redirect(titles.split('|')[i:i + 50])
            return out
        p = self.s.get(self.url,
                       params={
                           'action': 'query',
                           'titles': titles,
                           'prop': 'info',
                           'format': 'json',
                           }
                       ).json()['query']['pages']
        return [(p[i]['title'], 'redirect' in p[i]) for i in p]

    def get_info(self, titles, **kargs):
        """Get general informations about a list of pages."""
        return self.query_prop('info', None, titles, **kargs)
        # @todo: make this function safer
        p = {
                'action': 'query',
                'titles': titles,
                'prop': 'info',
                'format': 'json'
        }
        p.update(kargs)
        r = self.s.get(self.url, params=p).json()
        return r['query']['pages']

    def get_image_info(self, titles, **kargs):
        """
        Get general informations about a list of images.

        Do not include 'ii' prefix in kargs keys.
        """
        return self.query_prop('imageinfo', titles=titles, **kargs)
        # @todo: make this function safer
        p = {
                'action': 'query',
                'titles': titles,
                'prop': 'imageinfo',
                'format': 'json'
        }
        for i in kargs:
            p['ii' + i] = kargs[i]
        r = self.s.get(self.url, params=p).json()
        return r['query']['pages']

    def get_text(self, titles):
        """
        Return the content of a list of articles.

        Returns a list of tuples where first element is the title and the
        second the text.
        """
        articles = []
        for i in self.exist(titles):
            if i[1]:
                r = self.s.get(self.url,
                               params={
                                   'action': 'parse',
                                   'page': i[0],
                                   'prop': 'wikitext',
                                   'format': 'json',
                                   }).json()
                if 'parse' in r:
                    articles.append((r['parse']['title'],
                                     r['parse']['wikitext']['*']))
                else:
                    print(r)
            else:
                articles.append((i[0], None))
        return articles

    def lang_links(self, titles):
        """
        Return all interlanguage links of a list of articles.

        Returns a list of object, where 'main' is the original name.
        """
        articles = []
        # 'llcontinue' should be added here
        r = self.s.get(self.url,
                       params={
                            'action': 'query',
                            'titles': self.__split__(titles),
                            'prop': 'langlinks',
                            'lllimit': '5000' if self.bot else '500',
                            'format': 'json'
                        }).json()['query']['pages']
        for i in r:
            coco = {'*': r[i]['title']}
            if 'langlinks' in r[i]:
                for j in r[i]['langlinks']:
                    coco[j['lang']] = j['*']
            articles.append(coco)
        return articles

    def get_namespace(self, titles):
        """
        Return the namespaces of a list of articles.

        Returns a list of tuples where first element is the title and the
        second the text.
        """
        r = self.s.get(self.url,
                       params={
                           'action': 'query',
                           'titles': "|".join(titles),
                           'format': 'json'
                           }).json()['query']['pages']
        return [r[i] for i in r if "missing" not in r]

    def get_categories(self, titles):
        """
        Return the categories on the page.

        Data are organized in a list, where each element
        is a dict with "ns" (namespace) and "title".
        """
        return self.query_prop('categories', 'cl', titles=titles)
        articles = ''
        out = []
        if type(titles) == str:
            titles = titles.split('|')
        # Limit for query
        lim = 50
        if len(titles) > lim:
            for j in range(0, len(titles), lim):
                out += self.get_categories(titles[j:j + lim])
            return out
        for i in self.exist(titles):
            if i[1]:
                articles += i[0] + '|'
                out.append((i[0], []))
        # Limit for results
        lim *= 10
        p = {
            'action': 'query',
            'format': 'json',
            'prop': 'categories',
            'titles': articles[:-1],
            'cllimit': str(lim)
            }

        def sort_data(p, out):
            try:
                r = self.s.get(self.url, params=p).json()
            except Exception:
                print(p)
            if 'query' in r:
                egg = r['query']['pages']
                for i in egg:
                    if 'categories' in egg[i]:
                        for j in range(0, len(out)):
                            if egg[i]['title'] == out[j][0]:
                                # We append categories to the corresponding
                                # title
                                # a is used to edit the tuple
                                a = out[j][1]
                                a += egg[i]['categories']
                                break
                            elif j == len(out) - 1:
                                # If title isn't in the out variable yet
                                out[j][1].append((egg[i]['title'],
                                                  egg[i]['categories']))

            if 'query-continue' in r:
                return r['query-continue']['categories']['clcontinue']
            else:
                return ''

        coco = sort_data(p, out)
        # query-continue could have been replaced by continue
        while coco != '':
            p['clcontinue'] = coco
            coco = sort_data(p, out)

        return out

    def get_pages_with_category(self, title, maxi=5000,
                                search='page|subcat|file'):
        """
        Get pages within category  "title".

        Return 5000 results max (bot) or 500 (users).
        You must precise "Category:" in title
        (for example : Category:someCategoryTitle)

        Use search parameter to search only on specific type : page, subcat
        or file. Use "|" to join them.
        """
        return self.__list__('categorymembers', 'cm', maxi, title=title,
                             **{'type': search})
        p = {
            'action': 'query',
            'list': 'categorymembers',
            'cmtitle': title,
            'cmtype': search,
            'cmlimit': min(maxi, 5000 if self.bot else 500),
            'format': 'json',
        }
        r = self.s.get(self.url, params=p).json()
        out = r['query']['categorymembers']
        maxi -= len(out)
        if not self.exist(title)[0][1] and not len(out):
            print("Catégorie inexistante ne contenant"
                  " aucun élément de ce type.")
            return []
        while 'query-continue' in r and maxi > 0:
            p['cmcontinue'] = (r['query-continue']['categorymembers']
                               ['cmcontinue'])
            p['cmlimit'] = min(maxi, 5000 if self.bot else 500)
            r = self.s.get(self.url, params=p).json()
            maxi -= len(r['query']['categorymembers'])
            out += r['query']['categorymembers']
        return out

    def get_pages_with_category_recursive(self, title, maxi=5000,
                                          search='page|file', n=5):
        """
        Get the pages and look forward in the categories n times.

        In each category a maximum of maximum will be retriviewed.
        """
        # First we get the pages in the category
        out = self.get_pages_with_category(title, maxi, search)

        if n > 0:
            for i in self.get_pages_with_category(title, maxi, 'subcat'):
                out += self.get_pages_with_category_recursive(i['title'], maxi,
                                                              search, n-1)
        return out

    def get_all_pages(self, maxi=700, ns='0', redir='all', **kwargs):
        """
        Get a maximum number of maxi pages in the namespace nm.

        Retriview 5000 results for bots and 500 for others.
        Don't filter redirections by default.

        Redir can be :
            - all: list all pages regardless of their redirect flag
            - redirects: only list redirects
            - nonredirects: don't list redirects
        """
        return self.__list__('allpages', 'ap', maxi, namespace=ns,
                             filterredir=redir, **kwargs)

    def get_all_images(self, maxi=700, **kargs):
        """Return all images on this wiki."""
        return self.__list__('allimages', 'ai', maxi=maxi, **kargs)

    def get_backlinks(self, title, maxi=700, ns='0', redir='all', **kwargs):
        """
        Return backlinks to a page.

        Backlinks are all pages pointing at a specific page "title".
        """
        return self.__list__('backlinks', 'bl', maxi, namespace=ns,
                             filterredir=redir, title=title, **kwargs)

    def get_transclusions(self, title, maxi=700, ns='0', redir='all'):
        """
        Return all transclusions of a page.

        Transclusions are all pages embedding a specific page "title".
        """
        p = {
            'action': 'query',
            'list': 'embeddedin',
            'eititle': title,
            'eilimit': min(maxi, 5000 if self.bot else 500),
            'einamespace': ns,
            'eifilterredir': redir,
            'format': 'json'}
        r = self.s.get(self.url, params=p).json()
        out = r['query']['embeddedin']
        maxi -= len(out)
        while 'continue' in r and maxi > 0:
            p['eicontinue'] = r['continue']['embeddedin']['eicontinue']
            p['eilimit'] = min(maxi, 5000 if self.bot else 500)
            r = self.s.get(self.url, params=p).json()
            out += r['query']['embeddedin']
            maxi -= len(r['query']['embeddedin'])
        return out

    def parse(self, **kargs):
        """
        Parse wikitext.

        See: https://www.mediawiki.org/wiki/API:Parsing_wikitext
        """
        p = {
            'action': 'parse',
            'format': 'json'
            }
        p.update(kargs)
        r = self.s.get(self.url, params=p).json()
        if 'parse' in r:
            return r['parse']
        else:
            print(r)

Outils pratiques[]

En plus de la #Communication avec l'API MediaWiki, ce script ajoute des traitement spécifiques. Pour l'instant il s'agit de lire des modèles et d'en écrire.

moga_utils.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Dec 22 13:50:49 2020

A set of tools for general edition with MediaWiki.

@author: Houmgaor
"""
import re


def parse_template(name, text, return_raw=False):
    """
    Search a specific template in a text, and parse its first occurence.

    If return_raw is True, it returns the raw text of the template. Return
    nothing if the template is not found.
    """
    if not re.search(r'\{\{\s*(?:Template|Modèle)?\s*' + re.escape(name)
                     + r'\s*(\||\}\})',
                     text,
                     flags=re.I):
        return None
    # We start at the beginnig of the template
    text = re.sub(r'.+(?=\{\{\s*' + re.escape(name) + ')', '', text,
                  flags=re.I | re.S)
    # Then we stop at his end
    count = 1
    i = 2
    while i < len(text) and count > 0:
        if text[i] == '}':
            if text[i + 1] == '}':
                count -= 1
                i += 2
                continue
        elif text[i] == '{':
            if text[i + 1] == '{':
                count += 1
                i += 2
                continue
        i += 1
    text = text[:i]
    out = {}
    if len(text) < 5:
        if return_raw:
            return ''
        else:
            return out
    elif return_raw:
        return text
    # 0 : no register (title), 1 : argument name, 2 : argument value
    stat = 0
    i = 2
    arg = ['', '']
    while i < len(text):
        coco = re.search(r'^\{\{[^\}]+\}\}.*?', text[i:], flags=re.S)
        if coco:
            if stat:
                arg[stat-1] += coco.group(0)
                i += len(coco.group(0)) - 1
            else:
                print("Impossible de traiter "
                      "'Modèle:%s' dans '%s'" % (name, text)
                      )

        elif stat and text[i] != '|' and (text[i] != '=' or stat != 1):
            arg[stat - 1] += text[i]
        if text[i] == '|':
            if stat == 1:
                j = 0
                while str(j) in out:
                    j += 1
                out[str(j).strip()] = arg[0].strip()
            elif stat == 2:
                out[arg[0].strip()] = arg[1].strip()
            arg = ['', '']
            stat = 1
        elif text[i] == '=' and stat == 1:
            stat = 2
        elif text[i + 1:i + 3] == '}}':
            if arg[0].strip():
                if arg[1]:
                    out[arg[0].strip()] = arg[1].strip()
                else:
                    j = 0
                    while str(j).strip() in out:
                        j += 1
                    out[str(j).strip()] = arg[0].strip()
            break
        i += 1

    return out


def create_template(name, dic, compact=False):
    """Create the template with the name "name", and dict for its values."""
    new_l = spa = ''
    text = "{{" + name
    m = 0
    if not compact:
        m = max([len(i.strip()) for i in dic if not i.isdigit()])
        new_l = '\n'
        spa = ' '
    for i in dic:
        text += new_l + '|' + spa
        if not i.isdigit():
            text += i.strip().ljust(m) + spa + '=' + spa
        text += dic[i].strip()

    text += new_l + '}}'
    return text

Lieur interlangue[]

Le but de ce script est de créer des liens interlangue facilement entre les pages des différents wikis.

table_generator.py
# -*- coding: utf-8 -*-
"""
Created on Wed Jul 11 11:29:43 2018

@author: houmgaor
"""
import re
from media_moga import moga_session, parse_template, create_template
import webbrowser as wb

api = {}
# List of equivalences
equis = [
{'de': '/Ausrüstung', 'en': ' Equipment', 'es': ': Equipo', 'fr': '/Equipement'},
{'de': '/Materialien', 'en': ' Carves', 'es': ': Materiales', 'fr': '/Dépeçages'},
{'en': ' Photo Gallery', 'es': ': Galería', 'fr': '/Galerie de Photo'},
{'en': ' Videos', 'fr': '/Vidéos'},
{'de': '/Jagdhilfe', 'en': ' Ecology', 'es': ': Ecología', 'fr': '/Ethologie'},
{'en': ' Guides', 'es': ': Guías', 'fr': '/Guides'},
]
summary = {
    'en': 'Interlanguage links added',
    'es': 'Enlaces interlenguajes anadidos',
    'fr': 'Liens interlangue ajoutés',
}
user = 'HoumgaBot'
passw = input("Le mot de passe : ")
langs = ('de', 'en', 'es', 'fr', 'fi', 'it', 'pl', 'ja', 'ko', 'ru', 'zh')
for i in langs :
    url = 'http://'
    if i != 'en' :
        url += i + '.'
    if i == 'fr' :
        url += 'mogapedia'
    else :
        url += 'monsterhunter'
    if i == 'ko' :
        url += 's'
    url += '.wikia.com/api.php'
    print(i, end=' : ')
    api[i] = moga_session(user, passw, url=url)
    # Sécurité pour éviter d'être 
    if api[i].name != user :
        break

def link_pages(lim=50, src='en') :
    l = {}
    for i in api[src].get_all_pages(lim, redir='nonredirects') :
        if i['title'] < 'Abi' :
            continue
        print(i['title'])
        l.clear()
        l[src] = i['title']
        v = verif_links(l[src], src)
        if v != {} :
            for j in v :
                if j != src :
                    l[j] = v[j]
        # We show the pages
        choice = 'changer'
        while choice == 'changer' :
            print("%s (%s) donne en d'autres langages :" %(l[src], src))
            for j in l :
                print("    - %s (%s)" %(l[j], j))
            choice = input("Valider ? (oui/non/changer/voir) ")
            if choice == 'oui' :
                # Then we edit the pages, we first check if an interlanguage
                # template is avaliable
                for j in l :
                    if l[j] == '' :
                        continue
                    loc_text = api[j].get_text(l[j])[0][1]  
                    if j in 'esde' :
                        coco = 'Übersetzung' if j == 'de' else 'Interlenguaje'
                        temp = parse_template(coco, loc_text)
                        if temp :
                            pass
                        else :
                            args = {}
                            for k in l :
                                if k != j and l[k] :
                                    args[k.upper()] = l[k]
                            if args :
                                api[j].send_page(l[j], 
                                create_template(coco, args), append=True)
                    elif j in 'deenfr' and 'Monster Infobox' in loc_text :
                        pass
                    else :
                        links = api[j].lang_links(l[j])[0]
                        links[j] = links['*']
                        text = ''
                        summa = summary[j if j in summary else 'en']
                        for k in l :
                            # We avoid auto links, blank links and double links
                            if k not in links and k != j and l[k] != '':
                                text += '[[%s:%s]]' %(k, l[k])
                                summa += ' (' + k + ')'
                        if text :
                            api[j].send_page(l[j], text, summa, append=True)
                    
            elif choice == 'changer' :
                lan = input("Langue : ")
                lie = input("Nouveau lien : ")
                if lie :
                    l[lan] = lie
                    ################## USELESS ?
                    v = verif_links(lie, lan)
                    if v != {} :
                        for j in v :
                            if j != src :
                                set_link(l[src], src, links, j, v[j])
            elif choice == 'voir' :
                wb.open(api[j].url.replace('api.php', 'wiki/') + l[j])
                choice = 'changer'
                    
                    
def verif_links(title, src) :
    """ Tries to interlanguage link a page with some other pages """
    links = {}
    coco = api[src].lang_links(title)[0]
    for i in coco :
        if i != '*' :
            if api[i].exist(coco[i])[0][1] :
                links[i] = coco[i]
    text = api[src].get_text(title)[0][1]
    if src == 'de' :
        pass
    elif src == 'en' and re.search('\{\{\s*Monster Infobox', text, re.I):
        por = parse_template('Monster Infobox', text)
        for i in ('German', 'Spanish', 'French','Japanese','Chinese','Korean'):
            if i + ' Name' in por :
                if i[:2] == 'Ge' :
                    v = 'de'
                elif i[:2] == 'Sp' :
                    v = 'es'
                elif i[:2] == 'Ch' :
                    v = 'zh'
                elif i[:2] in 'FrJaKo' :
                    v = i[:2].lower()
                if api[v].exist(por[i + ' Name'])[0][1] :
                    set_link(title, src, links, v, por[i + ' Name'])
                elif api[v].exist(re.sub('<br\s*/?>.+', '', 
por[i + ' Name']))[0][1] :
                    set_link(title, src, links, v, re.rep('<br\s*/?>', '', 
                                                          por[i + ' Name']))
    elif src == 'fr' and re.search('\{\{\s*(Location|Monster) Infobox', 
                                        text, re.I):
        if re.search('\{\{\s*(Location|Monster) Infobox', 
                          text).group(1) == 'Location' :
            por = parse_template('Location Infobox', text)
            for i in ('EN', 'DE', 'ES') :
                if 'Nom ' + i in por : 
                    set_link(title, src, links, i.lower(), por['Nom ' + i])
        else :
            for i in ('Allemand', 'Anglais', 'Espagnol') :
                por = parse_template('Monster Infobox', text)
                if 'Nom ' + i in por :
                    if i[:2] == 'Al' :
                        v = 'de'
                    elif i[:2] == 'An' :
                        v = 'en'
                    else :
                        v = 'es'
                    set_link(title, src, links, v, por['Nom ' + i])
    elif src == 'fr' and re.search('\{\{\s*Objet', text, re.I ) :
        por = parse_template('Objet', text)
        if 'Anglais' in por :
            if api['en'].exist(por['Anglais'])[0][1] :
                set_link(title, src, links, v, por['Anglais'])
            
    # We try to use equivalences 
    for i in equis :
        if src not in i :
            continue
        if title.endswith(i[src]) :
            for j in i :
                if j == src :
                    continue
                if api[j].exist(title.replace(i[src], i[j]))[0][1] :
                    set_link(title, src, links, j, title.replace(i[src], i[j]))
                    
    # We try to get a page with the same name
    for i in api :
        if i == src :
            continue
        if api[i].exist(title)[0][1] :
            set_link(title, src, links, i, title)

    return links
                
def set_link(title, src, links, n_link_loc, n_link_tit) :
    """ Register new links and solves link conflicts """
    if n_link_loc == src :
        print("Auto-lien sur %s:%s (vers %s)" %(src, title,
              n_link_loc) )
    elif n_link_loc in links :
        if n_link_tit != links[n_link_loc] :
            print("Conflit de liens pour %s:%s :\n%s vers %s ou %s ? " %(
            src, title, n_link_loc, n_link_tit, links[n_link_loc]) )
    else :
        links[n_link_loc] = n_link_tit

Réorganisation des pages de composants de monstre[]

Vérifie les pages de composant de monstre, et les découpe pour enlever le tabber. Utilise le script media_moga, et des fichiers texte contenant tous les objets de MH3U, MH4U et MHGen.

component_converter.py
import xml.etree.ElementTree as ET
import re
from media_moga import moga_session, parse_template
import webbrowser as wb
from os import system
from time import sleep

games = ('MH3U', 'MH4U', 'MHGen')
m = moga_session()
url = 'https://mogapedia.fandom.com/fr/wiki/'


def say(s):
    """ Reads the string """
    system('spd-say "' + str(s).replace('"', "'") + '" -l fr -r -50')


def begin():
    """ Create the xml file with previous name, new name, name for MH3U, 4U
    and Gen for each component."""
    f = {'xml': ET.parse('Cdatabase.xml')}

    for i in games:
        f[i] = open('k' + i + '.txt', 'r')

    f['xml']


def component(obj):
    """ Returns 0 if the object is a not component, 1 if it carries category
    or component's template and 2 if it carries both. """
    if type(obj) == str:
        obj = {'title': obj}
    if not m.exist(obj['title']):
        print('Page inexistante')
        return 0
    else:
        obj['exist'] = True
    if m.is_redirect(obj['title'])[0][1]:
        return 0
    if not 'text' in obj:
        obj['title'], obj['text'] = m.get_text(obj['title'])[0]
        obj['cat'] = m.get_categories(obj['title'])[0][1]

    if '/Dépeçages' in obj['title']:
        return 0
    if re.compile('\{\{\s*Info modif objet').search(obj['text']) or re.search(
    "/MH\S*", obj['title']):
        print(obj['title'] + ' est une page principale')
        return 0
    if re.search('\{\{\s*page traitée\s*\}\}', obj['text'], re.I):
        print(obj['title'] + " : ne pas traiter")
        return 0
    marker = 0
    p = re.compile('\{\{\s*Objet[^\}]*\|\s*Catégorie\s*=\s*[Cc]om')
    if p.search(obj['text']):
        marker += 1
    p = re.compile('Catégorie:(MH\S{,10} - )?Composant de monstre')
    if obj['cat']:
        for i in obj['cat']:
            if p.search(i['title']):
                marker += 1
                break
    if marker == 1:
        print("Impossible d'évaluer la pertinence de la catégorie de "
        "{}".format(obj['title']))
    return marker


def proceed_components(maxi=5000, correct=False, publish=False):
    """ List components and check their conformity. Corrige les noms si
    l'argument correct est vrai, publie les sous-pages si l'argument publish
    est vrai et qu'il n'y a pas d'erreurs."""
    print("Récupération des fichiers...")
    # HACK : à effacer (débogage)
    global data
    files = [open(i, 'r', encoding='UTF-8') for i in [
    'k' + j + '.txt' for j in games]]
    data = [i.read() for i in files]
    data.insert(0, '\n'.join(data))
    for i in files:
        i.close()
    #print("Résultat :", replace_and_find("Cortex Tigrex", data[3]))
    if correct and 'anon' in m.logged():
        m.login()

    print("Obtention de la liste des pages...")
    # We create the dict of pages with name, text and categories
    pages = {}
    for i in m.get_pages_with_category_recursive(
    'Catégorie:Composant de monstre', maxi, 'page', n=3):
        pages[i['title']] = {
            'title': i['title'],
            'exist': True,
        }
    #Then categories, we ask for all category in once because requests are
    # grouped, so it is faster
    print('Obtention des catégories... ')
    foo = m.get_categories(list(pages.keys()))
    for i in foo:
        pages[i[0]]['cat'] = i[1]

    print('Début du traitement des informations...')
    for i in sorted(pages, key=lambda x: x.lower()):
        if i.lower() < "é".lower():
            continue
        title = i
        # We add text to the page
        text = pages[i]['text'] = m.get_text(title)[0][1]
        if component(pages[i]) == 2:
            print(title)
            # First we check if it exists, then the individual files
            for j, datum in enumerate(data):
                s = j
                # Checks alternative case
                esc = re.escape(title)
                r = re.compile('^(' + esc +')$', re.M|re.I)
                rep = replace_and_find(title, data[0])
                # If the component page is properly entitled
                if title in datum:
                    # Then we proceed text, if asked
                    split = split_page(pages[i])
                    # HACK : condition sur j pour ne faire ça qu'une fois par tour
                    if correct and not j:
                        correct_page(pages[i], split, data)
                    # If the component appears in a specific version
                    # HACK : on n'agit qu'au dernier tour de datum
                    if j == max((games.index(i) for i in split[0])) + 1:
                        r_titles = set()
                        for k, v in enumerate(data[1:]):
                            if r.search(v):
                                r_titles.update(set([(r.search(v).group(1),
                                                          games[k])]))
                            for l in replace_and_find(title, v):
                                r_titles.update(set([(l, games[k])]))
                            for l in re.findall(esc.replace(' ', ' ?'), v):
                                r_titles.update(set([(l, games[k])]))
                        # HACK : à corriger
                        #if len(r_titles) < len(split[0]):
                        #    continue
                        for l in split[0]:
                            if l not in games:
                                # On ajoute les titres pour les objets
                                # pas dans 3U, 4U et Gen
                                foo = re.search("\|\s*Nom\s*=(.+)\s*",
                                                split[0][l]).group(1).strip()
                                print("Données manquantes pour l'objet",
                                      foo, "dans " + l)
                                if input("Confirmer ce titre ? (oui/non) \
") == "oui":
                                    r_titles.update(set([(foo, l)]))
                            else:
                                # On cherche les titres liés dans la page
                                r = re.compile("\|\s*Nom\s*=(.+)\s*")
                                for k in games:
                                    if k not in split[0]:
                                        continue
                                    if not r.search(split[0][k]):
                                        continue
                                    game_title = r.search(split[0][k]
                                    ).group(1).strip()
                                    if k in (i[1] for i in r_titles):
                                        if game_title != next(filter(lambda x: x[1] == k, r_titles))[0]:
                                            say("Noms incompatibles")
                                            raise Exception ("Erreur : \
aberration de noms " + game_title + " != \
" + next(filter(lambda x: x[1] == k, r_titles))[0])
                                    elif "\n" + game_title + "\n" in data[games.index(k)+1]:
                                             r_titles.update(set([(game_title, k)]))
                        if len(r_titles) > 1:
                            print(title + ' lié à ' + str(r_titles))
                        save_c(title, j - 1, r_titles = r_titles)
                        if publish:
                            send_pages(title, split, r_titles)
                    s += 1
                elif title in data[-1]:
                    # We do not change component's name if it has the last
                    # opus name
                    pass
                elif r.search(datum):
                    n_title = r.search(datum).group(1)
                    if len(n_title) > 100:
                        print("Problème avec " + title + ' : '
                        + n_title[:100] + '...')
                        return
                    if j < 2 and title not in data[2] or j >= 2:
                        print(title + ' nommé ' + n_title + (' ('
                        + games[j - 1] + ')' if j else ''))
                        say(title + " dans " + games[j - 1])
                        if change_title(title, n_title):
                            break
                    s += 1
                elif len(replace_and_find(title, datum)):
                    if (len(rep) == 1
                        and rep[0] != title
                        and input('Valider le titre ' + rep[0] + ' ? ') == 'oui'):
                        for k in range(3, 0, -1):
                            if len(replace_and_find(title, data[k])):
                                print(title + ' nommé ' + rep[0] + (' ('
                                + games[k - 1] + ')' if j else '' ))
                                n_title = rep[0]
                                say("Meilleur nom trouvé pour le composant.")
                                break
                    else:
                        print(title + " correspond à  de multiples titres : ")
                        for k, v in enumerate(rep):
                            print("{} : {}".format(k, v))
                        say("C'est ambigüe")
                        n_title = 'voir'
                        while n_title == 'voir':
                            n_title = input("Votre choix ? (nombre/"
                            "voir/passer/autre) ")
                            if n_title == "voir":
                                wb.open(url + title)
                            elif n_title == "passer":
                                n_title = title
                            elif n_title.isdecimal() and n_title not in rep:
                                if 0 <= int(n_title) < len(rep):
                                    n_title = rep[int(n_title)]
                                else:
                                    print("Nombre invalide : " + n_title)
                                    n_title = 'voir'
                            elif n_title.isdecimal() and n_title in rep:
                                if input("Entrée ambigüe, s'agit-il du titre"
                                " ? (oui/non)") != 'oui':
                                    if 0 <= int(n_title) < len(rep):
                                        n_title = rep[int(n_title)]
                                    else:
                                        print("Nombre invalide : " + n_title)
                                        n_title = 'voir'
                            elif n_title == 'autre':
                                n_title = input('Nouveau titre (non pour '
                                'annuler)')
                                if n_title == 'non':
                                    n_title = "voir"
                            elif n_title not in rep:
                                print("Réponse non reconnue")
                                n_title = 'voir'
                    if change_title(title, n_title):
                        break
                    s += 1
                elif re.search(esc.replace(' ', ' ?'), datum):
                    n_title = re.search("(" + esc.replace(' ',' ?') + ")",
                                        datum).group(1)
                    print(title + ' nommé ' + n_title )
                    say("bip")
                    if change_title(title, n_title):
                        break
                elif title.replace(' ', '') in datum.replace(' ', ''):
                    say("Problème d'espacement.")
                    print(title + " : problème d'espacement")
                    if input("Changer de nom ? (oui/non) ") == 'oui':
                        if change_title(title):
                            break
                    s += 1
                # s == 0 only if it is the first turn with all the data
                if not s:
                    # HACK: solution temporaire, comportement indésirable sur
                    # HACK: le long terme
                    if 'MHW' in text or 'MHST' in text or 'Frontier' in text:
                        print(title + " : non traité \
(objet " + ("MHW" if "MHW" in text else "MH(ST|Frontier)") + ")")
                        break
                    print(title + ' : nom introuvable, ', end='')
                    say("Ce nom est inconnu.")
                    l = re.search('^' + '[\w\.]*\s*'.join([
                    re.escape(k[:3]) for k in title.split(' ')]) + '[\w\.]*$'
                    , datum, re.I|re.M)
                    if l:
                        if change_title(title, l.group(0)):
                            break
                        print('Changer', end='')
                    else:
                        print('changer', end='')
                    if input(' de nom ? (oui/non) ') == 'oui':
                        change_title(title)
                    break

def encode(s):
    r = s
    for i in ((' ', '0020'), ("'", '0027'), ('+', '002B'), ('.', '002E'),
              ('(', '0028'), (')', '0029') ):
                  r = r.replace(*i)
    return r


def save_c(title, game, comp=None, r_titles=set()):
    """ Save in the XML file the component and his game, unless it has already
    been registered. If the tag is not found in the root, then it will search
    for it in each root. If it is find, the program can adapt names.
    - title : name of the component
    - game : from 0 (MH3U) to 2 (MHGen)
    - r_tiles : a list of all other possible tiltes. Useful for research"""
    t = ET.parse('Cdatabase.xml')
    root = t.getroot()

    if game == - 1: print("game == -1, est-ce normal ?")
    ver = games[game]
    if comp is None:
        # First we look for the object in each node
        for obj in root:
            for i in obj:
                if i.text == title or i.text in (x[0] for x in r_titles):
                    # If we have found the object
                    if ver in obj and obj.find(ver).text != title:
                        print("Aberration de noms :\n"
                        "- Ancien : " + obj.find(ver).text +
                        "\n- Nouveau : " + title + "\n pour " + ver)
                    else:
                        save_c(title, game, obj, r_titles)
                    return t.write('Cdatabase.xml', 'UTF-8', True)

        # If nothing was find, we add the object
        obj = ET.SubElement(root, 'page')

        for i in ('prev', 'new'):
            j = ET.SubElement(obj, i)
            coco = (min if i=='prev' else max)(filter(lambda x:x[1] in games,
                   r_titles),
                   key=lambda x:x[1])
            j.text = coco[0]
            j.set('game', coco[1])
        for r_tit, r_ver in filter(lambda x:x[1] in games, r_titles):
            coco = ET.SubElement(obj, r_ver)
            coco.text = r_tit
        t.write('Cdatabase.xml', 'UTF-8', True)
    else:
        # on modifie comp, il n'y a rien à renvoyer
        for r_tit, r_ver in filter(lambda x:x[1] in games, r_titles):
            # If the new name of the object is overdated
            if comp.find('new').attrib['game'] < r_ver:
                comp.find('new').set('game', r_ver)
            if comp.find('prev').attrib['game'] > r_ver:
                comp.find('prev').set('game', r_ver)
            if comp.find(r_ver) is None:
                coco = ET.Element(r_ver)
                coco.text = r_tit
                # If we can insert an element at a specific location
                if len(comp) - 2 > games.index(r_ver):
                    comp.insert(games.index(r_ver) + 2, coco)
                else:
                    comp.append(coco)

def replace_and_find(string, data, rec=False, n=1):
    """ Essaie de plusieurs remplacements dans la chaîne pour trouver le même
    mot dans les données. Peut faire jusqu'à quatres remplacements en même
    temps.

    Par défaut, on renvoie autant de remplaçants sérieux que l'on trouve. La
    méthode est d'utilisé un certain nombre de remplacements usuels, par
    exemple : œ et oe, puis on exploite les abbréviations de mots courrament
    utilisées dans le jeu, mots tronqués, suivis ou non d'un point"""


    if "\n" + string + "\n" in data:
        return [string]
    l = []
    if not rec:
        # Tries specific replacements
        patterns = [('E', 'É'), ('+', ' +'), ('oe', 'œ'), ('Oe', 'Œ'),
                    ('de ', ''), ('des ', '')]
        pat = tuple(filter(lambda x: x[0] in string, patterns))
        # we reverse all the patterns
        pat += tuple((i[::-1] for i in pat))
        pat = (('', ''),) + pat
        # On s'occupe d'abord des remplacements considérés fiables
        for i in range(len(pat)):
            # We append 0 in order to do no replacement
            for j in (('', ''),) + pat[i:]:
                s = string.replace(*pat[i]).replace(*j)
                for k in replace_and_find(s, data, True):
                    if k not in l:
                        l.append(k)

    initials = re.findall(r"\w+\.?|\+", string)

    if n <= max(map(len, initials)):
        s = "^"
        for i in range(len(initials)):
            s += re.escape(initials[i][0])
            for j in range(1, min(n + 1, len(initials[i]))):
                if j == 1:
                    s += "(?:" + re.escape(initials[i][j])
                else:
                    s += "(?:" + re.escape(initials[i][j])
            s += (")?" * min(n, len(initials[i]) - 1))
            s += r"\w{0," + str(len(initials[i]) - n - 1) + r"}(?:\.\s?|\s)"
        s += "?$"
        # Tous les caractères sont optionnels sauf le premier
        #s = "^" + r"\w*(?:\.\s?|\s)".join(
        #        (re.sub("(?<=.)(.)", r"(?=(?:\1|\.))?", re.escape(j[:n])) for j in initials))
        #s += r"\w*\.?$"
        #print("s :", s)
        # On effectue une recherche par début de mot
        m = [j.strip() for j in re.findall(s, data, re.I|re.M)]
        #print("m :", m)
        for j in m:
            pat = r"\w*\s".join(re.findall(r"\w+", j)) + r"\w*"
            """print("j :", j, "pattern : ", pat, "search :",
                  bool(re.search(pat, string, re.I)),
                  " join :", r"\w*\s?".join(re.findall(r"\w+", j)) + r"\w*")"""
            if not re.search(pat, string, re.I):
                #print("Suppression : ", j)
                m.remove(j)
        #print("Restant : ", m)
        if 0 < len(m):
            step = replace_and_find(string, data, True, n + 1)
            #print("step :", step)
            if step:
                m = step
            elif n < 3:
                return []
        if rec:
            return m
        else:
            l.extend([j for j in m if j not in l])
    #print("Final :", l)
    return l


def split_page(page):
    """ Return a tuple containig three elements :
            * a dict of subpages in an article.
            * the previous text that does not belong to any section
            * the appendix text that does not belong to any section
    The function plits the page using a tabber. If the tabber doesnt exist,
    uses categories on page.

    The dict keys are game names where values are the text specific to a game.
    Could be empty.

    Le text non contenu dans les tabbers est renvoyé dans les arguments 1 et
    2 du tuple de sortie
    """
    if re.search(r'<tabber\s*>', page['text'], re.M):
        # If there is a tabber
        r = re.compile(r'</?tabber>|\|-\|')
        l = r.split(page['text'])
        r = re.compile(r'\s*(MH[\w\d]+)\s*=\s*(.+)\s*', re.S)
        # Parts which sould be kept
        rep = re.compile(r'^\s*(\{\{\s*Objet[^\}]+\}\})\s*$', re.S|re.I|re.M)
        out = {}
        # Texte à ajouter au début et à la fin
        pre_text, app_text = '', ''
        for i in l:
            # HACK : solution temporaire
            if rep.search(i) and not i.replace(rep.search(i).group(1), "").strip():
                pre_text += rep.search(i).group(1) + '\n'
                if '[[Catégorie:Ébauche]]' in page['text']:
                    app_text += '\n[[Catégorie:Ébauche]]'
            elif r.search(i):
                out[r.search(i).group(1)] = r.search(i).group(2)
            elif '[[Catégorie:' in i or not i.strip():
                continue
            else :
                print("Texte inclassable : " + encode(i))
        return out, pre_text, app_text
    else:
        if 'cat' not in page :
            page['cat'] = m.get_categories(page['title'])[0][1]
        r = re.compile(r'(MH[\w\d]{,10}) - (?:Objet|Composant de monstre)')
        game = ''
        for i in page['cat']:
            if r.search(i['title']):
                if game == '':
                    game = r.search(i['title']).group(1)
                elif game != r.search(i['title']).group(1) :
                    say("Versions multiples.")
                    print('Deux versions présentes : %s et %s' %(game,
                                            r.search(i['title']).group(1)))
                    if input('Continuer (oui/non) ? ') != 'oui':
                        return {}
        if game == '':
            print(page['title'] + ' version inidentifiable')
            say(page['title'] + '. bip bip !')
        return {game: page['text']}, '', ''

def change_title(title, n_title=''):
    """ Move the page from "title" to "n_title" with a textual interface.
    Return True if name was changed, False if not. """
    if n_title == '':
        n_title = input('Nouveau nom : ')
    if m.exist(n_title)[0][1] and not m.is_redirect(n_title)[0][1]:
        print("La page " + n_title + " existe déjà !")
    egg = ''
    while egg != 'oui' and egg != 'non':
        egg = input("Renommer ? (oui/non/voir/modifier/autre nom) ")
        if egg == 'oui':
            m.move_page(title, n_title, "Mise à jour des noms de composant. \
Renommage approuvé par [[Utilisateur:Houmgaor|Houmgaor]].")
            sleep(5)
            return True
        elif egg == 'voir':
            wb.open(url + title)
        elif egg == 'modifier':
            wb.open(url + title + '?action=edit')
        elif egg == 'autre nom':
            return change_title(title)
    return False

def create_table():
    """ Creates the file containing the table of all elements in the xml
    tree """
    r = ET.parse('Cdatabase.xml').getroot()
    text = """{| class="mogatable sortable"
|+ Tableau des composants de monstre
|-
! Ancien nom
! Nouveau nom
! Nom MH3U
! Nom MH4U
! Nom MHGen
"""
    t = ('prev', 'new', 'MH3U', 'MH4U', 'MHGen')
    for i in r:
        text += "|-\n"
        for j in t:
            k = i.find(j)
            text += '| '
            if k is None:
                text += '/'
            else:
                text += '[[' + k.text
                if j in t[2:]:
                    text += '/' + j
                text += ']]'
            text += '\n'

    text += "|}"
    with open('Exchange file.txt', 'w') as f:
        f.write(text)

def rename_descriptions(title='Template:Monster Description', lim=10000):
    """ N'a rien a voir, changement de "Double Cross" à Generations pour
    Monster Description"""
    for i in m.get_transclusions(title, lim, redir='nonredirects', ns='0') :
        i['text'] = m.get_text(i['title'])[0][1]
        print(i['title'])
        pre_tem = parse_template('Monster Description', i['text'], True)
        if "Monster Hunter Double Cross" not in pre_tem :
            print(pre_tem[-200:])
            continue
        n_tem = pre_tem.replace("Monster Hunter Double Cross",
                          "Monster Hunter Generations Ultimate", 1)
        #if input("Proceed (ok) ? ") == 'ok' :
        r = m.send_page(i['title'], i['text'].replace(pre_tem, n_tem, 1),
                    summary="Remplacement de Double Cross par Generations " +
                    "Ultimate dans le modèle [[Modèle:Monster Description]].",
                    minor=True)
        if r:
            print("Succès")
            sleep(5)
        else :
            print("Error")
            break

def correct_page(page, split, data):
    """ Vérifie et corrige la page (si possible). S'occupe notamment du
    nom du composant dans le modèle principal, remplace les ====<u> par =="""
    if split == {}:
        return
    summa = ""
    text = page['text']
    h3 = bool(re.match("^===(?!=\=)", text, flags=re.M))
    for game, game_text in split[0].items():
        if (not game_text.strip()):
            if (input("Section {} vide, souhaitez-vous l'effacer ? (oui/non)\
 ".format(game)) == 'oui'):
                text = re.sub("(?:\|-\|)?\s*" + game + "\s*=\s*", "", text)
                summa += "Suppression de la section " + game + ". "
        elif (text.count(game_text) != 1):
            print("Texte :", game_text)
            raise Warning("Le texte de l'opus {} apparaît plusieurs fois ({} \
fois) dans la page, est-ce normal ?\
".format(game, text.count(game_text)))
        sect = text[text.index(game_text):text.index(game_text)+len(game_text)]
        p_sect = sect
        if game not in games:
            if game.upper() in games:
                sect = sect.replace(game, game.upper())
                summa += "{}{}. ".format(game, game.upper())
                game = game.upper()
            else:
                continue
        datum = data[games.index(game) + 1]
        p = re.compile(r'\|(\s*)Nom(\s*)=\s*([^\|]+)', re.M)
        if p.search(sect):
            # L'infobox se trouve dans la section
            title = p.search(sect).group(3).strip()
        elif p.search(split[1]):
            # L'infobox se trouve hors du tabber (ou de la section)
            title = p.search(split[1]).group(3).strip()
            if len(split[0]) > 1:
                # title appears in previous text, however they are multiple
                # games, we check if the object has the same name in each game
                ok = True
                for i in split[0]:
                    ok = title in data[games.index(i) + 1]
                    if not ok:
                        print("Plusieurs sections peuvent correspondre au \
titre, arrêt. ({} pas dans les données {})".format(title, i))
                        return
                if not ok:
                    break
            # TODO : ligne très bizarre, à vérifier
            #p_sect = sect = split[1]
            #game = 'principale'
            #datum = data[games.index(tuple(split[0].keys())[0]) + 1]
        else:
            raise Warning("Titre introuvable !")
            continue
        if title not in datum:
            rep = replace_and_find(title, datum)
            esc = re.escape(title)
            r = re.compile('^(' + esc +')$', re.M|re.I)
            if len(rep) == 1:
                sect = p.sub(r'|\1Nom\2= ' + rep[0] + '\n', sect)
                summa += "{}{} (dans l'infobox principale, section {}). \
                ".format(title, rep[0], game)
            elif len(rep) > 1:
                print("Plusieurs remplacements possibles : ", rep)
                ans = "autre"
                while ans == "autre":
                    # TODO : has to be done
                    print("PAS encore implémenté !!!!!!!!!!!")
                    ans = input('Lequel choisisez vous (entrez son numéro, ou '
                    '"autre/passer") ? ')
                    if ans.isdigit():
                        ans = rep[int(ans)]
                    elif ans == 'passer':
                        return
                    elif ans == 'autre':
                        ans = input("Nouveau nom (annuler) : ")
                        ans = "autre "if ans == 'annuler' else ans
                summa += " {}{} (dans l'infobox \
                principale, section {}). ".format(title, r, game)
                sect = p.sub(r'|\1Nom\2= ' + rep[0] + '\n', sect)
            elif r.search(datum):
                sect = p.sub(r'|\1Nom\2= ' + r.search(datum).group(0) + '\n',
                             sect)
                summa += "{}{} (dans l'infobox principale, section {}). \
                ".format(title, r.search(datum).group(0), game)
        text = text.replace(p_sect, sect)
    foo = re.sub(r"^===?=?\s*(?:<u>)?\s*([^<:?]+?)(?:(?:</u>)?|\s*|([?:]?))*===?=?$",
                 lambda s: "== " + s.group(1) +
                 (" " + s.group(2) if s.group(2) is not None else "") + " ==",
                 text, flags=re.M)
    if foo != text:
        text = foo
        summa += "Changement des niveaux de titre. "
    if text != page['text']:
        if h3:
            print("Ancien texte :\n\n", page['text'],
                  "\n\nNouveau texte :\n\n", text)
            print("ATTENTION : il peut y avoir des titres niveau 3 !")
            say("Niveau 3 détecté !")
            if input("Valider ? (oui/non) ") != "oui":
                return
        assert m.send_page(page['title'], text, summa), "Erreur dans la \
correction"
        say("La page " + page['title'] + " a été corrigée.")
        sleep(5)


def send_pages(title, split, r_titles):
    """ Envoie les sous-pages, grâce à l'argument split, puis envoie la page
    principale """
    # Envoie des sous-pages
    for game in order_games(split[0].keys()):
        subpage = ''
        foo = tuple(filter(lambda x: x[1] == game, r_titles))
        if foo and foo[0][0] != title:
            subpage += "{{orphan|" + title + "}}\n"
        elif len(foo) == 0:
            raise Warning("Le jeu " + game + " ne figure pas dans les titres \
liés !")
        subpage = "{{page traitée}}\n"
        if split[1]:
            subpage += split[1] + "\n"
        subpage += split[0][game]
        if split[2]:
            subpage += "\n" + split[2]
        assert m.send_page(foo[0][0] + "/" + game, subpage,
                    "Réorganisation des composants de monstre. "), "\
Erreur dans l'envoie"
        sleep(5)
    # Envoie de la page principale
    main = "{{page traitée}}\n{{Info modif objet\n"
    n = max((len(i[1]) for i in r_titles))
    for i in order_games(tuple(x[1] for x in r_titles)):
        name, game = next(filter(lambda x: x[1] == i, r_titles))
        main += "| " + game.ljust(n) + " = "
        main += (name if name != title else "Y") + "\n"
    main += "}}\n[[Catégorie:Composant de monstre]]"
    assert m.send_page(title, main, "Réorganisation des composants de monstre.\
"), "Erreur dans l'envoie"
    say("Les pages " + title + " ont été envoyées.")
    sleep(10)

def order_games(ite):
    """ Returns the iterable with the element sorted in appropriate order """
    order = ("MH1", "MHF", "MHF2", "MHFU", "MHTri", "MH3U", "MH4U", "MHGen",
             "MHF-Z", "MHST", "MHW", "MHGU")
    for i in order:
        if i in ite:
            yield i
    for i in ite:
        if i not in order:
            say("Jeu inconnu : " + i + " !")
            print("Jeu inconnu " + i + " le tri peut être inefficace")
            yield i

m.login('HoumgaBot')
#rename_descriptions()
proceed_components(15000, correct=True, publish=True)

Relecture des pages d'objet[]

Suit la #Réorganisation des pages des composants de monstre, vérifie que les pages d'objet et de composant ont les bons noms grâce à des bases de données des noms d'objet (MH3U, MH4U, MHGen, MHST, MHW, MHGU et MHWI à ce jour). Corrige les niveaux de titre, met à jour l'infobox Modèle:Objet, et Modèle:Info modif objet.

items_converter.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Mar 30 23:29:25 2020.

@author: Houmgaor

Ce script repmplie de nombreuses fonctions POST-migration des pages objets et
composants de monstre :
    - Pour TOUTES les pages concernant un opus spécifique, et des liens vers
    des objet, utilisation du modèle "Lien objet"

    - Lié à la mise à jour des objets
        - Migration des dernières pages (celles qui ne figurent pas dans les
        opus principaux)
        - Vérification des pages migrées
        - Suppression des modèles "page traitée"
        - Utilisation du modèle orphan pour lié les pages orphelines
    - Suppression du texte "Modele:Orphan" oublié sur plusieurs pages par
    Draco :P
    - Corrections de l'infobox principale "Objet"
        - Argument "Anglais" -> "Nom anglais"
        - Vérification des arguments "Catégorie" et "Type" ?
    - Pour le modèle "Info modif objet"
        - Changement de nom ?
        - Ajout d'un argument pour savoir si c'est un objet ou un composant de
        monstre ?
"""

import re
import webbrowser as wb
from os import system
from time import sleep
from pymoga import parse_template, create_template, moga_session

games = ('MH3U', 'MH4U', 'MHST', 'MHGen', 'MHW', 'MHGU', 'MHWI')
# valid games names
v_games = ("MH", "MHF", "MHF2", "MHFU", "MHTri", "MH3U", "MH4U", "MHGen",
           "MHFZ", "MHST", "MHW", "MHGU", "MHWI", "MHRise")
m = moga_session()
url = 'https://mogapedia.fandom.com/fr/wiki/'


def say(s):
    """Read the string alloud."""
    system('spd-say "' + str(s).replace('"', "'") + '" -l fr -r -50')


def item(obj):
    """Return 0 if the object is not an item."""
    if type(obj) == str:
        return item({'title': obj})
    if m.is_redirect(obj['title'])[0][1]:
        return 0
    if 'text' not in obj:
        obj['title'], obj['text'] = m.get_text(obj['title'])[0]
        obj['cat'] = m.get_categories(obj['title'])[0][1]
    if 'ns' not in obj:
        obj.update(m.get_namespace(obj)[0])

    if '/Dépeçages' in obj['title']:
        return 0
    if re.compile(r"\{\{\s*Info modif objet").search(obj['text']) or re.search(
            r"/MH\S*", obj['title']):
        return 0
    marker = 0
    p = re.compile(r'\{\{\s*Objet\s*\|')
    if p.search(obj['text']):
        marker += 1
    p = re.compile(r'Catégorie:(MH\S{,10} - )?(Composant de monstre|Objet)')
    if obj['cat']:
        for cat in obj['cat']:
            if p.search(cat):
                marker += 1
                break
    if marker == 1:
        print("Impossible d'évaluer la pertinence de la catégorie de "
              "{}".format(obj['title']))
    return marker


def check_item_title(title, game):
    """
    Vérifie si le nom d'un objet est bon.

    Renvoie le bon nom s'il est trouvé (ne marche qu'avec 3U, 4U et Gen),
    sinon demande à l'humain.
    """
    global data
    if 'data' not in globals():
        load_data()

    if game not in games:
        print(title + " non vérifié (" + game + ")")
        say("Opus inconnu")
        coco = input("Valider le titre (oui/non/passer) ? ")
        if coco == "oui":
            return title
        elif coco == "non":
            return input("Nouveau titre ? ")
        else:
            return

    if "\n" + title + "\n" in data[games.index(game) + 1]:
        return title
    # Checks alternative case
    esc = re.escape(title)
    r = re.compile('^(' + esc + ')$', re.M | re.I)
    datum = data[games.index(game) + 1]
    rep = replace_and_find(title, data[0])
    if r.search(datum):
        n_title = r.search(datum).group(1)
        if len(n_title) > 100:
            print("Problème avec " + title + ' : ' + n_title[:100] + '...')
            return
        return n_title
    elif len(replace_and_find(title, datum)):
        if (len(rep) == 1 and rep[0] != title
           and input('Valider le titre ' + rep[0] + ' ? ') == 'oui'):
            for k in range(3, 0, -1):
                if len(replace_and_find(title, data[k])):
                    print(title + ' nommé ' + rep[0] + (' ('
                          + games[k - 1] + ')'))
                    n_title = rep[0]
                    say("Meilleur nom trouvé pour l'objet.")
                    return n_title
        else:
            print(title + " correspond à  de multiples titres : ")
            for k, v in enumerate(rep):
                print("{} : {}".format(k, v))
            say("C'est ambigüe")
            n_title = 'voir'
            while n_title == 'voir':
                n_title = input("Votre choix ? (nombre/"
                                "voir/passer/autre) ")
                if n_title == "voir":
                    wb.open(url + title)
                elif n_title == "passer":
                    n_title = title
                elif n_title.isdecimal() and n_title not in rep:
                    if 0 <= int(n_title) < len(rep):
                        n_title = rep[int(n_title)]
                    else:
                        print("Nombre invalide : " + n_title)
                        n_title = 'voir'
                elif n_title.isdecimal() and n_title in rep:
                    if input("Entrée ambigüe, s'agit-il du titre ? \
(oui/non)") != 'oui':
                        if 0 <= int(n_title) < len(rep):
                            n_title = rep[int(n_title)]
                        else:
                            print("Nombre invalide : " + n_title)
                            n_title = 'voir'
                elif n_title == 'autre':
                    n_title = input('Nouveau titre (non pour annuler)')
                    if n_title == 'non':
                        n_title = "voir"
                elif n_title not in rep:
                    print("Réponse non reconnue")
                    n_title = 'voir'
        return n_title
    elif re.search(esc.replace(' ', ' ?'), datum):
        n_title = re.search("(" + esc.replace(' ', ' ?') + ")", datum).group(1)
        return n_title
    else:
        say("Nom introuvable")
        n_title = "voir"
        while n_title == "voir":
            n_title = input("Nouveau nom (nom/passer/voir) ")
            if n_title == "passer":
                return
            elif n_title == "voir":
                wb.open(url + title)
            else:
                return n_title


def encode(s):
    """Encode les caractères spéciaux."""
    r = s
    for i in ((' ', '0020'), ("'", '0027'), ('+', '002B'), ('.', '002E'),
              ('(', '0028'), (')', '0029')):
        r = r.replace(*i)
    return r


def replace_and_find(string, data, rec=False, n=1):
    """
    Fais des remplacements dans la chaîne pour trouver une correspondance.

    Peut faire jusqu'à quatres remplacements en même
    temps. Par défaut, on renvoie autant de remplaçants sérieux que l'on
    trouve. La méthode est d'utiliser un certain nombre de remplacements
    usuels, par exemple : œ et oe, puis on exploite les abbréviations de mots
    courrament utilisées dans le jeu, mots tronqués, suivis ou non d'un point.
    """
    if "\n" + string + "\n" in data:
        return [string]
    li = []
    if not rec:
        # Tries specific replacements
        patterns = [('E', 'É'), ('+', ' +'), ('oe', 'œ'), ('Oe', 'Œ'),
                    ('de ', ''), ('des ', '')]
        pat = tuple(filter(lambda x: x[0] in string, patterns))
        # we reverse all the patterns
        pat += tuple((i[::-1] for i in pat))
        pat = (('', ''),) + pat
        # On s'occupe d'abord des remplacements considérés fiables
        for i in range(len(pat)):
            # We append 0 in order to do no replacement
            for j in (('', ''),) + pat[i:]:
                s = string.replace(*pat[i]).replace(*j)
                for k in replace_and_find(s, data, True):
                    if k not in li:
                        li.append(k)

    initials = re.findall(r"\w+\.?|\+", string)

    if n <= max(map(len, initials)):
        s = "^"
        for i in range(len(initials)):
            s += re.escape(initials[i][0])
            for j in range(1, min(n + 1, len(initials[i]))):
                if j == 1:
                    s += "(?:" + re.escape(initials[i][j])
                else:
                    s += "(?:" + re.escape(initials[i][j])
            s += (")?" * min(n, len(initials[i]) - 1))
            s += r"\w{0," + str(len(initials[i]) - n - 1) + r"}(?:\.\s?|\s)"
        s += "?$"
        # On effectue une recherche par début de mot
        m = [j.strip() for j in re.findall(s, data, re.I | re.M)]
        for j in m:
            pat = r"\w*\s".join(re.findall(r"\w+", j)) + r"\w*"
            if not re.search(pat, string, re.I):
                m.remove(j)
        if 0 < len(m):
            step = replace_and_find(string, data, True, n + 1)
            if step:
                m = step
            elif n < 3:
                return []
        if rec:
            return m
        else:
            li.extend([j for j in m if j not in li])
    return li


def split_page(page):
    """
    Return a tuple.

    It shall contain three elements :
            * a dict of subpages in an article.
            * the previous text that does not belong to any section
            * the appendix text that does not belong to any section
    The function plits the page using a tabber. If the tabber doesnt exist,
    uses categories on page.

    The dict keys are game names where values are the text specific to a game.
    Could be empty.

    Le text non contenu dans les tabbers est renvoyé dans les arguments 1 et
    2 du tuple de sortie
    """
    if re.search(r'<tabber\s*>', page['text'], re.M):
        # If there is a tabber
        r = re.compile(r'</?tabber>|\|-\|')
        li = r.split(page['text'])
        r = re.compile(r'\s*(MH[\w\d]+)\s*=\s*(.+)\s*', re.S)
        # Parts which sould be kept
        rep = re.compile(r'^\s*(\{\{\s*Objet[^\}]+\}\})\s*$',
                         re.S | re.I | re.M)
        out = {}
        # Texte à ajouter au début et à la fin
        pre_text, app_text = '', ''
        for i in li:
            # HACK : solution temporaire
            if rep.search(i) and not i.replace(
                    rep.search(i).group(1), "").strip():
                pre_text += rep.search(i).group(1) + '\n'
                if '[[Catégorie:Ébauche]]' in page['text']:
                    app_text += '\n[[Catégorie:Ébauche]]'
            elif r.search(i):
                out[r.search(i).group(1)] = r.search(i).group(2)
            elif '[[Catégorie:' in i or not i.strip():
                continue
            else:
                print("Texte inclassable : " + encode(i))
        return out, pre_text, app_text
    else:
        if 'cat' not in page:
            page['cat'] = m.get_categories(page['title'])[0][1]
        r = re.compile(r'(MH[\w\d]{,10}) - (?:Objet|Composant de monstre)')
        game = ''
        for i in page['cat']:
            if r.search(i['title']):
                if game == '':
                    game = r.search(i['title']).group(1)
                elif game != r.search(i['title']).group(1):
                    say("Versions multiples.")
                    print('Deux versions présentes : {} et {}'.format(game,
                          r.search(i['title']).group(1)))
                    if input('Continuer (oui/non) ? ') != 'oui':
                        return {}
        if game == '':
            print(page['title'] + ' version inidentifiable')
            say(page['title'] + '. bip bip !')
        return {game: page['text']}, '', ''


def change_title(title, n_title=''):
    """
    Move the page from "title" to "n_title" with a textual interface.

    Return True if name was changed, False if not.
    """
    if n_title == '':
        n_title = input('Nouveau nom : ')
    if m.exist(n_title)[0][1] and not m.is_redirect(n_title)[0][1]:
        print("La page " + n_title + " existe déjà !")
    egg = ''
    while egg != 'oui' and egg != 'non':
        egg = input("Renommer ? (oui/non/voir/modifier/autre nom) ")
        if egg == 'oui':
            m.move_page(title, n_title, "Mise à jour des noms de composant. \
Renommage approuvé par [[Utilisateur:Houmgaor|Houmgaor]].")
            sleep(5)
            return True
        elif egg == 'voir':
            wb.get('chromium-browser').open_new_tab(url + title)
        elif egg == 'modifier':
            wb.get('chromium-browser').open_new_tab(url+title+'?action=edit')
        elif egg == 'autre nom':
            return change_title(title)
    return False


def correct_page(page, split, data):
    """
    Vérifie et corrige la page (si possible).

    S'occupe notamment du nom du composant dans le modèle principal, remplace
    les ====<u> par ==.
    """
    if split == {}:
        return
    summa = ""
    text = page['text']
    h3 = bool(re.match(r"^===(?!=\=)", text, flags=re.M))
    for game, game_text in split[0].items():
        if (not game_text.strip()):
            if (input("Section {} vide, souhaitez-vous l'effacer ? (oui/non)\
 ".format(game)) == 'oui'):
                text = re.sub(r"(?:\|-\|)?\s*" + game + r"\s*=\s*", "", text)
                summa += "Suppression de la section " + game + ". "
        elif (text.count(game_text) != 1):
            print("Texte :", game_text)
            raise Warning("Le texte de l'opus {} apparaît plusieurs fois ({} \
fois) dans la page, est-ce normal ?\
".format(game, text.count(game_text)))
        sect = text[text.index(game_text):text.index(game_text)+len(game_text)]
        p_sect = sect
        if game not in games:
            if game.upper() in games:
                sect = sect.replace(game, game.upper())
                summa += "{}{}. ".format(game, game.upper())
                game = game.upper()
            else:
                continue
        datum = data[games.index(game) + 1]
        p = re.compile(r'\|(\s*)Nom(\s*)=\s*([^\|]+)', re.M)
        if p.search(sect):
            # L'infobox se trouve dans la section
            title = p.search(sect).group(3).strip()
        elif p.search(split[1]):
            # L'infobox se trouve hors du tabber (ou de la section)
            title = p.search(split[1]).group(3).strip()
            if len(split[0]) > 1:
                # title appears in previous text, however they are multiple
                # games, we check if the object has the same name in each game
                ok = True
                for i in split[0]:
                    ok = title in data[games.index(i) + 1]
                    if not ok:
                        print("Plusieurs sections peuvent correspondre au \
titre, arrêt. ({} pas dans les données {})".format(title, i))
                        return
                if not ok:
                    break
        else:
            raise Warning("Titre introuvable !")
            continue
        if title not in datum:
            rep = replace_and_find(title, datum)
            esc = re.escape(title)
            r = re.compile('^(' + esc + ')$', re.M | re.I)
            if len(rep) == 1:
                sect = p.sub(r'|\1Nom\2= ' + rep[0] + '\n', sect)
                summa += "{}{} (dans l'infobox principale, section {}). \
                ".format(title, rep[0], game)
            elif len(rep) > 1:
                print("Plusieurs remplacements possibles : ", rep)
                ans = "autre"
                while ans == "autre":
                    # TODO : has to be done
                    print("PAS encore implémenté !!!!!!!!!!!")
                    ans = input('Lequel choisisez vous (entrez son numéro, ou '
                                '"autre/passer") ? ')
                    if ans.isdigit():
                        ans = rep[int(ans)]
                    elif ans == 'passer':
                        return
                    elif ans == 'autre':
                        ans = input("Nouveau nom (annuler) : ")
                        ans = "autre "if ans == 'annuler' else ans
                summa += " {}{} (dans l'infobox \
                principale, section {}). ".format(title, r, game)
                sect = p.sub(r'|\1Nom\2= ' + rep[0] + '\n', sect)
            elif r.search(datum):
                sect = p.sub(r'|\1Nom\2= ' + r.search(datum).group(0) + '\n',
                             sect)
                summa += "{}{} (dans l'infobox principale, section {}). \
                ".format(title, r.search(datum).group(0), game)
        text = text.replace(p_sect, sect)
    foo = r"^===?=?\s*(?:<u>)?\s*([^<:?]+?)(?:(?:</u>)?|\s*|([?:]?))*===?=?$"
    foo = re.sub(foo,
                 lambda s: "== " + s.group(1) +
                 (" " + s.group(2) if s.group(2) is not None else "") + " ==",
                 text, flags=re.M)
    if foo != text:
        text = foo
        summa += "Changement des niveaux de titre. "
    if text != page['text']:
        if h3:
            print("Ancien texte :\n\n", page['text'],
                  "\n\nNouveau texte :\n\n", text)
            print("ATTENTION : il peut y avoir des titres niveau 3 !")
            say("Niveau 3 détecté !")
            if input("Valider ? (oui/non) ") != "oui":
                return
        assert m.send_page(page['title'], text, summa), "Erreur dans la \
correction"
        say("La page " + page['title'] + " a été corrigée.")
        sleep(5)


def send_pages(title, split, r_titles):
    """
    Envoie les sous-pages, grâce à l'argument split.

    Envoie ensuite la page principale.
    """
    # Envoie des sous-pages
    for game in order_games(split[0].keys()):
        subpage = ''
        foo = tuple(filter(lambda x: x[1] == game, r_titles))
        if foo and foo[0][0] != title:
            subpage += "{{orphan|" + title + "}}\n"
        elif len(foo) == 0:
            raise Warning("Le jeu " + game + " ne figure pas dans les titres \
liés !")
        subpage = "{{page traitée}}\n"
        if split[1]:
            subpage += split[1] + "\n"
        subpage += split[0][game]
        if split[2]:
            subpage += "\n" + split[2]
        assert m.send_page(foo[0][0] + "/" + game, subpage,
                           "Réorganisation des composants de monstre. "), "\
Erreur dans l'envoie"
        sleep(5)
    # Envoie de la page principale
    main = "{{page traitée}}\n{{Info modif objet\n"
    n = max((len(i[1]) for i in r_titles))
    for i in order_games(tuple(x[1] for x in r_titles)):
        name, game = next(filter(lambda x: x[1] == i, r_titles))
        main += "| " + game.ljust(n) + " = "
        main += (name if name != title else "Y") + "\n"
    main += "}}\n[[Catégorie:Composant de monstre]]"
    assert m.send_page(title, main, "Réorganisation des composants de monstre.\
"), "Erreur dans l'envoie"
    say("Les pages " + title + " ont été envoyées.")
    sleep(10)


def order_games(ite):
    """Return the iterable with the element sorted in appropriate order."""
    order = ("MH1", "MHF", "MHF2", "MHFU", "MHTri", "MH3U", "MH4U", "MHGen",
             "MHFZ", "MHST", "MHW", "MHGU")
    for i in order:
        if i in ite:
            yield i
    for i in ite:
        if i not in order:
            say("Jeu inconnu : " + i + " !")
            print("Jeu inconnu " + i + " le tri peut être inefficace")
            yield i


def link_pages(main, subpage):
    """
    Modifie la page principale en la liant à la sous-page.

    main est la page d'objet parent, subpage l'enfant.
    """
    text = n_text = m.get_text(main)[0][1]
    info_modif = parse_template("Info modif objet", text)
    explode_title = re.match("(.+)/(.+)", subpage)
    if explode_title:
        item_name, game = explode_title.group(1), explode_title.group(2)
        info_modif[game] = item_name if item_name != main else 'Y'
        n_text = n_text.replace(
            parse_template("Info modif objet", text, True),
            create_template("Info modif objet", info_modif)
        )
        print("Ajout de la sous-page " + subpage)
        m.send_page(main, n_text, "Ajout de " + subpage)
        sleep(4)
    else:
        print("Impossible de trouver le jeu dans \"" + subpage + "\"")


def load_data():
    """Charge les bases de données d'objets si nécessaire."""
    global data
    # Fichiers de données de tous les objets (composants inclus) 3U, 4U et Gen
    files = (open(i, 'r', encoding='UTF-8') for i in [
            'k' + j + '.txt' for j in games])
    data = [i.read() for i in files]
    data.insert(0, '\n'.join(data))
    for i in files:
        i.close()


def proceed_pages(maxi=5, correct=False, publish=False):
    """Boucle principale, appel d'autres fonctions pour tout traiter."""
    print("Récupération des fichiers...")
    load_data()

    if correct and 'anon' in m.logged():
        m.login()

    print("Début du traitement...")
    # We create the dict of pages with name, text and categories
    page = {}
    for i in m.get_all_pages(maxi, redir='nonredirects', **{'from': 'Poudre '}):
        title = i['title']
        print(title)
        # text est la référence, n_text sera modifié si besoin
        text = n_text = m.get_text(title)[0][1]
        coco = m.get_categories(title)[0]
        if 'categories' in coco:
            cat = coco['categories']
        else:
            cat = []
        # résumé des modifications
        summa = ""
        # Modification mineure ou non
        minor = True
        page = {
            'title': title,
            'missing': False,
            'cat': [i['title'] for i in cat],
            'text': text,
            'type': ""
        }
        # Suppression du modèle "page traitée"
        coco = parse_template("page traitée", n_text, True)
        if coco:
            # Suppression d'un retour à la ligne en début de texte
            n_text = n_text.replace(coco, "").lstrip()
            summa += "Suppression [[Modèle:Page traitée]]. "
        # On vérifie s'il s'agit d'une page principale
        if re.search(r"\{\{\s*(?:Modèle:)?\s*Info modif objet", n_text):
            p = parse_template("Info modif objet", n_text)
            for i in p:
                if i not in games:
                    continue
                # On vérifie le titre de l'objet
                item_title = check_item_title(title if p[i] == "Y" else p[i],
                                              game=i)
                if item_title and item_title != (title if p[i] == "Y"
                                                 else p[i]):
                    summa += p[i] + " → " + item_title + ". "
                    p[i] = "Y" if item_title == title else item_title
                    minor = False
                # On visite la sous-page
            if summa:
                n_text = n_text.replace(
                        parse_template("Info modif objet", text, True),
                        create_template("Info modif objet", p))
        elif item(page) == 2:
            # On vérifie si on doit découper la page
            if 'tabber' in n_text and '/' not in title:
                split = split_page(page)
                say("Page à découper")
                print("Page principale (" + title + ")")
                wb.get('chromium-browser').open_new_tab(
                    url + title + '?action=edit&summary=Découpage en '
                    + 'sous-pages.')
                dic = {'Informations': ''}
                for j in split[0]:
                    dic[j] = 'Y'
                    print(j, split[1] + split[0][j] + split[2], sep='\n\n')
                    wb.get('chromium-browser').open_new_tab(
                        url + title + '/' + j
                        + '?action=edit&summary=Déplacement depuis '
                        + '[[' + title + ']].&preload=' + title)
                print(create_template("Info modif objet", dic))
                continue
            # On vérifie le titre de la page
            if correct:
                item_name, game = re.search(r"(.+)/(.+)", title).groups()
                n_title = check_item_title(item_name, game)
                if n_title and title != n_title + "/" + game:
                    n_title += "/" + game
                    say("Renommage suggéré")
                    print("La page \"" + title + "\" se nomme probablement "
                          "\"" + n_title + "\"")
                    coco = input("Approuvez-vous le renommage (oui/non) ? ")
                    if coco == "oui":
                        m.move_page(title, n_title, "Correction du nom")
                        sleep(4)
                        continue
            # On change les arguments "Anglais" et "Japonais"
            infobox = parse_template("Objet", n_text)
            n_info = {}
            for arg in infobox:
                if arg == "Anglais":
                    n_info["Nom anglais"] = infobox["Anglais"]
                elif arg == "Japonais":
                    n_info["Nom japonais"] = infobox["Japonais"]
                else:
                    n_info[arg] = infobox[arg]
            # Remplacement des arguments de l'infobox
            if n_info != infobox:
                summa += "Mise à jour de l'infobox principale. "
                n_text = n_text.replace(
                    parse_template("Objet", n_text, True),
                    create_template("Objet", n_info))
            # Uniformisation des titres de section
            section = (r"^===?=?\s*(?:<u>)?\s*([^<:?]+?)"
                       r"(?:(?:</u>)?|\s*|([?:])?)*===?=?$")
            coco = n_text
            n_text = re.sub(
                section,
                # On ne retient que les "?" et pas les ":"
                lambda s: "== " + s.group(1) +
                (" " + s.group(2) if s.group(2) is not None
                 and s.group(2) != ":" else "") + " ==",
                n_text,
                flags=re.M
            )
            # Correction des titres de section eux-mêmes
            n_text = n_text.replace("== A quoi ser", "== À quoi ser")
            n_text = n_text.replace("== Combinaison ==", "== Combinaisons ==")
            if coco != n_text:
                summa += "Uniformisation des sections. "
            # On s'occupe du modèle "Orphan"
            orphan = parse_template("Orphan", n_text)
            if orphan is not None:
                if '0' in orphan:
                    main = orphan['0']
                else:
                    say("Page orpheline")
                    print("Merci de bien vouloir donner le nom de la page "
                          "principale associée à \"" + title + "\"")
                    main = input("Page principale : ")
                if correct:
                    link_pages(main, title)
            """
            # Ajout du Menu-Objets
            pat = r"\{\{\s*(?:[mM]odèle:)\s*Menu-"
            if orphan and '0' in orphan:
                pat += re.escape(orphan['0'])
            else:
                pat += r"[^\}]+"
            pat += r"\}\}"
            if not re.search(pat, n_text, re.I):
                n_text = "{{Menu-" + main + "}}\n" + n_text
                summa += "Ajout de [[Modèle:Menu-" + main + "]]. "
            """

        if n_text != text:
            if publish:
                m.send_page(title, n_text, summa, minor=minor)
                sleep(4)
            else:
                print("Ancien texte :", text, "Nouveau texte :", n_text,
                      "Résumé :", summa, sep='\n')

proceed_pages(200, correct=True, publish=True)

Mise à jour des titres et combinaisons des pages d'objets[]

Après avoir bien vérifié les page d'objet et de composants, il est temps d'implémenter de nouvelles fonctionnalités ! Le but original était de supprimer les tabviewes des pages principales. Les missions de ce script sont les suivantes :

  • Corriger les niveaux de titre
  • Modèle:Info modif objet est remplacé par Modèle:Menu-Objet.
    • Lorsque la page principale et la sous-page portent le même nom, remplacer le "| Jeu = Y" par "| Jeu"
  • La section description passe dans l'infobox Modèle:Objet.
  • La section "Comment l'obtenir" devient "Obtention" et "À quoi sert-il ?" devient "Utilité".
  • Plusieurs changements sur les combinaisons :
    • Modèle:Combinaison ajoute désormais la section du même nom à la page. Toutes les pages doivent donc porter ce modèle.
    • Uniformisation des différents styles de combinaisons vers ce modèle.
  • Les catégories sont passées au pluriel : "Catégorie:MH3U - Objet" devient Catégorie:MH3U - Objets.
posttabviewobjects.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 14 17:50:57 2021

@author: Houmgaor

Ce script remplie de nombreuses fonctions suite au retrait des tabviewes et
POST-migration des pages objets et composants de monstre :
    - Pour TOUTES les pages concernant un opus spécifique, et des liens vers
    des objet, utilisation du modèle "Lien objet"

    - Corrections de l'infobox principale "Objet"
        - Vérification des arguments "Catégorie" et "Type" ?
    - Pour le modèle "Info modif objet"
        - Renommage vers "Modèle:Menus-Objets"
        - Ajout d'un argument pour savoir si c'est un objet ou un composant de
        monstre ?
"""
import re
from time import sleep
from os import system
import mwparserfromhell as mwp
from pymoga.pymoga import moga_utils as mu
from pymoga.pymoga import moga_session
#from pymoga import moga_utils as mu
MS = moga_session()


def say(text):
    """Lit à haute voix."""
    system('spd-say "' + str(text).replace('"', "'") + '" -l fr -r -50')


def get_object_icon(title):
    """Renvoie le titre de l'icône de l'objet."""
    text = MS.get_text(title)[0][1]
    dic = mu.parse_template("Objet", text)
    if "Image" not in dic:
        raise Exception('L\'objet "' + title + '" n\'a pas d\'icône !')
    image = dic["Image"]
    if image.startswith('[['):
        return re.search(r'\[\[\s*(?:File|Fichier|Image)\s*:'
                         r'\s*(.+\.png)(?:\||\]\])', image).groups()[0]
    return re.search(r".+\.png", image).group()


def update_combs(text, object_color):
    """Met à jour la section combinaisons."""
    # Passe du féminin au masculin de la couleur
    transcolor = {
        'blanche':         'Blanc',
        'bleu':            'Bleu',
        'bleu foncée':     'Bleu foncé',
        'grise':           'Gris',
        'jaune':           'Jaune',
        'marron':          'Marron',
        'orange':          'Orange',
        'rose':            'Rose',
        'rouge foncée':    'Rouge foncé',
        'rouge':           'Rouge',
        'verte':           'Vert',
        'violette':        'Violet',
        'violette foncée': 'Violet foncé',
    }

    # Modèle combinaison classique
    comb = mu.parse_template("Combinaison", text)
    # Combinaison en toutes lettres de type
    # Composant1 x + Composant2 x = Obtenu x
    all_text = re.compile(r"(.+) \+ (.+)\s*=(.+)")
    if comb:
        comb_raw = mu.parse_template("Combinaison", text, True)
        if comb["Couleur"].lower() == object_color.lower():
            comb.pop("Couleur")
        else:
            print("Couleurs différentes pour les combinaisons :")
            print("Infobox :", object_color)
            print("Modèle combinaison :", comb["Couleur"])
            if input("Accepter l'uniformisation (oui/non) ? ") == "oui":
                comb.pop("Couleur")
                text = text.replace(comb_raw,
                                    mu.create_template("Combinaison", comb))
    elif re.search(r"\{\{\s*Combinaison ", text):
        for color in transcolor:
            comb = mu.new_parse_template("Combinaison " + color, text)
            if not comb:
                continue
            comb = comb[-1]
            comb_raw = str(comb)
            n_comb = {}
            if transcolor[color].lower() != object_color.lower():
                print("Couleurs différentes pour les combinaisons :")
                print("Infobox :", object_color)
                print("Modèle combinaison :", transcolor[color])
            for param in comb.params:
                if not re.match(r".+ \d\d?", str(param.name)):
                    pass
                elif re.match("Icon ", str(param.name)):
                    # Change style et nom
                    file = param.value.filter_wikilinks()[0].title
                    file = re.search(
                        r'(?:[fF]ile|[fF]ichier|Image)\s*:\s*([^\|\]]+)',
                        str(file))[1]
                    number = re.search(r'\d\d?', str(param.name))[0]
                    n_comb["Icône " + number] = file
                else:
                    n_comb[str(param.name)] = str(param.value)
            text = text.replace(comb_raw,
                                mu.create_template("Combinaison", n_comb))
    elif all_text.search(text):
        comb_dic = {}
        for i, m in enumerate(all_text.finditer(text)):
            comb_dic["N° " + str(i + 1)] = ''
            comb_dic["Icône " + str(i + 1)] = ''
            comb_dic["Nom " + str(i + 1)] = m.groups()[2]
            comb_dic["Composant1 " + str(i + 1)] = m.groups()[0]
            comb_dic["Composant2 " + str(i + 1)] = m.groups()[1]
            comb_dic["Réussite " + str(i + 1)] =   ''
            comb_dic["Obtenu " + str(i + 1)] =     ''
        text = mu.create_template("Combinaison", comb_dic) + text[m.end():]
    elif re.search(r"Aucune?(?: combinaison possible)?"
                   r"|Ne peut pas"
                   r"|\s*<br\s*/?>\s*"
                   r"|N/A", text):
        text = re.sub(r"^.+$", "{{Combinaison}}", text, count=1, flags=re.M)
    # Tableau de combinaisons
    elif re.search(r"\{\|.+\|\}", text, re.S):
        wikicode = mwp.parse(re.search(r"\{\|.+\|\}", text, re.S).group())
        headers = [x.groups()[0] for x in re.finditer(r'^!.+\|\s*([^\|]+)$',
                            str(wikicode), re.U|re.M)]
        contents = [x.groups()[0]
        for x in re.finditer(r'^\|[^\|\n]+\|([^\n]+)(?=\n\|)$', str(wikicode),
                          re.U|re.M)]
        if headers == ['n°', 'Nom', 'Composant 1', 'Composant 2', 'Réussite',
                       'Obtenu']:
            comb_dic = {}
            for i, line in enumerate(contents):
                si = " " + str(i // 7 + 1)
                if i % 7 == 0:
                    comb_dic["N°" + si] =         line
                elif i % 7 == 1:
                    file = mwp.parse(contents[i]).filter_wikilinks()[0].title
                    comb_dic["Icône" + si] =      re.search(r':(.+)',
                            str(file)).groups()[0]
                elif i % 7 == 2:
                    comb_dic["Nom" + si] =        line
                elif i % 7 == 3:
                    comb_dic["Composant1" + si] = line
                elif i % 7 == 4:
                    comb_dic["Composant2" + si] = line
                elif i % 7 == 5:
                    comb_dic["Réussite" + si] =   line.replace('%', '')
                elif i % 7 == 6:
                    comb_dic["Obtenu" + si] =     line.replace('x', '')
            #print(comb_dic)
            text = text.replace(str(wikicode),
                                mu.create_template("Combinaison", comb_dic))
            #print(text)
    else:
        print("/!\\------------------------------------------------------/!\\")
        print('Texte non reconnu !\n' + text)
        say("Non reconnu : " + text)
        sleep(2)
    return text

def update_subpage_layout(text, summary):
    """Update the objects subpages to the no tabview era."""
    # Remplacement des modèles combinaison
    # Changement des noms et niveaux de sections
    root = mu.get_sections_tree(text)
    if re.search(r"Description\s?:?", root[0][0].title):
        description = root[0][0].text.replace("''", "").strip()
        obj_template = mu.parse_template("Objet", root[0].text)
        obj_raw_template = mu.parse_template("Objet", root[0].text,
                                             True)
        obj_template['Description'] = description
        root[0].text = root[0].text.replace(
            obj_raw_template, mu.create_template("Objet",
                                                 obj_template))
        # root[0][0].text = "{{Description|" + description + "}}"
        root[0][0].text = ""
        root[0][0].merge_with_previous(sep='')
        summary += "Déplacement de la description. "
    else:
        print('Titre non reconnu pour la première '
              'section "{}"'.format(root[0][0].title))

    # On vient de supprimer une section, donc on recommence à vérifier la
    # section 0

    # On corrige les niveaux de titre
    changed_level = False
    subs = tuple(root[0])
    for i in subs:
        if i.level != 2:
            i.set_level_with_hierarchy(2)
            changed_level = True
    if changed_level:
        summary += "Correction niveaux de titres. "

    gain = re.compile(r"\s*Comment l(?:'|es )obtenir\s?:?")
    if gain.search(root[0][0].title):
        summary += gain.search(root[0][0].title).group()
        summary += " → Obtention. "
        root[0][0].title = "Obtention"
    else:
        print('Titre non reconnu pour la deuxième '
              'section "{}"'.format(root[0][1].title))

    use = re.compile(r"[AÀÁ] quoi ser(?:t[\s-]?|ven)?t[ -][ie]l(?:le)?s? ?\??")
    if use.search(root[0][1].title):
        summary += use.search( root[0][1].title).group()
        summary += "→ Utilité. "
        root[0][1].title = "Utilité"
        root[0][1].text = root[0][1].text.strip()
    else:
        print('Titre non reconnu pour la troisième '
              'section "{}"'.format(root[0][1].title))

    if len(root[0]) == 3:
        root[0][2].text = update_combs(
            root[0][2].text,
            mu.parse_template("Objet", text)["Couleur"])
        root[0][2].merge_with_previous()
    else:
        root[0][1].text = root[0][1].text.strip() + "\n{{Combinaison}}"
    summary += "Mise à jour Combinaison. "


    if not root.check_integrity():
        print(root.pretty_print())
        print('---------------------------------------------------------')
        say("Titres non consistants.")
        raise Exception('Les niveaux de titre ne sont pas consistants !')
    text = root.pretty_print()
    return text, summary

def check_updated_subpage(text, summary, title):
    """Verify new page layout, and correct it if necessary."""
    template = mu.parse_template("Objet", text)
    raw = mu.parse_template("Objet", text, True)
    basename = re.sub(
            r"(?: \(munition\))?/MH(?:F|P3rd|Tri|\d|Gen|ST|G|W|Rise|Frontier)[UI12]?",
            "", title)
    if basename != template['Nom']:
        say("Les titres diffèrent !")
        if mu.default_choice(
                f'La page "{basename}" porte le titre "{template["Nom"]}" dans '
                'son infobox. Remplacer le titre de l\'infobox par celui de '
                'la page', 'oui'):
            template["Nom"] = basename
            text = text.replace(raw, mu.create_template("Objet", template))
            summary += "Correction du nom dans l'infobox principale. "
    if 'Description' not in template:
        say(f'L\'objet "{title}" n\'a pas de description !')
        raise Exception("Pas de description !")
    root = mu.get_sections_tree(text)
    if root[0][0].title != "Obtention":
        say('Mauvais titre pour la section Obtention !')
        raise Exception("Mauvaise titre Obtention")
    if root[0][1].title != "Utilité":
        use = re.compile(r"[AÀÁ] quoi ser(?:t[\s-]?|ven)?t[ -][ie]l(?:le)?s? ?\??")
        if use.search(root[0][1].title):
            summary += "Renommage section Utilité. "
            text = use.sub("Utilité", text)
        else:
            say('Mauvais titre pour la section Utilité !')
            raise Exception("Mauvais titre Utilité")

    if len(root[0]) > 2:
        say(f"Trop de sections détectées ({len(root[0])}) !")
        raise Exception("Trop de sections !")

    if "\n\n\n{{Combinaison" in text:
        text = re.sub(r"\n+\{\{Combinaison", "\n\n{{Combinaison", text)
        summary += "Suppression retours à la ligne surnuméraires. "

    return text, summary


def proceed_main_pages(maxi=5):
    """Proceed all page that contains "Template:Info modif objet"."""
    pages = MS.get_transclusions("Template:Info modif objet", maxi=maxi,
                                 redir="nonredirects")
    for page in sorted(pages, key=lambda x: x['title']):
        print(page['title'])
        # On extrait le texte
        n_text = p_text = page['text'] = MS.get_text(page['title'])[0][1]
        summary = ""
        # Renommage de Modèle:Info modif objet
        dic = mu.parse_template("Info modif objet", n_text)
        raw_template = mu.parse_template("Info modif objet", n_text, True)
        # On supprime les "= Y"
        i = 0
        copy = tuple(dic)
        for k in copy:
            if dic[k] in ("Y", page['title']):
                dic.pop(k)
                dic[str(i)] = k
                i += 1
        n_text = n_text.replace(raw_template,
                                mu.create_template("Menu-Objet", dic))
        summary += "Changement de modèle : Modèle:Info modif objet "
        summary += "→ [[Modèle:Menu-Objet]]. "
        # Supression des catégories ?
        del_cat = False
        for cat in re.findall(r"(\[\[\s*Cat[eé]gorie\s*:\s*(.+?)\s*\]\])",
                              n_text, re.I):
            if re.match("(?:MH.{0,7} - )?(?:Objets?|Composants? de monstre)",
                        cat[1]):
                n_text = n_text.replace(cat[0], "")
                del_cat = True
        if del_cat:
            summary += "Suppression catégories inutiles. "
        # Suppression des retours à la ligne
        n_text = re.sub(r"\n+", "\n", n_text)
        if p_text != n_text:
            print("Ancien texte", p_text, "Nouveau texte :", n_text, sep='\n\n')
            MS.send_page(page['title'], n_text, summary)
            sleep(5)


def proceed_subpages(maxi=5):
    """Proceed subpages."""
    for page in sorted(
            MS.get_transclusions("Template:Objet", maxi=maxi,
                                 redir="nonredirects"),
            key=lambda x: x['title']):
        if '/' not in page['title']:
            continue
        print(page['title'])
        # On extrait le texte
        n_text = p_text = page['text'] = MS.get_text(page['title'])[0][1]
        # Résumé de modification
        summary = ""
        # Supression des catégories inutiles
        for cat in re.findall(r"(\[\[\s*Cat[eé]gorie\s*:\s*(.+?)\s*\]\])",
                              n_text, re.I):
            pat = re.compile(r"MH.{0,7} - (?:Objet|Composant de monstre)"
            r"|Objets?|Composants? de monstre")
            if pat.match(cat[1]):
                summary += "Suppression catégories. "
                n_text = n_text.replace(cat[0], "")
        # La page peut avoir déjà été traitée, on vérifie cela
        if 'Description' not in mu.parse_template("Objet", n_text):
            n_text, summary = update_subpage_layout(n_text, summary)
        mineur = False
        if n_text == p_text:
            mineur = True
        n_text, summary = check_updated_subpage(n_text, summary, page['title'])
        if not mineur:
            summary = summary.replace(
                    "Suppression retours à la ligne surnuméraires. ", "")
        if n_text != p_text:
            print('Mise à jour')
            MS.send_page(page['title'], n_text, summary, minor=mineur)
            sleep(5)


def main():
    """Run main functions."""
    proceed_main_pages(20000)
    proceed_subpages(20000)

if __name__ ==  '__main__':
    try:
        main()
    except Exception as err:
        say('Erreur !')
        raise Exception(err)
    input("Entrez quelque chose pour quitter ")
Advertisement