kenkov / kovot

Python Chatbot Framework
MIT License
4 stars 1 forks source link

Make Message and Response classes containable some meta information. #7

Open kazh98 opened 5 years ago

kazh98 commented 5 years ago

Dear kenkov-san,

Can we attach meta information, as an optionally given dict object for example, to Message and Response instances? This issue is related to the discussion of the pull request #4.

To use kovot for making a bot which communicates over SNS such as Mastodon, we need some additional information with Message and Response instances. For example, we need the sender's account ID for replying to a given toot in Mastodon. As another example, we have to specify additional parameters such as the sensitive flag, visibility flag, and spoiler text when we post some statuses into Mastodon.

I'd like to ask you for considering this extension and giving me advice. Thank you for considering this issue.

Sincerely yours, Kazuhiro Hishinuma.

kenkov commented 5 years ago

Thank you for you opinion about adding other information to Message and Response objects!

Adding stream-specific information to Message and Response objects doesn't seem to be good solution in this situation; if we pass stream-specific information to Message object, such information may be used in some Mod classes. Then, these classes don't work with other Stream like kovot.stream.StdIO.

The responsitility to treat stream-specific information belongs to its Stream class, so now I'm considering to reverse the dependency between Bot and Stream object. In the current Kovot, a Stream object is passed to a Bot object like bot.run(stream=stream). On the other hand, in my new idea, a Bot object is passed to a Stream object like stream(bot=bot).run().

For example, StdIO can be implemented as follows.

from kovot import Message
from kovot import Response
from kovot import Bot
import sys

class StdIO:
    """Stream class for standard I/O."""
    def __init__(self, bot):
        self._bot = bot

    def run(self):
        for line in sys.stdin:
            ipt = line.strip("\n")
            msg = Message(text=ipt)
            res = self._bot.talk(message=msg)
            print(res.text)

class EchoMod:
    """Echo bot"""
    def generate_responses(self, bot, message):
        res = Response(score=1.0,
                       text=message.text,
                       message=message,
                       source=self.__class__.__name__)
        return [res]

mods = [EchoMod()]
bot = Bot(mods=mods)
stream = StdIO(bot=bot)

stream.run()

Because this implementation treats getting input utterance and posting response utterance in the run method, this enables to pass original information to a Response object without passing it to a Message object.

If applying this modification to Mastodon stream from pull request #4, reply_id can be specified without passing it to a Message object, for example. The implmenetation seems to be like below;

class Mastodon:
    ...
    def run(self):
        while True:
            raw = self.queue.get()
            msg = Message(raw['content'])
            res = self._bot.talk(message=msg)
            ...
            self.api.status_post(res.text, in_reply_to_id=raw["id"])
            ...
    ...

How about this solution?

kazh98 commented 5 years ago

Thank you for your comment!

I understand the importance of the independence of each Mod. Your idea seems very good.

Reversing the dependency between Bot and Stream object is good for not only solving this problem but also solving a problem that we cannot duplicate Mastodon stream object due to the restriction of API.

I will deal with my pull request #4 at first, and I will continuously consider the implementation of the Mastodon stream based on this idea. I'll answer my final opinion to you after I complete the pull request.

Thanks.