1+ import random
12import re
23import string
3- import random
44from collections import defaultdict
55
66from sqlalchemy import Column
1010
1111from cloudbot import hook
1212from cloudbot .util import database
13+ from cloudbot .util .pager import paginated_list
1314
1415category_re = r"[A-Za-z0-9]+"
1516data_re = re .compile (r"({})\s(.+)" .format (category_re ))
1617
1718# borrowed pagination code from grab.py
1819cat_pages = defaultdict (dict )
19- cat_page_index = defaultdict (dict )
2020confirm_keys = defaultdict (dict )
2121
2222table = 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-
7248def 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 )
8157def 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 ()
11889def 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 ()
170135def 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 ()
197165def 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 )
216187def 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