too many things and I suck at git

pull/4/head
Blake DeMarcy 2017-03-01 15:54:56 -06:00
parent 4bb3340d43
commit 1ddf492249
6 changed files with 163 additions and 119 deletions

147
apidoc
View File

@ -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
View File

@ -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))))

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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)