Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contrib/slackv3 readchannel #29247

Merged
merged 26 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
97ad0a2
Contrib/slackv3 readchannel (#27985)
PaloSawyer Aug 27, 2023
dd4ed74
adding false secret to secrets ignore
maimorag Aug 27, 2023
3d1e6b1
update release note
maimorag Aug 27, 2023
061442e
Merge remote-tracking branch 'origin/master' into contrib/PaloSawyer_…
maimorag Aug 27, 2023
d815421
adding the new commands to read me
maimorag Aug 27, 2023
1d3655b
ruff error
maimorag Aug 27, 2023
dcfc1fa
fixing unit test
maimorag Aug 27, 2023
bbc40ee
fixing unit test
maimorag Aug 28, 2023
6ffe6bd
fixing unit tests
maimorag Aug 28, 2023
662cdf9
docker image upgrade
maimorag Aug 28, 2023
f9a7421
ifx unit tests test_list_channels
maimorag Aug 28, 2023
c8c3a89
fixing unit tests
maimorag Aug 29, 2023
09ce665
fix mypy
maimorag Aug 29, 2023
58cda73
remove unnecessary "get"
maimorag Aug 29, 2023
b3eaaf9
updating yml
maimorag Aug 29, 2023
45cab5d
adding display to yml
maimorag Aug 29, 2023
bfab4f9
updating unit test
maimorag Aug 29, 2023
e84140e
after running format
maimorag Aug 29, 2023
c108825
Merge remote-tracking branch 'origin/master' into contrib/PaloSawyer_…
maimorag Aug 30, 2023
684b651
Update Packs/Slack/Integrations/SlackV3/SlackV3.py
maimorag Aug 30, 2023
a643d7a
Update Packs/Slack/Integrations/SlackV3/SlackV3.py
maimorag Aug 30, 2023
6a10f37
Update Packs/Slack/Integrations/SlackV3/SlackV3.py
maimorag Aug 30, 2023
badadb4
Update Packs/Slack/Integrations/SlackV3/SlackV3.py
maimorag Aug 30, 2023
a077889
Update Packs/Slack/ReleaseNotes/3_1_47.md
maimorag Aug 30, 2023
9dec3d4
cr notes
maimorag Aug 30, 2023
c6e0f9e
bump to 3.2.0
maimorag Aug 30, 2023
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
3 changes: 2 additions & 1 deletion Packs/Slack/.secrets-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ https://avatars.slack-edge.com
C0R2D2C3PO
[email protected]
[email protected]
[email protected]
[email protected]
https://someimage.png
68 changes: 68 additions & 0 deletions Packs/Slack/Integrations/SlackV3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -660,3 +660,71 @@ Reset user session token in Slack.
#### Context Output

There is no context output for this command.
### slack-list-channels

***
List all of the channels in the organization workspace. This command required scopes depend on the type of channel-like object you're working with. To use the command, you'll need at least one of the channels:, groups:, im: or mpim: scopes corresponding to the conversation type you're working with.

#### Base Command

`slack-list-channels`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| name_filter | Supply this argument to only return channels with this name . | Optional |
| channel_types | Default is "public_channel". You can provide a comma separated list of other channels to include in your results. Possible options are: "public_channel", "private_channel", "mpim", and "im". Including these options may require changes to your Bot's OAuth scopes in order to read channels like private, group message, or personal messages. | Optional |
| exclude_archived | Default is true (exclude archived channels). This setting allows the command to read channels that have been archived. | Optional |
| limit | Default is 100. Set this argument to specify how many results to return. If you have more results than the limit you set, you will need to use the cursor argument to paginate your results. | Optional |
| cursor | Default is the first page of results. If you have more results than your limit, you need to paginate your results with this argument. This is found with the next_cursor attribute returned by a previous request's response_metadata . | Optional |

#### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| Slack.Channels.ID | string | The ID for the channel |
| Slack.Channels.Name | string | Name of the channel |
| Slack.Channels.Created | number | Epoch timestamp when the channel was created |
| Slack.Channels.Creator | string | ID for the creator of the channel |
| Slack.Channels.Purpose | string | The purpose, or description, of the channel |
### slack-get-conversation-history

***
Fetches a conversation's history of messages and events

#### Base Command

`slack-get-conversation-history`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| channel_id | The channel ID associated with the Slack channel. | Required |
| limit | Default is 100. Set this argument to specify how many results to return. If you have more results than the limit you set, you will need to use the cursor argument to paginate your results. | Optional |
| conversation_id | Conversation id. | Optional |

#### Context Output

There is no context output for this command.
### slack-get-conversation-replies

***
Retrieves replies to specific messages, regardless of whether it's from a public or private channel, direct message, or otherwise.

#### Base Command

`slack-get-conversation-replies`

#### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| channel_id | ID of the channel. | Required |
| thread_id | ID of the thread. | Required |
| limit | limit. | Optional |

#### Context Output

There is no context output for this command.
192 changes: 188 additions & 4 deletions Packs/Slack/Integrations/SlackV3/SlackV3.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import ssl
import threading
from distutils.util import strtobool

import aiohttp
import slack_sdk
from slack_sdk.errors import SlackApiError
Expand All @@ -17,7 +16,6 @@
from slack_sdk.web.async_slack_response import AsyncSlackResponse
from slack_sdk.web.slack_response import SlackResponse

from CommonServerUserPython import * # noqa

''' CONSTANTS '''

Expand Down Expand Up @@ -2570,6 +2568,189 @@ def pin_message():
return_error(f"{slack_error}")


def list_channels():
"""
List the conversations in the workspace
"""
args = demisto.args()
# Default for the SDK is public channels, but users can specify "public_channel", "private_channel", "mpim", and "im"
# Multiple values can be passed for this argument as a comma separated list
# By default archived channels are NOT included by the SDK. Explicitly set this if not set from the CLI or set to False
body = {
'types': args.get('channel_types'),
'exclude_archived': argToBoolean(args.get('exclude_archived', 'true')),
'limit': args.get('limit')
}
if args.get('cursor'):
body['cursor'] = args.get('cursor')
raw_response = send_slack_request_sync(CLIENT, 'conversations.list', http_verb="GET", body=body)
# Provide an option to only select a channel by a name. Instead of returning a full list of results this allows granularity
# Supports a single channel name
if name_filter := args.get('name_filter'):
for channel in raw_response['channels']:
if channel['name'] == name_filter:
channels = [channel]
break
else:
raise DemistoException(f'No channel found with name: {name_filter}')
else:
channels = raw_response['channels']
# Force list for consistent parsing
if isinstance(channels, dict):
channels = [channels]
context = [] # type: List
for channel in channels:
entry = {
'ID': channel.get('id'),
'Name': channel.get('name'),
'Created': channel.get('created'),
'Purpose': channel.get('purpose', {}).get('value')
}
if channel.get('creator'):
creator_details_response = send_slack_request_sync(CLIENT, 'users.info', http_verb="GET",
body={'user': channel.get('creator')})
entry['Creator'] = creator_details_response['user']['name']
context.append(entry)
readable_output = tableToMarkdown(f'Channels list for {args.get("channel_types")} with filter {name_filter}', context)
demisto.results({
'Type': entryTypes['note'],
'Contents': channels,
'EntryContext': {'Slack.Channels': context},
'ContentsFormat': formats['json'],
'HumanReadable': readable_output,
'ReadableContentsFormat': formats['markdown']
})


def conversation_history():
"""
Fetches a conversation's history of messages
and events
"""
args = demisto.args()
channel_id = args.get('channel_id')
limit = arg_to_number(args.get('limit'))
conversation_id = args.get('conversation_id')
body = {'channel': channel_id, 'limit': limit} if not conversation_id else {'channel': channel_id,
'oldest': conversation_id,
'inclusive': "true",
'limit': 1}
readable_output = ''
raw_response = send_slack_request_sync(CLIENT, 'conversations.history', http_verb="GET", body=body)
messages = raw_response.get('messages', '')
if not raw_response.get('ok'):
raise DemistoException(f'An error occurred while listing conversation history: {raw_response.get("error")}',
res=raw_response)
if isinstance(messages, dict):
messages = [messages]
if not isinstance(messages, list):
raise DemistoException(f'An error occurred while listing conversation history: {raw_response.get("error")}',
res=raw_response)
context = [] # type: List
for message in messages:
thread_ts = 'N/A'
has_replies = 'No'
name = 'N/A'
full_name = 'N/A'
if 'subtype' not in message:
user_id = message.get('user')
user_details_response = send_slack_request_sync(CLIENT, 'users.info', http_verb="GET",
body={'user': user_id})
user_details = user_details_response.get('user')
full_name = user_details.get('real_name')
name = user_details.get('name')
if 'thread_ts' in message:
thread_ts = message.get('thread_ts')
has_replies = 'Yes'
elif 'thread_ts' in message:
thread_ts = message.get('thread_ts')
has_replies = 'Yes'
full_name = message.get('username')
name = message.get('username')
thread_ts = message.get('thread_ts')
has_replies = 'Yes'
entry = {
'Type': message.get('type'),
'Text': message.get('text'),
'UserId': message.get('user'),
'Name': name,
'FullName': full_name,
'TimeStamp': message.get('ts'),
'HasReplies': has_replies,
'ThreadTimeStamp': thread_ts
}
context.append(entry)
readable_output = tableToMarkdown(f'Channel details from Channel ID - {channel_id}', context)
demisto.results({
'Type': entryTypes['note'],
'Contents': messages,
'EntryContext': {'Slack.Messages': context},
'ContentsFormat': formats['json'],
'HumanReadable': readable_output,
'ReadableContentsFormat': formats['markdown']
})


def conversation_replies():
"""
Retrieves replies to specific messages, regardless of whether it's
from a public or private channel, direct message, or otherwise.
"""
args = demisto.args()
channel_id = args.get('channel_id')
context: list = []
readable_output: str = ''
body = {
'channel': channel_id,
'ts': args.get('thread_timestamp'),
'limit': arg_to_number(args.get('limit'))
}
raw_response = send_slack_request_sync(CLIENT, 'conversations.replies', http_verb="GET", body=body)
messages = raw_response.get('messages', '')
if not raw_response.get('ok'):
error = raw_response.get('error')
return_error(f'An error occurred while listing conversation replies: {error}')
if isinstance(messages, dict):
messages = [messages]
if not isinstance(messages, list):
raise DemistoException(f'An error occurred while listing conversation replies: {raw_response.get("error")}')
for message in messages:
reply_count = 'No'
name = 'N/A'
full_name = 'N/A'
if 'subtype' not in message:
user_id = message.get('user')
body = {
'user': user_id
}
user_details_response = send_slack_request_sync(CLIENT, 'users.info', http_verb="GET", body=body)
user_details = user_details_response.get('user')
name = user_details.get('name')
full_name = user_details.get('real_name')
if 'reply_count' in message:
reply_count = 'Yes'
entry = {
'Type': message.get('type'),
'Text': message.get('text'),
'UserId': message.get('user'),
'Name': name,
'FullName': full_name,
'TimeStamp': message.get('ts'),
'ThreadTimeStamp': message.get('thread_ts'),
'IsParent': reply_count
}
context.append(entry)
readable_output = tableToMarkdown(f'Channel details from Channel ID - {channel_id}', context)
demisto.results({
'Type': entryTypes['note'],
'Contents': messages,
'EntryContext': {'Slack.Threads': context},
'ContentsFormat': formats['json'],
'HumanReadable': readable_output,
'ReadableContentsFormat': formats['markdown']
})


def long_running_main():
"""
Starts the long running thread.
Expand Down Expand Up @@ -2779,7 +2960,10 @@ def main() -> None:
'slack-get-integration-context': slack_get_integration_context,
'slack-edit-message': slack_edit_message,
'slack-pin-message': pin_message,
'slack-user-session-reset': user_session_reset
'slack-user-session-reset': user_session_reset,
'slack-get-conversation-history': conversation_history,
'slack-list-channels': list_channels,
'slack-get-conversation-replies': conversation_replies,
}

command_name: str = demisto.command()
Expand All @@ -2793,7 +2977,7 @@ def main() -> None:
support_multithreading()
command_func()
except Exception as e:
LOG(e)
demisto.debug(e)
return_error(str(e))
finally:
demisto.info(f'{command_name} completed.') # type: ignore
Expand Down
57 changes: 56 additions & 1 deletion Packs/Slack/Integrations/SlackV3/SlackV3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,62 @@ script:
required: true
description: Reset user session token in Slack.
name: slack-user-session-reset
dockerimage: demisto/slackv3:1.0.0.69226
- arguments:
- description: 'Supply this argument to only return channels with this name '
name: name_filter
- defaultValue: public_channel
description: 'You can provide a comma separated list of other channels to include in your results. Possible options are: "public_channel", "private_channel", "mpim", and "im". Including these options may require changes to your Bot''s OAuth scopes in order to read channels like private, group message, or personal messages.'
name: channel_types
- defaultValue: 'true'
description: Default is true (exclude archived channels). This setting allows the command to read channels that have been archived
name: exclude_archived
- defaultValue: 100
description: Set this argument to specify how many results to return. If you have more results than the limit you set, you will need to use the cursor argument to paginate your results.
name: limit
- description: 'Default is the first page of results. If you have more results than your limit, you need to paginate your results with this argument. This is found with the next_cursor attribute returned by a previous request''s response_metadata '
name: cursor
description: 'List all of the channels in the organization workspace. This command required scopes depend on the type of channel-like object you''re working with. To use the command, you''ll need at least one of the channels:, groups:, im: or mpim: scopes corresponding to the conversation type you''re working with.'
name: slack-list-channels
outputs:
- contextPath: Slack.Channels.ID
description: The ID for the channel
type: string
- contextPath: Slack.Channels.Name
description: Name of the channel
type: string
- contextPath: Slack.Channels.Created
description: Epoch timestamp when the channel was created
type: number
- contextPath: Slack.Channels.Creator
description: ID for the creator of the channel
type: string
- contextPath: Slack.Channels.Purpose
description: The purpose, or description, of the channel
type: string
- arguments:
- description: The channel ID associated with the Slack channel
name: channel_id
required: true
- defaultValue: 100
description: Set this argument to specify how many results to return. If you have more results than the limit you set, you will need to use the cursor argument to paginate your results.
name: limit
- description: The conversation ID.
name: conversation_id
description: Fetches a conversation's history of messages and events
name: slack-get-conversation-history
- arguments:
- name: channel_id
description: ID of the channel
required: true
- name: thread_timestamp
description: The timestamp of the thread, that can be extracted using "slack-get-conversation-history" command.
required: true
- defaultValue: 100
name: limit
description: Set this argument to specify how many results to return.
description: Retrieves replies to specific messages, regardless of whether it's from a public or private channel, direct message, or otherwise.
name: slack-get-conversation-replies
dockerimage: demisto/slackv3:1.0.0.72328
longRunning: true
runonce: false
script: '-'
Expand Down
Loading