fake messages, formatting endpoint, new help menus
parent
7eef803084
commit
09077baeac
|
@ -1,6 +1,7 @@
|
||||||
from urllib.error import URLError
|
from urllib.error import URLError
|
||||||
import urllib.request as url
|
import urllib.request as url
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
from time import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
@ -423,6 +424,32 @@ class BBJ(object):
|
||||||
return response["data"], response["usermap"]
|
return response["data"], response["usermap"]
|
||||||
|
|
||||||
|
|
||||||
|
def fake_message(self, body="!!", format="sequential", author=None, post_id=0):
|
||||||
|
"""
|
||||||
|
Produce a a valid message object with `body`. Useful for
|
||||||
|
testing and can also be used mimic server messages in a
|
||||||
|
client.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"body": self.format_message(body, format),
|
||||||
|
"author": author or self.user["user_id"],
|
||||||
|
"post_id": post_id,
|
||||||
|
"created": time(),
|
||||||
|
"edited": False,
|
||||||
|
"thread_id": "gibberish"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def format_message(self, body, format="sequential"):
|
||||||
|
"""
|
||||||
|
Send `body` to the server to be formatted according to `format`,
|
||||||
|
defaulting to the sequential parser. Returns the body object.
|
||||||
|
"""
|
||||||
|
response = self("format_message", body=body, format=format)
|
||||||
|
return response["data"]
|
||||||
|
|
||||||
|
|
||||||
def edit_query(self, thread_id, post_id):
|
def edit_query(self, thread_id, post_id):
|
||||||
"""
|
"""
|
||||||
Queries ther server database to see if a post can
|
Queries ther server database to see if a post can
|
||||||
|
|
|
@ -20,7 +20,6 @@ Please mail me (~desvox) for feedback and for any of your
|
||||||
"OH MY GOD WHY WOULD YOU DO THIS"'s or "PEP8 IS A THING"'s.
|
"OH MY GOD WHY WOULD YOU DO THIS"'s or "PEP8 IS A THING"'s.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from network import BBJ, URLError
|
from network import BBJ, URLError
|
||||||
from string import punctuation
|
from string import punctuation
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -62,6 +61,86 @@ welcome = """>>> Welcome to Bulletin Butter & Jelly! ------------------@
|
||||||
@_________________________________________________________@
|
@_________________________________________________________@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
format_help = [
|
||||||
|
"BBJ supports **bolding**, __underlining__, and [rainbow: coloring] text "
|
||||||
|
"using markdown-style symbols as well as tag-like expressions. Markdown "
|
||||||
|
"is **NOT** fully implemented, but several of the more obvious concepts "
|
||||||
|
"have been brought over. Additionally, we have chan-style greentext and "
|
||||||
|
"numeric post referencing, ala >>3 for the third reply.",
|
||||||
|
|
||||||
|
"[red: Whitespace]",
|
||||||
|
|
||||||
|
"When you're composing, it is desirable to introduce linebreaks into the "
|
||||||
|
"body to keep it from overflowing the screen. However, you __dont__ want "
|
||||||
|
"that same spacing to bleed over to other people's screens, because clients "
|
||||||
|
"will wrap the text themselves.",
|
||||||
|
|
||||||
|
"Single line breaks in the body join into eachother to form sentences, "
|
||||||
|
"putting a space where the break was. This works like html. When you want "
|
||||||
|
"to split it off into a paragraph, **use two line breaks.**",
|
||||||
|
|
||||||
|
"[red: Colors, Bold, Underline & Expressions]",
|
||||||
|
|
||||||
|
"You can use [rainbow: rainbow], [red: red], [yellow: yellow], [green: green], "
|
||||||
|
"[blue: blue], [cyan: cyan], [magenta: and magenta], **bold**, and __underline__ "
|
||||||
|
"inside of your posts. **bold\nworks like this**, __and\nunderlines like this__. "
|
||||||
|
"The symbolic, markdown form of these directives does NOT allow escaping, and "
|
||||||
|
"can only apply to up to 20 characters on the same line. They are best used on short "
|
||||||
|
"phrases. However, you can use a different syntax for it, which is also required to use "
|
||||||
|
"colors: these expressions \[bold: look like this] and are much more reliable. "
|
||||||
|
"The colon and the space following it are important. When you use these "
|
||||||
|
"expressions, the __first__ space is not part of the content, but any characters, "
|
||||||
|
"including spaces, that follow it are included in the body. The formatting will "
|
||||||
|
"apply until the closing ]. You can escape such an expression \\[cyan: like this]"
|
||||||
|
"and can also \\[blue: escape \\] other closing brackets] inside of it. Only "
|
||||||
|
"closing brackets need to be escaped within an expression. Any backslashes used "
|
||||||
|
"for escaping will not show in the body unless you use two slashes.",
|
||||||
|
|
||||||
|
"This peculiar syntax elimiates false positives. You never have to escape [normal] "
|
||||||
|
"brackets when using the board. Only expressions with **valid and defined** directives "
|
||||||
|
"will be affected. [so: this is totally valid and requires no escapes] because 'so' is "
|
||||||
|
"not a directive. [red this will pass too] because the colon is missing.",
|
||||||
|
|
||||||
|
"The following directives may be used in this form: red, yellow, green, blue, cyan, "
|
||||||
|
"magenta, bold, underline, and rainbow. Nesting expressions into eachother will "
|
||||||
|
"override the parent directives until it closes. Thus, nesting is valid but doesn't produce "
|
||||||
|
"layered results.",
|
||||||
|
|
||||||
|
"[red: Quotes & Greentext]",
|
||||||
|
|
||||||
|
"You can refer to a post number using two angle brackets pointing into a number. >>432 "
|
||||||
|
"like this. You can color a whole line green by proceeding it with a '>'. Note that "
|
||||||
|
"this violates the sentence structure outlined in the **Whitespace** section above, "
|
||||||
|
"so you may introduce >greentext without splicing into seperate paragraphs. The '>' "
|
||||||
|
"must be the first character on the line with no whitespace before it.\n>it looks like this\n"
|
||||||
|
"and the paragraph doesnt have to break on either side.",
|
||||||
|
|
||||||
|
"When using numeric quotes, they are highlighted and the author's name will show "
|
||||||
|
"next to them in the thread. You can press enter when focused on a message to view "
|
||||||
|
"the parent posts. You may insert these directives manually or use the <Reply> function "
|
||||||
|
"on post menus.",
|
||||||
|
|
||||||
|
"Quoting directives cannot be escaped."
|
||||||
|
]
|
||||||
|
|
||||||
|
general_help = [
|
||||||
|
("bold", "use q or escape to close dialogs and menus (including this one)\n\n"),
|
||||||
|
|
||||||
|
"You may use the arrow keys, or use jk/np/Control-n|p to move up and down by "
|
||||||
|
"an element. If an element is overflowing the screen, it will scroll only one line. "
|
||||||
|
"To make scrolling faster, hold shift when using a control: it will repeat 5 times.\n\n"
|
||||||
|
|
||||||
|
"To go back and forth between threads, you may also use the left/right arrow keys, "
|
||||||
|
"or h/l to do it vi-style.\n\n"
|
||||||
|
|
||||||
|
"In the thread index and any open thread, the b and t keys may be used to go to "
|
||||||
|
"very top or bottom.\n\n"
|
||||||
|
|
||||||
|
"Aside from those, primary controls are shown on the very bottom of the screen "
|
||||||
|
"in the footer line, or may be placed in window titles for other actions like "
|
||||||
|
"dialogs or composers."
|
||||||
|
]
|
||||||
|
|
||||||
colornames = [
|
colornames = [
|
||||||
"none", "red", "yellow", "green", "blue",
|
"none", "red", "yellow", "green", "blue",
|
||||||
"cyan", "magenta"
|
"cyan", "magenta"
|
||||||
|
@ -87,7 +166,7 @@ default_prefs = {
|
||||||
class App(object):
|
class App(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bars = {
|
self.bars = {
|
||||||
"index": "[Arrows|JK|NP]Navigate [RET]Open [C]ompose [R]efresh [O]ptions [?]Help [Q]uit",
|
"index": "[JKNP]Navigate [RET]Open [C]ompose [R]efresh [O]ptions [?]Help [Q]uit",
|
||||||
"thread": "[C]ompose [RET]Interact [Q]Back [R]efresh [B/T]End [?]Help/More"
|
"thread": "[C]ompose [RET]Interact [Q]Back [R]efresh [B/T]End [?]Help/More"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,9 +301,8 @@ class App(object):
|
||||||
focus = "[focused on thread]"
|
focus = "[focused on thread]"
|
||||||
attr = ("dim", "bar")
|
attr = ("dim", "bar")
|
||||||
|
|
||||||
control = "[save/quit to send]" if self.prefs["editor"] else "[F3]Send"
|
|
||||||
self.loop.widget.footer[0].set_text(
|
self.loop.widget.footer[0].set_text(
|
||||||
"[F1]Abort [F2]Swap %s %s" % (control, focus))
|
"[F1]Abort [F2]Swap [F3]Formatting Help [save/quit to send] " + focus)
|
||||||
|
|
||||||
# this hideous and awful sinful horrid unspeakable shithack changes
|
# this hideous and awful sinful horrid unspeakable shithack changes
|
||||||
# the color of the help/title lines and editor border to reflect which
|
# the color of the help/title lines and editor border to reflect which
|
||||||
|
@ -339,7 +417,7 @@ class App(object):
|
||||||
init_body=message["body"],
|
init_body=message["body"],
|
||||||
post_id=post_id,
|
post_id=post_id,
|
||||||
thread_id=thread_id),
|
thread_id=thread_id),
|
||||||
title="[F1]Abort (save/quit to commit)",
|
title="[F1]Abort [F3]Formatting Help (save/quit to commit)",
|
||||||
**frame_theme()),
|
**frame_theme()),
|
||||||
self.loop.widget,
|
self.loop.widget,
|
||||||
align="center",
|
align="center",
|
||||||
|
@ -382,7 +460,7 @@ class App(object):
|
||||||
align=("relative", 50),
|
align=("relative", 50),
|
||||||
valign=("relative", 50),
|
valign=("relative", 50),
|
||||||
width=30,
|
width=30,
|
||||||
height=len(buttons) + 3
|
height=len(buttons) + 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -399,7 +477,7 @@ class App(object):
|
||||||
return [value_type(q) for q in quotes]
|
return [value_type(q) for q in quotes]
|
||||||
|
|
||||||
|
|
||||||
def make_message_body(self, message):
|
def make_message_body(self, message, no_action=False):
|
||||||
"""
|
"""
|
||||||
Returns the widgets that comprise a message in a thread, including the
|
Returns the widgets that comprise a message in a thread, including the
|
||||||
text body, author info and the action button
|
text body, author info and the action button
|
||||||
|
@ -408,11 +486,12 @@ class App(object):
|
||||||
if message["edited"]:
|
if message["edited"]:
|
||||||
info += " [edited]"
|
info += " [edited]"
|
||||||
|
|
||||||
|
callback = self.on_post if not no_action else ignore
|
||||||
name = urwid.Text("~{}".format(self.usermap[message["author"]]["user_name"]))
|
name = urwid.Text("~{}".format(self.usermap[message["author"]]["user_name"]))
|
||||||
post = str(message["post_id"])
|
post = str(message["post_id"])
|
||||||
head = urwid.Columns([
|
head = urwid.Columns([
|
||||||
(2 + len(post), urwid.AttrMap(
|
(2 + len(post), urwid.AttrMap(
|
||||||
cute_button(">" + post, self.on_post, message), "button", "hover")),
|
cute_button(">" + post, callback, message), "button", "hover")),
|
||||||
(len(name._text) + 1, urwid.AttrMap(
|
(len(name._text) + 1, urwid.AttrMap(
|
||||||
name, str(self.usermap[message["author"]]["color"]))),
|
name, str(self.usermap[message["author"]]["color"]))),
|
||||||
urwid.AttrMap(urwid.Text(info), "dim")
|
urwid.AttrMap(urwid.Text(info), "dim")
|
||||||
|
@ -508,7 +587,7 @@ class App(object):
|
||||||
self.set_bars()
|
self.set_bars()
|
||||||
|
|
||||||
|
|
||||||
def refresh(self, bottom=False):
|
def refresh(self, bottom=True):
|
||||||
self.remove_overlays()
|
self.remove_overlays()
|
||||||
if self.mode == "index":
|
if self.mode == "index":
|
||||||
return self.index()
|
return self.index()
|
||||||
|
@ -578,6 +657,60 @@ class App(object):
|
||||||
self.options_menu()
|
self.options_menu()
|
||||||
|
|
||||||
|
|
||||||
|
def general_help(self):
|
||||||
|
"""
|
||||||
|
Show a general help dialog. In all honestly, its not
|
||||||
|
very useful and will only help people who have never
|
||||||
|
really used terminal software before =)
|
||||||
|
"""
|
||||||
|
widget = OptionsMenu(
|
||||||
|
urwid.ListBox(
|
||||||
|
urwid.SimpleFocusListWalker([
|
||||||
|
urwid_rainbows(
|
||||||
|
"This is BBJ, a client/server textboard made for tilde.town!",
|
||||||
|
True),
|
||||||
|
urwid.Text(("dim", "...by ~desvox")),
|
||||||
|
urwid.Divider("-"),
|
||||||
|
urwid.Button("Post Formatting Help", self.formatting_help),
|
||||||
|
urwid.Divider("-"),
|
||||||
|
urwid.Text(general_help)
|
||||||
|
])),
|
||||||
|
title="?????",
|
||||||
|
**frame_theme()
|
||||||
|
)
|
||||||
|
|
||||||
|
app.loop.widget = urwid.Overlay(
|
||||||
|
widget, app.loop.widget,
|
||||||
|
align=("relative", 50),
|
||||||
|
valign=("relative", 50),
|
||||||
|
width=30,
|
||||||
|
height=("relative", 60)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def formatting_help(self, *_):
|
||||||
|
"""
|
||||||
|
Pops a help window with formatting directives.
|
||||||
|
"""
|
||||||
|
message = network.fake_message("\n\n".join(format_help))
|
||||||
|
widget = OptionsMenu(
|
||||||
|
urwid.ListBox(
|
||||||
|
urwid.SimpleFocusListWalker([
|
||||||
|
*app.make_message_body(message, True)
|
||||||
|
])),
|
||||||
|
title="Formatting Help",
|
||||||
|
**frame_theme()
|
||||||
|
)
|
||||||
|
|
||||||
|
app.loop.widget = urwid.Overlay(
|
||||||
|
widget, app.loop.widget,
|
||||||
|
align=("relative", 50),
|
||||||
|
valign=("relative", 50),
|
||||||
|
width=("relative", 98),
|
||||||
|
height=("relative", 60)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def set_color(self, button, value, color):
|
def set_color(self, button, value, color):
|
||||||
if value == False:
|
if value == False:
|
||||||
return
|
return
|
||||||
|
@ -854,7 +987,7 @@ class App(object):
|
||||||
|
|
||||||
if self.mode == "index":
|
if self.mode == "index":
|
||||||
self.set_header('Composing "{}"', title)
|
self.set_header('Composing "{}"', title)
|
||||||
self.set_footer("[F1]Abort [Save and quit to submit your thread]")
|
self.set_footer("[F1]Abort [F3]Formatting Help [Save and quit to submit your thread]")
|
||||||
self.loop.widget = urwid.Overlay(
|
self.loop.widget = urwid.Overlay(
|
||||||
urwid.LineBox(
|
urwid.LineBox(
|
||||||
ExternalEditor("thread_create", title=title),
|
ExternalEditor("thread_create", title=title),
|
||||||
|
@ -1049,15 +1182,17 @@ class ExternalEditor(urwid.Terminal):
|
||||||
else:
|
else:
|
||||||
return app.temp_footer_message("EMPTY POST DISCARDED")
|
return app.temp_footer_message("EMPTY POST DISCARDED")
|
||||||
|
|
||||||
elif key not in ["f1", "f2"]:
|
elif key not in ["f1", "f2", "f3"]:
|
||||||
return super(ExternalEditor, self).keypress(size, key)
|
return super(ExternalEditor, self).keypress(size, key)
|
||||||
|
|
||||||
elif key == "f1":
|
elif key == "f1":
|
||||||
self.terminate()
|
self.terminate()
|
||||||
app.close_editor()
|
app.close_editor()
|
||||||
app.refresh()
|
app.refresh()
|
||||||
# key == "f2"
|
elif key == "f2":
|
||||||
app.switch_editor()
|
app.switch_editor()
|
||||||
|
elif key == "f3":
|
||||||
|
app.formatting_help()
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
@ -1077,6 +1212,12 @@ class OptionsMenu(urwid.LineBox):
|
||||||
# try to let the base class handle the key, if not, we'll take over
|
# try to let the base class handle the key, if not, we'll take over
|
||||||
elif not super(OptionsMenu, self).keypress(size, key):
|
elif not super(OptionsMenu, self).keypress(size, key):
|
||||||
return
|
return
|
||||||
|
elif key in ["shift down", "J", "N"]:
|
||||||
|
for x in range(5):
|
||||||
|
self.keypress(size, "down")
|
||||||
|
elif key in ["shift up", "K", "P"]:
|
||||||
|
for x in range(5):
|
||||||
|
self.keypress(size, "up")
|
||||||
elif key.lower() == "q":
|
elif key.lower() == "q":
|
||||||
app.loop.widget = app.loop.widget[0]
|
app.loop.widget = app.loop.widget[0]
|
||||||
elif key in ["ctrl n", "j", "n"]:
|
elif key in ["ctrl n", "j", "n"]:
|
||||||
|
@ -1102,11 +1243,11 @@ class ActionBox(urwid.ListBox):
|
||||||
elif key in ["k", "p", "ctrl p"]:
|
elif key in ["k", "p", "ctrl p"]:
|
||||||
self._keypress_up(size)
|
self._keypress_up(size)
|
||||||
|
|
||||||
elif key in ["J", "N"]:
|
elif key in ["shift down", "J", "N"]:
|
||||||
for x in range(5):
|
for x in range(5):
|
||||||
self._keypress_down(size)
|
self._keypress_down(size)
|
||||||
|
|
||||||
elif key in ["K", "P"]:
|
elif key in ["shift up", "K", "P"]:
|
||||||
for x in range(5):
|
for x in range(5):
|
||||||
self._keypress_up(size)
|
self._keypress_up(size)
|
||||||
|
|
||||||
|
@ -1131,6 +1272,9 @@ class ActionBox(urwid.ListBox):
|
||||||
elif key == "o":
|
elif key == "o":
|
||||||
app.options_menu()
|
app.options_menu()
|
||||||
|
|
||||||
|
elif key == "?":
|
||||||
|
app.general_help()
|
||||||
|
|
||||||
elif key.lower() == "q":
|
elif key.lower() == "q":
|
||||||
app.back()
|
app.back()
|
||||||
|
|
||||||
|
@ -1168,12 +1312,13 @@ def cute_button(label, callback=None, data=None):
|
||||||
return button
|
return button
|
||||||
|
|
||||||
|
|
||||||
def urwid_rainbows(string):
|
def urwid_rainbows(string, bold=False):
|
||||||
"""
|
"""
|
||||||
Same as below, but instead of printing rainbow text, returns
|
Same as below, but instead of printing rainbow text, returns
|
||||||
a markup list suitable for urwid's Text contructor.
|
a markup list suitable for urwid's Text contructor.
|
||||||
"""
|
"""
|
||||||
colors = [str(x) for x in range(1, 7)]
|
colors = [str(x) for x in range(1, 7)]
|
||||||
|
if bold: colors = [(c + "0") for c in colors]
|
||||||
return urwid.Text([(choice(colors), char) for char in string])
|
return urwid.Text([(choice(colors), char) for char in string])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1345,6 +1490,13 @@ def bbjrc(mode, **params):
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def ignore(*_, **__):
|
||||||
|
"""
|
||||||
|
The blackness of my soul.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
run("clear", shell=True)
|
run("clear", shell=True)
|
||||||
|
|
21
server.py
21
server.py
|
@ -238,7 +238,9 @@ class API(object):
|
||||||
def thread_load(self, args, database, user, **kwargs):
|
def thread_load(self, args, database, user, **kwargs):
|
||||||
"""
|
"""
|
||||||
Returns the thread object with all of its messages loaded.
|
Returns the thread object with all of its messages loaded.
|
||||||
Requires the argument `thread_id`
|
Requires the argument `thread_id`. `format` may also be
|
||||||
|
specified as a formatter to run the messages through.
|
||||||
|
Currently only "sequential" is supported.
|
||||||
"""
|
"""
|
||||||
validate(args, ["thread_id"])
|
validate(args, ["thread_id"])
|
||||||
thread = db.thread_get(database, args["thread_id"])
|
thread = db.thread_get(database, args["thread_id"])
|
||||||
|
@ -300,6 +302,23 @@ class API(object):
|
||||||
database, user["user_id"], args["thread_id"], args["post_id"])
|
database, user["user_id"], args["thread_id"], args["post_id"])
|
||||||
|
|
||||||
|
|
||||||
|
@api_method
|
||||||
|
def format_message(self, args, database, user, **kwargs):
|
||||||
|
"""
|
||||||
|
Requires the arguments `body` and `format`. Applies
|
||||||
|
`format` to `body` and returns the new object. See
|
||||||
|
`thread_load` for supported specifications for `format`.
|
||||||
|
"""
|
||||||
|
validate(args, ["format", "body"])
|
||||||
|
message = [{"body": args["body"]}]
|
||||||
|
if args["format"] == "sequential":
|
||||||
|
formatter = formatting.sequential_expressions
|
||||||
|
else:
|
||||||
|
raise BBJParameterError("invalid format directive.")
|
||||||
|
formatting.apply_formatting(message, formatter)
|
||||||
|
return message[0]["body"]
|
||||||
|
|
||||||
|
|
||||||
@api_method
|
@api_method
|
||||||
def set_thread_pin(self, args, database, user, **kwargs):
|
def set_thread_pin(self, args, database, user, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -11,7 +11,7 @@ A B A N D O N ,:
|
||||||
/.' '
|
/.' '
|
||||||
This module includes a couple '/'
|
This module includes a couple '/'
|
||||||
of custom (GROAN) formatting +
|
of custom (GROAN) formatting +
|
||||||
specifications and parsers ' me irl
|
specifications and parsers '
|
||||||
for them. Why did i do this? `.
|
for them. Why did i do this? `.
|
||||||
I have no idea! .-"-
|
I have no idea! .-"-
|
||||||
( |
|
( |
|
||||||
|
@ -70,7 +70,7 @@ colors = [
|
||||||
]
|
]
|
||||||
|
|
||||||
markup = [
|
markup = [
|
||||||
"bold", "italic", "underline", "linequote", "quote", "rainbow"
|
"bold", "underline", "linequote", "quote", "rainbow"
|
||||||
]
|
]
|
||||||
|
|
||||||
# PS: regex parsing is no longer used for these, preserving anyways
|
# PS: regex parsing is no longer used for these, preserving anyways
|
||||||
|
@ -82,6 +82,8 @@ markup = [
|
||||||
|
|
||||||
# quotes being references to other post_ids, like >>34 or >>0 for OP
|
# quotes being references to other post_ids, like >>34 or >>0 for OP
|
||||||
quotes = re.compile(">>([0-9]+)")
|
quotes = re.compile(">>([0-9]+)")
|
||||||
|
bold = re.compile(r"\*{2}(.{1,20})\*{2}")
|
||||||
|
underline = re.compile(r"__(.{1,20})__")
|
||||||
|
|
||||||
|
|
||||||
def parse_segments(text, sanitize_linequotes=True):
|
def parse_segments(text, sanitize_linequotes=True):
|
||||||
|
@ -98,6 +100,8 @@ def parse_segments(text, sanitize_linequotes=True):
|
||||||
if not segment:
|
if not segment:
|
||||||
continue
|
continue
|
||||||
segment = quotes.sub(lambda m: "[quote: %s]" % m.group(1), segment)
|
segment = quotes.sub(lambda m: "[quote: %s]" % m.group(1), segment)
|
||||||
|
segment = bold.sub(lambda m: "[bold: %s]" % m.group(1), segment)
|
||||||
|
segment = underline.sub(lambda m: "[underline: %s]" % m.group(1), segment)
|
||||||
if segment.startswith(">"):
|
if segment.startswith(">"):
|
||||||
if sanitize_linequotes:
|
if sanitize_linequotes:
|
||||||
inner = segment.replace("]", "\\]")
|
inner = segment.replace("]", "\\]")
|
||||||
|
|
Loading…
Reference in New Issue