Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/NextcloudApp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ It's advisable to write these steps as commands in a Makefile for quick use.

Examples for such Makefiles can be found in this repository:
`Skeleton <https://github.com/cloud-py-api/nc_py_api/blob/main/examples/as_app/skeleton/Makefile>`_ ,
`TalkBot <https://github.com/cloud-py-api/nc_py_api/blob/main/examples/as_app/talk_bot/Makefile>`_
`ToGif <https://github.com/cloud-py-api/nc_py_api/blob/main/examples/as_app/to_gif/Makefile>`_ ,
`nc_py_api <https://github.com/cloud-py-api/nc_py_api/blob/main/scripts/dev_register.sh>`_

Expand Down
77 changes: 74 additions & 3 deletions docs/NextcloudTalkBot.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,75 @@
Nextcloud Talk Bot API in Application
=====================================
Nextcloud Talk Bot API in Applications
======================================

to-do
The AppEcosystem is an excellent choice for developing and deploying bots for Nextcloud Talk.

Bots for Nextcloud Talk, in essence, don't differ significantly from regular external applications.
The functionality of an external application can include just the bot or provide additional functionalities as well.

Let's consider a simple example of how to transform the `skeleton` of an external application into a Nextcloud Talk bot.

The first step is to add the **TALK_BOT** and **TALK** scopes to your `info.xml` file:

.. code-block:: xml

<scopes>
<required>
<value>TALK</value>
<value>TALK_BOT</value>
</required>
<optional>
</optional>
</scopes>

The TALK_BOT scope enables your application to register the bot within the Nextcloud system, while the TALK scope permits access to Talk's endpoints.

In the global **enabled_handler**, you should include a call to your bot's enabled_handler, as shown in the bot example:

.. code-block:: python

def enabled_handler(enabled: bool, nc: NextcloudApp) -> str:
try:
CURRENCY_BOT.enabled_handler(enabled, nc) # registering/unregistering the bot's stuff.
except Exception as e:
return str(e)
return ""

Afterward, using FastAPI, you can define endpoints that will be invoked by Talk:

.. code-block:: python

@APP.post("/currency_talk_bot")
async def currency_talk_bot(
message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)],
background_tasks: BackgroundTasks,
):
return requests.Response()

.. note::
You must include to each endpoint your bot provides the **Depends(talk_bot_app)**.
**message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)]**

Depending on **talk_bot_app** serves as an automatic authentication handler for messages from the cloud, which returns the received message from Nextcloud upon successful authentication.

Additionally, if your bot can provide quick and fixed execution times, you may not need to create background tasks.
However, in most cases, it's recommended to segregate functionality and perform operations in the background, while promptly returning an empty response to Nextcloud.

An application can implement multiple bots concurrently, but each bot's endpoints must be unique.

All authentication is facilitated by the Python SDK, ensuring you needn't concern yourself with anything other than writing useful functionality.

Currently, bots have access only to three methods:

* :py:meth:`~nc_py_api.talk_bot.TalkBot.send_message`
* :py:meth:`~nc_py_api.talk_bot.TalkBot.react_to_message`
* :py:meth:`~nc_py_api.talk_bot.TalkBot.delete_reaction`

.. note:: **The usage of system application functionality for user impersonation in bot development is strongly discouraged**.
All bot messages should only be sent using the ``send_message`` method!

All other rules and algorithms remain consistent with regular external applications.

Full source of bot example can be found here:
`TalkBot <https://github.com/cloud-py-api/nc_py_api/blob/main/examples/as_app/talk_bot/src/main.py>`_

Wishing success with your Nextcloud bot integration! May the force be with you!
28 changes: 28 additions & 0 deletions examples/as_app/talk_bot/HOW_TO_INSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
How To Install
==============

Currently, while AppEcosystem hasn't been published on the App Store, and App Store support hasn't been added yet,
installation is a little bit tricky.

Steps to Install:

1. [Install AppEcosystem](https://cloud-py-api.github.io/app_ecosystem_v2/Installation.html)
2. Create a deployment daemon according to the [instructions](https://cloud-py-api.github.io/app_ecosystem_v2/CreationOfDeployDaemon.html#create-deploy-daemon) of the Appecosystem
3. php occ app_ecosystem_v2:app:deploy talk_bot "daemon_deploy_name" \
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot/appinfo/info.xml

to deploy a docker image with Bot to docker.

4. php occ app_ecosystem_v2:app:register talk_bot "daemon_deploy_name" -e --force-scopes \
--info-xml https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/examples/as_app/talk_bot/appinfo/info.xml

to call its **enable** handler and accept all required API scopes by default.


In a few months
===============

1. Install AppEcosystem from App Store
2. Configure Deploy Daemon with GUI provided by AppEcosystem
3. Go to External Applications page in Nextcloud UI
4. Find this bot in a list and press "Install" and "Enable" buttons, like with usual Applications.
4 changes: 2 additions & 2 deletions examples/as_app/talk_bot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ help:
@echo " "
@echo " deploy deploy example to registered 'docker_dev'"
@echo " "
@echo " run28 install ToGif for Nextcloud 28"
@echo " run27 install ToGif for Nextcloud 27"
@echo " run28 install TalkBot for Nextcloud 28"
@echo " run27 install TalkBot for Nextcloud 27"
@echo " "
@echo " For development of this example use PyCharm run configurations. Development is always set for last Nextcloud."
@echo " First run 'TalkBot' and then 'make manual_register', after that you can use/debug/develop it and easy test."
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 12 additions & 1 deletion examples/as_app/talk_bot/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
from nc_py_api.ex_app import run_app, set_handlers, talk_bot_app

APP = FastAPI()
# We define bot globally, so if no `multiprocessing` module is used, it can be reused by calls.
# All stuff in it works only with local variables, so in the case of multithreading, there should not be problems.
CURRENCY_BOT = talk_bot.TalkBot("/currency_talk_bot", "Currency convertor", "Usage: `@currency convert 100 EUR to USD`")


def convert_currency(amount, from_currency, to_currency):
"""Payload of bot, simplest currency convertor."""
base_url = "https://api.exchangerate-api.com/v4/latest/"

# Fetch latest exchange rates
Expand All @@ -37,17 +40,21 @@ def convert_currency(amount, from_currency, to_currency):

def currency_talk_bot_process_request(message: talk_bot.TalkBotMessage):
try:
# Ignore `system` messages
if message.object_name != "message":
return
# We use a wildcard search to only respond to messages sent to us.
r = re.search(
r"@currency\s(convert\s)?(\d*)\s(\w*)\sto\s(\w*)", message.object_content["message"], re.IGNORECASE
r"@currency\s(convert\s)?(\d*)\s(\w*)\sto\s(\w*)\s?", message.object_content["message"], re.IGNORECASE
)
if r is None:
return
converted_amount = convert_currency(int(r.group(2)), r.group(3), r.group(4))
converted_amount = round(converted_amount, 2)
# Send reply to chat
CURRENCY_BOT.send_message(f"{r.group(2)} {r.group(3)} is equal to {converted_amount} {r.group(4)}", message)
except Exception as e:
# In production, it is better to write to log, than in the chat ;)
CURRENCY_BOT.send_message(f"Exception: {str(e)}", message)


Expand All @@ -56,19 +63,23 @@ async def currency_talk_bot(
message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_app)],
background_tasks: BackgroundTasks,
):
# As during converting, we do not process converting locally, we perform this in background, in the background task.
background_tasks.add_task(currency_talk_bot_process_request, message)
# Return Response immediately for Nextcloud, that we are ok.
return requests.Response()


def enabled_handler(enabled: bool, nc: NextcloudApp) -> str:
print(f"enabled={enabled}")
try:
# `enabled_handler` will install or uninstall bot on the server, depending on ``enabled`` parameter.
CURRENCY_BOT.enabled_handler(enabled, nc)
except Exception as e:
return str(e)
return ""


# The same stuff as for usual External Applications
@APP.on_event("startup")
def initialization():
set_handlers(APP, enabled_handler)
Expand Down