From 2147757f0cb14ec7fdd23889d66dba23cb7d1670 Mon Sep 17 00:00:00 2001 From: Tim Diekmann Date: Sun, 30 Jun 2019 20:47:27 +0200 Subject: [PATCH] Complete API documentation --- app/__init__.py | 10 -- app/api/__init__.py | 7 +- app/api/task.py | 18 ---- app/api/token.py | 50 ---------- app/api/user.py | 125 +----------------------- app/models/permission.py | 11 --- app/models/token.py | 2 +- app/static/js/connection.js | 11 +-- app/templates/task.html | 31 ------ app/templates/token.html | 161 ------------------------------- docs/slurk_api.rst | 185 ++++++++++++++++++++++++++++++++++-- docs/slurk_multibots.rst | 2 +- multibot.sh | 55 ----------- 13 files changed, 186 insertions(+), 482 deletions(-) delete mode 100644 app/api/task.py delete mode 100644 app/api/token.py delete mode 100644 app/templates/task.html delete mode 100644 app/templates/token.html delete mode 100755 multibot.sh diff --git a/app/__init__.py b/app/__init__.py index 31fd5470..79bf36b9 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -63,11 +63,6 @@ def before_request(): if request.endpoint != 'login.index' and request.endpoint != "static": return login_manager.unauthorized() - if request.endpoint == 'admin.token' and not current_user.token.permissions.token_generate \ - or request.endpoint == 'admin.task' and not current_user.token.permissions.task_create: - flash("Permission denied", "error") - return login_manager.unauthorized() - if not Room.query.get("admin_room"): db.session.add(Room(name="admin_room", @@ -78,11 +73,7 @@ def before_request(): id='00000000-0000-0000-0000-000000000000' if settings.debug else None, permissions=Permissions( user_query=True, - user_log_query=True, user_log_event=True, - user_permissions_query=True, - user_permissions_update=True, - user_room_query=True, user_room_join=True, user_room_leave=True, message_text=True, @@ -93,7 +84,6 @@ def before_request(): room_log_query=True, room_create=True, room_update=True, - room_close=True, room_delete=True, layout_query=True, layout_create=True, diff --git a/app/api/__init__.py b/app/api/__init__.py index 5899efca..6a7c15d2 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -127,7 +127,7 @@ def get_tokens(): @api.route('/token/', methods=['GET']) @auth.login_required def get_token(id): - if not g.current_permissions.token_query: + if not g.current_permissions.token_query and str(g.current_user.token) != id: return make_response(jsonify({'error': 'insufficient rights'}), 403) token = Token.query.get(id) @@ -168,11 +168,7 @@ def post_token(): source=data.get("source", None), permissions=Permissions( user_query=data.get("user_query", False), - user_log_query=data.get("user_log_query", False), user_log_event=data.get("user_log_event", False), - user_permissions_query=data.get("user_permissions_query", False), - user_permissions_update=data.get("user_permissions_update", False), - user_room_query=data.get("user_room_query", False), user_room_join=data.get("user_room_join", False), user_room_leave=data.get("user_room_leave", False), message_text=data.get("message_text", False), @@ -183,7 +179,6 @@ def post_token(): room_log_query=data.get("room_log_query", False), room_create=data.get("room_create", False), room_update=data.get("room_update", False), - room_close=data.get("room_close", False), room_delete=data.get("room_delete", False), layout_query=data.get("layout_query", False), layout_create=data.get("layout_create", False), diff --git a/app/api/task.py b/app/api/task.py deleted file mode 100644 index e98849bd..00000000 --- a/app/api/task.py +++ /dev/null @@ -1,18 +0,0 @@ -from flask_login import current_user - -from .. import socketio - -from ..models.task import Task - - -@socketio.on('get_task') -def _get_task(id): - if not current_user.get_id(): - return False, "invalid session id" - if not current_user.token.permissions.task_query: - return False, "insufficient rights" - task = Task.query.get(id) - if task: - return True, task.as_dict() - else: - return False, "task does not exist" diff --git a/app/api/token.py b/app/api/token.py deleted file mode 100644 index 2957aef0..00000000 --- a/app/api/token.py +++ /dev/null @@ -1,50 +0,0 @@ -from flask_login import current_user -from sqlalchemy.exc import IntegrityError - -from .. import socketio, db - -from ..models.token import Token - - -@socketio.on('get_token') -def _get_token(id): - if not current_user.get_id(): - return False, "invalid session id" - if not current_user.token.permissions.token_query: - return False, "insufficient rights" - token = Token.query.get(id) - if token: - return True, token.as_dict() - else: - return False, "token does not exist" - - -@socketio.on('invalidate_token') -def _invalidate_token(id): - if not current_user.get_id(): - return False, "invalid session id" - if not current_user.token.permissions.token_invalidate: - return False, "insufficient rights" - token = Token.query.get(id) - if token: - was_valid = token.valid - token.valid = False - db.session.commit() - return True, was_valid - else: - return False, "token does not exist" - - -@socketio.on('remove_token') -def _remove_token(id): - if not current_user.get_id(): - return False, "invalid session id" - if not current_user.token.permissions.token_remove: - return False, "insufficient rights" - - try: - deleted = Token.query.filter_by(id=id).delete() - db.session.commit() - return True, bool(deleted) - except IntegrityError as e: - return False, str(e) diff --git a/app/api/user.py b/app/api/user.py index 5c6546be..f556985e 100644 --- a/app/api/user.py +++ b/app/api/user.py @@ -8,125 +8,6 @@ from ..api.log import log_event -@socketio.on('get_user') -def _get_user(id): - current_id = current_user.get_id() - if not current_id: - return False, "invalid session id" - - if id and not current_user.token.permissions.user_query: - return False, "insufficient rights" - user = User.query.get(id or current_id) - if user: - return True, user.as_dict() - else: - return False, "user does not exist" - - -@socketio.on('get_user_task') -def _get_user_task(id): - if not current_user.get_id(): - return False, "invalid session id" - if id and not current_user.token.permissions.task_query: - return False, "insufficient rights" - - if id: - user = User.query.get(id) - else: - user = current_user - - if user: - return True, user.token.task.as_dict() if user.token and user.token.task else None - else: - return False, "user does not exist" - - -@socketio.on('get_user_token') -def _get_user_task(id): - if not current_user.get_id(): - return False, "invalid session id" - if id and not current_user.token.permissions.token_query: - return False, "insufficient rights" - - if id: - user = User.query.get(id) - else: - user = current_user - - if user: - return True, user.token.as_dict() if user.token else None - else: - return False, "user does not exist" - - -@socketio.on('get_user_permissions') -def _get_user_permissions(id): - if not current_user.get_id(): - return False, "invalid session id" - if id and not current_user.token.permissions.permissions_query: - return False, "insufficient rights" - - if id: - user = User.query.get(id) - else: - user = current_user - - if user: - return True, user.token.permissions.as_dict() - else: - return False, "user does not exist" - - -@socketio.on('get_user_rooms') -def _get_user_rooms(user_id): - if not current_user.get_id(): - return False, "invalid session id" - if user_id and not current_user.token.permissions.user_room_query: - return False, "insufficient rights" - - if user_id: - user = User.query.get(user_id) - else: - user = current_user - - if user: - return True, [room.as_dict() for room in user.rooms] - else: - return False, "user does not exist" - - -@socketio.on('get_user_rooms_logs') -def _get_user_rooms_logs(user_id): - from ..models.user import User - - if not current_user.get_id(): - return False, "invalid session id" - if user_id and not current_user.token.permissions.user_log_query: - return False, "insufficient rights" - - if user_id: - user = User.query.get(user_id) - else: - user = current_user - - def filter_private_messages(logs, id): - for log in logs: - if log['event'] == "text_message" or log['event'] == "image_message": - # Filter only messages - if log['receiver']: - # Private message - if int(log['receiver']) != id and log['user']['id'] != id: - # User not affected, continue the loop - continue - yield log - - if user: - return True, {room.name: list(filter_private_messages([log.as_dict() for log in room.logs], user.id)) - for room in user.rooms} - else: - return False, "user does not exist" - - @socketio.on('join_room') def _join_room(data): id = data.get('user') @@ -159,7 +40,6 @@ def _join_room(data): log_event("join", user, room) db.session.commit() - print("join room:", room, user.session_id) join_room(room.name, user.session_id) return True @@ -172,7 +52,7 @@ def _leave_room(data): if not current_user.get_id(): return False, "invalid session id" - if id and not current_user.token.permissions.user_room_join: + if id and not current_user.token.permissions.user_room_leave: return False, "insufficient rights" if id: @@ -186,14 +66,11 @@ def _leave_room(data): if not room: return False, "room does not exist" - print(user.as_dict()) - print(room) user.rooms.remove(room) user.current_rooms.remove(room) socketio.emit('left_room', room.name, room=user.session_id) log_event("leave", user, room) db.session.commit() - print("leave room:", room, user.session_id) leave_room(room.name, user.session_id) return True diff --git a/app/models/permission.py b/app/models/permission.py index 6f890238..875c2232 100644 --- a/app/models/permission.py +++ b/app/models/permission.py @@ -7,11 +7,7 @@ class Permissions(Base): __tablename__ = 'Permissions' user_query = db.Column(db.Boolean, nullable=False, default=False) - user_log_query = db.Column(db.Boolean, nullable=False, default=False) user_log_event = db.Column(db.Boolean, nullable=False, default=False) - user_permissions_query = db.Column(db.Boolean, nullable=False, default=False) - user_permissions_update = db.Column(db.Boolean, nullable=False, default=False) - user_room_query = db.Column(db.Boolean, nullable=False, default=False) user_room_join = db.Column(db.Boolean, nullable=False, default=False) user_room_leave = db.Column(db.Boolean, nullable=False, default=False) message_text = db.Column(db.Boolean, nullable=False, default=False) @@ -22,7 +18,6 @@ class Permissions(Base): room_log_query = db.Column(db.Boolean, nullable=False, default=False) room_create = db.Column(db.Boolean, nullable=False, default=False) room_update = db.Column(db.Boolean, nullable=False, default=False) - room_close = db.Column(db.Boolean, nullable=False, default=False) room_delete = db.Column(db.Boolean, nullable=False, default=False) layout_query = db.Column(db.Boolean, nullable=False, default=False) layout_create = db.Column(db.Boolean, nullable=False, default=False) @@ -41,13 +36,8 @@ def as_dict(self): 'user': { 'query': self.user_query, 'log': { - 'query': self.user_log_query, 'event': self.user_log_event, }, - 'permissions': { - 'query': self.user_permissions_query, - 'update': self.user_permissions_update, - }, 'room': { 'join': self.user_room_join, 'leave': self.user_room_leave, @@ -63,7 +53,6 @@ def as_dict(self): 'query': self.room_query, 'create': self.room_create, 'update': self.room_update, - 'close': self.room_close, 'delete': self.room_delete, 'log': { 'query': self.room_log_query, diff --git a/app/models/token.py b/app/models/token.py index 11d2200d..2c5a9bc2 100644 --- a/app/models/token.py +++ b/app/models/token.py @@ -26,7 +26,7 @@ def as_dict(self): 'user': self.user_id, 'task': self.task_id, 'room': self.room_name, - 'permissions': self.permissions_id, + 'permissions': self.permissions.as_dict(), 'source': self.source, 'valid': self.valid, }, **super(Token, self).as_dict()) diff --git a/app/static/js/connection.js b/app/static/js/connection.js index 0142536a..0498d02c 100644 --- a/app/static/js/connection.js +++ b/app/static/js/connection.js @@ -90,6 +90,7 @@ $(document).ready(() => { let user = await user_request; self_user = { id: user.id, name: user.name }; + let token = $.get({ url: uri + "/token/" + user.token, beforeSend: headers }); users = {}; for (let user_id in room.current_users) { @@ -104,6 +105,8 @@ $(document).ready(() => { print_history(history[room.name]); } + apply_user_permissions((await token).permissions); + } async function left_room(data) {} @@ -111,14 +114,6 @@ $(document).ready(() => { socket.on('joined_room', joined_room); socket.on('left_room', left_room); - socket.on('connect', (data) => { - socket.emit("get_user_permissions", null, (success, permissions) => { - if (verify_query(success, permissions)) { - apply_user_permissions(permissions); - } - }); - }); - socket.on('status', function (data) { if (typeof self_user === "undefined") return; diff --git a/app/templates/task.html b/app/templates/task.html deleted file mode 100644 index 7ec30857..00000000 --- a/app/templates/task.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - {{ title }} - - - - - - -

Generate Task

-
- {{ form.hidden_tag() }} - - - - - - - - - -
{{ form.name.label }}{{ form.name(placeholder=form.name.label.text) }}
{{ form.num_users.label }}{{ form.num_users() }}
- - {{ form.submit() }} -
- - - \ No newline at end of file diff --git a/app/templates/token.html b/app/templates/token.html deleted file mode 100644 index 29243408..00000000 --- a/app/templates/token.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - {{ title }} - - - - - - -

Generate Tokens

-
- {{ form.hidden_tag() }} - - - - - - - - - - - - - - - - - -
{{ form.source.label }}{{ form.source(placeholder=form.source.label.text) }}
{{ form.room.label }}{{ form.room(placeholder=form.room.label.text) }}
{{ form.task.label }}{{ form.task(placeholder=form.task.label.text) }}
{{ form.count.label }}{{ form.count(placeholder=form.count.label.text) }}
- -

Permissions

- -

User

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ form.user_query.label }}{{ form.user_query() }}
{{ form.user_log_query.label }}{{ form.user_log_query() }}
{{ form.user_permissions_query.label }}{{ form.user_permissions_query() }}
{{ form.user_permissions_update.label }}{{ form.user_permissions_update() }}
{{ form.user_room_query.label }}{{ form.user_room_query() }}
{{ form.user_room_join.label }}{{ form.user_room_join() }}
{{ form.user_room_leave.label }}{{ form.user_room_leave() }}
- -

Message

- - - - - - - - - - - - - - - - - -
{{ form.message_text.label }}{{ form.message_text() }}
{{ form.message_image.label }}{{ form.message_image() }}
{{ form.message_command.label }}{{ form.message_command() }}
{{ form.message_broadcast.label }}{{ form.message_broadcast() }}
- -

Room

- - - - - - - - - - - - - - - - - - - - - - - - - -
{{ form.room_query.label }}{{ form.room_query() }}
{{ form.room_log_query.label }}{{ form.room_log_query() }}
{{ form.room_create.label }}{{ form.room_create() }}
{{ form.room_update.label }}{{ form.room_update() }}
{{ form.room_close.label }}{{ form.room_close() }}
{{ form.room_delete.label }}{{ form.room_delete() }}
- -

Layout

- - - - - -
{{ form.layout_query.label }}{{ form.layout_query() }}
- -

Task

- - - - - - - - - -
{{ form.task_create.label }}{{ form.task_create() }}
{{ form.task_query.label }}{{ form.task_query() }}
- -

Token

- - - - - - - - - - - - - - - - - -
{{ form.token_generate.label }}{{ form.token_generate() }}
{{ form.token_query.label }}{{ form.token_query() }}
{{ form.token_invalidate.label }}{{ form.token_invalidate() }}
{{ form.token_remove.label }}{{ form.token_remove() }}
- - {{ form.submit() }} -
- - - \ No newline at end of file diff --git a/docs/slurk_api.rst b/docs/slurk_api.rst index 49ce11d8..7bdc04d6 100644 --- a/docs/slurk_api.rst +++ b/docs/slurk_api.rst @@ -17,6 +17,33 @@ will also return as JSON. Example:: Token ----- +* ``GET /api/v2/tokens`` + + Returns a list of tokens: + + ========================= ================================================================================= + ``id`` ID of the token + ``permissions`` List of permissions for the token + ``room`` Room to land in with this token + ``source`` Optional source string + ``task`` Task assigned to this token + ``user`` User associated with this token + ``uri`` URI to query this token + ========================= ================================================================================= + +* ``GET /api/v2/token/`` + + Returns the layout by id: + + ========================= ================================================================================= + ``id`` ID of the token + ``permissions`` List of permissions for the token + ``room`` Room to land in with this token + ``source`` Optional source string + ``task`` Task assigned to this token + ``user`` User associated with this token + ========================= ================================================================================= + * ``POST /api/v2/token`` Generates a new token: @@ -24,11 +51,7 @@ Token ============================= =========================================================================================================== ``room`` * Room to land when using this token as login ``user_query`` Can query other users - ``user_log_query`` Can query the logs for an arbitrary user, the logs the user is in can always be queried ``user_log_event`` Can create custom log events - ``user_permissions_query`` Can query permissions of other user, the permissions for the current can always be queried - ``user_permissions_update`` Can update permissions of a user - ``user_room_query`` Can query the rooms for an arbitrary user, the rooms the user is in can always be queried ``user_room_join`` Can make users join a room ``user_room_leave`` Can make users leave a room ``message_text`` Can send text messages @@ -39,7 +62,6 @@ Token ``room_log_query`` Can query logs for an arbitrary rooms. Without this permission only the current room can be queried ``room_create`` Can create a room ``room_update`` Can update a rooms properties - ``room_close`` Can close a room ``room_delete`` Can delete a room if there are no backrefs to it (tokens, users etc.) ``layout_query`` Can query layouts of arbitrary rooms. The layout from the rooms the user is in can always be queried ``task_create`` Can generate tasks. Needed to open the task form @@ -51,6 +73,114 @@ Token ============================= =========================================================================================================== +* ``DELETE /api/v2/token/`` + + Invalidates the token + +User +---- + +* ``GET /api/v2/users`` + + Returns a list of users: + + ========================= ================================================================================= + ``id`` ID of the user + ``name`` Name of the user + ``rooms`` List of rooms, where the user is present + ``session_id`` Session ID used by SocketIO + ``token`` Token associated with the user + ``uri`` URI to query this user + ========================= ================================================================================= + +* ``GET /api/v2/user/`` + + Returns the specified user: + + ========================= ================================================================================= + ``id`` ID of the user + ``name`` Name of the user + ``rooms`` List of rooms, where the user is present + ``session_id`` Session ID used by SocketIO + ``token`` Token associated with the user + ========================= ================================================================================= + +* ``GET /api/v2/tasks`` + + Returns a list of tasks: + + ========================= ================================================================================= + ``id`` ID of the task + ``name`` Name of the task + ``num_users`` Number of user needed for this task + ``layout`` Layout used for task rooms + ``tokens`` Tokens associated with this task + ``uri`` URI to query this task + ========================= ================================================================================= + + +Task +---- + +* ``GET /api/v2/user//task`` + + Returns the task for the specified user if any: + + ========================= ================================================================================= + ``id`` ID of the task + ``name`` Name of the task + ``num_users`` Number of user needed for this task + ``layout`` Layout used for task rooms + ``tokens`` Tokens associated with this task + ``uri`` URI to query this task + ========================= ================================================================================= + +* ``GET /api/v2/tasks`` + + Returns a list of tasks: + + ========================= ================================================================================= + ``id`` ID of the task + ``name`` Name of the task + ``num_users`` Number of user needed for this task + ``layout`` Layout used for task rooms + ``tokens`` Tokens associated with this task + ``uri`` URI to query this task + ========================= ================================================================================= + +* ``GET /api/v2/task/`` + + Returns the specified user: + + ========================= ================================================================================= + ``id`` ID of the task + ``name`` Name of the task + ``num_users`` Number of user needed for this task + ``layout`` Layout used for task rooms + ``tokens`` Tokens associated with this task + ========================= ================================================================================= + +* ``POST /api/v2/task`` + + Creates a new task: + + ========================= ================================================================================= + ``name`` * Name of the task + ``num_users`` * Number of user needed for this task + ``layout`` Layout used for task rooms + ========================= ================================================================================= + +* ``PUT /api/v2/task/`` + + Updates the specified task: + + ========================= ================================================================================= + ``name`` Name of the task + ``num_users`` Number of user needed for this task + ``layout`` Layout used for task rooms + ========================= ================================================================================= + + Room ---- @@ -89,7 +219,7 @@ Room ``users`` List of users who were associated with this room at least once ========================= ================================================================================= -* ``POST /api/v2/room/`` +* ``POST /api/v2/room`` Creates a new room: @@ -120,6 +250,49 @@ Room Deletes the room by name if no associations to the room exist. Otherwise an error is returned. +Layouts +------- + +* ``GET /api/v2/layouts`` + + Returns a list of layouts: + + ========================= ================================================================================= + ``name`` Unique name for the layout + ``id`` ID of the layout + ``title`` Title of the layout + ``subtitle`` Subtitle of the layout + ``html`` HTML present in the layout + ``css`` CSS present in the layout + ``tokens`` List of tokens which are associated with this room + ``script`` Javascript present in this layout + ``uri`` URI to query this layout + ========================= ================================================================================= + +* ``GET /api/v2/layout/`` + + Returns the layout by id: + + ========================= ================================================================================= + ``name`` Unique name for the layout + ``id`` ID of the layout + ``title`` Title of the layout + ``subtitle`` Subtitle of the layout + ``html`` HTML present in the layout + ``css`` CSS present in the layout + ``tokens`` List of tokens which are associated with this room + ``script`` Javascript present in this layout + ========================= ================================================================================= + +* ``POST /api/v2/layout`` + + Creates a layout from the layout data. See :ref:`slurk_layouts` for more parameters + +* ``PUT /api/v2/layout`` + + Updates the layout from the layout data. See :ref:`slurk_layouts` for more parameters + + Logging ------- diff --git a/docs/slurk_multibots.rst b/docs/slurk_multibots.rst index ec083c35..0ab8919f 100644 --- a/docs/slurk_multibots.rst +++ b/docs/slurk_multibots.rst @@ -63,7 +63,7 @@ The token for the bot is stored in ``CONCIERGE_BOT_TOKEN``: :: -H "Authorization: Token $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ - -d '{"room": "waiting_room", "message_text": true, "room_create": true, "user_room_join": true}' \ + -d '{"room": "waiting_room", "message_text": true, "room_create": true, "user_room_join": true, "user_room_leave": true}' \ localhost/api/v2/token | sed 's/^"\(.*\)"$/\1/') Now start the concierge bot: :: diff --git a/multibot.sh b/multibot.sh deleted file mode 100755 index bc159191..00000000 --- a/multibot.sh +++ /dev/null @@ -1,55 +0,0 @@ -ADMIN_TOKEN=00000000-0000-0000-0000-000000000000 - -WAITING_ROOM_LAYOUT=$(curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d "@sample_bots/concierge/layout.json" \ - localhost:5000/api/v2/layout | jq .id) - -curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d "{\"name\": \"waiting_room\", \"label\": \"Waiting Room\", \"layout\": $WAITING_ROOM_LAYOUT}" \ - localhost:5000/api/v2/room - -TASK_ID=$(curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d '{"name": "Echo Task", "num_users": 2}' \ - localhost:5000/api/v2/task | jq .id) - -curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d "{\"room\": \"waiting_room\", \"message_text\": true, \"task\": $TASK_ID}" \ - localhost:5000/api/v2/token | sed 's/^"\(.*\)"$/\1/' - -curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d "{\"room\": \"waiting_room\", \"message_text\": true, \"task\": $TASK_ID}" \ - localhost:5000/api/v2/token | sed 's/^"\(.*\)"$/\1/' - -ECHO_BOT_TOKEN=$(curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d '{"room": "waiting_room", "message_text": true, "room_create": true, "user_room_join": true}' \ - localhost:5000/api/v2/token | sed 's/^"\(.*\)"$/\1/') - -CONCIERGE_BOT_TOKEN=$(curl -X POST \ - -H "Authorization: Token $ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d '{"room": "waiting_room", "message_text": true, "room_create": true, "user_room_join": true}' \ - localhost:5000/api/v2/token | sed 's/^"\(.*\)"$/\1/') - -docker run -e TOKEN=${ECHO_BOT_TOKEN} --net="host" -e CHAT_PORT=5000 -e ECHO_TASK_ID=${TASK_ID} -d slurk/echo-bot -docker run -e TOKEN=${CONCIERGE_BOT_TOKEN} --net="host" -e CHAT_PORT=5000 slurk/concierge-bot - -docker stop -t 0 $(docker ps -aq) > /dev/null \ No newline at end of file