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

Clarification Regarding Bot Creation #849

Open
jparr721 opened this issue Jan 6, 2025 · 6 comments
Open

Clarification Regarding Bot Creation #849

jparr721 opened this issue Jan 6, 2025 · 6 comments

Comments

@jparr721
Copy link

jparr721 commented Jan 6, 2025

Hi there,

Based on the documentation, it seems like I need to effectively clone this entire repository and run the provision script to create a bot of my own which can interact with on Zulip, is this correct? I'd really prefer to use alternative package management libraries etc to manage my environment, and being tied into design decisions outside of my control (not to mention cloning a whole lot of bots I don't intend to use) is not really something I'm interested in. Could I get some clarification here if possible?

Specifically, I was hoping to utilize the test server, but it seems quite rigid in that it mandates the use of this project structure for debugging etc.

@timabbott
Copy link
Member

timabbott commented Jan 6, 2025

I'm pretty sure you can just pip install it; it's just easier to write instructions that can make assumptions about the paths where everything lives; it'll be more fiddly with PYTHONPATH if you do it another way.

Note that the bot implementations are usually <100 lines of code, so there's no cost to having the whole collection and provisioning.

Probably reflects a failure of the bots documentation that we don't have a clean example of that form.

@jparr721
Copy link
Author

jparr721 commented Jan 6, 2025

No worries, I get it. To clarify though, I did copy the structure, just not by cloning the repository:

myproject
└── zulip_bots
    └── bots
        └── myproject

Where within the folder is the myproject.py and test_myproject.py scripts respectively. The content of each respective file is

from zulip_bots.test_lib import BotTestCase, DefaultTests


class TestMyProjectBot(BotTestCase, DefaultTests):
    bot_name = "myproject"

    def test_bot(self) -> None:
        help_text = "Info on Zulip can be found here:\nhttps://github.com/zulip/zulip"
        requests = ["", "help", "Hi, my name is abc"]

        dialog = [(request, help_text) for request in requests]

        self.verify_dialog(dialog)

And

from typing import Dict

from zulip_bots.lib import BotHandler


class MyProjectHandler:
    def usage(self) -> str:
        return """
            My Bot
            """

    def handle_message(self, message: Dict[str, str], bot_handler: BotHandler) -> None:
        help_content = "Welcome to My Project!"
        bot_handler.send_reply(message, help_content)


handler_class = MyProjectHandler

And yet both zulip-bot-shell and pytest both do not work, they cannot find the package. Granted, I did pip install zulip_bots only, so I could be goofing here.

@timabbott
Copy link
Member

#636 and the discussion linked from there is probably helpful.

@jparr721
Copy link
Author

jparr721 commented Jan 7, 2025

To clarify in case anyone else encounters this in the future. The bot setup process is somewhat involved, hence their setup system. My particular setup went against theirs a bit as I prefer uv to pip. The easiest way to get started rolling your own bot is to basically check out the tools/provision script and edit it to do what you need if you have an exotic environment you'd like to work within. If it's relevant to anyone who wants to use uv because it's oh so much better than pip, you can use my modified script from tools/provision:

#!/usr/bin/env python3

import argparse
import glob
import os
import subprocess
import sys

CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
ZULIP_BOTS_DIR = os.path.join(CURRENT_DIR, "..", "zulip_bots")
sys.path.append(ZULIP_BOTS_DIR)

red = "\033[91m"
green = "\033[92m"
end_format = "\033[0m"
bold = "\033[1m"


def main() -> None:
    base_dir = os.path.abspath(os.path.join(__file__, "..", ".."))

    # On OS X, ensure we use the virtualenv version of the python binary for
    # future subprocesses instead of the version that this script was launched with. See
    # https://stackoverflow.com/questions/26323852/whats-the-meaning-of-pyvenv-launcher-environment-variable
    if "__PYVENV_LAUNCHER__" in os.environ:
        del os.environ["__PYVENV_LAUNCHER__"]

    # In order to install all required packages for the venv, `pip` needs to be executed by
    # the venv's Python interpreter. `--prefix venv_dir` ensures that all modules are installed
    # in the right place.
    def install_dependencies(requirements_filename: str) -> None:
        print(base_dir)
        if subprocess.call(
            [
                "uv", "add", "-r",
                os.path.join(base_dir, requirements_filename),
            ]
        ):
            raise OSError(
                f"The command `uv add -r {os.path.join(base_dir, requirements_filename)}` failed. Dependencies not installed!"
            )

    install_dependencies("requirements.txt")

    # Install all requirements for all bots. get_bot_paths()
    # has requirements that must be satisfied prior to calling
    # it by setup().
    current_dir = os.path.dirname(os.path.abspath(__file__))
    bots_dir = os.path.join(current_dir, "zulip_bots", "zulip_bots", "bots")
    print('bots_dir', bots_dir)
    exit(0)
    req_paths = glob.glob(bots_dir + "/*/requirements.txt")
    for req_path in req_paths:
        path_split = req_path.split(os.path.sep)[-5:]
        relative_path = os.path.join(*path_split)
        install_dependencies(relative_path)

    print(green + "Success!" + end_format)


if __name__ == "__main__":
    main()

Note I've stripped out all their CLI args since I don't need them, YMMV if you decide to change this, but it's pretty easy. This also requires an odd project structure where you embed their project into yours and wrap it in a pyproject.toml file.

.
├── README.md
├── justfile
├── pyproject.toml
├── python-zulip-api
└── uv.lock

I also made use of the following requirements.txt file from within python-zulip-api.

setuptools
crayons
twine
mock
pytest
pytest-cov
-e ./python-zulip-api/zulip
-e ./python-zulip-api/zulip_bots
-e ./python-zulip-api/zulip_botserver
git+https://github.com/zulip/zulint@417b4e4971fdd5ca8e84847f1391b657b188631a#egg=zulint==1.0.0
types-beautifulsoup4
types-httplib2
types-python-dateutil
types-pytz
types-requests
gitlint>=0.13.0
matrix-nio

As of writing I have yet to deploy this, but all the tooling works so I assume that the deployment should go smoothly.

From there, embed your project in the appropriate structure as prescribed by the docs, and everything should wire up just fine for testing and whatever else you need. What was not immediately clear was that you have to install the packages in development mode into your local environment to get everyone working. Hopefully this helps someone.

@timabbott if you're open to feedback, the bot generation process is a bit tricky. Ideally these tools would just look at whatever path I give it and look for my exported bot and run it, instead of me needing to check in an entire pile of code that I have to explain away in the situation someone wants to edit it (and if I've made any bespoke changes, keeping this up to date is sure to be a headache).

Please let me know if something else could be done here where I don't have to use your toolchain, though I don't see how I'd do it since a perusal of the tooling seems pretty hard-wired to the zuilip_bots.bots.{your_module}.{your_module} syntax. Maybe as I grow more familiar with this tooling I will write something custom. If it doesn't suck, I'll see what you think.

Anyway, thank you for the prompt responses. I really do appreciate it.

@jparr721 jparr721 closed this as completed Jan 7, 2025
@timabbott
Copy link
Member

I'm gonna leave this open, since we do want to improve the tooling/documentation here.

@timabbott timabbott reopened this Jan 7, 2025
@krishna-shah-07
Copy link

@timabbott
Is it possible for me to contribute to the documentation?

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

No branches or pull requests

3 participants