#must include user agent, accept, content type (if post), otherwise ssc will complain
#if no pname+tname specified, session cookie is needed to avoid "Please wait while your request is processed - this may take up to a minute to complete."
headers={
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/114.0",
#after we sort through that section should not be bound to a specific section now since the data is in user.list
#we do need to set data though to allow other things (e.g. try_ping) to match on whether its in search or section mode
obj.section=True
courses=set(obj.metadata.user[user_id])
#now we have the proper name for the pinger
name=str(obj)
logger.info(f'Adding {name} to the pingers...')
merged=False
forpinger_name,pinger_objincoursedict(data):#coursedict not only for easier editing but also for copying for iteration (otherwise "dict was modified during iteration" might pop up)
#there should only be one of the cases below: either existing pinger(s) are subsets of the current one, or this pinger is a subset of an existing pinger
#otherwise it shouldve been optimized out since it is sequentially added and this optimzation runs every time
ifcourses.issuperset(against):
logger.info(f' Optimization: merging {pinger_name} into {name}')
logger.info(f' Optimization: merging {name} into {pinger_name}')
name=pinger_name
merged=True
break
ifnotmerged:
data[name]=obj
set_data(data)
#returns the list of courses this pinger will ping, and the name (changed if merged)
returnname,"\n".join(obj.metadata.user[user_id])
else:
raiseValueError('No sections found!')
#done add ppl to existing pingers? see above
#done optimization for when multiple people are looking at the same course but want different sections pinged (and not all of them)
#TODO currently the way courses in section mode are handled means that they basically dont get optimized into search modes
#it would be better if pinger.metadata.courses tracks the total set of courses its users are pinging instead,
#and then have the optimizer do special code on reading section mode to not match on the courses in the list, but on the course names
#aka if everything a user is pinging has the same prefix of CPSC 110 for example,
#then just throw it into CPSC 110 section mode regardless even if currently the CPSC 110 pinger.metadata.courses does not have it
#this means that the following has to be changed:
# - instead of obj.metadata.courses = list(resp.keys()), do obj.metadata.courses = <total of all user lists>
# - at the superset checks, add check on whether everything being added is in the same course, and if there is a course pinger in section mode for that already
#edit the menu instead of sending another ephemeral msg since we cant delete the old one
#TODO figure out a better way to print since ill be using that to show a dropdown menu for the removal ui too
awaitinteraction.response.edit_message(content=f'The pinger `{name}` is now tracking the requested classes! This includes the following:\n```\n{added}\n```\nYou will now get pinged when it opens up.',view=None)
exceptExceptionase:
logger.error('AddCourseSearch fail:',exc_info=e)
awaitinteraction.response.edit_message(content=f'Something went wrong (likely wrong params): {type(e)}\n{e}',view=None)
classAddCourseBase(ui.Modal,title='Add a course to ping'):
#cringe discord.py does some cringe things on reading global members but not the constructors before calling super.__init__ so put it here instead even though this means the values are shared across all instances
section=ui.TextInput(label='Section',placeholder='e.g. 101, T2*, 10*/201/T1D, leave blank to open search menu',min_length=1,max_length=3,required=False)
session=ui.TextInput(label='Session',placeholder='e.g. 2022S, 2023W, defaults to coming session',min_length=5,max_length=5,required=False)
#response in this case is the same msg as the button, dont do anything to it
awaitinteraction.response.defer()
#parse session
self.sesobj={}
ifself.session.value:
self.sesobj={
'sessyr':self.session.value[:4],
'sesscd':self.session.value[-1],
}
ifnotself.section.value:
#discord doesnt allow us to send another modal right away? (yep check InteractionResponseType, 9 is modal and not in the list)
awaitinteraction.followup.send('Configure the search parameters below:',view=AddCourseSearch(self),ephemeral=True)
else:
if'*'inself.subj.valueor'*'inself.course.value:
awaitinteraction.response.send_message('Wildcards for subject and courses are not allowed in section mode! Leave section blank to do a broad search.',ephemeral=True)
return
obj=coursedict({
'subj':self.subj.value,
'crsno':self.course.value,
#even though we dont actually use the wildcard we need to differentiate between no section and a fully wildcard section so dont remove yet
'section':self.section.value,
**self.sesobj,
#we are currently not in coursedict yet so the int to str normalization doesnt apply here, apply ourselves
awaitinteraction.followup.send(f'The pinger `{name}` is now tracking the requested classes! This includes the following:\n```\n{added}\n```\nYou will now get pinged when it opens up.',ephemeral=True)
exceptExceptionase:
logger.error('AddCourseBase fail:',exc_info=e)
awaitinteraction.followup.send(content=f'Something went wrong (likely wrong params): {type(e)}\n{e}',ephemeral=True)
#TODO dm option? or maybe just only allow dms (iirc the reason why i dont dm is coz theres no mechanism to dm multiple ppl or sth so i just send a single msg to ping everyone)
#common class for listing courses (used by remove pingers, and also pinger list)
#to be consistent we should use self.user instead of interaction.user since they might be different (even though when ephemeral=True they should be the same)
awaitinteraction.response.edit_message(content=f"Successfully removed your selected sections from the pinger `{self.pinger_name}` as follows:\n```\n{values}\n```\nYou will no longer get pinged even if the pinger sees the sections.",view=None)
awaitinteraction.followup.send("Choose a pinger to view sections",view=awaitViewCourseList.create(interaction.user),ephemeral=True)
exceptValueError:
awaitinteraction.followup.send("You currently have no pingers!",ephemeral=True)
#done remove button (only allow ppl to remove their own, except if user id matches me so i can actually do cleanup and stuff without going into the server)
#TODO button for viewing what courses you get pinged for in a pinger? rn its not too descriptive since the optimizer makes it the broadest possible (now that ppl can remove sections the searches might also need to be refined the same way (aka dynamically resize on remove or sth; idk sounds kinda hard))
#TODO that button would probably also work as a filtering system for when you dont need that many courses anymore