Send new captcha over websocket with js
このコミットが含まれているのは:
コミット
8589216bf1
|
@ -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),
|
||||
}
|
||||
|
|
読み込み中…
新しいイシューから参照