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