mirror of
https://github.com/TitanEmbeds/Titan.git
synced 2024-12-23 21:47:04 +01:00
Enhanced dashboard, bot adding handeling, list members, embed settings, etc
This commit is contained in:
parent
a6bc5f753e
commit
53c06ed050
@ -1,6 +1,7 @@
|
||||
from config import config
|
||||
from database import db
|
||||
from flask import Flask, render_template, request, session, url_for, redirect, jsonify
|
||||
from titanembeds.utils import cache
|
||||
import blueprints.api
|
||||
import blueprints.user
|
||||
import os
|
||||
@ -13,6 +14,7 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Suppress the warning/no
|
||||
app.secret_key = config['app-secret']
|
||||
|
||||
db.init_app(app)
|
||||
cache.init_app(app, config={'CACHE_TYPE': 'simple'})
|
||||
|
||||
app.register_blueprint(blueprints.api.api, url_prefix="/api", template_folder="/templates")
|
||||
app.register_blueprint(blueprints.user.user, url_prefix="/user", template_folder="/templates")
|
||||
@ -28,7 +30,7 @@ def post_set_username(guildid, channelid):
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return render_template("index.html.jinja2")
|
||||
return render_template("index.html.j2")
|
||||
|
||||
@app.route("/embed/<guildid>/<channelid>")
|
||||
def embed_get(guildid, channelid):
|
||||
|
@ -1,6 +1,6 @@
|
||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, AuthenticatedUsers
|
||||
from titanembeds.decorators import valid_session_required, discord_users_only
|
||||
from titanembeds.utils import discord_api, cache
|
||||
from titanembeds.utils import get_client_ipaddr, discord_api
|
||||
from flask import Blueprint, abort, jsonify, session, request
|
||||
from sqlalchemy import and_
|
||||
import random
|
||||
@ -39,20 +39,11 @@ def checkUserBanned(guild_id, ip_address=None):
|
||||
pass #todo: handle authenticated user banned status
|
||||
return banned
|
||||
|
||||
def get_client_ipaddr():
|
||||
if hasattr(request.headers, "X-Real-IP"): # pythonanywhere specific
|
||||
return request.headers['X-Real-IP']
|
||||
else: # general
|
||||
return request.remote_addr
|
||||
|
||||
def check_guild_existance(guild_id):
|
||||
dbGuild = Guilds.query.filter_by(guild_id=guild_id).first()
|
||||
if not dbGuild:
|
||||
return False
|
||||
guilds = cache.get('bot_guilds')
|
||||
if guilds is None:
|
||||
guilds = discord_api.get_all_guilds()
|
||||
cache.set('bot_guilds', guilds)
|
||||
guilds = discord_api.get_all_guilds()
|
||||
for guild in guilds:
|
||||
if guild_id == guild['id']:
|
||||
return True
|
||||
|
@ -2,7 +2,8 @@ from flask import Blueprint, request, redirect, jsonify, abort, session, url_for
|
||||
from requests_oauthlib import OAuth2Session
|
||||
from config import config
|
||||
from titanembeds.decorators import discord_users_only
|
||||
from titanembeds.utils import discord_api
|
||||
from titanembeds.utils import discord_api, cache, make_cache_key, make_guilds_cache_key
|
||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans
|
||||
|
||||
user = Blueprint("user", __name__)
|
||||
redirect_url = config['app-base-url'] + "/user/callback"
|
||||
@ -11,6 +12,9 @@ token_url = "https://discordapp.com/api/oauth2/token"
|
||||
avatar_base_url = "https://cdn.discordapp.com/avatars/"
|
||||
guild_icon_url = "https://cdn.discordapp.com/icons/"
|
||||
|
||||
def update_user_token(discord_token):
|
||||
session['user_keys'] = discord_token
|
||||
|
||||
def make_authenticated_session(token=None, state=None, scope=None):
|
||||
return OAuth2Session(
|
||||
client_id=config['client-id'],
|
||||
@ -18,6 +22,12 @@ def make_authenticated_session(token=None, state=None, scope=None):
|
||||
state=state,
|
||||
scope=scope,
|
||||
redirect_uri=url_for("user.callback", _external=True),
|
||||
auto_refresh_kwargs={
|
||||
'client_id': config['client-id'],
|
||||
'client_secret': config['client-secret'],
|
||||
},
|
||||
auto_refresh_url=token_url,
|
||||
token_updater=update_user_token,
|
||||
)
|
||||
|
||||
def discordrest_from_user(endpoint):
|
||||
@ -36,19 +46,50 @@ def get_current_authenticated_user():
|
||||
def user_has_permission(permission, index):
|
||||
return bool((int(permission) >> index) & 1)
|
||||
|
||||
@cache.cached(timeout=120, key_prefix=make_guilds_cache_key)
|
||||
def get_user_guilds():
|
||||
req = discordrest_from_user("/users/@me/guilds")
|
||||
return req
|
||||
|
||||
def get_user_managed_servers():
|
||||
guilds = get_user_guilds().json()
|
||||
guilds = get_user_guilds()
|
||||
if guilds.status_code != 200:
|
||||
print(guilds.text)
|
||||
print(guilds.headers)
|
||||
abort(guilds.status_code)
|
||||
guilds = guilds.json()
|
||||
filtered = []
|
||||
for guild in guilds:
|
||||
permission = guild['permissions'] # Manage Server, Ban Members, Kick Members
|
||||
if guild['owner'] or user_has_permission(permission, 5) or user_has_permission(permission, 2) or user_has_permission(permission, 1):
|
||||
filtered.append(guild)
|
||||
filtered = sorted(filtered, key=lambda guild: guild['name'])
|
||||
return filtered
|
||||
|
||||
def get_user_managed_servers_safe():
|
||||
guilds = get_user_managed_servers()
|
||||
if guilds:
|
||||
return guilds
|
||||
return []
|
||||
|
||||
def get_user_managed_servers_id():
|
||||
guilds = get_user_managed_servers_safe()
|
||||
ids=[]
|
||||
for guild in guilds:
|
||||
ids.append(guild['id'])
|
||||
return ids
|
||||
|
||||
def check_user_can_administrate_guild(guild_id):
|
||||
guilds = get_user_managed_servers_id()
|
||||
return guild_id in guilds
|
||||
|
||||
def check_user_permission(guild_id, id):
|
||||
guilds = get_user_managed_servers_safe()
|
||||
for guild in guilds:
|
||||
if guild['id'] == guild_id:
|
||||
return user_has_permission(guild['permissions'], id)
|
||||
return False
|
||||
|
||||
def generate_avatar_url(id, av):
|
||||
return avatar_base_url + str(id) + '/' + str(av) + '.jpg'
|
||||
|
||||
@ -90,28 +131,83 @@ def callback():
|
||||
session['username'] = user['username']
|
||||
session['avatar'] = generate_avatar_url(user['id'], user['avatar'])
|
||||
if session["redirect"]:
|
||||
return redirect(session["redirect"])
|
||||
redir = session["redirect"]
|
||||
session.pop('redirect', None)
|
||||
return redirect(redir)
|
||||
return redirect(url_for("user.dashboard"))
|
||||
|
||||
@user.route('/logout', methods=["GET"])
|
||||
def logout():
|
||||
redir = session.get("redirect", None)
|
||||
session.clear()
|
||||
if redir:
|
||||
session['redirect'] = redir
|
||||
return redirect(session['redirect'])
|
||||
return redirect(url_for("index"))
|
||||
|
||||
@user.route("/dashboard")
|
||||
@discord_users_only()
|
||||
def dashboard():
|
||||
return render_template("dashboard.html.jinja2", servers=get_user_managed_servers(), icon_generate=generate_guild_icon_url)
|
||||
guilds = get_user_managed_servers()
|
||||
if not guilds:
|
||||
session["redirect"] = url_for("user.dashboard")
|
||||
return redirect(url_for("user.logout"))
|
||||
return render_template("dashboard.html.j2", servers=guilds, icon_generate=generate_guild_icon_url)
|
||||
|
||||
@user.route("/administrate_guild/<guild_id>")
|
||||
@user.route("/administrate_guild/<guild_id>", methods=["GET"])
|
||||
@discord_users_only()
|
||||
def administrate_guild(guild_id):
|
||||
if not check_user_can_administrate_guild(guild_id):
|
||||
return redirect(url_for("user.dashboard"))
|
||||
guild = discord_api.get_guild(guild_id)
|
||||
if guild['code'] == 403:
|
||||
if guild['code'] != 200:
|
||||
return redirect(generate_bot_invite_url(guild_id))
|
||||
return str(guild)
|
||||
db_guild = Guilds.query.filter_by(guild_id=guild_id).first()
|
||||
if not db_guild:
|
||||
db_guild = Guilds(guild_id)
|
||||
db.session.add(db_guild)
|
||||
db.session.commit()
|
||||
permissions=[]
|
||||
if check_user_permission(guild_id, 5):
|
||||
permissions.append("Manage Embed Settings")
|
||||
if check_user_permission(guild_id, 2):
|
||||
permissions.append("Ban Members")
|
||||
if check_user_permission(guild_id, 1):
|
||||
permissions.append("Kick Members")
|
||||
all_members = db.session.query(UnauthenticatedUsers).filter(UnauthenticatedUsers.guild_id == guild_id).all()
|
||||
all_bans = db.session.query(UnauthenticatedBans).filter(UnauthenticatedBans.guild_id == guild_id).all()
|
||||
users = prepare_guild_members_list(all_members, all_bans)
|
||||
return render_template("administrate_guild.html.j2", guild=guild['content'], members=users, permissions=permissions)
|
||||
|
||||
@user.route('/me')
|
||||
@discord_users_only()
|
||||
def me():
|
||||
return jsonify(user=get_current_authenticated_user())
|
||||
|
||||
def prepare_guild_members_list(members, bans):
|
||||
all_users = []
|
||||
for member in members:
|
||||
user = {
|
||||
"id": member.id,
|
||||
"username": member.username,
|
||||
"discrim": member.discriminator,
|
||||
"ip": member.ip_address,
|
||||
"last_visit": member.last_timestamp,
|
||||
"kicked": member.revoked,
|
||||
"banned": False,
|
||||
"banned_timestamp": None,
|
||||
"banned_by": None,
|
||||
"banned_reason": None,
|
||||
"ban_lifted_by": None,
|
||||
}
|
||||
for banned in bans:
|
||||
if banned.ip_address == member.ip_address:
|
||||
if banned.lifter_id is None:
|
||||
user['banned'] = True
|
||||
user["banned_timestamp"] = banned.timestamp
|
||||
user['banned_by'] = banned.placer_id
|
||||
user['banned_reason'] = banned.reason
|
||||
user['ban_lifted_by'] = banned.lifter_id
|
||||
continue
|
||||
all_users.append(user)
|
||||
return all_users
|
||||
|
@ -8,7 +8,7 @@ class Guilds(db.Model):
|
||||
|
||||
def __init__(self, guild_id):
|
||||
self.guild_id = guild_id
|
||||
self.unauth_users = true # defaults to true
|
||||
self.unauth_users = True # defaults to true
|
||||
|
||||
def __repr__(self):
|
||||
return '<Guilds {0} {1}>'.format(self.id, self.guild_id)
|
||||
|
99
titanembeds/templates/administrate_guild.html.j2
Normal file
99
titanembeds/templates/administrate_guild.html.j2
Normal file
@ -0,0 +1,99 @@
|
||||
{% extends 'site_layout.html.j2' %}
|
||||
{% block title %}Administrate Guild: {{ guild['name'] }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Administrating: {{ guild['name'] }}</h1>
|
||||
<p class="flow-text">For this server, you are allowed the following actions:
|
||||
{% for permission in permissions %}
|
||||
{{ permission }}
|
||||
{% if not loop.last %}
|
||||
,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
.</p>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col s12">
|
||||
<h2 class="header">Embed URLs</h2>
|
||||
<div class="card horizontal black-text indigo lighten-5 z-depth-3 hoverable">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<p class="flow-text">Direct Link</p>
|
||||
<input disabled value="http://localhost:3000/embed/{{ guild['id'] }}" id="disabled" type="text"> <!-- Switch to url_for later -->
|
||||
<p class="flow-text">iFrame Embed</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if "Manage Embed Settings" in permissions %}
|
||||
<div class="col s12">
|
||||
<h2 class="header">Embed Settings</h2>
|
||||
<div class="card horizontal black-text indigo lighten-5 z-depth-3 hoverable">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<p class="flow-text">Unauthenticated (Guest) Users</p>
|
||||
<form action="#">
|
||||
<div class="switch">
|
||||
<label>
|
||||
Disable
|
||||
<input type="checkbox" id="unauth_users" name="unauth_users">
|
||||
<span class="lever"></span>
|
||||
Enable
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if "Ban Members" in permissions or "Kick Members" in permissions %}
|
||||
<div class="col s12">
|
||||
<h2 class="header">Moderate Unauthenticated Members</h2>
|
||||
<div class="card horizontal black-text indigo lighten-5 z-depth-3 hoverable">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<p class="flow-text">Select Action</p>
|
||||
<table class="striped responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Kick User</th>
|
||||
<th>Ban User</th>
|
||||
<th>Username</th>
|
||||
<th>Discrim</th>
|
||||
<th>Last Visit</th>
|
||||
<th>IP Address</th>
|
||||
<th>Banned Timestamp</th>
|
||||
<th>Banned by</th>
|
||||
<th>Banned Reason</th>
|
||||
<th>Ban Lifted by</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in members %}
|
||||
<tr>
|
||||
<td><a class="waves-effect waves-light btn orange" {% if "Kick Members" not in permissions %}disabled{% endif %} >Kick</a></td>
|
||||
<td><a class="waves-effect waves-light btn red" {% if "Ban Members" not in permissions %}disabled{% endif %} >Ban</a></td>
|
||||
<td>{{ member['username'] }}</td>
|
||||
<td>{{ member['discrim'] }}</td>
|
||||
<td>{{ member['last_visit'] }}</td>
|
||||
<td>{{ member['ip'] }}</td>
|
||||
<td>{{ member['banned_timestamp'] }}</td>
|
||||
<td>{{ member['banned_by'] }}</td>
|
||||
<td>{{ member['banned_reason'] }}</td>
|
||||
<td>{{ member['ban_lifted_by'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,4 +1,4 @@
|
||||
{% extends 'site_layout.html.jinja2' %}
|
||||
{% extends 'site_layout.html.j2' %}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -7,19 +7,19 @@
|
||||
<p>*List missing some servers? It's because you must have either <strong>Manage Server</strong>, <strong>Kick Members</strong>, or <strong>Ban Members</strong> permissions to modify embed settings.</p>
|
||||
<div class="row">
|
||||
{% for server in servers %}
|
||||
<div class="col s6">
|
||||
<div class="card-panel grey lighten-5 z-depth-1">
|
||||
<div class="col s4">
|
||||
<div class="card-panel indigo lighten-5 z-depth-3 hoverable">
|
||||
<div class="row valign-wrapper">
|
||||
<div class="col s2">
|
||||
<div class="col s5">
|
||||
{% if server.icon %}
|
||||
<img src="{{ icon_generate(server.id, server.icon) }}" alt="" class="circle responsive-img">
|
||||
{% else %}
|
||||
<span class="black-text">No icon :(</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col s10">
|
||||
<div class="col s7">
|
||||
<span class="black-text">
|
||||
<p class="flow-text">{{ server.name }}</p>
|
||||
<p class="flow-text truncate">{{ server.name }}</p>
|
||||
<br>
|
||||
<a class="waves-effect waves-light btn" href="{{url_for('user.administrate_guild', guild_id=server['id'])}}">Modify</a>
|
||||
</span>
|
@ -1,4 +1,4 @@
|
||||
{% extends 'site_layout.html.jinja2' %}
|
||||
{% extends 'site_layout.html.j2' %}
|
||||
{% block title %}Index{% endblock %}
|
||||
|
||||
{% block content %}
|
@ -1,6 +1,35 @@
|
||||
from werkzeug.contrib.cache import SimpleCache
|
||||
from titanembeds.discordrest import DiscordREST
|
||||
from flask import request, session
|
||||
from flask.ext.cache import Cache
|
||||
from config import config
|
||||
import random
|
||||
import string
|
||||
|
||||
discord_api = DiscordREST(config['bot-token'])
|
||||
cache = SimpleCache()
|
||||
cache = Cache()
|
||||
|
||||
def get_client_ipaddr():
|
||||
if hasattr(request.headers, "X-Real-IP"): # pythonanywhere specific
|
||||
return request.headers['X-Real-IP']
|
||||
else: # general
|
||||
return request.remote_addr
|
||||
|
||||
def generate_session_key():
|
||||
sess = session.get("cachestring", None)
|
||||
if not sess:
|
||||
rand_str = lambda n: ''.join([random.choice(string.lowercase) for i in xrange(n)])
|
||||
session['cachestring'] = rand_str(25)
|
||||
sess = session['cachestring']
|
||||
return sess #Totally unique
|
||||
|
||||
def make_cache_key(*args, **kwargs):
|
||||
path = request.path
|
||||
args = str(hash(frozenset(request.args.items())))
|
||||
ip = get_client_ipaddr()
|
||||
sess = generate_session_key()
|
||||
return (path + args + sess + ip).encode('utf-8')
|
||||
|
||||
def make_guilds_cache_key():
|
||||
sess = generate_session_key()
|
||||
ip = get_client_ipaddr()
|
||||
return (sess + ip + "user_guilds").encode('utf-8')
|
||||
|
Loading…
Reference in New Issue
Block a user