Skip to content

Commit 9169877

Browse files
committed
Switch profile.py over to using Pager class, and cleaned up the code
1 parent 84cd4ad commit 9169877

1 file changed

Lines changed: 68 additions & 96 deletions

File tree

plugins/profile.py

Lines changed: 68 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import random
12
import re
23
import string
3-
import random
44
from collections import defaultdict
55

66
from sqlalchemy import Column
@@ -10,13 +10,13 @@
1010

1111
from cloudbot import hook
1212
from cloudbot.util import database
13+
from cloudbot.util.pager import paginated_list
1314

1415
category_re = r"[A-Za-z0-9]+"
1516
data_re = re.compile(r"({})\s(.+)".format(category_re))
1617

1718
# borrowed pagination code from grab.py
1819
cat_pages = defaultdict(dict)
19-
cat_page_index = defaultdict(dict)
2020
confirm_keys = defaultdict(dict)
2121

2222
table = Table(
@@ -36,8 +36,7 @@ def load_cache(db):
3636
"""
3737
:type db: sqlalchemy.orm.Session
3838
"""
39-
global profile_cache
40-
profile_cache = {}
39+
profile_cache.clear()
4140
for row in db.execute(table.select().order_by(table.c.category)):
4241
nick = row["nick"].lower()
4342
cat = row["category"]
@@ -46,29 +45,6 @@ def load_cache(db):
4645
profile_cache.setdefault(chan, {}).setdefault(nick, {})[cat] = text
4746

4847

49-
# modified from grab.py
50-
def two_lines(bigstring, chan, nick):
51-
"""Receives a string with new lines. Groups the string into a list of strings with up to 3 new lines per string element. Returns first string element then stores the remaining list in search_pages."""
52-
global cat_pages
53-
global cat_page_index
54-
if nick not in cat_pages[chan]:
55-
cat_pages[chan][nick] = []
56-
temp = bigstring.split('\n')
57-
for i in range(0, len(temp), 2):
58-
cat_pages[chan][nick].append('\n'.join(temp[i:i + 2]))
59-
cat_page_index[chan][nick] = 0
60-
return cat_pages[chan][nick][0]
61-
62-
63-
# modified from grab.py
64-
def smart_truncate(content, length=355, suffix='...\n'):
65-
if len(content) <= length:
66-
return content
67-
else:
68-
return content[:length].rsplit(', ', 1)[0] + suffix + \
69-
content[:length].rsplit(', ', 1)[1] + smart_truncate(content[length:])
70-
71-
7248
def format_profile(nick, category, text):
7349
# Add zwsp to avoid pinging users
7450
nick = "{}{}{}".format(nick[0], "\u200B", nick[1:])
@@ -80,114 +56,106 @@ def format_profile(nick, category, text):
8056
@hook.command("moreprofile", autohelp=False)
8157
def moreprofile(text, chan, nick, notice):
8258
"""If a category search has lots of results the results are paginated. If the most recent search is paginated the pages are stored for retrieval. If no argument is given the next page will be returned else a page number can be specified."""
83-
if nick.lower() not in cat_pages[chan.lower()] or not cat_pages[chan.lower()][nick.lower()]:
59+
chan_pages = cat_pages[chan.casefold()]
60+
pages = chan_pages.get(nick.casefold())
61+
if not pages:
8462
notice("There are no category pages to show.")
8563
return
8664

8765
if text:
88-
index = ""
89-
if text.isnumeric():
66+
try:
9067
index = int(text)
91-
else:
68+
except ValueError:
9269
notice("Please specify a positive integer value")
9370
return
9471

95-
if index > len(cat_pages[chan.lower()][nick.lower()]) or index == 0:
96-
notice("Please specify a valid page number between 1 and {}.".format(len(cat_pages[chan.lower()][nick.lower()])))
72+
page = pages[index - 1]
73+
if page is None:
74+
notice("Please specify a valid page number between 1 and {}.".format(len(pages)))
9775
return
9876
else:
99-
msg = "{}(page {}/{})".format(cat_pages[chan.lower()][nick.lower()][index - 1], index, len(cat_pages[chan.lower()][nick.lower()]))
100-
for line in msg.splitlines():
77+
for line in page:
10178
notice(line)
102-
return
10379
else:
104-
cat_page_index[chan.lower()][nick.lower()] += 1
105-
if cat_page_index[chan.lower()][nick.lower()] < len(cat_pages[chan.lower()][nick.lower()]):
106-
msg = "{}(page {}/{})".format(cat_pages[chan.lower()][nick.lower()][cat_page_index[chan.lower()][nick.lower()]],
107-
cat_page_index[chan.lower()][nick.lower()] + 1,
108-
len(cat_pages[chan.lower()][nick.lower()]))
109-
for line in msg.splitlines():
80+
page = pages.next()
81+
if page is not None:
82+
for line in page:
11083
notice(line)
111-
return
11284
else:
11385
notice("All pages have been shown. You can specify a page number or start a new search")
114-
return
11586

11687

11788
@hook.command()
11889
def profile(text, chan, notice, nick):
11990
"""<nick> [category] Returns a user's saved profile data from \"<category>\", or lists all available profile categories for the user if no category specified"""
120-
global cat_pages
121-
global cat_page_index
91+
chan_cf = chan.casefold()
92+
nick_cf = nick.casefold()
12293

12394
# Check if we are in a PM with the user
124-
if nick.lower() == chan.lower():
95+
if nick_cf == chan_cf:
12596
return "Profile data not available outside of channels"
12697

98+
chan_profiles = profile_cache.get(chan_cf, {})
99+
127100
# Split the text in to the nick and requested category
128101
unpck = text.split(None, 1)
102+
pnick = unpck.pop(0)
103+
pnick_cf = pnick.casefold()
104+
user_profile = chan_profiles.get(pnick_cf, {})
105+
if not user_profile:
106+
notice("User {} has no profile data saved in this channel".format(pnick))
107+
return
129108

130109
# Check if the caller specified a profile category, if not, send a NOTICE with the users registered categories
131-
if len(unpck) < 2:
132-
cats = []
133-
cat_pages[chan.lower()][nick.lower()] = []
134-
cat_page_index[chan.lower()][nick.lower()] = 0
135-
pnick = unpck[0]
136-
if len(profile_cache.get(chan.lower(), {}).get(pnick.lower(), {})) == 0:
137-
notice("User {} has no profile data saved in this channel".format(pnick))
138-
return
110+
if not unpck:
111+
cats = list(user_profile.keys())
139112

140-
for e in profile_cache.get(chan.lower(), {}).get(pnick.lower(), {}):
141-
cats.append(e)
142-
143-
out = "Categories: "
144-
for cat in cats:
145-
out += "{}, ".format(cat)
146-
out = smart_truncate(out)
147-
out = out[:-2]
148-
out = two_lines(out, chan, nick)
149-
if len(cat_pages[chan].get(nick, [])) > 1:
150-
msg = "{}(page {}/{}) .moreprofile".format(out, cat_page_index[chan.lower()][nick.lower()] + 1,
151-
len(cat_pages[chan.lower()][nick.lower()]))
152-
for line in msg.splitlines():
153-
notice(line)
154-
return
113+
pager = paginated_list(cats, ', ')
114+
cat_pages[chan_cf][nick_cf] = pager
115+
page = pager.next()
116+
page[0] = "Categories: {}".format(page[0])
117+
if len(page) > 1:
118+
page[-1] += " .moreprofile"
155119

156-
for line in out.splitlines():
120+
for line in page:
157121
notice(line)
158-
return
159-
160-
pnick, category = unpck
161-
if category.lower() not in profile_cache.get(chan.lower(), {}).get(pnick.lower(), {}):
162-
notice("User {} has no profile data for category {} in this channel".format(pnick, category))
163122

164123
else:
165-
content = profile_cache[chan.lower()][pnick.lower()][category.lower()]
166-
return format_profile(pnick, category, content)
124+
category = unpck.pop(0)
125+
cat_cf = category.casefold()
126+
if cat_cf not in user_profile:
127+
notice("User {} has no profile data for category {} in this channel".format(pnick, category))
128+
129+
else:
130+
content = user_profile[cat_cf]
131+
return format_profile(pnick, category, content)
167132

168133

169134
@hook.command()
170135
def profileadd(text, chan, nick, notice, db):
171136
"""<category> <content> Adds data to your profile in the current channel under \"<category>\""""
172-
if nick.lower() == chan.lower():
137+
if nick.casefold() == chan.casefold():
173138
return "Profile data can not be set outside of channels"
174139

175140
match = data_re.match(text)
176141

177142
if not match:
178143
notice("Invalid data")
179144
else:
145+
chan_profiles = profile_cache.get(chan.casefold(), {})
146+
user_profile = chan_profiles.get(nick.casefold(), {})
180147
cat, data = match.groups()
181-
if cat.lower() not in profile_cache.get(chan.lower(), {}).get(nick.lower(), {}):
182-
db.execute(table.insert().values(chan=chan.lower(), nick=nick.lower(), category=cat.lower(), text=data))
148+
if cat.casefold() not in user_profile:
149+
db.execute(
150+
table.insert().values(chan=chan.casefold(), nick=nick.casefold(), category=cat.casefold(), text=data))
183151
db.commit()
184152
load_cache(db)
185153
return "Created new profile category {}".format(cat)
186154

187155
else:
188-
db.execute(table.update().values(text=data).where((and_(table.c.nick == nick.lower(),
189-
table.c.chan == chan.lower(),
190-
table.c.category == cat.lower()))))
156+
db.execute(table.update().values(text=data).where((and_(table.c.nick == nick.casefold(),
157+
table.c.chan == chan.casefold(),
158+
table.c.category == cat.casefold()))))
191159
db.commit()
192160
load_cache(db)
193161
return "Updated profile category {}".format(cat)
@@ -196,17 +164,20 @@ def profileadd(text, chan, nick, notice, db):
196164
@hook.command()
197165
def profiledel(nick, chan, text, notice, db):
198166
"""<category> Deletes \"<category>\" from a user's profile"""
199-
if nick.lower() == chan.lower():
167+
if nick.casefold() == chan.casefold():
200168
return "Profile data can not be set outside of channels"
201169

202170
category = text.split()[0]
203-
if category.lower() not in profile_cache.get(chan.lower(), {}).get(nick.lower(), {}):
171+
172+
chan_profiles = profile_cache.get(chan.casefold(), {})
173+
user_profile = chan_profiles.get(nick.casefold(), {})
174+
if category.casefold() not in user_profile:
204175
notice("That category does not exist in your profile")
205176
return
206177

207-
db.execute(table.delete().where((and_(table.c.nick == nick.lower(),
208-
table.c.chan == chan.lower(),
209-
table.c.category == category.lower()))))
178+
db.execute(table.delete().where((and_(table.c.nick == nick.casefold(),
179+
table.c.chan == chan.casefold(),
180+
table.c.category == category.casefold()))))
210181
db.commit()
211182
load_cache(db)
212183
return "Deleted profile category {}".format(category)
@@ -215,23 +186,24 @@ def profiledel(nick, chan, text, notice, db):
215186
@hook.command(autohelp=False)
216187
def profileclear(nick, chan, text, notice, db):
217188
"""Clears all of your profile data in the current channel"""
218-
if nick.lower() == chan.lower():
189+
if nick.casefold() == chan.casefold():
219190
return "Profile data can not be set outside of channels"
220191

221192
if text:
222-
if nick in confirm_keys[chan.lower()] and text == confirm_keys[chan.lower()][nick.lower()]:
223-
del confirm_keys[chan.lower()][nick.lower()]
224-
db.execute(table.delete().where((and_(table.c.nick == nick.lower(),
225-
table.c.chan == chan.lower()))))
193+
if nick in confirm_keys[chan.casefold()] and text == confirm_keys[chan.casefold()][nick.casefold()]:
194+
del confirm_keys[chan.casefold()][nick.casefold()]
195+
db.execute(table.delete().where((and_(table.c.nick == nick.casefold(),
196+
table.c.chan == chan.casefold()))))
226197
db.commit()
227198
load_cache(db)
228199
return "Profile data cleared for {}.".format(nick)
229200
else:
230201
notice("Invalid confirm key")
231202
return
232203
else:
233-
confirm_keys[chan.lower()][nick.lower()] = "".join(random.choice(string.ascii_letters + string.digits) for i in range(10))
204+
key = "".join(random.choice(string.ascii_letters + string.digits) for i in range(10))
205+
confirm_keys[chan.casefold()][nick.casefold()] = key
234206
notice("Are you sure you want to clear all of your profile data in {}? use \".profileclear {}\" to confirm"
235-
.format(chan, confirm_keys[chan.lower()][nick.lower()]))
207+
.format(chan, key))
236208
return
237209

0 commit comments

Comments
 (0)