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

[webhook] webhook for custom filter #116

Merged
merged 7 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
39 changes: 39 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ Each webhook type also supports pinging @users or @roles.

![Discord_G2hvTZG21a](https://github.com/40Cakes/pokebot-gen3/assets/16377135/3f04d1cf-4040-4163-80d2-13cac84eed1f)

`custom_filter_pokemon_encounter` - [Custom catch filter](#customcatchfilterspy---custom-catch-filters) encounters

</details>

## `catch_block.yml` - Catch block config
Expand Down Expand Up @@ -533,6 +535,43 @@ All HTTP responses are in JSON format.

</details>

## `customcatchfilters.py` - Custom catch filters

<details>
<summary>Click to expand</summary>

All Pokémon encounters are checked by custom catch filters, use this file if you are after Pokémon that match very specific criteria, some examples are provided (most are disabled by default).

These filters are checked *after* the catch block list, so if Wurmple is on your [catch block list](#catchblockyml---catch-block-config), the Wurmple evolution example below will still be checked.

If you are not familiar with Python, it is highly recommended to use an IDE such as [PyCharm](https://www.jetbrains.com/pycharm/) to edit this file as any syntax errors will be highlighted, and the `pokemon` object will auto-complete and show available parameters for you to filter on.

- `return "any message"` (string) - will command the bot to catch the current encounter, the string returned will be added to the Discord webhook if `custom_filter_pokemon_encounter` is enabled in [discord.yml](#discordyml---discord-integration-config)
- `pass` - will skip the check, and continue to check other criteria further down the file
- `save_pk3(pokemon)` instead of `return "any message"` will [dump a .pk3 file](#save-raw-pokémon-data-pk3) and continue without pausing the bot until auto-catch is ready

The following example will catch any shiny Wurmple that will evolve into Silcoon/Beautifly, and ignore any that would evolve into Cascoon/Dustox:

```py
# Shiny Wurmple evolving based on evolution
if pokemon.is_shiny and pokemon.species.name == "Wurmple":
if pokemon.wurmple_evolution == "silcoon":
return "Shiny Wurmple evolving into Silcoon/Beautifly"
if pokemon.wurmple_evolution == "cascoon":
pass
```

The following example will catch any Pokémon with all perfect IVs:
```py
# Pokémon with perfect IVs
if pokemon.ivs.sum() == (6 * 31):
return "Pokémon with perfect IVs"
```

- **Note**: you must restart the bot after editing this file for changes to take effect!

</details>

***

# ⏩ Tips/Tricks
Expand Down
10 changes: 10 additions & 0 deletions modules/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@
- ~
- user
- role
custom_filter_pokemon_encounter:
type: object
properties:
enable:
type: boolean
ping_mode:
enum:
- ~
- user
- role
"""

obs_schema = """
Expand Down
10 changes: 6 additions & 4 deletions modules/encounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ def encounter_pokemon(pokemon: Pokemon) -> None:
config_catch_block = load_config("catch_block.yml", catch_block_schema)
block_list = config_catch_block["block_list"]

total_stats.log_encounter(pokemon, block_list)
custom_filter_result = total_stats.custom_catch_filters(pokemon)
custom_found = isinstance(custom_filter_result, str)

total_stats.log_encounter(pokemon, block_list, custom_filter_result)
context.message = f"Encountered a {pokemon.species.name} with a shiny value of {pokemon.shiny_value:,}!"

# TODO temporary until auto-catch is ready
custom_found = total_stats.custom_catch_filters(pokemon)
if pokemon.is_shiny or custom_found:
if pokemon.is_shiny:
if not config["logging"]["save_pk3"]["all"] and config["logging"]["save_pk3"]["shiny"]:
Expand All @@ -51,10 +53,10 @@ def encounter_pokemon(pokemon: Pokemon) -> None:
save_pk3(pokemon)
state_tag = "customfilter"
console.print("[bold green]Custom filter Pokemon found!")
context.message = "Custom filter triggered! Bot has been switched to manual mode so you can catch it."
context.message = f"Custom filter triggered ({custom_filter_result})! Bot has been switched to manual mode so you can catch it."

alert_title = "Custom filter triggered!"
alert_message = f"Found a {pokemon.species.name} that matched one of your filters."
alert_message = f"Found a {pokemon.species.name} that matched one of your filters. ({custom_filter_result})"
else:
state_tag = ""
alert_title = None
Expand Down
13 changes: 9 additions & 4 deletions modules/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from datetime import datetime

from modules.config import config
from modules.console import console, print_stats
from modules.console import print_stats
from modules.context import context
from modules.csv import log_encounter_to_csv
from modules.files import read_file, write_file
Expand Down Expand Up @@ -302,13 +302,13 @@ def get_log_obj(self, pokemon: Pokemon) -> dict:
},
}

def log_encounter(self, pokemon: Pokemon, block_list: list) -> None:
def log_encounter(self, pokemon: Pokemon, block_list: list, custom_filter_result: str | bool) -> None:
if "pokemon" not in self.total_stats:
self.total_stats["pokemon"] = {}
if "totals" not in self.total_stats:
self.total_stats["totals"] = {}

if not pokemon.species.name in self.total_stats["pokemon"]: # Set up a Pokémon stats if first encounter
if pokemon.species.name not in self.total_stats["pokemon"]: # Set up a Pokémon stats if first encounter
self.total_stats["pokemon"].update({pokemon.species.name: {}})

self.update_incremental_stats(pokemon)
Expand Down Expand Up @@ -344,7 +344,12 @@ def log_encounter(self, pokemon: Pokemon, block_list: list) -> None:
print_stats(self.total_stats, pokemon, self.session_pokemon, self.get_encounter_rate())

# Run custom code in custom_hooks in a thread
hook = (Pokemon(pokemon.data), copy.deepcopy(self.total_stats), copy.deepcopy(block_list))
hook = (
Pokemon(pokemon.data),
copy.deepcopy(self.total_stats),
copy.deepcopy(block_list),
copy.deepcopy(custom_filter_result)
)
Thread(target=self.custom_hooks, args=(hook,)).start()

if pokemon.is_shiny:
Expand Down
66 changes: 31 additions & 35 deletions profiles/customcatchfilters.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
# TODO add list of available fields to filter on
# TODO add option for a Discord webhook when a custom is caught
from modules.console import console
from modules.files import save_pk3
from modules.pokemon import Pokemon


def custom_catch_filters(pokemon: Pokemon) -> bool:
def custom_catch_filters(pokemon: Pokemon) -> str | bool:
"""
Check the current encounter, catch if it matches any of the following criteria.
Some examples are provided (most are disabled by default).
These filters are checked *after* catch block list, so if Wurmple is on your catch block list, the Wurmple evolution
examples below will still be checked.

- `return True` will command the bot to catch the current encounter
- `pass` - will skip the check, and continue to check other criteria further down this file
- `save_pk3(pokemon)` instead of `return True` will dump a .pk3 file and continue without pausing the bot until
auto-catch is ready

Note: you must restart the bot after editing this file for changes to take effect!
See readme for documentation: https://github.com/40Cakes/pokebot-gen3#customcatchfilterspy---custom-catch-filters

:param pokemon: Pokémon object of the current encounter
"""
Expand Down Expand Up @@ -77,42 +65,50 @@ def custom_catch_filters(pokemon: Pokemon) -> bool:
]

if pokemon.species.name not in exceptions:
# Catch shiny Wurmple based on evolution
# Shiny Wurmple evolving based on evolution
if pokemon.is_shiny and pokemon.species.name == "Wurmple":
evolution = "Silcoon/Beautifly" if pokemon.wurmple_evolution == "silcoon" else "Cascoon/Dustox"
if evolution == "Silcoon/Beautifly":
pass # ❌ disabled
if evolution == "Cascoon/Dustox":
pass # ❌ disabled

# Catch perfect IV Pokémon
if pokemon.wurmple_evolution == "silcoon":
# return "Shiny Wurmple evolving into Silcoon/Beautifly"
pass
if pokemon.wurmple_evolution == "cascoon":
# return "Shiny Wurmple evolving into Cascoon/Dustox"
pass

# Pokémon with perfect IVs
if pokemon.ivs.sum() == (6 * 31):
return True # ✅ enabled
return "Pokémon with perfect IVs"
#pass

# Catch zero IV Pokémon
# Pokémon with all 0 IVs
if pokemon.ivs.sum() == 0:
return True # ✅ enabled
return "Pokémon with all 0 IVs"
#pass

# Catch Pokémon with 6 identical IVs of any value
# Pokémon with 6 identical IVs of any value
if all(v == ivs[0] for v in ivs):
return True # ✅ enabled
return "Pokémon with 6 identical IVs of any value"
#pass

# Catch Pokémon with 4 or more max IVs in any stat
# Pokémon with 4 or more max IVs in any stat
max_ivs = sum(1 for v in ivs if v == 31)
if max_ivs > 4:
pass # ❌ disabled
# return "Pokémon with 4 or more max IVs in any stat"
pass

# Catch Pokémon with a good IV sum of greater than or equal to 170
# Pokémon with IVs sum greater or equal to 170
if pokemon.ivs.sum() >= 170:
pass # ❌ disabled
# return "Pokémon with IVs sum greater or equal to 170"
pass

# Catch all Poochyena with a Pecha Berry
# Poochyena holding a Pecha Berry
if pokemon.species.name == "Poochyena" and pokemon.held_item and pokemon.held_item.name == "Pecha Berry":
pass # ❌ disable
# return "Poochyena holding a Pecha Berry"
pass

# Catch any Pokémon with perfect attack, spAttack and speed
# Pokémon with perfect attack, spAttack and speed
if pokemon.ivs.attack == 31 and pokemon.ivs.special_attack == 31 and pokemon.ivs.speed == 31:
pass # ❌ disable
# return "Pokémon with perfect attack, spAttack and speed"
pass

### Edit above this line ###

Expand Down
Loading