def savechars(self): ''' Saves current characters to file ''' console = ConsoleEngine() t = console.logtask("Saving character sessions to file...") with self.filelock: # Locks cannot be pickled, so prepare a lockless (shallow) copy charcopy = {} for x in self.getchars(): char = self.chars[x] # Acquire all char locks while we rig up conn_session char.lock.acquire() charcopy[char.name] = copy.copy(char) charcopy[char.name].lock = None # Hack conn_session into existence charcopy[char.name].conn_session.__attrs__.append('robin') if (os.path.exists(Config.charsave)): # Make a backup of previous save shutil.copy(Config.charsave, Config.charsave + ".bak") f = open(Config.charsave, 'w') pickle.dump(charcopy, f) f.close() # Undo our conn_session rigging so that it can be used properly again for x in self.getchars(): self.chars[x].conn_session.__attrs__.remove('robin') self.chars[x].lock.release() console.logstatus(t, "[OK]")
def addchar(self, option, agent): if (option.lower() == "adb"): console = ConsoleEngine() t = console.logtask("Character add from ADB requested... (Ctrl-C cancels)") try : f = os.popen("adb -d logcat -d") d = f.read() c = re.findall(r"session_id=[a-z0-9]*&viewer_id=[0-9]*", d) c.reverse() except KeyboardInterrupt: console.logstatus(t, "[CANCELLED]") return "CANCELLED" if (len(c) == 0): console.logstatus(t, "[FAILED]") return "FAILED" else: rawsess = c[0] console.logstatus(t, "[OK]") else: rawsess = option rawsess = rawsess.split("&") session = rawsess[0].split("=")[1].strip() viewer = rawsess[1].split("=")[1].strip() char = ROBChar(session, viewer, agent) # If under maintenance if self.conn.maintenance: return "MAINTENANCE" else: self.chars[char.name] = char return char.name
def update(self): with self.lock: conn = ROBConn() console = ConsoleEngine() t = console.logtask("Updating stats for [{}]".format(self.name)) self.updating = True # When doing arbitrary updates, emulate navbar press (i.e. send session) soup = conn.geturl('mypage', self.conn_session, send_session=self.session, send_viewer=self.viewer) soup = self.handlewelcome(soup) # Hmm. We only need to do this once, really. self.name = soup.find("div", "area_name").get_text().strip() self.conn_session.robin['name'] = self.name # Stats rawcards = soup.find("a", text="Cards").parent.find_next_sibling().get_text().split("/") self.cards = int(rawcards[0]) self.maxcards = int(rawcards[1]) self.rupies = soup.find('span', text=re.compile("Rupies:")).parent.find_next_sibling().get_text() self.rupies = int(self.rupies) rawstam = soup.find(text=re.compile("STAMINA:")).parent.span.get_text().strip().split("/") self.stamina = int(rawstam[0]) self.maxstamina = int(rawstam[1]) # Level and attribute points rawlevel = soup.find(text=re.compile("LVL:")).find_parent().span.get_text().strip() rawlevel = re.match("([0-9]*)(\(([0-9]*)\))?", rawlevel) self.level = int(rawlevel.group(1)) if (rawlevel.group(3) is not None): self.points = int(rawlevel.group(3)) else: self.points = 0 # Scrape Inbox inboxlinks = soup.find(text=re.compile(r'Inbox(?! \()')).parent.find_next_sibling().find_all("a") self.inbox = [(re.match("http://bahamut-n.cygames.jp/bahamut_n/(.*)", x['href']).group(1), x.get_text()) for x in inboxlinks] self.updating = False self.lastupdatetime = datetime.datetime.now() # Set next update anywhere between 10mins and 90mins self.nextupdate = datetime.timedelta(seconds=(random.randint(10, 90) * 60)) console.logstatus(t, "[OK]")
def _dispatcher_run(self, pre_console, work_target, work_extra_args): # Preamble console = ConsoleEngine() console.log("== Started: " + pre_console) # Start the actual worker thread work_args = (self.queue, self.workerqueue) work_args += work_extra_args self.worker = threading.Thread(target=work_target, args=work_args) self.worker.start() # Process output t = self.queue.get(True) while t != "END": if t == "CANCEL": self.workerqueue.put("CANCEL") t = self.queue.get(True) console.log("-- Cancelled: " + pre_console) self.cancelled = True continue self.client.send_cc(t) t = self.queue.get(True) # We made it if not self.cancelled: console.log("== Done: " + pre_console) # Hackity hack up a sendprompt() from telnet import TelnetEngine telnet = TelnetEngine() telnet.sendprompt(self.client)
def geturl(self, url, conn_session, send_session=None, send_viewer=None, get_peripherals=True, postdata=None, extra_headers={}, flash=False, spammy=False): ''' Makes a request to the ROB servers. Will not honour requests that have no user-agent, or are made with an expired session @keyword url: The url to request, relative to the ROB host @keyword conn_session: The requests session associated with a character @keyword send_session: The character session string to send, None if not needed @keyword send_viewer: The character viewer string to send, None if not needed @keyword get_peripherals: Should we parse for and request CSS and images? @keyword postdata: Data to send with POST requests @keyword extra_headers: Dictionary of extra headers to send @keyword flash: Is the target a flash file? @keyword spammy: Is the request a spammy one? ''' console = ConsoleEngine() # Extract viewer from conn_session for our internal use even if send_viewer is None id_viewer = conn_session.robin['viewer'] # Make sure that unclean shutdowns etc don't interfere with our request if 'robin' in conn_session.__attrs__: conn_session.__attrs__.remove('robin') # Prepare url finalurl = StringIO() finalurl.write('http://bahamut-n.cygames.jp/bahamut_n/') finalurl.write(url) if (send_session or send_viewer): finalurl.write('?') if (send_session): finalurl.write('session_id={}'.format(send_session)) if (send_viewer): finalurl.write('&') if (send_viewer): finalurl.write('viewer_id={}'.format(send_viewer)) url = finalurl.getvalue() if (flash): extra_headers['Accept'] = None # Check if user-agent is set uafound = False for x in conn_session.headers.keys(): if (x.lower() == "user-agent"): uafound = True for x in extra_headers.keys(): if (x.lower() == "user-agent"): uafound = True if not uafound: console.log("** ERROR: User-Agent not set properly for [{}]!".format(conn_session.robin['name'])) return -1 # Check if session has expired if conn_session.robin['expired']: console.log("** ERROR: Session for [{}] is expired!".format(conn_session.robin['name'])) return -2 # Perform actual request # Get spamlock first self.spamlock.acquire() # allow_redirects=False cannot be included with session so must be included here if (postdata is not None): if (postdata == ''): # No form data, but we need to add headers extra_headers['Content-Length'] = '0' extra_headers['Content-Type'] = "application/x-www-form-urlencoded" self.lastresponse[id_viewer] = conn_session.post(url, headers=extra_headers, allow_redirects=False, data=postdata) else: self.lastresponse[id_viewer] = conn_session.get(url, headers=extra_headers, allow_redirects=False) # Handle redirects properly, stripping custom headers etc while(self.lastresponse[id_viewer].status_code == 302): self.lastresponse[id_viewer] = conn_session.get(self.lastresponse[id_viewer].headers['Location'], allow_redirects=False, params={'viewer_id': id_viewer}) # If spammy, set time-release. If not, release now if spammy: t = threading.Timer(Config.spaminterval, self._spamrelease) t.name = "SpamLock" t.daemon = True t.start() else: self.spamlock.release() # Is ROB under maintenance if "/maintenance" in self.lastresponse[id_viewer].url: self.maintenance = True else: self.maintenance = False # Is the current session expired if "_reauthorize" in self.lastresponse[id_viewer].url: conn_session.robin['expired'] = True console.log("** ERROR: Session for [{}] is expired!".format(conn_session.robin['name'])) return -2 # If touching a flash file, we're done if (flash): return # Read server response and grab peripherals if needed soup = BeautifulSoup(self.lastresponse[id_viewer].text,'lxml') # Parse for peripheral items to retrieve if (get_peripherals): self.getperipherals(conn_session, soup, referer=self.lastresponse[id_viewer].url) return soup
def loadchars(self): ''' Loads characters from file Characters loaded are automatically scheduled for an update between 0.5 and 10mins later ''' console = ConsoleEngine() y = console.logtask("Loading character sessions from file...") t = MenuStr() with self.filelock: try: f = open(Config.charsave, 'r') tempchars = pickle.load(f) f.close() except IOError: console.logstatus(y, "[FAILED]") console.log("File '{}' not available for reading.".format(Config.charsave)) return except: console.logstatus(y, "[FAILED]") console.log("Error reading '{}'".format(Config.charsave)) return for x in tempchars.keys(): tchar = tempchars[x] # Check last update time - we don't want anything too old currtime = datetime.datetime.now() sinceupdate = currtime - tchar.lastupdatetime maxage = Config.maxloadage if (maxage > 150): maxage = 150 smaxage = maxage * 60 if (sinceupdate.seconds > smaxage): # Too old console.log("Load: Skipped [{}] - Session too stale".format(tchar.name)) t.add("Skipping [{}]: Session too stale.".format(tchar.name)) continue # Check if same as existing character if (tchar.name in self.chars): # If so, see which has a fresher logon tsl = currtime - tchar.logontime sl = currtime - self.chars[tchar.name].logontime if (tsl < sl): console.log("Load: Overrode [{}] - Fresher logon".format(tchar.name)) t.add("Overriding [{}]: Fresher logon.".format(tchar.name)) else: console.log("Load: Skipped [{}] - Not fresher than existing logon".format(tchar.name)) t.add("Skipping [{}]: Not fresher than existing logon.".format(tchar.name)) continue else: # New character console.log("Load: Added [{}]".format(tchar.name)) t.add("Adding [{}]".format(tchar.name)) # If we haven't continued by now, it's a valid character self.chars[tchar.name] = tchar self.chars[tchar.name].lock = threading.Lock() # Done. Let the other threads have at it self.chars[tchar.name].nextupdate = datetime.timedelta(seconds=(random.randint(30, 600))) console.logstatus(y, "[OK]") return t.get()
def __init__(self, session, viewer, agent): ''' Initializes cookies, auto-refresh ''' conn = ROBConn() console = ConsoleEngine() self.conn_session = conn.getnewsession(agent) self.agent = agent self.session = session self.viewer = viewer self.name = "<new>" # Ride on conn_session with some of our own data self.conn_session.robin = {} self.conn_session.robin['name'] = self.name self.conn_session.robin['expired'] = False self.conn_session.robin['viewer'] = self.viewer # Lock whenever modifications are being made self.lock = threading.Lock() # 1. Call ROB homepage to initialize cookies t = console.logtask("Initializing cookies for new character...") conn.geturl('', self.conn_session, send_session=self.session, send_viewer=self.viewer) self.logontime = datetime.datetime.now() if conn.maintenance: console.logstatus(t, "[MAINTENANCE]") return else: console.logstatus(t, "[OK]") # 2. Scrape 'My Page', check for welcome event t = console.logtask("Retrieving character stats...") self.update() console.logstatus(t, "[OK]") console.log("== New character: [{}] ({})".format(self.name, self.agent))