From b040f614c29a8cf5a279ca8d4223e9a1e4bbc3b6 Mon Sep 17 00:00:00 2001 From: Jeremy Zhang Date: Thu, 13 Apr 2017 23:10:13 -0700 Subject: [PATCH] Sorted online discord server members by roles and color. fixed caching a bit --- titanembeds/blueprints/api/api.py | 28 +++++- titanembeds/discordrest.py | 29 +++++- titanembeds/static/css/embedstyle.css | 8 +- titanembeds/static/js/embed.js | 128 +++++++++++++++++--------- titanembeds/templates/embed.html.j2 | 4 + 5 files changed, 147 insertions(+), 50 deletions(-) diff --git a/titanembeds/blueprints/api/api.py b/titanembeds/blueprints/api/api.py index 1d08bb4..75c1189 100644 --- a/titanembeds/blueprints/api/api.py +++ b/titanembeds/blueprints/api/api.py @@ -186,8 +186,8 @@ def get_guild_channels(guild_id): result["read"] = False result["write"] = False - #if result["read"]: - result_channels.append(result) + if result["read"]: + result_channels.append(result) return sorted(result_channels, key=lambda k: k['channel']['position']) def filter_guild_channel(guild_id, channel_id): @@ -199,6 +199,28 @@ def filter_guild_channel(guild_id, channel_id): def get_online_discord_users(guild_id): embed = discord_api.get_widget(guild_id) + apimembers = discord_api.list_all_guild_members(guild_id) + apimembers_filtered = {} + for member in apimembers: + apimembers_filtered[member["user"]["id"]] = member + guild_roles = discord_api.get_guild_roles(guild_id)["content"] + guildroles_filtered = {} + for role in guild_roles: + guildroles_filtered[role["id"]] = role + for member in embed['members']: + apimem = apimembers_filtered.get(member["id"]) + member["hoist-role"] = None + member["color"] = None + if apimem: + for roleid in reversed(apimem["roles"]): + role = guildroles_filtered[roleid] + if role["color"] != 0: + member["color"] = '{0:02x}'.format(role["color"]) #int to hex + if role["hoist"]: + member["hoist-role"] = {} + member["hoist-role"]["name"] = role["name"] + member["hoist-role"]["id"] = role["id"] + member["hoist-role"]["position"] = role["position"] return embed['members'] def get_online_embed_users(guild_id): @@ -287,7 +309,7 @@ def create_unauthenticated_user(): username = username.strip() if len(username) < 2 or len(username) > 32: abort(406) - if not all(x.isalnum() or x.isspace() or "-" == x or "_" == x for x in username): + if not all(x.isalpha() or x.isspace() or "-" == x or "_" == x for x in username): abort(406) if not check_guild_existance(guild_id): abort(404) diff --git a/titanembeds/discordrest.py b/titanembeds/discordrest.py index 48d522e..c3d8237 100644 --- a/titanembeds/discordrest.py +++ b/titanembeds/discordrest.py @@ -2,10 +2,12 @@ import requests import sys import time import json +from functools import partial from cachetools import cached, TTLCache +from cachetools.keys import hashkey _DISCORD_API_BASE = "https://discordapp.com/api/v6" -cache = TTLCache(200, 300) +cache = TTLCache(200, 200) def json_or_text(response): text = response.text @@ -120,7 +122,7 @@ class DiscordREST: r = self.request("GET", _endpoint) return r - @cached(cache) + @cached(cache, key=partial(hashkey, 'get_guild_member')) def get_guild_member(self, guild_id, user_id): _endpoint = "/guilds/{guild_id}/members/{user_id}".format(guild_id=guild_id, user_id=user_id) r = self.request("GET", _endpoint) @@ -158,11 +160,30 @@ class DiscordREST: r = self.request("GET", _endpoint) return r + @cached(cache, key=partial(hashkey, 'list_all_guild_members')) + def list_all_guild_members(self, guild_id): + _endpoint = "/guilds/{guild_id}/members".format(guild_id=guild_id) + count = 1 + last_usrid = "" + users = [] + params = {"limit": 1000} + while count > 0: + r = self.request("GET", _endpoint, params=params) + if r["success"] == True: + content = r["content"] + count = len(content) + users.extend(content) + if count > 0: + params["after"] = content[-1]["user"]["id"] + else: + count = 0 + return users + ##################### # User ##################### - @cached(cache) + @cached(cache, key=partial(hashkey, 'get_all_guilds')) def get_all_guilds(self): _endpoint = "/users/@me/guilds" params = {} @@ -185,7 +206,7 @@ class DiscordREST: # Widget Handler ##################### - @cached(cache) + @cached(cache, key=partial(hashkey, 'get_widget')) def get_widget(self, guild_id): _endpoint = _DISCORD_API_BASE + "/servers/{guild_id}/widget.json".format(guild_id=guild_id) embed = self.get_guild_embed(guild_id) diff --git a/titanembeds/static/css/embedstyle.css b/titanembeds/static/css/embedstyle.css index c889f93..46ee4b8 100644 --- a/titanembeds/static/css/embedstyle.css +++ b/titanembeds/static/css/embedstyle.css @@ -51,6 +51,12 @@ color: #cfd8dc; font-variant: small-caps; } +.role-title { + margin-bottom: -15px !important; + font-variant: normal !important; + font-size: 80% !important; +} + .divider { background-color: #90a4ae; } @@ -170,4 +176,4 @@ background-color: #546e7a; a { color: #82b1ff; -} \ No newline at end of file +} diff --git a/titanembeds/static/js/embed.js b/titanembeds/static/js/embed.js index 9934251..8a5d0d7 100644 --- a/titanembeds/static/js/embed.js +++ b/titanembeds/static/js/embed.js @@ -11,20 +11,20 @@ var last_message_id; // last message tracked var selected_channel = guild_id; // user selected channel, defaults to #general channel var guild_channels = {}; // all server channels used to highlight channels in messages - + function element_in_view(element, fullyInView) { var pageTop = $(window).scrollTop(); var pageBottom = pageTop + $(window).height(); var elementTop = $(element).offset().top; var elementBottom = elementTop + $(element).height(); - + if (fullyInView === true) { return ((pageTop < elementTop) && (pageBottom > elementBottom)); } else { return ((elementTop <= pageBottom) && (elementBottom >= pageTop)); } } - + function query_guild() { var funct = $.ajax({ dataType: "json", @@ -33,7 +33,7 @@ }); return funct.promise(); } - + function create_authenticated_user() { var funct = $.ajax({ method: "POST", @@ -43,7 +43,7 @@ }); return funct.promise(); } - + function create_unauthenticated_user(username) { var funct = $.ajax({ method: "POST", @@ -53,7 +53,7 @@ }); return funct.promise(); } - + function fetch(channel_id, after=null) { var funct = $.ajax({ method: "GET", @@ -63,7 +63,7 @@ }); return funct.promise(); } - + function post(channel_id, content) { var funct = $.ajax({ method: "POST", @@ -73,7 +73,7 @@ }); return funct.promise(); } - + $(function(){ $("#loginmodal").modal({ dismissible: false, // Modal can be dismissed by clicking outside of the modal @@ -84,18 +84,18 @@ endingTop: '10%', // Ending top style attribute } ); - + var guild = query_guild(); guild.fail(function() { $('#loginmodal').modal('open'); }); - + guild.done(function(data) { initialize_embed(data); //$('#loginmodal').modal('open'); }); }); - + function lock_login_fields() { $("#loginProgress").show(); $("#discordlogin_btn").attr("disabled",true); @@ -104,14 +104,14 @@ unlock_login_fields(); }, 60000); } - + function unlock_login_fields() { $("#loginProgress").hide(); $("#discordlogin_btn").attr("disabled",false); $("#custom_username_field").prop("disabled",false); clearTimeout(logintimer); } - + function initialize_embed(guildobj) { $('#loginmodal').modal('close'); unlock_login_fields(); @@ -124,7 +124,7 @@ prepare_guild(guildobj); } } - + function prepare_guild(guildobj) { fill_channels(guildobj.channels); fill_discord_members(guildobj.discordmembers); @@ -132,7 +132,7 @@ fill_unauthenticated_users(guildobj.embedmembers.unauthenticated); run_fetch_routine(); } - + function fill_channels(channels) { var template = $('#mustache_channellistings').html(); Mustache.parse(template); @@ -160,7 +160,7 @@ } $("#channel-"+selected_channel).parent().addClass("active"); } - + function mention_member(member_id) { if (!$('#messagebox').prop('disabled')) { $('#messagebox').val( $('#messagebox').val() + "[@" + member_id + "] " ); @@ -168,21 +168,65 @@ $("#messagebox").focus(); } } - + function fill_discord_members(discordmembers) { var template = $('#mustache_authedusers').html(); Mustache.parse(template); $("#discord-members").empty(); + var guild_members = {}; for (var i = 0; i < discordmembers.length; i++) { var member = discordmembers[i]; - var rendered = Mustache.render(template, {"id": member.id.toString() + "d", "username": member.username, "avatar": member.avatar_url}); - $("#discord-members").append(rendered); + if (member["hoist-role"]) { + if (!(member["hoist-role"]["id"] in guild_members)) { + guild_members[member["hoist-role"]["id"]] = {}; + guild_members[member["hoist-role"]["id"]]["name"] = member["hoist-role"]["name"]; + guild_members[member["hoist-role"]["id"]]["members"] = []; + guild_members[member["hoist-role"]["id"]]["position"] = member["hoist-role"]["position"] + } + guild_members[member["hoist-role"]["id"]]["members"].push(member); + } else { + if (!("0" in guild_members)) { + guild_members["0"] = {}; + guild_members["0"]["name"] = null; + guild_members["0"]["members"] = []; + guild_members["0"]["position"] = 0; + } + guild_members["0"]["members"].push(member); + } + } + var guild_members_arr = []; + for (key in guild_members) { + guild_members_arr.push(guild_members[key]); + } + guild_members_arr.sort(function(a, b) { + return parseInt(b.position) - parseInt(a.position); + }) + var template_role = $('#mustache_memberrole').html(); + Mustache.parse(template_role); + var template_user = $('#mustache_authedusers').html(); + Mustache.parse(template_user); + $("#discord-members").empty(); + for (var i = 0; i < guild_members_arr.length; i++) { + var roleobj = guild_members_arr[i]; + if (!roleobj["name"]) { + roleobj["name"] = "Uncategorized"; + } + var rendered_role = Mustache.render(template_role, {"name": roleobj["name"]}); + $("#discord-members").append(rendered_role); + for (var j = 0; j < roleobj.members.length; j++) { + var member = roleobj.members[j]; + var rendered_user = Mustache.render(template_user, {"id": member.id.toString() + "d", "username": member.username, "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); }); + if (member.color) { + $( "#discorduser-" + member.id.toString() + "d").css("color", "#" + member.color); + } + } } } - + function fill_authenticated_users(users) { var template = $('#mustache_authedusers').html(); Mustache.parse(template); @@ -196,7 +240,7 @@ }); } } - + function fill_unauthenticated_users(users) { var template = $('#mustache_unauthedusers').html(); Mustache.parse(template); @@ -207,11 +251,11 @@ $("#embed-unauth-users").append(rendered); } } - + function wait_for_discord_login() { _wait_for_discord_login(0); } - + function _wait_for_discord_login(index) { setTimeout(function() { var usr = create_authenticated_user(); @@ -228,7 +272,7 @@ }); }, 5000); } - + function select_channel(channel_id) { if (selected_channel != channel_id) { selected_channel = channel_id; @@ -239,7 +283,7 @@ run_fetch_routine(); } } - + function replace_message_mentions(message) { var mentions = message.mentions; for (var i = 0; i < mentions.length; i++) { @@ -250,11 +294,11 @@ } return message; } - + function getPosition(string, subString, index) { return string.split(subString, index).join(subString).length; } - + function format_bot_message(message) { if (message.author.id == bot_client_id && (message.content.includes("**") && ( (message.content.includes("<")&&message.content.includes(">")) || (message.content.includes("[") && message.content.includes("]")) ))) { var usernamefield = message.content.substring(getPosition(message.content, "**", 1)+3, getPosition(message.content, "**", 2)-1); @@ -264,14 +308,14 @@ } return message; } - + function parse_message_time(message) { var mome = moment(message.timestamp); message.formatted_timestamp = mome.toDate().toString(); message.formatted_time = mome.format("h:mm A"); return message; } - + function parse_message_attachments(message) { for (var i = 0; i < message.attachments.length; i++) { var attach = ""; @@ -283,7 +327,7 @@ } return message; } - + function handle_last_message_mention() { var lastmsg = $("#chatcontent p:last-child"); var content = lastmsg.text().toLowerCase(); @@ -293,7 +337,7 @@ lastmsg.css( "font-weight", "bold" ); } } - + function escapeHtml(unsafe) { /* http://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript */ return unsafe .replace(/&/g, "&") @@ -302,12 +346,12 @@ .replace(/"/g, """) .replace(/'/g, "'"); } - + function nl2br (str, is_xhtml) { /* http://stackoverflow.com/questions/2919337/jquery-convert-line-breaks-to-br-nl2br-equivalent/ */ - var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
'; + var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '
' : '
'; return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2'); } - + function parse_channels_in_message(message) { var channelids = Object.keys(guild_channels); for (var i = 0; i < channelids.length; i++) { @@ -316,7 +360,7 @@ } return message; } - + function fill_discord_messages(messages, jumpscroll) { if (messages.length == 0) { return last_message_id; @@ -342,7 +386,7 @@ }); return last; } - + function run_fetch_routine() { var channel_id = selected_channel; var fet; @@ -389,7 +433,7 @@ } }); } - + function update_embed_userchip(authenticated, avatar, username, userid, discrim=null) { if (authenticated) { $("#currentuserimage").show(); @@ -400,12 +444,12 @@ $("#currentusername").text(username + "#" + userid); } } - + $("#discordlogin_btn").click(function() { lock_login_fields(); wait_for_discord_login(); }); - + $("#custom_username_field").keyup(function(event){ if (event.keyCode == 13) { if (!(new RegExp(/^[a-z\d\-_\s]+$/i).test($(this).val()))) { @@ -431,7 +475,7 @@ } } }); - + $("#messagebox").keyup(function(event){ if ($(this).val().length == 1) { $(this).val($.trim($(this).val())); @@ -459,7 +503,7 @@ }); } }); - + $('#guild-btn').sideNav({ menuWidth: 300, // Default is 300 edge: 'left', // Choose the horizontal origin @@ -467,7 +511,7 @@ draggable: true // Choose whether you can drag to open on touch screens } ); - + $('#members-btn').sideNav({ menuWidth: 300, // Default is 300 edge: 'right', // Choose the horizontal origin diff --git a/titanembeds/templates/embed.html.j2 b/titanembeds/templates/embed.html.j2 index b5ca2fe..b59dfb7 100644 --- a/titanembeds/templates/embed.html.j2 +++ b/titanembeds/templates/embed.html.j2 @@ -123,6 +123,10 @@ + + {% endraw %}