def step3(self, state, input, subreddit): """PotW Step 3 - Re-interpret promotions.""" r = self.r while True: message = "" removeIdxs = [] # remove those only after doing the other changes so that the indices dont get mixed up for line in input: if not line.strip(): continue parts = line.split(")") num = 0 try: num = int(parts[0]) except: message += "* Couldn't understand entry: " + line + "\n" continue num-=1 # CONVERT TO INTERNAL REPRESENTATION if num >= len(state["finalWinnerInfo"]) or num < 0: message += "* The entry id has to be one of the numbers in the last message in: " + line + "\n" continue if len(parts) < 2: message += "* The entry here lacks a command verb: " + line + "\n" continue # get the verb cmd = parts[1].lower().strip() if cmd.startswith("remove"): removeIdxs.append(num) elif cmd.startswith("change "): rankNameLower = cmd[len("change "):].lower().strip() newRank = RankUtils.getRankByName(rankNameLower) if newRank is None: message += "Couldn't find the rank " + \ rankNameLower + " in [{}]".format(num) continue state["finalWinnerInfo"][num]["promoteTo"] = newRank if state["finalWinnerInfo"][num]["promoteTo"] != state["finalWinnerInfo"][num]["currentRank"]: state["finalWinnerInfo"][num]["willBePromoted"] = True else: state["finalWinnerInfo"][num]["willBePromoted"] = False logging.info("Changed to newRank.") else: message += "* Warning: Did not understand the command '{}'".format(cmd) removeIdxs = sorted(removeIdxs, reverse=True) for idx in removeIdxs: del state["finalWinnerInfo"][idx] logging.info("Removed {}".format(idx)) message += "\n\n" message += formatFinalWinnerInfo(state["finalWinnerInfo"]) message += "\n\n" message += InfoMessages.promotionsCorrected self.messageHandler.dispatchOutput(message) message = "" inpt = self.waitForNewInstructions() if inpt is not None and len(inpt) > 0 and inpt[0].lower().startswith("okay"): break input = inpt message="Notes (If there's anything in this section, please heed the message):" # get the nominations nominationsThread = state["nominationsThread"] nominations = [] for comment in nominationsThread.comments: if not comment.is_root: message += "* Ignored [comment]({}) because it was not a root comment in the nominations thread.\n\n".format(comment.permalink) continue if comment.author is None or comment.author.name is None: message += "* Ignored deleted [comment]({}) in the nominations thread.\n\n".format(comment.permalink) continue if comment.banned_by is not None: message += "* Ignored removed [comment]({}) in the nominations thread.\n\n".format(comment.permalink) continue winnerInfo = RedditUtils.findFirstLinkedObject(comment.body, subreddit, r) if winnerInfo is None: message += "* Couldn't find the thread/comment referenced by the [nomination entry]({}). **Please handle this manually LATER. DON'T Forget.\n\n** ".format( comment.permalink) continue if winnerInfo[2].author is None: message += "* The comment linked to by this [nomination entry]({}) appears to have been deleted. **Please handle this manually LATER. DON'T Forget.\n\n** ".format( comment.permalink) continue # get the user from the nomination comment username = "" match = re.search(r'\/u\/(.*?)\s', comment.body, re.IGNORECASE) if match is None: message += ("* The user in [this]({}) nomination comment didn't specify a username. Better double-check" + " that they at least got the permalink right and didn't accidentially link the thread. " + "If they did mess up, remove the entry from the bots nominations in the next step and add it to the vote thread after the bot has finished. **DON'T Forget**\n\n** ").format(comment.permalink) username = winnerInfo[2].author.name else: username = match.group(1).strip() # ensure that they are both the same - reddit usernames are case # insensitive if username.lower() != winnerInfo[2].author.name.lower(): message += "This [nomination entry]{}'s link doesn't match the user they nominated. **Please handle this manually LATER (after the bot has finished). DON'T Forget.\n\n** ".format(comment.permalink) continue nominations.append({"name": winnerInfo[2].author.name, "reason": winnerInfo[ 0], "permalink": winnerInfo[1]}) state["nominations"] = nominations state["messageCarryover"] = message return state
def step2(self, state, input, subreddit): """Post of the Week Step 2 - Calculate Promotions""" if len(input) < 1 or len(input) > 2: state["error"] = "Your comment has a line number other than two or one. Please keep to the aforementioned syntax." return state if not input[0].lower().startswith("potw "): state["error"] = "Your comments first line must start with 'POTW '" return state potwIndices = input[0][len("POTW "):].split(" ") ecIndices = [] if len(input) == 2: if not input[1].lower().startswith("ec "): state["error"] = "Your comments second line must start with 'EC '" return state ecIndices = input[1][len("EC "):].split(" ") # important: user-facing indices are in [1,n+1], move them back to [0,n], also convert them to numbers try: potwIndices = [int(x)-1 for x in potwIndices] ecIndices = [int(x)-1 for x in ecIndices] # remove duplicates potwIndices = list(set(potwIndices)) ecIndices = list(set(ecIndices)) if any([x < 0 or x >= len(state["voteEntries"]) for x in potwIndices]): raise ValueError("") if any([x < 0 or x >= len(state["voteEntries"]) for x in ecIndices]): raise ValueError("") except: return {"error": "Some of the indices you provided were not numbers, or not within the required range."} if len(set(ecIndices) ^ set(potwIndices)) != len(ecIndices) + len(potwIndices): return {"error": "No post can win BOTH Exemplary Contribution and Post of the Week."} # Assemble the winners and candidates for the CPO position cpoCandidates = [] # list of (user, current_flair_css) winnersInfo = [] for idx,voteEntry in enumerate(state["voteEntries"]): # Do not check for winning-state in a misguided attempt at optimization here, need to go through it all to be # able to see if anyone's rank entitles them to a CPO promotion. winnerInfo = RedditUtils.findFirstLinkedObject(voteEntry.body, subreddit, self.r) if winnerInfo is None: state["error"] = "Couldn't find the thread/comment referenced by the vote entry {}\n\n".format(idx) return state linkText, linkURL, contributionObject = winnerInfo # get the user from the vote comment match = re.search(r'\/u\/(.*?)\s', voteEntry.body, re.IGNORECASE) if match is None: state["error"] = "Couldn't find the username from the vote comment for '{}'\n\n".format(voteEntry.body) return state # ensure that they are both the same username = match.group(1).strip() if contributionObject.author is None or username.lower() != contributionObject.author.name.lower(): state["error"] = ("Username in the voting thread doesn't match the username of the nominated object." + " Username: {}, Nominated Object: {}").format(username, idx) return state winnerUser = contributionObject.author # returns dict {"flair_css_class":?, "flair_text":?, "user":?} winnerFlair = self.r.get_flair(subreddit, winnerUser) winnerCurrentRank = RankUtils.getRankLevelFromFlair(winnerFlair["flair_css_class"]) # cpo possible at all? if winnerCurrentRank in RankUtils.cpoStepRanks: cpoCandidates.append((winnerUser, winnerFlair["flair_css_class"] if winnerFlair["flair_css_class"] is not None else "")) logging.info("CPO Candidate: " + winnerUser.name) if not idx in potwIndices+ecIndices: continue logging.info("{} - {} - {}".format(winnerUser.name, winnerCurrentRank, RankUtils.rankLevels[winnerCurrentRank]["name"])) winnersInfo.append({"user": winnerUser, "currentRank": winnerCurrentRank, "promotionKind": "potw" if idx in potwIndices else "ec", "comment": voteEntry, "text": winnerInfo[0]}) notes = "" message = "" finalWinnerInfo = [] # now that we have all the winners, lets see who gets promoted to what for wi in winnersInfo: # calculate the promotion of that person, to that end get his service # record username = wi["user"].name careerPotW = 0 careerDelphi = 0 total = 0 serviceRecord = self.r.get_wiki_page(subreddit, username) # check if the record exists. This combersome way currently seems to be # the only working one pageExists = False try: serviceRecord.content_md pageExists = True except: pageExists = False temporaryNoPromotion = False if serviceRecord is None or pageExists is False: if wi["currentRank"] >= RankUtils.serviceRecordThresholdRank: # Ensign ought to have a page ... notes += "* Person /u/{} ought to have a service record, but hasn't gotten one. Setting them to not promoted temporarily, **please review and, if applicable, change this**.\n".format(username) temporaryNoPromotion = True else: # split into lines lines = serviceRecord.content_md.split("\n") ret = RankUtils.analyzeServiceRecord(lines) if ret is None: notes += "* Couldn't read the [service record](https://reddit.com/r/DaystromInstitute/wiki/{}) of user /u/{}. Setting them to not promoted temporarily, **please review and, if applicable, change this**.\n".format(username, username) temporaryNoPromotion = True else: careerPotW, careerDelphi, total = ret logging.info("Analyzed the service record of /u/{}. They have {} PotW, {} DELPHI totalling {}".format(username, careerPotW, careerDelphi, total)) # check the next promotion total += 1 # if they were promoted careerPotW += 1 resDict = {"serviceRecord": serviceRecord, "user": wi["user"], "currentRank": RankUtils.rankLevels[wi["currentRank"]], "promotionKind": wi["promotionKind"], "comment": wi["comment"], "text": wi["text"]} res = None newRank = wi["currentRank"] if temporaryNoPromotion: res = (False, "") elif wi["currentRank"] < 3: # crewman, cpo and citizen get autopromoted to ensign on PotW res = (True, "") newRank = RankUtils.stepFinishRank # ensign elif wi["currentRank"] < len(RankUtils.rankLevels) - 1: nextRank = RankUtils.rankLevels[wi["currentRank"] + 1] res = nextRank["advanceFunction"](total, careerPotW, careerDelphi) newRank = newRank + 1 else: res = (False, "") resDict["willBePromoted"] = res[0] resDict["promoteAftermessage"] = res[1] resDict["promoteTo"] = RankUtils.rankLevels[newRank] finalWinnerInfo.append(resDict) # format this for the message message += "\n\n" message += formatFinalWinnerInfo(finalWinnerInfo) message += "\nPlease note: \n\n" message += notes message += "\n" + InfoMessages.step2 self.messageHandler.dispatchOutput(message) # put it into the state state["finalWinnerInfo"] = finalWinnerInfo state["cpoCandidates"] = cpoCandidates state["reminders"].append(notes) return state