From 7358758987f72fc3c56f1eb732c641cded4f4226 Mon Sep 17 00:00:00 2001 From: fariparedes Date: Fri, 18 Jun 2021 14:26:36 -0400 Subject: [PATCH] Add MXP support for anchor tags --- CHANGELOG.md | 1 + docs/source/Concepts/Clickable-Links.md | 7 ++++--- evennia/server/portal/mxp.py | 3 +++ evennia/utils/ansi.py | 6 +++++- evennia/utils/tests/test_tagparsing.py | 4 ++++ evennia/utils/text2html.py | 19 ++++++++++++++++++- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e45bc07133..6775456698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ Up requirements to Django 3.2+ into a more consistent structure for overriding. Expanded webpage documentation considerably. - REST API list-view was shortened (#2401). New CSS/HTML. Add ReDoc for API autodoc page. - Update and fix dummyrunner with cleaner code and setup. +- Added an MXP anchor tag in addition to the command tag ### Evennia 0.9.5 (2019-2020) diff --git a/docs/source/Concepts/Clickable-Links.md b/docs/source/Concepts/Clickable-Links.md index b29065e9e2..5a4f9ed262 100644 --- a/docs/source/Concepts/Clickable-Links.md +++ b/docs/source/Concepts/Clickable-Links.md @@ -1,11 +1,12 @@ ## Clickable links Evennia supports clickable links for clients that supports it. This marks certain text so it can be -clicked by a mouse and trigger a given Evennia command. To support clickable links, Evennia requires -the webclient or an third-party telnet client with [MXP](http://www.zuggsoft.com/zmud/mxp.htm) -support (*Note: Evennia only supports clickable links, no other MXP features*). +clicked by a mouse and either trigger a given Evennia command, or open a URL in an external web +browser. To support clickable links, Evennia requires the webclient or an third-party telnet client +with [MXP](http://www.zuggsoft.com/zmud/mxp.htm) support (*Note: Evennia only supports clickable links, no other MXP features*). - `|lc` to start the link, by defining the command to execute. +- `|lu` to start the link, by defining the URL to open. - `|lt` to continue with the text to show to the user (the link text). - `|le` to end the link text and the link definition. diff --git a/evennia/server/portal/mxp.py b/evennia/server/portal/mxp.py index 6c938ab709..7f5b39d392 100644 --- a/evennia/server/portal/mxp.py +++ b/evennia/server/portal/mxp.py @@ -16,12 +16,14 @@ http://www.gammon.com.au/mushclient/addingservermxp.htm import re LINKS_SUB = re.compile(r"\|lc(.*?)\|lt(.*?)\|le", re.DOTALL) +URL_SUB = re.compile(r"\|lu(.*?)\|lt(.*?)\|le", re.DOTALL) # MXP Telnet option MXP = bytes([91]) # b"\x5b" MXP_TEMPSECURE = "\x1B[4z" MXP_SEND = MXP_TEMPSECURE + '' + "\\2" + MXP_TEMPSECURE + "" +MXP_URL = MXP_TEMPSECURE + '' + "\\2" + MXP_TEMPSECURE + "" def mxp_parse(text): @@ -38,6 +40,7 @@ def mxp_parse(text): text = text.replace("&", "&").replace("<", "<").replace(">", ">") text = LINKS_SUB.sub(MXP_SEND, text) + text = URL_SUB.sub(MXP_URL, text) return text diff --git a/evennia/utils/ansi.py b/evennia/utils/ansi.py index 0dd2d8234c..490621329c 100644 --- a/evennia/utils/ansi.py +++ b/evennia/utils/ansi.py @@ -223,6 +223,7 @@ class ANSIParser(object): ansi_xterm256_bright_bg_map += settings.COLOR_ANSI_XTERM256_BRIGHT_BG_EXTRA_MAP mxp_re = r"\|lc(.*?)\|lt(.*?)\|le" + mxp_url_re = r"\|lu(.*?)\|lt(.*?)\|le" # prepare regex matching brightbg_sub = re.compile( @@ -237,6 +238,7 @@ class ANSIParser(object): # xterm256_sub = re.compile(r"|".join([tup[0] for tup in xterm256_map]), re.DOTALL) ansi_sub = re.compile(r"|".join([re.escape(tup[0]) for tup in ansi_map]), re.DOTALL) mxp_sub = re.compile(mxp_re, re.DOTALL) + mxp_url_sub = re.compile(mxp_url_re, re.DOTALL) # used by regex replacer to correctly map ansi sequences ansi_map_dict = dict(ansi_map) @@ -424,7 +426,9 @@ class ANSIParser(object): string (str): The processed string. """ - return self.mxp_sub.sub(r"\2", string) + string = self.mxp_sub.sub(r"\2", string) + string = self.mxp_url_sub.sub(r"\2", string) + return string def parse_ansi(self, string, strip_ansi=False, xterm256=False, mxp=False): """ diff --git a/evennia/utils/tests/test_tagparsing.py b/evennia/utils/tests/test_tagparsing.py index 5c47bc5b6a..ae78c8fd0d 100644 --- a/evennia/utils/tests/test_tagparsing.py +++ b/evennia/utils/tests/test_tagparsing.py @@ -145,13 +145,17 @@ class ANSIStringTestCase(TestCase): """ mxp1 = "|lclook|ltat|le" mxp2 = "Start to |lclook here|ltclick somewhere here|le first" + mxp3 = "Check out |luhttps://www.example.com|ltmy website|le!" self.assertEqual(15, len(ANSIString(mxp1))) self.assertEqual(53, len(ANSIString(mxp2))) + self.assertEqual(53, len(ANSIString(mxp3))) # These would indicate an issue with the tables. self.assertEqual(len(ANSIString(mxp1)), len(ANSIString(mxp1).split("\n")[0])) self.assertEqual(len(ANSIString(mxp2)), len(ANSIString(mxp2).split("\n")[0])) + self.assertEqual(len(ANSIString(mxp3)), len(ANSIString(mxp3).split("\n")[0])) self.assertEqual(mxp1, ANSIString(mxp1)) self.assertEqual(mxp2, str(ANSIString(mxp2))) + self.assertEqual(mxp3, str(ANSIString(mxp3))) def test_add(self): """ diff --git a/evennia/utils/text2html.py b/evennia/utils/text2html.py index 7fb19a8ab8..bb4251caf7 100644 --- a/evennia/utils/text2html.py +++ b/evennia/utils/text2html.py @@ -103,9 +103,10 @@ class TextToHTMLparser(object): ) re_dblspace = re.compile(r" {2,}", re.M) re_url = re.compile( - r'((?:ftp|www|https?)\W+(?:(?!\.(?:\s|$)|&\w+;)[^"\',;$*^\\(){}<>\[\]\s])+)(\.(?:\s|$)|&\w+;|)' + r'(?\[\]\s])+)(\.(?:\s|$)|&\w+;|)' ) re_mxplink = re.compile(r"\|lc(.*?)\|lt(.*?)\|le", re.DOTALL) + re_mxpurl = re.compile(r"\|lu(.*?)\|lt(.*?)\|le", re.DOTALL) def _sub_bgfg(self, colormatch): # print("colormatch.groups()", colormatch.groups()) @@ -290,6 +291,21 @@ class TextToHTMLparser(object): ) return val + def sub_mxp_urls(self, match): + """ + Helper method to be passed to re.sub, + replaces MXP links with HTML code. + Args: + match (re.Matchobject): Match for substitution. + Returns: + text (str): Processed text. + """ + url, text = [grp.replace('"', "\\"") for grp in match.groups()] + val = ( + r"""{text}""".format(url=url, text=text) + ) + return val + def sub_text(self, match): """ Helper method to be passed to re.sub, @@ -337,6 +353,7 @@ class TextToHTMLparser(object): # convert all ansi to html result = re.sub(self.re_string, self.sub_text, text) result = re.sub(self.re_mxplink, self.sub_mxp_links, result) + result = re.sub(self.re_mxpurl, self.sub_mxp_urls, result) result = self.re_color(result) result = self.re_bold(result) result = self.re_underline(result)