def _test_chain_execution(session): def job1(outbox, params): print 'did 1' outbox.send('do_2') def job2(outbox, params): print 'did 1' outbox.send('do_3') def job3(outbox, params): print 'did 1' outbox.send('done') disp = Dispatcher() disp.register('do_1', job1) disp.register('do_2', job2) disp.register('do_3', job3) disp.start() try: messenger = Messenger(session) # db.get_session()) assert not messenger.recv() messenger.send('do_1') messenger.subscribe('done') import time time.sleep(1) assert messenger.recv() finally: disp.stop()
def authenticate_as_client(self, session_socket): # authenticates an external server connected via session_socket iv = self.receive_iv(session_socket) master_encrypter = Encrypter(self.master_key, iv) m = Messenger(session_socket, master_encrypter, self.continueHandler) client_challenge = genStr(CHALLENGE_LENGTH) client_challenge_hash = str(create_key(client_challenge)) hash_len = len(client_challenge_hash) secretA = generateAorB() publicA = pow(g, secretA, p) m.send(client_challenge + str(publicA)) response = m.recv() while not response: response = m.recv() if response[:hash_len] != client_challenge_hash: m.close() raise Exception('client could not authenticate') server_challenge_hash = str(create_key(response[hash_len:hash_len + CHALLENGE_LENGTH])) m.send(server_challenge_hash) public_b = int(response[hash_len + CHALLENGE_LENGTH:]) self.log.info('g^b mod p is {}'.format(public_b)) session_key = create_key(str(pow(public_b, secretA, p))) self.log.info('Session key generated by the client is {}'.format(session_key)) session_encrypter = Encrypter(session_key, iv) session_m = Messenger(session_socket, session_encrypter, self.continueHandler) self._messenger = session_m
def client_thread(conn): current_state = WAITING_TO_START_GAME messenger = Messenger(conn) while current_state is not GAME_OVER: if current_state is WAITING_TO_START_GAME: game = init_game(messenger) current_state = WAITING_FOR_MOVE elif current_state is WAITING_FOR_MOVE: letter = messenger.read() if game.already_guessed(letter): reply = "You've already guessed %r." % letter else: game.guess_letter(letter) reply = str(game) if game.gameover: current_state = WAITING_TO_PLAY_AGAIN messenger.send(reply) messenger.send(str(game.gameover)) elif current_state is WAITING_TO_PLAY_AGAIN: play_again = messenger.read() if play_again == "play": current_state = WAITING_TO_START_GAME elif play_again == "quit": current_state = GAME_OVER conn.close()
def _run(self): messenger = Messenger(db.get_session()) running_jobs = set() for dest in self._mapping: messenger.subscribe(dest) counter = 0 while self._running: print 'counter = ', counter counter += 1 messages = messenger.recv() for msg in messages: running_jobs.add((msg, self._dispatch_job(msg))) gevent.sleep(0.01) done = gevent.joinall( [j[1] for j in running_jobs], timeout=0, ) for j in done: for msg, job in running_jobs: if j == job: running_jobs.remove((msg, job)) messenger.retire(msg.id) break while self._outgoing_messages: to, params = self._outgoing_messages.pop() messenger.send(to, **params)
def test_recv_twice(session): m = Messenger(session) m.send('test_recv_twice', a='a', b=1) m.subscribe('test_recv_twice') assert len(m.recv()) == 1 assert len(m.recv()) == 0
def messenger_benchmark(N, is_server): received = 0 def cb(msg): nonlocal received received += 1 m = Messenger(cb) m.connect("uni49que", is_server) print("Starting test ...") gc.collect() try: t_start = time.ticks_ms() for i in range(N): if is_server: m.send("msg {}".format(i)) m.wait_msg() else: m.wait_msg() m.send("ok {}".format(i)) t_stop = time.ticks_ms() dt = time.ticks_diff(t_stop, t_start) if N != received: print("***** sent {} messages, received {}".format(N, received)) print(">>> Messenger throughput: {:8.4f} msg/s (N={})".format( 1000 * received / dt, N)) finally: m.disconnect()
class Subway(object): def __init__(self): self.header = DEFAULT_HEADER self.send_message = global_config.getboolean('messenger', 'enable') self.messenger = Messenger(global_config.get('messenger', 'sckey')) if self.send_message else None def make_reserve_by_time(self,lineName,stationName,enterDate,timeSlot,begin_time,retry=10,interval=1): """预约地铁。 :param lineName: 几号线(昌平线) :param stationName: 地铁站名(沙河站) :param enterDate: 预约日期 :param timeSlot: 时间段(0730-0740) :param begin_time: 开始执行时间 :return: """ token = self.header.get("Authorization") if token.find("!!") != -1 or len(token) == 0: logger.error("token未填写,请先填写token") abort() t = Timer(begin_time=begin_time) t.start() for count in range(1, retry + 1): logger.info('第[%s/%s]次尝试定时预约', count, retry) if self.make_reserve(lineName=lineName,stationName=stationName, enterDate=enterDate,timeSlot=timeSlot): return True logger.info('休息%ss', interval) time.sleep(interval) logger.info('执行结束,预约失败!') return False def make_reserve(self,lineName,stationName,enterDate,timeSlot): """地铁预约 :param lineName: 几号线(昌平线) :param stationName: 地铁站名(沙河站) :param enterDate: 预约日期 :param timeSlot: 时间段(0730-0740) :return:bool """ url = BASE_URL+"/Appointment/CreateAppointment" data = {"lineName": lineName, "snapshotWeekOffset": 0, "stationName": stationName, "enterDate": enterDate, "snapshotTimeSlot": "0630-0930", "timeSlot": timeSlot} result = requests.post(url, json=data, headers=self.header,verify=False) resp_json =parse_json(result.text) try: if(resp_json['balance']<=0): logger.error("本次预约失败:无票可抢。余额:%s",resp_json['balance']) if self.send_message: self.messenger.send(text='预约失败:%s' % result.text) return False logger.info(result.text) if self.send_message: self.messenger.send(text='预约成功:%s' % result.text) return True except KeyError: logger.error("无法预约,请检查错误:%s",result.text) return False
def check_schedule(self, localtime): client = Client() for volume in client.get_volumes(): try: self._check_volume_schedule(localtime, volume) except: error_msg = 'Scheduler failed to check volume schedule. %r' % { 'volume_id': volume.id } logger.exception(error_msg) msg = Messenger(volume) msg.send('%s\n\n%s' % (error_msg, traceback.format_exc()))
def run(): """ :return: """ message = Messenger() while True: try: r = ping() if r != 0: message.send("ping bitmex.com 不通") except Exception as e: print(e) time.sleep(60)
def _test_run_once_job(session): event = threading.Event() def append_job(outbox, params): event.set() disp = Dispatcher() disp.register('/append', append_job) disp.start() try: messenger = Messenger(session) # db.get_session()) messenger.send('/append') assert event.wait(timeout=1) finally: disp.stop()
def test_retire(session): m = Messenger(session) sent_id = m.send('test_retire', a='a', b=1) m.subscribe('test_retire') m.retire(sent_id) assert not m.recv()
def test_counter(session): event = threading.Event() def job(outbox, params): print params if params.counter == 0: event.set() else: outbox.send('counter', counter=params.counter - 1) disp = Dispatcher() disp.register('counter', job) disp.start() try: messenger = Messenger(session) messenger.send('counter', counter=100) assert event.wait(timeout=20) finally: disp.stop()
def challenge(io): Messenger.register(io) handle = cookied_handle() if handle is None: handle = io.prompt('login: '******'') # story(player, io) io.say('would you like to play a game?') # list games # for idx, game in enumerate(GAMES): # name = game.TITLE # io.say(f"{idx}. {name}") # game_num = int(io.ask('#')) # play_game(game_num) while(True): # sendkeys to game # sendkeys to messenger message = io.ask('>') message = f"{player} says: {message}" Messenger.send(message, io) io.say('>', end='')
def test_recv(session): m = Messenger(session) sent_id = m.send('test_recv', a='a', b=1) m.subscribe('test_recv') incoming = m.recv() assert len(incoming) == 1 mm = incoming.pop() assert mm.id == sent_id assert mm.destination == 'test_recv' assert len(mm.params) == 2 assert mm.params.a == 'a' assert mm.params.b == 1
def authenticate_as_server(self, session_socket): # authenticates an external client connected via session_socket iv = self.generate_and_send_iv(session_socket) # the server should generate a random iv master_encrypter = Encrypter(self.master_key, iv) m_messenger = Messenger(session_socket, master_encrypter, self.continueHandler) secret_b = generateAorB() public_b = str(pow(g, secret_b, p)) server_challenge = genStr(CHALLENGE_LENGTH) server_challenge_hash = str(create_key(server_challenge)) response = m_messenger.recv() while not response: response = m_messenger.recv() client_challenge = response[:CHALLENGE_LENGTH] client_challenge_hash = str(create_key(client_challenge)) public_a = response[CHALLENGE_LENGTH:] self.log.info('publicA is {}'.format(public_a)) m_messenger.send(client_challenge_hash + server_challenge + public_b) session_key = create_key(str(pow(int(public_a), secret_b, p))) self.log.info('session key is {}'.format(session_key)) response = m_messenger.recv() while not response: response = m_messenger.recv() if response != server_challenge_hash: self.log.warn('Client could not be authenticated. Session will be terminated!') m_messenger.close() else: print('Server Authentication Successful!!!') session_encrypter = Encrypter(session_key, iv) self._messenger = Messenger(session_socket, session_encrypter, self.continueHandler)
class Controller: def __init__(self): self.siteID = sys.argv[1] with open('knownhosts.json') as hosts_file: self.hosts = json.load(hosts_file)['hosts'] #self.hostToID = dict() count = 0 for key in sorted(self.hosts.keys()): #self.hostToID[key] = count self.hosts[key]['id'] = count count += 1 self.messenger = Messenger(self.hosts[self.siteID], self.hosts) #self.airport = planes.Planes() self.store = StableStorage() if len(sys.argv) >= 3: handle_test_file() else: #wu = imp.Wuubern(len(hosts), hostToID[siteID]) wu, self.airport = self.store.initialize( len(self.hosts), self.hosts[self.siteID]['id']) self.messenger.add_listener(wu) self.messenger.add_listener(self.store) self.messenger.add_listener(self.airport) self.handle_user_input(wu) #def handle_user_input(wu, messenger, hosts, hostToID, siteID, airport, stable_storage): def handle_user_input(self, wu): ''' Main loop for handling user input. ''' command = input().split(" ") counter = 0 while command[0] != 'quit': if command[0] == "reserve" and len(command) == 3: counter += 1 spotsLeft = self.airport.checkPlanes(command[2], command[1]) if not spotsLeft: print("Cannot schedule reservation for " + command[1] + ".") else: plns = command[2].split(',') plns = [int(x) for x in plns] self.airport.addUser(command[1], self.hosts[self.siteID]['id'], len(self.hosts.keys()), plns) ev = event.Event("Reservation", counter, self.hosts[self.siteID]['id']) ev.resInfo(command[1], "pending", command[2]) wu.insert(ev) print("Reservation submitted for " + command[1] + ".") elif command[0] == "cancel": counter += 1 for e in wu.dct: if (e.resUser == command[1]): if e.resStatus == "confirmed": plns = e.resPlaneList.split(',') plns = [int(x) for x in plns] for pln in plns: self.airport.removeSpot(pln, e.resUser) wu.delete(e) break print("Reservation for", command[1], "cancelled.") elif command[0] == "view": for ev in sorted(wu.dct, key=lambda event: event.resUser): print(ev.resUser, ev.resPlaneList, ev.resStatus) elif command[0] == "log": for ev in sorted(wu.log, key=lambda event: event.timeStamp): if (ev.type == "insert"): print(ev.type, ev.inserted.resUser, ev.inserted.resPlaneList) elif (ev.type == "delete"): print(ev.type, ev.deleted.resUser) elif command[0] == "send": np, myMC = wu.send(self.hosts[command[1]]['id']) if (len(command) > 2): m = Message(np, myMC, self.siteID, command[2]) else: m = Message(np, myMC, self.siteID) host = command[1] self.messenger.send((self.hosts[host]['ip_address'], self.hosts[host]['udp_end_port']), pickle.dumps(m)) elif command[0] == "sendall": np1 = set() for key, host in self.hosts.items(): if key != self.siteID: np, myMC = wu.send(host['id']) np1 = np1.union(np) m = Message(np1, myMC, self.siteID) for key, host in self.hosts.items(): self.messenger.send( (host['ip_address'], host['udp_end_port']), pickle.dumps(m)) elif command[0] == "clock": for i in range(len(wu.myMC)): for j in range(len(wu.myMC[i]) - 1): print(wu.myMC[i][j], end=" ") print(wu.myMC[i][-1]) else: print("invalid command") # Write to stable storage self.store.store() # Wait for next command command = input().split(" ") print("exiting...") os._exit(0)
import sys import time import warnings from messenger import Messenger from colorama import init from colorama import Fore, Back, Style init(autoreset=True) login_info = open(sys.argv[1]).read().splitlines() username = login_info[0] password = login_info[1] with warnings.catch_warnings(): warnings.simplefilter("ignore") # Log in print "Logging in..." m = Messenger(username=username, password=password) print "Logged in!" while True: # Get friend to talk to friend = raw_input("Who do you want to talk to? ") # Get message to send message = raw_input("What do you want to tell " + friend + "?") m.send(friend, message) # my id: 1326450295 # my id: 1664490365
class Registry(object): """ Hub of-sorts to talk with different `Cosmid` related files and resources. Can be seen as the API endpoint for `Cosmid`. """ def __init__(self): super(Registry, self).__init__() # Set up YAML parser for optional config file self.config_path = path("cosmid.yaml") self.config = ConfigReader(self.config_path) # Extract stuff from config self.email = self.config.find("email") # Path to resource storage directory self.directory = path( self.config.find("directory", default="resources")) # Load history file consisting of already downloaded resources self.history_path = path(self.directory + "/.cosmid.yaml") self.history = HistoryReader(self.history_path) # Set up a :class:`cosmid.messenger.Messenger` self.messenger = Messenger("cosmid") def get(self, resource_id, type_="class"): """ <public> Returns an instance of the specified resource class. Dodges an ``ImportError`` when failing to import a resource and returns ``None`` instead. .. code-block:: python >>> resource = registry.get("ccds") >>> resource.latest() 'Hs104' :param str resource_id: The resource key (name of module) :returns: A class instance of the resource """ try: if type_ == "class": return load_class( "cosmid.resources.{}.Resource".format(resource_id))() elif type_ == "module": return importlib.import_module("cosmid.resources." + resource_id) else: raise ValueError( "Argument must be either 'class' or 'module'.") except ImportError: return None def grab(self, resource_id, target, collapse=False): """ <public> Returns all that's nessesary to download a specific resource. The method will try to correct both ``resource_id`` and the ``target`` release tag. :param str resource_id: What resource to download :param str target: What release of the resource to download """ # Either import resource class or print warning and move on. # Test matching the resource ID options = [item[0] for item in self.ls()] resource_id = self.matchOne(resource_id, options) if resource_id is None: message = "Couldn't match resource ID: '{}'".format(resource_id) self.messenger.send("warning", message) return None, None, None, None # Get the resource resource = self.get(resource_id) # Now let's figure out the version # No specified version will match to the latest resource release if target == "latest": version = resource.latest() else: options = resource.versions() version = self.matchOne(target, options) if version is None: message = ("Couldn't match version '{id}#{v}'; {vers}".format( v=target, id=resource.id, vers=", ".join(options))) self.messenger.send("warning", message) return None, None, None, None # Get the goahead! (that we haven't already downloaded it) if self.goahead(resource, version): # Finally we can determine the paths to download and save the files dl_paths = resource.paths(version) if collapse: # The user can select to store all downloaded files in the same # directory resource_dir = "" else: # Or by default separate different resources into subdirectories resource_dir = "/" + resource.id save_paths = [("{dir}{mid}/{file}".format(dir=self.directory, mid=resource_dir, file=name)) for name in resource.names] # Add the resource to the history file as downloaded self.history.add( resource_id, { "version": version, "target": target, "names": resource.names, "sources": dl_paths }) return resource, dl_paths, save_paths, version else: # The resource was already downloaded return None, None, None, None def ls(self): """ <public> Returns a list of resource IDs and docstrings for all the included resource modules. *Reference*: http://stackoverflow.com/questions/1707709/list-all-the-modules-that-are-part-of-a-python-package .. code-block:: python >>> registry.ls() [('ccds', 'A curated database of generic element'), ...] :returns: A list of tuples: ``(resource_id, docstring)`` :rtype: list """ # Store everything here items = [] prefix = resources.__name__ + "." # Fetch all the resource modules modules = pkgutil.iter_modules(resources.__path__, prefix) # Loop over all resource modules for importer, modpath, ispkg in modules: # Strip path modname = modpath.split(".")[-1] # Load the `Resource` class for the module module = self.get(modname, type_="module") # Save name and docstring items.append((modname, module.__doc__)) return items def search(self, query, limit=5): """ <public> Fuzzy matches a query string against each of the resource IDs and returns a limited number of results in order of match score. .. code-block:: python >>> registry.search("asmebly", limit=2) [('ensembl_assembly', 68), ('ncbi_assembly', 68)] :param str query: A string to match against the resource IDs :param int limit: (optional) A maximum number of results to return :returns: A list of tuples: ``(resource_id, score)` :rtype: list """ # List all the available resources resources = self.ls() # Focus on resource IDs resource_ids = [resource[0] for resource in resources] # Fuzzy match against the resource IDs and return in order of best match return process.extract(query, resource_ids, limit=limit) def matchOne(self, target, options, threshold=60): """ <public> Fuzzy matches e.g. a target version tag against a list of options. Returns the most likely match if the match score is sufficient. .. code-block:: python >>> resource = registry.get("ccds") >>> registry.matchOne(104, resource.versions()) 'Hs104' >>> registry.matchOne("ensembl", registry.ls()) 'ensembl_assembly' :param object target: Any Python object to match with :param list options: A list of possible options to match against :param int threshold: A lower threshold for accepting a best match :returns: The object with the best match (unless score is below threshold) :rtype: Python object """ # Match against the options and extract the top match only result, score = process.extractOne(target, map(str, options)) # Arbitrary lower limit for returning a *mathcing* result if score >= threshold: return result else: return None def goahead(self, resource, version): """ Determines whether it's any idea in going ahead with a download. """ # Get any currently downloaded resources current = self.history.find(resource.id, default={}) # Make sure we haven't already downloaded the resource if current.get("version") == version: message = "'{}' already downloaded and up-to-date.".format( resource.id) self.messenger.send("update", message) return False return True
def test_send(session): m = Messenger(session) m.send('test_send', a='b', b=1)
class MockExchange(object): """ """ def __init__(self): """ """ self.exchange = ccxt.bitmex() self.messager = Messenger() self.config = {} self.config_file = "config.json" self.load_config() self.fee_rate = 0.002 def load_config(self): """ 加载运行中产生的数据 :return: """ with open(self.config_file) as f: self.config = json.load(f) def save_config(self): """ 保存运行过程中产生的数据 :return: """ print(self.config) with open(self.config_file, 'w') as f: json.dump(self.config, f, sort_keys=True, indent=4, separators=(",", ":")) def get_last_price(self): """ :return: """ return self.exchange.fetch_ticker("BTC/USD")['last'] def buy(self, price, volume): """ :param price: :param volume: :return: """ self.config["money"] -= price * volume self.config["btc"] += volume * (1 - self.fee_rate) op = "{0} buy {2} at {1}".format( datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), price, volume) self.config["history"].append(op) self.messager.send(op) def sell(self, price, volume): """ :param price: :param volume: :return: """ self.config["money"] += price * volume * (1 - self.fee_rate) self.config["btc"] -= volume op = "{0} sell {2} at {1}".format( datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), price, volume) self.config["history"].append(op) self.messager.send(op) def trade(self, position): """ :param position: 仓位比例 :return: """ price = self.get_last_price() hold_value = self.config['btc'] * price total_value = self.config['money'] + hold_value need_value = total_value * position if need_value > hold_value: volume = (need_value - hold_value) / price print( "volume:{0}, hold_value:{1},need_value:{2},price:{3},position:{4}" .format(volume, hold_value, need_value, price, position)) self.buy(price, volume) if need_value < hold_value: volume = (hold_value - need_value) / price print( "volume:{0}, hold_value:{1},need_value:{2},price:{3},position:{4}" .format(volume, hold_value, need_value, price, position)) self.sell(price, volume) self.save_config()
def thread_client(conn, addr, db_conn, active_clients): """ Checks and verifies password/username, and handles adding new users to the database. Main client loop Accepts messages from client socket then broadcasts message to all clients. If the connection is broken the loop will break, database will be updated(active state). :param conn: socket objected connected with remote client :param addr: tuple of the remote clients address and port :param db_conn: connection to the sqlite3 database containing user-info """ length_struct = struct.Struct("!I") local_messenger = Messenger(conn, length_struct) lock = _thread.allocate_lock() verified = False # used to control looping while not verified: # handle client login/signup credentials try: """ first message received will be a login or sign up attempt message_type will be "LOGIN" or "SIGNUP" """ message = local_messenger.recv() message_type, username, password = message.split("`", 2) except ValueError or ConnectionResetError: print("bad connection at {}".format(addr)) break # retrieve user info from database. username_check() returns two boolean values lock.acquire() username_exists, correct_password = username_check(db_conn, username, password) lock.release() # add new users to database if message_type == "SIGNUP": if username_exists: # username already taken local_messenger.send("UNAVAILABLE") else: # acquire lock and add user to database and active_clients lock.acquire() new_user(db_conn, username, password) active_clients[username] = conn lock.release() local_messenger.send("OK") verified = True # login existing users elif message_type == "LOGIN": if username_exists and correct_password: if username not in active_clients: # username is not already signed in # acquire lock and add username to active_clients lock.acquire() active_clients[username] = conn lock.release() local_messenger.send("OK") verified = True else: local_messenger.send("USER_ACTIVE") # user is already active else: local_messenger.send("BAD") # wrong password or username while verified: """ client will only be verified when an existing username and password have been submitted, or a new username and password has been created. verified loop will handle all incoming messages, errors, socket closures """ try: message = local_messenger.recv() except socket.error or struct.error: print("bad connection at {}".format(addr)) break if message: lock.acquire() local_messenger.broadcast(active_clients, message) lock.release() else: # empty string signaling connection closed lock.acquire() del active_clients[username] lock.release() conn.close() break # clean up after client disconnects or the connection is broken if username in active_clients: lock.acquire() del active_clients[username] lock.release() conn.close() print("{} DISCONNECTED".format(addr))
if current_state is WAITING_FOR_WELCOME: print messenger.read() current_state = WAITING_FOR_BOARD elif current_state is WAITING_FOR_BOARD: print messenger.read() current_state = WAITING_FOR_GAME_STATUS elif current_state is WAITING_FOR_GAME_STATUS: msg = messenger.read() if msg == "True": current_state = WAITING_TO_PLAY_AGAIN elif msg == "False": current_state = WAITING_FOR_USER_INPUT elif current_state is WAITING_FOR_USER_INPUT: guess = raw_input("Guess a letter: ").lower() if len(guess) > 1 or guess not in string.lowercase: print "Please guess a letter." else: messenger.send(guess) current_state = WAITING_FOR_BOARD elif current_state is WAITING_TO_PLAY_AGAIN: play_again = raw_input("Play again? (y/n): ").lower() if "y" in play_again: current_state = WAITING_FOR_WELCOME messenger.send("play") else: current_state = GAME_OVER messenger.send("quit") print "Thanks for playing!" sock.close()
class Controller: def __init__(self): self.siteID = sys.argv[1] with open('knownhosts.json') as hosts_file: self.hosts = json.load(hosts_file)['hosts'] self.messenger = Messenger(self.hosts[self.siteID], self.hosts, self.siteID) self.paxos_driver = PaxosDriver(self.siteID, self.hosts, self.messenger) self.handle_user_input() def handle_user_input(self): #Main loop for handling user input command = input().split(" ") while command[0] != 'quit': if command[0] == "reserve" and len(command) == 3: plns = command[2].split(',') plns = [int(x) for x in plns] id = random.randint(0, 1000) ev = Event(id, command[0], command[1], plns) if not self.paxos_driver.createReservation(ev): print("Cannot create reservation") elif command[0] == "cancel" and len(command) == 2: id = random.randint(0, 1000) ev = Event(id, command[0], command[1]) if not self.paxos_driver.cancelReservation(ev): print("Reservation already canceled") elif command[0] == "view" and len(command) == 1: view = self.paxos_driver.airport.getView() for v in view: print(v[0], end=" ") for p in range(len(v[1]) - 1): print(v[1][p], end=", ") print(v[1][-1]) elif command[0] == "log" and len(command) == 1: log = self.paxos_driver.learner.log for event in log: if event.type == "filler": continue print(event) #print(self.paxos_driver.proposer.proposals) elif command[0] == "send" and len(command) == 4: Message(self.siteID, command[3], command[1], command[2], 0) tempHost = self.hosts[command[3]] host = (tempHost['ip_address'], tempHost['udp_end_port']) self.messenger.send(command[3], command[1], command[2], 0) #self.messenger.send(host, pickle.dumps(m)) elif command[0] == "sendall" and len(command) == 3: #m = Message(self.siteID, command[3], command[1], command[2], 0) tempHosts = list() for _, host in self.hosts.items(): tempHosts.append( (host['ip_address'], host['udp_end_port'])) #self.messenger.sendAll(tempHosts, pickle.dumps(m)) self.messenger.sendAll(command[1], command[2], 0) elif command[0] == "prepare": self.paxos_driver.testBoi(command[1]) else: print("invalid command") # Wait for next command command = input().split(" ") print("exiting...") os._exit(0)
from messenger import Messenger from colorama import init from colorama import Fore, Back, Style init(autoreset=True) login_info = open(sys.argv[1]).read().splitlines() username = login_info[0] password = login_info[1] with warnings.catch_warnings(): warnings.simplefilter("ignore") # Log in print "Logging in..." m = Messenger(username=username, password=password) print "Logged in!" while True: # Get friend to talk to friend = raw_input("Who do you want to talk to? ") # Get message to send message = raw_input("What do you want to tell " + friend + "?") m.send(friend, message) # my id: 1326450295 # my id: 1664490365
class UiManager(): """Everything that is clicking on some static 2D UI or is checking anything in regard to it should be placed here.""" def __init__(self, screen: Screen, template_finder: TemplateFinder): self._config = Config() self._template_finder = template_finder self._messenger = Messenger() self._screen = screen self._curr_stash = { "items": 0, "gold": 0 } #0: personal, 1: shared1, 2: shared2, 3: shared3 def use_wp(self, act: int, idx: int): """ Use Waypoint. The menu must be opened when calling the function. :param act: Index of the desired act starting at 1 [A1 = 1, A2 = 2, A3 = 3, ...] :param idx: Index of the waypoint from top. Note that it start at 0! """ str_to_idx_map = { "WP_A3_ACTIVE": 3, "WP_A4_ACTIVE": 4, "WP_A5_ACTIVE": 5 } template_match = self._template_finder.search( [*str_to_idx_map], self._screen.grab(), threshold=0.7, best_match=True, roi=self._config.ui_roi["wp_act_roi"]) curr_active_act = str_to_idx_map[ template_match.name] if template_match.valid else -1 if curr_active_act != act: pos_act_btn = (self._config.ui_pos["wp_act_i_btn_x"] + self._config.ui_pos["wp_act_btn_width"] * (act - 1), self._config.ui_pos["wp_act_i_btn_y"]) x, y = self._screen.convert_screen_to_monitor(pos_act_btn) mouse.move(x, y, randomize=8) mouse.click(button="left") wait(0.3, 0.4) pos_wp_btn = (self._config.ui_pos["wp_first_btn_x"], self._config.ui_pos["wp_first_btn_y"] + self._config.ui_pos["wp_btn_height"] * idx) x, y = self._screen.convert_screen_to_monitor(pos_wp_btn) mouse.move(x, y, randomize=[60, 9], delay_factor=[0.9, 1.4]) wait(0.4, 0.5) mouse.click(button="left") # wait till loading screen is over if self.wait_for_loading_screen(): while 1: if not self.wait_for_loading_screen(0.2): return def is_right_skill_active(self) -> bool: """ :return: Bool if skill is red/available or not. Skill must be selected on right skill slot when calling the function. """ roi = [ self._config.ui_pos["skill_right_x"] - (self._config.ui_pos["skill_width"] // 2), self._config.ui_pos["skill_y"] - (self._config.ui_pos["skill_height"] // 2), self._config.ui_pos["skill_width"], self._config.ui_pos["skill_height"] ] img = cut_roi(self._screen.grab(), roi) avg = np.average(img) return avg > 75.0 def is_right_skill_selected(self, template_list: List[str]) -> bool: """ :return: Bool if skill is currently the selected skill on the right skill slot. """ for template in template_list: if self._template_finder.search( template, self._screen.grab(), threshold=0.87, roi=self._config.ui_roi["skill_right"]).valid: return True return False def is_left_skill_selected(self, template_list: List[str]) -> bool: """ :return: Bool if skill is currently the selected skill on the left skill slot. """ for template in template_list: if self._template_finder.search( template, self._screen.grab(), threshold=0.87, roi=self._config.ui_roi["skill_left"]).valid: return True return False def is_overburdened(self) -> bool: """ :return: Bool if the last pick up overburdened your char. Must be called right after picking up an item. """ img = cut_roi(self._screen.grab(), self._config.ui_roi["is_overburdened"]) _, filtered_img = color_filter(img, self._config.colors["gold"]) templates = [ cv2.imread("assets/templates/inventory_full_msg_0.png"), cv2.imread("assets/templates/inventory_full_msg_1.png") ] for template in templates: _, filtered_template = color_filter(template, self._config.colors["gold"]) res = cv2.matchTemplate(filtered_img, filtered_template, cv2.TM_CCOEFF_NORMED) _, max_val, _, _ = cv2.minMaxLoc(res) if max_val > 0.8: return True return False def wait_for_loading_screen(self, time_out: float = None) -> bool: """ Waits until loading screen apears :param time_out: Maximum time to search for a loading screen :return: True if loading screen was found within the timeout. False otherwise """ start = time.time() while True: img = self._screen.grab() is_loading_black_roi = np.average( img[:, 0:self._config.ui_roi["loading_left_black"][2]]) < 1.5 if is_loading_black_roi: return True if time_out is not None and time.time() - start > time_out: return False def save_and_exit(self, does_chicken: bool = False) -> bool: """ Performes save and exit action from within game :return: Bool if action was successful """ start = time.time() while (time.time() - start) < 15: templates = [ "SAVE_AND_EXIT_NO_HIGHLIGHT", "SAVE_AND_EXIT_HIGHLIGHT" ] if not self._template_finder.search( templates, self._screen.grab(), roi=self._config.ui_roi["save_and_exit"], threshold=0.85).valid: keyboard.send("esc") wait(0.3) exit_btn_pos = (self._config.ui_pos["save_and_exit_x"], self._config.ui_pos["save_and_exit_y"]) x_m, y_m = self._screen.convert_screen_to_monitor(exit_btn_pos) # TODO: Add hardcoded coordinates to ini file away_x_m, away_y_m = self._screen.convert_abs_to_monitor((-167, 0)) while self._template_finder.search_and_wait( templates, roi=self._config.ui_roi["save_and_exit"], time_out=1.5, take_ss=False).valid: delay = [0.9, 1.1] if does_chicken: delay = [0.3, 0.4] mouse.move(x_m, y_m, randomize=[38, 7], delay_factor=delay) wait(0.03, 0.06) mouse.press(button="left") wait(0.06, 0.1) mouse.release(button="left") if does_chicken: # lets just try again just in case wait(0.05, 0.08) # mouse.click(button="left") wait(1.5, 2.0) mouse.move(away_x_m, away_y_m, randomize=40, delay_factor=[0.6, 0.9]) wait(0.1, 0.5) return True return False def start_game(self) -> bool: """ Starting a game. Will wait and retry on server connection issue. :return: Bool if action was successful """ Logger.debug("Wait for Play button") # To test the start_game() function seperatly, just run: # (botty) >> python src/ui_manager.py # then go to D2r window -> press "f11", you can exit with "f12" while 1: # grab img which will be used to search the "play button" img = self._screen.grab() # the template finder can be used to search for a specific template, in this case the play btn. # it returns a bool value (True or False) if the button was found, and the position of it # roi = Region of interest. It reduces the search area and can be adapted within game.ini # by running >> python src/screen.py you can visualize all of the currently set region of interests found_btn = self._template_finder.search( ["PLAY_BTN", "PLAY_BTN_GRAY"], img, roi=self._config.ui_roi["offline_btn"], threshold=0.8, best_match=True) if found_btn.name == "PLAY_BTN": # We need to convert the position to monitor coordinates (e.g. if someone is using 2 monitors or windowed mode) x, y = self._screen.convert_screen_to_monitor( found_btn.position) Logger.debug(f"Found Play Btn") mouse.move(x, y, randomize=[35, 7], delay_factor=[1.0, 1.8]) wait(0.1, 0.15) mouse.click(button="left") break else: found_btn = self._template_finder.search( "PLAY_BTN", img, roi=self._config.ui_roi["online_btn"], threshold=0.8) if found_btn.valid: Logger.warning( "WARNING CHOPPA GOOCH IN TEH CODEZ - Lets get this party started!" ) x, y = self._screen.convert_screen_to_monitor( found_btn.position) mouse.move(x, y, randomize=[35, 7], delay_factor=[1.0, 1.8]) wait(0.1, 0.15) mouse.click(button="left") break time.sleep(3.0) difficulty = self._config.general["difficulty"].upper() while 1: template_match = self._template_finder.search_and_wait( ["LOADING", f"{difficulty}_BTN"], time_out=8, roi=self._config.ui_roi["difficulty_select"], threshold=0.9) if not template_match.valid: Logger.debug( f"Could not find {difficulty}_BTN, try from start again") return self.start_game() if template_match.name == "LOADING": Logger.debug(f"Found {template_match.name} screen") return True x, y = self._screen.convert_screen_to_monitor( template_match.position) mouse.move(x, y, randomize=[50, 9], delay_factor=[1.0, 1.8]) wait(0.15, 0.2) mouse.click(button="left") break # check for server issue wait(2.0) server_issue = self._template_finder.search("SERVER_ISSUES", self._screen.grab()).valid if server_issue: Logger.warning("Server connection issue. waiting 20s") x, y = self._screen.convert_screen_to_monitor( (self._config.ui_pos["issue_occured_ok_x"], self._config.ui_pos["issue_occured_ok_y"])) mouse.move(x, y, randomize=10, delay_factor=[2.0, 4.0]) mouse.click(button="left") wait(1, 2) keyboard.send("esc") wait(18, 22) return self.start_game() else: return True @staticmethod def _slot_has_item(slot_img: np.ndarray) -> bool: """ Check if a specific slot in the inventory has an item or not based on color :param slot_img: Image of the slot :return: Bool if there is an item or not """ slot_img = cv2.cvtColor(slot_img, cv2.COLOR_BGR2HSV) avg_brightness = np.average(slot_img[:, :, 2]) return avg_brightness > 16.0 @staticmethod def get_slot_pos_and_img(config: Config, img: np.ndarray, column: int, row: int) -> tuple[tuple[int, int], np.ndarray]: """ Get the pos and img of a specific slot position in Inventory. Inventory must be open in the image. :param config: The config which should be used :param img: Image from screen.grab() not cut :param column: Column in the Inventory :param row: Row in the Inventory :return: Returns position and image of the cut area as such: [[x, y], img] """ top_left_slot = (config.ui_pos["inventory_top_left_slot_x"], config.ui_pos["inventory_top_left_slot_y"]) slot_width = config.ui_pos["slot_width"] slot_height = config.ui_pos["slot_height"] slot = (top_left_slot[0] + slot_width * column, top_left_slot[1] + slot_height * row) # decrease size to make sure not to have any borders of the slot in the image offset_w = int(slot_width * 0.12) offset_h = int(slot_height * 0.12) min_x = slot[0] + offset_w max_x = slot[0] + slot_width - offset_w min_y = slot[1] + offset_h max_y = slot[1] + slot_height - offset_h slot_img = img[min_y:max_y, min_x:max_x] center_pos = (int(slot[0] + (slot_width // 2)), int(slot[1] + (slot_height // 2))) return center_pos, slot_img def _inventory_has_items(self, img, num_loot_columns: int) -> bool: """ Check if Inventory has any items :param img: Img from screen.grab() with inventory open :param num_loot_columns: Number of columns to check from left :return: Bool if inventory still has items or not """ for column, row in itertools.product(range(num_loot_columns), range(4)): _, slot_img = self.get_slot_pos_and_img(self._config, img, column, row) if self._slot_has_item(slot_img): return True return False def _keep_item(self, item_finder: ItemFinder, img: np.ndarray) -> bool: """ Check if an item should be kept, the item should be hovered and in own inventory when function is called :param item_finder: ItemFinder to check if item is in pickit :param img: Image in which the item is searched (item details should be visible) :return: Bool if item should be kept """ wait(0.2, 0.3) _, w, _ = img.shape img = img[:, (w // 2):, :] original_list = item_finder.search(img) filtered_list = [] for x in original_list: if ("potion" in x.name) or (self._config.items[x.name].pickit_type == 0): continue include_props = self._config.items[x.name].include exclude_props = self._config.items[x.name].exclude if not (include_props or exclude_props): Logger.debug(f"{x.name}: Stashing") filtered_list.append(x) continue include = True include_logic_type = self._config.items[x.name].include_type if include_props: include = False found_props = [] for prop in include_props: try: template_match = self._template_finder.search( prop, img, threshold=0.95) except: Logger.error( f"{x.name}: can't find template file for required {prop}, ignore just in case" ) template_match = lambda: None template_match.valid = True if template_match.valid: if include_logic_type == "AND": found_props.append(True) else: include = True break else: found_props.append(False) if include_logic_type == "AND" and len( found_props) > 0 and all(found_props): include = True if not include: Logger.debug( f"{x.name}: Discarding. Required {include_logic_type}({include_props})={include}" ) continue exclude = False exclude_logic_type = self._config.items[x.name].exclude_type if exclude_props: found_props = [] for prop in exclude_props: try: template_match = self._template_finder.search( prop, img, threshold=0.97) except: Logger.error( f"{x.name}: can't find template file for exclusion {prop}, ignore just in case" ) template_match = lambda: None template_match.valid = False if template_match.valid: if exclude_logic_type == "AND": found_props.append(True) else: exclude = True break else: found_props.append(False) if exclude_logic_type == "AND" and len( exclude_props) > 0 and all(found_props): exclude = True break if include and not exclude: Logger.debug( f"{x.name}: Stashing. Required {include_logic_type}({include_props})={include}, exclude {exclude_logic_type}({exclude_props})={exclude}" ) filtered_list.append(x) return len(filtered_list) > 0 def _move_to_stash_tab(self, stash_idx: int): """Move to a specifc tab in the stash :param stash_idx: idx of the stash starting at 0 (personal stash) """ str_to_idx_map = { "STASH_0_ACTIVE": 0, "STASH_1_ACTIVE": 1, "STASH_2_ACTIVE": 2, "STASH_3_ACTIVE": 3 } template_match = self._template_finder.search( [*str_to_idx_map], self._screen.grab(), threshold=0.7, best_match=True, roi=self._config.ui_roi["stash_btn_roi"]) curr_active_stash = str_to_idx_map[ template_match.name] if template_match.valid else -1 if curr_active_stash != stash_idx: # select the start stash personal_stash_pos = (self._config.ui_pos["stash_personal_btn_x"], self._config.ui_pos["stash_personal_btn_y"]) stash_btn_width = self._config.ui_pos["stash_btn_width"] next_stash_pos = (personal_stash_pos[0] + stash_btn_width * stash_idx, personal_stash_pos[1]) x_m, y_m = self._screen.convert_screen_to_monitor(next_stash_pos) mouse.move(x_m, y_m, randomize=[30, 7], delay_factor=[1.0, 1.5]) wait(0.2, 0.3) mouse.click(button="left") wait(0.3, 0.4) def stash_all_items(self, num_loot_columns: int, item_finder: ItemFinder): """ Stashing all items in inventory. Stash UI must be open when calling the function. :param num_loot_columns: Number of columns used for loot from left """ Logger.debug("Searching for inventory gold btn...") # Move cursor to center x, y = self._screen.convert_abs_to_monitor((0, 0)) mouse.move(x, y, randomize=[40, 200], delay_factor=[1.0, 1.5]) # Wait till gold btn is found gold_btn = self._template_finder.search_and_wait( "INVENTORY_GOLD_BTN", roi=self._config.ui_roi["gold_btn"], time_out=20) if not gold_btn.valid: Logger.error( "Could not determine to be in stash menu. Continue...") return Logger.debug("Found inventory gold btn") # stash gold if self._config.char["stash_gold"]: inventory_no_gold = self._template_finder.search( "INVENTORY_NO_GOLD", self._screen.grab(), roi=self._config.ui_roi["inventory_gold"], threshold=0.83) if inventory_no_gold.valid: Logger.debug("Skipping gold stashing") else: Logger.debug("Stashing gold") self._move_to_stash_tab(min(3, self._curr_stash["gold"])) x, y = self._screen.convert_screen_to_monitor( gold_btn.position) mouse.move(x, y, randomize=4) wait(0.1, 0.15) mouse.press(button="left") wait(0.25, 0.35) mouse.release(button="left") wait(0.4, 0.6) keyboard.send( "enter" ) #if stash already full of gold just nothing happens -> gold stays on char -> no popup window wait(1.0, 1.2) # move cursor away from button to interfere with screen grab mouse.move(-120, 0, absolute=False, randomize=15) inventory_no_gold = self._template_finder.search( "INVENTORY_NO_GOLD", self._screen.grab(), roi=self._config.ui_roi["inventory_gold"], threshold=0.83) if not inventory_no_gold.valid: Logger.info( "Stash tab is full of gold, selecting next stash tab.") self._curr_stash["gold"] += 1 if self._config.general["info_screenshots"]: cv2.imwrite( "./info_screenshots/info_gold_stash_full_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab()) if self._curr_stash["gold"] > 3: # turn of gold pickup self._config.char["stash_gold"] = False self._config.items["misc_gold"] = False item_finder.update_items_to_pick(self._config) # inform user about it msg = "All stash tabs and character are full of gold, turn of gold pickup" Logger.info(msg) if self._config.general["custom_message_hook"]: self._messenger.send( msg=f"{self._config.general['name']}: {msg}") else: # move to next stash wait(0.5, 0.6) return self.stash_all_items(num_loot_columns, item_finder) # stash stuff self._move_to_stash_tab(self._curr_stash["items"]) center_m = self._screen.convert_abs_to_monitor((0, 0)) for column, row in itertools.product(range(num_loot_columns), range(4)): img = self._screen.grab() slot_pos, slot_img = self.get_slot_pos_and_img( self._config, img, column, row) if self._slot_has_item(slot_img): x_m, y_m = self._screen.convert_screen_to_monitor(slot_pos) mouse.move(x_m, y_m, randomize=10, delay_factor=[1.0, 1.3]) # check item again and discard it or stash it wait(1.2, 1.4) hovered_item = self._screen.grab() if self._keep_item(item_finder, hovered_item): keyboard.send('ctrl', do_release=False) wait(0.2, 0.25) mouse.press(button="left") wait(0.2, 0.25) mouse.release(button="left") wait(0.2, 0.25) keyboard.send('ctrl', do_press=False) else: # make sure there is actually an item time.sleep(0.3) curr_pos = mouse.get_position() # move mouse away from inventory, for some reason it was sometimes included in the grabed img x, y = self._screen.convert_abs_to_monitor((0, 0)) mouse.move(x, y, randomize=[40, 200], delay_factor=[1.0, 1.5]) item_check_img = self._screen.grab() mouse.move(*curr_pos, randomize=2) wait(0.4, 0.6) slot_pos, slot_img = self.get_slot_pos_and_img( self._config, item_check_img, column, row) if self._slot_has_item(slot_img): if self._config.general["info_screenshots"]: cv2.imwrite( "./info_screenshots/info_discard_item_" + time.strftime("%Y%m%d_%H%M%S") + ".png", hovered_item) mouse.press(button="left") wait(0.2, 0.4) mouse.release(button="left") mouse.move(*center_m, randomize=20) wait(0.2, 0.3) mouse.press(button="left") wait(0.2, 0.3) mouse.release(button="left") wait(0.5, 0.5) Logger.debug("Check if stash is full") time.sleep(0.6) # move mouse away from inventory, for some reason it was sometimes included in the grabed img x, y = self._screen.convert_abs_to_monitor((0, 0)) mouse.move(x, y, randomize=[40, 200], delay_factor=[1.0, 1.5]) img = self._screen.grab() if self._inventory_has_items(img, num_loot_columns): Logger.info("Stash page is full, selecting next stash") if self._config.general["info_screenshots"]: cv2.imwrite( "./info_screenshots/debug_info_inventory_not_empty_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img) self._curr_stash["items"] += 1 if self._curr_stash["items"] > 3: Logger.error("All stash is full, quitting") if self._config.general["custom_message_hook"]: self._messenger.send( msg= f"{self._config.general['name']}: all stash is full, quitting" ) os._exit(1) else: # move to next stash wait(0.5, 0.6) return self.stash_all_items(num_loot_columns, item_finder) Logger.debug("Done stashing") wait(0.4, 0.5) keyboard.send("esc") def should_stash(self, num_loot_columns: int): """ Check if there are items that need to be stashed in the inventory :param num_loot_columns: Number of columns used for loot from left """ wait(0.2, 0.3) keyboard.send(self._config.char["inventory_screen"]) wait(0.7, 1.0) should_stash = self._inventory_has_items(self._screen.grab(), num_loot_columns) keyboard.send(self._config.char["inventory_screen"]) wait(0.4, 0.6) return should_stash def close_vendor_screen(self): keyboard.send("esc") # just in case also bring cursor to center and click x, y = self._screen.convert_screen_to_monitor( (self._config.ui_pos["center_x"], self._config.ui_pos["center_y"])) mouse.move(x, y, randomize=25, delay_factor=[1.0, 1.5]) mouse.click(button="left") def repair_and_fill_up_tp(self) -> bool: """ Repair and fills up TP buy selling tome and buying. Vendor inventory needs to be open! :return: Bool if success """ repair_btn = self._template_finder.search_and_wait( "REPAIR_BTN", roi=self._config.ui_roi["repair_btn"], time_out=4) if not repair_btn.valid: return False x, y = self._screen.convert_screen_to_monitor(repair_btn.position) mouse.move(x, y, randomize=12, delay_factor=[1.0, 1.5]) wait(0.1, 0.15) mouse.click(button="left") wait(0.1, 0.15) x, y = self._screen.convert_screen_to_monitor( (self._config.ui_pos["vendor_misc_x"], self._config.ui_pos["vendor_misc_y"])) mouse.move(x, y, randomize=[20, 6], delay_factor=[1.0, 1.5]) wait(0.1, 0.15) mouse.click(button="left") # another click to dismiss popup message in case you have not enough gold to repair, preventing tome not being bought back wait(0.1, 0.15) mouse.click(button="left") wait(0.5, 0.6) tp_tome = self._template_finder.search_and_wait( ["TP_TOME", "TP_TOME_RED"], roi=self._config.ui_roi["inventory"], time_out=3) if not tp_tome.valid: return False x, y = self._screen.convert_screen_to_monitor(tp_tome.position) keyboard.send('ctrl', do_release=False) mouse.move(x, y, randomize=8, delay_factor=[1.0, 1.5]) wait(0.1, 0.15) mouse.press(button="left") wait(0.25, 0.35) mouse.release(button="left") wait(0.5, 0.6) keyboard.send('ctrl', do_press=False) tp_tome = self._template_finder.search_and_wait( "TP_TOME", roi=self._config.ui_roi["vendor_stash"], time_out=3) if not tp_tome.valid: return False x, y = self._screen.convert_screen_to_monitor(tp_tome.position) keyboard.send('ctrl', do_release=False) mouse.move(x, y, randomize=8, delay_factor=[1.0, 1.5]) wait(0.1, 0.15) mouse.click(button="right") wait(0.1, 0.15) keyboard.send('ctrl', do_press=False) # delay to make sure the tome has time to transfer to other inventory before closing window tp_tome = self._template_finder.search_and_wait( "TP_TOME", roi=self._config.ui_roi["inventory"], time_out=3) if not tp_tome.valid: return False return True def has_tps(self) -> bool: """ :return: Returns True if botty has town portals available. False otherwise """ if self._config.char["tp"]: keyboard.send(self._config.char["tp"]) template_match = self._template_finder.search_and_wait( ["TP_ACTIVE", "TP_INACTIVE"], roi=self._config.ui_roi["skill_right"], best_match=True, threshold=0.79, time_out=4) if not template_match.valid: Logger.warning("You are out of tps") if self._config.general["info_screenshots"]: cv2.imwrite( "./info_screenshots/debug_out_of_tps_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab()) return template_match.valid else: return False def repair_needed(self) -> bool: template_match = self._template_finder.search( "REPAIR_NEEDED", self._screen.grab(), roi=self._config.ui_roi["repair_needed"], use_grayscale=True) return template_match.valid def enable_no_pickup(self) -> bool: """ Checks the best match between enabled and disabled an retrys if already set. :return: Returns True if we succesfully set the nopickup option """ keyboard.send('enter') wait(0.1, 0.25) keyboard.write('/nopickup', delay=.20) keyboard.send('enter') wait(0.1, 0.25) no_pickup = self._template_finder.search_and_wait( ["ITEM_PICKUP_ENABLED", "ITEM_PICKUP_DISABLED"], roi=self._config.ui_roi["no_pickup"], best_match=True, time_out=3) if not no_pickup.valid: return False if no_pickup.name == "ITEM_PICKUP_DISABLED": return True keyboard.send('enter') wait(0.1, 0.25) keyboard.send('up') wait(0.1, 0.25) keyboard.send('enter') wait(0.1, 0.25) return True
class App(tkinter.Tk): def __init__(self): super().__init__() self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.protocol("WM_DELETE_WINDOW", lambda: destroy_all(self.connection, self)) self.title("Chat Client") self.length_struct = struct.Struct("!I") self.messenger = Messenger(self.connection, self.length_struct) self.username = "" self.password = "" self.connect = ConnectScreen(self, self.connect_to_server) self.login = LoginScreen(self, self.check_data) self.chat = ChatScreen(self, self.handle_out_going) self.connect.pack() self.connect.pack_children() def connect_to_server(self): """ Callback for self.connect. Retrieves the user submitted address and port. Attempts to make connection. If any errors are caught the connect_message widget will be updated with information about the error. """ host_address = self.connect.ip_entry.get() host_port = self.connect.port_entry.get() try: host_port = int(host_port) self.connection.connect((host_address, host_port)) self.connect.pack_forget() self.login.pack() self.login.pack_children() except ValueError: self.connect.connect_message.config(text="Invalid Entry For Port\nMust Be an Integer", fg="red") except ConnectionRefusedError: self.connect.connect_message.config(text="Server Refused Connection", fg="red") except socket.gaierror: self.connect.connect_message.config(text="Invalid Address", fg="red") def check_data(self, message_type): """ Communicates with chat server to verify login information. If the login or sign up attempt fails a message is displayed on the login screen. :param message_type: tells the server whether it is a login attempt or signup request """ self.username = self.login.name_entry.get() self.password = self.login.pass_entry.get() # restrict user names to alpha numeric values if not self.username.isalnum(): self.login.display_message.config(text="Username can only be numbers and letters", fg="red") return # format message to be sent message = "{}`{}`{}".format(message_type, self.username, self.password) reply = "" # try communicating with server try: self.messenger.send(message) reply = self.messenger.recv() except ConnectionResetError or ValueError: self.login.display_message.config(text="Connection with server lost...restarting", fg="red") self.login.pack_forget() self.connection.detach() self.connect.pack() # check for all possible server responses if reply == "OK": self.login.pack_forget() self.title(self.username) self.chat.pack() self.chat.pack_children() self.connection.settimeout(.10) # prevents blocking calls of handle_incoming() self.handle_incoming() elif reply == "UNAVAILABLE": self.login.display_message.config(text="Username Unavailable", fg="red") elif reply == "BAD": self.login.display_message.config(text="Incorrect user Info", fg="red") elif reply == "USER ACTIVE": self.login.display_message.config(text="Username is currently already logged in", fg="red") else: self.login.display_message.config(text="Unexpected Server Response") def handle_out_going(self, event=None): """ reads from the chat_entry and sends it to the server. :param event: is used as a place holder for the event information sent by self.chat_entry.bind(<RETURN>) it is not used. """ text = self.chat.chat_entry.get() if text: # prevent empty messages from being sent try: message = "{}: {}".format(self.username, text) # This should be handled by server self.messenger.send(message) self.chat.chat_entry.delete(0, "end") except ConnectionResetError: self.chat.pack_forget() self.login.pack() self.login.display_message.config(text="Connection with server lost") def handle_incoming(self): """ called every 500 milliseconds from within the tkinter mainloop. Will check for incoming socket data, but will pass if socket timeout limit is reached. """ try: message = self.messenger.recv() self.chat.add_message(message) except socket.timeout: pass except ConnectionResetError or struct.error: self.chat.pack_forget() self.login.pack() self.login.display_message.config(text="Connection with server lost") return finally: self.after(500, self.handle_incoming)
class Registry(object): """ Hub of-sorts to talk with different `Cosmid` related files and resources. Can be seen as the API endpoint for `Cosmid`. """ def __init__(self): super(Registry, self).__init__() # Set up YAML parser for optional config file self.config_path = path("cosmid.yaml") self.config = ConfigReader(self.config_path) # Extract stuff from config self.email = self.config.find("email") # Path to resource storage directory self.directory = path(self.config.find("directory", default="resources")) # Load history file consisting of already downloaded resources self.history_path = path(self.directory + "/.cosmid.yaml") self.history = HistoryReader(self.history_path) # Set up a :class:`cosmid.messenger.Messenger` self.messenger = Messenger("cosmid") def get(self, resource_id, type_="class"): """ <public> Returns an instance of the specified resource class. Dodges an ``ImportError`` when failing to import a resource and returns ``None`` instead. .. code-block:: python >>> resource = registry.get("ccds") >>> resource.latest() 'Hs104' :param str resource_id: The resource key (name of module) :returns: A class instance of the resource """ try: if type_ == "class": return load_class("cosmid.resources.{}.Resource".format(resource_id))() elif type_ == "module": return importlib.import_module("cosmid.resources." + resource_id) else: raise ValueError("Argument must be either 'class' or 'module'.") except ImportError: return None def grab(self, resource_id, target, collapse=False): """ <public> Returns all that's nessesary to download a specific resource. The method will try to correct both ``resource_id`` and the ``target`` release tag. :param str resource_id: What resource to download :param str target: What release of the resource to download """ # Either import resource class or print warning and move on. # Test matching the resource ID options = [item[0] for item in self.ls()] resource_id = self.matchOne(resource_id, options) if resource_id is None: message = "Couldn't match resource ID: '{}'".format(resource_id) self.messenger.send("warning", message) return None, None, None, None # Get the resource resource = self.get(resource_id) # Now let's figure out the version # No specified version will match to the latest resource release if target == "latest": version = resource.latest() else: options = resource.versions() version = self.matchOne(target, options) if version is None: message = ("Couldn't match version '{id}#{v}'; {vers}" .format(v=target, id=resource.id, vers=", ".join(options))) self.messenger.send("warning", message) return None, None, None, None # Get the goahead! (that we haven't already downloaded it) if self.goahead(resource, version): # Finally we can determine the paths to download and save the files dl_paths = resource.paths(version) if collapse: # The user can select to store all downloaded files in the same # directory resource_dir = "" else: # Or by default separate different resources into subdirectories resource_dir = "/" + resource.id save_paths = [("{dir}{mid}/{file}" .format(dir=self.directory, mid=resource_dir, file=name)) for name in resource.names] # Add the resource to the history file as downloaded self.history.add(resource_id, { "version": version, "target": target, "names": resource.names, "sources": dl_paths }) return resource, dl_paths, save_paths, version else: # The resource was already downloaded return None, None, None, None def ls(self): """ <public> Returns a list of resource IDs and docstrings for all the included resource modules. *Reference*: http://stackoverflow.com/questions/1707709/list-all-the-modules-that-are-part-of-a-python-package .. code-block:: python >>> registry.ls() [('ccds', 'A curated database of generic element'), ...] :returns: A list of tuples: ``(resource_id, docstring)`` :rtype: list """ # Store everything here items = [] prefix = resources.__name__ + "." # Fetch all the resource modules modules = pkgutil.iter_modules(resources.__path__, prefix) # Loop over all resource modules for importer, modpath, ispkg in modules: # Strip path modname = modpath.split(".")[-1] # Load the `Resource` class for the module module = self.get(modname, type_="module") # Save name and docstring items.append((modname, module.__doc__)) return items def search(self, query, limit=5): """ <public> Fuzzy matches a query string against each of the resource IDs and returns a limited number of results in order of match score. .. code-block:: python >>> registry.search("asmebly", limit=2) [('ensembl_assembly', 68), ('ncbi_assembly', 68)] :param str query: A string to match against the resource IDs :param int limit: (optional) A maximum number of results to return :returns: A list of tuples: ``(resource_id, score)` :rtype: list """ # List all the available resources resources = self.ls() # Focus on resource IDs resource_ids = [resource[0] for resource in resources] # Fuzzy match against the resource IDs and return in order of best match return process.extract(query, resource_ids, limit=limit) def matchOne(self, target, options, threshold=60): """ <public> Fuzzy matches e.g. a target version tag against a list of options. Returns the most likely match if the match score is sufficient. .. code-block:: python >>> resource = registry.get("ccds") >>> registry.matchOne(104, resource.versions()) 'Hs104' >>> registry.matchOne("ensembl", registry.ls()) 'ensembl_assembly' :param object target: Any Python object to match with :param list options: A list of possible options to match against :param int threshold: A lower threshold for accepting a best match :returns: The object with the best match (unless score is below threshold) :rtype: Python object """ # Match against the options and extract the top match only result, score = process.extractOne(target, map(str, options)) # Arbitrary lower limit for returning a *mathcing* result if score >= threshold: return result else: return None def goahead(self, resource, version): """ Determines whether it's any idea in going ahead with a download. """ # Get any currently downloaded resources current = self.history.find(resource.id, default={}) # Make sure we haven't already downloaded the resource if current.get("version") == version: message = "'{}' already downloaded and up-to-date.".format(resource.id) self.messenger.send("update", message) return False return True
class SessionManager: def __init__(self, port, ip_address, secret_value, continueHandler): # can be either a server or client. if ip_address=None, be a server on port. Otherwise, try to connect to # ip_address:port self.port = port self.ip_address = ip_address self.master_key = create_key(secret_value) self.log = logging.getLogger(__name__) self.continueHandler = continueHandler self._messenger = None self.reset_messenger() def generate_and_send_iv(self, session_socket): iv = urandom(16) self.continueHandler(iv) # send iv over socket first! sent_len = 0 while sent_len < len(iv): sent = session_socket.send(iv[sent_len:]) if sent == 0: raise RuntimeError("socket send connection issue") sent_len += sent # how much of the message we have sent logging.getLogger(__name__).info("sent iv: " + str(iv)) return iv def receive_iv(self, session_socket): iv = b'' while len(iv) < IV_LENGTH: chunk = session_socket.recv(IV_LENGTH - len(iv)) if chunk == b'': session_socket.close() raise RuntimeError("socket closed") iv += chunk logging.getLogger(__name__).info('received iv: {}'.format(str(iv))) return iv def authenticate_as_server(self, session_socket): # authenticates an external client connected via session_socket iv = self.generate_and_send_iv(session_socket) # the server should generate a random iv master_encrypter = Encrypter(self.master_key, iv) m_messenger = Messenger(session_socket, master_encrypter, self.continueHandler) secret_b = generateAorB() public_b = str(pow(g, secret_b, p)) server_challenge = genStr(CHALLENGE_LENGTH) server_challenge_hash = str(create_key(server_challenge)) response = m_messenger.recv() while not response: response = m_messenger.recv() client_challenge = response[:CHALLENGE_LENGTH] client_challenge_hash = str(create_key(client_challenge)) public_a = response[CHALLENGE_LENGTH:] self.log.info('publicA is {}'.format(public_a)) m_messenger.send(client_challenge_hash + server_challenge + public_b) session_key = create_key(str(pow(int(public_a), secret_b, p))) self.log.info('session key is {}'.format(session_key)) response = m_messenger.recv() while not response: response = m_messenger.recv() if response != server_challenge_hash: self.log.warn('Client could not be authenticated. Session will be terminated!') m_messenger.close() else: print('Server Authentication Successful!!!') session_encrypter = Encrypter(session_key, iv) self._messenger = Messenger(session_socket, session_encrypter, self.continueHandler) def authenticate_as_client(self, session_socket): # authenticates an external server connected via session_socket iv = self.receive_iv(session_socket) master_encrypter = Encrypter(self.master_key, iv) m = Messenger(session_socket, master_encrypter, self.continueHandler) client_challenge = genStr(CHALLENGE_LENGTH) client_challenge_hash = str(create_key(client_challenge)) hash_len = len(client_challenge_hash) secretA = generateAorB() publicA = pow(g, secretA, p) m.send(client_challenge + str(publicA)) response = m.recv() while not response: response = m.recv() if response[:hash_len] != client_challenge_hash: m.close() raise Exception('client could not authenticate') server_challenge_hash = str(create_key(response[hash_len:hash_len + CHALLENGE_LENGTH])) m.send(server_challenge_hash) public_b = int(response[hash_len + CHALLENGE_LENGTH:]) self.log.info('g^b mod p is {}'.format(public_b)) session_key = create_key(str(pow(public_b, secretA, p))) self.log.info('Session key generated by the client is {}'.format(session_key)) session_encrypter = Encrypter(session_key, iv) session_m = Messenger(session_socket, session_encrypter, self.continueHandler) self._messenger = session_m def reset_messenger(self): if self._messenger is not None: self._messenger.close() self._messenger = None # AF_INET = ipv4, SOCK_STREAM = tcp s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # assuming we can use the same "client socket" for both reading and writing if self.ip_address is None: # server init s.bind(('', self.port)) self.log.info("Listening for connection on port {}".format(self.port)) s.listen(1) # listen for only one connection session_socket, addr = s.accept() self.log.info("Accepted connection from {}".format(addr)) self.authenticate_as_server(session_socket) s.close() else: # client init: specify ip address and port to try to ping self.log.info("Trying to connect to {}:{}".format(self.ip_address, self.port)) s.connect((self.ip_address, self.port)) self.authenticate_as_client(s) def checkReceivedMessages(self): try: nextReceivedMessage = self.recv() # Get and Send Messages return nextReceivedMessage except Exception as e: self.log.warning("Session closed: {}".format(e)) self.reset_messenger() # Return 0 as default return 0 def send(self, msg): self._messenger.send(msg) def recv(self): data_in = self._messenger.recv() if len(data_in) > 0: self.log.info('received data: ' + data_in) return data_in def close(self): self._messenger.close()
class App(tkinter.Tk): def __init__(self): super().__init__() self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.protocol("WM_DELETE_WINDOW", lambda: destroy_all(self.connection, self)) self.title("Chat Client") self.length_struct = struct.Struct("!I") self.messenger = Messenger(self.connection, self.length_struct) self.username = "" self.password = "" self.connect = ConnectScreen(self, self.connect_to_server) self.login = LoginScreen(self, self.check_data) self.chat = ChatScreen(self, self.handle_out_going) self.connect.pack() self.connect.pack_children() def connect_to_server(self): """ Callback for self.connect. Retrieves the user submitted address and port. Attempts to make connection. If any errors are caught the connect_message widget will be updated with information about the error. """ host_address = self.connect.ip_entry.get() host_port = self.connect.port_entry.get() try: host_port = int(host_port) self.connection.connect((host_address, host_port)) self.connect.pack_forget() self.login.pack() self.login.pack_children() except ValueError: self.connect.connect_message.config( text="Invalid Entry For Port\nMust Be an Integer", fg="red") except ConnectionRefusedError: self.connect.connect_message.config( text="Server Refused Connection", fg="red") except socket.gaierror: self.connect.connect_message.config(text="Invalid Address", fg="red") def check_data(self, message_type): """ Communicates with chat server to verify login information. If the login or sign up attempt fails a message is displayed on the login screen. :param message_type: tells the server whether it is a login attempt or signup request """ self.username = self.login.name_entry.get() self.password = self.login.pass_entry.get() # restrict user names to alpha numeric values if not self.username.isalnum(): self.login.display_message.config( text="Username can only be numbers and letters", fg="red") return # format message to be sent message = "{}`{}`{}".format(message_type, self.username, self.password) reply = "" # try communicating with server try: self.messenger.send(message) reply = self.messenger.recv() except ConnectionResetError or ValueError: self.login.display_message.config( text="Connection with server lost...restarting", fg="red") self.login.pack_forget() self.connection.detach() self.connect.pack() # check for all possible server responses if reply == "OK": self.login.pack_forget() self.title(self.username) self.chat.pack() self.chat.pack_children() self.connection.settimeout( .10) # prevents blocking calls of handle_incoming() self.handle_incoming() elif reply == "UNAVAILABLE": self.login.display_message.config(text="Username Unavailable", fg="red") elif reply == "BAD": self.login.display_message.config(text="Incorrect user Info", fg="red") elif reply == "USER ACTIVE": self.login.display_message.config( text="Username is currently already logged in", fg="red") else: self.login.display_message.config( text="Unexpected Server Response") def handle_out_going(self, event=None): """ reads from the chat_entry and sends it to the server. :param event: is used as a place holder for the event information sent by self.chat_entry.bind(<RETURN>) it is not used. """ text = self.chat.chat_entry.get() if text: # prevent empty messages from being sent try: message = "{}: {}".format( self.username, text) # This should be handled by server self.messenger.send(message) self.chat.chat_entry.delete(0, "end") except ConnectionResetError: self.chat.pack_forget() self.login.pack() self.login.display_message.config( text="Connection with server lost") def handle_incoming(self): """ called every 500 milliseconds from within the tkinter mainloop. Will check for incoming socket data, but will pass if socket timeout limit is reached. """ try: message = self.messenger.recv() self.chat.add_message(message) except socket.timeout: pass except ConnectionResetError or struct.error: self.chat.pack_forget() self.login.pack() self.login.display_message.config( text="Connection with server lost") return finally: self.after(500, self.handle_incoming)
class Jd_test(object): def __init__(self): use_random_ua = global_config.getboolean('config', 'random_useragent') self.user_agent = DEFAULT_USER_AGENT if not use_random_ua else get_random_useragent( ) self.headers = {'User-Agent': self.user_agent} self.is_login = False self.nick_name = '' ################## self.eid = global_config.get('config', 'eid') self.fp = global_config.get('config', 'fp') self.track_id = global_config.get('config', 'track_id') self.item_cat = dict() self.item_vender_ids = dict() # 记录商家id self.timeout = float( global_config.get('config', 'timeout') or DEFAULT_TIMEOUT) self.send_message = global_config.getboolean('messenger', 'enable') self.messenger = Messenger(global_config.get( 'messenger', 'sckey')) if self.send_message else None self.sess = requests.session() ################################# for qrcode login start #################################################### def login_by_QRcode(self): """二维码登陆 :return: """ if self.is_login: logger.info('登录成功') return self._get_login_page() # download QR code if not self._get_QRcode(): raise AsstException('二维码下载失败') # get QR code ticket ticket = None retry_times = 85 for _ in range(retry_times): ticket = self._get_QRcode_ticket() if ticket: break time.sleep(2) else: raise AsstException('二维码过期,请重新获取扫描') # validate QR code ticket if not self._validate_QRcode_ticket(ticket): raise AsstException('二维码信息校验失败') logger.info('二维码登录成功') self.is_login = True self.nick_name = self.get_user_info() self._save_cookies() def _get_login_page(self): url = "https://passport.jd.com/new/login.aspx" page = self.sess.get(url, headers=self.headers) return page def _get_QRcode(self): url = 'https://qr.m.jd.com/show' payload = { 'appid': 133, 'size': 147, 't': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://passport.jd.com/new/login.aspx', } resp = self.sess.get(url=url, headers=headers, params=payload) if not response_status(resp): logger.info('获取二维码失败') return False QRCode_file = 'QRcode.png' save_image(resp, QRCode_file) logger.info('二维码获取成功,请打开京东APP扫描') open_image(QRCode_file) return True def _get_QRcode_ticket(self): url = 'https://qr.m.jd.com/check' payload = { 'appid': '133', 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 'token': self.sess.cookies.get('wlfstk_smdl'), '_': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://passport.jd.com/new/login.aspx', } resp = self.sess.get(url=url, headers=headers, params=payload) if not response_status(resp): logger.error('获取二维码扫描结果异常') return False resp_json = parse_json(resp.text) if resp_json['code'] != 200: logger.info('Code: %s, Message: %s', resp_json['code'], resp_json['msg']) return None else: logger.info('已完成手机客户端确认') return resp_json['ticket'] def _validate_QRcode_ticket(self, ticket): url = 'https://passport.jd.com/uc/qrCodeTicketValidation' print(url) headers = { 'User-Agent': self.user_agent, 'Referer': 'https://passport.jd.com/uc/login?ltype=logout', } resp = self.sess.get(url=url, headers=headers, params={'t': ticket}) if not response_status(resp): return False resp_json = json.loads(resp.text) if resp_json['returnCode'] == 0: return True else: logger.info(resp_json) return False @check_login def get_user_info(self): """获取用户信息 :return: 用户名 """ url = 'https://passport.jd.com/user/petName/getUserInfoForMiniJd.action' payload = { 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), '_': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://order.jd.com/center/list.action', } try: resp = self.sess.get(url=url, params=payload, headers=headers) resp_json = parse_json(resp.text) # many user info are included in response, now return nick name in it # jQuery2381773({"imgUrl":"//storage.360buyimg.com/i.imageUpload/xxx.jpg","lastLoginTime":"","nickName":"xxx","plusStatus":"0","realName":"xxx","userLevel":x,"userScoreVO":{"accountScore":xx,"activityScore":xx,"consumptionScore":xxxxx,"default":false,"financeScore":xxx,"pin":"xxx","riskScore":x,"totalScore":xxxxx}}) return resp_json.get('nickName') or 'jd' except Exception: return 'jd' def _save_cookies(self): cookies_file = './cookies/{0}.cookies'.format(self.nick_name) directory = os.path.dirname(cookies_file) if not os.path.exists(directory): os.makedirs(directory) with open(cookies_file, 'wb') as f: print(self.sess.cookies) pickle.dump(self.sess.cookies, f) ############################## for qrcode login end ####################################################### ############################## for user pwd login start ####################################################### @deprecated def login_by_username(self): if self.is_login: logger.info('登录成功') return True username = input('账号:') password = input('密码:') if (not username) or (not password): logger.error('用户名或密码不能为空') return False self.username = username data = self._get_login_data() uuid = data['uuid'] auth_code = '' if self._need_auth_code(username): logger.info('本次登录需要验证码') auth_code = self._get_auth_code(uuid) else: logger.info('本次登录不需要验证码') login_url = "https://passport.jd.com/uc/loginService" payload = { 'uuid': uuid, 'version': 2015, 'r': random.random(), } data['authcode'] = auth_code data['loginname'] = username data['nloginpwd'] = encrypt_pwd(password) headers = { 'User-Agent': self.user_agent, 'Origin': 'https://passport.jd.com', } resp = self.sess.post(url=login_url, data=data, headers=headers, params=payload) if not response_status(resp): logger.error('登录失败') return False if not self._get_login_result(resp): return False # login success logger.info('登录成功') self.nick_name = self.get_user_info() self._save_cookies() self.is_login = True return True @deprecated def _get_login_data(self): page = self._get_login_page() soup = BeautifulSoup(page.text, "html.parser") input_list = soup.select('.form input') # eid & fp are generated by local javascript code according to browser environment return { 'sa_token': input_list[0]['value'], 'uuid': input_list[1]['value'], '_t': input_list[4]['value'], 'loginType': input_list[5]['value'], 'pubKey': input_list[7]['value'], 'eid': self.eid, 'fp': self.fp, } @deprecated def _need_auth_code(self, username): url = 'https://passport.jd.com/uc/showAuthCode' data = { 'loginName': username, } payload = { 'version': 2015, 'r': random.random(), } resp = self.sess.post(url, params=payload, data=data, headers=self.headers) if not response_status(resp): logger.error('获取是否需要验证码失败') return False resp_json = json.loads(resp.text[1:-1]) # ({"verifycode":true}) return resp_json['verifycode'] @deprecated def _get_auth_code(self, uuid): image_file = os.path.join(os.getcwd(), 'jd_authcode.jpg') url = 'https://authcode.jd.com/verify/image' payload = { 'a': 1, 'acid': uuid, 'uid': uuid, 'yys': str(int(time.time() * 1000)), } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://passport.jd.com/uc/login', } resp = self.sess.get(url, params=payload, headers=headers) if not response_status(resp): logger.error('获取验证码失败') return '' save_image(resp, image_file) open_image(image_file) return input('验证码:') @deprecated def _get_login_result(self, resp): print(resp.text) print("bug1") resp_json = parse_json(resp.text) error_msg = '' print("bug2") if 'success' in resp_json: print("bug3") # {"success":"http://www.jd.com"} return True elif 'emptyAuthcode' in resp_json: print("bug4") # {'_t': '_t', 'emptyAuthcode': '请输入验证码'} # {'_t': '_t', 'emptyAuthcode': '验证码不正确或验证码已过期'} error_msg = resp_json['emptyAuthcode'] elif 'username' in resp_json: print("bug5") # {'_t': '_t', 'username': '******'} # {'username': '******', 'venture': 'xxxx', 'p': 'xxxx', 'ventureRet': 'http://www.jd.com/', '_t': '_t'} if resp_json['username'] == '服务器繁忙,请稍后再试': error_msg = resp_json['username'] + '(预计账户存在风险,需短信激活)' else: error_msg = resp_json['username'] elif 'pwd' in resp_json: print("bug6") # {'pwd': '账户名与密码不匹配,请重新输入', '_t': '_t'} error_msg = resp_json['pwd'] else: print("bug7") error_msg = resp_json logger.error(error_msg) print("bugx") return False ############################## for user pwd login end ####################################################### ############################## for buy start ####################################################### @check_login def buy_item_in_stock(self, sku_ids, area, wait_all=False, stock_interval=3, submit_retry=3, submit_interval=5): """根据库存自动下单商品 :param sku_ids: 商品id。可以设置多个商品,也可以带数量,如:'1234' 或 '1234,5678' 或 '1234:2' 或 '1234:2,5678:3' :param area: 地区id :param wait_all: 是否等所有商品都有货才一起下单,可选参数,默认False :param stock_interval: 查询库存时间间隔,可选参数,默认3秒 :param submit_retry: 提交订单失败后重试次数,可选参数,默认3次 :param submit_interval: 提交订单失败后重试时间间隔,可选参数,默认5秒 :return: """ items_dict = parse_sku_id(sku_ids) items_list = list(items_dict.keys()) area_id = parse_area_id(area=area) if not wait_all: logger.info('下单模式:%s 任一商品有货并且未下架均会尝试下单', items_list) while True: for (sku_id, count) in items_dict.items(): if not self.if_item_can_be_ordered(sku_ids={sku_id: count}, area=area_id): logger.info('%s 不满足下单条件,%ss后进行下一次查询', sku_id, stock_interval) else: logger.info('%s 满足下单条件,开始执行', sku_id) self._cancel_select_all_cart_item() self._add_or_change_cart_item(self.get_cart_detail(), sku_id, count) if self.submit_order_with_retry( submit_retry, submit_interval): return time.sleep(stock_interval) else: logger.info('下单模式:%s 所有都商品同时有货并且未下架才会尝试下单', items_list) while True: if not self.if_item_can_be_ordered(sku_ids=sku_ids, area=area_id): logger.info('%s 不满足下单条件,%ss后进行下一次查询', items_list, stock_interval) else: logger.info('%s 满足下单条件,开始执行', items_list) self._cancel_select_all_cart_item() shopping_cart = self.get_cart_detail() for (sku_id, count) in items_dict.items(): self._add_or_change_cart_item(shopping_cart, sku_id, count) if self.submit_order_with_retry(submit_retry, submit_interval): return time.sleep(stock_interval) @check_login def if_item_can_be_ordered(self, sku_ids, area): """判断商品是否能下单 :param sku_ids: 商品id,多个商品id中间使用英文逗号进行分割 :param area: 地址id :return: 商品是否能下单 True/False """ items_dict = parse_sku_id(sku_ids=sku_ids) area_id = parse_area_id(area) # 判断商品是否能下单 if len(items_dict) > 1: return self.get_multi_item_stock_new(sku_ids=items_dict, area=area_id) sku_id, count = list(items_dict.items())[0] return self.get_single_item_stock(sku_id=sku_id, num=count, area=area_id) def get_multi_item_stock_new(self, sku_ids, area): """获取多个商品库存状态(新) 当所有商品都有货,返回True;否则,返回False。 :param sku_ids: 多个商品的id。可以传入中间用英文逗号的分割字符串,如"123,456" :param area: 地区id :return: 多个商品是否同时有货 True/False """ items_dict = parse_sku_id(sku_ids=sku_ids) area_id = parse_area_id(area=area) url = 'https://c0.3.cn/stocks' payload = { 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 'type': 'getstocks', 'skuIds': ','.join(items_dict.keys()), 'area': area_id, '_': str(int(time.time() * 1000)) } headers = {'User-Agent': self.user_agent} resp_text = '' try: resp_text = requests.get(url=url, params=payload, headers=headers, timeout=self.timeout).text stock = True for sku_id, info in parse_json(resp_text).items(): sku_state = info.get('skuState') # 商品是否上架 stock_state = info.get('StockState') # 商品库存状态 if sku_state == 1 and stock_state in (33, 40): continue else: stock = False break return stock except requests.exceptions.Timeout: logger.error('查询 %s 库存信息超时(%ss)', list(items_dict.keys()), self.timeout) return False except requests.exceptions.RequestException as request_exception: logger.error('查询 %s 库存信息发生网络请求异常:%s', list(items_dict.keys()), request_exception) return False except Exception as e: logger.error('查询 %s 库存信息发生异常, resp: %s, exception: %s', list(items_dict.keys()), resp_text, e) return False def get_single_item_stock(self, sku_id, num, area): """获取单个商品库存状态 :param sku_id: 商品id :param num: 商品数量 :param area: 地区id :return: 商品是否有货 True/False """ area_id = parse_area_id(area) cat = self.item_cat.get(sku_id) vender_id = self.item_vender_ids.get(sku_id) if not cat: page = self._get_item_detail_page(sku_id) match = re.search(r'cat: \[(.*?)\]', page.text) cat = match.group(1) self.item_cat[sku_id] = cat match = re.search(r'venderId:(\d*?),', page.text) vender_id = match.group(1) self.item_vender_ids[sku_id] = vender_id url = 'https://c0.3.cn/stock' payload = { 'skuId': sku_id, 'buyNum': num, 'area': area_id, 'ch': 1, '_': str(int(time.time() * 1000)), 'callback': 'jQuery{}'.format(random.randint(1000000, 9999999)), 'extraParam': '{"originid":"1"}', # get error stock state without this param 'cat': cat, # get 403 Forbidden without this param (obtained from the detail page) 'venderId': vender_id # return seller information with this param (can't be ignored) } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://item.jd.com/{}.html'.format(sku_id), } resp_text = '' try: resp_text = requests.get(url=url, params=payload, headers=headers, timeout=self.timeout).text resp_json = parse_json(resp_text) stock_info = resp_json.get('stock') sku_state = stock_info.get('skuState') # 商品是否上架 stock_state = stock_info.get( 'StockState' ) # 商品库存状态:33 -- 现货 0,34 -- 无货 36 -- 采购中 40 -- 可配货 return sku_state == 1 and stock_state in (33, 40) except requests.exceptions.Timeout: logger.error('查询 %s 库存信息超时(%ss)', sku_id, self.timeout) return False except requests.exceptions.RequestException as request_exception: logger.error('查询 %s 库存信息发生网络请求异常:%s', sku_id, request_exception) return False except Exception as e: logger.error('查询 %s 库存信息发生异常, resp: %s, exception: %s', sku_id, resp_text, e) return False def _get_item_detail_page(self, sku_id): """访问商品详情页 :param sku_id: 商品id :return: 响应 """ url = 'https://item.jd.com/{}.html'.format(sku_id) page = requests.get(url=url, headers=self.headers) return page def _cancel_select_all_cart_item(self): """取消勾选购物车中的所有商品 :return: 取消勾选结果 True/False """ url = "https://cart.jd.com/cancelAllItem.action" data = { 't': 0, 'outSkus': '', 'random': random.random() # 'locationId' can be ignored } resp = self.sess.post(url, data=data) return response_status(resp) def _add_or_change_cart_item(self, cart, sku_id, count): """添加商品到购物车,或修改购物车中商品数量 如果购物车中存在该商品,会修改该商品的数量并勾选;否则,会添加该商品到购物车中并勾选。 :param cart: 购物车信息 dict :param sku_id: 商品id :param count: 商品数量 :return: 运行结果 True/False """ if sku_id in cart: logger.info('%s 已在购物车中,调整数量为 %s', sku_id, count) cart_item = cart.get(sku_id) return self._change_item_num_in_cart( sku_id=sku_id, vender_id=cart_item.get('vender_id'), num=count, p_type=cart_item.get('p_type'), target_id=cart_item.get('target_id'), promo_id=cart_item.get('promo_id')) else: logger.info('%s 不在购物车中,开始加入购物车,数量 %s', sku_id, count) return self.add_item_to_cart(sku_ids={sku_id: count}) @check_login def get_cart_detail(self): """获取购物车商品详情 :return: 购物车商品信息 dict """ url = 'https://cart.jd.com/cart.action' resp = self.sess.get(url) soup = BeautifulSoup(resp.text, "html.parser") cart_detail = dict() for item in soup.find_all(class_='item-item'): try: sku_id = item['skuid'] # 商品id # 例如:['increment', '8888', '100001071956', '1', '13', '0', '50067652554'] # ['increment', '8888', '100002404322', '2', '1', '0'] item_attr_list = item.find(class_='increment')['id'].split('_') p_type = item_attr_list[4] promo_id = target_id = item_attr_list[-1] if len( item_attr_list) == 7 else 0 cart_detail[sku_id] = { 'name': get_tag_value(item.select('div.p-name a')), # 商品名称 'verder_id': item['venderid'], # 商家id 'count': int(item['num']), # 数量 'unit_price': get_tag_value(item.select('div.p-price strong'))[1:], # 单价 'total_price': get_tag_value(item.select('div.p-sum strong'))[1:], # 总价 'is_selected': 'item-selected' in item['class'], # 商品是否被勾选 'p_type': p_type, 'target_id': target_id, 'promo_id': promo_id } except Exception as e: logger.error("某商品在购物车中的信息无法解析,报错信息: %s,该商品自动忽略。 %s", e, item) logger.info('购物车信息:%s', cart_detail) return cart_detail def _change_item_num_in_cart(self, sku_id, vender_id, num, p_type, target_id, promo_id): """修改购物车商品的数量 修改购物车中商品数量后,该商品将会被自动勾选上。 :param sku_id: 商品id :param vender_id: 商家id :param num: 目标数量 :param p_type: 商品类型(可能) :param target_id: 参数用途未知,可能是用户判断优惠 :param promo_id: 参数用途未知,可能是用户判断优惠 :return: 商品数量修改结果 True/False """ url = "https://cart.jd.com/changeNum.action" data = { 't': 0, 'venderId': vender_id, 'pid': sku_id, 'pcount': num, 'ptype': p_type, 'targetId': target_id, 'promoID': promo_id, 'outSkus': '', 'random': random.random(), # 'locationId' } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://cart.jd.com/cart', } resp = self.sess.post(url, data=data, headers=headers) return json.loads( resp.text)['sortedWebCartResult']['achieveSevenState'] == 2 @check_login def submit_order_with_retry(self, retry=3, interval=4): """提交订单,并且带有重试功能 :param retry: 重试次数 :param interval: 重试间隔 :return: 订单提交结果 True/False """ ############ 定时器触发下单 ########## # while True: # time_now = time.strftime("%H:%M:%S", time.localtime()) # 刷新 # print(time_now) # if time_now == "10:00:00": #此处设置每天定时的时间 # # 此处3行替换为需要执行的动作 # for i in range(1, retry + 1): # logger.info('第[%s/%s]次尝试提交订单', i, retry) # self.get_checkout_page_detail() # if self.submit_order(): # logger.info('第%s次提交订单成功', i) # return True # else: # if i < retry: # logger.info('第%s次提交失败,%ss后重试', i, interval) # time.sleep(interval) # else: # logger.info('重试提交%s次结束', retry) # return False # time.sleep(2) # 因为以秒定时,所以暂停2秒,使之不会在1秒内执行多次 ################# ########直接下单############# for i in range(1, retry + 1): logger.info('第[%s/%s]次尝试提交订单', i, retry) self.get_checkout_page_detail() if self.submit_order(): logger.info('第%s次提交订单成功', i) return True else: if i < retry: logger.info('第%s次提交失败,%ss后重试', i, interval) time.sleep(interval) else: logger.info('重试提交%s次结束', retry) return False @check_login def add_item_to_cart(self, sku_ids): """添加商品到购物车 重要: 1.商品添加到购物车后将会自动被勾选✓中。 2.在提交订单时会对勾选的商品进行结算。 3.部分商品(如预售、下架等)无法添加到购物车 京东购物车可容纳的最大商品种数约为118-120种,超过数量会加入购物车失败。 :param sku_ids: 商品id,格式:"123" 或 "123,456" 或 "123:1,456:2"。若不配置数量,默认为1个。 :return: """ url = 'https://cart.jd.com/gate.action' headers = { 'User-Agent': self.user_agent, } for sku_id, count in parse_sku_id(sku_ids=sku_ids).items(): payload = { 'pid': sku_id, 'pcount': count, 'ptype': 1, } resp = self.sess.get(url=url, params=payload, headers=headers) if 'https://cart.jd.com/cart.action' in resp.url: # 套装商品加入购物车后直接跳转到购物车页面 result = True else: # 普通商品成功加入购物车后会跳转到提示 "商品已成功加入购物车!" 页面 soup = BeautifulSoup(resp.text, "html.parser") result = bool(soup.select( 'h3.ftx-02')) # [<h3 class="ftx-02">商品已成功加入购物车!</h3>] if result: logger.info('%s x %s 已成功加入购物车', sku_id, count) else: logger.error('%s 添加到购物车失败', sku_id) @check_login def get_checkout_page_detail(self): """获取订单结算页面信息 该方法会返回订单结算页面的详细信息:商品名称、价格、数量、库存状态等。 :return: 结算信息 dict """ url = 'http://trade.jd.com/shopping/order/getOrderInfo.action' # url = 'https://cart.jd.com/gotoOrder.action' payload = { 'rid': str(int(time.time() * 1000)), } try: resp = self.sess.get(url=url, params=payload) if not response_status(resp): logger.error('获取订单结算页信息失败') return soup = BeautifulSoup(resp.text, "html.parser") self.risk_control = get_tag_value(soup.select('input#riskControl'), 'value') order_detail = { 'address': soup.find( 'span', id='sendAddr').text[5:], # remove '寄送至: ' from the begin 'receiver': soup.find( 'span', id='sendMobile').text[4:], # remove '收件人:' from the begin 'total_price': soup.find( 'span', id='sumPayPriceId').text[1:], # remove '¥' from the begin 'items': [] } # TODO: 这里可能会产生解析问题,待修复 # for item in soup.select('div.goods-list div.goods-items'): # div_tag = item.select('div.p-price')[0] # order_detail.get('items').append({ # 'name': get_tag_value(item.select('div.p-name a')), # 'price': get_tag_value(div_tag.select('strong.jd-price'))[2:], # remove '¥ ' from the begin # 'num': get_tag_value(div_tag.select('span.p-num'))[1:], # remove 'x' from the begin # 'state': get_tag_value(div_tag.select('span.p-state')) # in stock or out of stock # }) logger.info("下单信息:%s", order_detail) return order_detail except Exception as e: logger.error('订单结算页面数据解析异常(可以忽略),报错信息:%s', e) @check_login def submit_order(self): """提交订单 重要: 1.该方法只适用于普通商品的提交订单(即可以加入购物车,然后结算提交订单的商品) 2.提交订单时,会对购物车中勾选✓的商品进行结算(如果勾选了多个商品,将会提交成一个订单) :return: True/False 订单提交结果 """ url = 'https://trade.jd.com/shopping/order/submitOrder.action' # js function of submit order is included in https://trade.jd.com/shopping/misc/js/order.js?r=2018070403091 data = { 'overseaPurchaseCookies': '', 'vendorRemarks': '[]', 'submitOrderParam.sopNotPutInvoice': 'false', 'submitOrderParam.trackID': 'TestTrackId', 'submitOrderParam.ignorePriceChange': '0', 'submitOrderParam.btSupport': '0', 'riskControl': self.risk_control, 'submitOrderParam.isBestCoupon': 1, 'submitOrderParam.jxj': 1, 'submitOrderParam.trackId': self.track_id, # Todo: need to get trackId 'submitOrderParam.eid': self.eid, 'submitOrderParam.fp': self.fp, 'submitOrderParam.needCheck': 1, } # add payment password when necessary payment_pwd = global_config.get('account', 'payment_pwd') if payment_pwd: data['submitOrderParam.payPassword'] = encrypt_payment_pwd( payment_pwd) headers = { 'User-Agent': self.user_agent, 'Host': 'trade.jd.com', 'Referer': 'http://trade.jd.com/shopping/order/getOrderInfo.action', } try: resp = self.sess.post(url=url, data=data, headers=headers) resp_json = json.loads(resp.text) # 返回信息示例: # 下单失败 # {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60123, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '请输入支付密码!'} # {'overSea': False, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'orderXml': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60017, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '您多次提交过快,请稍后再试'} # {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 60077, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': '获取用户订单信息失败'} # {"cartXml":null,"noStockSkuIds":"xxx","reqInfo":null,"hasJxj":false,"addedServiceList":null,"overSea":false,"orderXml":null,"sign":null,"pin":"xxx","needCheckCode":false,"success":false,"resultCode":600157,"orderId":0,"submitSkuNum":0,"deductMoneyFlag":0,"goJumpOrderCenter":false,"payInfo":null,"scaleSkuInfoListVO":null,"purchaseSkuInfoListVO":null,"noSupportHomeServiceSkuList":null,"msgMobile":null,"addressVO":{"pin":"xxx","areaName":"","provinceId":xx,"cityId":xx,"countyId":xx,"townId":xx,"paymentId":0,"selected":false,"addressDetail":"xx","mobile":"xx","idCard":"","phone":null,"email":null,"selfPickMobile":null,"selfPickPhone":null,"provinceName":null,"cityName":null,"countyName":null,"townName":null,"giftSenderConsigneeName":null,"giftSenderConsigneeMobile":null,"gcLat":0.0,"gcLng":0.0,"coord_type":0,"longitude":0.0,"latitude":0.0,"selfPickOptimize":0,"consigneeId":0,"selectedAddressType":0,"siteType":0,"helpMessage":null,"tipInfo":null,"cabinetAvailable":true,"limitKeyword":0,"specialRemark":null,"siteProvinceId":0,"siteCityId":0,"siteCountyId":0,"siteTownId":0,"skuSupported":false,"addressSupported":0,"isCod":0,"consigneeName":null,"pickVOname":null,"shipmentType":0,"retTag":0,"tagSource":0,"userDefinedTag":null,"newProvinceId":0,"newCityId":0,"newCountyId":0,"newTownId":0,"newProvinceName":null,"newCityName":null,"newCountyName":null,"newTownName":null,"checkLevel":0,"optimizePickID":0,"pickType":0,"dataSign":0,"overseas":0,"areaCode":null,"nameCode":null,"appSelfPickAddress":0,"associatePickId":0,"associateAddressId":0,"appId":null,"encryptText":null,"certNum":null,"used":false,"oldAddress":false,"mapping":false,"addressType":0,"fullAddress":"xxxx","postCode":null,"addressDefault":false,"addressName":null,"selfPickAddressShuntFlag":0,"pickId":0,"pickName":null,"pickVOselected":false,"mapUrl":null,"branchId":0,"canSelected":false,"address":null,"name":"xxx","message":null,"id":0},"msgUuid":null,"message":"xxxxxx商品无货"} # {'orderXml': None, 'overSea': False, 'noStockSkuIds': 'xxx', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'cartXml': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': False, 'resultCode': 600158, 'orderId': 0, 'submitSkuNum': 0, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': {'oldAddress': False, 'mapping': False, 'pin': 'xxx', 'areaName': '', 'provinceId': xx, 'cityId': xx, 'countyId': xx, 'townId': xx, 'paymentId': 0, 'selected': False, 'addressDetail': 'xxxx', 'mobile': 'xxxx', 'idCard': '', 'phone': None, 'email': None, 'selfPickMobile': None, 'selfPickPhone': None, 'provinceName': None, 'cityName': None, 'countyName': None, 'townName': None, 'giftSenderConsigneeName': None, 'giftSenderConsigneeMobile': None, 'gcLat': 0.0, 'gcLng': 0.0, 'coord_type': 0, 'longitude': 0.0, 'latitude': 0.0, 'selfPickOptimize': 0, 'consigneeId': 0, 'selectedAddressType': 0, 'newCityName': None, 'newCountyName': None, 'newTownName': None, 'checkLevel': 0, 'optimizePickID': 0, 'pickType': 0, 'dataSign': 0, 'overseas': 0, 'areaCode': None, 'nameCode': None, 'appSelfPickAddress': 0, 'associatePickId': 0, 'associateAddressId': 0, 'appId': None, 'encryptText': None, 'certNum': None, 'addressType': 0, 'fullAddress': 'xxxx', 'postCode': None, 'addressDefault': False, 'addressName': None, 'selfPickAddressShuntFlag': 0, 'pickId': 0, 'pickName': None, 'pickVOselected': False, 'mapUrl': None, 'branchId': 0, 'canSelected': False, 'siteType': 0, 'helpMessage': None, 'tipInfo': None, 'cabinetAvailable': True, 'limitKeyword': 0, 'specialRemark': None, 'siteProvinceId': 0, 'siteCityId': 0, 'siteCountyId': 0, 'siteTownId': 0, 'skuSupported': False, 'addressSupported': 0, 'isCod': 0, 'consigneeName': None, 'pickVOname': None, 'shipmentType': 0, 'retTag': 0, 'tagSource': 0, 'userDefinedTag': None, 'newProvinceId': 0, 'newCityId': 0, 'newCountyId': 0, 'newTownId': 0, 'newProvinceName': None, 'used': False, 'address': None, 'name': 'xx', 'message': None, 'id': 0}, 'msgUuid': None, 'message': 'xxxxxx商品无货'} # 下单成功 # {'overSea': False, 'orderXml': None, 'cartXml': None, 'noStockSkuIds': '', 'reqInfo': None, 'hasJxj': False, 'addedServiceList': None, 'sign': None, 'pin': 'xxx', 'needCheckCode': False, 'success': True, 'resultCode': 0, 'orderId': 8740xxxxx, 'submitSkuNum': 1, 'deductMoneyFlag': 0, 'goJumpOrderCenter': False, 'payInfo': None, 'scaleSkuInfoListVO': None, 'purchaseSkuInfoListVO': None, 'noSupportHomeServiceSkuList': None, 'msgMobile': None, 'addressVO': None, 'msgUuid': None, 'message': None} if resp_json.get('success'): order_id = resp_json.get('orderId') logger.info('订单提交成功! 订单号:%s', order_id) if self.send_message: self.messenger.send(text='jd-assistant 订单提交成功', desp='订单号:%s' % order_id) return True else: message, result_code = resp_json.get('message'), resp_json.get( 'resultCode') if result_code == 0: self._save_invoice() message = message + '(下单商品可能为第三方商品,将切换为普通发票进行尝试)' elif result_code == 60077: message = message + '(可能是购物车为空 或 未勾选购物车中商品)' elif result_code == 60123: message = message + '(需要在config.ini文件中配置支付密码)' logger.info('订单提交失败, 错误码:%s, 返回信息:%s', result_code, message) logger.info(resp_json) return False except Exception as e: logger.error(e) return False def _save_invoice(self): """下单第三方商品时如果未设置发票,将从电子发票切换为普通发票 http://jos.jd.com/api/complexTemplate.htm?webPamer=invoice&groupName=%E5%BC%80%E6%99%AE%E5%8B%92%E5%85%A5%E9%A9%BB%E6%A8%A1%E5%BC%8FAPI&id=566&restName=jd.kepler.trade.submit&isMulti=true :return: """ url = 'https://trade.jd.com/shopping/dynamic/invoice/saveInvoice.action' data = { "invoiceParam.selectedInvoiceType": 1, "invoiceParam.companyName": "个人", "invoiceParam.invoicePutType": 0, "invoiceParam.selectInvoiceTitle": 4, "invoiceParam.selectBookInvoiceContent": "", "invoiceParam.selectNormalInvoiceContent": 1, "invoiceParam.vatCompanyName": "", "invoiceParam.code": "", "invoiceParam.regAddr": "", "invoiceParam.regPhone": "", "invoiceParam.regBank": "", "invoiceParam.regBankAccount": "", "invoiceParam.hasCommon": "true", "invoiceParam.hasBook": "false", "invoiceParam.consigneeName": "", "invoiceParam.consigneePhone": "", "invoiceParam.consigneeAddress": "", "invoiceParam.consigneeProvince": "请选择:", "invoiceParam.consigneeProvinceId": "NaN", "invoiceParam.consigneeCity": "请选择", "invoiceParam.consigneeCityId": "NaN", "invoiceParam.consigneeCounty": "请选择", "invoiceParam.consigneeCountyId": "NaN", "invoiceParam.consigneeTown": "请选择", "invoiceParam.consigneeTownId": 0, "invoiceParam.sendSeparate": "false", "invoiceParam.usualInvoiceId": "", "invoiceParam.selectElectroTitle": 4, "invoiceParam.electroCompanyName": "undefined", "invoiceParam.electroInvoiceEmail": "", "invoiceParam.electroInvoicePhone": "", "invokeInvoiceBasicService": "true", "invoice_ceshi1": "", "invoiceParam.showInvoiceSeparate": "false", "invoiceParam.invoiceSeparateSwitch": 1, "invoiceParam.invoiceCode": "", "invoiceParam.saveInvoiceFlag": 1 } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://trade.jd.com/shopping/dynamic/invoice/saveInvoice.action', } self.sess.post(url=url, data=data, headers=headers) ############################## for buy end ####################################################### ############################## for auction start ####################################################### # @check_login def auction_bid(self, address, auctionId, eid, price, token, trackId): self.nick_name = "runsparrow" self.load_cookies() url = 'https://used-api.jd.com/auctionRecord/offerPrice' data = { "address": address, "auctionId": auctionId, "eid": eid, "entryid": "", "initFailed": "false", "price": price, "token": token, "trackId": trackId } headers = { 'User-Agent': self.user_agent, 'Referer': 'https://paipai.jd.com/auction-detail/{}'.format(auctionId), } resp = self.sess.post(url=url, data=data, headers=headers) print(resp.text) return resp def load_cookies(self): cookies_file = './cookies/{0}.cookies'.format(self.nick_name) directory = os.path.dirname(cookies_file) with open(cookies_file, 'rb') as f: cookiesdata = pickle.load(f) print(cookiesdata) print("load finished") self.sess.cookies = cookiesdata ############################## for auction end ####################################################### ############################## for coupon start ####################################################### # @check_login def coupon_get(self): self.nick_name = "runsparrow" self.load_cookies() url = 'https://api.m.jd.com/client.action' payload = { "functionId": "newBabelAwardCollection", "body": "{\"activityId\":\"2keLqKB79sxgUcJM8YmbaBpVKeyU\",\"scene\":\"1\",\"args\":\"key=572B71D4718E3E654D32BF5D0C69460D9ECB6FB68080E0F610465AC69EC093862DE520F74806730ED8C8A3FE4FB60917_babel,roleId=F7FE3438F3C6BDA1078A1887E27F6FC8_babel\",\"eid\":\"UNFRVR2PEWUFH7S4HNDQBBTSJPWQEQFRUKFT4NDAHABWOUW5DYAXZYRXDCLYMGOBWWTHIQ7ZS6HNY4ETHQGUXZIMCM\",\"fp\":\"c540fb4a1a6c7e4cb1d8b8ecd5b82cb7\",\"pageClick\":\"Babel_Coupon\",\"mitemAddrId\":\"\",\"geo\":{\"lng\":\"\",\"lat\":\"\"}}", "screen": "750*1334", "client": "wh5", "clientVersion": "1.0.0", "sid": "", "uuid": "", "area": "", "loginType": "3", "callback": "jsonp1" } headers = { 'User-Agent': self.user_agent, 'Host': 'api.m.jd.com', 'Referer': 'Referer: https://pro.jd.com/mall/active/2keLqKB79sxgUcJM8YmbaBpVKeyU/index.html,https://pro.m.jd.com/mall/active/2keLqKB79sxgUcJM8YmbaBpVKeyU/index.html', } resp_text = '' try: resp_text = self.sess.get(url=url, params=payload, headers=headers).text print(resp_text) return True except Exception as e: logger.error(e) return False
class TestBroker(unittest.TestCase): def setUp(self): r = rconfig.Read() self.messenger = Messenger(topic_name) self.redis = redis.StrictRedis(host=r.host, port=r.port, db=r.db, password=r.password) def test_send(self): oldLen = self.redis.llen(input_channel_key(topic_name)) self.messenger.send("To test messenger send a msg.") len = self.redis.llen(input_channel_key(topic_name)) - oldLen self.assertEquals(1, len) self.redis.lpop(input_channel_key(topic_name)) def test_request(self): flag = [True] exception = [None] rst = [None] exit_event = threading.Event() def on_resolve(data): rst[0] = data exit_event.set() def on_reject(ex): print ex flag[0] = False exception[0] = ex exit_event.set() self.messenger.request("To test messenger request a msg.", 5).then(on_resolve, on_reject) pck = self.messenger.broker.get_input_channel( "request_test_input_reader").read(1) pck.respond("OK!") pck.ack() exit_event.wait(5) self.assertEquals("OK!", rst[0].Payload) def test_register_handler(self): messenger = Messenger("TestBot") messenger.register_handler(EchoHandler()) msg_client = Messenger("TestBot") flag = [True] exception = [None] rst = [None] exit_event = threading.Event() def on_resolve(data): rst[0] = data exit_event.set() def on_reject(ex): print ex flag[0] = False exception[0] = ex exit_event.set() try: msg_client.request("To test handler response", 5).then(on_resolve, on_reject) exit_event.wait(5) self.assertEquals("OK!", rst[0].Payload) finally: messenger.stop_monitor()