From d06c3eb34686779acb98a4d96a91ef1fecbc06a4 Mon Sep 17 00:00:00 2001 From: Noikoio Date: Tue, 28 Aug 2018 11:17:22 -0700 Subject: [PATCH] Display small list of users in list --- commands.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++------ common.py | 11 -------- userdb.py | 36 +++++++++++++++++++++++-- 3 files changed, 102 insertions(+), 21 deletions(-) diff --git a/commands.py b/commands.py index a622d6a..1f46e69 100644 --- a/commands.py +++ b/commands.py @@ -4,13 +4,59 @@ from textwrap import dedent import discord +import pytz +from datetime import datetime from userdb import UserDatabase -from common import tzlcmap, tzPrint, logPrint +from common import tzlcmap, logPrint # All command functions are expected to have this signature: # def cmd_NAME(self, guild: discord.Guild, channel: discord.TextChannel, author: discord.User, msgcontent: str) +def _tzPrint(zone : str): + """ + Returns a string displaying the current time in the given time zone. + Resulting string should be placed in a code block. + """ + padding = '' + now_time = datetime.now(pytz.timezone(zone)) + if len(now_time.strftime("%Z")) != 4: padding = ' ' + return "{:s}{:s} | {:s}".format(now_time.strftime("%H:%M %d-%b %Z%z"), padding, zone) + +def _userResolve(guild: discord.Guild, userIds: list): + """ + Given a list with user IDs, returns a string, the second half of a + list entry, describing the users for which a zone is represented by. + """ + if len(userIds) == 0: + return " -- Representing 0 users. Bug?" + + # Try given entries. For each entry tried, attempt to get their nickname + # or username. Failed attempts are anonymized instead of discarded. + # Attempt at most three entries. + namesProcessed = 0 + namesSkipped = 0 + processedNames = [] + while namesProcessed < 3 and len(userIds) > 0: + namesProcessed += 1 + uid = userIds.pop() + mem = guild.get_member(int(uid)) + if mem is not None: + processedNames.append(mem.display_name) + else: + namesSkipped += 1 + leftovers = namesSkipped + len(userIds) + if len(processedNames) == 0: + return " -- Representing {0} user{1}.".format(leftovers, "s" if leftovers != 1 else "") + result = " -- Representing " + while len(processedNames) > 0: + result += processedNames.pop() + ", " + if leftovers != 0: + result += "{0} other user{1}.".format(leftovers, "s" if leftovers != 1 else "") + else: + result = result[:-2] + "." + return result + class WtCommands: def __init__(self, userdb: UserDatabase, client: discord.Client): self.userdb = userdb @@ -31,9 +77,6 @@ class WtCommands: logPrint('Command invoked', '{0}/{1}: tz.{2}'.format(message.guild, message.author, cmdBase)) await command(message.guild, message.channel, message.author, message.content) - # ------ - # Helper functions - # ------ # Individual command handlers @@ -68,7 +111,7 @@ class WtCommands: except KeyError: await channel.send(':x: Not a valid zone name.') return - resultstr = '```\n' + tzPrint(zoneinput) + '\n```' + resultstr = '```\n' + _tzPrint(zoneinput) + '\n```' await channel.send(resultstr) async def cmd_set(self, guild: discord.Guild, channel: discord.TextChannel, author: discord.User, msgcontent: str): @@ -88,7 +131,7 @@ class WtCommands: async def cmd_list(self, guild: discord.Guild, channel: discord.TextChannel, author: discord.User, msgcontent: str): wspl = msgcontent.split(' ', 1) if len(wspl) == 1: - await self._list_noparam(guild, channel) + await self._list_noparam2(guild, channel) else: await self._list_userparam(guild, channel, author, wspl[1]) @@ -101,13 +144,14 @@ class WtCommands: # Supplemental command functions async def _list_noparam(self, guild: discord.Guild, channel: discord.TextChannel): + # To do: improve and merge into noparam2 clist = self.userdb.get_list(guild.id) if len(clist) == 0: await channel.send(':x: No users with known zones have been active in the last 72 hours.') return resultarr = [] for i in clist: - resultarr.append(tzPrint(i)) + resultarr.append(_tzPrint(i)) resultarr.sort() resultstr = '```\n' for i in resultarr: @@ -115,6 +159,22 @@ class WtCommands: resultstr += '```' await channel.send(resultstr) + async def _list_noparam2(self, guild: discord.Guild, channel: discord.TextChannel): + rawlist = self.userdb.get_list2(guild.id) + if len(rawlist) == 0: + await channel.send(':x: No users with known zones have been active in the last 72 hours.') + return + + resultData = [] + for key, value in rawlist.items(): + resultData.append(_tzPrint(key) + '\n' + _userResolve(guild, value)) + resultData.sort() + resultFinal = '```\n' + for i in resultData: + resultFinal += i + '\n' + resultFinal += '```' + await channel.send(resultFinal) + async def _list_userparam(self, guild: discord.Guild, channel: discord.TextChannel, author: discord.User, param): # wishlist: search based on username/nickname param = str(param) @@ -132,5 +192,5 @@ class WtCommands: if spaghetti: await channel.send(':x: You do not have a time zone. Set it with `tz.set`.') else: await channel.send(':x: The given user has not set a time zone. Ask to set it with `tz.set`.') return - resultstr = '```\n' + tzPrint(res[0]) + '\n```' + resultstr = '```\n' + _tzPrint(res[0]) + '\n```' await channel.send(resultstr) \ No newline at end of file diff --git a/common.py b/common.py index 8b83c24..468c912 100644 --- a/common.py +++ b/common.py @@ -7,17 +7,6 @@ from datetime import datetime # entires with proper case. pytz is case sensitive. tzlcmap = {x.lower():x for x in pytz.common_timezones} -timefmt = "%H:%M %d-%b %Z%z" -def tzPrint(zone : str): - """ - Returns a string displaying the current time in the given time zone. - Resulting string should be placed in a code block. - """ - padding = '' - now_time = datetime.now(pytz.timezone(zone)) - if len(now_time.strftime("%Z")) != 4: padding = ' ' - return "{:s}{:s} | {:s}".format(now_time.strftime(timefmt), padding, zone) - def logPrint(label, line): """ Print with timestamp in a way that resembles some of my other projects diff --git a/userdb.py b/userdb.py index 3d02490..42c9e4f 100644 --- a/userdb.py +++ b/userdb.py @@ -1,4 +1,4 @@ -# Abstracts away details of the SQLite database that stores user information. +# User database abstractions import sqlite3 @@ -77,4 +77,36 @@ class UserDatabase: results = c.fetchall() c.close() - return [i[0] for i in results] \ No newline at end of file + return [i[0] for i in results] + + def get_list2(self, serverid): + ''' + Retrieves data for the tz.list command. + Returns a dictionary. Keys are zone name, values are arrays with user IDs. + ''' + c = self.db.cursor() + c.execute(''' + SELECT zone, user + FROM users + WHERE + lastactive >= strftime('%s','now') - (72 * 60 * 60) -- only users active in the last 72 hrs + AND guild = '{0}' + AND zone in (SELECT zone from ( + SELECT zone, count(*) as ct + FROM users + WHERE + guild = '{0}' + AND lastactive >= strftime('%s','now') - (72 * 60 * 60) + GROUP BY zone + LIMIT 10 + )) + ORDER BY RANDOM() -- Randomize display order (done by consumer) + '''.format(serverid)) + result = {} + for row in c: + inlist = result.get(row[0]) + if inlist is None: + result[row[0]] = [] + inlist = result[row[0]] + inlist.append(row[1]) + return result \ No newline at end of file