mirror of
				https://github.com/TitanEmbeds/Titan.git
				synced 2025-11-04 15:57:02 +01:00 
			
		
		
		
	Add user cards and badges, Closes #25
This commit is contained in:
		@@ -4,6 +4,7 @@ from functools import wraps
 | 
			
		||||
from titanembeds.database import db, get_administrators_list, Cosmetics, Guilds, UnauthenticatedUsers, UnauthenticatedBans, TitanTokens, TokenTransactions, get_titan_token, set_titan_token
 | 
			
		||||
from titanembeds.oauth import generate_guild_icon_url
 | 
			
		||||
import datetime
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
admin = Blueprint("admin", __name__)
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +40,7 @@ def cosmetics_post():
 | 
			
		||||
    css = request.form.get("css", None)
 | 
			
		||||
    css_limit = int(request.form.get("css_limit", 0))
 | 
			
		||||
    guest_icon = request.form.get("guest_icon", None)
 | 
			
		||||
    badges = request.form.get("badges", None)
 | 
			
		||||
    entry = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first()
 | 
			
		||||
    if entry:
 | 
			
		||||
        abort(409)
 | 
			
		||||
@@ -51,6 +53,11 @@ def cosmetics_post():
 | 
			
		||||
    if guest_icon is not None:
 | 
			
		||||
        guest_icon = guest_icon.lower() == "true"
 | 
			
		||||
        user.guest_icon = guest_icon
 | 
			
		||||
    if badges is not None:
 | 
			
		||||
        badges = badges.split(",")
 | 
			
		||||
        if badges == [""]:
 | 
			
		||||
            badges = []
 | 
			
		||||
        user.badges = json.dumps(badges)
 | 
			
		||||
    db.session.add(user)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return ('', 204)
 | 
			
		||||
@@ -77,6 +84,7 @@ def cosmetics_patch():
 | 
			
		||||
    css = request.form.get("css", None)
 | 
			
		||||
    css_limit = request.form.get("css_limit", None)
 | 
			
		||||
    guest_icon = request.form.get("guest_icon", None)
 | 
			
		||||
    badges = request.form.get("badges", None)
 | 
			
		||||
    entry = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first()
 | 
			
		||||
    if not entry:
 | 
			
		||||
        abort(409)
 | 
			
		||||
@@ -88,8 +96,14 @@ def cosmetics_patch():
 | 
			
		||||
    if guest_icon:
 | 
			
		||||
        guest_icon = guest_icon.lower() == "true"
 | 
			
		||||
        entry.guest_icon = guest_icon
 | 
			
		||||
    if badges is not None:
 | 
			
		||||
        badges = badges.split(",")
 | 
			
		||||
        if badges == [""]:
 | 
			
		||||
            badges = []
 | 
			
		||||
        entry.badges = json.dumps(badges)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    return ('', 204)
 | 
			
		||||
    
 | 
			
		||||
def prepare_guild_members_list(members, bans):
 | 
			
		||||
    all_users = []
 | 
			
		||||
    ip_pool = []
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, AuthenticatedUsers, KeyValueProperties, GuildMembers, Messages, get_channel_messages, list_all_guild_members, get_guild_member, get_administrators_list
 | 
			
		||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, AuthenticatedUsers, KeyValueProperties, GuildMembers, Messages, get_channel_messages, list_all_guild_members, get_guild_member, get_administrators_list, get_badges
 | 
			
		||||
from titanembeds.decorators import valid_session_required, discord_users_only
 | 
			
		||||
from titanembeds.utils import check_guild_existance, guild_accepts_visitors, guild_query_unauth_users_bool, get_client_ipaddr, discord_api, rate_limiter, channel_ratelimit_key, guild_ratelimit_key, user_unauthenticated, checkUserRevoke, checkUserBanned, update_user_status, check_user_in_guild, get_guild_channels, guild_webhooks_enabled, guild_unauthcaptcha_enabled
 | 
			
		||||
from titanembeds.oauth import user_has_permission, generate_avatar_url, check_user_can_administrate_guild
 | 
			
		||||
@@ -429,6 +429,10 @@ def create_authenticated_user():
 | 
			
		||||
            response.status_code = 403
 | 
			
		||||
            return response
 | 
			
		||||
 | 
			
		||||
@api.route("/badges/<user_id>")
 | 
			
		||||
def badges(user_id):
 | 
			
		||||
    return jsonify(get_badges(user_id))
 | 
			
		||||
 | 
			
		||||
def canCleanupDB():
 | 
			
		||||
    canclean = False
 | 
			
		||||
    if request.form.get("secret", None) == config['app-secret']:
 | 
			
		||||
@@ -437,6 +441,7 @@ def canCleanupDB():
 | 
			
		||||
        if session['user_id'] in get_administrators_list():
 | 
			
		||||
            canclean = True
 | 
			
		||||
    return canclean
 | 
			
		||||
    
 | 
			
		||||
@api.route("/cleanup-db", methods=["DELETE"])
 | 
			
		||||
def cleanup_keyval_db():
 | 
			
		||||
    if canCleanupDB():
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ from flask import current_app as app
 | 
			
		||||
from flask_socketio import emit
 | 
			
		||||
from config import config
 | 
			
		||||
from titanembeds.decorators import discord_users_only
 | 
			
		||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, Cosmetics, UserCSS, Patreon, set_titan_token, get_titan_token
 | 
			
		||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, Cosmetics, UserCSS, Patreon, set_titan_token, get_titan_token, add_badge
 | 
			
		||||
from titanembeds.oauth import authorize_url, token_url, make_authenticated_session, get_current_authenticated_user, get_user_managed_servers, check_user_can_administrate_guild, check_user_permission, generate_avatar_url, generate_guild_icon_url, generate_bot_invite_url
 | 
			
		||||
import time
 | 
			
		||||
import datetime
 | 
			
		||||
@@ -431,6 +431,7 @@ def donate_confirm():
 | 
			
		||||
        action = "PAYPAL {}".format(trans_id)
 | 
			
		||||
        set_titan_token(session["user_id"], tokens, action)
 | 
			
		||||
        session["tokens"] = get_titan_token(session["user_id"])
 | 
			
		||||
        add_badge(session["user_id"], "supporter")
 | 
			
		||||
        return redirect(url_for('user.donate_thanks', transaction=trans_id))
 | 
			
		||||
    else:
 | 
			
		||||
        return redirect(url_for('index'))
 | 
			
		||||
@@ -555,6 +556,7 @@ def patreon_sync_post():
 | 
			
		||||
    db.session.add(dbpatreon)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
    set_titan_token(session["user_id"], usr["titan"]["eligible_tokens"], "PATREON {} [{}]".format(usr["attributes"]["full_name"], usr["id"]))
 | 
			
		||||
    add_badge(session["user_id"], "supporter")
 | 
			
		||||
    session["tokens"] = get_titan_token(session["user_id"])
 | 
			
		||||
    return ('', 204)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ from .authenticated_users import AuthenticatedUsers
 | 
			
		||||
from .guild_members import GuildMembers, list_all_guild_members, get_guild_member
 | 
			
		||||
from .keyvalue_properties import KeyValueProperties, set_keyvalproperty, get_keyvalproperty, getexpir_keyvalproperty, setexpir_keyvalproperty, ifexists_keyvalproperty, delete_keyvalproperty
 | 
			
		||||
from .messages import Messages, get_channel_messages
 | 
			
		||||
from .cosmetics import Cosmetics
 | 
			
		||||
from .cosmetics import Cosmetics, set_badges, get_badges, add_badge, remove_badge
 | 
			
		||||
from .user_css import UserCSS
 | 
			
		||||
from .administrators import Administrators, get_administrators_list
 | 
			
		||||
from .titan_tokens import TitanTokens, get_titan_token
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
from titanembeds.database import db
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
class Cosmetics(db.Model):
 | 
			
		||||
    __tablename__ = "cosmetics"
 | 
			
		||||
@@ -7,6 +8,7 @@ class Cosmetics(db.Model):
 | 
			
		||||
    css = db.Column(db.Boolean(), nullable=False)                   # If they can create/edit custom CSS
 | 
			
		||||
    css_limit = db.Column(db.Integer, nullable=False, server_default="0") # Custom CSS Limit
 | 
			
		||||
    guest_icon = db.Column(db.Boolean(), nullable=False, server_default=db.false()) # If they can set the guest icon for all guilds
 | 
			
		||||
    badges = db.Column(db.String(255), nullable=False, server_default="[]") # JSON list of all the badges the user has
 | 
			
		||||
    
 | 
			
		||||
    def __init__(self, user_id, **kwargs):
 | 
			
		||||
        self.user_id = user_id
 | 
			
		||||
@@ -24,4 +26,35 @@ class Cosmetics(db.Model):
 | 
			
		||||
        if "guest_icon" in kwargs:
 | 
			
		||||
            self.guest_icon = kwargs["guest_icon"]
 | 
			
		||||
        else:
 | 
			
		||||
            self.guest_icon = False
 | 
			
		||||
            self.guest_icon = False
 | 
			
		||||
        
 | 
			
		||||
        if "badges" in kwargs:
 | 
			
		||||
            self.badges = json.dumps(kwargs["badges"])
 | 
			
		||||
        else:
 | 
			
		||||
            self.badges = "[]"
 | 
			
		||||
 | 
			
		||||
def set_badges(user_id, badges):
 | 
			
		||||
    usr = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first()
 | 
			
		||||
    if not usr:
 | 
			
		||||
        usr = Cosmetics(user_id)
 | 
			
		||||
    usr.badges = json.dumps(badges)
 | 
			
		||||
    db.session.add(usr)
 | 
			
		||||
    db.session.commit()
 | 
			
		||||
 | 
			
		||||
def get_badges(user_id):
 | 
			
		||||
    usr = db.session.query(Cosmetics).filter(Cosmetics.user_id == user_id).first()
 | 
			
		||||
    if usr:
 | 
			
		||||
        return json.loads(usr.badges)
 | 
			
		||||
    return []
 | 
			
		||||
 | 
			
		||||
def add_badge(user_id, name):
 | 
			
		||||
    bgs = get_badges(user_id)
 | 
			
		||||
    if name not in bgs:
 | 
			
		||||
        bgs.append(name)
 | 
			
		||||
        set_badges(user_id, bgs)
 | 
			
		||||
 | 
			
		||||
def remove_badge(user_id, name):
 | 
			
		||||
    bgs = get_badges(user_id)
 | 
			
		||||
    if name in bgs:
 | 
			
		||||
        bgs.remove(name)
 | 
			
		||||
        set_badges(user_id, bgs)
 | 
			
		||||
@@ -679,6 +679,73 @@ p.mentioned span.chatmessage {
 | 
			
		||||
  top: -5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .identity {
 | 
			
		||||
  display: inline;
 | 
			
		||||
  vertical-align: sub;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .identity .hash, #usercard .identity .discriminator {
 | 
			
		||||
  font-size: 1.30rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .bottag {
 | 
			
		||||
  background-color: #5DADE2;
 | 
			
		||||
  padding: 5px;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
  margin-left: 7px;
 | 
			
		||||
  font-size: 10pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .role .bubble {
 | 
			
		||||
  color: #cacbce;
 | 
			
		||||
  border: 1px solid;
 | 
			
		||||
  border-color: inherit;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .role .text, #usercard .role .color {
 | 
			
		||||
  margin-left: 5px;
 | 
			
		||||
  margin-right: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .role .color {
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  width: 10px;
 | 
			
		||||
  height: 10px;
 | 
			
		||||
  background-color: #cacbce;
 | 
			
		||||
  margin-right: 0;
 | 
			
		||||
  border-radius: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .role .text {
 | 
			
		||||
  color: lightgray;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .game {
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .game .text {
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .badges .star {
 | 
			
		||||
  color: yellow;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .badges .supporter {
 | 
			
		||||
  color: limegreen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .badges .administrator {
 | 
			
		||||
  color: hotpink;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#usercard .badges .partner {
 | 
			
		||||
  color: orange;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#google-recaptcha {
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  width: 302px;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,13 @@
 | 
			
		||||
/* global $, Materialize, location */
 | 
			
		||||
 | 
			
		||||
function postForm(user_id, css, css_limit, guest_icon) {
 | 
			
		||||
function postForm(user_id, css, css_limit, guest_icon, badges) {
 | 
			
		||||
    if (css_limit == "") {
 | 
			
		||||
        css_limit = 0;
 | 
			
		||||
    }
 | 
			
		||||
    var funct = $.ajax({
 | 
			
		||||
        dataType: "json",
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        data: {"user_id": user_id, "css": css, "css_limit": css_limit, "guest_icon": guest_icon}
 | 
			
		||||
        data: {"user_id": user_id, "css": css, "css_limit": css_limit, "guest_icon": guest_icon, "badges": badges}
 | 
			
		||||
    });
 | 
			
		||||
    return funct.promise();
 | 
			
		||||
}
 | 
			
		||||
@@ -32,6 +32,7 @@ function patchForm(user_id, param) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$(function() {
 | 
			
		||||
    $('select').material_select();
 | 
			
		||||
    $("#new_submit").click(function () {
 | 
			
		||||
        var user_id = $("#new_user_id").val();
 | 
			
		||||
        if (user_id.length < 1) {
 | 
			
		||||
@@ -41,7 +42,8 @@ $(function() {
 | 
			
		||||
        var css_checked = $("#new_css_switch").is(':checked');
 | 
			
		||||
        var css_limit = $("#new_css_limit").val();
 | 
			
		||||
        var guest_icon_checked = $("#new_guest_icon_switch").is(':checked');
 | 
			
		||||
        var formPost = postForm(user_id, css_checked, css_limit, guest_icon_checked);
 | 
			
		||||
        var badges = $("#new_badges > option:selected").map(function(){ return this.value }).get().join();
 | 
			
		||||
        var formPost = postForm(user_id, css_checked, css_limit, guest_icon_checked, badges);
 | 
			
		||||
        formPost.done(function (data) {
 | 
			
		||||
            location.reload();
 | 
			
		||||
        });
 | 
			
		||||
@@ -114,4 +116,19 @@ function update_guest_icon_switch(user_id, element) {
 | 
			
		||||
            Materialize.toast('Oh no! Something has failed changing the guest icon toggle!', 10000);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function update_badges(user_id, element) {
 | 
			
		||||
    var badges = $(element).val().join();
 | 
			
		||||
    var formPatch = patchForm(user_id, {"badges": badges});
 | 
			
		||||
    formPatch.done(function (data) {
 | 
			
		||||
        Materialize.toast('Badges updated!', 10000);
 | 
			
		||||
    });
 | 
			
		||||
    formPatch.fail(function (data) {
 | 
			
		||||
        if (data.status == 409) {
 | 
			
		||||
            Materialize.toast('This user id does not exists!', 10000);
 | 
			
		||||
        } else {
 | 
			
		||||
            Materialize.toast('Oh no! Something has failed changing the badges!', 10000);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 | 
			
		||||
(function () {
 | 
			
		||||
    const theme_options = ["DiscordDark", "BetterTitan"]; // All the avaliable theming names
 | 
			
		||||
    const badges_options = ["administrator", "partner", "supporter", "star"]; // All badges avaliable
 | 
			
		||||
    
 | 
			
		||||
    var user_def_css; // Saves the user defined css
 | 
			
		||||
    var has_already_been_initially_resized = false; // keep track if the embed initially been resized
 | 
			
		||||
@@ -134,6 +135,14 @@
 | 
			
		||||
        return funct.promise();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    function api_badges(user_id) {
 | 
			
		||||
        var funct = $.ajax({
 | 
			
		||||
            dataType: "json",
 | 
			
		||||
            url: "/api/badges/" + user_id,
 | 
			
		||||
        });
 | 
			
		||||
        return funct.promise();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    $(function() {
 | 
			
		||||
        if ($("#user-defined-css").length > 0) {
 | 
			
		||||
            user_def_css = $("#user-defined-css").text();
 | 
			
		||||
@@ -165,6 +174,9 @@
 | 
			
		||||
            inDuration: 400,
 | 
			
		||||
            outDuration: 400,
 | 
			
		||||
        });
 | 
			
		||||
        $("#usercard").modal({
 | 
			
		||||
            opacity: .5,
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        $("#nameplate").click(function () {
 | 
			
		||||
            $("#userembedmodal").modal("open");
 | 
			
		||||
@@ -607,7 +619,7 @@
 | 
			
		||||
            var rendered_user = Mustache.render(template_user, {"id": member.id.toString() + "d", "username": member_name, "avatar": member.avatar_url});
 | 
			
		||||
            $("#discord-members").append(rendered_user);
 | 
			
		||||
            $( "#discorduser-" + member.id.toString() + "d").click({"member_id": member.id.toString()}, function(event) {
 | 
			
		||||
              mention_member(event.data.member_id);
 | 
			
		||||
              openUserCard(event.data.member_id);
 | 
			
		||||
            });
 | 
			
		||||
            if (member.color) {
 | 
			
		||||
              $( "#discorduser-" + member.id.toString() + "d").css("color", "#" + member.color);
 | 
			
		||||
@@ -631,7 +643,7 @@
 | 
			
		||||
            var rendered = Mustache.render(template, {"id": member.id.toString() + "a", "username": username, "avatar": member.avatar_url});
 | 
			
		||||
            $("#embed-discord-members").append(rendered);
 | 
			
		||||
            $( "#discorduser-" + member.id.toString() + "a").click({"member_id": member.id.toString()}, function(event) {
 | 
			
		||||
              mention_member(event.data.member_id);
 | 
			
		||||
              openUserCard(event.data.member_id);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        authenticated_users_list = users;
 | 
			
		||||
@@ -679,6 +691,60 @@
 | 
			
		||||
        }, 5000);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    function openUserCard(user_id) {
 | 
			
		||||
        var bgs = api_badges(user_id);
 | 
			
		||||
        bgs.done(function (data) {
 | 
			
		||||
            for (var i = 0; i < badges_options.length; i++) {
 | 
			
		||||
                var badge = badges_options[i];
 | 
			
		||||
                if (data.indexOf(badge) != -1) {
 | 
			
		||||
                    $(`#usercard .badges .${badge}`).show();
 | 
			
		||||
                } else {
 | 
			
		||||
                    $(`#usercard .badges .${badge}`).hide();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        for (var i = 0; i < discord_users_list.length; i++) {
 | 
			
		||||
            var usr = discord_users_list[i];
 | 
			
		||||
            if (usr.id == user_id) {
 | 
			
		||||
                $("#usercard .avatar").attr("src", usr.avatar_url);
 | 
			
		||||
                $("#usercard .identity .username").text(usr.username);
 | 
			
		||||
                $("#usercard .identity .discriminator").text(usr.discriminator);
 | 
			
		||||
                if (usr.bot) {
 | 
			
		||||
                    $("#usercard .bottag").show();
 | 
			
		||||
                } else {
 | 
			
		||||
                    $("#usercard .bottag").hide();
 | 
			
		||||
                }
 | 
			
		||||
                if (usr.status == "offline") {
 | 
			
		||||
                    $("#usercard .offline-text").show();
 | 
			
		||||
                } else {
 | 
			
		||||
                    $("#usercard .offline-text").hide();
 | 
			
		||||
                }
 | 
			
		||||
                if (usr["hoist-role"]) {
 | 
			
		||||
                    $("#usercard .role").show();
 | 
			
		||||
                    $("#usercard .role .text").text(usr["hoist-role"].name);
 | 
			
		||||
                    if (usr.color) {
 | 
			
		||||
                        $("#usercard .role .bubble").css("color", "#" + usr.color);
 | 
			
		||||
                        $("#usercard .role .color").css("background-color", "#" + usr.color);
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    $("#usercard .role").hide();
 | 
			
		||||
                }
 | 
			
		||||
                if (usr.game) {
 | 
			
		||||
                    $("#usercard .game").show();
 | 
			
		||||
                    $("#usercard .game .text").text(usr.game.name);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $("#usercard .game").hide();
 | 
			
		||||
                }
 | 
			
		||||
                $("#usercard-mention-btn").off("click");
 | 
			
		||||
                $("#usercard-mention-btn").click(function () {
 | 
			
		||||
                    mention_member(user_id);
 | 
			
		||||
                    $("#usercard").modal('close');
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $("#usercard").modal('open');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    function flashElement(element) {
 | 
			
		||||
        var opacity = element.css("opacity");
 | 
			
		||||
        for (var i = 0; i < 3; i++) {
 | 
			
		||||
@@ -966,7 +1032,7 @@
 | 
			
		||||
                $("#chatcontent .chatusername").last().click(function () {
 | 
			
		||||
                    var discordid = $(this).parent().attr("discord_userid");
 | 
			
		||||
                    if (discordid) {
 | 
			
		||||
                        mention_member(discordid);
 | 
			
		||||
                        openUserCard(discordid);
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                $("#chatcontent p:last-child").find(".channellink").click(function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
              <th>CSS</th>
 | 
			
		||||
              <th>CSS Limit</th>
 | 
			
		||||
              <th>Guest Icon</th>
 | 
			
		||||
              <th>Badges</th>
 | 
			
		||||
              <th>Submit</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
@@ -50,6 +51,17 @@
 | 
			
		||||
                    </label>
 | 
			
		||||
                </div>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
              <div class="input-field col s12">
 | 
			
		||||
                <select multiple id="new_badges">
 | 
			
		||||
                  <option value="" disabled>Choose your option</option>
 | 
			
		||||
                  <option value="administrator">TitanEmbeds Administrators</option>
 | 
			
		||||
                  <option value="partner">TitanEmbeds Partner</option>
 | 
			
		||||
                  <option value="supporter">TitanEmbeds Supporter</option>
 | 
			
		||||
                  <option value="star">GitHub Stargazer</option>
 | 
			
		||||
                </select>
 | 
			
		||||
              </div>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
                <a class="waves-effect waves-light btn" id="new_submit">Submit</a>
 | 
			
		||||
            </td>
 | 
			
		||||
@@ -69,6 +81,7 @@
 | 
			
		||||
              <th>CSS</th>
 | 
			
		||||
              <th>CSS Limit</th>
 | 
			
		||||
              <th>Guest Icon</th>
 | 
			
		||||
              <th>Badges</th>
 | 
			
		||||
          </tr>
 | 
			
		||||
        </thead>
 | 
			
		||||
        <tbody>
 | 
			
		||||
@@ -101,6 +114,17 @@
 | 
			
		||||
                    </label>
 | 
			
		||||
                </div>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td>
 | 
			
		||||
              <div class="input-field col s12">
 | 
			
		||||
                <select multiple id="new_badges" onchange="update_badges('{{ cosmetic.user_id }}', this)">
 | 
			
		||||
                  <option value="" disabled>Choose your option</option>
 | 
			
		||||
                  <option value="administrator" {% if "administrator" in cosmetic.badges %}selected{% endif %}>TitanEmbeds Administrators</option>
 | 
			
		||||
                  <option value="partner" {% if "partner" in cosmetic.badges %}selected{% endif %}>TitanEmbeds Partner</option>
 | 
			
		||||
                  <option value="supporter" {% if "supporter" in cosmetic.badges %}selected{% endif %}>TitanEmbeds Supporter</option>
 | 
			
		||||
                  <option value="star" {% if "star" in cosmetic.badges %}selected{% endif %}>GitHub Stargazer</option>
 | 
			
		||||
                </select>
 | 
			
		||||
              </div>
 | 
			
		||||
            </td>
 | 
			
		||||
          </tr>
 | 
			
		||||
          {% endfor %}
 | 
			
		||||
        </tbody>
 | 
			
		||||
 
 | 
			
		||||
@@ -175,6 +175,30 @@
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div id="usercard" class="modal bottom-sheet">
 | 
			
		||||
      <div class="modal-content">
 | 
			
		||||
        <div class="row">
 | 
			
		||||
          <div class="col s12 m1">
 | 
			
		||||
            <img class="circle avatar" src="">
 | 
			
		||||
            <br>
 | 
			
		||||
            <a class="waves-effect waves-light btn" id="usercard-mention-btn">Mention</a>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="col s12 m11">
 | 
			
		||||
            <h4 class="identity"><span class="username"></span><span class="hash">#</span><span class="discriminator"></span></h4> <span class="bottag">BOT</span>
 | 
			
		||||
            <div class="badges">
 | 
			
		||||
              <a class="administrator tooltipped" data-tooltip="TitanEmbeds Administrator" data-position="top" href="https://titanembeds.com/" target="_blank"><i class="material-icons">gavel</i></a>
 | 
			
		||||
              <a class="partner tooltipped" data-tooltip="TitanEmbeds Partner" data-position="top" href="https://titanembeds.com/about" target="_blank"><i class="material-icons">person_pin</i></a>
 | 
			
		||||
              <a class="supporter tooltipped" data-tooltip="TitanEmbeds Supporter" data-position="top" href="https://titanembeds.com/user/donate" target="_blank"><i class="material-icons">attach_money</i></a>
 | 
			
		||||
              <a class="star tooltipped" data-tooltip="GitHub Stargazer" data-position="top" href="https://github.com/TitanEmbeds/Titan" target="_blank"><i class="material-icons">star</i></a>
 | 
			
		||||
            </div>
 | 
			
		||||
            <p class="offline-text">User is Offline.</p>
 | 
			
		||||
            <p class="game">Playing <span class="text"></span></p>
 | 
			
		||||
            <p class="role">Highest Role: <span class="bubble"><span class="color"></span> <span class="text"></span></span></p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
    <div id="emoji-picker">
 | 
			
		||||
      <div id="emoji-picker-content">
 | 
			
		||||
        <div class="row">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user