diff --git a/litecord/blueprints/admin_api/features.py b/litecord/blueprints/admin_api/features.py
index ca14309..b786692 100644
--- a/litecord/blueprints/admin_api/features.py
+++ b/litecord/blueprints/admin_api/features.py
@@ -17,33 +17,75 @@ along with this program. If not, see .
"""
-from quart import Blueprint, current_app as app
+from quart import Blueprint, current_app as app, jsonify, request
from litecord.auth import admin_check
from litecord.errors import BadRequest
+from litecord.schemas import validate, FEATURES
+from litecord.enums import Feature
bp = Blueprint('features_admin', __name__)
-FEATURES = [
- ''
-]
-@bp.route('//', methods=['PUT'])
-async def insert_feature(guild_id: int, feature: str):
+async def _features(guild_id: int):
+ return jsonify({
+ 'features': await app.storage.guild_features(guild_id)
+ })
+
+
+async def _update_features(guild_id: int, features: list):
+ await app.db.execute("""
+ UPDATE guilds
+ SET features = $1
+ WHERE id = $2
+ """, features, guild_id)
+
+
+@bp.route('//features', methods=['PATCH'])
+async def replace_features(guild_id: int):
+ """Replace the feature list in a guild"""
+ await admin_check()
+ j = validate(await request.get_json(), FEATURES)
+ features = j['features']
+
+ # yes, we need to pass it to a set and then to a list before
+ # doing anything, since the api client might just
+ # shove 200 repeated features to us.
+ await _update_features(guild_id, list(set(features)))
+ return await _features(guild_id)
+
+
+@bp.route('//features', methods=['PUT'])
+async def insert_features(guild_id: int):
"""Insert a feature on a guild."""
await admin_check()
+ j = validate(await request.get_json(), FEATURES)
- # TODO
- if feature not in FEATURES:
- raise BadRequest('invalid feature')
+ to_add = j['features']
+ features = await app.storage.guild_features(guild_id)
+ features = set(features)
- return '', 204
+ # i'm assuming set.add is mostly safe
+ for feature in to_add:
+ features.add(feature)
+
+ await _update_features(guild_id, list(features))
+ return await _features(guild_id)
@bp.route('//', methods=['DELETE'])
-async def remove_feature(guild_id: int, feature: str):
+async def remove_feature(guild_id: int):
"""Remove a feature from a guild"""
await admin_check()
- # TODO
- await app.db
- return '', 204
+ j = validate(await request.get_json(), FEATURES)
+ to_remove = j['features']
+ features = await app.storage.guild_features(guild_id)
+
+ for feature in to_remove:
+ try:
+ features.remove(feature)
+ except ValueError:
+ raise BadRequest('Trying to remove already removed feature.')
+
+ await _update_features(guild_id, features)
+ return await _features(guild_id)
diff --git a/litecord/enums.py b/litecord/enums.py
index 4107fd5..0a7c075 100644
--- a/litecord/enums.py
+++ b/litecord/enums.py
@@ -197,12 +197,27 @@ class RelationshipType(EasyEnum):
class MessageNotifications(EasyEnum):
+ """Message notifications"""
ALL = 0
MENTIONS = 1
NOTHING = 2
class PremiumType:
+ """Premium (Nitro) type."""
TIER_1 = 1
TIER_2 = 2
NONE = None
+
+
+class Feature(Enum):
+ """Guild features."""
+ invite_splash = 'INVITE_SPLASH'
+ vip = 'VIP_REGIONS'
+ vanity = 'VANITY_URL'
+ emoji = 'MORE_EMOJI'
+ verified = 'VERIFIED'
+
+ # unknown
+ commerce = 'COMMERCE'
+ news = 'NEWS'
diff --git a/litecord/schemas.py b/litecord/schemas.py
index 3683334..40499da 100644
--- a/litecord/schemas.py
+++ b/litecord/schemas.py
@@ -655,3 +655,10 @@ GET_MENTIONS = {
'everyone': {'coerce': bool, 'default': True},
'guild_id': {'coerce': int, 'required': False}
}
+
+FEATURES = {
+ 'features': {
+ 'type': 'list', 'required': True,
+ 'schema': {'type': 'guild_feature'}
+ }
+}
diff --git a/litecord/storage.py b/litecord/storage.py
index 0c8f6d4..d3b2257 100644
--- a/litecord/storage.py
+++ b/litecord/storage.py
@@ -146,6 +146,13 @@ class Storage:
WHERE username = $1 AND discriminator = $2
""", username, discriminator)
+ async def guild_features(self, guild_id: int) -> Optional[List[str]]:
+ """Get a list of guild features for the given guild."""
+ return await self.db.fetchval("""
+ SELECT features FROM guilds
+ WHERE id = $1
+ """, guild_id)
+
async def get_guild(self, guild_id: int, user_id=None) -> Optional[Dict]:
"""Get gulid payload."""
row = await self.db.fetchrow("""
diff --git a/litecord/types.py b/litecord/types.py
index 1a845f8..2c9c122 100644
--- a/litecord/types.py
+++ b/litecord/types.py
@@ -17,6 +17,8 @@ along with this program. If not, see .
"""
+from typing import Optional
+
# size units
KILOBYTES = 1024
@@ -45,5 +47,6 @@ class Color:
return self.value
-def timestamp_(dt):
+def timestamp_(dt) -> Optional[str]:
+ """safer version for dt.isoformat()"""
return f'{dt.isoformat()}+00:00' if dt else None