Send new captcha over websocket with js

このコミットが含まれているのは:
n9k 2022-02-25 23:44:54 +00:00
コミット fa7ab5d3ed
4個のファイルの変更99行の追加36行の削除

ファイルの表示

@ -296,13 +296,31 @@ const chat_form_captcha_digest = document.getElementById("chat-form_js__captcha-
const chat_form_captcha_image = document.getElementById("chat-form_js__captcha-image");
const chat_form_captcha_answer = document.getElementById("chat-form_js__captcha-answer");
chat_form_captcha_image.addEventListener("loadstart", (event) => {
chat_form_captcha_image.removeAttribute("title");
chat_form_captcha_image.removeAttribute("data-reloadable");
chat_form_captcha_image.alt = "Loading...";
});
chat_form_captcha_image.addEventListener("load", (event) => {
chat_form_captcha_image.removeAttribute("alt");
chat_form_captcha_image.dataset.reloadable = "";
chat_form_captcha_image.title = "Click for a new captcha";
});
chat_form_captcha_image.addEventListener("error", (event) => {
chat_form_captcha_image.alt = "Captcha failed to load";
chat_form_captcha_image.dataset.reloadable = "";
chat_form_captcha_image.title = "Click for a new captcha";
});
chat_form_captcha_image.addEventListener("click", (event) => {
if (chat_form_captcha_image.dataset.reloadable === undefined) {
return;
}
chat_form_submit.disabled = true;
chat_form_captcha_image.alt = "Waiting...";
chat_form_captcha_image.removeAttribute("title");
chat_form_captcha_image.removeAttribute("data-reloadable");
chat_form_captcha_image.removeAttribute("src");
const payload = {type: "captcha"};
ws.send(JSON.stringify(payload));
});
const enable_captcha = (digest) => {
chat_form_captcha_digest.value = digest;
@ -313,6 +331,7 @@ const enable_captcha = (digest) => {
chat_form_comment.required = false;
chat_form_captcha_image.removeAttribute("src");
chat_form_captcha_image.src = `/captcha.jpg?token=${encodeURIComponent(token)}&digest=${encodeURIComponent(digest)}`;
chat_form_submit.disabled = false;
chat_form.dataset.captcha = "";
}
const disable_captcha = () => {
@ -323,6 +342,7 @@ const disable_captcha = () => {
chat_form_captcha_digest.value = "";
chat_form_captcha_answer.value = "";
chat_form_captcha_answer.required = false;
chat_form_submit.disabled = false;
chat_form_captcha_image.removeAttribute("alt");
chat_form_captcha_image.removeAttribute("src");
}
@ -373,6 +393,7 @@ const on_websocket_message = (event) => {
switch (receipt.type) {
case "error":
console.log("ws error", receipt);
chat_form_submit.disabled = false;
break;
case "init":
@ -394,6 +415,9 @@ const on_websocket_message = (event) => {
// chat form captcha digest
receipt.digest === null ? disable_captcha() : enable_captcha(receipt.digest);
// chat form submit button
chat_form_submit.disabled = false;
// remove messages the server isn't acknowledging the existance of
const seqs = new Set(receipt.messages.map((message) => {return message.seq;}));
const to_delete = [];
@ -486,6 +510,11 @@ const on_websocket_message = (event) => {
update_user_tripcodes();
break;
case "captcha":
console.log("ws captcha", receipt);
receipt.digest === null ? disable_captcha() : enable_captcha(receipt.digest);
break;
default:
console.log("incomprehensible websocket message", receipt);
}
@ -547,7 +576,8 @@ const chat_form_comment = document.getElementById("chat-form_js__comment");
const chat_form_submit = document.getElementById("chat-form_js__submit");
chat_form.addEventListener("submit", (event) => {
event.preventDefault();
const payload = Object.fromEntries(new FormData(chat_form));
const form = Object.fromEntries(new FormData(chat_form));
const payload = {type: "message", form: form};
chat_form_submit.disabled = true;
ws.send(JSON.stringify(payload));
});

ファイルの表示

@ -187,6 +187,9 @@ noscript {
color: inherit;
font-size: 8pt;
}
#chat-form_js__captcha-image[data-reloadable] {
cursor: pointer;
}
#chat-form_js__captcha-answer {
width: 8ch;
}

ファイルの表示

@ -1,19 +1,31 @@
class Malformed(Exception):
pass
def get(t, pairs, key, default=None):
value = pairs.get(key, default)
if isinstance(value, t):
return value
else:
raise Malformed(f'malformed {key}')
def parse_websocket_data(receipt):
if not isinstance(receipt, dict):
raise Malformed('not a json object')
comment = receipt.get('comment')
if not isinstance(comment, str):
raise Malformed('malformed comment')
match receipt.get('type'):
case 'message':
form = get(dict, receipt, 'form')
nonce = get(str, form, 'nonce')
comment = get(str, form, 'comment')
digest = get(str, form, 'captcha-digest', '')
answer = get(str, form, 'captcha-answer', '')
return nonce, comment, digest, answer
nonce = receipt.get('nonce')
if not isinstance(nonce, str):
raise Malformed('malformed nonce')
case 'appearance':
raise NotImplemented
digest = receipt.get('captcha-digest', '')
answer = receipt.get('captcha-answer', '')
case 'captcha':
return None
return nonce, comment, digest, answer
case _:
raise Malformed('malformed type')

ファイルの表示

@ -44,7 +44,7 @@ async def websocket_inbound(queue, user):
finally:
see(user)
try:
nonce, comment, digest, answer = parse_websocket_data(receipt)
parsed = parse_websocket_data(receipt)
except Malformed as e:
error , *_ = e.args
payload = {
@ -52,29 +52,47 @@ async def websocket_inbound(queue, user):
'because': error,
}
else:
try:
verification_happened = verify(user, digest, answer)
except BadCaptcha as e:
notice, *_ = e.args
else:
try:
message_was_added = add_chat_message(
user,
nonce,
comment,
ignore_empty=verification_happened,
)
except Rejected as e:
notice, *_ = e.args
else:
deverify(user)
notice = None
payload = {
'type': 'ack',
'nonce': nonce,
'next': generate_nonce(),
'notice': notice,
'clear': message_was_added,
'digest': get_random_captcha_digest_for(user),
}
match parsed:
case [nonce, comment, digest, answer]:
payload = handle_inbound_message(user, *parsed)
case None:
payload = handle_inbound_captcha(user)
queue.put_nowait(payload)
def handle_inbound_captcha(user):
return {
'type': 'captcha',
'digest': get_random_captcha_digest_for(user),
}
def handle_inbound_message(user, nonce, comment, digest, answer):
try:
verification_happened = verify(user, digest, answer)
except BadCaptcha as e:
notice, *_ = e.args
message_was_added = False
else:
try:
message_was_added = add_chat_message(
user,
nonce,
comment,
ignore_empty=verification_happened,
)
except Rejected as e:
notice, *_ = e.args
message_was_added = False
else:
notice = None
if message_was_added:
deverify(user)
return {
'type': 'ack',
'nonce': nonce,
'next': generate_nonce(),
'notice': notice,
'clear': message_was_added,
'digest': get_random_captcha_digest_for(user),
}