Skip to content

Commit

Permalink
Merge pull request #5878 from PokemonGoF/dev
Browse files Browse the repository at this point in the history
Feature Enhancement: Snip request through telegram (#5877)
  • Loading branch information
solderzzc authored Jan 17, 2017
2 parents dbb59cf + e89ed28 commit 4548479
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 24 deletions.
1 change: 1 addition & 0 deletions docs/configuration_files.md
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@ This task is an upgrade version of the MoveToMapPokemon task. It will fetch poke
* `mode` - The mode on which the sniper will fetch the informations. (default: social)
- `social` - Information will come from the social network.
- `url` - Information will come from one or multiple urls.
- `telegram` - Manual snipping through telegram. In telegram, use "/snipe <PokemonName> <Lat> <Lng>" to snipe. Subscript to "/sub sniper_log" and "/sub pokemon_vip_caught" to retrieve snipping results through telegram.
* `bullets` - Each bullet corresponds to an **ATTEMPT** of catching a pokemon. (default: 1)
* `homing_shots` - This will ensure that each bullet **will catch** a target. If disabled, a target might not exist and thus it wont be caught. When enabled, this will jump to the next target (if any) and try again to catch it. This will be repeated untill you've spent all the bullets. (default: true)
* `special_iv` - This will skip the catch list if the value is greater than or equal to the target's IV. This currently does not work with `social` mode and only works if the given `url` has this information. (default: 100)
Expand Down
108 changes: 84 additions & 24 deletions pokemongo_bot/cell_workers/sniper.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
from random import uniform
from operator import itemgetter, methodcaller
from datetime import datetime
from itertools import izip
from pokemongo_bot import inventory
from pokemongo_bot.item_list import Item
from pokemongo_bot.base_task import BaseTask
from pokemongo_bot.inventory import Pokemons
from pokemongo_bot.worker_result import WorkerResult
from pokemongo_bot.event_handlers.telegram_handler import TelegramSnipe
from pokemongo_bot.cell_workers.pokemon_catch_worker import PokemonCatchWorker

# Represents a URL source and its mappings
Expand Down Expand Up @@ -219,13 +221,14 @@ class SniperOrderMode(object):
class SniperMode(object):
URL = 'url'
SOCIAL = 'social'
TELEGRAM = 'telegram'
DEFAULT = SOCIAL

# Teleports the player to a target gotten from either social or a single/multiple URL sources
class Sniper(BaseTask):
SUPPORTED_TASK_API_VERSION = 1
MIN_SECONDS_ALLOWED_FOR_CELL_CHECK = 10
MIN_SECONDS_ALLOWED_FOR_REQUESTING_DATA = 5
MIN_SECONDS_ALLOWED_FOR_CELL_CHECK = 60
MIN_SECONDS_ALLOWED_FOR_REQUESTING_DATA = 10
MIN_BALLS_FOR_CATCHING = 10
MAX_CACHE_LIST_SIZE = 300

Expand All @@ -239,7 +242,7 @@ def initialize(self):
self.inventory = inventory.items()
self.pokedex = inventory.pokedex()
self.debug = self.config.get('debug', False)
self.special_iv = self.config.get('special_iv', 100)
self.special_iv = self.config.get('special_iv', 0)
self.bullets = self.config.get('bullets', 1)
self.homing_shots = self.config.get('homing_shots', True)
self.mode = self.config.get('mode', SniperMode.DEFAULT)
Expand Down Expand Up @@ -277,28 +280,33 @@ def initialize(self):
self.sources.remove(source)

# Notify user if all sources are invalid and cant proceed
if not self.sources:
if not self.sources :
self._error("There is no source available. Disabling Sniper...")
self.disabled = True

# Re-enable snipping if source is from telegram
if self.mode == SniperMode.TELEGRAM:
self.disabled = False

def is_snipeable(self, pokemon):
pokeballs_count = self.inventory.get(Item.ITEM_POKE_BALL.value).count
greatballs_count = self.inventory.get(Item.ITEM_GREAT_BALL.value).count
ultraballs_count = self.inventory.get(Item.ITEM_ULTRA_BALL.value).count
all_balls_count = pokeballs_count + greatballs_count + ultraballs_count

# Skip if expired (cast milliseconds to seconds for comparision)
if (pokemon.get('expiration_timestamp_ms', 0) or pokemon.get('last_modified_timestamp_ms', 0)) / 1000 < time.time():
self._trace('{} is expired! Skipping...'.format(pokemon.get('pokemon_name')))
return False
# Skip if expired (cast milliseconds to seconds for comparision), snipe check if source is from telegram
if self.mode != SniperMode.TELEGRAM:
if (pokemon.get('expiration_timestamp_ms', 0) or pokemon.get('last_modified_timestamp_ms', 0)) / 1000 < time.time():
self._trace('{} is expired! Skipping...'.format(pokemon.get('pokemon_name')))
return False

# Skip if not enought balls. Sniping wastes a lot of balls. Theres no point to let user decide this amount
if all_balls_count < self.MIN_BALLS_FOR_CATCHING:
self._trace('Not enought balls left! Skipping...')
return False

# Skip if not in catch list, not a VIP and/or IV sucks (if any)
if pokemon.get('pokemon_name', '') in self.catch_list:
# Skip if not in catch list, not a VIP and/or IV sucks (if any), ignore telegram mode
if pokemon.get('pokemon_name', '') in self.catch_list or self.mode == SniperMode.TELEGRAM:
self._trace('{} is catchable!'.format(pokemon.get('pokemon_name')))
else:
# Not catchable. Having a good IV should suppress the not in catch/vip list (most important)
Expand All @@ -322,7 +330,7 @@ def snipe(self, pokemon):
success = False

# Apply snipping business rules and snipe if its good
if not self.is_snipeable(pokemon):
if not self.is_snipeable(pokemon) and not self.mode == SniperMode.TELEGRAM:
self._trace('{} is not snipeable! Skipping...'.format(pokemon['pokemon_name']))
else:
# Have we already tried this pokemon?
Expand All @@ -341,32 +349,61 @@ def snipe(self, pokemon):
# Teleport, so that we can see nearby stuff
self.bot.hb_locked = True
self._teleport_to(pokemon)


# If social is enabled and if no verification is needed, trust it. Otherwise, update IDs!
verify = not pokemon.get('encounter_id') or not pokemon.get('spawn_point_id')
exists = not verify or self.mode == SniperMode.SOCIAL
success = exists

# Always verify if it's from telegram
if TelegramSnipe.ENABLED == True:
verify = True

# If information verification have to be done, do so
if verify:
seconds_since_last_check = time.time() - self.last_cell_check_time

# Wait a maximum of MIN_SECONDS_ALLOWED_FOR_CELL_CHECK seconds before requesting nearby cells
if (seconds_since_last_check < self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK):
time.sleep(self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK - seconds_since_last_check)

self._trace('Pausing for {} secs before checking for Pokemons'.format(self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK))

#recode it to check every 5 secs, first check for wild then catchable
nearby_pokemons = []
nearby_stuff = self.bot.get_meta_cell()
self.last_cell_check_time = time.time()

# Retrieve nearby pokemons for validation
nearby_pokemons.extend(nearby_stuff.get('wild_pokemons', []))
nearby_pokemons.extend(nearby_stuff.get('catchable_pokemons', []))

nearby_stuff = []
num = 0
for num in range(0,self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK):
if num%5 == 0:
nearby_stuff = self.bot.get_meta_cell()
self.last_cell_check_time = time.time()

# Retrieve nearby pokemons for validation
nearby_pokemons.extend(nearby_stuff.get('wild_pokemons', []))
if nearby_pokemons:
break

time.sleep(1)
num += 1

num = 0
for num in range(0,self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK):
if num%5 == 0:
nearby_stuff = self.bot.get_meta_cell()
self.last_cell_check_time = time.time()

# Retrieve nearby pokemons for validation
nearby_pokemons.extend(nearby_stuff.get('catchable_pokemons', []))
if nearby_pokemons:
break

time.sleep(1)
num += 1

self._trace('Pokemon Nearby: {}'.format(nearby_pokemons))

# Make sure the target really/still exists (nearby_pokemon key names are game-bound!)
for nearby_pokemon in nearby_pokemons:
nearby_pokemon_id = nearby_pokemon.get('pokemon_data', {}).get('pokemon_id') or nearby_pokemon.get('pokemon_id')

# If we found the target, it exists and will very likely be encountered/caught (success)
if nearby_pokemon_id == pokemon.get('pokemon_id', 0):
exists = True
Expand All @@ -382,9 +419,13 @@ def snipe(self, pokemon):
if exists:
self._log('Yay! There really is a wild {} nearby!'.format(pokemon.get('pokemon_name')))
self._teleport_back_and_catch(last_position, pokemon)

else:
self._error('Damn! Its not here. Reasons: too far, caught, expired or fake data. Skipping...')
self._teleport_back(last_position)

#Set always to false to re-enable sniper to check for telegram data
TelegramSnipe.ENABLED = False

# Save target and unlock heartbeat calls
self._cache(uniqueid)
Expand All @@ -393,6 +434,8 @@ def snipe(self, pokemon):
return success

def work(self):
#Check if telegram is called

# Do nothing if this task was invalidated
if self.disabled:
self._error("Sniper was disabled for some reason. Scroll up to find out.")
Expand All @@ -412,6 +455,8 @@ def work(self):
targets = self._get_pokemons_from_social()
elif self.mode == SniperMode.URL:
targets = self._get_pokemons_from_url()
elif self.mode == SniperMode.TELEGRAM and TelegramSnipe.ENABLED:
targets = self._get_pokemons_from_telegram()

if targets:
# Order the targets (descending)
Expand Down Expand Up @@ -442,6 +487,9 @@ def work(self):
if shots < self.bullets and index < len(targets):
self._trace('Waiting a few seconds to teleport again to another target...')
time.sleep(3)

# Always set telegram back to false
TelegramSnipe.ENABLED = False

return WorkerResult.SUCCESS

Expand All @@ -450,7 +498,7 @@ def _parse_pokemons(self, pokemon_dictionary_list):

# Build up the pokemon. Pops are used to destroy random attribute names and keep the known ones!
for pokemon in pokemon_dictionary_list:
pokemon['iv'] = pokemon.get('iv', 0)
pokemon['iv'] = pokemon.get('iv', 100)
pokemon['pokemon_name'] = pokemon.get('pokemon_name', Pokemons.name_for(pokemon.get('pokemon_id')))
pokemon['vip'] = pokemon.get('pokemon_name') in self.bot.config.vips
pokemon['missing'] = not self.pokedex.captured(pokemon.get('pokemon_id'))
Expand All @@ -459,8 +507,20 @@ def _parse_pokemons(self, pokemon_dictionary_list):
# Check whether this is a valid target
if self.is_snipeable(pokemon):
result.append(pokemon)

return result

def _get_pokemons_from_telegram(self):
if not TelegramSnipe.ENABLED:
return {}

pokemons = []
pokemon = {'iv': int(0), 'pokemon_id': int(TelegramSnipe.ID), 'pokemon_name': str(TelegramSnipe.POKEMON_NAME), 'latitude': float(TelegramSnipe.LATITUDE), 'longitude': float(TelegramSnipe.LONGITUDE)}
self._log('Telegram snipe request: {}'.format(pokemon.get('pokemon_name')))

pokemons = [pokemon]

return self._parse_pokemons(pokemons)

def _get_pokemons_from_social(self):
if not hasattr(self.bot, 'mqtt_pokemon_list') or not self.bot.mqtt_pokemon_list:
Expand Down
34 changes: 34 additions & 0 deletions pokemongo_bot/event_handlers/telegram_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@
import re
from telegram.utils import request
from chat_handler import ChatHandler
from pokemongo_bot.inventory import Pokemons

DEBUG_ON = False

class TelegramSnipe(object):
ENABLED = False
ID = int(0)
POKEMON_NAME = ''
LATITUDE = float(0)
LONGITUDE = float(0)

class TelegramClass:
update_id = None
Expand Down Expand Up @@ -187,7 +194,25 @@ def send_caught(self, update, num, order):
else:
self.sendMessage(chat_id=update.message.chat_id, parse_mode='Markdown',
text="No Pokemon Caught Yet.\n")

def request_snipe(self, update, pkm, lat, lng):
snipeSuccess = False
try:
id = Pokemons.id_for(pkm)
except:
self.sendMessage(chat_id=update.message.chat_id, parse_mode='Markdown', text="Invaild Pokemon")
return

#Set Telegram Snipe to true and let sniper do its work
TelegramSnipe.ENABLED = True
TelegramSnipe.ID = int(id)
TelegramSnipe.POKEMON_NAME = str(pkm)
TelegramSnipe.LATITUDE = float(lat)
TelegramSnipe.LONGITUDE = float(lng)

outMsg = 'Catching pokemon: ' + TelegramSnipe.POKEMON_NAME + ' at Latitude: ' + str(TelegramSnipe.LATITUDE) + ' Longitude: ' + str(TelegramSnipe.LONGITUDE) + '\n'
self.sendMessage(chat_id=update.message.chat_id, parse_mode='Markdown', text="".join(outMsg))

def send_evolved(self, update, num, order):
evolved = self.chat_handler.get_evolved(num, order)
outMsg = ''
Expand Down Expand Up @@ -303,6 +328,7 @@ def send_start(self, update):
"/pokestops - show last x pokestops visited",
"/released <num> <cp-or-iv-or-dated> - show top x released, sorted by CP, IV, or Date",
"/vanished <num> <cp-or-iv-or-dated> - show top x vanished, sorted by CP, IV, or Date",
"/snipe <PokemonName> <Lat> <Lng> - to snipe a pokemon at location Latitude, Longitude",
"/softbans - info about possible softbans"
)
self.sendMessage(chat_id=update.message.chat_id, parse_mode='Markdown',
Expand Down Expand Up @@ -432,6 +458,14 @@ def run(self):
(cmd, num, order) = self.tokenize(update.message.text, 3)
self.send_vanished(update, num, order)
continue
if re.match(r'^/snipe ', update.message.text):
try:
(cmd, pkm, lat, lng) = self.tokenize(update.message.text, 4)
self.request_snipe(update, pkm, lat, lng)
except:
self.sendMessage(chat_id=update.message.chat_id, parse_mode='Markdown',
text="An Error has occured")
continue
if re.match(r'^/softbans ', update.message.text):
(cmd, num) = self.tokenize(update.message.text, 2)
self.send_softbans(update, num)
Expand Down

0 comments on commit 4548479

Please sign in to comment.