コミットを比較

...

6 コミット

作成者 SHA1 メッセージ 日付
n9k 55a3d7fe1f v1.6.6 2022-08-02 04:57:30 +00:00
n9k f3de542e3b Merge branch 'dev' 2022-08-02 04:57:19 +00:00
n9k f3d613de3b Control socket: add chat messages 2022-08-02 04:57:07 +00:00
n9k 6ddab6c969 Minor: control socket: move around ArgsUser stuff 2022-08-02 04:56:25 +00:00
n9k 4a22ca8a92 Minor: `add_chat_message` returns seq (or None)
It now returns the seq of the just-added message if one was added, and
None otherwise.  The previous behaviour was to return True and False
respectively.
2022-08-02 04:52:11 +00:00
n9k e0f3ec0e07 Control socket: add new users 2022-08-02 04:52:07 +00:00
8個のファイルの変更91行の追加40行の削除

ファイルの表示

@ -13,7 +13,7 @@ from anonstream.quart import Quart
from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer
from anonstream.utils.user import generate_blank_allowedness from anonstream.utils.user import generate_blank_allowedness
__version__ = '1.6.5' __version__ = '1.6.6'
def create_app(toml_config): def create_app(toml_config):
app = Quart('anonstream', static_folder=None) app = Quart('anonstream', static_folder=None)

ファイルの表示

@ -31,9 +31,9 @@ def get_all_messages_for_websocket():
)) ))
def add_chat_message(user, nonce, comment, ignore_empty=False): def add_chat_message(user, nonce, comment, ignore_empty=False):
# Special case: if the comment is empty, do nothing and return # Special case: if the comment is empty, do nothing and return None
if ignore_empty and len(comment) == 0: if ignore_empty and len(comment) == 0:
return False return None
timestamp_ms = time.time_ns() // 1_000_000 timestamp_ms = time.time_ns() // 1_000_000
timestamp = timestamp_ms // 1000 timestamp = timestamp_ms // 1000
@ -137,7 +137,7 @@ def add_chat_message(user, nonce, comment, ignore_empty=False):
}, },
) )
return True return seq
def delete_chat_messages(seqs): def delete_chat_messages(seqs):
seq_set = set(seqs) seq_set = set(seqs)

ファイルの表示

@ -3,9 +3,14 @@
import json import json
from anonstream.control.spec import Spec, NoParse, Ambiguous, Parsed from quart import current_app
from anonstream.control.spec import Spec, NoParse, Ambiguous, BadArgument, Parsed
from anonstream.control.spec.utils import get_item, startswith from anonstream.control.spec.utils import get_item, startswith
USERS_BY_TOKEN = current_app.users_by_token
USERS = current_app.users
class Str(Spec): class Str(Spec):
AS_ARG = False AS_ARG = False
@ -146,3 +151,26 @@ class ArgsJsonStringArray(ArgsJson):
f'bad argument at position {index} {obj_json!r}: ' f'bad argument at position {index} {obj_json!r}: '
f'could not decode json array of strings' f'could not decode json array of strings'
) )
class ArgsJsonTokenUser(ArgsJsonString):
def transform_obj(self, token):
try:
user = USERS_BY_TOKEN[token]
except KeyError:
raise BadArgument(f'no user with token {token!r}')
return user
class ArgsJsonHashUser(ArgsString):
def transform_string(self, token_hash):
for user in USERS:
if user['token_hash'] == token_hash:
break
else:
raise BadArgument(f'no user with token_hash {token_hash!r}')
return user
def ArgsUser(spec):
return Str({
'token': ArgsJsonTokenUser(spec),
'hash': ArgsJsonHashUser(spec),
})

ファイルの表示

@ -4,9 +4,11 @@
import itertools import itertools
from anonstream.chat import delete_chat_messages from anonstream.chat import delete_chat_messages
from anonstream.control.exceptions import CommandFailed
from anonstream.control.spec import NoParse from anonstream.control.spec import NoParse
from anonstream.control.spec.common import Str, End, Args from anonstream.control.spec.common import Str, End, Args, ArgsJsonString, ArgsUser
from anonstream.control.spec.utils import get_item, json_dumps_contiguous from anonstream.control.spec.utils import get_item, json_dumps_contiguous
from anonstream.chat import add_chat_message, Rejected
class ArgsSeqs(Args): class ArgsSeqs(Args):
def consume(self, words, index): def consume(self, words, index):
@ -33,12 +35,18 @@ async def cmd_chat_help():
response = ( response = (
'Usage: chat delete SEQS\n' 'Usage: chat delete SEQS\n'
'Commands:\n' 'Commands:\n'
#' chat show [MESSAGES]......show chat messages\n' #' chat show [MESSAGES]...........show chat messages\n'
' chat delete SEQS..........delete chat messages\n' ' chat delete SEQS...............delete chat messages\n'
' chat add USER NONCE COMMENT....add chat message\n'
'Definitions:\n' 'Definitions:\n'
#' MESSAGES..................undefined\n' #' MESSAGES...................undefined\n'
' SEQS......................=SEQ [SEQ...]\n' ' SEQS.......................=SEQ [SEQ...]\n'
' SEQ.......................a chat message\'s seq, base-10 integer\n' ' SEQ........................a chat message\'s seq, base-10 integer\n'
' USER.......................={token TOKEN | hash HASH}\n'
' TOKEN......................a user\'s token, json string\n'
' HASH.......................a user\'s token hash\n'
' NONCE......................a chat message\'s nonce, json string\n'
' COMMENT....................json string\n'
) )
return normal, response return normal, response
@ -48,8 +56,24 @@ async def cmd_chat_delete(*seqs):
response = '' response = ''
return normal, response return normal, response
async def cmd_chat_add(user, nonce, comment):
try:
seq = add_chat_message(user, nonce, comment)
except Rejected as e:
raise CommandFailed(f'rejected: {e}') from e
else:
assert seq is not None
normal = [
'chat', 'add',
'token', json_dumps_contiguous(user['token']),
json_dumps_contiguous(nonce), json_dumps_contiguous(comment),
]
response = str(seq) + '\n'
return normal, response
SPEC = Str({ SPEC = Str({
None: End(cmd_chat_help), None: End(cmd_chat_help),
'help': End(cmd_chat_help), 'help': End(cmd_chat_help),
'delete': ArgsSeqs(End(cmd_chat_delete)), 'delete': ArgsSeqs(End(cmd_chat_delete)),
'add': ArgsUser(ArgsJsonString(ArgsJsonString(End(cmd_chat_add)))),
}) })

ファイルの表示

@ -18,8 +18,10 @@ async def cmd_help():
' user set USER ATTR VALUE.......set an attribute of a user\n' ' user set USER ATTR VALUE.......set an attribute of a user\n'
' user eyes USER [show]..........show a list of active video responses\n' ' user eyes USER [show]..........show a list of active video responses\n'
' user eyes USER delete EYES_ID..end a video response\n' ' user eyes USER delete EYES_ID..end a video response\n'
' user add VERIFIED TOKEN........add new user\n'
#' chat show MESSAGES.............show a list of messages\n' #' chat show MESSAGES.............show a list of messages\n'
' chat delete SEQS...............delete a set of messages\n' ' chat delete SEQS...............delete a set of messages\n'
' chat add USER NONCE COMMENT....add chat message\n'
' allowedness [show].............show the current allowedness\n' ' allowedness [show].............show the current allowedness\n'
' allowedness setdefault BOOLEAN.set the default allowedness\n' ' allowedness setdefault BOOLEAN.set the default allowedness\n'
' allowedness add SET STRING.....add to the blacklist/whitelist\n' ' allowedness add SET STRING.....add to the blacklist/whitelist\n'

ファイルの表示

@ -6,38 +6,15 @@ import json
from quart import current_app from quart import current_app
from anonstream.control.exceptions import CommandFailed from anonstream.control.exceptions import CommandFailed
from anonstream.control.spec import BadArgument from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonBoolean, ArgsJsonString, ArgsUser
from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonString from anonstream.control.spec.utils import json_dumps_contiguous
from anonstream.control.spec.utils import get_item, json_dumps_contiguous
from anonstream.utils.user import USER_WEBSOCKET_ATTRS from anonstream.utils.user import USER_WEBSOCKET_ATTRS
from anonstream.routes.wrappers import generate_and_add_user
from anonstream.wrappers import get_timestamp
USERS_BY_TOKEN = current_app.users_by_token USERS_BY_TOKEN = current_app.users_by_token
USERS = current_app.users
USERS_UPDATE_BUFFER = current_app.users_update_buffer USERS_UPDATE_BUFFER = current_app.users_update_buffer
class ArgsJsonTokenUser(ArgsJsonString):
def transform_obj(self, token):
try:
user = USERS_BY_TOKEN[token]
except KeyError:
raise BadArgument(f'no user with token {token!r}')
return user
class ArgsJsonHashUser(ArgsString):
def transform_string(self, token_hash):
for user in USERS:
if user['token_hash'] == token_hash:
break
else:
raise BadArgument(f'no user with token_hash {token_hash!r}')
return user
def ArgsUser(spec):
return Str({
'token': ArgsJsonTokenUser(spec),
'hash': ArgsJsonHashUser(spec),
})
async def cmd_user_help(): async def cmd_user_help():
normal = ['user', 'help'] normal = ['user', 'help']
response = ( response = (
@ -49,6 +26,7 @@ async def cmd_user_help():
' user set USER ATTR VALUE......set an attribute of a user\n' ' user set USER ATTR VALUE......set an attribute of a user\n'
' user eyes USER [show].........show a user\'s active video responses\n' ' user eyes USER [show].........show a user\'s active video responses\n'
' user eyes USER delete EYES_ID.end a video response to a user\n' ' user eyes USER delete EYES_ID.end a video response to a user\n'
' user add VERIFIED TOKEN.......add new user\n'
'Definitions:\n' 'Definitions:\n'
' USER..........................={token TOKEN | hash HASH}\n' ' USER..........................={token TOKEN | hash HASH}\n'
' TOKEN.........................a token, json string\n' ' TOKEN.........................a token, json string\n'
@ -56,6 +34,7 @@ async def cmd_user_help():
' ATTR..........................a user attribute, re:[a-z0-9_]+\n' ' ATTR..........................a user attribute, re:[a-z0-9_]+\n'
' VALUE.........................json value\n' ' VALUE.........................json value\n'
' EYES_ID.......................a user\'s eyes_id, base 10 integer\n' ' EYES_ID.......................a user\'s eyes_id, base 10 integer\n'
' VERIFIED......................user\'s verified state: true = normal, false = can\'t chat, null = will be kicked to access captcha\n'
) )
return normal, response return normal, response
@ -132,6 +111,21 @@ async def cmd_user_eyes_delete(user, eyes_id):
response = '' response = ''
return normal, response return normal, response
async def cmd_user_add(verified, token):
if token in USERS_BY_TOKEN:
raise CommandFailed(f'user with token {token!r} already exists')
_user = generate_and_add_user(
timestamp=get_timestamp(),
token=token,
verified=verified,
)
normal = [
'user', 'add',
json_dumps_contiguous(verified), json_dumps_contiguous(token),
]
response = ''
return normal, response
SPEC = Str({ SPEC = Str({
None: End(cmd_user_show), None: End(cmd_user_show),
'help': End(cmd_user_help), 'help': End(cmd_user_help),
@ -144,4 +138,5 @@ SPEC = Str({
'show': End(cmd_user_eyes_show), 'show': End(cmd_user_eyes_show),
'delete': ArgsInt(End(cmd_user_eyes_delete)), 'delete': ArgsInt(End(cmd_user_eyes_delete)),
})), })),
'add': ArgsJsonBoolean(ArgsJsonString(End(cmd_user_add))),
}) })

ファイルの表示

@ -135,12 +135,13 @@ async def nojs_submit_message(timestamp, user):
try: try:
# If the comment is empty but the captcha was just solved, # If the comment is empty but the captcha was just solved,
# be lenient: don't raise an exception and don't create a notice # be lenient: don't raise an exception and don't create a notice
message_was_added = add_chat_message( seq = add_chat_message(
user, user,
nonce, nonce,
comment, comment,
ignore_empty=verification_happened, ignore_empty=verification_happened,
) )
message_was_added = seq is not None
except Rejected as e: except Rejected as e:
notice, *_ = e.args notice, *_ = e.args
state_id = add_state( state_id = add_state(

ファイルの表示

@ -140,12 +140,13 @@ def handle_inbound_message(timestamp, queue, user, nonce, comment, digest, answe
message_was_added = False message_was_added = False
else: else:
try: try:
message_was_added = add_chat_message( seq = add_chat_message(
user, user,
nonce, nonce,
comment, comment,
ignore_empty=verification_happened, ignore_empty=verification_happened,
) )
message_was_added = seq is not None
except Rejected as e: except Rejected as e:
notice, *_ = e.args notice, *_ = e.args
message_was_added = False message_was_added = False