Skip to content

Commit

Permalink
Merge pull request #49 from avrae/Jokeen/paint-it-black
Browse files Browse the repository at this point in the history
Paint it black (Service Edition)
  • Loading branch information
zhudotexe committed Apr 12, 2022
2 parents ca2a47d + 083f16b commit e2d9cb0
Show file tree
Hide file tree
Showing 37 changed files with 912 additions and 737 deletions.
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: stable
hooks:
- id: black
language_version: python3
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,21 @@ The service should now be accessible at http://localhost:58000.
2. Deploy it in whatever fashion your production environment requires.

The service should now be accessible at http://IP.ADDRESS:8000.



### Committing, Formatting, and Linting

Avrae uses [Black](https://black.readthedocs.io/) to format and lint its Python code.
Black is automatically run on every commit via pre-commit hook, and takes its configuration options from the `pyproject.toml` file.

The pre-commit hook is installed by by running `pre-commit install` from the repo root.
The hook's configuration is governed by the `.pre-commit-config.yaml` file.

#### Dependencies

In order to run `pre-commit` or `black`, they must be installed.
These dependencies are contained within the `requirements.txt` file, and can be installed like so:

```bash
(venv) $ pip install -r requirements.txt
```
43 changes: 23 additions & 20 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@
from lib.utils import jsonify

if config.SENTRY_DSN is not None:
sentry_sdk.init(
dsn=config.SENTRY_DSN,
environment=config.ENVIRONMENT,
integrations=[FlaskIntegration()]
)
sentry_sdk.init(dsn=config.SENTRY_DSN, environment=config.ENVIRONMENT, integrations=[FlaskIntegration()])

# app init
app = Flask(__name__)
Expand All @@ -39,7 +35,7 @@
CORS(app)

# logging init
log_formatter = logging.Formatter('%(levelname)s:%(name)s: %(message)s')
log_formatter = logging.Formatter("%(levelname)s:%(name)s: %(message)s")
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(log_formatter)
logger = logging.getLogger()
Expand All @@ -48,47 +44,54 @@


# routes
@app.route('/', methods=["GET"])
@app.route("/", methods=["GET"])
def hello_world():
return 'Hello World!'
return "Hello World!"


@app.route('/user', methods=["GET"])
@app.route("/user", methods=["GET"])
@requires_auth
def user(the_user):
info = get_user_info(discord_token_for(the_user.id))
data = {
"username": info.username,
"discriminator": info.discriminator,
"id": info.id,
"avatarUrl": info.get_avatar_url()
"avatarUrl": info.get_avatar_url(),
}
return jsonify(data)


@app.route('/userStats', methods=["GET"])
@app.route("/userStats", methods=["GET"])
@requires_auth
def user_stats(the_user):
data = {
"numCharacters": app.mdb.characters.count_documents({"owner": the_user.id}),
"numCustomizations": sum((app.mdb.aliases.count_documents({"owner": the_user.id}),
app.mdb.snippets.count_documents({"owner": the_user.id})))
"numCustomizations": sum(
(
app.mdb.aliases.count_documents({"owner": the_user.id}),
app.mdb.snippets.count_documents({"owner": the_user.id}),
)
),
}
return jsonify(data)


@app.route('/roll', methods=['GET'])
@app.route("/roll", methods=["GET"])
def roll():
to_roll = request.args.get('dice') or '1d20'
adv = request.args.get('adv', 0)
to_roll = request.args.get("dice") or "1d20"
adv = request.args.get("adv", 0)
try:
rolled = d20.roll(to_roll, advantage=adv)
except Exception as e:
result = {'success': False, 'error': str(e)}
result = {"success": False, "error": str(e)}
else:
result = {
'success': True, 'total': rolled.total, 'result': rolled.result, 'is_crit': rolled.crit,
'repr': repr(rolled)
"success": True,
"total": rolled.total,
"result": rolled.result,
"is_crit": rolled.crit,
"repr": repr(rolled),
}

return jsonify(result)
Expand All @@ -108,5 +111,5 @@ def roll():
compendium.reload(mdb)
elasticsearch.init()

if __name__ == '__main__':
if __name__ == "__main__":
app.run()
4 changes: 2 additions & 2 deletions blueprints/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

from lib.utils import jsonify

bot = Blueprint('bot', __name__)
bot = Blueprint("bot", __name__)


def requires_secret(f):
@wraps(f)
def decorated_function(*args, **kwargs):
app_3pp_secret = request.headers.get('authorization')
app_3pp_secret = request.headers.get("authorization")
if current_app.mdb.api_apps.find_one({"key": app_3pp_secret}) is None:
return abort(403)
return f(*args, **kwargs)
Expand Down
29 changes: 13 additions & 16 deletions blueprints/characters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,37 @@
from lib.utils import error, jsonify, success
from lib.validation import Automation, parse_validation_error, str1024, str255

characters = Blueprint('characters', __name__)
characters = Blueprint("characters", __name__)


@characters.route('', methods=["GET"])
@characters.route("", methods=["GET"])
@requires_auth
def character_list(user):
data = list(current_app.mdb.characters.find({"owner": user.id}))
return jsonify(data)


@characters.route('/meta', methods=["GET"])
@characters.route("/meta", methods=["GET"])
@requires_auth
def meta(user):
data = list(
current_app.mdb.characters.find(
{"owner": user.id},
["upstream", "active", "name", "description", "image", "levels",
"import_version", "overrides"]
["upstream", "active", "name", "description", "image", "levels", "import_version", "overrides"],
)
)
return jsonify(data)


@characters.route('/<upstream>/attacks', methods=["GET"])
@characters.route("/<upstream>/attacks", methods=["GET"])
@requires_auth
def attacks(user, upstream):
"""Returns a character's overriden attacks."""
data = current_app.mdb.characters.find_one(
{"owner": user.id, "upstream": upstream},
["overrides"]
)
return jsonify(data['overrides']['attacks'])
data = current_app.mdb.characters.find_one({"owner": user.id, "upstream": upstream}, ["overrides"])
return jsonify(data["overrides"]["attacks"])


@characters.route('/<upstream>/attacks', methods=["PUT"])
@characters.route("/<upstream>/attacks", methods=["PUT"])
@requires_auth
def put_attacks(user, upstream):
"""Sets a character's attack overrides. Must PUT a list of attacks."""
Expand All @@ -58,7 +54,7 @@ def put_attacks(user, upstream):
# write
response = current_app.mdb.characters.update_one(
{"owner": user.id, "upstream": upstream},
{"$set": {"overrides.attacks": [a.dict(exclude_none=True, exclude_defaults=True) for a in validated_attacks]}}
{"$set": {"overrides.attacks": [a.dict(exclude_none=True, exclude_defaults=True) for a in validated_attacks]}},
)

# respond
Expand All @@ -67,7 +63,7 @@ def put_attacks(user, upstream):
return success("Attacks updated")


@characters.route('/attacks/validate', methods=["POST"])
@characters.route("/attacks/validate", methods=["POST"])
def validate_attacks():
reqdata = request.json
if not isinstance(reqdata, list):
Expand All @@ -82,9 +78,9 @@ def validate_attacks():
return success("OK")


@characters.route('/attacks/srd', methods=['GET'])
@characters.route("/attacks/srd", methods=["GET"])
def srd_attacks():
with open('static/template-attacks.json') as f:
with open("static/template-attacks.json") as f:
_items = json.load(f)
return jsonify(_items)

Expand All @@ -107,6 +103,7 @@ def dict(self, *args, **kwargs):

class AttackList(BaseModel):
"""Helper type used for validation"""

__root__: List[Attack]

def dict(self, *args, **kwargs):
Expand Down
62 changes: 33 additions & 29 deletions blueprints/customizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from lib.auth import requires_auth
from lib.utils import jsonify

customizations = Blueprint('customizations', __name__)
customizations = Blueprint("customizations", __name__)


@customizations.route("", methods=["GET"])
Expand All @@ -14,7 +14,7 @@ def customization_list(user):
data = {
"aliases": list(current_app.mdb.aliases.find({"owner": user.id})),
"snippets": list(current_app.mdb.snippets.find({"owner": user.id})),
"uvars": list(current_app.mdb.uvars.find({"owner": user.id}))
"uvars": list(current_app.mdb.uvars.find({"owner": user.id})),
}
return jsonify(data)

Expand All @@ -32,19 +32,20 @@ def alias_update(user, name):
data = request.json
if data is None:
return "No data found", 400
if 'commands' not in data:
if "commands" not in data:
return "Missing commands field", 400
if not data['commands']:
if not data["commands"]:
return "Commands cannot be blank", 400
if " " in name:
return "Name cannot contain whitespace", 400
if name in current_app.rdb.jget("default_commands", []):
return "Alias is already built-in", 409
if len(data['commands']) > 4000:
if len(data["commands"]) > 4000:
return "Alias commands must be less than 4KB", 400

current_app.mdb.aliases.update_one({"owner": user.id, "name": name}, {"$set": {"commands": data['commands']}},
upsert=True)
current_app.mdb.aliases.update_one(
{"owner": user.id, "name": name}, {"$set": {"commands": data["commands"]}}, upsert=True
)
return "Alias updated."


Expand All @@ -70,19 +71,20 @@ def snippet_update(user, name):
data = request.json
if data is None:
return "No data found", 400
if 'snippet' not in data:
if "snippet" not in data:
return "Missing snippet field", 400
if not data['snippet']:
if not data["snippet"]:
return "Snippet cannot be blank", 400
if " " in name:
return "Name cannot contain whitespace", 400
if len(data['snippet']) > 2000:
if len(data["snippet"]) > 2000:
return "Snippet must be less than 2KB", 400
if len(name) < 2:
return "Name must be at least 2 characters", 400

current_app.mdb.snippets.update_one({"owner": user.id, "name": name}, {"$set": {"snippet": data['snippet']}},
upsert=True)
current_app.mdb.snippets.update_one(
{"owner": user.id, "name": name}, {"$set": {"snippet": data["snippet"]}}, upsert=True
)
return "Snippet updated."


Expand All @@ -108,14 +110,14 @@ def uvar_update(user, name):
data = request.json
if data is None:
return "No data found", 400
if 'value' not in data:
if "value" not in data:
return "Missing value field", 400
if not data['value']:
if not data["value"]:
return "Value cannot be blank", 400
if len(data['value']) > 4000:
if len(data["value"]) > 4000:
return "Value must be less than 4KB", 400

current_app.mdb.uvars.update_one({"owner": user.id, "name": name}, {"$set": {"value": data['value']}}, upsert=True)
current_app.mdb.uvars.update_one({"owner": user.id, "name": name}, {"$set": {"value": data["value"]}}, upsert=True)
return "Uvar updated."


Expand All @@ -131,8 +133,10 @@ def uvar_delete(user, name):
@customizations.route("/gvars", methods=["GET"])
@requires_auth
def gvar_list(user):
data = {"owned": list(current_app.mdb.gvars.find({"owner": user.id})),
"editable": list(current_app.mdb.gvars.find({"editors": user.id}))}
data = {
"owned": list(current_app.mdb.gvars.find({"owner": user.id})),
"editable": list(current_app.mdb.gvars.find({"editors": user.id})),
}
return jsonify(data)


Expand All @@ -156,17 +160,17 @@ def gvar_new(user):
data = request.json
if data is None:
return "No data found", 400
if 'value' not in data:
if "value" not in data:
return "Missing value field", 400
if len(data['value']) > 100000:
if len(data["value"]) > 100000:
return "Gvars must be less than 100KB", 400
key = str(uuid.uuid4())
gvar = {
"owner": user.id,
"key": key,
"owner_name": f"{user.username}#{user.discriminator}",
"value": data['value'],
"editors": []
"value": data["value"],
"editors": [],
}
current_app.mdb.gvars.insert_one(gvar)
return f"Gvar {key} created."
Expand All @@ -186,28 +190,28 @@ def get_specific_gvar(_, key):
@requires_auth
def gvar_update(user, key):
data = request.json
gvar = current_app.mdb.gvars.find_one({"key": key}, ['owner', 'editors'])
gvar = current_app.mdb.gvars.find_one({"key": key}, ["owner", "editors"])
if data is None:
return "No data found", 400
if 'value' not in data:
if "value" not in data:
return "Missing value field", 400
if gvar is None:
return "Gvar not found", 404
if gvar['owner'] != user.id and user.id not in gvar.get('editors', []):
if gvar["owner"] != user.id and user.id not in gvar.get("editors", []):
return "You do not have permission to edit this gvar", 403
if len(data['value']) > 100000:
if len(data["value"]) > 100000:
return "Gvars must be less than 100KB", 400
current_app.mdb.gvars.update_one({"key": key}, {"$set": {"value": data['value']}})
current_app.mdb.gvars.update_one({"key": key}, {"$set": {"value": data["value"]}})
return "Gvar updated."


@customizations.route("/gvars/<key>", methods=["DELETE"])
@requires_auth
def gvar_delete(user, key):
gvar = current_app.mdb.gvars.find_one({"key": key}, ['owner'])
gvar = current_app.mdb.gvars.find_one({"key": key}, ["owner"])
if gvar is None:
return "Gvar not found", 404
if gvar['owner'] != user.id:
if gvar["owner"] != user.id:
return "You do not have permission to delete this gvar", 403
current_app.mdb.gvars.delete_one({"key": key})
return "Gvar deleted."
Loading

0 comments on commit e2d9cb0

Please sign in to comment.