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

ファイルの表示

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

ファイルの表示

@ -1,19 +1,31 @@
class Malformed(Exception): class Malformed(Exception):
pass 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): def parse_websocket_data(receipt):
if not isinstance(receipt, dict): if not isinstance(receipt, dict):
raise Malformed('not a json object') raise Malformed('not a json object')
comment = receipt.get('comment') match receipt.get('type'):
if not isinstance(comment, str): case 'message':
raise Malformed('malformed comment') 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') case 'appearance':
if not isinstance(nonce, str): raise NotImplemented
raise Malformed('malformed nonce')
digest = receipt.get('captcha-digest', '') case 'captcha':
answer = receipt.get('captcha-answer', '') return None
return nonce, comment, digest, answer case _:
raise Malformed('malformed type')

ファイルの表示

@ -44,7 +44,7 @@ async def websocket_inbound(queue, user):
finally: finally:
see(user) see(user)
try: try:
nonce, comment, digest, answer = parse_websocket_data(receipt) parsed = parse_websocket_data(receipt)
except Malformed as e: except Malformed as e:
error , *_ = e.args error , *_ = e.args
payload = { payload = {
@ -52,29 +52,47 @@ async def websocket_inbound(queue, user):
'because': error, 'because': error,
} }
else: else:
try: match parsed:
verification_happened = verify(user, digest, answer) case [nonce, comment, digest, answer]:
except BadCaptcha as e: payload = handle_inbound_message(user, *parsed)
notice, *_ = e.args
else: case None:
try: payload = handle_inbound_captcha(user)
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),
}
queue.put_nowait(payload) 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),
}