예제 #1
0
def upload_and_deploy(package: AnyStr, bot_uid: str = None) -> str:
    """Uploads the given archive, triggering deployment of the chatbot.

    :param package: Path to the packaged chatbot zip.
    :param bot_uid: Used to modify an existing bot: the UID of the previously-deployed bot.
    :return bot_uid: The UID of the deployed bot.
    """
    package = Path(package)

    deployment_file = package.parent / "_deployment"

    if deployment_file.exists() and bot_uid is None:
        with deployment_file.open("r") as f:
            previous_bot_uid = f.read().strip()
        print("Detected previous deployment from this location. Use the same bot UID as before?")
        print(f" [y] (default) Yes. Update the bot ({previous_bot_uid}).")
        print(f" [n] No. Deploy as a new bot.")
        input_key = input().lower()
        if input_key == "y" or input_key == "":
            bot_uid = previous_bot_uid
            print(f"Using previous bot UID: {bot_uid}")
        elif input_key == "n":
            pass
        else:
            raise RuntimeError("Unknown input.")

    auth = get_auth()
    try:
        r = requests.post(
            ENDPOINT,
            params={'uid': auth.uid, 'key': auth.key, 'bot_uid': bot_uid}
        )
        try:
            r.raise_for_status()
        except Exception:
            raise RuntimeError(r.json())
    except Exception:
        print(r.content, r.reason)
        raise RuntimeError("Failed to retrieve signed URL.")

    url = r.text
    if bot_uid is None:
        print("Creating new bot.")
        bot_uid = parse_signed_url_for_bot_uid(url)
        print(f"Received bot UID: {bot_uid}")
    with deployment_file.open("w") as f:
        f.write(bot_uid)

    with package.open("rb") as f:
        r = requests.put(url, data=f, headers={'content-type': 'application/zip'})
        r.raise_for_status()
    print(f"Successfully uploaded {package}.")
    return bot_uid
예제 #2
0
class Metadata:
    """Information required for bot deployment."""
    # Name of the bot.
    name: str
    # Profile image for the bot. Has to be a valid URL.
    image_url: str
    # The alphanumeric part of a hex color code. (E.g. ffffff)
    color: str
    # Description of the bot.
    description: str
    # Python class (N.B. not object!) that inherits from ChaiBot.
    input_class: Type[ChaiBot]

    # Developer Unique ID.
    developer_uid: str = field(default_factory=lambda: get_auth().uid)

    # Total available memory for the bot in MB. This includes memory needed to store sources and data.
    memory: int = 256

    def verify(self, bot_file: Path):
        """Performs basic checks to ensure validity of the metadata."""
        assert isinstance(self.name, str)
        assert len(self.name) >= 3, "Bot name has to be at least 3 characters."

        assert len(self.description) > 0, "Bot has to have description."
        assert self.input_class.__init__ is ChaiBot.__init__, \
            "Do not override ChaiBot.__init__(). Override the setup() method instead."

        assert not (bot_file.parent / "main.py").exists(
        ), "Do not create a main.py file in your bot's root directory."

        try:
            verify_image_url(self.image_url)
        except Exception:
            raise ValueError(f"Could not verify image url ({self.image_url})")

        assert isinstance(self.color, str)
        assert re.search(r"^(?:[0-9a-fA-F]{3}){1,2}$", self.color), \
            f"Color has to be provided as the alphanumeric part of the hex code (e.g. ffffff), found {self.color}"

        assert isinstance(
            self.memory, int
        ), f"Attribute .memory has to be an integer (found type {type(self.memory)})."
        assert self.memory <= MAX_SUPPORTED_MEMORY, f"Attribute .memory has to be less than or equal to {MAX_SUPPORTED_MEMORY} (found {self.memory})."
예제 #3
0
def delete_bot(bot_uid: str) -> str:
    """
    Uses an HTTPS request to trigger deletion of bot with specified UID.

    :param bot_uid:
    :return: The url for the bot.
    """
    auth = get_auth()
    try:
        req = requests.delete(
            url=ENDPOINT,
            params={'uid': auth.uid, 'key': auth.key, 'bot_uid': bot_uid}
        )
        try:
            req.raise_for_status()
        except Exception:
            raise RuntimeError(req.json())
    except Exception as exc:
        raise RuntimeError(f"Failed to delete bot {bot_uid}.")

    print(f"Successfully deleted {bot_uid}.")
    return bot_uid
예제 #4
0
def get_logs(bot_uid: str, errors: bool = False) -> List[Log]:
    """Retrieves logs for specified bot.

    Logs can only be pulled by the bot's developer.

    :param bot_uid: Bot UID
    :param errors: If True, only retrieves logs with severity: Error
    :return:
    """
    auth = get_auth()
    r = requests.get(
        ENDPOINT,
        params={
            'uid': auth.uid,
            'key': auth.key,
            'bot_uid': bot_uid,
            'item': 'logs',
            'errors': True if errors else None
        }
    )
    r.raise_for_status()
    return r.json()['data']
예제 #5
0
def get_bot_status(bot_uid: str) -> BotStatus:
    """Gets the status of the bot.

    :param bot_uid:
    :return:
    """
    auth = get_auth()
    try:
        req = requests.get(
            url=ENDPOINT,
            params={
                'uid': auth.uid,
                'key': auth.key,
                'bot_uid': bot_uid,
                'item': 'status'
                }
        )
        try:
            req.raise_for_status()
        except Exception:
            raise RuntimeError(req.json())
    except Exception:
        raise RuntimeError(f"Failed to retrieve status for bot {bot_uid}.")
    return req.json()
예제 #6
0
def _credentials():
    auth = get_auth()
    return requests.auth.HTTPBasicAuth(auth.uid, auth.key)
예제 #7
0
def _get_developer_uid():
    return get_auth().uid