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

Add support for "sub apps" #320

Open
2 of 4 tasks
davidolrik opened this issue Apr 30, 2021 · 8 comments
Open
2 of 4 tasks

Add support for "sub apps" #320

davidolrik opened this issue Apr 30, 2021 · 8 comments

Comments

@davidolrik
Copy link

When creating a large slack application it would be nice to be able to reuse components in a more pythonic way than what is possible right now.
A workaround is described in issue #236, but it requires passing the app object around and manually wiring up things by registering and calling decorators as functions directly.

I suggest adding a feature much like include_router in FastAPI or add_typer in Typer.

This feature will let us create sub-apps in separate files or even separate packages, like plugins that can be published on pypi.org separately.

Here is a small example illustrating what I mean:

main.py

Here is our main app, where we add a plugin from a separate package

import os
from slack_bolt import App

from my_plugin import my_plugin


# Initializes your app with your bot token and signing secret
app = App(
    token=os.environ.get("SLACK_BOT_TOKEN"),
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET")
)
app.add_app(my_plugin)

my_plugin.py

A simple plugin that just creates a slack_bolt app, but without any config

from slack_bolt import App


my_plugin = App()

@my_plugin.message("hello")
def message_hello(message, say):
    say("Hello world")

Category

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Others
@seratch
Copy link
Member

seratch commented Apr 30, 2021

Hi @davidolrik, thanks for writing in!

I also like the reusable component concepts like Blueprints in Flask and APIRouter in FastAPI. However, I'm afraid that reusing App in the way you illustrated in my_plugin.py does not match the Bolt framework concept across the three supported languages/runtimes (Node.js / Python / Java). App requires a complete initialization of a Slack app. Thus, a subset of listeners or incomplete initialization won't be compatible with the concept.

If we name the pluggable components differently, it can be reasonable to have such in the Bolt design (or only for Python). With being that said, please do not expect such enhancement happens in the short term. We are not planning to work on this so far.

Anyone's inputs and feedback related to this topic are greatly appreciated, but we don't guarantee the Bolt framework will support this kind of abstraction in future versions. Let me keep this issue in a discussion phase.

@davidolrik
Copy link
Author

My main goal was to facilitate better code reuse, but most importantly better organization of large complex slack apps while still being able to use the @app.* decorators.

Maybe a kind of proxy sub app object would make sense then, and app.add_sub_app(my_plugin) could then transfer what ever was defined on the sub app to the main app.

from slack_bolt import SubApp


my_plugin = SubApp()

@my_plugin.message("hello")
def message_hello(message, say):
    say("Hello world")

The important part is that the SubApp should be able to use all the decorators from App.

@senpos
Copy link

senpos commented May 19, 2021

Hi!

I totally agree that it would be awesome to have some kind of modularity built-in. If that's already possible, maybe you could add more examples to documentation?

I was struggling to separate my Bolt instance from the webserver (I use Starlette adapter for Bolt) and ended up creating a function which creates a Bolt app AND declares event listeners inside it, which feels a bit like a dirty approach.

I would appreciate any resources on making Bolt apps modular.

Thanks for your work!

@github-actions
Copy link

github-actions bot commented Dec 5, 2021

👋 It looks like this issue has been open for 30 days with no activity. We'll mark this as stale for now, and wait 10 days for an update or for further comment before closing this issue out.

@seratch
Copy link
Member

seratch commented Dec 5, 2021

Marked as "auto-triage-skip" -- we are still interested in exploring recommended ways for modularity in the future.

@jianyuan
Copy link

jianyuan commented Jun 3, 2022

Here's what I came up with:

class SubApp:
    def __init__(self) -> None:
        self.actions = []
        self.events = []
        self.views = []

    def action(self, *args, **kwargs):
        def __call__(func):
            self.actions.append((args, kwargs, func))
            return func

        return __call__

    def event(self, *args, **kwargs):
        def __call__(func):
            self.events.append((args, kwargs, func))
            return func

        return __call__

    def view(self, *args, **kwargs):
        def __call__(func):
            self.views.append((args, kwargs, func))
            return func

        return __call__

    def register_to(self, app: App):
        for args, kwargs, func in self.actions:
            app.action(*args, **kwargs)(func)
        for args, kwargs, func in self.events:
            app.event(*args, **kwargs)(func)
        for args, kwargs, func in self.views:
            app.view(*args, **kwargs)(func)

Usage:

sub_app = SubApp()

@sub_app.event("app_mention")
def handle_app_mentions(logger, event, say):
    logger.info(event)
    say(f"Hi there, <@{event['user']}>")

# Then...

app = App(...)
sub_app.register_to(app)

@ryanmsnyder
Copy link

ryanmsnyder commented Sep 7, 2022

I agree that this framework needs a built in way to create sub apps. The decorator syntax that bolt allows is extremely useful but it's almost completely nullified because of the lack of modularity.

@DonDebonair
Copy link

Hey all! Bolt is an excellent framework with many good ideas, but I too was looking for a way to build bigger, modular Slack bots in Python. For this purpose, I built Slack Machine. I started building it a couple of years before Bolt was created and I have been working on it off and on since then.

Recently, I pushed a major new release that converted the whole framework to use the Events API, SocketMode and Python's asyncio features. I feel that Slack Machine is now modern and robust enough to be used by others.

Slack Machine is built around the idea of plugins, where plugins can be external Python packages, or live alongside your Slack Machine configuration. It also makes heavy use of decorators just like Bolt, to make it easy to respond to various events from Slack.

This should give you an idea of what a plugin could look like:

from machine.plugins.base import MachineBasePlugin, Message
from machine.plugins.decorators import respond_to

class DeploymentPlugin(MachineBasePlugin):
    """Deployments"""
    @respond_to(r"deploy (?P<application>\w+) to (?P<environment>\w+)")
    async def deploy(self, msg: Message, application, environment):
        """deploy <application> <environment>: deploy application to target environment"""
        await msg.say(f"Deploying {application} to {environment}")

Let me know if you have any questions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants