Gebruiker:Wikiwernerbot/sinebot.py
Script voor het toevoegen van {{Afzender}} aan niet-ondertekende overlegbijdrages, naar aanleiding van een botverzoek. Dit script is gedeeltelijk gebaseerd op en:User:Ritchie333/arcsinebot.py en en:User:SineBot/ChangeLog.
import pywikibot
import re
import datetime
site = pywikibot.Site()
pagesignedre = re.compile(r'^Wikipedia:(Samenvoegen|Te beoordelen|Verzoekpagina)[^/]*/[^/]+$')
def ispagesigned(page, edittype):
ns = page.namespace().id
if ns%2 == 1 and not 'rchief' in page.title():
return True # Overlegnaamruimtes hebben een oneven nummer.
if page.title() in ['Help:Helpdesk', 'Wikipedia:Achterkamertje', 'Wikipedia:Misbruikfilter/Foutieve meldingen']:
return True
if pagesignedre.search(page.title()) and edittype != 'new':
return True
if ns == 4: # Wikipedianaamruimte
istalkpage = False
cats = page.categories()
for cat in cats:
if cat.title() == 'Categorie:Wikipedia:Overlegruimte':
istalkpage = True
elif cat.title() in ['Categorie:Wikipedia:Nuweg', 'Categorie:Wikipedia:Afgehandeld']:
return False
if istalkpage:
return True
return False
nosigntags = ['AWB', 'OAuth CID: 429', 'mw-new-redirect', 'mw-changed-redirect-target', 'mw-undo', 'mw-rollback', 'mw-manual-revert', 'mw-reverted', 'twinkle', 'massmessage-delivery']
# Zie [[Speciaal:Labels]].
def hasnosigntag(tags):
for tag in tags:
if tag in nosigntags:
return True
return False
idre = re.compile(r'\| (\d+) \|\|')
def alluseredits(pagehistory, revid, user):
histlines = pagehistory.split('\n|----\n')
for l in range(len(histlines)):
if '| ' + str(revid) + ' ||' in histlines[l]:
revidline = l
break
# Zoek navolgende bewerkingen van dezelfde gebruiker:
lastid = revid
while l > 0:
if not '|| ' + user + ' ||' in histlines[l]:
lastid = idre.search(histlines[l + 1]).group(1)
break
l -= 1 # De eerste regels van de bewerkingsgeschiedenis zijn de recentste.
# Zoek voorgaande bewerkingen van dezelfde gebruiker:
l = revidline
oldid = 0
while l < len(histlines):
if not '|| ' + user + ' ||' in histlines[l]:
oldid = idre.search(histlines[l]).group(1)
break
l += 1
return int(oldid), int(lastid)
# = de laatste bewerking van de vorige gebruiker en de laatste bewerking van de huidige gebruiker.
trre = re.compile(r'<tr>.*?</tr>', re.DOTALL)
messagelinere = re.compile(r'<td class="diff-addedline diff-side-added">(<div>)?(.*?)(</div>)?</td>')
def findmessages(diff):
messages = []
message = ''
trmatches = trre.finditer(diff)
for trmatch in trmatches:
messagelinematch = messagelinere.search(trmatch.group(0))
# Als er een match is die niet een verplaatste regel is:
if messagelinematch and '<td colspan="2" class="diff-empty diff-side-deleted"></td>\n <td class="diff-marker" data-marker="+"></td>' in trmatch.group(0):
if not messagelinematch.group(2) == '<br />':
message += messagelinematch.group(2)
message += '\n'
elif '<td colspan="2" class="diff-empty diff-side-added"></td>' in trmatch.group(0):
continue # Beschouwt opeenvolgende toegevoegde en verwijderde regels als 1 bericht.
elif message:
message = message.strip()
message = message.replace('<', '<')
message = message.replace('>', '>')
message = message.replace('&', '&')
# Deze als laatste, voor als de brontekst < bevat: de html in m bevat dan &lt;
messages.append(message)
message = ''
return messages
galleryre = re.compile(r'<gallery>.*?</gallery>', re.DOTALL)
headingre = re.compile(r'^=.*=$', re.MULTILINE)
htmlcommre = re.compile(r'<\!--.*?-->', re.DOTALL)
htmltagsre = re.compile(r'</?(div|font|span|includeonly|noinclude).*?>', re.IGNORECASE)
timestampre = re.compile(r'\d\d? [A-Za-z][a-z]{2,8} \d{4} (CES?T )?\d\d:\d\d')
nosignre = re.compile(r"""( [:;}*\s] | | ----+ | <[BbHh][Rr]\s?/?> | <references\s?/> |
\[\[\s*([Aa]fbeelding|[Bb]estand|[Cc]ategor(ie|y)|[Ff]ile|[Ii]mage):([^[\]]|\[\[[^\]]*\]\])*\]\] |
[#|].* | \{\{([^{}]|\{\{[^}]*\}\})*\}\} )* ( \{\{([^{}]|\{\{[^}])* )?""", re.VERBOSE)
# bestanden en categorieën, met eventueel [[wikilinks]] erin,
# regels van een vandalismedossier, tabel- of sjabloonregels, en/of (geneste) sjablonen,
# met als laatste eventueel een sjabloon dat niet afgesloten wordt (bijv. afsluiting van {{Uitklappen}})
def needssign(m):
if not m:
return False
m = galleryre.sub('', m)
m = headingre.sub('', m)
m = htmlcommre.sub('', m)
m = htmltagsre.sub('', m)
if timestampre.search(m) or nosignre.fullmatch(m):
return False
return True
def afzender(timestamp, user): # 'timestamp': '2022-06-14T17:40:05Z'
y = int(timestamp[0:4])
month = int(timestamp[5:7])
d = int(timestamp[8:10])
h = int(timestamp[11:13])
minutes = int(timestamp[14:16])
s = int(timestamp[17:19])
sign_utc = datetime.datetime(y, month, d, h, minutes, s)
issummertime = True
if month in [1, 2, 11, 12]:
issummertime = False
elif month == 3:
March31 = datetime.datetime(y, 3, 31, 1, 0, 0)
if March31.weekday() == 6:
startsummertime = March31
else:
startsummertime = March31 - datetime.timedelta(days = March31.weekday() + 1)
if sign_utc < startsummertime:
issummertime = False
elif month == 10:
October31 = datetime.datetime(y, 10, 31, 1, 0, 0)
if October31.weekday() == 6:
endsummertime = October31
else:
endsummertime = October31 - datetime.timedelta(days = October31.weekday() + 1)
if sign_utc >= endsummertime:
issummertime = False
if issummertime:
diffhours = 2
tzname = ' (CEST)'
else:
diffhours = 1
tzname = ' (CET)'
signtime = sign_utc + datetime.timedelta(hours = diffhours)
monthabb = site.months_names[int(signtime.strftime('%m')) - 1][1]
signtimestring = signtime.strftime('%d ' + monthabb + ' %Y %H:%M').removeprefix('0') + tzname
return '{{Afzender|' + signtimestring + '|' + user + '}}'
start = site.server_time() - datetime.timedelta(hours=1) # Te snel signen kan leiden tot bewerkingsconflicten.
end = start - datetime.timedelta(hours=23)
# Doorzoek alle overlegnaamruimtes + Wikipedia + Help:
recentchanges = site.recentchanges(namespaces=u'4|12|1|3|5|7|9|11|13|15|101|711|829', bot=False, start=start, end=end)
pagehistsdone = dict()
for rc in recentchanges:
if not rc['type'] in ['new', 'edit'] or 'Overleg gebruiker:' + rc['user'] in rc['title'] or hasnosigntag(rc['tags']):
continue
pages = site.load_pages_from_pageids([rc['pageid']]) # Dit houdt rekening met navolgende titelwijzigingen.
for p in pages:
page = p
if not page.exists() or page.isRedirectPage() or not ispagesigned(page, rc['type']):
continue
print(rc['title'] + ', [[Speciaal:Diff/' + str(rc['revid']) + ']]')
if rc['pageid'] in pagehistsdone:
hist = pagehistsdone[rc['pageid']]
else:
hist = page.getVersionHistoryTable(total = 1000) # Kan lang duren, dus bewaren:
pagehistsdone[rc['pageid']] = hist
oldid, lastid = alluseredits(hist, rc['revid'], rc['user'])
if lastid != rc['revid'] or not page.getOldVersion(lastid):
continue # De navolgende bewerking(en) zijn van dezelfde gebruiker, of verborgen versie.
if oldid == 0:
messages = [page.getOldVersion(lastid)]
else:
if not page.getOldVersion(oldid):
continue
userdiff = site.compare(oldid, lastid)
messages = findmessages(userdiff)
issignadded = False
for m in messages:
if not needssign(m):
continue
mre = re.compile('^' + re.escape(m) + r' *$', re.MULTILINE)
if mre.search(page.text):
print('* Bericht:')
print(m + ' ' + afzender(rc['timestamp'], rc['user']))
page.text = mre.sub(m + ' ' + afzender(rc['timestamp'], rc['user']), page.text)
issignadded = True
if issignadded:
opslaan = input('Wil je deze opslaan? ')
if opslaan == 'ja':
page.save('Script: +{{Afzender}}')