Control socket: view and change users' attributes

Changing things without thinking about it is probably going to cause
weird undefined behaviour.
このコミットが含まれているのは:
n9k 2022-06-14 01:11:29 +00:00
コミット 31ce80b2bf
2個のファイルの変更126行の追加3行の削除

ファイルの表示

@ -1,8 +1,15 @@
import asyncio
import json
from quart import current_app
from anonstream.chat import delete_chat_messages
from anonstream.stream import get_stream_title, set_stream_title
from anonstream.utils.user import USER_WEBSOCKET_ATTRS
USERS_BY_TOKEN = current_app.users_by_token
USERS = current_app.users
USERS_UPDATE_BUFFER = current_app.users_update_buffer
class UnknownMethod(Exception):
pass
@ -218,6 +225,112 @@ async def command_title_set(args):
raise Garbage(garbage)
return normal_options, response
async def command_user_help(args):
match args:
case []:
normal_options = ['help']
response = (
'Usage: user [show | attr USER | get USER ATTR | set USER ATTR VALUE]\n'
'Commands:\n'
' user [show]...........show all users\' tokens\n'
' user attr USER........show names of a user\'s attributes\n'
' user get USER ATTR....show an attribute of a user\n'
' user set USER ATTR....set an attribute of a user\n'
'Definitions:\n'
' USER..................={token TOKEN | hash HASH}\n'
' TOKEN.................a token\n'
' HASH..................a token hash\n'
' ATTR..................a user attribute, re:[a-z0-9_]+\n'
)
case [*garbage]:
raise Garbage(garbage)
return normal_options, response
async def command_user_show(args):
match args:
case []:
normal_options = ['show']
response = json.dumps(tuple(USERS_BY_TOKEN)) + '\n'
case [*garbage]:
raise Garbage(garbage)
return normal_options, response
async def command_user_attr(args):
match args:
case []:
raise Incomplete
case ['token', token_json]:
try:
token = json.loads(token_json)
except json.JSONDecodeError:
raise BadArgument('could not decode token as json')
try:
user = USERS_BY_TOKEN[token]
except KeyError:
raise Failed(f"no user exists with token {token!r}, try 'user show'")
normal_options = ['attr', 'token', json.dumps(token).replace(' ', r'\u0020')]
response = json.dumps(tuple(user.keys())) + '\n'
case [*garbage]:
raise Garbage(garbage)
return normal_options, response
async def command_user_get(args):
match args:
case ['token', token_json, attr]:
try:
token = json.loads(token_json)
except json.JSONDecodeError:
raise BadArgument('could not decode token as json')
try:
user = USERS_BY_TOKEN[token]
except KeyError:
raise Failed(f"no user exists with token {token!r}, try 'user show'")
try:
value = user[attr]
except KeyError:
raise Failed(f"user has no attribute {attr!r}, try 'user attr token {token}'")
try:
value_json = json.dumps(value)
except TypeError:
raise Failed(f'attribute {attr!r} is not JSON serializable')
normal_options = ['get', 'token', json.dumps(token).replace(' ', r'\u0020'), attr]
response = value_json + '\n'
case []:
raise Incomplete
case [*garbage]:
raise Garbage(garbage)
return normal_options, response
async def command_user_set(args):
match args:
case ['token', token_json, attr, value_json]:
try:
token = json.loads(token_json)
except json.JSONDecodeError:
raise BadArgument('could not decode token as json')
try:
user = USERS_BY_TOKEN[token]
except KeyError:
raise Failed(f"no user exists with token {token!r}, try 'user show'")
try:
value = user[attr]
except KeyError:
raise Failed(f"user has no attribute {attr!r}, try 'user attr token {TOKEN}")
try:
value = json.loads(value_json)
except JSON.JSONDecodeError:
raise Failed('could not decode json')
user[attr] = value
if attr in USER_WEBSOCKET_ATTRS:
USERS_UPDATE_BUFFER.add(token)
normal_options = ['set', 'token', json.dumps(token).replace(' ', r'\u0020'), attr, json.dumps(value)]
response = ''
case []:
raise Incomplete
case [*garbage]:
raise Garbage(garbage)
return normal_options, response
async def command_chat_help(args):
match args:
case []:
@ -254,6 +367,7 @@ METHOD_HELP = 'help'
METHOD_EXIT = 'exit'
METHOD_TITLE = 'title'
METHOD_CHAT = 'chat'
METHOD_USER = 'user'
METHOD_COMMAND_FUNCTIONS = {
METHOD_HELP: {
@ -274,5 +388,13 @@ METHOD_COMMAND_FUNCTIONS = {
None: command_chat_help,
'help': command_chat_help,
'delete': command_chat_delete,
}
},
METHOD_USER: {
None: command_user_show,
'help': command_user_help,
'show': command_user_show,
'attr': command_user_attr,
'get': command_user_get,
'set': command_user_set,
},
}

ファイルの表示

@ -10,6 +10,8 @@ from math import inf
from quart import escape, Markup
USER_WEBSOCKET_ATTRS = {'broadcaster', 'name', 'color', 'tripcode', 'tag'}
Presence = Enum(
'Presence',
names=(
@ -44,8 +46,7 @@ def trilean(presence):
return None
def get_user_for_websocket(user):
keys = ('broadcaster', 'name', 'color', 'tripcode', 'tag')
return {
**{key: user[key] for key in keys},
**{key: user[key] for key in USER_WEBSOCKET_ATTRS},
'watching': trilean(user['presence']),
}