def login(self, username, password, serverNumber=0): """ Perform a KoL login given a username and password. A server number may also be specified to ensure that the user logs in using that particular server. This can be helpful if the user continues to be redirected to a server that is down. """ self.userName = username self.userPasswordHash = hashlib.md5(password).hexdigest() # Grab the KoL homepage. homepageRequest = HomepageRequest(self, serverNumber=serverNumber) homepageResponse = homepageRequest.doRequest() self.serverURL = homepageResponse["serverURL"] # Perform the login. loginRequest = LoginRequest(self, homepageResponse["loginChallenge"]) loginRequest.doRequest() # Load the charpane once to make StatusRequest report the rollover time charpaneRequest = CharpaneRequest(self) charpaneRequest.doRequest() # Get pwd, user ID, and the user's name. request = StatusRequest(self) response = request.doRequest() self.pwd = response["pwd"] self.userName = response["name"] self.userId = int(response["playerid"]) self.rollover = int(response["rollover"])
def login(self, username, password, serverNumber=0): """ Perform a KoL login given a username and password. A server number may also be specified to ensure that the user logs in using that particular server. This can be helpful if the user continues to be redirected to a server that is down. """ self.userName = username self.userPasswordHash = hashlib.md5(password).hexdigest() # Grab the KoL homepage. homepageRequest = HomepageRequest(self, serverNumber=serverNumber) homepageResponse = homepageRequest.doRequest() self.serverURL = homepageResponse["serverURL"] # Perform the login. loginRequest = LoginRequest(self, homepageResponse["loginChallenge"]) loginRequest.doRequest() # Load the charpane once to make StatusRequest report the rollover time charpaneRequest = CharpaneRequest(self) charpaneRequest.doRequest() # Get pwd, user ID, and the user's name. request = StatusRequest(self) response = request.doRequest() self.pwd = response["pwd"] self.userName = response["name"] self.userId = int(response["playerid"]) self.rollover = int(response["rollover"])
def _buff(self, uid, buff): n = buff['casts'] cost = buff['mp_cost'] mpRequired = n * cost r2 = StatusRequest(self.session) d2 = self.tryRequest(r2) mpBefore = int(d2['mp']) self.log("Preparing to cast buff {} (requires {} mp)" .format(buff['description'], mpRequired)) if mpBefore < mpRequired: try: self.debugLog("Requesting healing from module {}" .format(self._healer)) replies = self._raiseEvent("heal", self._healer, {'type': 'mp', 'points': mpRequired, 'percent': self._mpMax}) healResult = replies[-1].data mpBefore = healResult['mp'] except IndexError: raise FatalError("Invalid healer {}".format(self._healer)) self.log("Casting skill {} x{}".format(buff['id'], n)) r1 = UseSkillRequest(self.session, str(buff['id']), n, uid) _d1 = self.tryRequest(r1, numTries=1) r2 = StatusRequest(self.session) d2 = self.tryRequest(r2) mpAfter = int(d2['mp']) self.log("Used {} mp. (Now at {}/{})" .format(mpBefore - mpAfter, mpAfter, d2['maxmp'])) return mpBefore - mpAfter
def initialize(self, lastKnownState, initData): super(PeriodicAnnouncementModule, self).initialize(lastKnownState, initData) for k,v in lastKnownState['messages'].items(): if v['order'] is None: v['order'] = [] if k in self._messages and v['hard']: messageEntry = self._messages[k] messageEntry['last'] = int(v['last']) if v['mode'] != messageEntry['mode']: messageEntry.update({'order': [], 'index': 0}) else: order = v['order'] if order: for k2 in messageEntry['messages'].keys(): if k2 not in order: order.append(k2) messageEntry.update( {'order': order, 'index': min(v['index'], len(order))}) elif not v['hard']: self._messages[k] = v self._initializationValid = True r2 = StatusRequest(self.session) d2 = self.tryRequest(r2) self._rolloverTime = int(d2['rollover']) self._run.set()
def hpMpStatus(self): rStat = StatusRequest(self.session) dStat = (self.tryRequest(rStat)) return { 'hp': int(dStat['hp']), 'mp': int(dStat['mp']), 'maxhp': int(dStat['maxhp']), 'maxmp': int(dStat['maxmp']) }
def _checkStock(self): with InventoryLock.lock: self._invMan.refreshInventory() inv = self._invMan.completeInventory() r = StatusRequest(self._s) d = tryRequest(r, numTries=6, initialDelay=3, scaleFactor=1.25) meat = int(d['meat']) itemsOwed = {} meatOwed = 0 con = self._db.getDbConnection() c = con.cursor() c.execute("SELECT * FROM {}".format(self._name)) msg = c.fetchone() while msg is not None: if msg['state'] in [self.OUTBOX_SENDING, self.OUTBOX_DEFERRED]: message = decode(msg['data']) itemsOwed = _itemsToDict(message.get('items', []), itemsOwed) meatOwed += message.get('meat', 0) msg = c.fetchone() difference = dict((iid, inv.get(iid, 0) - qty) for iid,qty in itemsOwed.items()) deficit = dict((iid, -diff) for iid,diff in difference.items() if diff < 0) if deficit: # get items in display case r2 = GetDisplayCaseRequest(self._s) d2 = tryRequest(r2) display = _itemsToDict(d2['items']) difference = dict( (iid, inv.get(iid, 0) + display.get(iid, 0) - qty) for iid,qty in itemsOwed.items()) deficit = dict((iid, -diff) for iid,diff in difference.items() if diff < 0) if deficit or meatOwed > meat: # notify admins of item deficit! warningText = ("Warning: {} has an item deficit of: \n" .format(self._props.userName)) for iid, d in deficit.items(): warningText += ("\n{}: {}" .format( d, getItemFromId(iid).get( 'name', "item ID {}".format(iid)))) if meatOwed > meat: warningText += "\n{} meat".format(meatOwed-meat) with con: c2 = con.cursor() for adminUid in self._props.getAdmins("mail_fail"): # notify admins of deficit newMsg = {'userId': adminUid, 'meat': 0, 'text': warningText, 'items': []} self._insertSplitKmail(c2, self.OUTBOX_SENDING, newMsg, reserveItems=False)
def heal(self, hint, args, status): if args.get('__lucifer__', False): self.parent.log("Skipping Lucifer (no recursive Lucifer allowed)") return with InventoryLock.lock: invMan = self.parent.inventoryManager invMan.refreshInventory() inv = invMan.inventory() if inv.get(571, 0) == 0: qty = 0 if self.buyFromMall: try: qty = buyFromMall(self.parent.session, 571, logFunc=self.parent.log) except kol.Error.Error: pass if qty == 0: self.parent.log("Out of Lucifers.") return r1 = StatusRequest(self.parent.session) d1 = tryRequest(r1) if self.maxFull is not None and int(d1['full']) >= self.maxFull: self.parent.log("Reached reserve fullness.") return mpToHeal = status['maxmp'] - status['mp'] hpNeeded = min(1 + (mpToHeal / 9 + 1), status['maxhp']) self.parent.log("Lucifer: requires {} hp, have {} hp.".format( hpNeeded, status['hp'])) if hpNeeded > status['hp']: curHp = None self.parent.log("Healing for Lucifer...") if self.extHealer is None: reply = self.parent._heal({ 'type': 'hp', 'points': hpNeeded, '__lucifer__': True }) curHp = reply['hp'] else: replies = self.parent._raiseEvent( "heal", "__" + self.extHealer + "__", { 'type': 'hp', 'points': hpNeeded, '__lucifer__': True }) curHp = replies[-1]['hp'] if curHp < hpNeeded: self.parent.log("Failed to heal for Lucifer.") return self.parent.log("Healed for Lucifer!") r2 = EatFoodRequest(self.parent.session, 571) try: tryRequest(r2, numTries=1) except kol.Error.Error as e: self.parent.log("Lucifer error: {}".format(e[0]))
def _online(self): now = time.time() if now - self._lastOnlineCheck <= 2: return True try: tryRequest(StatusRequest(self._s), nothrow=False, numTries=6, initialDelay=10, scaleFactor=1) self._lastOnlineCheck = now return True except (kol.Error.Error, urllib2.HTTPError): pass return False
def canReceiveItems(self): now = time.time() if now - self._lastCanReceiveItemsCheck <= 3600: return self._lastCanReceiveItems self._lastCanReceiveItemsCheck = now r = StatusRequest(self._s) d = tryRequest(r, numTries=6, initialDelay=3, scaleFactor=1.25) canReceive = ((int(d.get('hardcore',"1")) == 0 and int(d.get('roninleft',"1")) == 0) or int(d.get('casual',"0")) == 1 or int(d.get('freedralph',"0")) == 1) self._lastCanReceiveItems = canReceive return canReceive
def buyFromMall(session, itemId, quantity=1, maxPrice=0, logFunc=None): if logFunc is None: logFunc = _empty s = tryRequest(StatusRequest(session)) canBuy = ((int(s.get('hardcore', "1")) == 0 and int(s.get('roninleft', "1")) == 0) or int(s.get('casual', "0")) == 1 or int(s.get('freedralph', "0")) == 1) if not canBuy: raise kol.Error.Error("Can't buy from mall in Ronin/Hardcore", kol.Error.USER_IN_HARDCORE_RONIN) with InventoryLock.lock: item = getItemFromId(itemId) itemName = item.get('name', str(itemId)) numTries = 0 numBought = 0 numResults = 10 logFunc("Trying to buy {}x {} from mall...".format(quantity, itemName)) while numTries < 10: r1 = MallItemSearchRequest(session, itemName, maxPrice=maxPrice, numResults=numResults) d1 = tryRequest(r1, numTries=1) itemList = [ item for item in d1['results'] if item.get('id', -1) == itemId ] listFull = (len(itemList) == numResults) availableList = [ item for item in itemList if not item.get('hitLimit', False) ] if not itemList: return numBought if not availableList and listFull: numResults *= 2 while availableList and numBought < quantity: item = availableList[0] limitedMode = False qty = min(quantity, item['quantity']) if 'limit' in item: qty = 1 limitedMode = True price = item['price'] storeId = item['storeId'] logFunc("Buying {}x {} @ {} meat from store {}".format( qty, itemName, price, storeId)) r2 = MallItemPurchaseRequest(session, storeId, itemId, price, qty) try: d2 = tryRequest(r2, numTries=1) numBought += d2['items'][0]['quantity'] logFunc("Spent {} meat and got {}x {}.".format( d2['meatSpent'], d2['items'][0]['quantity'], itemName)) if not limitedMode: availableList.pop(0) except kol.Error.Error as e: if e.code == kol.Error.ITEM_NOT_FOUND: logFunc("Could not buy item. Refreshing...") # refresh search availableList = [] continue else: logFunc("Error buying from this store. Moving on...") availableList.pop(0) continue numTries += 1 if numBought >= quantity: break return numBought
def __init__(self, s, c, props, inv, configFile, db, exitEvent): """ Initialize the BotSystem """ self._exitEvent = exitEvent self._initialized = False # trigger to stop heartbeat subsystem self._hbStop = threading.Event() # start subsystems try: oldTxt = None hbSys = HeartbeatSubsystem(numThreads=6, period=5, stopEvent=self._hbStop) evSys = EventSubsystem() # initialize subsystems super(BotSystem, self).__init__(name="sys.system", identity="system", evSys=evSys, hbSys=hbSys) if exitEvent.is_set(): sys.exit() # copy arguments self._s = s self._c = c self._props = props self._inv = inv self._db = db self._log = logging.getLogger() # initialize some RunProperties data now that we are logged on self._log.debug("Getting my userId...") r1 = StatusRequest(self._s) d1 = tryRequest(r1) self._props.userId = int(d1['playerid']) self._log.info("Getting my clan...") r2 = UserProfileRequest(self._s, self._props.userId) d2 = tryRequest(r2) self._props.clan = d2.get('clanId', -1) self._log.info("I am a member of clan #{} [{}]!".format( self._props.clan, d2.get('clanName', "Not in any clan"))) # config file stuff self._config = None self._overWriteConfig = False oldTxt = self._loadConfig(configFile) # listen to channels self._initializeChatChannels(self._config) c.getNewChatMessages() # discard old PM's and whatnot # initialize directors self._dir = None iData = InitData(s, c, props, inv, db) self._log.info("Starting communication system...") self._dir = CommunicationDirector(self, iData, self._config['director']) self._lastCheckedChat = 0 self._initialized = True except: self._hbStop.set() raise finally: # rewrite config file (values may be modified by modules/managers) if oldTxt is not None: self._saveConfig(configFile, oldTxt)
def _finishInitialization(self): r2 = StatusRequest(self.session) d2 = self.tryRequest(r2) self._rolloverTime = int(d2['rollover'])
def getUniqueDateString(session): r = StatusRequest(session) d = tryRequest(r) s = str(d['rollover']) return s
def _finishInitialization(self): # get list of clan members (both in whitelist and roster) self.log("Initializing ranks...") r1 = ClanWhitelistRequest(self.session) d1 = self.tryRequest(r1) self._ranks = { _rankTransform(rank['rankName']): rank for rank in d1['ranks'] } r2 = StatusRequest(self.session) d2 = self.tryRequest(r2) self._rolloverTime = int(d2['rollover']) # load promotion rules self._promotionRules = {} for rankname, rule in self._ruleConfig.items(): key = _rankTransform(rankname) nextRankName = toTypeOrNone(rule['next_rank']) nextkey = _rankTransform(nextRankName) if nextRankName else None nextRankId = self._ranks.get(nextkey, {}).get('rankId') if key not in self._ranks: raise FatalError( "Invalid clan rank: {} (available ranks: {})".format( key, ", ".join(self._ranks.keys()))) if nextkey is not None and nextkey not in self._ranks: raise FatalError( "Invalid clan rank: {} (available ranks: {})".format( nextkey, ", ".join(self._ranks.keys()))) try: self._promotionRules[self._ranks[key]['rankId']] = ({ 'demotionAllowed': stringToBool(rule['demotion_allowed']), 'minKarma': int(rule['min_karma']), 'minDaysBeforePromotion': int(rule['min_days_until_next_promotion']), 'minDaysInClan': int(rule['min_days_in_clan']), 'nextRankId': nextRankId, 'rankName': rankname }) except ValueError: raise "ClanRankModule: error parsing rank {}".format(rankname) # pick a random time to run today assumedExecTime = 7200 latestPossibleExecTime = self._rolloverTime - assumedExecTime if time.time() < self._lastRun: self.log("Already performed ranking today.") elif time.time() > latestPossibleExecTime: self.log("Too late to run rankings today.") else: if self._immediate: self._execTime = int(time.time()) + 15 else: self._execTime = random.randint(int(time.time()), latestPossibleExecTime) self.log("Running rankings in {} minutes.".format( int((self._execTime - time.time()) / 60)))
def _processCommand(self, message, cmd, args): if cmd == "die": args = args.strip() if args == "": args = "0" try: t = int(args) e = kol.Error.Error("Manual crash") e.timeToWait = t * 60 + 1 self.chat("Coming online in {} minutes.".format(t)) raise e except kol.Error.Error: raise except: pass return "Invalid argument to !die '{}'".format(args) elif cmd == "simulate": self.parent.director._processChat([{ 'text': args, 'userName': "******", 'userId': -2, 'channel': "hobopolis", 'type': "normal", 'simulate': True }]) return "Simulated message: {}".format(args) elif cmd == "spam": return "\n".join(["SPAM"] * 15) elif cmd == "restart": self._raiseEvent("RESTART", "__system__") elif cmd == "raise_event": r = self._raiseEvent(args) return "Reply to event '{}': {}".format(args, r) elif cmd == "kmail_test": n = 1000 try: n = int(args) except Exception: pass text = "" count = 0 startChar = "A" while n >= 100: count += 100 n -= 100 newText = "A" * 100 + "{}".format(count) newText = startChar + newText[-99:] text += newText startChar = " " k = Kmail(message['userId'], text) self.sendKmail(k) elif cmd == "bot_status": r = StatusRequest(self.session) d = self.tryRequest(r) return "\n".join("{}: {}".format(k, v) for k, v in d.items() if k not in ["pwd", "eleronkey"]) elif cmd == "inclan": tf = self.parent.checkClan(int(args)) return str(tf) elif cmd == "plist": return str(self.properties.getPermissions(int(args))) elif cmd == "say": txtMatch = re.search(r"^\s*(?:/(\w+))?\s*(.*)", args) if txtMatch: self.chat(txtMatch.group(2), channel=txtMatch.group(1)) return None return "Error matching text" return None