Участник:LankLinkBot/zlv.py

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

Бот для добавления шаблона {{Сообщение ЗЛВ}} на страницу обсуждения статей.

Использование

[править | править код]

Запуск без параметров - обработать архив текущего месяца (или текущий и предыдущий, если с начала месяца прошло меньше семи дней).

Запуск с параметрами - можно указать два параметра: месяц и год с которых надо начать обработку архивов. Пример:

$ python zlv.py 2 2005

обработать архивы начиная с февраля 2005 года (с самого начала).

Особенности работы бота

[править | править код]

Для правильной вставки иллюстраций они должны располагаться перед сообщением к которому относятся на отдельной строке (как правило так и делается).

Сообщения с датами на границе года (XX декабря 20YY года - XX января 20YY+1 года) должны попадать в архив следующего года (сейчас - по-разному).

Если на странице обсуждения уже имеется шаблон (включая закомментированные), такие страницы пропускаются.

Исходный код бота

[править | править код]
#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
# (c) Lankier mailto:lankier@gmail.com
#
# $Id$
#
import sys
import re
import time
import pywikibot

TEST = False

site = pywikibot.Site('ru', 'wikipedia')
message = 'Бот: добавление шаблона [[Шаблон:Сообщение ЗЛВ|Сообщение ЗЛВ]]'

stoptempl = ('начало цитаты', 'новые сверху', 'новые снизу', 'википедия:рецензирование/', 'вп:рецензирование/', 'график просмотров', 'ежегодные просмотры')

def replace_quote(text):
    # safe replace quote (do not replace inside [[...|]])
    i = 0
    ret = ''
    while True:
        j = text.find('[[', i)
        if j < 0:
            break
        ret += text[i:j].replace('«', '„').replace('»', '“')
        k = text.find(']]', j)
        if k < 0:
            pywikibot.logging.info('replace_quote error: '+text)
            i = j
            break
        ret += '[['
        wlink = text[j+2:k]
        c = wlink.count('|')
        if c == 1:
            s1, s2 = wlink.split('|')
            wlink = s1+'|'+s2.replace('«', '„').replace('»', '“')
        elif c == 0 and ('«' in wlink or '»' in wlink):
            wlink = wlink+'|'+wlink.replace('«', '„').replace('»', '“')
        ret += wlink
        i = k
    ret += text[i:].replace('«', '„').replace('»', '“')
    return ret

def in_brackets(s, brackets='{}'):
    '''search for text inside brackets'''
    # from retempl.py
    first, last = brackets
    i = 0
    count = 0
    start = None
    for c in s:
        if c == first:
            if start is None:
                start = i
            count += 1
        if c == last:
            if count == 1:
                return s[start:i+1]
            count -= 1
        i += 1
        if i > len(s):
            break
    return ''

t = time.localtime()
# 7 days before today
t = time.localtime(time.mktime((t.tm_year, t.tm_mon, t.tm_mday-7,
                                -1, -1, -1, -1, -1, -1)))
start_month = t.tm_mon
start_year = t.tm_year
del t
end_month = time.localtime().tm_mon
end_year = time.localtime().tm_year
# print('>>', start_month, start_year, '-', end_month, end_year)
def page_gen():
    for y in range(start_year, end_year+1):
        if y < start_year:
            continue
        for m in range(1, 13):
            if y == start_year and m < start_month:
                continue
            if y == end_year and m > end_month:
                break
            pn = 'Проект:Знаете_ли_вы/Архив_рубрики/%d-%02d' % (y, m)
            page = pywikibot.Page(site, pn)
            if not page.exists():
                pywikibot.logging.info('skip: '+page.title(as_link=True))
                continue
            yield page, y, m

wpat = re.compile(r"(?:'''[^\]]*?\[\[([^\]]+?)\]\][^\]]*?'''"
                  r"|\[\[([^\|\]]+?)\|'''[^\]]+?'''\]\])")
def parse_page(page, year, month):
    text = page.text
    date = None
    image = None
    for line in text.splitlines():
        line = line.strip()
        if line.startswith('=='):
            # date
            d = line.strip('=').strip()
            if re.match(r'[0-9]', d):
                date = d #line.strip('=').strip()
        elif line.startswith('[['): # and line.strip().endswith(']]'):
            # image
            line = line.strip()[2:-2]
            im = line.split('|')[0]
            impage = pywikibot.Page(site, im)
            if impage.namespace() == 6:
                image = impage
        elif date and line.startswith('*'):
            # text
            arch = ' [[Проект:Знаете_ли_вы/Архив_рубрики/%d-%02d#%s]]' % (year, month, date)
            line = line.lstrip('*').strip()
            for match in wpat.finditer(line):
                s = match.group(1) or match.group(2)
                assert s
                title = s.split('|')[0]
                link = pywikibot.Page(site, title)
                if link.section():
                    link = pywikibot.Page(site, link.title(with_section=False))
                if link.namespace() != 0:
                    continue
                if not link.exists():
                    pywikibot.logging.info('** not exists: '+link.title(as_link=True)+arch)
                    continue
                if link.isRedirectPage():
                    link = link.getRedirectTarget()
                if link.isDisambig():
                    pywikibot.logging.info('** disambig: '+link.title(as_link=True)+arch)
                    continue
                t = line.rstrip('.,;…')
                yield link.toggleTalkPage(), t, date, image
            image = None


def format_date(date, year):
    if '—' in date:                    # mdash
        f, t = date.split('—')
    elif '-' in date:                   # minus
        f, t = date.split('-')
    else:
        return '%s %s года' % (date, year)
    d2, m2 = t.split()
    dm1 = f.split()
    if len(dm1) == 1:
        d1 = dm1[0]
        m1 = m2
    else:
        d1, m1 = dm1
    # assert
    int(d1)
    int(d2)
    #
    if m1 == 'декабря' and m2 == 'января':
        return '%s %s %d года — %s %s %d года' % (d1, m1, year-1, d2, m2, year)
    if m1 == m2:
        return '%s%s %s %d года' % (d1, d2, m1, year)
    return '%s %s%s %s %s года' % (d1, m1, d2, m2, year)

def archive_date(date):
    return date.replace('-','—');

# template without image
templ = '{{Сообщение ЗЛВ|даты=%s|текст=%s|архив=%s}}\n'
# template within image
templ_im = '{{Сообщение ЗЛВ|даты=%s|текст=%s|иллюстрация=%s|архив=%s}}\n'
def insert_templ(talk, text, date, year, month, image):
    #print('insert_templ:', talk)
    if talk.exists():
        ptext = talk.text
        if '{{Сообщение ЗЛВ}}' in ptext:
            pywikibot.logging.info('*** need expand: '+talk.title(as_link=True))
            #return False
        else:
            for t in ('Сообщение ЗЛВ',
                     'Знаете ли вы-статья',
                     'ЗЛВ'):
                t = t.replace(' ', '[ _]')
                pat = re.compile(r'{{\s*'+t+r'\s*[\|}<]', re.I|re.S)
                match = pat.search(ptext)
                if match:
                    # template exists
                    pywikibot.logging.info('*** template exists: '+talk.title(as_link=True))
                    return False
    text = replace_quote(text)
    d = format_date(date, year)
    arch = '%d-%02d#%s' % (year, month, archive_date(date))
    if image:
        ptempl = templ_im % (d, text, image.title(with_ns=False), arch)
    else:
        ptempl = templ % (d, text, arch)
    #print(ptempl)
    if not talk.exists():
        pywikibot.logging.info('new page: '+talk.title(as_link=True))
        pywikibot.logging.info(ptempl)
        if not TEST:
            talk.text = ptempl
            talk.save(message)
        return True
    ptext = talk.text
    if '{{Сообщение ЗЛВ}}' in ptext:
        # replace
        ptext = ptext.replace('{{Сообщение ЗЛВ}}', ptempl, 1)
    else:
        # add
        # skip the header templates
        i = 0
        while True:
            match = re.match((r'\s*({{)'), ptext[i:])
            if not match:
                break
            j = match.start(1)
            tt = in_brackets(ptext[i+j:])
            if not tt:
                break
            found = False
            for st in stoptempl:
                if tt.lower().startswith('{{'+st):
                    found = True
                    break
            if found:
                break
            i = i+j+len(tt)
        if i == 0:
            ptext = ptempl + ptext
        elif len(ptext) > i and ptext[i] == '\n':
            ptext = ptext[:i+1] + ptempl + ptext[i+1:]
        else:
            ptext = ptext[:i]+'\n' + ptempl + ptext[i:]
    # put
    pywikibot.logging.info('== '+talk.title(as_link=True)+' ==')
    pywikibot.showDiff(talk.text+'\n', ptext, context=1)
    if not TEST:
        talk.text = ptext
        talk.save(message)
    return True


if len(sys.argv) > 1:
    start_month = int(sys.argv[1])
    start_year = int(sys.argv[2])
for page, year, month in page_gen():
    pywikibot.logging.info('>> %s %d %d' % (page, year, month))
    for talk, text, date, image in parse_page(page, year, month):
        insert_templ(talk, text, date, year, month, image)