Add user cards and badges, Closes #25

This commit is contained in:
Jeremy Zhang 2017-12-07 06:49:32 +00:00
parent 3af5dc703a
commit 204858d011
12 changed files with 293 additions and 10 deletions

View File

@ -0,0 +1,28 @@
"""Added badges column to cosmetics
Revision ID: 66971a97040e
Revises: 16b4fdbbe155
Create Date: 2017-12-07 04:30:25.639794
"""
# revision identifiers, used by Alembic.
revision = '66971a97040e'
down_revision = '16b4fdbbe155'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('cosmetics', sa.Column('badges', sa.String(length=255), server_default='[]', nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('cosmetics', 'badges')
# ### end Alembic commands ###

View File

@ -7,6 +7,9 @@ def init_debug():
from flask import jsonify, request
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # Testing oauthlib
app.jinja_env.auto_reload = True
app.config['TEMPLATES_AUTO_RELOAD'] = True
# Session viewer https://gist.github.com/babldev/502364a3f7c9bafaa6db
def decode_flask_cookie(secret_key, cookie_str):

View File

@ -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 = []

View File

@ -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():

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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);
}
});
}

View File

@ -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 () {

View File

@ -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>

View File

@ -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">