Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 804438f045 | |||
| ab1550c09c | |||
| 315a249ef6 | |||
| 92bdd7b660 | |||
| 77d38011b5 | |||
| 4364c16625 | |||
| deb8d3ba4b | |||
| f63233dbb7 | |||
| ab42b4a27d | |||
| 8ae384b1cd | |||
| 5e7168c848 | |||
| 2e54939345 | |||
| ceb4937c60 | |||
| 3e4f3de2a8 | |||
| b1d1de659d | |||
| df9d419919 | 
							
								
								
									
										32
									
								
								bundle.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								bundle.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| import zipfile | ||||
| import glob | ||||
| import os | ||||
| 
 | ||||
| 
 | ||||
| # TODO: should we include .pyc files? | ||||
| # TODO: add urwid source into the repo somewhere | ||||
| 
 | ||||
| files = { | ||||
|     '__main__.py': 'clients/urwid/main.py', | ||||
|     'network.py': 'clients/network_client.py', | ||||
|     'urwid': 'env/lib/python3.8/site-packages/urwid/*.py', | ||||
| } | ||||
| 
 | ||||
| with open('bbj_demo', 'wb') as f: | ||||
|     f.write(b"#!/usr/bin/env python3\n") | ||||
|     with zipfile.ZipFile(f, 'w', compression=zipfile.ZIP_DEFLATED) as z: | ||||
|         z.comment = b'BBJ' | ||||
|         for name, source in files.items(): | ||||
|             if '*' in source: | ||||
|                 dirname = name | ||||
|                 for path in sorted(glob.glob(source)): | ||||
|                     name = dirname + '/' + os.path.basename(path) | ||||
|                     z.write(path, name) | ||||
|             else: | ||||
|                 z.write(source, name) | ||||
| try: | ||||
|     mask = os.umask(0) | ||||
|     os.umask(mask) | ||||
| except OSError: | ||||
|     mask = 0 | ||||
| os.chmod(z.filename, 0o777&~mask) | ||||
| @ -29,27 +29,18 @@ from getpass import getpass | ||||
| from subprocess import call | ||||
| from random import choice | ||||
| from code import interact | ||||
| from sys import exit | ||||
| import rlcompleter | ||||
| import readline | ||||
| import tempfile | ||||
| import shlex | ||||
| import urwid | ||||
| import json | ||||
| import os | ||||
| import re | ||||
| 
 | ||||
| # XxX_N0_4rgP4rs3_XxX ###yoloswag | ||||
| def get_arg(key, default=None, get_value=True): | ||||
|     try: | ||||
|         spec = argv.index("--" + key) | ||||
|         value = argv[spec + 1] if get_value else True | ||||
|     except ValueError: # --key not specified | ||||
|         value = default | ||||
|     except IndexError: # flag given but no value | ||||
|         exit("invalid format for --" + key) | ||||
|     return value | ||||
| 
 | ||||
| if get_arg("help", False, False): | ||||
|     print("""BBJ Urwid Client | ||||
| help_text = """\ | ||||
| BBJ Urwid Client | ||||
| Available options: | ||||
|     --help: this message | ||||
|     --https: enable use of https, requires host support | ||||
| @ -62,16 +53,7 @@ Available environment variables: | ||||
|     BBJ_PASSWORD: set your password to log in automatically. | ||||
|       if the password is wrong, will prompt you as normal. | ||||
| Please note that these environment variables need to be exported, and are | ||||
| visible to all other programs run from your shell.""") | ||||
|     exit() | ||||
| 
 | ||||
| try: | ||||
|     network = BBJ(get_arg("host", "127.0.0.1"), | ||||
|                   get_arg("port", 7099), | ||||
|                   get_arg("https", False, False)) | ||||
| except URLError as e: | ||||
|     # print the connection error in red | ||||
|     exit("\033[0;31m%s\033[0m" % repr(e)) | ||||
| visible to all other programs run from your shell.""" | ||||
| 
 | ||||
| obnoxious_logo = """ | ||||
|   %     _                 *              !            * | ||||
| @ -273,14 +255,6 @@ colormap = [ | ||||
|     ("60", "light magenta", "default") | ||||
| ] | ||||
| 
 | ||||
| def replace_color(name,fg,bg=None): | ||||
| 	for i,t in enumerate(colormap): | ||||
| 		t = list(t) | ||||
| 		if t[0]==name: | ||||
| 			t[1] = fg | ||||
| 			t[2] = bg if bg is not None else t[2] | ||||
| 			colormap.__setitem__(i,tuple(t)) | ||||
| 
 | ||||
| escape_map = { | ||||
|     key: urwid.vterm.ESC + sequence | ||||
|       for sequence, key in urwid.escape.input_sequences | ||||
| @ -336,7 +310,8 @@ markpath = os.path.join(os.getenv("HOME"), ".bbjmarks") | ||||
| pinpath = os.path.join(os.getenv("HOME"), ".bbjpins") | ||||
| 
 | ||||
| class App(object): | ||||
|     def __init__(self): | ||||
|     def __init__(self, network): | ||||
|         self.network = network | ||||
|         self.prefs = bbjrc("load") | ||||
|         self.client_pinned_threads = load_client_pins() | ||||
|         self.usermap = {} | ||||
| @ -395,7 +370,7 @@ class App(object): | ||||
|         bar formatting to it. | ||||
|         """ | ||||
|         header = ("{}@bbj | " + text).format( | ||||
|             (network.user_name or "anonymous"), | ||||
|             (self.network.user_name or "anonymous"), | ||||
|             *format_specs | ||||
|         ) | ||||
|         self.loop.widget.header = urwid.AttrMap(urwid.Text(header), "bar") | ||||
| @ -601,7 +576,7 @@ class App(object): | ||||
|         # first we need to get the server's version of the message | ||||
|         # instead of our formatted one | ||||
|         try: | ||||
|             message = network.edit_query(thread_id, post_id) | ||||
|             message = self.network.edit_query(thread_id, post_id) | ||||
|         except UserWarning as e: | ||||
|             self.remove_overlays() | ||||
|             return self.temp_footer_message(e.description) | ||||
| @ -624,7 +599,7 @@ class App(object): | ||||
|             urwid.Text(("bold", "Delete this %s?" % ("whole thread" if op else "post"))), | ||||
|             urwid.Divider(), | ||||
|             cute_button(("10" , ">> Yes"), lambda _: [ | ||||
|                 network.message_delete(message["thread_id"], message["post_id"]), | ||||
|                 self.network.message_delete(message["thread_id"], message["post_id"]), | ||||
|                 self.remove_overlays(), | ||||
|                 self.index() if op else self.refresh() | ||||
|             ]), | ||||
| @ -646,7 +621,7 @@ class App(object): | ||||
|     def toggle_formatting(self, button, message): | ||||
|         self.remove_overlays() | ||||
|         raw = not message["send_raw"] | ||||
|         network.set_post_raw(message["thread_id"], message["post_id"], raw) | ||||
|         self.network.set_post_raw(message["thread_id"], message["post_id"], raw) | ||||
|         return self.refresh() | ||||
| 
 | ||||
| 
 | ||||
| @ -663,7 +638,7 @@ class App(object): | ||||
|                 "View %sQuote" % ("a " if len(quotes) != 1 else ""), | ||||
|                 self.quote_view_menu, quotes)) | ||||
| 
 | ||||
|         if network.can_edit(message["thread_id"], message["post_id"]) \ | ||||
|         if self.network.can_edit(message["thread_id"], message["post_id"]) \ | ||||
|                and not self.window_split: | ||||
| 
 | ||||
|             if message["post_id"] == 0: | ||||
| @ -676,7 +651,7 @@ class App(object): | ||||
|                 "Enable Formatting" if raw else "Disable Formatting", | ||||
|                 self.toggle_formatting, message)) | ||||
|             buttons.insert(0, urwid.Button("Edit Post", self.edit_post, message)) | ||||
|             if network.user["is_admin"]: | ||||
|             if self.network.user["is_admin"]: | ||||
|                 buttons.insert(0, urwid.Text(("20", "Reminder: You're an admin!"))) | ||||
| 
 | ||||
|         if not buttons: | ||||
| @ -818,7 +793,7 @@ class App(object): | ||||
|         """ | ||||
|         if self.mode == "thread": | ||||
|             # mark the current position in this thread before going back to the index | ||||
|             mark() | ||||
|             self.mark() | ||||
| 
 | ||||
|         self.body.attr_map = {None: "default"} | ||||
|         self.mode = "index" | ||||
| @ -829,7 +804,7 @@ class App(object): | ||||
|             # narrowed selection of content, so we dont want to resume last_index_pos | ||||
|             self.last_index_pos = False | ||||
|         else: | ||||
|             threads, usermap = network.thread_index() | ||||
|             threads, usermap = self.network.thread_index() | ||||
|             self.usermap.update(usermap) | ||||
|         self.walker.clear() | ||||
| 
 | ||||
| @ -875,7 +850,7 @@ class App(object): | ||||
|             self.body.attr_map = {None: "default"} | ||||
| 
 | ||||
|         self.mode = "thread" | ||||
|         thread, usermap = network.thread_load(thread_id, format="sequential") | ||||
|         thread, usermap = self.network.thread_load(thread_id, format="sequential") | ||||
|         self.usermap.update(usermap) | ||||
|         self.thread = thread | ||||
|         self.match_data["matches"].clear() | ||||
| @ -884,7 +859,7 @@ class App(object): | ||||
|             self.walker += self.make_message_body(message) | ||||
|         self.set_default_header() | ||||
|         self.set_default_footer() | ||||
|         self.goto_post(mark(thread_id)) | ||||
|         self.goto_mark(thread_id) | ||||
| 
 | ||||
| 
 | ||||
|     def toggle_client_pin(self): | ||||
| @ -896,16 +871,16 @@ class App(object): | ||||
| 
 | ||||
| 
 | ||||
|     def toggle_server_pin(self): | ||||
|         if self.mode != "index" or not network.user["is_admin"]: | ||||
|         if self.mode != "index" or not self.network.user["is_admin"]: | ||||
|             return | ||||
|         thread = self.walker.get_focus()[0].thread | ||||
|         network.thread_set_pin(thread["thread_id"], not thread["pinned"]) | ||||
|         self.network.thread_set_pin(thread["thread_id"], not thread["pinned"]) | ||||
|         self.index() | ||||
| 
 | ||||
| 
 | ||||
|     def search_index_callback(self, query): | ||||
|         simple_query = query.lower().strip() | ||||
|         threads, usermap = network.thread_index() | ||||
|         threads, usermap = self.network.thread_index() | ||||
|         self.usermap.update(usermap) | ||||
|         results = [ | ||||
|             thread for thread in threads | ||||
| @ -1008,10 +983,10 @@ class App(object): | ||||
|                 self.last_index_pos = self.get_focus_post(True).thread["thread_id"] | ||||
|             self.index() | ||||
|         else: | ||||
|             mark() | ||||
|             self.mark() | ||||
|             thread = self.thread["thread_id"] | ||||
|             self.thread_load(None, thread) | ||||
|             self.goto_post(mark(thread)) | ||||
|             self.goto_mark(thread) | ||||
|         self.temp_footer_message("Refreshed content!", 1) | ||||
| 
 | ||||
| 
 | ||||
| @ -1046,10 +1021,31 @@ class App(object): | ||||
|                 width=30, height=6) | ||||
| 
 | ||||
|         else: | ||||
|             mark() | ||||
|             self.mark() | ||||
|             self.index() | ||||
| 
 | ||||
| 
 | ||||
|     def mark(self, thread_id=None): | ||||
|         if self.mode != "thread": | ||||
|             return | ||||
| 
 | ||||
|         if thread_id is None: | ||||
|             thread_id = self.thread['thread_id'] | ||||
|         pos = self.get_focus_post() | ||||
|         mark(thread_id, pos, default=0) | ||||
|         return pos | ||||
| 
 | ||||
| 
 | ||||
|     def goto_mark(self, thread_id=None): | ||||
|         if self.mode != "thread": | ||||
|             return | ||||
| 
 | ||||
|         if thread_id is None: | ||||
|             thread_id = self.thread['thread_id'] | ||||
|         pos = mark(thread_id, default=0) | ||||
|         self.goto_post(pos) | ||||
| 
 | ||||
| 
 | ||||
|     def get_focus_post(self, return_widget=False): | ||||
|         pos = self.box.get_focus_path()[0] | ||||
|         if self.mode == "thread": | ||||
| @ -1180,10 +1176,10 @@ class App(object): | ||||
|         """ | ||||
|         self.loop.widget = self.loop.widget[0] | ||||
|         self.loop.stop() | ||||
|         call("clear", shell=True) | ||||
|         call("clear") | ||||
|         print(welcome) | ||||
|         try: | ||||
|             log_in(relog=True) | ||||
|             log_in(self.network) | ||||
|         except (KeyboardInterrupt, InterruptedError): | ||||
|             pass | ||||
|         self.loop.start() | ||||
| @ -1196,8 +1192,9 @@ class App(object): | ||||
|         Options menu callback to anonymize the user and | ||||
|         then redisplay the options menu. | ||||
|         """ | ||||
|         network.user_name = network.user_auth = None | ||||
|         network.user = network("get_me")["data"] | ||||
|         self.network.user_name = None | ||||
|         self.network.user_auth = None | ||||
|         self.network.user = self.network.request("get_me")["data"] | ||||
|         self.loop.widget = self.loop.widget[0] | ||||
|         self.set_default_header() | ||||
|         self.options_menu() | ||||
| @ -1239,7 +1236,7 @@ class App(object): | ||||
|         """ | ||||
|         # we can "recycle" the server's formatting abilities to | ||||
|         # use the same syntax for the help text itself | ||||
|         message = network.fake_message( | ||||
|         message = self.network.fake_message( | ||||
|             "\n\n".join(format_help), format="sequential") | ||||
| 
 | ||||
|         widget = OptionsMenu( | ||||
| @ -1261,7 +1258,7 @@ class App(object): | ||||
|     def set_color(self, button, value, color): | ||||
|         if value == False: | ||||
|             return | ||||
|         network.user_update(color=color) | ||||
|         self.network.user_update(color=color) | ||||
| 
 | ||||
| 
 | ||||
|     def toggle_exit(self, button, value): | ||||
| @ -1288,10 +1285,10 @@ class App(object): | ||||
| 
 | ||||
|     def change_username(self, *_): | ||||
|         self.loop.stop() | ||||
|         call("clear", shell=True) | ||||
|         call("clear") | ||||
|         try: | ||||
|             name = nameloop("Choose a new username", True) | ||||
|             network.user_update(user_name=name) | ||||
|             name = nameloop(self.network, "Choose a new username") | ||||
|             self.network.user_update(user_name=name) | ||||
|             motherfucking_rainbows("~~hello there %s~~" % name) | ||||
|             sleep(0.8) | ||||
|             self.loop.start() | ||||
| @ -1304,10 +1301,10 @@ class App(object): | ||||
| 
 | ||||
|     def change_password(self, *_): | ||||
|         self.loop.stop() | ||||
|         call("clear", shell=True) | ||||
|         call("clear") | ||||
|         try: | ||||
|             password = password_loop("Choose a new password. Can be empty", True) | ||||
|             network.user_update(auth_hash=network._hash(password)) | ||||
|             password = password_loop("Choose a new password. Can be empty") | ||||
|             self.network.user_update(auth_hash=self.network._hash(password)) | ||||
|             motherfucking_rainbows("SET NEW PASSWORD") | ||||
|             sleep(0.8) | ||||
|             self.loop.start() | ||||
| @ -1403,8 +1400,8 @@ class App(object): | ||||
|         editor_buttons = [] | ||||
|         edit_mode = [] | ||||
| 
 | ||||
|         if network.user_auth: | ||||
|             account_message = "Logged in as %s." % network.user_name | ||||
|         if self.network.user_auth: | ||||
|             account_message = "Logged in as %s." % self.network.user_name | ||||
|             account_stuff = [ | ||||
|                 urwid.Button("Relog", on_press=self.relog), | ||||
|                 urwid.Button("Go anonymous", on_press=self.unlog), | ||||
| @ -1421,7 +1418,7 @@ class App(object): | ||||
|             for index, color in enumerate(colornames): | ||||
|                 urwid.RadioButton( | ||||
|                     user_colors, color.title(), | ||||
|                     network.user["color"] == index, | ||||
|                     self.network.user["color"] == index, | ||||
|                     self.set_color, index) | ||||
| 
 | ||||
|             for item in user_colors: | ||||
| @ -1626,7 +1623,9 @@ class App(object): | ||||
|         descriptor, path = tempfile.mkstemp() | ||||
|         with open(path, "w") as _: | ||||
|             _.write(init_body) | ||||
|         call("export LANG=en_US.UTF-8; %s %s" % (self.prefs["editor"], path), shell=True) | ||||
|         env = os.environ.copy() | ||||
|         env['LANG'] = 'en_US.UTF-8' | ||||
|         call("%s %s" % (self.prefs["editor"], shlex.quote(path)), env=env, shell=True) | ||||
|         with open(path) as _: | ||||
|             body = _.read() | ||||
|         os.remove(path) | ||||
| @ -1643,7 +1642,7 @@ class App(object): | ||||
|             return self.footer_prompt("Title", self.compose) | ||||
| 
 | ||||
|         elif title: | ||||
|             try: network.validate("title", title) | ||||
|             try: self.network.validate("title", title) | ||||
|             except AssertionError as e: | ||||
|                 return self.footer_prompt( | ||||
|                     "Title", self.compose, extra_text=e.description) | ||||
| @ -1652,24 +1651,19 @@ class App(object): | ||||
|             body = self.overthrow_ext_edit(init_body) | ||||
|             if not body or re.search("^>>[0-9]+$", body): | ||||
|                 return self.temp_footer_message("EMPTY POST DISCARDED") | ||||
|             params = {"body": body} | ||||
| 
 | ||||
|             if self.mode == "thread" and not edit: | ||||
|                 endpoint = "thread_reply" | ||||
|                 params.update({"thread_id": self.thread["thread_id"]}) | ||||
|                 self.network.thread_reply(self.thread["thread_id"], body) | ||||
| 
 | ||||
|             elif edit: | ||||
|                 endpoint = "edit_post" | ||||
|                 params.update({ | ||||
|                     "thread_id": self.thread["thread_id"], | ||||
|                     "post_id": edit["post_id"] | ||||
|                 }) | ||||
|                 self.network.edit_message( | ||||
|                     thread_id=self.thread["thread_id"], | ||||
|                     post_id=edit["post_id"], | ||||
|                     body=body) | ||||
| 
 | ||||
|             else: | ||||
|                 endpoint = "thread_create" | ||||
|                 params.update({"title": title}) | ||||
|                 self.network.thread_create(title, body) | ||||
| 
 | ||||
|             network.request(endpoint, **params) | ||||
|             self.refresh() | ||||
|             if edit: | ||||
|                 self.goto_post(edit["post_id"]) | ||||
| @ -1722,6 +1716,12 @@ class App(object): | ||||
|         self.window_split=True | ||||
|         self.switch_editor() | ||||
| 
 | ||||
|     def repaint_screen(self): | ||||
|         """ | ||||
|         Force urwid to repaint the whole screen. | ||||
|         """ | ||||
|         self.loop.screen.clear() | ||||
| 
 | ||||
| 
 | ||||
| class MessageBody(urwid.Text): | ||||
|     """ | ||||
| @ -1778,7 +1778,7 @@ class MessageBody(urwid.Text): | ||||
|                         if _c != 0: | ||||
|                             color = str(_c) | ||||
| 
 | ||||
|                         if user != "anonymous" and user["user_name"] == network.user_name: | ||||
|                         if user != "anonymous" and user["user_name"] == app.network.user_name: | ||||
|                             display = "[You]" | ||||
|                             # bold it | ||||
|                             color += "0" | ||||
| @ -1986,7 +1986,7 @@ class ExternalEditor(urwid.Terminal): | ||||
|         # should use the options menu to switch to Overthrow mode. | ||||
|         env.update({"LANG": "POSIX"}) | ||||
|         command = ["bash", "-c", "{} {}; echo Press any key to kill this window...".format( | ||||
|             app.prefs["editor"], self.path)] | ||||
|             app.prefs["editor"], shlex.quote(self.path))] | ||||
|         super(ExternalEditor, self).__init__(command, env, app.loop, app.prefs["edit_escapes"]["abort"]) | ||||
|         urwid.connect_signal(self, "closed", self.exterminate) | ||||
| 
 | ||||
| @ -1995,7 +1995,7 @@ class ExternalEditor(urwid.Terminal): | ||||
|     #     app.loop.widget = app.loop.widget[0] | ||||
|     #     if not value: | ||||
|     #         app.loop.stop() | ||||
|     #         call("clear", shell=True) | ||||
|     #         call("clear") | ||||
|     #         print(welcome) | ||||
|     #         log_in(True) | ||||
|     #         app.loop.start() | ||||
| @ -2003,9 +2003,21 @@ class ExternalEditor(urwid.Terminal): | ||||
| 
 | ||||
| 
 | ||||
|     def exterminate(self, *_, anon_confirmed=False): | ||||
|         # close the editor and grab the post body | ||||
|         app.close_editor() | ||||
|         with open(self.path) as _: | ||||
|             body = _.read().strip() | ||||
|         os.remove(self.path) | ||||
| 
 | ||||
|         # make sure its not empty | ||||
|         if not body or re.search("^>>[0-9]+$", body): | ||||
|             app.temp_footer_message("EMPTY POST DISCARDED") | ||||
|             return | ||||
| 
 | ||||
|         # are we anonymous? check if the user wants to log in first | ||||
|         if app.prefs["confirm_anon"] \ | ||||
|            and not anon_confirmed    \ | ||||
|            and network.user["user_name"] == "anonymous": | ||||
|            and app.network.user["user_name"] == "anonymous": | ||||
|             # TODO fixoverlay: urwid terminal widgets have been mucking | ||||
|             # up overlay dialogs since the wee days of bbj, i really | ||||
|             # need to find a real solution instead of dodging the issue | ||||
| @ -2032,7 +2044,7 @@ class ExternalEditor(urwid.Terminal): | ||||
|             #     return | ||||
|             # else: | ||||
|             app.loop.stop() | ||||
|             call("clear", shell=True) | ||||
|             call("clear") | ||||
|             print(anon_warn) | ||||
|             choice = paren_prompt( | ||||
|                 "Post anonymously?", default="yes", choices=["Yes", "no"] | ||||
| @ -2041,27 +2053,20 @@ class ExternalEditor(urwid.Terminal): | ||||
|                 log_in(True) | ||||
|             app.loop.start() | ||||
| 
 | ||||
|         app.close_editor() | ||||
|         with open(self.path) as _: | ||||
|             body = _.read().strip() | ||||
|         os.remove(self.path) | ||||
|         # ok; do the post | ||||
|         self.params.update({"body": body}) | ||||
|         app.network.request(self.endpoint, **self.params) | ||||
|         if self.endpoint == "edit_post": | ||||
|             app.refresh() | ||||
|             app.goto_post(self.params["post_id"]) | ||||
| 
 | ||||
|         if body and not re.search("^>>[0-9]+$", body): | ||||
|             self.params.update({"body": body}) | ||||
|             network.request(self.endpoint, **self.params) | ||||
|             if self.endpoint == "edit_post": | ||||
|                 app.refresh() | ||||
|                 app.goto_post(self.params["post_id"]) | ||||
|         elif app.mode == "thread": | ||||
|             app.refresh() | ||||
|             app.goto_post(app.thread["reply_count"]) | ||||
| 
 | ||||
|             elif app.mode == "thread": | ||||
|                 app.refresh() | ||||
|                 app.goto_post(app.thread["reply_count"]) | ||||
| 
 | ||||
|             else: | ||||
|                 app.last_pos = None | ||||
|                 app.index() | ||||
|         else: | ||||
|             app.temp_footer_message("EMPTY POST DISCARDED") | ||||
|             app.last_pos = None | ||||
|             app.index() | ||||
| 
 | ||||
| 
 | ||||
|     def keypress(self, size, key): | ||||
| @ -2079,7 +2084,7 @@ class ExternalEditor(urwid.Terminal): | ||||
| 
 | ||||
|         if keyl == "ctrl l": | ||||
|             # always do this, and also pass it to the terminal | ||||
|             wipe_screen() | ||||
|             app.repaint_screen() | ||||
| 
 | ||||
|         elif key == app.prefs["edit_escapes"]["abort"]: | ||||
|             self.terminate() | ||||
| @ -2157,7 +2162,7 @@ class OptionsMenu(urwid.LineBox): | ||||
|             return self.keypress(size, "enter") | ||||
| 
 | ||||
|         elif keyl == "ctrl l": | ||||
|             wipe_screen() | ||||
|             app.repaint_screen() | ||||
| 
 | ||||
| 
 | ||||
|     def mouse_event(self, size, event, button, x, y, focus): | ||||
| @ -2213,7 +2218,7 @@ class ActionBox(urwid.ListBox): | ||||
|                 self.change_focus(size, 0) | ||||
| 
 | ||||
|         elif key == "ctrl l": | ||||
|             wipe_screen() | ||||
|             app.repaint_screen() | ||||
| 
 | ||||
|         elif keyl == "o": | ||||
|             app.options_menu() | ||||
| @ -2260,13 +2265,13 @@ class ActionBox(urwid.ListBox): | ||||
|         elif key == "~": | ||||
|             # sssssshhhhhhhh | ||||
|             app.loop.stop() | ||||
|             try: call("sl", shell=True) | ||||
|             try: call("sl") | ||||
|             except: pass | ||||
|             app.loop.start() | ||||
| 
 | ||||
|         elif keyl == "$": | ||||
|             app.loop.stop() | ||||
|             call("clear", shell=True) | ||||
|             call("clear") | ||||
|             readline.set_completer(rlcompleter.Completer().complete) | ||||
|             readline.parse_and_bind("tab: complete") | ||||
|             interact(banner="Python " + version + "\nBBJ Interactive Console\nCtrl-D exits.", local=globals()) | ||||
| @ -2314,7 +2319,7 @@ def frilly_exit(): | ||||
|         out = "  ~~CoMeE BaCkK SooOn~~  0000000" | ||||
|         motherfucking_rainbows(out.zfill(width)) | ||||
|     else: | ||||
|         call("clear", shell=True) | ||||
|         call("clear") | ||||
|         motherfucking_rainbows("Come back soon! <3") | ||||
|     exit() | ||||
| 
 | ||||
| @ -2393,47 +2398,57 @@ def paren_prompt(text, positive=True, choices=[], function=input, default=None): | ||||
|         return "" | ||||
| 
 | ||||
| 
 | ||||
| def sane_value(key, prompt, positive=True, return_empty=False): | ||||
|     response = paren_prompt(prompt, positive) | ||||
|     if return_empty and response == "": | ||||
|         return response | ||||
|     try: network.validate(key, response) | ||||
|     except AssertionError as e: | ||||
|         return sane_value(key, e.description, False) | ||||
|     return response | ||||
| 
 | ||||
| def sane_value(prompt, validate, positive=True, function=input): | ||||
|     """Prompts for an input with paren_prompt until validate(response) | ||||
|     returns None (or something falsy). Otherwise the returned string is used | ||||
|     as the new prompt. | ||||
|     """ | ||||
|     while 1: | ||||
|         response = paren_prompt(prompt, positive, function=function) | ||||
|         error = validate(response) | ||||
|         if not error: | ||||
|             return response | ||||
|         prompt = str(error) if error != None else "" | ||||
|         positive = False | ||||
| 
 | ||||
| def password_loop(prompt, positive=True): | ||||
|     response1 = paren_prompt(prompt, positive, function=getpass) | ||||
|     if response1 == "": | ||||
|         confprompt = "Confirm empty password" | ||||
|     else: | ||||
|         confprompt = "Confirm it" | ||||
|     response2 = paren_prompt(confprompt, function=getpass) | ||||
|     if response1 != response2: | ||||
|         return password_loop("Those didnt match. Try again", False) | ||||
|     return response1 | ||||
|     while 1: | ||||
|         response1 = paren_prompt(prompt, positive, function=getpass) | ||||
|         if response1 == "": | ||||
|             confprompt = "Confirm empty password" | ||||
|         else: | ||||
|             confprompt = "Confirm it" | ||||
|         response2 = paren_prompt(confprompt, function=getpass) | ||||
|         if response1 != response2: | ||||
|             prompt = "Those didnt match. Try again" | ||||
|             positive = False | ||||
|         else: | ||||
|             return response1 | ||||
| 
 | ||||
| 
 | ||||
| def nameloop(prompt, positive): | ||||
|     name = sane_value("user_name", prompt, positive) | ||||
|     if network.user_is_registered(name): | ||||
|         return nameloop("%s is already registered" % name, False) | ||||
|     return name | ||||
| def nameloop(network, prompt, positive=True): | ||||
|     def validate_name(name): | ||||
|         try: network.validate("user_name", name) | ||||
|         except AssertionError as e: | ||||
|             return e.description | ||||
|         if network.user_is_registered(name): | ||||
|             return "%s is already registered" % name | ||||
|     return sane_value(prompt, validate_name, positive) | ||||
| 
 | ||||
| 
 | ||||
| def log_in(relog=False): | ||||
| def log_in(network, name="", password=""): | ||||
|     """ | ||||
|     Handles login or registration using an oldschool input() | ||||
|     chain. The user is run through this before starting the | ||||
|     Handles login or registration. If name and/or password are not | ||||
|     provided, the user is prompted for them using an oldschool | ||||
|     input() chain. The user is run through this before starting the | ||||
|     curses app. | ||||
|     """ | ||||
|     if relog: | ||||
|         name = sane_value("user_name", "Username", return_empty=True) | ||||
|     else: | ||||
|         name = get_arg("user") \ | ||||
|            or os.getenv("BBJ_USER") \ | ||||
|            or sane_value("user_name", "Username", return_empty=True) | ||||
|     if not name: | ||||
|         def validate_name(response): | ||||
|             if response != "": # allow empty username | ||||
|                 try: network.validate("user_name", response) | ||||
|                 except AssertionError as e: | ||||
|                     return e.description | ||||
|         name = sane_value("Username", validate_name) | ||||
|     if name == "": | ||||
|         motherfucking_rainbows("~~W3 4R3 4n0nYm0u5~~") | ||||
|     else: | ||||
| @ -2442,21 +2457,18 @@ def log_in(relog=False): | ||||
|         try: | ||||
|             network.set_credentials( | ||||
|                 name, | ||||
|                 os.getenv("BBJ_PASSWORD", default="") | ||||
|                   if not relog else "" | ||||
|                 password if name and password else "" | ||||
|             ) | ||||
|             # make it easy for people who use an empty password =) | ||||
|             motherfucking_rainbows("~~welcome back {}~~".format(network.user_name)) | ||||
| 
 | ||||
|         except ConnectionRefusedError: | ||||
|             def login_loop(prompt, positive): | ||||
|             def validate_creds(password): | ||||
|                 try: | ||||
|                     password = paren_prompt(prompt, positive, function=getpass) | ||||
|                     network.set_credentials(name, password) | ||||
|                 except ConnectionRefusedError: | ||||
|                     login_loop("// R E J E C T E D //.", False) | ||||
| 
 | ||||
|             login_loop("Enter your password", True) | ||||
|                     return "// R E J E C T E D //." | ||||
|             password = sane_value("Enter your password", validate_creds, function=getpass) | ||||
|             motherfucking_rainbows("~~welcome back {}~~".format(network.user_name)) | ||||
| 
 | ||||
|         except ValueError: | ||||
| @ -2467,7 +2479,7 @@ def log_in(relog=False): | ||||
|             ) | ||||
| 
 | ||||
|             if response == "c": | ||||
|                 name = nameloop("Pick a new name", True) | ||||
|                 name = nameloop(network, "Pick a new name") | ||||
| 
 | ||||
|             elif response == "n": | ||||
|                 raise InterruptedError | ||||
| @ -2510,11 +2522,16 @@ def bbjrc(mode, **params): | ||||
|     return values | ||||
| 
 | ||||
| 
 | ||||
| def mark(directive=True): | ||||
| def mark(key, value=None, default=None): | ||||
|     """ | ||||
|     Set and retrieve positional marks for threads. | ||||
|     Sets a value in the markfile and returns the old value (or default). | ||||
|     This uses a seperate file from the preferences | ||||
|     to keep it free from clutter. | ||||
| 
 | ||||
|     The key must be a string, and the value must be | ||||
|     json-encodable. If value isn't provided (or is None) | ||||
|     then this doesn't set anything and it is only a | ||||
|     read operation. | ||||
|     """ | ||||
|     try: | ||||
|         with open(markpath, "r") as _in: | ||||
| @ -2522,19 +2539,14 @@ def mark(directive=True): | ||||
|     except FileNotFoundError: | ||||
|         values = {} | ||||
| 
 | ||||
|     if directive == True and app.mode == "thread": | ||||
|         pos = app.get_focus_post() | ||||
|         values[app.thread["thread_id"]] = pos | ||||
|     old = values.get(key, default) | ||||
| 
 | ||||
|     if value is not None and value != old: | ||||
|         values[key] = value | ||||
|         with open(markpath, "w") as _out: | ||||
|             json.dump(values, _out) | ||||
|         return pos | ||||
| 
 | ||||
|     elif isinstance(directive, str): | ||||
|         try: | ||||
|             return values[directive] | ||||
|         except KeyError: | ||||
|             return 0 | ||||
| 
 | ||||
|     return old | ||||
| 
 | ||||
| def load_client_pins(): | ||||
|     """ | ||||
| @ -2570,25 +2582,39 @@ def ignore(*_, **__): | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| def wipe_screen(*_): | ||||
|     """ | ||||
|     A crude hack to repaint the whole screen. I didnt immediately | ||||
|     see anything to acheive this in the MainLoop methods so this | ||||
|     will do, I suppose. | ||||
|     """ | ||||
|     app.loop.stop() | ||||
|     call("clear", shell=True) | ||||
|     app.loop.start() | ||||
| 
 | ||||
| # XxX_N0_4rgP4rs3_XxX ###yoloswag | ||||
| def get_arg(key, default=None, get_value=True): | ||||
|     try: | ||||
|         spec = argv.index("--" + key) | ||||
|         value = argv[spec + 1] if get_value else True | ||||
|     except ValueError: # --key not specified | ||||
|         value = default | ||||
|     except IndexError: # flag given but no value | ||||
|         exit("invalid format for --" + key) | ||||
|     return value | ||||
| 
 | ||||
| def main(): | ||||
|     if get_arg("help", False, False): | ||||
|         print(help_text) | ||||
|         exit() | ||||
| 
 | ||||
|     try: | ||||
|         network = BBJ(get_arg("host", "127.0.0.1"), | ||||
|                       get_arg("port", 7099), | ||||
|                       get_arg("https", False, False)) | ||||
|     except URLError as e: | ||||
|         # print the connection error in red | ||||
|         exit("\033[0;31m%s\033[0m" % repr(e)) | ||||
| 
 | ||||
|     global app | ||||
|     app = App() | ||||
|     call("clear", shell=True) | ||||
|     app = App(network) | ||||
|     call("clear") | ||||
|     motherfucking_rainbows(obnoxious_logo) | ||||
|     print(welcome) | ||||
|     try: | ||||
|         log_in() | ||||
|         name = get_arg("user") or os.getenv("BBJ_USER") | ||||
|         password = os.getenv("BBJ_PASSWORD", default="") | ||||
|         log_in(network, name, password) | ||||
|         app.index() | ||||
|         app.loop.run() | ||||
|     except (InterruptedError, KeyboardInterrupt): | ||||
|  | ||||
| @ -1 +0,0 @@ | ||||
| theme: jekyll-theme-midnight | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user