nextcloud / spreed

🗨️ Nextcloud Talk – chat, video & audio calls for Nextcloud
https://nextcloud.com/talk
GNU Affero General Public License v3.0
1.61k stars 428 forks source link

Webhook Support / bot integration #1879

Closed fti7 closed 1 year ago

fti7 commented 5 years ago

Is your feature request related to a problem? Please describe. I would like to integrate my Smarthome System into Nextcloud Talk Chat for Notifications etc.

Describe the solution you'd like Webhook Implementation (Similiar like these on Rocket.Chat/Slack etc) Webhooks usually per Channel and multiple ones can be added for various integrations

Incoming Webhooks: Generates an Webhook Token/Credentials to use in Scripts/Tools. This will allow to send Messages to an specific Channel with an simple API Call. But not to read any messages for Security Reasons.

Outgoing Webhooks: Send new Messages to an specific URL. (Standardized Format) Optionally only if the text includes an "trigger word"

Describe alternatives you've considered Currently im using an Dummy User and fake an normal "Web Client" to send Messages to an Channel. This basically works, but this User can also read Messages (Security Issue).


BRadHoc commented 5 years ago

Having a go at implementing this, will let you know how I get on

image

mar565 commented 5 years ago

Can you show us how you did the dummy User solution?

fti7 commented 5 years ago

@mar565 Basically it does the same as the Browser.

<?

function NextcloudTalk_SendMessage($channel_id, $message) {

    $SERVER = "https://XYZ";
    $USER = "notify1";
    $PASS = "notify1pw";

    // notify hack
    $data = array(
                "token" => $channel_id,
                "message" => $message,
                "actorDisplayName" => "PYTHON-NOTIFICATION",
                "actorType" => "",
                "actorId" => "",
                "timestamp" => 0,
                "messageParameters" => array()
    );

    $payload = json_encode($data);

    $ch = curl_init($SERVER . '/ocs/v2.php/apps/spreed/api/v1/chat/' . $channel_id);

    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLINFO_HEADER_OUT, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_USERPWD, "$USER:$PASS");
    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);

    // Set HTTP Header
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Content-Length: ' . strlen($payload),
        'Accept: application/json',
        'OCS-APIRequest: true')
    );

    $result = curl_exec($ch);
    curl_close($ch);

}

NextcloudTalk_SendMessage('jatdu2cc', 'Hello From SYMCON');

?>
q-wertz commented 4 years ago

Having a go at implementing this, will let you know how I get on

@BRadHoc any news on this?

loxK commented 4 years ago

Could help. Here is how rocket chat handles webhooks. https://rocket.chat/docs/administrator-guides/integrations/gitlab/

BRadHoc commented 4 years ago

Appologies for the long delay on this, I've had a lot of stuff going on with work and personal life.

I used a different approach to @fti7 in that I copied the functonality of the Update conversation to create a new window and the same process for putting messages into it.

It's spread across a few files, so not the simplest of solutions.

Also I believe nextcloud flow coming in server v18 will be able to cover this functionality?

David-Development commented 4 years ago

If anyone is interested in @fti7 solution converted to python3, here you go:

import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')

However it would still be nice to have some official documentation about this or some official endpoint.

Window-Hero commented 4 years ago

Hey, any updates on this feature, or any similar features to do with NextCloud Talk? As of now, it seems to only be possible to have automated messages sent as a result of things that occur within NextCloud, seemingly only file changes at the moment, while the original request (as well as my own interest) is about integrating an external system with NextCloud Talk, which is functionality not currently covered by Flows.

Lulalaby commented 4 years ago

If anyone is interested in @fti7 solution converted to python3, here you go:

  • Create an Dummy Nextcloud User
  • Create an Channel an get the Channel ID from the URL (in the browser)
import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')

However it would still be nice to have some official documentation about this or some official endpoint.

Is this working atm?

nickvergessen commented 4 years ago

Yes, long polling or regularly pinging the api works, but it's still on our todo to implement this properly

Lulalaby commented 4 years ago

Yes, long polling or regularly pinging the api works, but it's still on our todo to implement this properly

Thanks for the update about this. I'm looking forward!

niklasgrewe commented 3 years ago

If anyone is interested in @fti7 solution converted to python3, here you go:

  • Create an Dummy Nextcloud User
  • Create an Channel an get the Channel ID from the URL (in the browser)
import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')

However it would still be nice to have some official documentation about this or some official endpoint.

possible to convert this to curl, so i can use it in bash scripts like this?

#!/usr/bin/env bash
function usage {
    programName=$0
    echo "description: use this program to post messages to Rocket.chat channel"
    echo "usage: $programName [-b \"message body\"] [-u \"rocket.chat url\"]"
    echo "      -b    The message body"
    echo "      -u    The rocket.chat hook url to post to"
    exit 1
}
while getopts ":b:u:h" opt; do
  case ${opt} in
    u) rocketUrl="$OPTARG"
    ;;
    b) msgBody="$OPTARG"
    ;;
    h) usage
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    ;;
  esac
done
if [[ ! "${rocketUrl}" || ! "${msgBody}" ]]; then
    echo "all arguments are required"
    usage
fi
read -d '' payLoad << EOF
{"text": "${msgBody}"}
EOF
echo $payLoad
statusCode=$(curl \
        --write-out %{http_code} \
        --silent \
        --output /dev/null \
        -X POST \
        -H 'Content-type: application/json' \
        --data "${payLoad}" ${rocketUrl})
echo ${statusCode}
markuman commented 3 years ago

When you got already a dummy user, this ansible collection can post messages to talk conversations.
Maybe it is a workaround for some people, because you can easily integrate it into CI/CD processes.

FocusDesigner commented 3 years ago

If anyone is interested in @fti7 solution converted to python3, here you go:

* Create an Dummy Nextcloud User

* Create an Channel an get the Channel ID from the URL (in the browser)
import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')

However it would still be nice to have some official documentation about this or some official endpoint.

Thanks for the script. This is working as expected.

Sadly it seems I've problems figuring out the right syntax for a get request. Any chance someone could provide a sample how to get a list of channelIDs or messages?

David-Development commented 3 years ago

@FocusDesigner

Any chance someone could provide a sample how to get a list of channelIDs or messages?

Just had a quick look at the Nextcloud Talk Web-UI. As far as I can see they make a request to the following endpoint to receive a list of all channels available on the server (for that user)

https://nextcloud.example.com/ocs/v2.php/apps/spreed/api/v3/room

In terms of messages I'm afraid you need to receive them for each channel seperately

https://nextcloud.example.com/ocs/v2.php/apps/spreed/api/v1/chat/CHANNEL-ID?lookIntoFuture=0&lastKnownMessageId=84&includeLastKnown=1 (replace CHANNEL-ID with the channel you want to receive the messages for - and play around with the query parameters)

FocusDesigner commented 3 years ago

@David-Development

the v3 did the trick, I always tried v4. So far parameter lookIntoFuture=0 is the only one I need.

Thanks for your help

nickvergessen commented 3 years ago

you have to use v4 from Talk version 12 onwards

mahmoudajawad commented 2 years ago

Has any work been done for this so far?

BRadHoc commented 2 years ago

I might pick this up again after I've finished the project that I'm working on. We should keep block elements in mind too as this would be good for creating useful applications with formatted data.

The biggest task I believe to start this off would be the authentication mechanism for webhooks, there would need to be generated an individual app key so that an application could provide it in the header of a request sent to Talk and they could be revoked.

mahmoudajawad commented 2 years ago

@BRadHoc, thanks for replying.

Can I suggest something? Skip the authentication part at the beginning and directly dive into this issue concern, while keeping authentication as simple username/password combination, where any valid user in the setup can be used for this step. I'm suggesting this, as authentication isn't the hurdle here, but more of the implementation of webhooks and applications interface to using Talk, where authentication can be then added based on any known authentication mechanisms.

rokdd commented 2 years ago

To get a json response you need to set the accept-header:

import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json','accept': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')
mkrecek234 commented 2 years ago

Hi there, Any news on this? Since Talk is able to create guest links - why is it not possible to create a safe webhook link where then any external application can send a message into a chat? I do not want to require a username/password for the bot to be able to post. People should themselves create a webhook link and enter it into any external app. Should be sinple as we have guest support??

arturrocha commented 2 years ago

To get a json response you need to set the accept-header:

import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json','accept': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')

How can we send files?

mkrecek234 commented 2 years ago

@arturrocha The key point is to have a user-generated webhook link. You don't want to have a bot username and password. So everyone can create his own webhook per chat. This is the concept that Slack, Microsoft Teams, Discord, Telegram, and many others use and it makes a lot of sense to integrate the chat in other applications.

mahmoudajawad commented 2 years ago

To get a json response you need to set the accept-header:

import requests
import json

server = "https://nextcloud.xxx.com"
username = "my-bot"
password = "abc"

def NextcloudTalkSendMessage(channelId, message):
    data = {
        "token": channelId,
        "message": message,
        "actorDisplayName": "PYTHON-NOTIFICATION",
        "actorType": "",
        "actorId": "",
        "timestamp": 0,
        "messageParameters": []
    }

    url = "{}/ocs/v2.php/apps/spreed/api/v1/chat/{}".format(server, channelId)
    # print(url)
    payload = json.dumps(data);

    headers = {'content-type': 'application/json','accept': 'application/json', 'OCS-APIRequest': 'true'}
    resp = requests.post(url, data=payload, headers=headers, auth=(username, password))
    print(resp)
    # print(resp.text)

NextcloudTalkSendMessage('7vijpxv5', 'Hello From Python')

How can we send files?

@arturrocha, are you referring to sending files to webhook (to Talk) or in response of webhook request? If first, the API for webhooks for Talk could implement two formats for sending data (to webhook, Talk) as json and as multipart-form-data, where the second can be used to send complex data including files.

oneWaveAdrian commented 2 years ago

Does @David-Development's python script still work? Double checked all variables but all I get is a 404 which would mean the chat was not found. However I copied the important variables such as token and URL from the network tab in the browser, so they have to be correct - what's the issue/missing setting here?

I would need incoming webhooks to display post messages from external tools such as gitlab/uptimerobot etc. in a Talk chat

erik78se commented 2 years ago

Whats the status on this feature?

I agree on the arguments against needing a username/password to do this since its an obvious security issue to pass around full login capabilities to enable simple message posting.

I'm super keep on getting this functionality available since it would allow me to replace Slack for this purpose.

mahmoudajawad commented 2 years ago

I'm super keep on getting this functionality available since it would allow me to replace Slack for this purpose.

Same here. I'm looking to replace Teams.

jancborchardt commented 1 year ago

Some examples from other projects:

nickvergessen commented 1 year ago

All done

mkrecek234 commented 8 months ago

@nickvergessen Sorry, but I still struggle with the implementation of the new webhook feature. The key logic in all other chat solutions like Slack, MS Teams etc. is that every user can create for example a webhook (i.e. a random tokenized URL to authenticate) that can then RECEIVE messages from other applications and let them post messages into a chat group that the user chooses.

So the steps are: 1) (Any) user can create a receiving webhook and chooses a chat group where it is allowed to send to - he receives a tokenized URL by Nextcloud Talk 2) The URL then can be inserted into other apps, like slack who then creates a simple JSON POST to that URL to send messages.

Am I wrong that this is not yet possible? Or that kind of webhook has to be created by the admin? If so, how? (I don't want the webhook to send user chat messages to the outside, only be used to post messages from external apps into defined a defined chat group)

mkrecek234 commented 8 months ago

To add on that: Why not just setup an app username and password and pass this to Slack etc.? Simply because that would allow any third-party service to access my WHOLE Nextcloud and not just push messages to my Talk group

nickvergessen commented 8 months ago

every user can create for example a webhook (i.e. a random tokenized URL to authenticate) that can then RECEIVE messages from other applications and let them post messages

This is still an open item on the list.

I don't want the webhook to send user chat messages to the outside, only be used to post messages from external apps into defined a defined chat group

I did thinks like this already in couple of places and e.g. we post "notification chat messages" when something was reported in our security bug bounty program. The easiest sample is the one from: https://github.com/nextcloud/spreed/pull/9458

The code inside the if ($config['server']) { is what allows to post a message to a chat. Not leaking messages of users to the outside is also easily possible. When installing the bot with the occ command specify only the response feature talk:bot:install --feature=response. The bot will then only be able to post messages but never receive messages:

  -f, --feature=FEATURE  Specify the list of features for the bot
                         - webhook: The bot receives posted chat messages as webhooks
                         - response: The bot can post messages and reactions as a response
                         - none: When all features should be disabled for the bot (multiple values allowed)

To add on that: Why not just setup an app username and password and pass this to Slack etc.? Simply because that would allow any third-party service to access my WHOLE Nextcloud and not just push messages to my Talk group

Exactly why we did all this :)