Change parameterless tz.list

This commit is contained in:
Noi 2020-03-01 14:47:51 -08:00
parent 82f956f47a
commit 2db1cd05ac
3 changed files with 53 additions and 103 deletions

View file

@ -1,12 +1,13 @@
# Command handlers # Command handlers
# Incoming commands are fully handled by functions defined here. # Incoming commands are fully handled by functions defined here.
from common import BotVersion from common import BotVersion, tzPrint
from textwrap import dedent from textwrap import dedent
import discord import discord
import pytz import pytz
from datetime import datetime from datetime import datetime
import re import re
import collections
from userdb import UserDatabase from userdb import UserDatabase
from common import tzlcmap, logPrint from common import tzlcmap, logPrint
@ -34,47 +35,6 @@ class WtCommands:
# ------ # ------
# Helper methods # Helper methods
def _tzPrint(self, zone : str):
"""
Returns a string displaying the current time in the given time zone.
String begins with four numbers for sorting purposes and must be trimmed before output.
"""
now_time = datetime.now(pytz.timezone(zone))
return "{:s}{:s}".format(now_time.strftime("%m%d"), now_time.strftime("%d-%b %H:%M %Z (UTC%z)"))
def _userResolve(self, 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 " → This text should never appear."
# Try given entries. For each entry tried, attempt to get their nickname
# or username. Failed attempts are anonymized instead of discarded.
namesProcessed = 0
namesSkipped = 0
processedNames = []
while namesProcessed < 4 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 "{0} user{1}.".format(leftovers, "s" if leftovers != 1 else "")
result = ""
while len(processedNames) > 0:
result += processedNames.pop() + ", "
if leftovers != 0:
result += "{0} other{1}.".format(leftovers, "s" if leftovers != 1 else "")
else:
result = result[:-2] + "."
return result
def _userFormat(self, member: discord.Member): def _userFormat(self, member: discord.Member):
""" """
@ -236,26 +196,33 @@ class WtCommands:
# Supplemental command functions # Supplemental command functions
async def _list_noparam(self, guild: discord.Guild, channel: discord.TextChannel): async def _list_noparam(self, guild: discord.Guild, channel: discord.TextChannel):
rawlist = self.userdb.get_list2(guild.id) userlist = self.userdb.get_users(guild.id)
if len(rawlist) == 0: if len(userlist) == 0:
await channel.send(':x: No users with registered time zones have been active in the last 30 days.') await channel.send(':x: No users with registered time zones have been active in the last 30 days.')
return return
if guild.large:
# Get full user data here if not available (used by _userResolve) orderedList = collections.OrderedDict(sorted(userlist.items()))
await self.dclient.request_offline_members(guild) result = ''
resultData = []
for key, value in rawlist.items(): for k, v in orderedList.items():
resultData.append(self._tzPrint(key) + '\n' + self._userResolve(guild, value)) foundUsers = 0
resultData.sort() line = k[4:] + ": "
resultFinal = '```\n' for id in v:
for i in resultData: member = await self._resolve_member(guild, id)
resultFinal += i + '\n' if member is None:
resultFinal += '```' continue
await channel.send(resultFinal) if foundUsers > 10:
line += "and others... "
foundUsers += 1
line += self._userFormat(member) + ", "
if foundUsers > 0: result += line[:-2] + "\n"
em = discord.Embed(description=result.strip())
await channel.send(embed=em)
async def _list_userparam(self, guild: discord.Guild, channel: discord.TextChannel, author: discord.User, param): async def _list_userparam(self, guild: discord.Guild, channel: discord.TextChannel, author: discord.User, param):
param = str(param) param = str(param)
usersearch = self._resolve_member(guild, param) usersearch = await self._resolve_member(guild, param)
if usersearch is None: if usersearch is None:
await channel.send(':x: Cannot find the specified user.') await channel.send(':x: Cannot find the specified user.')
return return
@ -266,23 +233,23 @@ class WtCommands:
if ownsearch: await channel.send(':x: You do not have a time zone. Set it with `tz.set`.') if ownsearch: await channel.send(':x: You do not have a time zone. Set it with `tz.set`.')
else: await channel.send(':x: The given user does not have a time zone set.') else: await channel.send(':x: The given user does not have a time zone set.')
return return
em = discord.Embed(description=self._tzPrint(res)[4:] + ": " + self._userFormat(usersearch)) em = discord.Embed(description=tzPrint(res)[4:] + ": " + self._userFormat(usersearch))
await channel.send(embed=em) await channel.send(embed=em)
def _resolve_member(self, guild: discord.Guild, inputstr: str): async def _resolve_member(self, guild: discord.Guild, inputstr: str):
""" """
Takes a string input and attempts to find the corresponding member. Takes a string input and attempts to find the corresponding member.
""" """
await self.dclient.request_offline_members(guild)
idsearch = None idsearch = None
try: try:
idsearch = int(inputstr) idsearch = int(inputstr)
except ValueError: except ValueError:
pass if inputstr.startswith('<@!') and inputstr.endswith('>'):
if inputstr.startswith('<@!') and inputstr.endswith('>'): idsearch = int(inputstr[3:][:-1])
idsearch = int(inputstr[3:][:-1]) elif inputstr.startswith('<@') and inputstr.endswith('>'):
elif inputstr.startswith('<@') and inputstr.endswith('>'): idsearch = int(inputstr[2:][:-1])
idsearch = int(inputstr[2:][:-1])
if idsearch is not None: if idsearch is not None:
return guild.get_member(idsearch) return guild.get_member(idsearch)

View file

@ -4,7 +4,7 @@ import pytz
from datetime import datetime from datetime import datetime
# Bot's current version (as a string), for use in the help command # Bot's current version (as a string), for use in the help command
BotVersion = "1.2.1" BotVersion = "1.3.0"
# For case-insensitive time zone lookup, map lowercase tzdata entries with # For case-insensitive time zone lookup, map lowercase tzdata entries with
# entires with proper case. pytz is case sensitive. # entires with proper case. pytz is case sensitive.
@ -15,4 +15,12 @@ def logPrint(label, line):
Print with timestamp in a way that resembles some of my other projects Print with timestamp in a way that resembles some of my other projects
""" """
resultstr = datetime.utcnow().strftime('%Y-%m-%d %H:%m:%S') + ' [' + label + '] ' + line resultstr = datetime.utcnow().strftime('%Y-%m-%d %H:%m:%S') + ' [' + label + '] ' + line
print(resultstr) print(resultstr)
def tzPrint(zone : str):
"""
Returns a string displaying the current time in the given time zone.
String begins with four numbers for sorting purposes and must be trimmed before output.
"""
now_time = datetime.now(pytz.timezone(zone))
return "{:s}{:s}".format(now_time.strftime("%m%d"), now_time.strftime("%d-%b %H:%M %Z (UTC%z)"))

View file

@ -1,6 +1,7 @@
# User database abstractions # User database abstractions
import psycopg2 import psycopg2
from common import tzPrint
class UserDatabase: class UserDatabase:
def __init__(self, connstr): def __init__(self, connstr):
@ -63,7 +64,7 @@ class UserDatabase:
def get_user(self, serverid, userid): def get_user(self, serverid, userid):
''' '''
Retrieves the time zone name of a single user Retrieves the time zone name of a single user.
''' '''
c = self.db.cursor() c = self.db.cursor()
c.execute(""" c.execute("""
@ -75,36 +76,12 @@ class UserDatabase:
if result is None: return None if result is None: return None
return result[0] return result[0]
def get_users(self, serverid):
def get_list(self, serverid, userid=None): """
''' Retrieves all user time zones for all recently active members.
Retrieves a list of recent time zones based on Users not present are not filtered here. Must be handled by the caller.
recent activity per user. For use in the list command. Returns a dictionary of lists - Key is formatted zone, value is list of users represented.
''' """
c = self.db.cursor()
if userid is None:
c.execute("""
SELECT zone, count(*) as ct FROM userdata
WHERE guild_id = %s
AND last_active >= now() - INTERVAL '30 DAYS' -- only users active in the last 30 days
GROUP BY zone -- separate by popularity
ORDER BY ct DESC LIMIT 20 -- top 20 zones are given
""", (serverid))
else:
c.execute("""
SELECT zone, '0' as ct FROM userdata
WHERE guild_id = %s AND user_id = %s
""", (serverid, userid))
results = c.fetchall()
c.close()
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 = self.db.cursor()
c.execute(""" c.execute("""
SELECT zone, user_id SELECT zone, user_id
@ -125,11 +102,9 @@ class UserDatabase:
""", {'guild': serverid}) """, {'guild': serverid})
result = {} result = {}
for row in c: for row in c:
inlist = result.get(row[0]) resultrow = tzPrint(row[0])
if inlist is None: result[resultrow] = result.get(resultrow, list())
result[row[0]] = [] result[resultrow].append(row[1])
inlist = result[row[0]]
inlist.append(row[1])
c.close() c.close()
return result return result