too many things and I suck at git
parent
4bb3340d43
commit
1ddf492249
147
apidoc
147
apidoc
|
@ -4,7 +4,7 @@ Text Entities
|
|||
The `entities` attribute is an array of objects that represent blocks
|
||||
of text within a post that have special properties. Clients may safely
|
||||
ignore these things without losing too much meaning, but in a rich
|
||||
application like an Emacs or GUI implementation, they can provide
|
||||
implementation like an Emacs or GUI, they can provide
|
||||
some highlighting and navigation perks. The array object may be
|
||||
empty. If its not, its populated with arrays representing the
|
||||
modifications to be made.
|
||||
|
@ -19,31 +19,35 @@ property in the body string. The way clients are to access these
|
|||
indices is beyond the scope of this document; accessing a subsequence
|
||||
varies a lot between programming languages.
|
||||
|
||||
Some objects will provide further arguments beyond those 3.
|
||||
|
||||
|--------------|----------------------------------------------------------|
|
||||
| Name | Description |
|
||||
|--------------|----------------------------------------------------------|
|
||||
| `quote` | This is a string that refers to a previous post number. |
|
||||
| | These are formatted like >>5, which means it is a |
|
||||
| | reference to `post_id` 5. These are not processed in |
|
||||
| | thread OPs. >>0 may be used to refer to the OP. |
|
||||
|--------------|----------------------------------------------------------|
|
||||
| `blockquote` | This is a block of text, denoted by a newline during |
|
||||
| | composure, representing text that is assumed to be |
|
||||
| | a quote of someone else. |
|
||||
|--------------|----------------------------------------------------------|
|
||||
| `color` | This is a block of text, denoted by [[color: body]] |
|
||||
| | during composure. The body may span across newlines. |
|
||||
| | A fourth item is provided in the array: it is one of the |
|
||||
| | following strings representing the color. |
|
||||
| | `red`, `green`, `yellow`, `blue`, `magenta`, or `cyan`. |
|
||||
|--------------|----------------------------------------------------------|
|
||||
| `bold` | Like color, except that no additional attribute is |
|
||||
| | provided. it is denoted as [[bold: body]] during |
|
||||
| | composure. |
|
||||
|--------------|----------------------------------------------------------|
|
||||
Some objects will provide further arguments beyond those 3. They will
|
||||
always be at the end of the array.
|
||||
|
||||
|-------------|----------------------------------------------------------|
|
||||
| Name | Description |
|
||||
|-------------|----------------------------------------------------------|
|
||||
| `quote` | This is a string that refers to a previous post number. |
|
||||
| | These are formatted like >>5, which means it is a |
|
||||
| | reference to `post_id` 5. These are not processed in |
|
||||
| | thread OPs. >>0 may be used to refer to the OP. In |
|
||||
| | addition to the indices at i[1] and i[2], a fourth value |
|
||||
| | is provided, which is an integer of the `post_id` being |
|
||||
| | quoted. Note that the string indices include the >>'s. |
|
||||
|-------------|----------------------------------------------------------|
|
||||
| `linequote` | This is a line of text, denoted by a newline during |
|
||||
| | composure, representing text that is assumed to be |
|
||||
| | a quote of someone else. The indices span from the > |
|
||||
| | until (not including) the newline. |
|
||||
|-------------|----------------------------------------------------------|
|
||||
| `color` | This is a block of text, denoted by [[color: body]] |
|
||||
| | during composure. The body may span across newlines. |
|
||||
| | A fourth item is provided in the array: it is one of the |
|
||||
| | following strings representing the color. |
|
||||
| | `red`, `green`, `yellow`, `blue`, `magenta`, or `cyan`. |
|
||||
|-------------|----------------------------------------------------------|
|
||||
| `bold` | Like color, except that no additional attribute is |
|
||||
| `italic` | provided. it is denoted as [[directive: body]] during |
|
||||
| `underline` | composure. |
|
||||
|-------------|----------------------------------------------------------|
|
||||
|
||||
|
||||
Threads & Replies
|
||||
|
@ -57,13 +61,13 @@ available on the parent object:
|
|||
|---------------|------------------------------------------------------|
|
||||
| Name | Description |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `author` | The ID of the author |
|
||||
| `author` | The ID string of the author. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `thread_id` | The ID of the thread. |
|
||||
| `thread_id` | The ID string of the thread. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `title` | The title string of the thread |
|
||||
| `title` | The title string of the thread. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `body` | The body of the post's text. |
|
||||
| `body` | The body string of the post's text. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `entities` | A (possibly empty) array of entity objects for |
|
||||
| | the post `body`. |
|
||||
|
@ -74,7 +78,7 @@ available on the parent object:
|
|||
|---------------|------------------------------------------------------|
|
||||
| `replies` | An array containing full reply objects in |
|
||||
| | the order they were posted. Your clients |
|
||||
| | do not need to sort these. |
|
||||
| | do not need to sort these. Array can be empty. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `reply_count` | An integer representing the number of replies |
|
||||
| | that have been posted in this thread. |
|
||||
|
@ -82,8 +86,7 @@ available on the parent object:
|
|||
| `lastmod` | Unix timestamp of when the thread was last |
|
||||
| | posted in, or a message was edited. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `edited` | Boolean of whether this post has been edited since |
|
||||
| | it was made. |
|
||||
| `edited` | Boolean of whether the post has been edited. |
|
||||
|---------------|------------------------------------------------------|
|
||||
| `created` | Unix timestamp of when the post was originally made. |
|
||||
|---------------|------------------------------------------------------|
|
||||
|
@ -97,11 +100,11 @@ The following attributes are available on each reply object in `replies`:
|
|||
| `post_id` | An integer of the posts ID; unlike thread and user ids, |
|
||||
| | this is not a uuid but instead is incremental, starting |
|
||||
| | from 1 as the first reply and going up by one for each |
|
||||
| | post. |
|
||||
| | post. These may be referenced by `quote` entities. |
|
||||
|------------|---------------------------------------------------------|
|
||||
| `author` | Author ID |
|
||||
| `author` | Author ID string |
|
||||
|------------|---------------------------------------------------------|
|
||||
| `body` | The body the reply's text. |
|
||||
| `body` | The body string the reply's text. |
|
||||
|------------|---------------------------------------------------------|
|
||||
| `entities` | A (possibly empty) array of entity objects for |
|
||||
| | the reply `body`. |
|
||||
|
@ -126,37 +129,43 @@ the problem. `description` is intended for human consumption; in your client
|
|||
code, use the error codes to handle conditions. The `presentable` column
|
||||
indicates whether the `description` should be shown to users verbatim.
|
||||
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| Code | Presentable | Documentation |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 0 | Never, fix | Malformed json input. `description` is the error |
|
||||
| | your client | string thrown by the server-side json decoder. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 1 | Not a good | Internal server error. Unaltered exception text |
|
||||
| | idea, the | is returned as `description`. This shouldn't |
|
||||
| | exceptions | happen, and if it does, make a bug report. |
|
||||
| | are not | clients should not attempt to intelligently |
|
||||
| | helpful | recover from any errors of this class. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 2 | Nadda. | Unknown `method` was requested. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 3 | Fix. Your. | Missing or malformed parameter values for the |
|
||||
| | Client. | requested `method`. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 4 | Only during | Invalid or unprovided `user`. During |
|
||||
| | registration | registration, this code is returned if the name |
|
||||
| | | is already occupied or contains illegal chars. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 5 | Always | `user` is not registered. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 6 | Always | User `auth_hash` failed or was not provided. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 7 | Always | Requested thread does not exist. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 8 | Always | Requested thread does not allow posts. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 9 | Always | Message edit failed; there is a 24hr limit for |
|
||||
| | | editing posts. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
| 10 | Always | User action requires `admin` privilege. |
|
||||
|------|--------------|--------------------------------------------------|
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| Code | Presentable | Documentation |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 0 | Never, fix | Malformed json input. `description` is the error |
|
||||
| | your client | string thrown by the server-side json decoder. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 1 | Not a good | Internal server error. Unaltered exception text |
|
||||
| | idea, the | is returned as `description`. This shouldn't |
|
||||
| | exceptions | happen, and if it does, make a bug report. |
|
||||
| | are not | clients should not attempt to intelligently |
|
||||
| | helpful | recover from any errors of this class. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 2 | Nadda. | Unknown `method` was requested. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 3 | Fix. Your. | Missing or malformed parameter values for the |
|
||||
| | Client. | requested `method`. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 4 | Only during | Invalid or unprovided `user`. |
|
||||
| | registration | |
|
||||
| | | During registration, this code is returned with a |
|
||||
| | | `description` that should be shown to the user. |
|
||||
| | | It could indicate an invalid name input, an |
|
||||
| | | occupied username, invalid/missing `auth_hash`, |
|
||||
| | | etc. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 5 | Always | `user` is not registered. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 6 | Always | User `auth_hash` failed or was not provided. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 7 | Always | Requested thread does not exist. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 8 | Always | Requested thread does not allow posts. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 9 | Always | Message edit failed; there is a 24hr limit for |
|
||||
| | | editing posts. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 10 | Always | User action requires `admin` privilege. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
| 11 | Always | Invalid formatting directives in text submission. |
|
||||
|------|--------------|---------------------------------------------------|
|
||||
|
|
77
bbj.el
77
bbj.el
|
@ -6,32 +6,79 @@
|
|||
(defvar bbj:user nil)
|
||||
(defvar bbj:hash nil)
|
||||
|
||||
(make-variable-buffer-local
|
||||
(defvar bbj:aux-callback #'ignore))
|
||||
|
||||
(define-derived-mode bbj-mode fundamental-mode "[BBJ]"
|
||||
"Mode for browsing and posting to BBJ."
|
||||
:group 'bbj-mode
|
||||
(local-set-key (kbd "C-c C-c") 'bbj:aux)
|
||||
(local-set-key (kbd "+") 'bbj:compose)
|
||||
(local-set-key (kbd "RET") 'bbj:enter))
|
||||
|
||||
(defun bbj:request (&rest cells)
|
||||
(push (cons 'user bbj:user) cells)
|
||||
(push (cons 'auth_hash bbj:hash) cells)
|
||||
(with-temp-buffer
|
||||
(insert (json-encode cells))
|
||||
(shell-command-on-region
|
||||
(point-min) (point-max)
|
||||
(format "nc %s %s" bbj:host bbj:port)))
|
||||
(with-current-buffer "*Shell Command Output*"
|
||||
(json-read-from-string
|
||||
(buffer-substring-no-properties
|
||||
(point-min) (point-max)))))
|
||||
(goto-char (point-min))
|
||||
(let (json-false json-null)
|
||||
(json-read))))
|
||||
|
||||
|
||||
(bbj:request '(user . "desvox")
|
||||
'(auth_hash . "nrr")
|
||||
'(method . "check_auth"))
|
||||
(defun bbj:sethash (&optional password)
|
||||
(unless password (setq password
|
||||
(read-from-minibuffer "(Password)> ")))
|
||||
(setq bbj:hash (secure-hash 'sha256 password)))
|
||||
|
||||
|
||||
(defun bbj:login ()
|
||||
(interactive)
|
||||
(setq bbj:user (read-from-minibuffer "(BBJ Username)> "))
|
||||
(if (bbj:request '(method . "is_registered")
|
||||
`(target_user . ,bbj:user))
|
||||
(setq bbj:hash (secure-hash 'sha256 (read-from-minibuffer "(BBJ Password)> ")))
|
||||
(when (y-or-n-p (format "Register for BBJ as %s? " bbj:user))
|
||||
(message
|
||||
(bbj:request (cons 'auth_hash bbj:hash)
|
||||
(cons 'user bbj:user)
|
||||
(cons 'avatar nil)
|
||||
(cons 'bio (read-from-minibuffer "(Enter a short bio about youself!)> ")))))))
|
||||
(cond
|
||||
((bbj:request '(method . "is_registered")
|
||||
`(target_user . ,bbj:user))
|
||||
(bbj:sethash)
|
||||
(unless (bbj:request '(method . "check_auth"))
|
||||
(message "(Invalid Password!)")))
|
||||
((y-or-n-p (format "Register for BBJ as %s? " bbj:user))
|
||||
(let ((response (bbj:request
|
||||
(cons 'method "user_register")
|
||||
(cons 'auth_hash (bbj:sethash))
|
||||
(cons 'user bbj:user)
|
||||
(cons 'quip (read-from-minibuffer "(Quip)> "))
|
||||
(cons 'bio (read-from-minibuffer "(Bio)> ")))))
|
||||
(if (alist-get 'error response)
|
||||
(message "%s" (alist-get 'error response))
|
||||
(setq bbj:logged-in t)
|
||||
(message "Logged in as %s!" bbj:user))))))
|
||||
|
||||
|
||||
(defun bbj:compose-in-window (title callback &rest cbargs)
|
||||
(let ((buffer (get-buffer-create "*BBJ: Compose*")))
|
||||
(pop-to-buffer buffer)
|
||||
(with-current-buffer buffer
|
||||
(erase-buffer)
|
||||
(bbj-mode)
|
||||
(setq header-line-format title
|
||||
bbj:aux-callback callback))))
|
||||
|
||||
|
||||
(defun bbj:consume-window (buffer)
|
||||
(interactive)
|
||||
(with-current-buffer buffer
|
||||
(let ((content (buffer-substring-no-properties
|
||||
(point-min) (point-max))))
|
||||
(quit-window t)
|
||||
content)))
|
||||
|
||||
|
||||
(defun bbj:browse-index ()
|
||||
(interactive)
|
||||
(let ((response (bbj:request '(method . "thread_index"))))
|
||||
(cl-loop for thread across (alist-get 'threads response) do
|
||||
(message "%s" thread))))
|
||||
|
|
|
@ -30,7 +30,7 @@ def thread_index(key="lastmod"):
|
|||
thread = thread_load(ID)
|
||||
thread.pop("replies")
|
||||
result.append(thread)
|
||||
return sorted(result, key=lambda i: i[key])
|
||||
return sorted(result, key=lambda i: i[key], reverse=True)
|
||||
|
||||
|
||||
def thread_create(author, body, title, tags):
|
||||
|
|
|
@ -31,11 +31,11 @@ def create_usermap(thread, index=False):
|
|||
|
||||
|
||||
def is_registered(json):
|
||||
return dumps(bool(db.USERDB["mapname"].get(json["target_user"])))
|
||||
return bool(db.USERDB["mapname"].get(json["target_user"]))
|
||||
|
||||
|
||||
def check_auth(json):
|
||||
return dumps(bool(db.user_auth(json["user"], json["auth_hash"])))
|
||||
return bool(db.user_auth(json["user"], json["auth_hash"]))
|
||||
|
||||
|
||||
def user_register(json):
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from src import formatting
|
||||
from time import time
|
||||
|
||||
def base():
|
||||
|
@ -48,12 +49,13 @@ def user_external(ID, name, quip, bio, admin):
|
|||
|
||||
|
||||
def thread(ID, author, body, title, tags):
|
||||
text, entities = formatting.parse(body, doquotes=False)
|
||||
now = time()
|
||||
return {
|
||||
"thread_id": ID,
|
||||
"author": author,
|
||||
"body": body,
|
||||
"entities": list(),
|
||||
"body": text,
|
||||
"entities": entities, # of type list()
|
||||
"title": title,
|
||||
"tags": tags,
|
||||
"replies": list(),
|
||||
|
@ -65,12 +67,13 @@ def thread(ID, author, body, title, tags):
|
|||
|
||||
|
||||
def reply(ID, author, body):
|
||||
text, entities = formatting.parse(body)
|
||||
now = time()
|
||||
return {
|
||||
"post_id": ID,
|
||||
"author": author,
|
||||
"body": body,
|
||||
"entities": list(),
|
||||
"body": text,
|
||||
"entities": entities, # of type list()
|
||||
"lastmod": now,
|
||||
"edited": False,
|
||||
"created": now
|
||||
|
|
|
@ -8,7 +8,7 @@ import json
|
|||
class RequestHandler(StreamRequestHandler):
|
||||
"""
|
||||
Receieves and processes json input; dispatches input to the
|
||||
approproate endpoint, or responds with error objects.
|
||||
requested endpoint, or responds with error objects.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -22,52 +22,37 @@ class RequestHandler(StreamRequestHandler):
|
|||
endpoint = request.get("method")
|
||||
|
||||
if endpoint not in endpoints.endpoints:
|
||||
raise IndexError("Invalid endpoint")
|
||||
return self.reply(schema.error(2, "Invalid endpoint"))
|
||||
|
||||
# check to make sure all the arguments for endpoint are provided
|
||||
elif any([key not in request for key in endpoints.endpoints[endpoint]]):
|
||||
raise ValueError("{} requires: {}".format(
|
||||
endpoint, ", ".join(endpoints.endpoints[endpoint])))
|
||||
return self.reply(schema.error(3, "{} requires: {}".format(
|
||||
endpoint, ", ".join(endpoints.endpoints[endpoint]))))
|
||||
|
||||
elif endpoint not in endpoints.authless:
|
||||
if not request.get("user"):
|
||||
raise ConnectionError("No username provided.")
|
||||
return self.reply(schema.error(4, "No username provided."))
|
||||
|
||||
user = db.user_resolve(request["user"])
|
||||
request["user"] = user
|
||||
|
||||
if not user:
|
||||
raise ConnectionAbortedError("User not registered")
|
||||
return self.reply(schema.error(5, "User not registered"))
|
||||
|
||||
elif endpoint != "check_auth" and not db.user_auth(user, request.get("auth_hash")):
|
||||
raise ConnectionRefusedError("Authorization failed.")
|
||||
elif endpoint != "check_auth" and not \
|
||||
db.user_auth(user, request.get("auth_hash")):
|
||||
return self.reply(schema.error(6, "Authorization failed."))
|
||||
|
||||
# exception handling is now passed to the endpoints;
|
||||
# anything unhandled beyond here is a code 1
|
||||
self.reply(eval("endpoints." + endpoint)(request))
|
||||
|
||||
except json.decoder.JSONDecodeError as E:
|
||||
return self.reply(schema.error(0, str(E)))
|
||||
|
||||
except IndexError as E:
|
||||
return self.reply(schema.error(2, str(E)))
|
||||
|
||||
except ValueError as E:
|
||||
return self.reply(schema.error(3, str(E)))
|
||||
|
||||
except ConnectionError as E:
|
||||
return self.reply(schema.error(4, str(E)))
|
||||
|
||||
except ConnectionAbortedError as E:
|
||||
return self.reply(schema.error(5, str(E)))
|
||||
|
||||
except ConnectionRefusedError as E:
|
||||
return self.reply(schema.error(6, str(E)))
|
||||
|
||||
except Exception as E:
|
||||
return self.reply(schema.error(1, str(E)))
|
||||
|
||||
try:
|
||||
self.reply(eval("endpoints." + endpoint)(request))
|
||||
except Exception as E:
|
||||
self.reply(schema.error(1, str(E)))
|
||||
|
||||
|
||||
def run(host, port):
|
||||
server = TCPServer((host, port), RequestHandler)
|
||||
|
|
Loading…
Reference in New Issue