MattermostV2

Mattermost is an open-source, self-hostable online chat service with file sharing, search, and integrations. It is designed as an internal chat for organizations and companies.

Messaging and Conferencing · Mattermost

Details

IDMattermostV2
ProviderMattermost Inc
CategoryMessaging and Conferencing
From Version6.10.0
Docker Imagedemisto/netutils:1.0.0.10187688
Supported ModulesAgentix XSIAM

README

Mattermost is an open-source, self-hostable online chat service with file sharing, search, and integrations. It is designed as an internal chat for organizations and companies.
This integration was integrated and tested with version 4.0.0 of Mattermost API.

Some changes have been made that might affect your existing content.
If you are upgrading from a previous version of this integration, see Breaking Changes.

Configure Mattermost v2 in Cortex

Parameter Description Required
Server URL   True
Bot Access Token The Bot Access Token to use for connection. True
Personal Access Token The Personal Access Token to use for connection. True
Team Name   True
Default Notifications Channel If Notifications Channel name is not specified, will send notification to incidentNotificationChannel channel. False
Enable Incident Mirroring   False
Allow external users to create incidents via DM.   False
Types of Notifications to Send Notifications of type ‘externalAskSubmit’ or ‘externalFormSubmit’ are not configurable because they are required to allow Ask tasks to be sent correctly. False
Long running instance. Required for investigation mirroring and direct messages.   False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

mattermost-get-team


Gets a team’s details.

Required Permissions

Must be authenticated and have the view_team permission.

Base Command

mattermost-get-team

Input

Argument Name Description Required
team_name The name of the team to retrieve. Required

Context Output

Path Type Description
Mattermost.Team.id String The ID of the team.
Mattermost.Team.create_at Unknown When was the team created.
Mattermost.Team.update_at Unknown When was the team updated.
Mattermost.Team.delete_at Unknown When was the team deleted.
Mattermost.Team.display_name String The display name of the team.
Mattermost.Team.name String The name of the team.
Mattermost.Team.description String The description of the team.
Mattermost.Team.email String The email of the team.
Mattermost.Team.type String The type of the team.
Mattermost.Team.company_name String The company name of the team.
Mattermost.Team.allowed_domains String The allowed domains of the team.
Mattermost.Team.invite_id String The allowed domains of the team.
Mattermost.Team.allow_open_invite Unknown Does the team allow open invites.
Mattermost.Team.scheme_id String The scheme ID of the team.
Mattermost.Team.policy_id String The policy ID of the team.

Command example

!mattermost-get-team team_name=panw

Context Example

{
    "Mattermost": {
        "Team": {
            "allow_open_invite": false,
            "allowed_domains": "",
            "cloud_limits_archived": false,
            "company_name": "",
            "create_at": 1696486762638,
            "delete_at": 0,
            "description": "",
            "display_name": "PANW",
            "email": "rrapoport@example.com",
            "group_constrained": false,
            "id": "6ie46zmi4fdqiqqe7p5gfki9hr",
            "invite_id": "ocoh4fcrw7dzxgfu5bdtqpy7cr",
            "name": "panw",
            "policy_id": null,
            "scheme_id": "",
            "type": "O",
            "update_at": 1696486762638
        }
    }
}

Human Readable Output

Team details

allow_open_invite allowed_domains cloud_limits_archived company_name create_at delete_at description display_name email group_constrained id invite_id name policy_id scheme_id type update_at
false   false   1696486762638 0   PANW email false id id panw     O 1696486762638

mattermost-list-channels


Lists channels.

Required Permissions

manage_system

Base Command

mattermost-list-channels

Input

Argument Name Description Required
team The name of the team to list channels from. Default is the team name from the integration configuration. Optional
include_private_channels Whether to include private channels. Default is false. Possible values are: true, false. Optional
page The page number to retrieve. Default value is 0. Optional
page_size The size of the page to retrieve. Default value is 50. Optional
limit How many results to retrieve. Will override the page and page_size arguments if given. Optional

Context Output

Path Type Description
Mattermost.Channel.id String The ID of the channel.
Mattermost.Channel.create_at Unknown When was the channel created.
Mattermost.Channel.update_at Unknown When was the channel updated.
Mattermost.Channel.delete_at Unknown When was the channel deleted.
Mattermost.Channel.display_name String The display name of the channel.
Mattermost.Channel.name String The name of the channel.
Mattermost.Channel.description String The description of the channel.
Mattermost.Channel.header String The header of the channel.
Mattermost.Channel.purpose String The purpose of the channel.
Mattermost.Channel.last_post_at Unknown When was the last post to the channel made.
Mattermost.Channel.total_msg_count Unknown The total massage count of the channel.
Mattermost.Channel.extra_update_at Unknown When was the channel updated.
Mattermost.Channel.creator_id String The creator ID of the channel.

Command example

!mattermost-list-channels limit=2 include_private_channels=true

Context Example

{
    "Mattermost": {
        "Channel": [
            {
                "create_at": 1697024204532,
                "creator_id": "creator_id",
                "delete_at": 0,
                "display_name": "Name",
                "extra_update_at": 0,
                "group_constrained": null,
                "header": "",
                "id": "id",
                "last_post_at": 1712503619042,
                "last_root_post_at": 1712503619042,
                "name": "name",
                "policy_id": null,
                "props": null,
                "purpose": "",
                "scheme_id": null,
                "shared": null,
                "team_id": "team_id",
                "total_msg_count": 58,
                "total_msg_count_root": 56,
                "type": "O",
                "update_at": 1697024204532
            },
            {
                "create_at": 1696486762650,
                "creator_id": "",
                "delete_at": 0,
                "display_name": "Off-Topic",
                "extra_update_at": 0,
                "group_constrained": null,
                "header": "",
                "id": "id",
                "last_post_at": 1712501916866,
                "last_root_post_at": 1712501916866,
                "name": "off-topic",
                "policy_id": null,
                "props": null,
                "purpose": "",
                "scheme_id": null,
                "shared": null,
                "team_id": "team_id",
                "total_msg_count": 4,
                "total_msg_count_root": 4,
                "type": "O",
                "update_at": 1696486762650
            }
        ]
    }
}

Human Readable Output

Channels

name display_name type id
name Display_Name O id
off-topic Off-Topic O id

mattermost-list-private-channels-for-user


Get all the private channels on a team for a user.

Required Permissions

Logged in as the user, or have edit_other_users permission, and view_team permission for the team.

Base Command

mattermost-list-private-channels-for-user

Input

Argument Name Description Required
team_name The name of the team to list channels from. Default is the team name from the integration configuration. Optional
user_id User ID to get channel memberships for. Required

Context Output

Path Type Description
Mattermost.User.Channels Unknown List of channels where the user is member of.
Mattermost.User.id String The ID of the user.

Command example

!mattermost-list-private-channels-for-user user_id=user_id team_name=panw

Context Example

{
    "Mattermost": {
        "User": [
            {
                "channel_id": "channel_id",
                "user_id": "user_id",
                "roles": "Admin",
                "last_viewed_at": 0,
                "msg_count": 0,
                "mention_count": 0,
                "notify_props": {},
                "last_update_at": 0
            }
        ]
    }
}

Human Readable Output

Channels for UserName

name display_name type id
name Display_Name O channel_id

mattermost-create-channel


Creates a channel.

Required Permissions

If creating a public channel, create_public_channel permission is required. If creating a private channel, create_private_channel permission is required.

Base Command

mattermost-create-channel

Input

Argument Name Description Required
display_name The display name of the channel to create. Required
name The name of the channel to create. Required
type The type of the channel to create. Possible values are: public, private. Default is public. Optional
purpose The purpose of the channel to create. Optional
header The header of the channel to create. Optional
team The team name of the channel to create. Default is the team name from the integration configuration. Optional

Context Output

Path Type Description
Mattermost.Channel.id String The ID of the channel.
Mattermost.Channel.create_at Unknown When was the channel created.
Mattermost.Channel.update_at Unknown When was the channel updated.
Mattermost.Channel.delete_at Unknown When was the channel deleted.
Mattermost.Channel.display_name String The display name of the channel.
Mattermost.Channel.name String The name of the channel.
Mattermost.Channel.description String The description of the channel.
Mattermost.Channel.header String The header of the channel.
Mattermost.Channel.purpose String The purpose of the channel.
Mattermost.Channel.last_post_at Unknown When was the last post to the channel made.
Mattermost.Channel.total_msg_count Unknown The total massage count of the channel.
Mattermost.Channel.extra_update_at Unknown When was the channel updated.
Mattermost.Channel.creator_id String The creator ID of the channel.
Mattermost.Channel.scheme_id String The scheme ID of the channel.

Command example

!mattermost-create-channel display_name=channel_name name=channel_name type=Private

Context Example

{
    "Mattermost": {
        "Channel": {
            "create_at": 1712649608411,
            "creator_id": "creator_id",
            "delete_at": 0,
            "display_name": "channel_name",
            "extra_update_at": 0,
            "group_constrained": null,
            "header": "",
            "id": "id",
            "last_post_at": 1712649608426,
            "last_root_post_at": 1712649608426,
            "name": "channel_name",
            "policy_id": null,
            "props": null,
            "purpose": "",
            "scheme_id": null,
            "shared": null,
            "team_id": "team_id",
            "total_msg_count": 0,
            "total_msg_count_root": 0,
            "type": "P",
            "update_at": 1712649608411
        }
    }
}

Human Readable Output

Channel channel_name was created successfully.

mattermost-add-channel-member


Adds a channel member.

Required Permissions

No permissions required.

Base Command

mattermost-add-channel-member

Input

Argument Name Description Required
team The team name of the channel to add the user to. Default is the team name from the integration configuration. Optional
channel The name of the channel to add the user to. Required
user_id The ID of the user to add. Use the command ‘mattermost-list-users’ to fetch the user ID. Required

Context Output

There is no context output for this command.

mattermost-remove-channel-member


Removes a channel member.

Required Permissions

manage_public_channel_members permission if the channel is public. manage_private_channel_members permission if the channel is private.

Base Command

mattermost-remove-channel-member

Input

Argument Name Description Required
team The team name of the channel to add the user to. Default is the team name from the integration configuration. Optional
channel The channel name of the channel to remove the user from. Required
user_id The ID of the user to remove. Use the command ‘mattermost-list-users’ to fetch the user ID. Required

Context Output

There is no context output for this command.

mattermost-list-users


Lists users.

Required Permissions

Requires an active session and (if specified) membership to the channel or team being selected from.

Base Command

mattermost-list-users

Input

Argument Name Description Required
team_name The name of the team to filter users by. Optional
channel The name of the channel to filters users by. If mentioned, a team name must be mentioned as well. Optional
page The page number to retrieve. Should be provided with the page_size argument. Default value is 0. Optional
page_size The size of the page to retrieve. Should be provided with the page argument. Default value is 50. Optional
limit How many results to retrieve. If provided, overrides the page and page_size arguments. Optional

Context Output

Path Type Description
Mattermost.User.id String The ID of the user.
Mattermost.User.create_at Unknown When was the user created.
Mattermost.User.update_at Unknown When was the user updated.
Mattermost.User.delete_at Unknown When was the user deleted.
Mattermost.User.username String The username of the user.
Mattermost.User.auth_data String The authorization data of the user.
Mattermost.User.auth_service String The authorization service of the user.
Mattermost.User.email String The email of the user.
Mattermost.User.nickname String The nickname of the user.
Mattermost.User.first_name Unknown The first name of the user.
Mattermost.User.last_name Unknown The last name of the user.
Mattermost.User.position Unknown The position of the user.
Mattermost.User.roles String The roles of the channel.
Mattermost.User.locale String The locale of the channel.
Mattermost.User.timezone Unknown The timezone of the user.

Command example

!mattermost-list-users limit=2 team_name=panw

Context Example

{
    "Mattermost": {
        "User": [
            {
                "auth_data": "",
                "auth_service": "",
                "create_at": 1696486752272,
                "delete_at": 0,
                "disable_welcome_email": false,
                "email": "email",
                "first_name": "",
                "id": "id",
                "last_name": "",
                "locale": "en",
                "nickname": "",
                "position": "",
                "roles": "system_admin system_user",
                "timezone": {
                    "automaticTimezone": "Asia/Jerusalem",
                    "manualTimezone": "",
                    "useAutomaticTimezone": "true"
                },
                "update_at": 1696486762658,
                "username": "admin"
            },
            {
                "auth_data": "",
                "auth_service": "",
                "create_at": 1696500307646,
                "delete_at": 0,
                "disable_welcome_email": false,
                "email": "email",
                "first_name": "",
                "id": "id",
                "last_name": "",
                "locale": "en",
                "nickname": "",
                "position": "",
                "roles": "system_user system_admin",
                "timezone": {
                    "automaticTimezone": "Asia/Jerusalem",
                    "manualTimezone": "",
                    "useAutomaticTimezone": "true"
                },
                "update_at": 1697354262697,
                "username": "username"
            }
        ]
    }
}

Human Readable Output

Users

username email role id
admin admin@admin.com   8a6t7whumbdbxrawretujh6rre
dev admin@ddev.com   o9hpcwz73fdwxe9adue8jxo16o

mattermost-send-file


Sends a file.

Required Permissions

Must have upload_file permission.

Base Command

mattermost-send-file

Command example

!mattermost-send-file message=check entry_id=85@109 channel=test

Input

Argument Name Description Required
team_name The team name of the channel to send the file to. Default is the team name from the integration configuration. Optional
channel The channel name of the channel to send the file to. Cannot be combined with the to argument. Optional
message The message to send to the channel along with the file. Required
entry_id The entry ID of the file. Required
to The username or email of the user to send the file to. Optional

Context Output

There is no context output for this command.

Human Readable Output

file test.txt was successfully sent to channel test

send-notification


Send a message using a chatbot app.

Required Permissions

Must have create_post permission for the channel the post is being created in.

Base Command

send-notification

Input

Argument Name Description Required
message The message to send. Required
channel The channel name to send the notification to. Default value is the channel configuration parameter. Optional
entry An entry ID to send as a link. Optional
to The username or email of the user to send the file to. Optional
ignoreAddURL Adds the War Room link to the message. Possible values are: true, false. Optional
mattermost_ask The message as a JSON for asking questions to the user. Default value is false. Possible values are: true, false. Optional

Context Output

There is no context output for this command.

mattermost-close-channel


Closes a channel.

Required Permissions

delete_public_channel permission if the channel is public. delete_private_channel permission if the channel is private, or has manage_system permission.

Base Command

mattermost-close-channel

Input

Argument Name Description Required
team_name The team name of the channel to close. Default value is the team name from the integration configuration. Optional
channel The channel name of the channel to close. If not provided, the mirrored investigation channel is archived (if the channel exists). Optional

Context Output

There is no context output for this command.

close-channel


Closes a mirrored MatterMost channel. If not provided, the mirrored investigation channel is archived (if the channel exists).

Required Permissions

delete_public_channel permission if the channel is public. delete_private_channel permission if the channel is private, or has manage_system permission.

Base Command

close-channel

Input

Argument Name Description Required
team_name The team name of the channel to delete. Default value is the team name from the integration configuration. Optional
channel The channel name of the channel to close. Optional

Context Output

There is no context output for this command.

mirror-investigation


Mirrors the investigation between Mattermost and the Cortex XSOAR War Room.

Required Permissions

No permissions required.

Base Command

mirror-investigation

Input

Argument Name Description Required
type The mirroring type. Can be “all”, which mirrors everything, “chat”, which mirrors only chats (not commands), or “none”, which stops all mirroring. Possible values are: all, chat, none. Default is all. Optional
autoclose Whether the channel is auto-closed when an investigation is closed. Possible values are: true, false. Default is true. Optional
direction The mirroring direction. Possible values are: Both, FromDemisto, ToDemisto. Default is Both. Optional
channel The name of the channel. The default is “incident-<incidentID>”. Optional
kickAdmin Whether to remove the admin from the newly created channel. Default value is false. Possible values are: true, false. Default is false. Optional
mirrorTo Mirrors the investigation to a group (private channel) or a public channel. Default is group. Possible values are: group, channel. Default is group. Optional

Context Output

There is no context output for this command.

close-channel


Closes a mirrored Mattermost channel. If not provided, the mirrored investigation channel is archived (if the channel exists).

Base Command

close-channel

Input

Argument Name Description Required
team_name The team name of the channel to delete. Default value is the team name from the integration configuration. Optional
channel_name The channel name of the channel to delete. Optional

Context Output

There is no context output for this command.

mattermost-mirror-investigation


Mirrors the investigation between Mattermost and the Cortex XSOAR War Room.

Required Permissions

No permissions channel.

Base Command

mattermost-mirror-investigation

Input

Argument Name Description Required
type The mirroring type. Can be “all”, which mirrors everything, “chat”, which mirrors only chats (not commands), or “none”, which stops all mirroring. Possible values are: all, chat, none. Default is all. Optional
autoclose Whether the channel is auto-closed when an investigation is closed. Possible values are: true, false. Default is true. Optional
direction The mirroring direction. Possible values are: Both, FromDemisto, ToDemisto. Default is Both. Optional
channel The name of the channel. The default is “incident-<incidentID>”. Optional
kickAdmin Whether to remove the admin from the newly created channel. Default value is false. Possible values are: true, false. Default is false. Optional
mirrorTo Mirrors the investigation to a group (private channel) or a public channel. Possible values are: group, channel. Default is group. Optional

Context Output

There is no context output for this command.

mattermost-list-groups


Lists groups. Groups are available on Enterprise and Professional plans. Minimum server version: 5.11

Required Permissions

No permissions required.

Base Command

mattermost-list-groups

Input

Argument Name Description Required
page The page number to retrieve. Default value is 0. Optional
page_size The size of the page to retrieve. Default value is 50. Optional
limit How many results to retrieve. Will override the page and page_size arguments if given. Optional
group Search for a specific user by this pattern. Optional

Context Output

Path Type Description
Mattermost.Groups.id String The ID of the group.
Mattermost.Groups.create_at Unknown The datetime the group was created.
Mattermost.Groups.update_at Unknown The datetime the group was updated.
Mattermost.Groups.delete_at Unknown The datetime the group was deleted.
Mattermost.Groups.display_name String The display name of the group.
Mattermost.Groups.name String The name of the group.
Mattermost.Groups.description String The description of the group.
Mattermost.Groups.source String The source of the group.
Mattermost.Groups.remote_id String The remote ID of the group.
Mattermost.Groups.has_syncables boolean If the group has any syncables.

Command example


#### Context Example

```json
{
    "Mattermost": {
        "Groups": [
            {
                "id": "0815xyz",
                "name": "name",
                "display_name": "display name",
                "description": "description",
                "source": "custom",
                "remote_id": null,
                "create_at": 0,
                "update_at": 0,
                "delete_at": 0,
                "has_syncables": false
            },
            {
                "id": "0815abc",
                "name": "name",
                "display_name": "display name",
                "description": "",
                "source": "custom",
                "remote_id": null,
                "create_at": 0,
                "update_at": 0,
                "delete_at": 0,
                "has_syncables": false
            }
        ]
    }
}

Human Readable Output

Groups

name display_name description id
name display name   0815abc
name display name description 0815xyz

mattermost-list-group-members


Lists group members. Groups are available on Enterprise and Professional plans. Minimum server version: 5.11

Required Permissions

Must have manage_system permission.

Base Command

mattermost-list-group-members

Input

Argument Name Description Required
page The page number to retrieve. Default value is 0. Optional
page_size The size of the page to retrieve. Default value is 50. Optional
limit How many results to retrieve. Will override the page and page_size arguments if given. Optional
group_id The group ID of the user group to list members for. Use the command ‘mattermost-list-groups’ to fetch the group ID. Required

Context Output

Path Type Description
Mattermost.Members.id String The ID of the group member.
Mattermost.Members.first_name String The first name of the group member.
Mattermost.Members.last_name String The last name of the group member.
Mattermost.Members.username String The username of the group member.

Command example

!mattermost-list-group-members group_id=group_id

Human Readable Output

Group moi9ygz8qby1pr1xgkcfuqww9r members (2/2)

username email id
username1 admin@admin.com 8a6t7whumbdbxrawretujh6rre
username2 admin@ddev.com o9hpcwz73fdwxe9adue8jxo16o

mattermost-add-group-member


Add group member(s). Groups are available on Enterprise and Professional plans. Minimum server version: 6.3

Required Permissions

Must have custom_group_manage_members permission for the given group.

Base Command

mattermost-add-group-member

Input

Argument Name Description Required
group_id The group ID of the user group to list members for. Use the command ‘mattermost-list-groups’ to fetch the group ID. Required
user_ids A comma-separated list of IDs of the users to add. Use the command ‘mattermost-list-users’ to fetch the user ID(s). Required

Context Output

There is no context output for this command.

Command example

!mattermost-add-group-member group_id=group_id user_ids=user_ids

Human Readable Output

The member username was added to the group successfully, with group ID: moi9ygz8qby1pr1xgkcfuqww9r

mattermost-remove-group-member


Remove group member(s). Groups are available on Enterprise and Professional plans. Minimum server version: 6.3

Required Permissions

Must have custom_group_manage_members permission for the given group.

Base Command

mattermost-remove-group-member

Input

Argument Name Description Required
group_id The group ID of the user group to list members for. Use the command ‘mattermost-list-groups’ to fetch the group ID. Required
user_ids A comma-separated list of IDs of the users to remove. Use the command ‘mattermost-list-users’ to fetch the user ID(s). Required

Context Output

There is no context output for this command.

Command example

!mattermost-remove-group-member group_id=group_id user_ids=user_ids

Human Readable Output

The member username was removed from group successfully, with group ID: moi9ygz8qby1pr1xgkcfuqww9r

mattermost-set-channel-role


Update a user’s roles for a channel. The user has to already be a member of the channel.

Required Permissions

Must have manage_channel_roles permission for the channel.

Base Command

mattermost-set-channel-role

Input

Argument Name Description Required
channel_id The channel ID to set the role in. Use the command ‘mattermost-list-channels’ to fetch the channel ID. Required
user_id The ID of the user to set role for. Use the command ‘mattermost-list-users’ to fetch the user ID. Required
role The role to set for the user. Possible values are: Admin, Member. Default is Member. Optional

Context Output

There is no context output for this command.

Breaking changes from the previous version of this integration - Mattermost v2

A new required configuration parameters was added: Bot Access Token.

Configuration parameters

  • url — Server URL (required)
  • bot_access_token — (required)
  • personal_access_token — (required)
  • team_name — Team Name (required)
  • notification_channel — Default Notifications Channel
  • mirroring — Enable Incident Mirroring
  • allow_incidents — Allow external users to create incidents via DM.
  • permitted_notifications — Types of Notifications to Send
  • longRunning — Long running instance. Required for investigation mirroring and direct messages.
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (18)

  • close-channel

    Closes a mirrored Mattermost channel. If not provided, the mirrored investigation channel is archived (if the channel exists).

  • mattermost-add-channel-member

    Adds a channel member.

  • mattermost-add-group-member

    Add group member(s). Groups are available on Enterprise and Professional plans. Minimum server version: 6.3

  • mattermost-close-channel

    Closes a channel.

  • mattermost-create-channel

    Creates a channel.

  • mattermost-get-team

    Gets a team's details.

  • mattermost-list-channels

    Lists channels.

  • mattermost-list-group-members

    Lists group members. Groups are available on Enterprise and Professional plans. Minimum server version: 5.11

  • mattermost-list-groups

    Lists groups. Groups are available on Enterprise and Professional plans. Minimum server version: 5.11

  • mattermost-list-private-channels-for-user

    Get all the private channels on a team for a user.

  • mattermost-list-users

    Lists users.

  • mattermost-mirror-investigation

    Mirrors the investigation between Mattermost and the Cortex XSOAR War Room.

  • mattermost-remove-channel-member

    Removes a channel member.

  • mattermost-remove-group-member

    Remove group member(s). Groups are available on Enterprise and Professional plans. Minimum server version: 6.3

  • mattermost-send-file

    Sends a file.

  • mattermost-set-channel-role

    Update a user's roles for a channel. The user has to already be a member of the channel.

  • mirror-investigation

    Mirrors the investigation between Mattermost and the Cortex XSOAR War Room.

  • send-notification

    Send a message using a chatbot app.

import json
from unittest.mock import patch

import demistomock as demisto
import pytest
from freezegun import freeze_time
from MattermostV2 import (
    INCIDENT_NOTIFICATION_CHANNEL,
    add_channel_member_command,
    add_group_member_command,
    answer_question,
    close_channel_command,
    create_channel_command,
    create_incidents,
    event_handler,
    extract_entitlement,
    get_channel_id_from_context,
    get_channel_id_to_send_notif,
    get_team_command,
    get_war_room_url,
    handle_posts,
    handle_text_received_from_mm,
    list_channels_command,
    list_groups_command,
    list_group_members_command,
    list_private_channels_for_user_command,
    list_users_command,
    mirror_investigation,
    remove_channel_member_command,
    remove_group_member_command,
    send_file_command,
    send_notification,
    set_channel_role_command,
)


def util_load_json(path):
    with open(path, encoding="utf-8") as f:
        return json.loads(f.read())


def http_mock(
    method: str,
    url_suffix: str = "",
    full_url: str = "",
    params: dict = {},
    data: dict = {},
    files: dict = {},
    json_data: dict = {},
    headers: dict = {},
):
    if "bot_access_token" in headers.get("Authorization", ""):
        if url_suffix == "/api/v4/users/me":
            return util_load_json("test_data/get_bot_response.json")
        if url_suffix == "/api/v4/posts":
            return util_load_json("test_data/create_post_response.json")

    if url_suffix == "/api/v4/teams/name/team_name":
        return util_load_json("test_data/get_team_response.json")
    elif url_suffix == "/api/v4/teams/team_id/channels" or url_suffix == "/api/v4/teams/team_id/channels/private":
        return util_load_json("test_data/list_channels_response.json")
    elif url_suffix == "/api/v4/users/user_id/teams/team_id/channels":
        return util_load_json("test_data/list_channel_memberships_for_user.json")
    elif url_suffix == "/api/v4/channels":
        return util_load_json("test_data/create_channel_response.json")
    elif url_suffix == "/api/v4/users":
        return util_load_json("test_data/list_users_response.json")
    elif url_suffix == "/api/v4/files":
        return util_load_json("test_data/send_file_response.json")
    elif (
        url_suffix == "/api/v4/users/email/user_email"
        or url_suffix == "/api/v4/users/username/username"
        or url_suffix == "/api/v4/users/me"
        or url_suffix == "/api/v4/users/user_id"
    ):
        return util_load_json("test_data/list_users_response.json")[0]
    elif url_suffix == "/api/v4/users/username/username2":
        return util_load_json("test_data/list_users_response.json")[1]
    elif url_suffix == "/api/v4/channels/direct":
        channel = util_load_json("test_data/create_channel_response.json")
        channel["id"] = "direct_id"
        channel["type"] = "D"
        return channel
    elif url_suffix == "/api/v4/channels/group":
        channel = util_load_json("test_data/create_channel_response.json")
        channel["id"] = "group_id"
        channel["type"] = "G"
        return channel
    elif url_suffix == "/api/v4/groups":
        return util_load_json("test_data/list_groups_response.json")
    elif url_suffix == "/api/v4/groups/group_id/members":
        return util_load_json("test_data/list_group_members_response.json")
    else:
        return {}


@pytest.fixture(autouse=True)
def ws_client(mocker):
    from MattermostV2 import WebSocketClient

    return WebSocketClient(
        base_url="mock url",
        verify=True,
        proxy=False,
        token="personal_access_token",
    )


@pytest.fixture(autouse=True)
def http_client(mocker):
    from MattermostV2 import HTTPClient

    headers = {"Authorization": "Token mock"}
    http_client = HTTPClient(
        base_url="mock url",
        headers=headers,
        verify=True,
        proxy=False,
        bot_access_token="bot_access_token",
        personal_access_token="personal_access_token",
        team_name="team_name",
        notification_channel="notification_channel",
    )
    mocker.patch.object(http_client, "_http_request", side_effect=http_mock)
    return http_client


def test_get_team_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running get_team_command with a team name.
    Then: Ensure we get the result.
    """
    args = {"team_name": "team_name"}
    results = get_team_command(http_client, args)
    assert results.outputs.get("name", "") == "team_name"


def test_list_channels_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running list_channels_command with a team name.
    Then: Ensure we get the result.
    """
    args = {"team_name": "team_name", "include_private_channels": True}
    results = list_channels_command(http_client, args)
    assert results.outputs[0].get("name") == "name"
    assert len(results.outputs) == 2


def test_list_private_channels_for_user_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running list_private_channels_for_user_command with a team name and user id.
    Then: Ensure we get the result.
    """
    args = {"team_name": "team_name", "user_id": "user_id"}
    results = list_private_channels_for_user_command(http_client, args)
    assert results.outputs["channels"][0].get("name") == "name"
    assert len(results.outputs["channels"]) == 1


def test_create_channel_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running create_channel_command with a team name.
    Then: Ensure we get the result.
    """
    args = {
        "team_name": "team_name",
        "name": "channel_name",
        "display_name": "display_name",
        "type": "Public",
        "purpose": "purpose",
        "header": "header",
    }
    results = create_channel_command(http_client, args)
    assert results.outputs.get("name") == "name"


def test_add_channel_member_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running add_channel_member_command with a team name.
    Then: Ensure we get the result.
    """
    args = {
        "team_name": "team_name",
        "channel_name": "channel_name",
        "user_id": "user_id",
    }
    results = add_channel_member_command(http_client, args)
    assert "The member username was added to the channel successfully" in results.readable_output


def test_remove_channel_member_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running remove_channel_member_command with a team name.
    Then: Ensure we get the result.
    """
    args = {
        "team_name": "team_name",
        "channel_name": "channel_name",
        "user_id": "user_id",
    }
    results = remove_channel_member_command(http_client, args)
    assert "The member username was removed from the channel successfully." in results.readable_output


def test_list_users_command(http_client):
    """
    Given: A mock MatterMost client.
    When: Running list_users_command with a team name.
    Then: Ensure we get the result.
    """
    args = {
        "team_name": "team_name",
        "channel_id": "channel_id",
    }
    results = list_users_command(http_client, args)
    assert results.outputs[0].get("first_name") == "first_name"


def test_close_channel_command_no_mirror(http_client):
    """
    Given: A mock MatterMost client.
    When: Running close_channel_command with a team name.
    Then: Ensure we get the result.
    """
    args = {
        "team_name": "team_name",
        "channel": "channel_name",
    }
    results = close_channel_command(http_client, args)
    assert "The channel channel_name was delete successfully." in results.readable_output


def test_close_channel_command_mirror(http_client, mocker):
    """
    Given: A mock MatterMost client.
    When: Running close_channel_command with a team name.
    Then: Ensure we get the result, and  was called only once with the first mirror
    """
    args = {
        "team_name": "team_name",
        "channel": "channel_name",
    }

    import MattermostV2

    MattermostV2.CACHE_EXPIRY = False
    MattermostV2.CACHED_INTEGRATION_CONTEXT = ""
    mock_integration_context = {
        "mirrors": json.dumps(
            [
                {
                    "channel_name": "Channel1",
                    "team_id": "team_id",
                    "channel_id": "channel_id",
                    "mirrored": False,
                    "investigation_id": "Incident123",
                    "mirror_direction": "toDemisto",
                    "auto_close": True,
                    "mirror_type": "all",
                },
                {
                    "channel_name": "Channel2",
                    "team_id": "team_id",
                    "channel_id": "channel_id_different_channel",
                    "mirrored": True,
                    "investigation_id": "Incident123",
                    "mirror_direction": "both",
                    "auto_close": True,
                    "mirror_type": "chat",
                },
            ]
        )
    }
    mocker.patch("MattermostV2.get_integration_context", return_value=mock_integration_context)
    mocker.patch.object(demisto, "investigation", return_value={"id": "Incident123"})
    mocker.patch.object(demisto, "mirrorInvestigation")
    results = close_channel_command(http_client, args)

    demisto.mirrorInvestigation.assert_called_once_with("Incident123", "none:toDemisto", True)
    assert "The channel channel_name was delete successfully." in results.readable_output


def test_send_file_command(http_client, mocker):
    """
    Given: A mock MatterMost client.
    When: Running send_file_command with a team name.
    Then: Ensure we get the result.
    """
    expected_file_info = {"name": "test_file.txt", "path": "/path/to/test_file.txt"}
    mocker.patch("MattermostV2.demisto.getFilePath", return_value=expected_file_info)
    mocker.patch.object(http_client, "send_file_request", return_value=util_load_json("test_data/send_file_response.json"))

    args = {
        "team_name": "team_name",
        "channel": "channel_name",
    }
    send_file_command(http_client, args)


def test_get_channel_id_to_send_notif(http_client, mocker):
    """
    Given: A mock MatterMost client.
    When: Running get_channel_id_to_send_notif for a single user.
    Then: Ensure we get the result.
    """
    results = get_channel_id_to_send_notif(http_client, ["username"], "channel_name", "investigation_id")
    assert results == "direct_id"


def test_get_channel_id_to_send_notif_multiple_users(http_client, mocker):
    """
    Given: A mock MatterMost client.
    When: Running get_channel_id_to_send_notif for two users.
    Then: Ensure we get the result.
    """
    results = get_channel_id_to_send_notif(http_client, ["username", "username2"], "channel_name", "investigation_id")
    assert results == "group_id"


def test_get_channel_id_from_context(mocker):
    """
    Given: A mock MatterMost client.
    When: Running get_channel_id_from_context.
    Then: Ensure we get the result.
    """
    import MattermostV2

    MattermostV2.CACHE_EXPIRY = False
    MattermostV2.CACHED_INTEGRATION_CONTEXT = ""
    mock_integration_context = {
        "mirrors": json.dumps(
            [
                {
                    "channel_name": "Channel1",
                    "team_id": "team_id",
                    "channel_id": "ID1",
                    "investigation_id": "Incident123",
                    "mirror_direction": "both",
                    "auto_close": True,
                },
                {
                    "channel_name": "Channel2",
                    "team_id": "team_id",
                    "channel_id": "ID2",
                    "investigation_id": "Incident123",
                    "mirror_direction": "both",
                    "auto_close": True,
                },
            ]
        )
    }
    mocker.patch("MattermostV2.get_integration_context", return_value=mock_integration_context)
    results = get_channel_id_from_context("Channel1", "Incident123")
    assert results


def test_save_entitlement():
    """
    Given:
    - arguments.
    When:
    - Calling the save_entitlement function.
    Then:
    - Validate that the mocked functions were called with the expected arguments
    """
    entitlement = "Test Entitlement"
    message_id = "123"
    reply = "Test Reply"
    expiry = "2023-09-09"
    default_response = "Default Response"
    to_id = "user@example.com"
    OBJECTS_TO_KEYS = {
        "mirrors": "investigation_id",
        "messages": "entitlement",
    }

    with (
        patch("MattermostV2.get_integration_context") as mock_get_integration_context,
        patch("MattermostV2.set_to_integration_context_with_retries") as mock_set_integration_context,
    ):
        mock_get_integration_context.return_value = {"messages": []}
        fixed_timestamp = "2023-09-09T20:08:50Z"

        with freeze_time(fixed_timestamp):
            from MattermostV2 import save_entitlement

            save_entitlement(entitlement, message_id, reply, expiry, default_response, to_id)

        expected_data = {
            "messages": [
                {
                    "root_id": message_id,
                    "entitlement": entitlement,
                    "reply": reply,
                    "expiry": expiry,
                    "sent": fixed_timestamp,
                    "default_response": default_response,
                    "to_id": to_id,
                }
            ]
        }

        mock_get_integration_context.assert_called_once_with()
        mock_set_integration_context.assert_called_once_with(expected_data, OBJECTS_TO_KEYS)


@pytest.mark.parametrize(
    "entitlement, expected_result",
    [
        ("guid123@incident456|task789", ("guid123", "incident456", "task789")),  # Scenario 1: Full entitlement
        ("guid123@incident456", ("guid123", "incident456", "")),  # Scenario 2: No task ID
        ("guid123@", ("guid123", "", "")),  # Scenario 3: No incident ID or task ID
    ],
)
def test_extract_entitlement(entitlement, expected_result):
    """
    Test the extract_entitlement function.
    Given:
    - Input entitlement string.
    When:
    - Calling the extract_entitlement function with the given input entitlement.
    Then:
    - Validate that the function correctly extracts the entitlement components: guid, incident_id, and task_id.
    """
    result = extract_entitlement(entitlement)

    assert result == expected_result


def test_mirror_investigation_create_new_channel(http_client, mocker):
    """
    Given a mock client and relevant arguments,
    When calling the mirror_investigation function to create a new channel,
    Then validate that the function returns the expected CommandResults.
    """
    import MattermostV2

    MattermostV2.MIRRORING_ENABLED = True
    MattermostV2.LONG_RUNNING = True
    MattermostV2.SYNC_CONTEXT = True
    mocker.patch.object(demisto, "demistoUrls", return_value={"server": "mock_server_url"})

    # Test data
    args = {
        "type": "all",
        "direction": "Both",
        "channelName": "mirror-channel",
        "autoclose": True,
    }
    mock_integration_context = {
        "mirrors": json.dumps(
            [
                {
                    "channel_name": "Channel1",
                    "team_id": "team_id",
                    "channel_id": "channel_id",
                    "mirrored": False,
                    "investigation_id": "Incident123",
                    "mirror_direction": "toDemisto",
                    "auto_close": True,
                    "mirror_type": "all",
                },
                {
                    "channel_name": "Channel2",
                    "team_id": "team_id",
                    "channel_id": "channel_id",
                    "mirrored": True,
                    "investigation_id": "Incident123",
                    "mirror_direction": "both",
                    "auto_close": True,
                    "mirror_type": "chat",
                },
            ]
        )
    }
    mocker.patch("MattermostV2.get_integration_context", return_value=mock_integration_context)
    mocker.patch.object(demisto, "mirrorInvestigation")
    # Call the function
    result = mirror_investigation(http_client, **args)

    # Assert the result

    demisto.mirrorInvestigation.assert_called_once_with("1", "all:Both", True)
    assert "Investigation mirrored successfully" in result.readable_output


def test_send_notification_command_with_not_permitted_notif_type(http_client, mocker):
    """
    Given -
        client
    When -
        send message to channel
    Then -
        Validate that
    """
    import MattermostV2

    MattermostV2.PERMITTED_NOTIFICATION_TYPES = []
    mocker.patch.object(http_client, "send_notification_request", return_value={"id": "message_id"})
    result = send_notification(http_client, user_id="user1", message="Hello", to="channel1", messageType="not permitted")

    assert result == "Message type is not in permitted options. Received: not permitted"


def test_send_notification_command_with_generic_notif_channel_name(http_client, mocker):
    """
    Given -
        client
    When -
        send message to channel
    Then -
        Validate that
    """
    import MattermostV2

    MattermostV2.PERMITTED_NOTIFICATION_TYPES = ["incidentOpened"]
    mocker.patch.object(http_client, "send_notification_request", return_value={"id": "message_id"})
    mocker.patch.object(MattermostV2, "get_channel_id_from_context", return_value="channel_id")
    result = send_notification(
        http_client, user_id="user1", message="Hello", channel=INCIDENT_NOTIFICATION_CHANNEL, messageType="incidentOpened"
    )

    assert result.readable_output == "Message sent to MatterMost successfully. Message ID is: message_id"


def test_send_notification_command(http_client, mocker):
    """
    Given -
        client
    When -
        send message to channel
    Then -
        Validate that
    """
    mocker.patch.object(http_client, "send_notification_request", return_value={"id": "message_id"})
    result = send_notification(
        http_client,
        user_id="user1",
        message="Hello",
        to="channel1",
    )

    assert result.readable_output == "Message sent to MatterMost successfully. Message ID is: message_id"


def test_list_groups_command(http_client):
    """
    Given -
        client
        arguments
    When -
        list group
    Then -
        Validate that the function returns the expected CommandResults.
    """
    args = {
        "group": "user_group",
    }
    results = list_groups_command(http_client, args)
    assert results.outputs[0].get("id") == "group_id"


def test_list_group_members_command(http_client):
    """
    Given -
        client
        arguments
    When -
        list group members
    Then -
        Validate that the function returns the expected CommandResults.
    """
    args = {
        "group_id": "group_id",
    }
    results = list_group_members_command(http_client, args)
    assert len(results.outputs) == 1
    assert results.outputs[0]["username"] == "username"


def test_add_group_member_command(http_client):
    """
    Given -
        client
        arguments
    When -
        add group member
    Then -
        Validate that the function returns the expected CommandResults.
    """
    args = {"group_id": "group_id", "user_ids": "user_id"}
    results = add_group_member_command(http_client, args)
    assert results.readable_output == "The member username was added to the group successfully, with group ID: group_id"


def test_remove_group_member_command(http_client):
    """
    Given -
        client
        arguments
    When -
        remove group member
    Then -
        Validate that the function returns the expected CommandResults.
    """
    args = {"group_id": "group_id", "user_ids": "user_id"}
    results = remove_group_member_command(http_client, args)
    assert results.readable_output == "The member username was removed from the group successfully, with group ID: group_id"


def test_set_channel_role_command(http_client, mocker):
    """
    Given -
        client
        arguments
    When -
        remove user group member
    Then -
        Validate that the function returns the expected CommandResults.
    """
    args = {"channel_id": "channel_id", "user_id": "user_id", "role": "admin"}
    mocker.patch.object(http_client, "remove_group_member_request", return_value={"status": "ok"})
    results = set_channel_role_command(http_client, args)
    assert results.readable_output == "Set channel role for username successfully to Admin."


######### async tests #########


@pytest.mark.asyncio
async def test_handle_posts_regular_post(http_client, mocker):
    """
    Given:
    - Post payload.
    When:
    - Calling the handle_posts function.
    Then:
    - Validate that the mirror investigation func was called. only once, as one of the mirrors was already mirrored.
    """
    import MattermostV2

    payload = util_load_json("test_data/posted_data_user.json")
    mock_integration_context = {
        "mirrors": json.dumps(
            [
                {
                    "channel_name": "Channel1",
                    "team_id": "team_id",
                    "channel_id": "channel_id",
                    "mirrored": False,
                    "investigation_id": "Incident123",
                    "mirror_direction": "toDemisto",
                    "auto_close": True,
                    "mirror_type": "all",
                },
                {
                    "channel_name": "Channel2",
                    "team_id": "team_id",
                    "channel_id": "channel_id",
                    "mirrored": True,
                    "investigation_id": "Incident123",
                    "mirror_direction": "both",
                    "auto_close": True,
                    "mirror_type": "chat",
                },
            ]
        )
    }
    MattermostV2.CLIENT = http_client
    MattermostV2.CACHE_EXPIRY = False
    mocker.patch("MattermostV2.get_integration_context", return_value=mock_integration_context)
    mocker.patch("MattermostV2.handle_text_received_from_mm", return_value=None)
    mocker.patch.object(demisto, "mirrorInvestigation")
    await handle_posts(payload)
    demisto.mirrorInvestigation.assert_called_once_with("Incident123", "all:toDemisto", True)


@pytest.mark.asyncio
async def test_handle_text(mocker):
    """
    Given:
    - arguments.
    When:
    - Calling the handle_text_received_from_mm function.
    Then:
    - Validate that the `demisto.addEntry` method was called with the expected arguments
    """

    investigation_id = "123"
    text = "Hello, this is a test message"
    operator_email = "test@example.com"
    operator_name = "Test User"
    MESSAGE_FOOTER = "\n**From Mattermost**"

    with patch("MattermostV2.demisto") as mock_demisto:
        await handle_text_received_from_mm(investigation_id, text, operator_email, operator_name)
        mock_demisto.addEntry.assert_called_once_with(
            id=investigation_id, entry=text, username=operator_name, email=operator_email, footer=MESSAGE_FOOTER
        )


@pytest.mark.asyncio
async def test_event_handler_error(ws_client, mocker):
    """
    Given:
    - Error post payload.
    When:
    - Calling the handle_posts function.
    Then:
    - Validate that the demisto.error func was called.
    """
    error_payload = """{"status": "FAIL",
                     "seq_reply": 2,
                     "error": {"id": "some.error.id.here", "message": "Some error message here"
                               }
                     }"""
    error_mock = mocker.patch.object(demisto, "error")
    mocker.patch.object(demisto, "updateModuleHealth")

    await event_handler(ws_client, error_payload)

    assert error_mock.call_count == 1


@pytest.mark.asyncio
async def test_event_handler_bot_message(http_client, mocker):
    """
    Given:
    - Bot post payload.
    When:
    - Calling the handle_posts function.
    Then:
    - Validate that the demisto.debug func was called.
    """
    import MattermostV2

    MattermostV2.CLIENT = http_client
    bot_payload = util_load_json("test_data/posted_data_bot.json")
    mocker.patch.object(demisto, "updateModuleHealth")
    mocker.patch.object(demisto, "debug")

    await handle_posts(bot_payload)
    demisto.debug.assert_called_once_with("MM: Got a bot message. Will not mirror.")


@pytest.mark.asyncio
async def test_event_handler_direct_message(http_client, mocker):
    """
    Given:
    - dm post payload.
    When:
    - Calling the handle_posts function.
    Then:
    - Validate that the demisto.debug func was called.
    """
    import MattermostV2

    MattermostV2.CLIENT = http_client
    MattermostV2.ALLOW_INCIDENTS = True

    payload = util_load_json("test_data/posted_data_user.json")
    payload["data"]["channel_type"] = "D"
    mocker.patch.object(demisto, "updateModuleHealth")
    mocker.patch.object(demisto, "directMessage", return_value={})

    await handle_posts(payload)
    demisto.directMessage.assert_called_once_with("message", "", "", True)


def test_answer_question(http_client, mocker):
    """
    Test the answer_question function.
    Given:
    - A mocked question dictionary.
    When:
    - Calling the answer_question function with the mocked question.
    Then:
    - Validate that the function correctly handles the entitlement and returns the incident_id.
    """
    import MattermostV2

    MattermostV2.CLIENT = http_client
    mock_question = {"entitlement": "guid123@incident456|task789", "to_id": "123"}

    mocker.patch("MattermostV2.process_entitlement_reply")

    result = answer_question("Answer123", mock_question, "user@example.com")
    assert result == "incident456"


@pytest.mark.asyncio
async def test_create_incidents(mocker):
    """
    Given:
    - Incidents
    When:
    - Calling the create_incidents function.
    Then:
    - Validate that the demisto.createIncidents func was called.
    """

    mocker.patch.object(demisto, "createIncidents", return_value="nice")

    incidents = [{"name": "xyz", "details": "1.1.1.1,8.8.8.8"}]

    incidents_with_labels = [
        {
            "name": "xyz",
            "details": "1.1.1.1,8.8.8.8",
            "labels": [
                {"type": "Reporter", "value": "spengler"},
                {"type": "ReporterEmail", "value": "test@test.com"},
                {"type": "Source", "value": "Slack"},
            ],
        }
    ]

    data = await create_incidents(incidents, "spengler", "test@test.com", "demisto_user")

    incident_arg = demisto.createIncidents.call_args[0][0]
    user_arg = demisto.createIncidents.call_args[1]["userID"]

    assert incident_arg == incidents_with_labels
    assert user_arg == "demisto_user"
    assert data == "nice"


class TestGetWarRoomURL:
    def test_get_war_room_url_with_xsiam_from_incident_war_room(self, mocker):
        url = "https://example.com/WarRoom/INCIDENT-2930"
        expected_war_room_url = "https://example.com/incidents/war_room?caseId=2930"
        mocker.patch("MattermostV2.is_xsiam", return_value=True)
        mocker.patch.dict(demisto.callingContext, {"context": {"Inv": {"id": "INCIDENT-2930"}}})

        assert get_war_room_url(url) == expected_war_room_url

    def test_get_war_room_url_without_xsiam_from_incident_war_room(self, mocker):
        url = "https://example.com/WarRoom/INCIDENT-2930"
        mocker.patch("MattermostV2.is_xsiam", return_value=False)
        expected_war_room_url = "https://example.com/WarRoom/INCIDENT-2930"
        assert get_war_room_url(url) == expected_war_room_url

    def test_get_war_room_url_with_xsiam_from_alert_war_room(self, mocker):
        url = "https://example.com/WarRoom/ALERT-1234"
        mocker.patch("MattermostV2.is_xsiam", return_value=True)
        mocker.patch.dict(demisto.callingContext, {"context": {"Inv": {"id": "1234"}}})
        expected_war_room_url = (
            "https://example.com/incidents/alerts_and_insights?caseId=1234&action:openAlertDetails=1234-warRoom"
        )
        assert get_war_room_url(url) == expected_war_room_url