leonhard-s / auraxium

A high-level Python wrapper for the PlanetSide 2 API.
https://auraxium.readthedocs.io/
MIT License
28 stars 8 forks source link

Cannot pass conditions with @client.trigger() #39

Closed LordFlashmeow closed 3 years ago

LordFlashmeow commented 3 years ago

When using the @client.trigger(EventType.DEATH) (or any other trigger), you cannot pass a list of conditions to filter the websocket events. Here's your event streaming example, with the simplest condition I could find:

loop = asyncio.get_event_loop()

def example_condition(payload):
    if payload['world_id'] == "1":    # I know you can use the world filter via subscription, but this is a simple filter that allows many results
        return True
    return False

async def main():
    @client.trigger(auraxium.EventType.BATTLE_RANK_UP, conditions=[example_condition])
    async def print_levelup(event):
        char_id = int(event.payload['character_id'])
        char = await client.get_by_id(ps2.Character, char_id)

        # NOTE: This value is likely different from char.data.battle_rank as
        # the REST API tends to lag by a few minutes.
        new_battle_rank = int(event.payload['battle_rank'])

        print(f'{await char.name_long()} has reached BR {new_battle_rank}!')

loop.create_task(main())
loop.run_forever()

This raises the error

  File "virtualenvs/ps2elo-VDVCoL-1/lib/python3.9/site-packages/auraxium/event.py", line 680, in trigger
    trigger = Trigger(event, *args, name=name, **kwargs)
TypeError: __init__() got an unexpected keyword argument 'conditions'

In the init for Trigger, there's no way to provide conditions, except after initializing the object. As far as I know, it's not possible to modify the trigger object while also using the decorator.

https://github.com/leonhard-s/auraxium/blob/5a8034505592086291f9f3f20f44c31411741bd9/auraxium/event.py#L267-L288

Unless I'm missing something obvious that hasn't been documented, it seems like the easiest thing to do would be something like:

def __init__(self, ..., conditions: List[Union[bool, Callable[[CensusData], bool]]] = None, ...):
    ...
    self.conditions: List[Union[bool, Callable[[CensusData], bool]]] = conditions if conditions is not None else []
    ...

I'm happy to make a PR to fix this if you want.

leonhard-s commented 3 years ago

Hi,

This was/is intentional, if a bit arbitrary. I felt like the Trigger class's initialiser was quite cluttered already at the time and didn't want to add too many other kwargs, especially since conditions and the characters/worlds lists do overlap conceptually.

@client.trigger() was meant to be a shorthand that would cover most use cases, with more complex setups being entirely programmatical, or following this pattern:

my_trigger = auraxium.Trigger(auraxium.EventType.BATTLE_RANK_UP)
my_trigger.conditions = [example_condition]

@my_trigger.action
async def print_levelup(event):
    ...

client.add_trigger(my_trigger)

You are correct that this isn't properly documented - I am in the process of rewriting the documentation to clarify these areas (which covers the alternate pattern above), but it will take me a few more days/weeks to finish as it also covers the object model.

That said, the current solution is neither elegant nor intuitive, and the limitation is completely arbitrary - so I would be open to adding the conditions= keyword argument to the Trigger class as you suggested.

Feel free to create a PR, otherwise I will add it by the end of the week.

LordFlashmeow commented 3 years ago

Done. I'm also interested in adding some of the event classes in #34, if you'd like a hand.