Example #1
0
def showDiff(oldtext, newtext, context=0):
    """
    Output a string showing the differences between oldtext and newtext.

    The differences are highlighted (only on compatible systems) to show which
    changes were made.
    """
    PatchManager(oldtext, newtext, context=context).print_hunks()
Example #2
0
 def _convert_revisions_to_diffs(
         revisions: List[Revision]) -> List[RevisionDiff]:
     if len(revisions) <= 1:
         return []
     result = []
     revisions.sort(key=lambda val: val.timestamp)
     for i in range(1, len(revisions)):
         old_rev = revisions[i - 1]
         new_rev = revisions[i]
         pm = PatchManager(old_rev.slots['main']['*'],
                           new_rev.slots['main']['*'])
         rev_diff = RevisionDiff.of_hunks(revisions[i - 1].revid,
                                          revisions[i].revid,
                                          revisions[i - 1].timestamp,
                                          revisions[i].timestamp, pm.hunks)
         result.append(rev_diff)
     return result
Example #3
0
 def test_patch_manager_no_diff(self):
     """Test PatchManager for the same strings."""
     for context in range(2):
         p = PatchManager('Pywikibot', 'Pywikibot', context=context)
         with self.subTest(context=context):
             self.assertIsEmpty(p.hunks)
Example #4
0
    def run(self):
        self.page = pywikibot.Page(self.site, self.change['title'], ns=self.change['namespace'])
        self.output(u"Handling")
        if self.page.isRedirectPage():
            self.output(u"Redirect")
            return
        if self.page.namespace() == 4:
            # Project pages needs attention (__NEWSECTIONLINK__)
            if not self.isDiscussion(self.page):
                self.output(u"Not a discussion")
                return
        if self.page.namespace() == 3:
	   # See diff=43294520
            if self.change['user'] in self.change['title']:
                self.output(u"User edit his own talk page -- ignored")
                return	
            if '/' in self.change['title']:
                self.output(u"User edit sub-talk page   -- ignored")
                return	
				
        user = pywikibot.User(self.site, self.change['user'])
        if self.isOptout(user):
            self.output(u"%s opted-out" % user)
            return

        # diff-reading.
        if self.change['type'] == 'new':
            old_text = ''
        else:
            old_text = self.page.getOldVersion(self.change['revision']['old'])

        new_text = self.page.getOldVersion(self.change['revision']['new'])

        if "{{delete" in new_text.lower():
            self.output(u"{{delete -- ignored")
            return

        diff = PatchManager(old_text.split("\n") if old_text else [], new_text.split("\n"), by_letter=True)
        diff.print_hunks()

        tosignstr = False
        tosignnum = False

        for block in diff.blocks:
            if block[0] < 0: continue
            hunk = diff.hunks[block[0]]
            group = hunk.group

#            print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
#                (block[0], block[1][0], block[1][1], hunk.a[block[1][0]:block[1][1]], block[2][0], block[2][1], hunk.b[block[2][0]:block[2][1]]))

            for tag, i1, i2, j1, j2 in group:
#                print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
#                    (tag, i1, i2, hunk.a[i1:i2], j1, j2, hunk.b[j1:j2]))
                if tag == 'insert':
                    for j in range(j1, j2):
                        line = hunk.b[j]
                        if self.page == user.getUserTalkPage() or self.page.title().startswith(user.getUserTalkPage().title() + '/'):
		          if "{{" in line.lower():
                                self.output(u"User adding templates to his own talk page -- ignored")
                                return
								
                        excluderegextest = self.matchExcludeRegex(line)
	                if excluderegextest is not None:
                             self.output(u"%s -- ignored" % excluderegextest)
                             return
   
                        if self.isComment(line):
                            tosignnum = j
                            tosignstr = line
                            if self.isSigned(user, tosignstr):
                                self.output(u"Signed")
                                return
                        
        if tosignstr is False:
            self.output(u"No inserts")
            return
        if self.isSigned(user, tosignstr):
            self.output(u"Signed")
            return

        if not self.isFreqpage(self.page):
            self.output(u"Waiting")
            time.sleep(60)
            pass

        currenttext = self.page.get(force=True).split("\n")
        if currenttext[tosignnum] == tosignstr:
            currenttext[tosignnum] += self.getSignature(tosignstr, user)
        elif currenttext.count(tosignstr) == 1:
            currenttext[currenttext.index(tosignstr)] += self.getSignature(tosignstr, user)
        else:
            self.output(u"Line no longer found, probably signed")
        
        summary = u'机器人:为 %s 补充签名 "%s"' % (self.userlink(user), self.change['comment'])
        
        self.userPut(self.page, self.page.get(), u"\n".join(currenttext), comment=summary)

        # self.notify(user) {{subst:Uw-tilde}} -- ignore {{bots}}
        if self.controller.checknotify(user):
            self.output(u"Notifying %s" % user)
            talk = user.getUserTalkPage()
            if talk.isRedirectPage(): talk = talk.getRedirectTarget()
            try:
                talktext = talk.get(force=True, get_redirect=True) + u'\n\n'
            except pywikibot.NoPage:
                talktext = ''

            talktext += u'{{subst:User:Crystal-bot/uw-tilde}} --~~~~'
            self.userPut(talk, talk.text, talktext, comment=u"机器人:添加[[Template:Uw-tilde]]模板提示", minor=False)
Example #5
0
    def run(self):
        self.page = pywikibot.Page(self.site,
                                   self.change['title'],
                                   ns=self.change['namespace'])
        self.output(u"Handling")
        if self.page.isRedirectPage():
            self.output(u"Redirect")
            return
        if self.page.namespace() == 4:
            # Project pages needs attention (__NEWSECTIONLINK__)
            if not self.isDiscussion(self.page):
                self.output(u"Not a discussion")
                return
        user = pywikibot.User(self.site, self.change['user'])
        if self.isOptout(user):
            self.output(u"%s opted-out" % user)
            return

        # diff-reading.
        if self.change['type'] == 'new':
            old_text = ''
        else:
            old_text = self.page.getOldVersion(self.change['revision']['old'])

        new_text = self.page.getOldVersion(self.change['revision']['new'])

        if "{{speedy" in new_text.lower():
            self.output(u"{{speedy -- ignored")
            return

        diff = PatchManager(old_text.split("\n") if old_text else [],
                            new_text.split("\n"),
                            by_letter=True)
        diff.print_hunks()

        tosignstr = False
        tosignnum = False

        for block in diff.blocks:
            if block[0] < 0: continue
            hunk = diff.hunks[block[0]]
            group = hunk.group

            #            print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
            #                (block[0], block[1][0], block[1][1], hunk.a[block[1][0]:block[1][1]], block[2][0], block[2][1], hunk.b[block[2][0]:block[2][1]]))

            for tag, i1, i2, j1, j2 in group:
                #                print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
                #                    (tag, i1, i2, hunk.a[i1:i2], j1, j2, hunk.b[j1:j2]))
                if tag == 'insert':
                    for j in range(j1, j2):
                        line = hunk.b[j]
                        if self.page == user.getUserTalkPage(
                        ) or self.page.title().startswith(
                                user.getUserTalkPage().title() + '/'):
                            if "{{" in line.lower():
                                self.output(
                                    u"User adding templates to his own talk page -- ignored"
                                )
                                return

                        excluderegextest = self.matchExcludeRegex(line)
                        if excluderegextest is not None:
                            self.output(u"%s -- ignored" % excluderegextest)
                            return

                        if self.isComment(line):
                            tosignnum = j
                            tosignstr = line
                            if self.isSigned(user, tosignstr):
                                self.output(u"Signed")
                                return

        if tosignstr is False:
            self.output(u"No inserts")
            return
        if self.isSigned(user, tosignstr):
            self.output(u"Signed")
            return

        if not self.isFreqpage(self.page):
            self.output(u"Waiting")
            time.sleep(60)
            pass

        currenttext = self.page.get(force=True).split("\n")
        if currenttext[tosignnum] == tosignstr:
            currenttext[tosignnum] += self.getSignature(tosignstr, user)
        elif currenttext.count(tosignstr) == 1:
            currenttext[currenttext.index(tosignstr)] += self.getSignature(
                tosignstr, user)
        else:
            self.output(u"Line no longer found, probably signed")
            return

        summary = u'Signing comment by %s - "%s"' % (self.userlink(user),
                                                     self.change['comment'])

        self.userPut(self.page,
                     self.page.get(),
                     u"\n".join(currenttext),
                     comment=summary)

        # self.notify(user) {{subst:Please sign}} -- ignore {{bots}}
        if self.controller.checknotify(user):
            self.output(u"Notifying %s" % user)
            talk = user.getUserTalkPage()
            if talk.isRedirectPage(): talk = talk.getRedirectTarget()
            try:
                talktext = talk.get(force=True, get_redirect=True) + u'\n\n'
            except pywikibot.NoPage:
                talktext = ''

            talktext += u'{{subst:Please sign}} --~~~~'
            self.userPut(
                talk,
                talk.text,
                talktext,
                comment=
                u"Added {{subst:[[Template:Please sign|Please sign]]}} note.",
                minor=False)
Example #6
0
    def changeShouldBeHandled(self) -> Tuple[bool, Optional[ShouldBeHandledResult]]:
        self.page = pywikibot.Page(self.site, self.revInfo.title, ns=self.revInfo.namespace)
        self.output("Handling")

        if self.isPageOptOut():
            self.output("Page %s on opt-out list" % self.page.title())
            return False, None

        if (
            self.page.title().find("/Archiv/") > 0
            or self.page.title().find("/Archiv ") > 0
            or self.page.title().endswith("/Archiv")
        ):
            self.output("Suspected archive page")
            return False, None

        if self.page.title().startswith("Portal Diskussion:") and (
            self.page.title().endswith("/Artikel des Monats") or self.page.title().endswith("/Neue Artikel")
        ):
            return False, None

        if self.page.isRedirectPage():
            self.output("Redirect")
            return False, None
        if self.page.namespace() == 4:
            # Project pages needs attention (__NEWSECTIONLINK__)
            if not self.isDiscussion():
                self.output("Not a discussion")
                return False, None

        if {"mw-undo", "mw-rollback"}.intersection(self.getTags()):
            self.output("undo / rollback")
            return False, None

        user = pywikibot.User(self.site, self.revInfo.user)
        if self.isUserOptOut(user.username):
            self.output("%s opted-out" % user.username)
            return False, None

        if (
            self.page.namespace() == pywikibot.site.Namespace.USER_TALK
            and (
                self.page.title(with_ns=False) == user.username
                or self.page.title(with_ns=False).startswith(user.username + "/")
            )
            and self.controller.isExperiencedUser(user)
        ):
            self.output("Experienced user %s edited own talk page" % user.username)
            return False, None

        # diff-reading.
        if self.revInfo.type == "new":
            old_text = ""
        else:
            assert self.revInfo.oldRevision is not None
            try:
                old_text = self.page.getOldVersion(self.revInfo.oldRevision)
            except KeyError:
                self.warning("Old revision %d not found, retrying..." % self.revInfo.oldRevision)
                time.sleep(10)
                old_text = self.page.getOldVersion(self.revInfo.oldRevision)

        try:
            new_text = self.page.getOldVersion(self.revInfo.newRevision)
        except KeyError:
            self.warning("New revision %d not found, retrying..." % self.revInfo.newRevision)
            time.sleep(10)
            new_text = self.page.getOldVersion(self.revInfo.newRevision)

        new_lines = new_text.split("\n")
        diff = PatchManager(old_text.split("\n") if old_text else [], new_lines, by_letter=True)
        #        diff.print_hunks()

        tosignstr = ""
        tosignnum = -1

        if len(diff.hunks) > 1:
            self.output("Multiple diff hunks %d" % len(diff.blocks))
            return False, None

        hunk = diff.hunks[0]
        group = hunk.group

        timestamp1 = self.getSignatureTimestampString(self.revInfo.timestamp)
        timestamp2 = self.getSignatureTimestampString(self.revInfo.timestamp - 60)

        exactTimeSigned = False
        timeSigned = False
        userSigned = False

        signatureTimestampCount = 0

        for tag, _, _, j1, j2 in group:
            if tag == "insert":
                insertStartLine = j1
                for j in range(j1, j2):
                    line = hunk.b[j]
                    if self.page == user.getUserTalkPage() or self.page.title().startswith(
                        user.getUserTalkPage().title() + "/"
                    ):
                        if "{{" in line.lower():
                            self.output("User adding templates to their " "own talk page -- ignored")
                            return False, None

                    excluderegextest = self.matchExcludeRegex(line)
                    if excluderegextest is not None:
                        self.output("Matches %s -- ignored" % excluderegextest)
                        return False, None

                    if self.isNotExcludedLine(line):
                        tosignnum = j
                        tosignstr = line

                        exactTimeSigned = tosignstr.find(timestamp1) >= 0 or tosignstr.find(timestamp2) >= 0

                        lineTimeSigned = self.hasAnySignatureTimestamp(line)
                        if lineTimeSigned:
                            signatureTimestampCount += 1
                        timeSigned = timeSigned or lineTimeSigned

                        userSigned = userSigned or self.isUserSigned(user, tosignstr)
                        if timeSigned and userSigned:
                            self.controller.clearnotify(user)
                            self.output("Already signed")
                            return False, None

            if tag == "delete":
                self.output("Line deletion found")
                return False, None
            if tag == "replace":
                self.output("Line replacement found")
                return False, None

        if not tosignstr:
            self.output("No inserts")
            return False, None

        if signatureTimestampCount > 1:
            self.output("Multiple timestamps found")
            return False, None

        if self.hasAnySignatureAllowedUserLink(tosignstr) and timeSigned and self.controller.isExperiencedUser(user):
            self.output("Timestamp and other user link found - likely copied")
            return False, None

        if not timeSigned and not userSigned:
            if self.isAlreadySignedInFollowingLines(user, new_lines, tosignnum):
                return False, None

        if not timeSigned and not userSigned and self.isPostscriptum(tosignstr) and tosignnum > 1:
            checkLineNo = tosignnum - 1
            if not new_lines[checkLineNo].strip() and checkLineNo > 0:
                checkLineNo -= 1
            if self.isUserSigned(user, new_lines[checkLineNo]) and self.hasAnySignatureTimestamp(
                new_lines[checkLineNo]
            ):
                self.output("Postcriptum found")
                return False, None

        if not timeSigned:
            precedingSignatureOrSectionFound = False
            for i in range(0, insertStartLine):
                if new_lines[i].strip().startswith("=="):
                    precedingSignatureOrSectionFound = True
                    break
                if self.hasAnySignature(new_lines[i]):
                    precedingSignatureOrSectionFound = True
                    break
            followingSignatureOrSectionFound = False
            for i in range(tosignnum + 1, len(new_lines)):
                if new_lines[i].strip().startswith("=="):
                    followingSignatureOrSectionFound = True
                    break
                if self.hasAnySignature(new_lines[i]):
                    followingSignatureOrSectionFound = True
                    break
            if not precedingSignatureOrSectionFound:
                if (
                    new_lines[insertStartLine].strip().startswith("{{") or tosignstr.strip().startswith("{{")
                ) and tosignstr.strip().endswith("}}"):
                    self.output("Insertion of template at beginning of page")
                    return False, None
                if followingSignatureOrSectionFound and self.controller.isExperiencedUser(user):
                    self.output(
                        "Insertion by experienced user at the beginning of talk page before any sections or signatures"
                    )
                    return False, None

        if self.hasApplicableNobotsTemplate(new_lines, insertStartLine):
            return False, None

        # if not user-signed don't consider
        if not userSigned and timeSigned and not exactTimeSigned:
            timeSigned = False

        # all checks passed
        return True, ShouldBeHandledResult(tosignnum, tosignstr, timeSigned, userSigned)