예제 #1
0
    def test_ItemDrop_model(self):
        # ItemDrops just use string-times
        # This item is for BRD and CLR, and is droppable with min_dkp 3
        itemdrop = models.ItemDrop("Ochre Tessera", "Bob",
                                   "Mon Aug 17 07:15:39 2020")
        self.assertEqual("BRD, CLR", itemdrop.classes())
        self.assertEqual("Yes", itemdrop.droppable())
        self.assertEqual(config.MIN_DKP, itemdrop.min_dkp())

        # This item is for BRD only and is NODROP
        itemdrop = models.ItemDrop("Light Woolen Mask", "Bob",
                                   "Mon Aug 17 07:15:39 2020")
        self.assertEqual("BRD", itemdrop.classes())
        self.assertEqual("NO", itemdrop.droppable())

        # This item should have the case fixed
        itemdrop = models.ItemDrop("light WOOLEN Mask", "Bob",
                                   "Mon Aug 17 07:15:39 2020")
        self.assertEqual("Light Woolen Mask", itemdrop.name)

        # Should be JSON Encodable
        itemdrop_json = json.dumps(itemdrop, cls=utils.JSONEncoder)

        # Should be JSON Decodable
        loaded_itemdrop = json.loads(itemdrop_json, cls=utils.JSONDecoder)
        self.assertEqual(itemdrop, loaded_itemdrop)
예제 #2
0
    def test_DKPAuction_model_bid_text(self):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        auc = models.DKPAuction(itemdrop, 'VCR', min_dkp=3)

        # No bids
        config.PRIMARY_BID_CHANNEL = 'auc'
        self.assertEqual(
            "/AUC ~[Copper Disc] (DRU, SHD) - BID IN /AUC, MIN 3 DKP. "
            "You MUST include the item name in your bid! Closing in {}. ".
            format(auc.time_remaining_text()), auc.bid_text())

        # Valid bid
        result = auc.add(3, 'Peter')
        self.assertTrue(result)
        self.assertListEqual([('Peter', 3)], auc.highest())

        # Bid exists
        config.PRIMARY_BID_CHANNEL = 'shout'
        self.assertEqual(
            "/SHOUT ~[Copper Disc] (DRU, SHD) - BID IN /SHOUT. "
            "You MUST include the item name in your bid! Currently: "
            "`Peter` with 3 DKP - Closing in {}! ".format(
                auc.time_remaining_text()), auc.bid_text())

        item_name = 'Golden Jasper Earring'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        auc = models.DKPAuction(itemdrop, 'VCR', min_dkp=3)

        # No bids
        config.PRIMARY_BID_CHANNEL = 'gu'
        self.assertEqual(
            "/GU ~[Golden Jasper Earring] - BID IN /GU, MIN 3 DKP. "
            "You MUST include the item name in your bid! Closing in {}. ".
            format(auc.time_remaining_text()), auc.bid_text())
예제 #3
0
    def test_handle_gratss(self, mock_post_event, mock_store_state):
        config.PENDING_AUCTIONS.clear()
        config.ACTIVE_AUCTIONS.clear()

        # Set up a historical auction with bids
        jerkin_1 = models.ItemDrop('Shiverback-hide Jerkin', 'Jim',
                                   'Sun Aug 16 22:47:31 2020')
        config.PENDING_AUCTIONS.append(jerkin_1)
        auction1 = utils.start_auction_dkp(jerkin_1, 'VCR')
        self.assertEqual(config.ACTIVE_AUCTIONS.get(auction1.item.uuid),
                         auction1)
        bid_line = ("[Sun Aug 16 22:47:31 2020] Toald tells the guild, "
                    "'Shiverback-hide Jerkin 1 main'")
        config.RESTRICT_BIDS = False
        bid_match = config.MATCH_BID_GU.match(bid_line)
        message_handlers.handle_bid(bid_match, 'window')
        config.HISTORICAL_AUCTIONS[auction1.item.uuid] = (
            config.ACTIVE_AUCTIONS.pop(auction1.item.uuid))

        # Set up a historical auction without bids (rot)
        disc_1 = models.ItemDrop('Copper Disc', 'Jim',
                                 'Sun Aug 16 22:47:31 2020')
        config.PENDING_AUCTIONS.append(disc_1)
        auction2 = utils.start_auction_dkp(disc_1, 'VCR')
        self.assertEqual(config.ACTIVE_AUCTIONS.get(auction2.item.uuid),
                         auction2)
        config.HISTORICAL_AUCTIONS[auction2.item.uuid] = (
            config.ACTIVE_AUCTIONS.pop(auction2.item.uuid))

        # A gratss message from auction history should not register (bids)
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'~Gratss Toald on [Shiverback-hide Jerkin] (1 DKP)!'")
        match = config.MATCH_GRATSS.match(line)
        self.assertFalse(message_handlers.handle_gratss(match, 'window'))

        # A gratss message from auction history should not register (no bids)
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'~Gratss ROT on [Copper Disc] (0 DKP)!'")
        match = config.MATCH_GRATSS.match(line)
        self.assertFalse(message_handlers.handle_gratss(match, 'window'))

        # A gratss message that doesn't match auction history SHOULD register
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'~Gratss Jim on [Bladestopper] (100 DKP)!'")
        match = config.MATCH_GRATSS.match(line)
        self.assertTrue(message_handlers.handle_gratss(match, 'window'))

        # A gratss message direct to /tell should register (no tell windows)
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells you, "
                "'~Gratss Jim on [Bladestopper] (100 DKP)!'")
        match = config.MATCH_GRATSS.match(line)
        self.assertTrue(message_handlers.handle_gratss(match, 'window'))

        # A gratss message direct to /tell should register (tell windows)
        line = ("[Sun Aug 16 22:47:31 2020] Jim -> You, "
                "'~Gratss Jim on [Bladestopper] (100 DKP)!'")
        match = config.MATCH_GRATSS.match(line)
        self.assertTrue(message_handlers.handle_gratss(match, 'window'))
예제 #4
0
def add_sample_data():
    copper_disc = models.ItemDrop('Copper Disc', 'Bob',
                                  'Mon Aug 17 07:15:39 2020')
    platinum_disc1 = models.ItemDrop('Platinum Disc', 'Jim',
                                     'Mon Aug 17 07:16:05 2020')
    platinum_disc2 = models.ItemDrop('Platinum Disc', 'Bill',
                                     'Mon Aug 17 07:16:05 2020')
    config.PENDING_AUCTIONS.append(copper_disc)
    config.PENDING_AUCTIONS.append(platinum_disc1)
    config.PENDING_AUCTIONS.append(platinum_disc2)
예제 #5
0
def handle_drop(match: re.Match, window: wx.Frame, skip_store=False) -> list:
    timestamp = match.group("time")
    name = match.group("name")
    text = match.group("text")
    guild = config.LAST_WHO_SNAPSHOT.get(name, models.Player(name)).guild
    if text.lower().startswith("looted"):
        LOG.info("Ignoring drop message starting with 'looted'")
        return list()
    if AWARD_MESSAGE_MATCHER.match(text):
        LOG.info("Ignoring drop message that matches Gratss")
        return list()
    if NUMBER_MATCHER.match(text):
        # line contains a number, it's probably a bid, ignore it
        LOG.info("Ignoring drop message with a number, probably a bid")
        return list()
    if config.RESTRICT_BIDS and guild and guild not in config.ALLIANCE_MAP:
        # Some other guild is talking, discard line
        LOG.info("Ignoring ooc from guild %s", guild)
        return list()

    # Handle text to return a list of items linked
    found_items = utils.get_items_from_text(text)
    used_found_items = []
    now = datetime.datetime.now()
    skip = False
    for item in found_items:
        if item.lower() in utils.get_active_item_names():
            LOG.debug("Skipping drop %s because it is already up for auction.")
            continue
        for pending in config.PENDING_AUCTIONS:
            pending_time = dateutil.parser.parse(pending.timestamp)
            if (item.lower() == pending.name.lower()
                    and (now - pending_time).seconds < config.DROP_COOLDOWN):
                skip = True
                LOG.debug("Skipping drop %s because of DROP_COOLDOWN config.",
                          item)
                break
        if skip:
            skip = False
            continue
        drop = models.ItemDrop(item, name, timestamp)
        if (config.NODROP_ONLY and item in extra_data.EXTRA_ITEM_DATA
                and not extra_data.EXTRA_ITEM_DATA[item].get('nodrop', True)):
            config.IGNORED_AUCTIONS.append(drop)
            LOG.info("Added droppable item to IGNORED AUCTIONS: %s", drop)
        else:
            config.PENDING_AUCTIONS.append(drop)
            LOG.info("Added item to PENDING AUCTIONS: %s", drop)
            used_found_items.append(item)
    if not found_items:
        return list()
    if used_found_items:
        wx.PostEvent(window, models.DropEvent())
        utils.alert_message(
            "New Drops Detected",
            '\n'.join(["\u00A0\u2022 %s" % drop for drop in used_found_items]))
        utils.alert_sound(config.NEW_DROP_SOUND)
    if not skip_store:
        utils.store_state()
    return found_items
예제 #6
0
    def test_RandomAuction_model_bid_text(self):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        config.NUMBERS = ['12345']
        auc = models.RandomAuction(itemdrop)

        config.PRIMARY_BID_CHANNEL = 'auc'
        self.assertEqual("/AUC ~[Copper Disc] (DRU, SHD) ROLL 12345 NOW!",
                         auc.bid_text())

        item_name = 'Golden Jasper Earring'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        auc = models.RandomAuction(itemdrop)

        config.PRIMARY_BID_CHANNEL = 'gu'
        self.assertEqual("/GU ~[Golden Jasper Earring] ROLL 12345 NOW!",
                         auc.bid_text())
예제 #7
0
    def test_Auction_base_model(self):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        auc = models.Auction(itemdrop)
        self.assertRaises(NotImplementedError, auc.add, 1, 'Jim')
        self.assertRaises(NotImplementedError, auc.highest)

        # Should be JSON Encodable
        auc_json = json.dumps(auc, cls=utils.JSONEncoder)

        # Should be JSON Decodable
        loaded_auc = json.loads(auc_json, cls=utils.JSONDecoder)
        self.assertEqual(auc, loaded_auc)
예제 #8
0
    def test_RandomAuction_model_add(self):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        auc = models.RandomAuction(itemdrop)
        self.assertListEqual([], auc.highest())

        # First roll, valid
        result = auc.add(10, 'Peter')
        self.assertTrue(result)
        self.assertListEqual([('Peter', 10)], auc.highest())

        # Second roll, lower than first roll
        result = auc.add(8, 'Paul')
        self.assertTrue(result)
        self.assertListEqual([('Peter', 10)], auc.highest())

        # Third roll, higher than first roll
        result = auc.add(12, 'Mary')
        self.assertTrue(result)
        self.assertListEqual([('Mary', 12)], auc.highest())

        # Fifth roll, player rolls a second time
        result = auc.add(18, 'Paul')
        self.assertFalse(result)
        self.assertListEqual([('Mary', 12)], auc.highest())

        # Fifth roll, tied with highest roll
        result = auc.add(12, 'Dan')
        self.assertTrue(result)
        self.assertEqual(2, len(tuple(auc.highest())))
        self.assertIn(('Mary', 12), tuple(auc.highest()))
        self.assertIn(('Dan', 12), tuple(auc.highest()))

        # Invalid roll
        result = auc.add(None, 'Fred')
        self.assertFalse(result)

        # All rolls should be tracked
        self.assertDictEqual({
            'Dan': 12,
            'Mary': 12,
            'Paul': 8,
            'Peter': 10
        }, auc.rolls)

        # Should be JSON Encodable
        auc_json = json.dumps(auc, cls=utils.JSONEncoder)

        # Should be JSON Decodable
        loaded_auc = json.loads(auc_json, cls=utils.JSONDecoder)
        self.assertEqual(auc, loaded_auc)
예제 #9
0
    def test_start_auction_dkp(self):
        pending = utils.config.PENDING_AUCTIONS
        active = utils.config.ACTIVE_AUCTIONS
        pending.clear()
        active.clear()

        copper_disc = models.ItemDrop('Copper Disc', 'Jim', 'timestamp')
        pending.append(copper_disc)

        utils.start_auction_dkp(copper_disc, 'VCR')

        self.assertListEqual([], pending)
        self.assertIn(copper_disc.uuid, active)
        self.assertIsInstance(active[copper_disc.uuid], models.DKPAuction)
예제 #10
0
    def test_RandomAuction_model_win_text(self):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        config.NUMBERS = ['12345']
        auc = models.RandomAuction(itemdrop)

        config.PRIMARY_BID_CHANNEL = 'auc'

        auc.add(10, "Bill")
        auc.add(20, "Tom")
        auc.add(5, "James")

        self.assertEqual("/AUC ~Gratss Tom on [Copper Disc] with 20 / 12345!",
                         auc.win_text())
예제 #11
0
    def test_handle_bid_all_matchers(self, mock_post_event, mock_store_state):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        disc_auction = models.DKPAuction(itemdrop, 'VCR')
        config.ACTIVE_AUCTIONS = {itemdrop.uuid: disc_auction}

        # Check SAY
        line = ("[Sun Aug 16 22:47:31 2020] Jim says, " "'Copper Disc 10 DKP'")
        match = config.MATCH_BID_SAY.match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)

        # Check OOC
        line = ("[Sun Aug 16 22:47:31 2020] Jim says out of character, "
                "'Copper Disc 11 DKP'")
        match = config.MATCH_BID_OOC.match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)

        # Check AUC
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'Copper Disc 12 DKP'")
        match = config.MATCH_BID_AUC.match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)

        # Check SHOUT
        line = ("[Sun Aug 16 22:47:31 2020] Jim shouts, "
                "'Copper Disc 13 DKP'")
        match = config.MATCH_BID_SHOUT.match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)

        # Check GU
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'Copper Disc 14 DKP'")
        match = config.MATCH_BID_GU.match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)

        config.ACTIVE_AUCTIONS.clear()
예제 #12
0
    def test_DKPAuction_model_add(self):
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        auc = models.DKPAuction(itemdrop, 'VCR', min_dkp=3)
        self.assertListEqual([], auc.highest())

        # Bid too low
        result = auc.add(2, 'Peter')
        self.assertFalse(result)
        self.assertListEqual([], auc.highest())

        # First bid, valid
        result = auc.add(10, 'Peter')
        self.assertTrue(result)
        self.assertListEqual([('Peter', 10)], auc.highest())

        # Second bid, lower than first bid
        result = auc.add(8, 'Paul')
        self.assertFalse(result)
        self.assertListEqual([('Peter', 10)], auc.highest())

        # Third bid, higher than first bid
        result = auc.add(12, 'Mary')
        self.assertTrue(result)
        self.assertListEqual([('Mary', 12)], auc.highest())

        # Fourth bid, tied with highest bid
        result = auc.add(12, 'Dan')
        self.assertFalse(result)
        self.assertListEqual([('Mary', 12)], auc.highest())

        # Invalid bid
        result = auc.add(None, 'Fred')
        self.assertFalse(result)

        # Should be JSON Encodable
        auc_json = json.dumps(auc, cls=utils.JSONEncoder)

        # Should be JSON Decodable
        loaded_auc = json.loads(auc_json, cls=utils.JSONDecoder)
        self.assertEqual(auc, loaded_auc)
예제 #13
0
    def test_handle_bid(self, mock_post_event, mock_store_state):
        config.LAST_WHO_SNAPSHOT = {
            'Jim': models.Player('Jim', None, None, 'Venerate'),
            'Pim': models.Player('Pim', None, None, 'Castle'),
            'Tim': models.Player('Tim', None, None, 'Kingdom'),
            'Dan': models.Player('Dan', None, None, 'Dial a Daniel'),
        }
        item_name = 'Copper Disc'
        itemdrop = models.ItemDrop(item_name, "Jim", "timestamp")
        disc_auction = models.DKPAuction(itemdrop, 'VCR')
        config.ACTIVE_AUCTIONS = {itemdrop.uuid: disc_auction}

        # FILTER ON - Someone in the alliance bids on an inactive item
        config.RESTRICT_BIDS = True
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'Platinum Disc 10 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertListEqual([], disc_auction.highest())
        self.assertEqual(1, len(config.ACTIVE_AUCTIONS))
        mock_post_event.assert_not_called()

        # FILTER ON - Someone outside the alliance bids on an active item
        line = ("[Sun Aug 16 22:47:31 2020] Dan auctions, "
                "'Copper Disc 10 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertEqual([], disc_auction.highest())
        mock_post_event.assert_not_called()

        # FILTER OFF - Someone in the alliance bids on an inactive item
        config.RESTRICT_BIDS = False
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'Platinum Disc 10 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertListEqual([], disc_auction.highest())
        self.assertEqual(1, len(config.ACTIVE_AUCTIONS))
        mock_post_event.assert_not_called()

        # FILTER ON - Someone outside the alliance bids on an active item
        config.RESTRICT_BIDS = True
        line = ("[Sun Aug 16 22:47:31 2020] Dan auctions, "
                "'Copper Disc 10 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertEqual([], disc_auction.highest())
        mock_post_event.assert_not_called()

        # Someone in the alliance says random stuff with a number
        line = ("[Sun Aug 16 22:47:31 2020] Tim auctions, "
                "'I am 12 and what channel is this'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertListEqual([], disc_auction.highest())
        mock_post_event.assert_not_called()

        # Someone in the alliance bids on two items at once
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'Copper Disc 10 DKP Platinum Disc'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertListEqual([], disc_auction.highest())
        mock_post_event.assert_not_called()

        # Someone we haven't seen bids on an active item
        line = ("[Sun Aug 16 22:47:31 2020] Paul auctions, "
                "'Copper Disc 5 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)
        self.assertListEqual([('Paul', 5)], disc_auction.highest())
        mock_post_event.assert_called_once_with('window',
                                                models.BidEvent(disc_auction))
        mock_post_event.reset_mock()

        # Someone in the alliance bids on an active item
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'Copper Disc 10 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)
        self.assertIn(('Jim', 10), disc_auction.highest())
        mock_post_event.assert_called_once_with('window',
                                                models.BidEvent(disc_auction))
        mock_post_event.reset_mock()

        # Someone in the alliance bids on an active item with wrong case
        line = ("[Sun Aug 16 22:47:31 2020] Pim auctions, "
                "'copper DISC 11 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)
        self.assertIn(('Pim', 11), disc_auction.highest())
        mock_post_event.assert_called_once_with('window',
                                                models.BidEvent(disc_auction))
        mock_post_event.reset_mock()

        # Someone in the alliance bids on an active item for their 2nd main
        # This would trigger a bug with "2nd" being read as "2 DKP"
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'Copper Disc 2nd main 12dkp'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertTrue(result)
        self.assertIn(('Jim', 12), disc_auction.highest())
        mock_post_event.assert_called_once_with('window',
                                                models.BidEvent(disc_auction))
        mock_post_event.reset_mock()

        # Someone in the alliance avoids bidding using ~
        line = ("[Sun Aug 16 22:47:31 2020] Jim auctions, "
                "'~Copper Disc 14 DKP'")
        match = config.MATCH_BID[1].match(line)
        result = message_handlers.handle_bid(match, 'window')
        self.assertFalse(result)
        self.assertListEqual([('Jim', 12)], disc_auction.highest())
        mock_post_event.assert_not_called()

        config.ACTIVE_AUCTIONS.clear()
예제 #14
0
    def test_handle_drop(self, mock_post_event, mock_store_state):
        config.LAST_WHO_SNAPSHOT = {
            'Jim': models.Player('Jim', None, None, 'Force of Will'),
            'James': models.Player('James', None, None, 'Kingdom'),
            'Dan': models.Player('Dan', None, None, 'Dial a Daniel'),
        }
        config.PENDING_AUCTIONS = list()
        config.ACTIVE_AUCTIONS = {}
        # # FILTER OFF - Item linked by a non-federation guild member
        # config.RESTRICT_BIDS = False
        # line = ("[Sun Aug 16 22:47:31 2020] Dan says out of character, "
        #         "'Belt of Iniquity'")
        # match = config.MATCH_DROP.match(line)
        # items = message_handlers.handle_drop(match, 'window')
        # self.assertEqual(1, len(items))
        # self.assertEqual(1, len(config.PENDING_AUCTIONS))
        # mock_post_event.assert_called_once_with(
        #     'window', models.DropEvent())
        # mock_post_event.reset_mock()

        # config.PENDING_AUCTIONS = list()
        # # FILTER ON - Item linked by a non-federation guild member
        # config.RESTRICT_BIDS = True
        # line = ("[Sun Aug 16 22:47:31 2020] Dan says out of character, "
        #         "'Belt of Iniquity'")
        # match = config.MATCH_DROP.match(line)
        # items = message_handlers.handle_drop(match, 'window')
        # self.assertEqual(0, len(items))
        # self.assertEqual(0, len(config.PENDING_AUCTIONS))
        # mock_post_event.assert_not_called()

        # Item linked by a federation guild member

        # NODROP filter on, droppable item
        config.NODROP_ONLY = True
        line = ("[Sun Aug 16 22:47:31 2020] Jim says out of character, "
                "'Copper Disc'")
        jim_disc_1_uuid = "jim_disc_1_uuid"
        jim_disc_1 = models.ItemDrop('Copper Disc',
                                     'Jim',
                                     'Sun Aug 16 22:47:31 2020',
                                     uuid=jim_disc_1_uuid)
        match = config.MATCH_DROP_OOC.match(line)
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertEqual(1, len(items))
        self.assertIn('Copper Disc', items)
        self.assertEqual(0, len(config.PENDING_AUCTIONS))
        mock_post_event.assert_not_called()
        mock_post_event.reset_mock()
        self.mock_playsound.assert_not_called()
        self.mock_playsound.reset_mock()

        # NODROP filter on, NODROP item
        line = ("[Sun Aug 16 22:47:31 2020] Jim says, " "'Belt of Iniquity'")
        jim_belt_1_uuid = "jim_belt_1_uuid"
        jim_belt_1 = models.ItemDrop('Belt of Iniquity',
                                     'Jim',
                                     'Sun Aug 16 22:47:31 2020',
                                     uuid=jim_belt_1_uuid)
        match = config.MATCH_DROP_SAY.match(line)
        with mock.patch('uuid.uuid4') as mock_uuid4:
            mock_uuid4.return_value = jim_belt_1_uuid
            items = list(message_handlers.handle_drop(match, 'window'))
        self.assertEqual(1, len(items))
        self.assertIn('Belt of Iniquity', items)
        self.assertEqual(1, len(config.PENDING_AUCTIONS))
        self.assertListEqual([jim_belt_1], config.PENDING_AUCTIONS)
        mock_post_event.assert_called_once_with('window', models.DropEvent())
        mock_post_event.reset_mock()
        self.mock_playsound.assert_called_once()
        self.mock_playsound.reset_mock()

        # NODROP filter off, droppable item
        config.NODROP_ONLY = False
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'Copper Disc'")
        match = config.MATCH_DROP_GU.match(line)
        with mock.patch('uuid.uuid4') as mock_uuid4:
            mock_uuid4.return_value = jim_disc_1_uuid
            items = list(message_handlers.handle_drop(match, 'window'))
        self.assertEqual(1, len(items))
        self.assertIn('Copper Disc', items)
        self.assertEqual(2, len(config.PENDING_AUCTIONS))
        self.assertListEqual([jim_belt_1, jim_disc_1], config.PENDING_AUCTIONS)
        mock_post_event.assert_called_once_with('window', models.DropEvent())
        mock_post_event.reset_mock()
        self.mock_playsound.assert_called_once()
        self.mock_playsound.reset_mock()

        # Two items linked by a federation guild member, plus chat
        line = ("[Sun Aug 16 22:47:41 2020] James tells the guild, "
                "'Platinum Disc and Golden Amber Earring woo'")
        james_disc_uuid = "james_disc_uuid"
        james_earring_uuid = "james_earring_uuid"
        james_disc = models.ItemDrop('Platinum Disc',
                                     'James',
                                     'Sun Aug 16 22:47:41 2020',
                                     uuid=james_disc_uuid)
        james_earring = models.ItemDrop('Golden Amber Earring',
                                        'James',
                                        'Sun Aug 16 22:47:41 2020',
                                        uuid=james_earring_uuid)
        match = config.MATCH_DROP_GU.match(line)
        with mock.patch('uuid.uuid4') as mock_uuid4:
            mock_uuid4.side_effect = [james_disc_uuid, james_earring_uuid]
            items = list(message_handlers.handle_drop(match, 'window'))
        self.assertEqual(2, len(items))
        self.assertListEqual(['Platinum Disc', 'Golden Amber Earring'], items)
        self.assertListEqual(
            [jim_belt_1, jim_disc_1, james_disc, james_earring],
            config.PENDING_AUCTIONS)
        mock_post_event.assert_called_once_with('window', models.DropEvent())
        mock_post_event.reset_mock()
        self.mock_playsound.assert_called_once()
        self.mock_playsound.reset_mock()

        # Random chatter by federation guild member
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'four score and seven years ago, we wanted pixels'")
        match = config.MATCH_DROP_GU.match(line)
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertEqual(0, len(items))
        self.assertListEqual(
            [jim_belt_1, jim_disc_1, james_disc, james_earring],
            config.PENDING_AUCTIONS)
        mock_post_event.assert_not_called()
        self.mock_playsound.assert_not_called()

        # Someone reports they looted an item
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'looted Belt of Iniquity'")
        match = config.MATCH_DROP_GU.match(line)
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertEqual(0, len(items))
        self.assertListEqual(
            [jim_belt_1, jim_disc_1, james_disc, james_earring],
            config.PENDING_AUCTIONS)
        mock_post_event.assert_not_called()
        self.mock_playsound.assert_not_called()

        # Bid message doesn't register as a drop
        config.ACTIVE_AUCTIONS.clear()
        jerkin_1 = models.ItemDrop('Shiverback-hide Jerkin', 'Jim',
                                   'Sun Aug 16 22:47:31 2020')
        config.PENDING_AUCTIONS.append(jerkin_1)
        auction1 = utils.start_auction_dkp(jerkin_1, 'VCR')
        self.assertEqual(config.ACTIVE_AUCTIONS.get(auction1.item.uuid),
                         auction1)
        config.PENDING_AUCTIONS.clear()
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'Shiverback-hide Jerkin'")
        match = config.MATCH_DROP_GU.match(line)
        items = list(message_handlers.handle_drop(match, 'window'))
        # One item should be found
        self.assertListEqual(['Shiverback-hide Jerkin'], items)
        self.assertListEqual([], config.PENDING_AUCTIONS)
        mock_post_event.assert_not_called()
        self.mock_playsound.assert_not_called()

        # A gratss message from another app should not register as a drop
        bid_line = ("[Sun Aug 16 22:47:31 2020] Toald tells the guild, "
                    "'Shiverback-hide Jerkin 1 main'")
        config.RESTRICT_BIDS = False
        bid_match = config.MATCH_BID_GU.match(bid_line)
        message_handlers.handle_bid(bid_match, 'window')
        config.HISTORICAL_AUCTIONS[auction1.item.uuid] = (
            config.ACTIVE_AUCTIONS.pop(auction1.item.uuid))
        line = ("[Sun Aug 16 22:47:31 2020] Jim tells the guild, "
                "'~Gratss Toald on [Shiverback-hide Jerkin] (1 DKP)!'")
        match = config.MATCH_DROP_GU.match(line)
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertListEqual([], items)

        # Ignore items if a number is present, it's probably a bid
        match = config.MATCH_DROP_GU.match(bid_line)
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertListEqual([], items)

        # second same drop shouldn't record if it is within cooldown time
        jerkin_2 = models.ItemDrop(
            'Shiverback-hide Jerkin', 'Jim',
            utils.datetime_to_eq_format(datetime.datetime.now()))
        config.PENDING_AUCTIONS.append(jerkin_2)
        line = ("[{}] Jim tells the guild, 'Shiverback-hide Jerkin'".format(
            utils.datetime_to_eq_format(datetime.datetime.now())))
        match = config.MATCH_DROP_GU.match(line)
        self.assertEqual([jerkin_2], config.PENDING_AUCTIONS)
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertListEqual([jerkin_2.name], items)
        self.assertEqual([jerkin_2], config.PENDING_AUCTIONS)

        # second same drop should record if it is past cooldown time
        jerkin_2.timestamp = utils.datetime_to_eq_format(
            datetime.datetime.now() -
            datetime.timedelta(seconds=config.DROP_COOLDOWN))
        self.assertEqual(1, len(config.PENDING_AUCTIONS))
        items = list(message_handlers.handle_drop(match, 'window'))
        self.assertListEqual([jerkin_2.name], items)
        self.assertEqual(2, len(config.PENDING_AUCTIONS))