From 2a413dcc891692275b3e9fefec34b55e92f29792 Mon Sep 17 00:00:00 2001
From: Dpeta <jasprose@protonmail.com>
Date: Thu, 23 Jun 2022 19:14:28 +0200
Subject: [PATCH] Option to exclude smilies/links via split. (Undoes previous
 change to pesterQuirk.apply)

---
 dataobjs.py | 210 ++++++++++++++++++++++------------------------------
 menus.py    |  58 ++++++++++-----
 2 files changed, 127 insertions(+), 141 deletions(-)

diff --git a/dataobjs.py b/dataobjs.py
index 4c0d1ad..5306ad8 100644
--- a/dataobjs.py
+++ b/dataobjs.py
@@ -45,32 +45,8 @@ class pesterQuirk(object):
             self.checkstate = self.quirk["checkstate"]
         except KeyError:
             pass
-    def apply(self, string, first=False, last=False, checkstate=0):
-        # This function applies the quirks :3
-        print("checkstate: " + str(checkstate))
-
-        # Try to get a list of breakable links, smilies,
-        # @handle, #memo, in the message.
-        try:
-            # Check for links, store list of links.
-            links = list()
-            for match in re.findall(_urlre, string):
-                links.append(match)
-            # Check for smilies, store list of smilies.
-            smilies = list()
-            for match in re.findall(_smilere, string):
-                smilies.append(match)
-            # Check for @handles, store list of @handles.
-            handles = list()
-            for match in re.findall(_handlere, string):
-                smilies.append(match)
-            # Check for #memos, store list of #memos.
-            memos = list()
-            for match in re.findall(_memore, string):
-                smilies.append(match)
-        except Exception as e:
-                PchumLog.warning("Quirk issue: " + str(e))
         
+    def apply(self, string, first=False, last=False):
         if not self.on:
             return string
         elif self.type == "prefix":
@@ -78,25 +54,7 @@ class pesterQuirk(object):
         elif self.type == "suffix":
             return string + self.quirk["value"]
         elif self.type == "replace":
-            try:
-                # Replace like normal
-                output = string.replace(self.quirk["from"], self.quirk["to"])
-                # Try to revert links based on list.
-                for link in links:
-                    output = output.replace(link.replace(self.quirk["from"], self.quirk["to"]), link)
-                # Try to revert smilies based on list.
-                for smiley in smilies:
-                    output = output.replace(smiley.replace(self.quirk["from"], self.quirk["to"]), smiley)
-                # Try to revert @handles based on list.
-                for handle in handles:
-                    output = output.replace(handle.replace(self.quirk["from"], self.quirk["to"]), handle)
-                # Try to revert #memos based on list.
-                for memo in memos:
-                    output = output.replace(memo.replace(self.quirk["from"], self.quirk["to"]), memo)
-                return output
-            except Exception as e:
-                PchumLog.warning("Replace issue: " + str(e))
-                return string.replace(self.quirk["from"], self.quirk["to"])
+            return string.replace(self.quirk["from"], self.quirk["to"])
         elif self.type == "regexp":
             fr = self.quirk["from"]
             if not first and len(fr) > 0 and fr[0] == "^":
@@ -105,21 +63,11 @@ class pesterQuirk(object):
                 return string
             to = self.quirk["to"]
             pt = parseRegexpFunctions(to)
-            output = re.sub(fr, pt.expand, string)
-            # Reverse sub for links/smilies if enabled.
-            if checkstate == 2:
-                for smiley in smilies:
-                    output = output.replace(re.sub(fr, pt.expand, smiley), smiley)
-                for link in links:
-                    output = output.replace(re.sub(fr, pt.expand, link), link)
-                for handle in handles:
-                    output = output.replace(re.sub(fr, pt.expand, handle), handle)
-                for memo in memos:
-                    output = output.replace(re.sub(fr, pt.expand, memo), memo)
-            return output
+            return re.sub(fr, pt.expand, string)
         elif self.type == "random":
             if len(self.quirk["randomlist"]) == 0:
                 return string
+            fr = self.quirk["from"]
             if not first and len(fr) > 0 and fr[0] == "^":
                 return string
             if not last and len(fr) > 0 and fr[len(fr)-1] == "$":
@@ -128,69 +76,29 @@ class pesterQuirk(object):
                 choice = random.choice(self.quirk["randomlist"])
                 pt = parseRegexpFunctions(choice)
                 return pt.expand(mo)
-            return self.quirk["from"]
+            return re.sub(self.quirk["from"], randomrep, string)
         elif self.type == "spelling":
             percentage = self.quirk["percentage"]/100.0
             words = string.split(" ")
             newl = []
-            p = random.random()
-
-            # Main /word loop
+            ctag = re.compile("(</?c=?.*?>)", re.I)
             for w in words:
-                if re.match(_urlre, w):
-                    # Word is an url, don't break.
-                    newl.append(w)
-                elif re.match(_memore, w):
-                    # Word is an @memo, don't break.
-                    newl.append(w)
-                elif re.match(_handlere, w):
-                    # Word is an @handle, don't break.
-                    newl.append(w)
-                elif re.match(_smilere, w):
-                    # Word contains a smiley
-                    # Split by ':' and only skip the smiley,
-                    # this part is very messy and optional really.
-                    stripped_smiles = list()
-                    for smiley in smilies:
-                        stripped_smiles.append(smiley.strip(':'))
-                    denominated = w.split(':')
-                    output = ''
-                    for part in range(0, len(denominated)):
-                        if denominated[part] in stripped_smiles:
-                            output +=  denominated[part]
+                p = random.random()
+                if not ctag.search(w) and p < percentage:
+                    newl.append(mispeller(w))
+                elif p < percentage:
+                    split = ctag.split(w)
+                    tmp = []
+                    for s in split:
+                        if s and not ctag.search(s):
+                            tmp.append(mispeller(s))
                         else:
-                            if not _ctagre.search(denominated[part]) and p < percentage:
-                                output += mispeller(denominated[part])
-                            elif p < percentage:
-                                split = _ctagre.split(denominated[part])
-                                tmp = []
-                                for s in split:
-                                    if s and not _ctagre.search(s):
-                                        tmp.append(mispeller(s))
-                                    else:
-                                        tmp.append(s)
-                                output += tmp
-                            else:
-                                output += denominated[part]
-                        if part != len(denominated)-1:
-                            output += ':'
-                    newl.append(output)
+                            tmp.append(s)
+                    newl.append("".join(tmp))
                 else:
-                    if not _ctagre.search(w) and p < percentage:
-                        newl.append(mispeller(w))
-                    elif p < percentage:
-                        split = _ctagre.split(w)
-                        tmp = []
-                        for s in split:
-                            if s and not _ctagre.search(s):
-                                tmp.append(mispeller(s))
-                            else:
-                                tmp.append(s)
-                        newl.append("".join(tmp))
-                    else:
-                        newl.append(w)
+                    newl.append(w)
             return " ".join(newl)
-
+        
     def __str__(self):
         if self.type == "prefix":
             return "BEGIN WITH: %s" % (self.quirk["value"])
@@ -238,20 +146,76 @@ class pesterQuirks(object):
                     checkstate = int(q.checkstate)
                 except Exception:
                     checkstate = 0
-                if q.type != 'prefix' and q.type != 'suffix':
-                    if q.type == 'regexp' or q.type == 'random':
-                        string = q.apply(string,
-                                         first=(i==0),
-                                         last=lastStr,
-                                         checkstate=checkstate)
-                    else:
+                # Check for substring that should be excluded.
+                excludes = list()
+                # Check for links, store in list.
+                for match in re.finditer(_urlre, string):
+                    excludes.append(match)
+                # Check for smilies, store in list.
+                for match in re.finditer(_smilere, string):
+                    excludes.append(match)
+                # Check for @handles, store in list.
+                for match in re.finditer(_handlere, string):
+                    excludes.append(match)
+                # Check for #memos, store in list.
+                for match in re.finditer(_memore, string):
+                    excludes.append(match)
+                    
+                # Checkstate == 0 means the exclude option is unchecked.
+                if (checkstate == 0) or (len(excludes) == 0):
+                    # No split, apply like normal.
+                    if q.type != 'prefix' and q.type != 'suffix':
+                        if q.type == 'regexp' or q.type == 'random':
+                            string = q.apply(string,
+                                             first=(i==0),
+                                             last=lastStr)
+                        else:
+                            string = q.apply(string)
+                    elif q.type == 'prefix' and i == 0:
                         string = q.apply(string)
-                elif q.type == 'prefix' and i == 0:
-                    string = q.apply(string)
-                elif q.type == 'suffix' and lastStr:
-                    string = q.apply(string)
-            newlist.append(string)
+                    elif q.type == 'suffix' and lastStr:
+                        string = q.apply(string)
+                # Exclude option is checked, split string and only quirk
+                # the parts without links/smilies/@handle/#memo
+                elif (checkstate == 2) and (len(excludes) >= 1):
+                    # Seperate parts to be quirked.
+                    sendparts = list()
+                    # Add string until start of exclude at index 0.
+                    until = excludes[0].start()
+                    sendparts.append(string[:until])  
+                    # Add strings between excludes.
+                    for part in range(1, len(excludes)):
+                        after = excludes[part-1].end()
+                        until = excludes[part].start()
+                        sendparts.append(string[after:until])
+                    # Add string after exclude at last index.
+                    after = excludes[-1].end()
+                    sendparts.append(string[after:])
 
+                    # Quirk to-be-quirked parts.
+                    recvparts = list()
+                    for part in sendparts:
+                        # No split, apply like normal.
+                        if q.type == 'regexp' or q.type == 'random':
+                            recvparts.append(q.apply(part,
+                                                     first=(i==0),
+                                                     last=lastStr))
+                        elif q.type == 'prefix' and i == 0:
+                            recvparts.append(q.apply(part))
+                        elif q.type == 'suffix' and lastStr:
+                            recvparts.append(q.apply(part))
+                        else:
+                            recvparts.append(q.apply(part))
+                    # Reconstruct and update string.
+                    string = ''
+                    #print("sendparts: " + str(sendparts))
+                    #print("recvparts: " + str(recvparts))
+                    for part in range(0, len(excludes)):
+                        string += recvparts[part]
+                        string += excludes[part].group()
+                    string += recvparts[-1]
+                    
+            newlist.append(string)
         final = []
         for n in newlist:
             if type(n) in [str, str]:
diff --git a/menus.py b/menus.py
index 7d283e4..dc2ba9a 100644
--- a/menus.py
+++ b/menus.py
@@ -13,7 +13,6 @@ from convo import PesterInput, PesterText
 from parsetools import lexMessage
 
 QString = str
-
 _datadir = ostools.getDataDir()
 
 class PesterQuirkItem(QtWidgets.QTreeWidgetItem):
@@ -390,6 +389,12 @@ class PesterQuirkTypes(QtWidgets.QDialog):
         layout_3.addWidget(QtWidgets.QLabel("With:"))
         layout_3.addWidget(QtWidgets.QLineEdit())
         layout_replace.addLayout(layout_3)
+        layout_3 = QtWidgets.QHBoxLayout()
+        excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies")
+        excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos."
+                                   + "\nThe replace is applied on every substring individually.")
+        layout_3.addWidget(excludeCheckbox)
+        layout_replace.addLayout(layout_3)
 
         # Regexp Replace
         widget = QtWidgets.QWidget()
@@ -407,9 +412,10 @@ class PesterQuirkTypes(QtWidgets.QDialog):
         layout_3.addWidget(QtWidgets.QLineEdit())
         layout_regexp.addLayout(layout_3)
         layout_3 = QtWidgets.QHBoxLayout()
-        excludeCheckbox = QtWidgets.QCheckBox("Fix links and smilies")
-        excludeCheckbox.setToolTip("Tries to revert changes to links, smilies, @handles, and #memos."
-                                   + "\nMay behave weirdly with more complex patterns and functions.")
+        excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies")
+        excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos."
+                                   + "\nSince the replace is applied on every substring individually,"
+                                   + "\ncertain patterns or functions like gradients may not work correctly.")
         layout_3.addWidget(excludeCheckbox)
         layout_regexp.addLayout(layout_3)
         layout_all.addLayout(layout_f)
@@ -449,13 +455,13 @@ class PesterQuirkTypes(QtWidgets.QDialog):
         layout_6.addWidget(self.replaceinput)
         layout_6.addLayout(layout_7)
         layout_random.addLayout(layout_6)
-        
-        #layout_9 = QtWidgets.QHBoxLayout()
-        #excludeCheckbox = QtWidgets.QCheckBox("Fix links and smilies")
-        #excludeCheckbox.setToolTip("Tries to revert changes to links, smilies, @handles, and #memos."
-        #                           + "\nMay behave weirdly with more complex patterns and functions.")
-        #layout_9.addWidget(excludeCheckbox)
-        #layout_random.addLayout(layout_9)
+        layout_9 = QtWidgets.QHBoxLayout()
+        excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies")
+        excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos."
+                                   + "\nSince the replace is applied on every substring individually,"
+                                   + "\ncertain patterns or functions like gradients may not work correctly.")
+        layout_9.addWidget(excludeCheckbox)
+        layout_random.addLayout(layout_9)
 
         # Misspeller
         widget = QtWidgets.QWidget()
@@ -483,6 +489,13 @@ class PesterQuirkTypes(QtWidgets.QDialog):
         layout_0.addWidget(self.pages)
         layout_0.addLayout(layout_2)
 
+        layout_3 = QtWidgets.QHBoxLayout()
+        excludeCheckbox = QtWidgets.QCheckBox("Exclude links and smilies")
+        excludeCheckbox.setToolTip("Splits input to exclude smilies, weblinks, @handles, and #memos."
+                                   + "\nThe replace is applied on every substring individually.")
+        layout_3.addWidget(excludeCheckbox)
+        layout_mispeller.addLayout(layout_3)
+
         if quirk:
             types = ["prefix","suffix","replace","regexp","random","spelling"]
             for (i,r) in enumerate(self.radios):
@@ -496,23 +509,31 @@ class PesterQuirkTypes(QtWidgets.QDialog):
             elif q["type"] == "replace":
                 page.itemAt(1).layout().itemAt(1).widget().setText(q["from"])
                 page.itemAt(2).layout().itemAt(1).widget().setText(q["to"])
+                try:
+                    page.itemAt(3).layout().itemAt(0).widget().setCheckState(int(q["checkstate"]))
+                except (KeyError, ValueError) as e:
+                    print("KeyError: %s" % str(e))
             elif q["type"] == "regexp":
                 page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().setText(q["from"])
                 page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().setText(q["to"])
                 try:
                     page.itemAt(2).layout().itemAt(3).layout().itemAt(0).widget().setCheckState(int(q["checkstate"]))
-                except KeyError as e:
+                except (KeyError, ValueError) as e:
                     print("KeyError: %s" % str(e))
             elif q["type"] == "random":
                 self.regexp.setText(q["from"])
                 for v in q["randomlist"]:
                     item = QtWidgets.QListWidgetItem(v, self.replacelist)
-                #try:
-                #    page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().setCheckState(int(q["checkstate"]))
-                #except KeyError as e:
-                #    print("KeyError: %s" % str(e))
+                try:
+                    page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().setCheckState(int(q["checkstate"]))
+                except (KeyError, ValueError) as e:
+                    print("KeyError: %s" % str(e))
             elif q["type"] == "spelling":
                 self.slider.setValue(q["percentage"])
+                try:
+                    page.itemAt(3).layout().itemAt(0).widget().setCheckState(int(q["checkstate"]))
+                except (KeyError, ValueError) as e:
+                    print("KeyError: %s" % str(e))
 
         self.setLayout(layout_0)
 
@@ -693,19 +714,20 @@ class PesterChooseQuirks(QtWidgets.QDialog):
         elif vdict["type"] == "replace":
             vdict["from"] = str(page.itemAt(1).layout().itemAt(1).widget().text())
             vdict["to"] = str(page.itemAt(2).layout().itemAt(1).widget().text())
+            vdict["checkstate"] = str(page.itemAt(3).layout().itemAt(0).widget().checkState())
         elif vdict["type"] == "regexp":
             vdict["from"] = str(page.itemAt(2).layout().itemAt(1).layout().itemAt(1).widget().text())
             vdict["to"] = str(page.itemAt(2).layout().itemAt(2).layout().itemAt(1).widget().text())
             vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(3).layout().itemAt(0).widget().checkState())
         elif vdict["type"] == "random":
             vdict["from"] = str(self.quirkadd.regexp.text())
-            #vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().checkState())
+            vdict["checkstate"] = str(page.itemAt(2).layout().itemAt(2).layout().itemAt(0).widget().checkState())
             randomlist = [str(self.quirkadd.replacelist.item(i).text())
                           for i in range(0,self.quirkadd.replacelist.count())]
             vdict["randomlist"] = randomlist
         elif vdict["type"] == "spelling":
             vdict["percentage"] = self.quirkadd.slider.value()
-
+            vdict["checkstate"] = str(page.itemAt(3).layout().itemAt(0).widget().checkState())
         if vdict["type"] in ("regexp", "random"):
             try:
                 re.compile(vdict["from"])