Esempio n. 1
0
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()
Esempio n. 2
0
    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
Esempio n. 3
0
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()
Esempio n. 4
0
    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)
Esempio n. 5
0
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
Esempio n. 6
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
Esempio n. 8
0
    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()))
Esempio n. 9
0
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)
Esempio n. 10
0
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()
Esempio n. 11
0
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()
Esempio n. 12
0
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()
Esempio n. 13
0
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='')
Esempio n. 14
0
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
Esempio n. 15
0
    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)
Esempio n. 16
0
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)
Esempio n. 17
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
Esempio n. 18
0
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
Esempio n. 19
0
def test_send(session):
    m = Messenger(session)
    m.send('test_send', a='b', b=1)
Esempio n. 20
0
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()
Esempio n. 21
0
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))
Esempio n. 22
0
        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()
Esempio n. 23
0
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)
Esempio n. 24
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
Esempio n. 25
0
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
Esempio n. 26
0
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)
Esempio n. 27
0
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
Esempio n. 28
0
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()
Esempio n. 29
0
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)
Esempio n. 30
0
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
Esempio n. 31
0
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()