Control socket: view and change users' attributes
Changing things without thinking about it is probably going to cause weird undefined behaviour.
このコミットが含まれているのは:
コミット
31ce80b2bf
|
@ -1,8 +1,15 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from quart import current_app
|
||||||
|
|
||||||
from anonstream.chat import delete_chat_messages
|
from anonstream.chat import delete_chat_messages
|
||||||
from anonstream.stream import get_stream_title, set_stream_title
|
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):
|
class UnknownMethod(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -218,6 +225,112 @@ async def command_title_set(args):
|
||||||
raise Garbage(garbage)
|
raise Garbage(garbage)
|
||||||
return normal_options, response
|
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):
|
async def command_chat_help(args):
|
||||||
match args:
|
match args:
|
||||||
case []:
|
case []:
|
||||||
|
@ -254,6 +367,7 @@ METHOD_HELP = 'help'
|
||||||
METHOD_EXIT = 'exit'
|
METHOD_EXIT = 'exit'
|
||||||
METHOD_TITLE = 'title'
|
METHOD_TITLE = 'title'
|
||||||
METHOD_CHAT = 'chat'
|
METHOD_CHAT = 'chat'
|
||||||
|
METHOD_USER = 'user'
|
||||||
|
|
||||||
METHOD_COMMAND_FUNCTIONS = {
|
METHOD_COMMAND_FUNCTIONS = {
|
||||||
METHOD_HELP: {
|
METHOD_HELP: {
|
||||||
|
@ -274,5 +388,13 @@ METHOD_COMMAND_FUNCTIONS = {
|
||||||
None: command_chat_help,
|
None: command_chat_help,
|
||||||
'help': command_chat_help,
|
'help': command_chat_help,
|
||||||
'delete': command_chat_delete,
|
'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
|
from quart import escape, Markup
|
||||||
|
|
||||||
|
USER_WEBSOCKET_ATTRS = {'broadcaster', 'name', 'color', 'tripcode', 'tag'}
|
||||||
|
|
||||||
Presence = Enum(
|
Presence = Enum(
|
||||||
'Presence',
|
'Presence',
|
||||||
names=(
|
names=(
|
||||||
|
@ -44,8 +46,7 @@ def trilean(presence):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_user_for_websocket(user):
|
def get_user_for_websocket(user):
|
||||||
keys = ('broadcaster', 'name', 'color', 'tripcode', 'tag')
|
|
||||||
return {
|
return {
|
||||||
**{key: user[key] for key in keys},
|
**{key: user[key] for key in USER_WEBSOCKET_ATTRS},
|
||||||
'watching': trilean(user['presence']),
|
'watching': trilean(user['presence']),
|
||||||
}
|
}
|
||||||
|
|
読み込み中…
新しいイシューから参照