mirror of https://gitlab.com/litecord/litecord.git
186 lines
4.9 KiB
Python
186 lines
4.9 KiB
Python
from quart import Blueprint, request, current_app as app, jsonify
|
|
|
|
from litecord.blueprints.auth import token_check
|
|
from litecord.blueprints.checks import guild_owner_check
|
|
|
|
from litecord.schemas import validate, GUILD_PRUNE
|
|
|
|
bp = Blueprint('guild_moderation', __name__)
|
|
|
|
|
|
async def remove_member(guild_id: int, member_id: int):
|
|
"""Do common tasks related to deleting a member from the guild,
|
|
such as dispatching GUILD_DELETE and GUILD_MEMBER_REMOVE."""
|
|
|
|
await app.db.execute("""
|
|
DELETE FROM members
|
|
WHERE guild_id = $1 AND user_id = $2
|
|
""", guild_id, member_id)
|
|
|
|
await app.dispatcher.dispatch_user(member_id, 'GUILD_DELETE', {
|
|
'guild_id': guild_id,
|
|
'unavailable': False,
|
|
})
|
|
|
|
await app.dispatcher.unsub('guild', guild_id, member_id)
|
|
|
|
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_MEMBER_REMOVE', {
|
|
'guild_id': str(guild_id),
|
|
'user': await app.storage.get_user(member_id),
|
|
})
|
|
|
|
|
|
async def remove_member_multi(guild_id: int, members: list):
|
|
"""Remove multiple members."""
|
|
for member_id in members:
|
|
await remove_member(guild_id, member_id)
|
|
|
|
|
|
@bp.route('/<int:guild_id>/members/<int:member_id>', methods=['DELETE'])
|
|
async def kick_guild_member(guild_id, member_id):
|
|
"""Remove a member from a guild."""
|
|
user_id = await token_check()
|
|
|
|
# TODO: check KICK_MEMBERS permission
|
|
await guild_owner_check(user_id, guild_id)
|
|
await remove_member(guild_id, member_id)
|
|
return '', 204
|
|
|
|
|
|
@bp.route('/<int:guild_id>/bans', methods=['GET'])
|
|
async def get_bans(guild_id):
|
|
user_id = await token_check()
|
|
|
|
# TODO: check BAN_MEMBERS permission
|
|
await guild_owner_check(user_id, guild_id)
|
|
|
|
bans = await app.db.fetch("""
|
|
SELECT user_id, reason
|
|
FROM bans
|
|
WHERE bans.guild_id = $1
|
|
""", guild_id)
|
|
|
|
res = []
|
|
|
|
for ban in bans:
|
|
res.append({
|
|
'reason': ban['reason'],
|
|
'user': await app.storage.get_user(ban['user_id'])
|
|
})
|
|
|
|
return jsonify(res)
|
|
|
|
|
|
@bp.route('/<int:guild_id>/bans/<int:member_id>', methods=['PUT'])
|
|
async def create_ban(guild_id, member_id):
|
|
user_id = await token_check()
|
|
|
|
# TODO: check BAN_MEMBERS permission
|
|
await guild_owner_check(user_id, guild_id)
|
|
|
|
j = await request.get_json()
|
|
|
|
await app.db.execute("""
|
|
INSERT INTO bans (guild_id, user_id, reason)
|
|
VALUES ($1, $2, $3)
|
|
""", guild_id, member_id, j.get('reason', ''))
|
|
|
|
await remove_member(guild_id, member_id)
|
|
|
|
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_BAN_ADD', {
|
|
'guild_id': str(guild_id),
|
|
'user': await app.storage.get_user(member_id)
|
|
})
|
|
|
|
return '', 204
|
|
|
|
|
|
@bp.route('/<int:guild_id>/bans/<int:banned_id>', methods=['DELETE'])
|
|
async def remove_ban(guild_id, banned_id):
|
|
user_id = await token_check()
|
|
|
|
# TODO: check BAN_MEMBERS permission
|
|
await guild_owner_check(guild_id, user_id)
|
|
|
|
res = await app.db.execute("""
|
|
DELETE FROM bans
|
|
WHERE guild_id = $1 AND user_id = $@
|
|
""", guild_id, banned_id)
|
|
|
|
# we don't really need to dispatch GUILD_BAN_REMOVE
|
|
# when no bans were actually removed.
|
|
if res == 'DELETE 0':
|
|
return '', 204
|
|
|
|
await app.dispatcher.dispatch_guild(guild_id, 'GUILD_BAN_REMOVE', {
|
|
'guild_id': str(guild_id),
|
|
'user': await app.storage.get_user(banned_id)
|
|
})
|
|
|
|
return '', 204
|
|
|
|
|
|
async def get_prune(guild_id: int, days: int) -> list:
|
|
"""Get all members in a guild that:
|
|
|
|
- did not login in ``days`` days.
|
|
- don't have any roles.
|
|
"""
|
|
# a good solution would be in pure sql.
|
|
member_ids = await app.db.fetch(f"""
|
|
SELECT id
|
|
FROM users
|
|
JOIN members
|
|
ON members.guild_id = $1 AND members.user_id = users.id
|
|
WHERE users.last_session < (now() - (interval '{days} days'))
|
|
""", guild_id)
|
|
|
|
member_ids = [r['id'] for r in member_ids]
|
|
members = []
|
|
|
|
for member_id in member_ids:
|
|
role_count = await app.db.fetchval("""
|
|
SELECT COUNT(*)
|
|
FROM member_roles
|
|
WHERE guild_id = $1 AND user_id = $2
|
|
""", guild_id, member_id)
|
|
|
|
if role_count == 0:
|
|
members.append(member_id)
|
|
|
|
return members
|
|
|
|
|
|
@bp.route('/<int:guild_id>/prune', methods=['GET'])
|
|
async def get_guild_prune_count(guild_id):
|
|
user_id = await token_check()
|
|
|
|
# TODO: check KICK_MEMBERS
|
|
await guild_owner_check(user_id, guild_id)
|
|
|
|
j = validate(await request.get_json(), GUILD_PRUNE)
|
|
days = j['days']
|
|
member_ids = await get_prune(guild_id, days)
|
|
|
|
return jsonify({
|
|
'pruned': len(member_ids),
|
|
})
|
|
|
|
|
|
@bp.route('/<int:guild_id>/prune', methods=['POST'])
|
|
async def begin_guild_prune(guild_id):
|
|
user_id = await token_check()
|
|
|
|
# TODO: check KICK_MEMBERS
|
|
await guild_owner_check(user_id, guild_id)
|
|
|
|
j = validate(await request.get_json(), GUILD_PRUNE)
|
|
days = j['days']
|
|
member_ids = await get_prune(guild_id, days)
|
|
|
|
app.loop.create_task(remove_member_multi(guild_id, member_ids))
|
|
|
|
return jsonify({
|
|
'pruned': len(member_ids)
|
|
})
|