Utilisateur:Eikubot/pour Chrisaix.py

Ce programme nécessite Utilisateur:Eikubot/neti.py. Je ne l’ai testé que sous linux, mais il devrait marcher sous Windows et Mac OS aussi.

Pour l’utiliser, le mettre, ainsi que neti.py, dans un dossier qui contient un fichier nommé "creabot_articles.txt", qui contient les articles selon le format décrit un peu plus bas (dans le commentaire entre """ """). Il nécessite python 2.5 ou ultérieur, et il faut bien que son répertoire de travail soit celui dans lequel il se trouve.

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Bot pour créer des articles en masse.
On suppose que le fichier est formaté comme suit :

==== [[nom de l’article]] ====
Contenu de l’article

==== [[titre d’un autre article]] ====
Contenu de l’article

etc.

Il y a une certaine tolérance à l’erreur : les espaces 
et les crochets [[]] sont facultatifs entre les ==== ====.
"""

import neti
import os, sys
import re
import time
import atexit

interface = neti.interface

# 5 secondes d’attente minimum entre chaque création
interface.DELAY = 5 

## CHOSES À CHANGER SI LE BOT EST EMPLOYÉ PAR QUELQU’UN D’AUTRE QU’Eikubot !!!
interface.EMERGENCY_SHUTOFF_PAGE = "Utilisateur:Eikubot/Arrêt"
interface.USER_NAME = "Eikubot"
interface.USER_PASS = neti.ask_password ()

REPERTOIRE_DE_TRAVAIL = r"./" 
# À remplacer si besoin est par le chemin complet du 
# répertoire dans lequel se trouvent les fichiers de travail

def main ():
	"""
	"""
	FICHIER_ARTICLES = "./creabot_articles.txt"
	FICHIER_TODO = "./creabot_todo.txt"
	FICHIER_DONE = "./creabot_done.txt"
	FICHIER_ERROR = "./creabot_error.txt"

	PREFIX = ""
	# Pour la phase de test du bot, on ne travaille pas sur
	# l’espace principal. Supprimer la ligne une fois le bot
	# testé.
	PREFIX = "Utilisateur:Eiku/brouillons/"

	def load_list (filename):
		"""Juste une fonction simple pour charger une liste"""
		ret = open (filename, 'r').read ().replace ("\r\n","\n").split ("\n")
		while '' in ret: ret.remove ('')
		return ret
	def write_list (filename, liste):
		"""Juste pour écrire le fichier"""
		sys.stdout.write ("Saving %s…"%filename)
		open (filename, 'w').write ("\r\n".join (liste))
		print "OK"
	## 1 ) Charger la bdd des articles

	# on lit le fichier et on change les fins de lignes win→unix
	if os.path.isfile (FICHIER_ARTICLES):
		brut = open (FICHIER_ARTICLES, 'r').read ().replace ("\r\n", "\n")
	else:
		sys.stderr.write ("Error: file doesn’t exist: %s"%FICHIER_ARTICLES)
		return
	
	# on commence par obtenir une liste non hiérarchisée, comme suit :
	# ["premier élément, inutile", "titre", "article", "titre", "article", ...]
	separes = re.split (r"(?m)^====\s*(?:\[\[)?(.*?)(?:\]\])?\s*====\n?$", brut)

	# on en fait une liste comme suit :
	# [("titre", "article"), ("titre", "article"), ("titre", "article"), etc.]
	articles = dict (zip (separes [1::2], separes [2::2]))

	## 2 ) Charger les listes (todo, done et error)

	# articles restant à faire
	if os.path.exists (FICHIER_TODO): articles_todo = load_list (FICHIER_TODO)
	else: articles_todo = separes [1::2]
	# articles faits
	if os.path.exists (FICHIER_DONE): articles_done = load_list (FICHIER_DONE)
	else: articles_done = []
	# articles ayant causé des erreurs
	if os.path.exists (FICHIER_ERROR): articles_error = load_list (FICHIER_ERROR)
	else: articles_error = []

	# supprimer les articles faits/bugués de la liste des à faire
	for nom in articles_done + articles_error:
		if nom in articles_todo:
			articles_todo.remove (nom)

	## 3 ) Dire à python de sauver les listes à la sortie, quoi qu’il arrive
	def sauver_toutes_les_listes ():
		write_list (FICHIER_TODO, articles_todo)
		write_list (FICHIER_DONE, articles_done)
		print "..saved %s"%FICHIER_DONE
		write_list (FICHIER_ERROR, articles_error)
		print "..saved %s"%FICHIER_ERROR
	atexit.register (sauver_toutes_les_listes)

	## 4 ) Créer le plus d’articles possible

	def create_one_article ():
		nom_article_courant = articles_todo [0]
		print "\n\n=========== %s ==========="%nom_article_courant
		page = neti.Page (PREFIX + nom_article_courant)

		commentaire = "(bot) : upload depuis bdd de [[Utilisateur:Chrisaix|]]"
		contenu = articles [nom_article_courant]
		try:
			resultat, reponseAPI = page.create (
				commentaire, # commentaire de création
				contenu      # contenu de la page
				)
		# Erreur de programmation
		except:
			sys.stderr.write ("Error with article %s"%nom_article_courant)
			articles_todo.remove (nom_article_courant)
			articles_error.append (nom_article_courant)
			return False, "unknown", nom_article_courant
		
		# Page non créée (erreur de réseau, blocage du bot, serveurs surchargés, etc.): 
		probleme_reseau_persiste = False
		if not resultat:
			if 'code="articleexists"' in reponseAPI:
				articles_todo.remove (nom_article_courant)
				articles_done.append (nom_article_courant)
				# on l’ajoute aussi aux erreurs
				articles_error.append (nom_article_courant)
				return False, "already there", nom_article_courant
			probleme_reseau_persiste = True
			# on réessaye en se reloguant
			neti.logging_in ()
			for i in range (2): # on fait maximum deux nouvelles tentatives
				print "Tentative n°%d"%(i+1)
				resultat, reponseAPI = page.create (commentaire, contenu)
				if resultat:
					print "Ouf, c'est enfin bon."
					probleme_reseau_persiste = False
					break

		# Toujours pas réussi à créer
		if probleme_reseau_persiste:
			sys.stderr.write ("Error: couldn't create page %s"%nom_article_courant)
			return False, "network", nom_article_courant
		# page créée
		articles_todo.remove (nom_article_courant)
		articles_done.append (nom_article_courant)
		return True, "", nom_article_courant
	
	neti.logging_in ()
	while articles_todo:
		# Essaye de créer l’article courant
		resultat, nom_erreur, nom_article = create_one_article ()

		# Affiche le résultat. S’il y a un problème de réseau, arrête le bot. 
		if not resultat:
			print "Article non créé."
			if nom_erreur == "network":
				print "Arrêt du bot: erreurs de réseau."
				print "1 Vérifiez votre connexion à internet."
				print "2 Vérifiez que le bot n'a pas de bug ;-)"
				print "3 Vérifiez que le bot n'a pas été bloqué."
				print "4 Vérifiez que les serveurs MediaWiki sont up."
				print "         http://ganglia.wikimedia.org"
				return
			elif nom_erreur == "already there":
				print "L’article %s existe déjà."%nom_article
		else:
			print "%s créé."%nom_article
			print "%d articles deja créés. Encore %d à créer"% (
					len (articles_done),
					len (articles_todo))
		# vérifie régulièrement que personne n’a demandé l’arrêt d’urgence
		# (par défaut, toutes les 30 secondes)
		neti.verifier_arret_urgence ()

class Complete_logging:
	def __init__ (self, file):
		self.log = open (file, "a")
		self.lastflush = time.time()
	def write (self, txt):
		if isinstance (txt, unicode): 
			txt = txt.encode ("utf-8")
		self.log.write (txt)
		sys.__stdout__.write (txt)
	def flush (self):
		sys.__stdout__.flush ()
		if time.time > self.lastflush + 10:
			self.log.flush ()
			self.lastflush = time.time ()

os.chdir (REPERTOIRE_DE_TRAVAIL)
sys.stdout = Complete_logging ("./creabot_complete_log.txt")

if __name__ == "__main__":
	try: main ()
	except KeyboardInterrupt: print "\033[31mLeaving: User typed Ctrl+C.\033[0m"