Skip to content

Commit 1604c2a

Browse files
committed
refactor: syncplay server boot script
1 parent 63498de commit 1604c2a

File tree

1 file changed

+101
-122
lines changed

1 file changed

+101
-122
lines changed

boot.py

Lines changed: 101 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,107 @@
11
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
23

34
import os
45
import sys
6+
import yaml
7+
import argparse
8+
from typing import Any
59
from syncplay import ep_server
610

7-
8-
def checkOpt(args: list, option: str) -> bool:
9-
if option not in args: # option not found
10-
return False
11-
args.remove(option) # remove target option
12-
return True
13-
14-
15-
def fetchOpt(args: list, option: str, default):
16-
if option not in args: # option not found
17-
return default
18-
index = args.index(option)
19-
if index + 1 == len(args):
20-
print('Error: `%s` missing value' % option, file = sys.stderr)
21-
sys.exit(1)
22-
targetVal = args[index + 1]
23-
del sys.argv[index : index + 2] # remove target option and value
24-
return targetVal
25-
26-
27-
isDebug = checkOpt(sys.argv, '--debug')
28-
29-
30-
portValue = None # no specify in default
31-
if 'PORT' in os.environ: # `PORT` env variable
32-
portValue = os.environ['PORT']
33-
portValue = fetchOpt(sys.argv, '--port', portValue)
34-
35-
36-
passwdStr = None # no password in default
37-
if 'PASSWD' in os.environ: # `PASSWD` env variable
38-
passwdStr = os.environ['PASSWD']
39-
passwdStr = fetchOpt(sys.argv, '--password', passwdStr)
40-
41-
42-
saltValue = '' # using empty string in default
43-
if 'SALT' in os.environ: # `SALT` env variable
44-
saltValue = os.environ['SALT']
45-
if checkOpt(sys.argv, '--random-salt'):
46-
saltValue = None
47-
saltValue = fetchOpt(sys.argv, '--salt', saltValue)
48-
49-
50-
isolateRoom = False # disable isolate room in default
51-
if 'ISOLATE' in os.environ and os.environ['ISOLATE'] in ['ON', 'TRUE']:
52-
isolateRoom = True
53-
if checkOpt(sys.argv, '--isolate-room'):
54-
isolateRoom = True
55-
56-
57-
tlsPath = '/certs'
58-
if 'TLS_PATH' in os.environ: # `TLS_PATH` env variable
59-
tlsPath = os.environ['TLS_PATH']
60-
tlsPath = fetchOpt(sys.argv, '--tls', tlsPath)
61-
62-
enableTls = False
63-
if checkOpt(sys.argv, '--enable-tls'):
64-
enableTls = True
65-
if 'TLS' in os.environ and os.environ['TLS'] in ['ON', 'TRUE']:
66-
enableTls = True
67-
68-
69-
motdMessage = None # without motd message in default
70-
if 'MOTD' in os.environ: # `MOTD` env variable
71-
motdMessage = os.environ['MOTD']
72-
motdMessage = fetchOpt(sys.argv, '--motd', motdMessage)
73-
74-
motdFile = fetchOpt(sys.argv, '--motd-file', None)
75-
if motdFile is not None:
76-
motdMessage = None # cover motd message
77-
elif motdMessage is not None:
78-
motdFile = '/app/syncplay/motd'
79-
os.system('mkdir -p /app/syncplay/')
80-
with open(motdFile, mode = 'w', encoding = 'utf-8') as fileObj:
81-
fileObj.write(motdMessage)
82-
83-
84-
if isDebug: # print debug log
85-
if portValue is not None:
86-
print('Port -> %s' % portValue, file = sys.stderr)
87-
88-
if saltValue is None:
89-
print('Using random salt', file = sys.stderr)
90-
else:
91-
print('Salt -> `%s`' % saltValue, file = sys.stderr)
92-
93-
if isolateRoom:
94-
print('Isolate room enabled', file = sys.stderr)
95-
96-
if passwdStr is None:
97-
print('Running without password', file = sys.stderr)
98-
else:
99-
print('Password -> `%s`' % passwdStr, file = sys.stderr)
100-
101-
if enableTls:
102-
print('TLS enabled -> `%s`' % tlsPath, file = sys.stderr)
103-
104-
if motdFile is not None:
105-
print('MOTD File -> `%s`' % motdFile, file = sys.stderr)
106-
if motdMessage is not None:
107-
print('MOTD message -> `%s`' % motdMessage, file = sys.stderr)
108-
109-
110-
if portValue is not None:
111-
sys.argv += ['--port', portValue]
112-
if passwdStr is not None:
113-
sys.argv += ['--password', passwdStr]
114-
if saltValue is not None:
115-
sys.argv += ['--salt', saltValue]
116-
if enableTls:
117-
sys.argv += ['--tls', tlsPath]
118-
if isolateRoom:
119-
sys.argv += ['--isolate-room']
120-
if motdFile is not None:
121-
sys.argv += ['--motd-file', motdFile]
122-
123-
124-
if isDebug: # print debug log
125-
print('Boot args -> %s' % sys.argv, file = sys.stderr)
126-
127-
128-
sys.exit(ep_server.main())
11+
WorkDir = '/data/'
12+
CertDir = '/certs/'
13+
ConfigFile = 'config.yml'
14+
15+
16+
def debug(msg: str) -> None:
17+
""" Print out debug information. """
18+
if 'DEBUG' in os.environ and os.environ['DEBUG'] in ['ON', 'TRUE']:
19+
print(f'\033[90m{msg}\033[0m', file=sys.stderr)
20+
21+
22+
def temp_file(file: str, content: str) -> str:
23+
""" Create and save content to temporary files. """
24+
file = os.path.join('/tmp/', file)
25+
with open(file, 'w') as fp:
26+
fp.write(f'{content}\n')
27+
return file
28+
29+
30+
def load_args() -> dict[str, Any]:
31+
""" Loading arguments from the command line. """
32+
parser = argparse.ArgumentParser(description='Syncplay Docker Bootstrap')
33+
parser.add_argument('-p', '--port', type=int, help='listen port of syncplay server')
34+
parser.add_argument('--password', type=str, help='authentication of syncplay server')
35+
parser.add_argument('--motd', type=str, help='welcome text after the user enters the room')
36+
parser.add_argument('--salt', type=str, help='string used to secure passwords')
37+
parser.add_argument('--random-salt', action='store_true', help='use a randomly generated salt value')
38+
parser.add_argument('--isolate-rooms', action='store_true', help='room isolation enabled')
39+
parser.add_argument('--disable-chat', action='store_true', help='disables the chat feature')
40+
parser.add_argument('--disable-ready', action='store_true', help='disables the readiness indicator feature')
41+
parser.add_argument('--enable-stats', action='store_true', help='enable syncplay server statistics')
42+
parser.add_argument('--enable-tls', action='store_true', help='enable tls support of syncplay server')
43+
parser.add_argument('--persistent', action='store_true', help='enables room persistence')
44+
parser.add_argument('--max-username', type=int, help='maximum length of usernames')
45+
parser.add_argument('--max-chat-message', type=int, help='maximum length of chat messages')
46+
parser.add_argument('--permanent-rooms', type=str, nargs='*', help='permanent rooms of syncplay server')
47+
args = parser.parse_args()
48+
debug(f'Command line arguments -> {args}')
49+
return {k.replace('_', '-'): v for k, v in vars(args).items()}
50+
51+
52+
def load_config(args: dict[str, Any], file: str) -> dict[str, Any]:
53+
""" Complete uninitialized arguments from configure file. """
54+
if not os.path.exists(file):
55+
return args
56+
config = yaml.safe_load(open(file).read())
57+
options = [
58+
'port', 'password', 'motd', 'salt', 'random-salt',
59+
'isolate-rooms', 'disable-chat', 'disable-ready',
60+
'enable-stats', 'enable-tls', 'persistent',
61+
'max-username', 'max-chat-message', 'permanent-rooms',
62+
]
63+
override = {x: config[x] for x in options if not args[x] and x in config}
64+
debug(f'Configure file override -> {override}')
65+
return args | override
66+
67+
68+
def build_args(opts: dict):
69+
""" Construct the startup arguments for syncplay server. """
70+
args = ['--port', opts.get('port', '8999')]
71+
if 'password' in opts:
72+
args += ['--password', opts['password']]
73+
if 'motd' in opts:
74+
args += ['--motd-file', temp_file('motd.data', opts['motd'])]
75+
76+
salt = opts.get('salt', None if 'random-salt' in opts else '')
77+
if salt is not None:
78+
args += ['--salt', salt] # using random salt without this option
79+
for opt in ['isolate-rooms', 'disable-chat', 'disable-ready']:
80+
if opt in opts:
81+
args.append(f'--{opt}')
82+
83+
if 'enable-stats' in opts:
84+
args += ['--stats-db-file', os.path.join(WorkDir, 'stats.db')]
85+
if 'enable-tls' in opts:
86+
args += ['--tls', CertDir]
87+
if 'persistent' in opts:
88+
args += ['--rooms-db-file', os.path.join(WorkDir, 'rooms.db')]
89+
90+
if 'max-username' in opts:
91+
args += ['--max-username-length', str(opts['max-username'])]
92+
if 'max-chat-message' in opts:
93+
args += ['--max-chat-message-length', str(opts['max-chat-message'])]
94+
if 'permanent-rooms' in opts:
95+
rooms = '\n'.join(opts['permanent-rooms'])
96+
args += ['--permanent-rooms-file', temp_file('rooms.list', rooms)]
97+
return args
98+
99+
100+
if __name__ == '__main__':
101+
origin_args = load_config(load_args(), os.path.join(WorkDir, ConfigFile))
102+
origin_args = {k: v for k, v in origin_args.items() if v is not None and v is not False} # remove invalid items
103+
debug(f'Parsed arguments -> {origin_args}')
104+
syncplay_args = build_args(origin_args)
105+
debug(f'Syncplay startup arguments -> {syncplay_args}')
106+
sys.argv = ['syncplay'] + syncplay_args
107+
sys.exit(ep_server.main())

0 commit comments

Comments
 (0)