Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved item recycling #2482

Merged
merged 43 commits into from
Aug 11, 2016
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
815f42a
Now recycling only if less than 5 space left in inventory
BriceSD Aug 3, 2016
fe6f7b9
Removed recycling before moving to/spinning fort if bags are almost full
BriceSD Aug 3, 2016
dc5cdde
Removed recycling before moving to/spinning fort if bags are almost full
BriceSD Aug 3, 2016
62f916d
Removed unused import
BriceSD Aug 3, 2016
ef240a1
Now recycling only if less than 5 space left in inventory
BriceSD Aug 3, 2016
2d2c1a3
Removed recycling before moving to/spinning fort if bags are almost full
BriceSD Aug 3, 2016
eaa6082
Merge remote-tracking branch 'origin/dev' into dev
BriceSD Aug 4, 2016
c1718cf
Added documentation
BriceSD Aug 4, 2016
59df587
Merge pull request #1 from PokemonGoF/dev
BriceSD Aug 4, 2016
d7345d4
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 7, 2016
acfabca
Merge branch 'PokemonGoF-dev' into dev
BriceSD Aug 7, 2016
fafcafd
Updating fork
BriceSD Aug 7, 2016
2ad09c5
Deleted change on files not concerned
BriceSD Aug 7, 2016
653532a
Deleted change on files not concerned
BriceSD Aug 7, 2016
d398e45
The inner class is now "private"
BriceSD Aug 7, 2016
bcbbf09
new class to centralize inventory management
aeckert Jul 31, 2016
3600f70
use new inventory to display # candy after catch
aeckert Aug 3, 2016
dce922b
use new inventory class in evolve_pokemon
aeckert Aug 3, 2016
f5fef8b
Merge remote-tracking branch 'aeckert_inventory-refac/inventory-refac…
BriceSD Aug 8, 2016
385721f
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 9, 2016
7f35134
Merge branch 'PokemonGoF-dev' into dev
BriceSD Aug 9, 2016
bf0995f
Now using the new inventory (#2528)
BriceSD Aug 9, 2016
5c3e10a
Fixed #3256
BriceSD Aug 9, 2016
1e42340
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 10, 2016
f4dbe1c
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 10, 2016
f18997b
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 10, 2016
06178bf
Merge branch 'PokemonGoF-dev' into dev
BriceSD Aug 10, 2016
9992dbb
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 10, 2016
802e7c4
Merge branch 'PokemonGoF-dev' into dev
BriceSD Aug 10, 2016
9981b91
Fixed error if item_count result is false
BriceSD Aug 10, 2016
36a001e
Now keeps track of item inventory
BriceSD Aug 10, 2016
568fead
Updating fork
BriceSD Aug 10, 2016
19370f8
Moved inventory update in request_recycle method
BriceSD Aug 10, 2016
c6ea433
Minor comment change
BriceSD Aug 10, 2016
7214101
Fixed not running if had more item than inventory size (#3531)
BriceSD Aug 10, 2016
7cc0c3c
Added to the inventory class the necessary to keep trace of items
BriceSD Aug 11, 2016
055e223
Now using the new inventory class properly
BriceSD Aug 11, 2016
a13e8e9
Decoupled when to recycle an item from how to do it.
BriceSD Aug 11, 2016
9982ae3
Merge branch 'dev' of https://github.com/PokemonGoF/PokemonGo-Bot int…
BriceSD Aug 11, 2016
057c488
Updating fork. Merge branch 'PokemonGoF-dev' into dev
BriceSD Aug 11, 2016
36823f5
Updating fork
BriceSD Aug 11, 2016
930300e
Moved the recycler in the services folder
BriceSD Aug 11, 2016
d7d32de
Merge remote-tracking branch 'origin/dev' into dev
BriceSD Aug 11, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 155 additions & 64 deletions pokemongo_bot/cell_workers/recycle_items.py
Original file line number Diff line number Diff line change
@@ -1,85 +1,176 @@
import json
import os
from pokemongo_bot import inventory
from pokemongo_bot.base_dir import _base_dir
from pokemongo_bot.base_task import BaseTask
from pokemongo_bot.worker_result import WorkerResult
from pokemongo_bot.tree_config_builder import ConfigException

DEFAULT_MIN_EMPTY_SPACE = 6

class RecycleItems(BaseTask):
SUPPORTED_TASK_API_VERSION = 1

"""
Recycle undesired items if there is less than five space in inventory.
You can use either item's name or id. For the full list of items see ../../data/items.json

It's highly recommended to put this task before move_to_fort and spin_fort task in the config file so you'll most likely be able to loot.

Example config :
{
"type": "RecycleItems",
"config": {
"min_empty_space": 6, # 6 by default
"item_filter": {
"Pokeball": {"keep": 20},
"Greatball": {"keep": 50},
"Ultraball": {"keep": 100},
"Potion": {"keep": 0},
"Super Potion": {"keep": 0},
"Hyper Potion": {"keep": 20},
"Max Potion": {"keep": 50},
"Revive": {"keep": 0},
"Max Revive": {"keep": 20},
"Razz Berry": {"keep": 20}
}
}
}
"""

def initialize(self):
self.items_filter = self.config.get('item_filter', {})
self.min_empty_space = self.config.get('min_empty_space', None)
self.item_filter = self.config.get('item_filter', {})
self._validate_item_filter()

def _validate_item_filter(self):
"""
Validate user's item filter config
:return: Nothing.
:rtype: None
:raise: ConfigException: When an item doesn't exist in ../../data/items.json
"""
item_list = json.load(open(os.path.join(_base_dir, 'data', 'items.json')))
for config_item_name, bag_count in self.item_filter.iteritems():
for config_item_name, bag_count in self.items_filter.iteritems():
if config_item_name not in item_list.viewvalues():
if config_item_name not in item_list:
raise ConfigException(
"item {} does not exist, spelling mistake? (check for valid item names in data/items.json)".format(
config_item_name))
raise ConfigException("item {} does not exist, spelling mistake? (check for valid item names in data/items.json)".format(config_item_name))

def should_run(self):
"""
Returns a value indicating whether the recycling process should be run.
:return: True if the recycling process should be run; otherwise, False.
:rtype: bool
"""
if inventory.items().get_space_left() >= (DEFAULT_MIN_EMPTY_SPACE if self.min_empty_space is None else self.min_empty_space):
return False
return True

def work(self):
items_in_bag = self.bot.get_inventory_count('item')
total_bag_space = self.bot.player_data['max_item_storage']
free_bag_space = total_bag_space - items_in_bag

if self.min_empty_space is not None:
if free_bag_space >= self.min_empty_space:
self.emit_event(
'item_discard_skipped',
formatted="Skipping Recycling of Items. {space} space left in bag.",
data={
'space': free_bag_space
}
)
return

self.bot.latest_inventory = None
item_count_dict = self.bot.item_inventory_count('all')

for item_id, bag_count in item_count_dict.iteritems():
item_name = self.bot.item_list[str(item_id)]
id_filter = self.item_filter.get(item_name, 0)
if id_filter is not 0:
id_filter_keep = id_filter.get('keep', 20)
"""
Discard items if necessary.
:return: Always returns WorkerResult.SUCCESS.
:rtype: WorkerResult
"""
# Updating inventory
inventory.init_inventory(self.bot)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inventory is already initialized when the API is set up, you don't need this here. This will ready all JSON cache files again and can slow down the tick a lot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once every task track inventory changes I’ll hapily remove the api call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If everyone thinks like that, we will never reach full integration. You can use the cached inventory here, we update other tasks as necessary.

if self.should_run():
# For each user's item in inventory recycle it if needed
for item_in_inventory in inventory.items().all():
item = RecycleItems._Item(item_in_inventory['item_id'], self.items_filter, self)

if item.should_be_recycled():
item.request_recycle()
item.emit_recycle_result()
# TODO : Inventory should keep track of items beeing discarded rather than making an other api call
inventory.refresh_inventory()
return WorkerResult.SUCCESS

class _Item:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excuse my python noobishness, but what is the difference between class _Item: and class _Item(object):?

Copy link
Member

@douglascamata douglascamata Aug 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you inherit from object you have new-style class.

New style objects have a different object model to classic objects, and some things won't work properly with old style objects (those that doesn't inherit from object), for instance, super(), @property and descriptors.

In Python 3 it's not necessary to inherit from object anymore.

"""
An item found in user's inventory.

This class contains details of recycling process.
"""
def __init__(self, item_id, items_filter, recycle_items):
"""
Initializes an item
:param item_id: Item's id.
:param items_filter: List of items and their maximum amount to keep.
:param recycle_items: The recycle_items instance.
"""
self.recycle_items = recycle_items
self.bot = recycle_items.bot
self.id = item_id
self.name = recycle_items.bot.item_list[str(item_id)]
self.items_filter = items_filter
self.amount_to_keep = self._get_amount_to_keep()
self.amount_in_inventory = inventory.items().count_for(self.id)
self.amount_to_recycle = 0 if self.amount_to_keep is None else self.amount_in_inventory - self.amount_to_keep
self.recycle_item_request_result = None

def _get_amount_to_keep(self):
"""
Determine item's amount to keep in inventory.
:return: Item's amount to keep in inventory.
:rtype: int
"""
item_filter_config = self.items_filter.get(self.name, 0)
if item_filter_config is not 0:
return item_filter_config.get('keep', 20)
else:
item_filter_config = self.items_filter.get(str(self.id), 0)
if item_filter_config is not 0:
return item_filter_config.get('keep', 20)

def should_be_recycled(self):
"""
Returns a value indicating whether the item should be recycled.
:return: True if the title should be recycled; otherwise, False.
:rtype: bool
"""
return (self.name in self.items_filter or str(self.id) in self.items_filter) and self.amount_to_recycle > 0

def request_recycle(self):
"""
Request recycling of the item and store api call response's result.
:return: Nothing.
:rtype: None
"""
response = self.bot.api.recycle_inventory_item(item_id=self.id, count=self.amount_to_recycle)
# Example of good request response
# {'responses': {'RECYCLE_INVENTORY_ITEM': {'result': 1, 'new_count': 46}}, 'status_code': 1, 'auth_ticket': {'expire_timestamp_ms': 1469306228058L, 'start': '/HycFyfrT4t2yB2Ij+yoi+on778aymMgxY6RQgvrGAfQlNzRuIjpcnDd5dAxmfoTqDQrbz1m2dGqAIhJ+eFapg==', 'end': 'f5NOZ95a843tgzprJo4W7Q=='}, 'request_id': 8145806132888207460L}
self.recycle_item_request_result = response.get('responses', {}).get('RECYCLE_INVENTORY_ITEM', {}).get('result', 0)

def _is_recycling_success(self):
"""
Returns a value indicating whether the item has been successfully recycled.
:return: True if the item has been successfully recycled; otherwise, False.
:rtype: bool
"""
return self.recycle_item_request_result == 1

def emit_recycle_result(self):
"""
Emits recycle result in logs
:return: Nothing.
:rtype: None
"""
if self._is_recycling_success():
self.recycle_items.emit_event(
'item_discarded',
formatted='Discarded {amount}x {item} (maximum {maximum}).',
data={
'amount': str(self.amount_to_recycle),
'item': self.name,
'maximum': str(self.amount_to_keep)
}
)
else:
id_filter = self.item_filter.get(str(item_id), 0)
if id_filter is not 0:
id_filter_keep = id_filter.get('keep', 20)

bag_count = self.bot.item_inventory_count(item_id)
if (item_name in self.item_filter or str(item_id) in self.item_filter) and bag_count > id_filter_keep:
items_recycle_count = bag_count - id_filter_keep
response_dict_recycle = self.send_recycle_item_request(item_id=item_id, count=items_recycle_count)
result = response_dict_recycle.get('responses', {}).get('RECYCLE_INVENTORY_ITEM', {}).get('result', 0)

if result == 1: # Request success
self.emit_event(
'item_discarded',
formatted='Discarded {amount}x {item} (maximum {maximum}).',
data={
'amount': str(items_recycle_count),
'item': item_name,
'maximum': str(id_filter_keep)
}
)
else:
self.emit_event(
'item_discard_fail',
formatted="Failed to discard {item}",
data={
'item': item_name
}
)

def send_recycle_item_request(self, item_id, count):
# Example of good request response
# {'responses': {'RECYCLE_INVENTORY_ITEM': {'result': 1, 'new_count': 46}}, 'status_code': 1, 'auth_ticket': {'expire_timestamp_ms': 1469306228058L, 'start': '/HycFyfrT4t2yB2Ij+yoi+on778aymMgxY6RQgvrGAfQlNzRuIjpcnDd5dAxmfoTqDQrbz1m2dGqAIhJ+eFapg==', 'end': 'f5NOZ95a843tgzprJo4W7Q=='}, 'request_id': 8145806132888207460L}
return self.bot.api.recycle_inventory_item(
item_id=item_id,
count=count
)
self.recycle_items.emit_event(
'item_discard_fail',
formatted="Failed to discard {item}",
data={
'item': self.name
}
)
35 changes: 34 additions & 1 deletion pokemongo_bot/inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,27 @@ class Items(_BaseInventoryComponent):
STATIC_DATA_FILE = os.path.join(_base_dir, 'data', 'items.json')

def count_for(self, item_id):
return self._data[item_id]['count']
return self._data[item_id].get('count', False)

def get_space_used(self):
"""
Counts the space used in item inventory.
:return: The space used in item inventory.
:rtype: int
"""
itemcount = 1
for item in self._data:
itemcount += self.count_for(item)
Copy link
Member

@douglascamata douglascamata Aug 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if self._data[item_id].get('count', False) fails and return False, then this line raises an error. It should only add if self.count_for(item) is an instance of int.

return itemcount

def get_space_left(self):
"""
Compute the space left in item inventory.
:return: The space left in item inventory.
:rtype: int
"""
_inventory.retrieve_item_inventory_size()
return _inventory.item_inventory_size - self.get_space_used()


class Pokemons(_BaseInventoryComponent):
Expand Down Expand Up @@ -214,6 +234,7 @@ def __init__(self, bot):
self.items = Items()
self.pokemons = Pokemons()
self.refresh()
self.item_inventory_size = None

def refresh(self):
# TODO: it would be better if this class was used for all
Expand All @@ -227,6 +248,15 @@ def refresh(self):
with open(user_web_inventory, 'w') as outfile:
json.dump(inventory, outfile)

def retrieve_item_inventory_size(self):
"""
Retrieves the item inventory size
:return: Nothing.
:rtype: None
"""
# TODO: Force update of _item_inventory_size if the player upgrades its size
if self.item_inventory_size is None:
self.item_inventory_size = self.bot.api.get_player()['responses']['GET_PLAYER']['player_data']['max_item_storage']

_inventory = None

Expand All @@ -238,6 +268,9 @@ def init_inventory(bot):
def refresh_inventory():
_inventory.refresh()

def get_item_inventory_size():
_inventory.retrieve_item_inventory_size()
return _inventory.item_inventory_size

def pokedex():
return _inventory.pokedex
Expand Down