#!/usr/bin/python3 """ This bot resets a (user) sandbox with predefined text. This script understands the following command-line arguments: ¶ms; Furthermore, the following command line parameters are supported: -hours:# Use this parameter if to make the script repeat itself after # hours. Hours can be defined as a decimal. 0.01 hours are 36 seconds; 0.1 are 6 minutes. -delay:# Use this parameter for a wait time after the last edit was made. If no parameter is given it takes it from hours and limits it between 5 and 15 minutes. The minimum delay time is 5 minutes. -text The text that substitutes in the sandbox, you can use this when you haven't configured clean_candbox for your wiki. -summary Summary of the edit made by bot. Overrides the default from i18n. This script is a :py:obj:`ConfigParserBot `. All local parameters can be given inside a scripts.ini file. Options passed to the script are priorized over options read from ini file. .. seealso:: :python:`Supported .ini File Structure ` For example: [clean_sandbox] # the parameter section for clean_sandbox script summary = Bot: Cleaning sandbox text = {{subst:Clean Sandbox}} hours: 0.5 delay: 7 """ # # (C) Pywikibot team, 2006-2022 # # Distributed under the terms of the MIT license. # import datetime import sys import time import pywikibot from pywikibot import i18n, pagegenerators from pywikibot.bot import Bot, ConfigParserBot from pywikibot.exceptions import EditConflictError, NoPageError content = { 'commons': '{{Sandbox}}\n', 'meta': '{{Meta:Sandbox/Please do not edit this line}}' '', 'test': '{{Sandbox}}\n' '== Please start your testing below this line ==', 'wikidata': '{{Please leave this line alone (sandbox heading)}}', 'wikibooks': { 'ru': '{{/Шапка}}\n' '', }, 'wikivoyage': { 'es': '' '{{Zona de pruebas}}' '\n\n' '== Las pruebas en esta sección ==\n', }, 'als': '{{subst:/Vorlage}}', 'ar': '{{عنوان الملعب}}\n', 'arz': '{{عنوان السبوره}}\n', 'az': '\n{{Qaralama dəftəri}}\n' '', 'bar': '{{Bitte erst NACH dieser Zeile schreiben! (Begrüßungskasten)}}\n', 'bn': '{{খেলাঘর}}', 'cs': '{{Tento řádek neměňte}}\n\n\n' "== Bábovičky ==\n#'''první'''\n#''druhá''\n*třetí\n" "*'''''čtvrtá'''''\n pátá\n;šestá\n:sedmá", 'da': '{{subst:Sandkasse tekst}}', 'de': '{{subst:Wikipedia:Spielwiese/Vorlage}}', 'en': '{{Sandbox heading}}\n', 'eo': '{{Bonvolu ne forigi tiun ĉi linion (Provejo)}}', 'fa': '{{subst:Wikipedia:ربات/sandbox}}', 'fi': '{{subst:Hiekka}}', 'fr': '{{subst:Préchargement pour Bac à sable}}', 'he': '{{ארגז חול}}\n', 'hi': '{{User sandbox}}\n', 'id': '{{Bakpasir}}\n', 'it': '{{sandbox}}' '', 'ja': '{{subst:サンドボックス}}', 'ko': '{{연습장 안내문}}', 'ksh': '{{subst:/Schablon}}', 'mzn': '{{ویکی‌پدیا:چنگ‌مویی صفحه/پیغوم}}\n', 'my': '{{subst:Sandbox reset}}', 'nds': '{{subst:/Vörlaag}}', 'ne': '{{User sandbox}}\n' '', 'nl': '{{subst:Wikipedia:Zandbak/schoon zand}}', 'nn': '{{sandkasse}}\n', 'no': '{{Sandkasse}}\n}}', 'pl': '{{Prosimy - NIE ZMIENIAJ, NIE KASUJ, NIE PRZENOŚ tej linijki ' '- pisz niżej}}', 'pt': '' '{{página de testes}}\n', 'ru': '{{/Пишите ниже}}\n' '', 'simple': '{{subst:/Text}}', 'sco': '{{subst:Saundbox}}', 'shn': '{{subst:Sandbox reset}}', 'sr': '{{песак}}', 'sv': '{{subst:Sandlådan}}', 'th': '{{กระบะทราย}}\n', 'tr': '{{/Bu satırı değiştirmeden bırakın}}', 'zh': '{{subst:User:Sz-iwbot/sandbox}}\n', } sandbox_titles = ('Q3938', 'Q28939665') # This is required for the text that is shown when you run this script # with the parameter -help. docuReplacements = { '¶ms;': pagegenerators.parameterHelp, } class SandboxBot(Bot, ConfigParserBot): """Sandbox reset bot.""" available_options = { 'hours': -1.0, # do not repeat if hours < 0 'delay': -1, 'text': '', 'summary': '', } def __init__(self, **kwargs) -> None: """Initializer.""" super().__init__(**kwargs) if self.opt.delay < 0: d = min(15, max(5, int(self.opt.hours * 60))) self.delay_td = datetime.timedelta(minutes=d) else: d = max(5, self.opt.delay) self.delay_td = datetime.timedelta(minutes=d) self.site = pywikibot.Site() self.translated_content = self.opt.text or i18n.translate( self.site, content) if not self.translated_content: raise RuntimeError( 'No content is given for sandbox pages, exiting.') if not self.generator: pages = [] for item in sandbox_titles: p = self.site.page_from_repository(item) if p is not None: pages.append(p) if not pages: pywikibot.bot.suggest_help(missing_generator=True) sys.exit() self.generator = pages def run(self) -> None: """Run bot.""" self.site.login() while True: wait = False now = time.strftime('%d %b %Y %H:%M:%S (UTC)', time.gmtime()) for sandbox_page in self.generator: pywikibot.info('Preparing to process sandbox page ' + sandbox_page.title(as_link=True)) if sandbox_page.isRedirectPage(): pywikibot.warning( '{} is a redirect page, cleaning it anyway' .format(sandbox_page.title(as_link=True))) try: text = sandbox_page.text if self.opt.summary: translated_msg = self.opt.summary else: translated_msg = i18n.twtranslate( self.site, 'clean_sandbox-cleaned') subst = 'subst:' in self.translated_content pos = text.find(self.translated_content.strip()) if text.strip() == self.translated_content.strip(): pywikibot.info( 'The sandbox is still clean, no change necessary.') elif subst and sandbox_page.userName() == self.site.user(): pywikibot.info( 'The sandbox might be clean, no change necessary.') elif pos != 0 and not subst: sandbox_page.put(self.translated_content, translated_msg) pywikibot.showDiff(text, self.translated_content) pywikibot.info( 'Standard content was changed, sandbox cleaned.') else: edit_delta = (datetime.datetime.utcnow() - sandbox_page.latest_revision.timestamp) delta = self.delay_td - edit_delta # Is the last edit more than 'delay' minutes ago? if delta <= datetime.timedelta(0): sandbox_page.put( self.translated_content, translated_msg) pywikibot.showDiff(text, self.translated_content) pywikibot.info('Standard content was changed, ' 'sandbox cleaned.') else: # wait for the rest pywikibot.info( 'Sandbox edited {:.1f} minutes ago...' .format(edit_delta.seconds / 60.0)) pywikibot.info( f'Sleeping for {delta.seconds // 60} minutes.') pywikibot.sleep(delta.seconds) wait = True except EditConflictError: pywikibot.info( '*** Loading again because of edit conflict.\n') except NoPageError: pywikibot.info( '*** The sandbox is not existent, skipping.') continue if self.opt.hours < 0: pywikibot.info('\nDone.') return if not wait: if self.opt.hours < 1.0: pywikibot.info( f'\nSleeping {self.opt.hours * 60} minutes, now {now}') else: pywikibot.info( f'\nSleeping {self.opt.hours} hours, now {now}') pywikibot.sleep(self.opt.hours * 60 * 60) def main(*args: str) -> None: """ Process command line arguments and invoke bot. If args is an empty list, sys.argv is used. :param args: command line arguments """ opts = {} local_args = pywikibot.handle_args(args) gen_factory = pagegenerators.GeneratorFactory() for arg in local_args: opt, _, value = arg.partition(':') if opt.startswith('-'): opt = opt[1:] else: continue if opt == 'hours': opts[opt] = float(value) elif opt == 'delay': opts[opt] = int(value) elif opt == 'text': opts[opt] = value or pywikibot.input( 'What text do you want to substitute?') elif opt == 'summary': opts[opt] = value or pywikibot.input('Enter the summary:') else: gen_factory.handle_arg(arg) generator = gen_factory.getCombinedGenerator() bot = SandboxBot(generator=generator, **opts) bot.run() if __name__ == '__main__': main()