archangelic / pinhook

the pluggable python framework for IRC bots and Twitch bots
https://archangelic.github.io/pinhook/
MIT License
31 stars 4 forks source link

Add bot as argument for Message class & pass self in calls #60

Closed ixxie closed 5 years ago

ixxie commented 5 years ago

Passing the bot object as an argument to the Message class make the pinhook Bot class more extensible. Now it is easy to inherit and pass attributes of the inherited class into plugins.

See https://github.com/archangelic/pinhook/issues/59

Example usage:

from pinhook.bot import Bot
from mybot.nlp.model import Model

class Mybot(Bot):

    def __init__(self, **config):

        super().__init__(**config)

        self.config = config
        self.model = Model()

Now I can use my new attribute in a plugin:

import pinhook

@pinhook.plugin.listener('parser')
def parse(msg):
    return pinhook.plugin.message(msg.bot.model.parse(msg.text))
archangelic commented 5 years ago

I have waffled on this for so long, but I hesitate to say that the solution is to send the entire bot object to the plugins. I am trying to figure out a way to be explicit about what things need to be added to Message

ixxie commented 5 years ago

I see your point, this seems extreme.

A less aggressive solution is to instatiate Bot and Message with a new components attribute into which a dict with various objects can be passes, and then pass self.components between them as a sort of explicit interface.

However it should be noted that my original approach allows for interesting possibilities. One could write plugins that call Bot methods, and while this could be abused respecting the underscores in methods and attributes should be a decent guide for safe use. This may be a matter if taste, but I also feel extending the class in some ways feels natural to me as someone writing a bot based on a framework.

I guess its a tradeoff between giving pinhook devs power to customize their bots, and giving them the chance to shoot their bots in the foot!

ixxie commented 5 years ago

Maybe the way to do this which most respects the current design is to implement components as a new type of plugin.

You would have a Component (abstract) base class with methods that would be called in various points of the Bot lifecycle. Then in addition to these lifecycle methods of the class would be decorated just like every other plugin in order to be triggered.

Using multiple inheritence, one could mix classes from another library with the Component class, create plugin methods explicitly which call the other class's methods in particular ways, and thus be able to let arbitrary objects piggyback on the bot.

archangelic commented 5 years ago

I think i've decided to merge this for now. I want a way to be more explicit, but it means a few changes to the way things would have to be created, and this would solve a lot of problems for more advanced users of the framework.