Adopted new slack api auth
This commit is contained in:
parent
4b7fea7ec1
commit
36e9911dfc
4 changed files with 114 additions and 41 deletions
26
README.md
26
README.md
|
@ -10,15 +10,9 @@ There are two ways to use `slack-exporter` (detailed below). Both require a Slac
|
|||
|
||||
1. Visit [https://api.slack.com/apps/](https://api.slack.com/apps/) and sign in to your workspace.
|
||||
2. Click `Create New App`, enter a name (e.g., `Slack Exporter`), and select your workspace.
|
||||
3. In the left-hand panel, navigate to `OAuth & Permissions`, and scroll to `User Token Scopes` (**not** `Bot Token Scopes`).
|
||||
4. Select the following permissions:
|
||||
- `channels:read`, `channels:history`
|
||||
- `groups:read`, `groups:history`
|
||||
- `mpim:read`, `mpim:history`
|
||||
- `im:read`, `im:history`
|
||||
- `users:read`
|
||||
5. Select `Install to Workspace` at the top of that page (or `Reinstall to Workspace` if you have done this previously) and accept at the prompt.
|
||||
6. Copy the `OAuth Access Token` (which will generally start with `xoxp` for user-level permissions)
|
||||
3. In prior versions of the Slack API, OAuth permissions had to be specified manually. Now, when prompted for an App Manifest, just paste in the contents of the `slack.yaml` file in the root of this repo.
|
||||
4. Select `Install to Workspace` at the top of that page (or `Reinstall to Workspace` if you have done this previously) and accept at the prompt.
|
||||
5. Copy the `OAuth Access Token` (which will generally start with `xoxp` for user-level permissions)
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -56,16 +50,14 @@ To use the ngrok method:
|
|||
2. Run `python bot.py`
|
||||
3. Run the ngrok binary with `path/to/ngrok http 5000`, where `5000` is the port on which the Flask application (step 2) is running. Copy the forwarding HTTPS address provided.
|
||||
|
||||
Return to the Slack app you created in [Authentication with Slack](#authentication-with-slack) and navigate to the `Slash Commands` page in the sidebar. Create the following slash commands (one for each applicable Flask route in `bot.py`):
|
||||
4. Create the following slash commands will be created (one for each applicable Flask route in `bot.py`):
|
||||
|
||||
| Command | Request URL | Arguments | Example Usage |
|
||||
|-----------------|-------------------------------------------|--------------|----------------------|
|
||||
| /export-channel | https://`[host_url]`/slack/export-channel | json \| text | /export-channel text |
|
||||
| /export-replies | https://`[host_url]`/slack/export-replies | json \| text | /export-replies json |
|
||||
| Command | Request URL | Arguments | Example Usage |
|
||||
|-----------------|-------------------------------------------|--------------|----------------------|
|
||||
| /export-channel | https://`[host_url]`/slack/export-channel | json \| text | /export-channel text |
|
||||
| /export-replies | https://`[host_url]`/slack/export-replies | json \| text | /export-replies json |
|
||||
|
||||
where, if using ngrok, `[domain]` would be replaced with something like `https://xxxxxxxxxxxx.ngrok.io`.
|
||||
|
||||
Navigate back to `OAuth & Permissions` and click `(Re)install to Workspace` to add these slash commands to the workspace.
|
||||
To do this, uncomment the `slash-commands` section in `slack.yaml` and replace `YOUR_HOST_URL_HERE` with something like `https://xxxxxxxxxxxx.ngrok.io` (if using ngrok). Then navigate back to `OAuth & Permissions` and click `(Re)install to Workspace` to add these slash commands to the workspace (ensure the OAuth token in your `.env` file is still correct).
|
||||
|
||||
## Author
|
||||
|
||||
|
|
10
bot.py
10
bot.py
|
@ -113,7 +113,7 @@ def channel_replies(timestamps, channel_id, response_url):
|
|||
# Flask routes
|
||||
|
||||
|
||||
@app.route("/slack/export-channel", methods=["POST"])
|
||||
@app.route("/slack/events/export-channel", methods=["POST"])
|
||||
def export_channel():
|
||||
data = request.form
|
||||
|
||||
|
@ -168,7 +168,7 @@ def export_channel():
|
|||
return Response(), 200
|
||||
|
||||
|
||||
@app.route("/slack/export-replies", methods=["POST"])
|
||||
@app.route("/slack/events/export-replies", methods=["POST"])
|
||||
def export_replies():
|
||||
data = request.form
|
||||
|
||||
|
@ -187,7 +187,9 @@ def export_replies():
|
|||
ch_hist = channel_history(ch_id, response_url)
|
||||
print(ch_hist)
|
||||
ch_replies = channel_replies(
|
||||
[x["ts"] for x in ch_hist if "reply_count" in x], ch_id, response_url
|
||||
[x["ts"] for x in ch_hist if "reply_count" in x],
|
||||
ch_id,
|
||||
response_url=response_url,
|
||||
)
|
||||
|
||||
export_mode = str(command_args).lower()
|
||||
|
@ -244,4 +246,4 @@ def download(filename):
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=False)
|
||||
app.run(debug=True)
|
||||
|
|
91
exporter.py
91
exporter.py
|
@ -11,22 +11,49 @@ if os.path.isfile(env_file):
|
|||
load_dotenv(env_file)
|
||||
|
||||
|
||||
# write handling
|
||||
|
||||
|
||||
def post_response(response_url, text):
|
||||
requests.post(response_url, json={"text": text})
|
||||
|
||||
|
||||
# use this to say anything
|
||||
# will print to stdout if no response_url is given
|
||||
# or post_response to given url if provided
|
||||
def handle_print(text, response_url=None):
|
||||
if response_url is None:
|
||||
print(text)
|
||||
else:
|
||||
post_response(response_url, text)
|
||||
|
||||
|
||||
# pagination handling
|
||||
|
||||
|
||||
def get_at_cursor(url, params, cursor=None):
|
||||
def get_at_cursor(url, params, cursor=None, response_url=None):
|
||||
if cursor is not None:
|
||||
params["cursor"] = cursor
|
||||
|
||||
r = requests.get(url, params=params)
|
||||
if r.status_code != 200:
|
||||
print("ERROR: %s %s" % (r.status_code, r.reason))
|
||||
# slack api (OAuth 2.0) now requires auth tokens in HTTP Authorization header
|
||||
# instead of passing it as a query parameter
|
||||
try:
|
||||
headers = {"Authorization": "Bearer %s" % os.environ["SLACK_USER_TOKEN"]}
|
||||
except KeyError:
|
||||
handle_print("Missing SLACK_USER_TOKEN in environment variables", response_url)
|
||||
sys.exit(1)
|
||||
|
||||
r = requests.get(url, headers=headers, params=params)
|
||||
|
||||
if r.status_code != 200:
|
||||
handle_print("ERROR: %s %s" % (r.status_code, r.reason), response_url)
|
||||
sys.exit(1)
|
||||
|
||||
d = r.json()
|
||||
|
||||
try:
|
||||
if d["ok"] is False:
|
||||
print("I encountered an error: %s" % d)
|
||||
handle_print("I encountered an error: %s" % d, response_url)
|
||||
sys.exit(1)
|
||||
|
||||
next_cursor = None
|
||||
|
@ -38,16 +65,26 @@ def get_at_cursor(url, params, cursor=None):
|
|||
return next_cursor, d
|
||||
|
||||
except KeyError as e:
|
||||
print("Something went wrong: %s." % e)
|
||||
handle_print("Something went wrong: %s." % e, response_url)
|
||||
return None, []
|
||||
|
||||
|
||||
def paginated_get(url, params, combine_key=None):
|
||||
def paginated_get(url, params, combine_key=None, response_url=None):
|
||||
next_cursor = None
|
||||
result = []
|
||||
while True:
|
||||
next_cursor, data = get_at_cursor(url, params, cursor=next_cursor)
|
||||
result.extend(data) if combine_key is None else result.extend(data[combine_key])
|
||||
next_cursor, data = get_at_cursor(
|
||||
url, params, cursor=next_cursor, response_url=response_url
|
||||
)
|
||||
|
||||
try:
|
||||
result.extend(data) if combine_key is None else result.extend(
|
||||
data[combine_key]
|
||||
)
|
||||
except KeyError:
|
||||
handle_print("Something went wrong: %s." % e, response_url)
|
||||
sys.exit(1)
|
||||
|
||||
if next_cursor is None:
|
||||
break
|
||||
|
||||
|
@ -57,44 +94,57 @@ def paginated_get(url, params, combine_key=None):
|
|||
# GET requests
|
||||
|
||||
|
||||
def channel_list(team_id=None):
|
||||
def channel_list(team_id=None, response_url=None):
|
||||
params = {
|
||||
"token": os.environ["SLACK_USER_TOKEN"],
|
||||
# "token": os.environ["SLACK_USER_TOKEN"],
|
||||
"team_id": team_id,
|
||||
"types": "public_channel,private_channel,mpim,im",
|
||||
"limit": 200,
|
||||
}
|
||||
|
||||
return paginated_get(
|
||||
"https://slack.com/api/conversations.list", params, combine_key="channels"
|
||||
"https://slack.com/api/conversations.list",
|
||||
params,
|
||||
combine_key="channels",
|
||||
response_url=response_url,
|
||||
)
|
||||
|
||||
|
||||
def channel_history(channel_id):
|
||||
def channel_history(channel_id, response_url=None):
|
||||
params = {
|
||||
"token": os.environ["SLACK_USER_TOKEN"],
|
||||
# "token": os.environ["SLACK_USER_TOKEN"],
|
||||
"channel": channel_id,
|
||||
"limit": 200,
|
||||
}
|
||||
|
||||
return paginated_get(
|
||||
"https://slack.com/api/conversations.history", params, combine_key="messages"
|
||||
"https://slack.com/api/conversations.history",
|
||||
params,
|
||||
combine_key="messages",
|
||||
response_url=response_url,
|
||||
)
|
||||
|
||||
|
||||
def user_list(team_id=None):
|
||||
params = {"token": os.environ["SLACK_USER_TOKEN"], "limit": 200, "team_id": team_id}
|
||||
def user_list(team_id=None, response_url=None):
|
||||
params = {
|
||||
# "token": os.environ["SLACK_USER_TOKEN"],
|
||||
"limit": 200,
|
||||
"team_id": team_id,
|
||||
}
|
||||
|
||||
return paginated_get(
|
||||
"https://slack.com/api/users.list", params, combine_key="members"
|
||||
"https://slack.com/api/users.list",
|
||||
params,
|
||||
combine_key="members",
|
||||
response_url=response_url,
|
||||
)
|
||||
|
||||
|
||||
def channel_replies(timestamps, channel_id):
|
||||
def channel_replies(timestamps, channel_id, response_url=None):
|
||||
replies = []
|
||||
for timestamp in timestamps:
|
||||
params = {
|
||||
"token": os.environ["SLACK_USER_TOKEN"],
|
||||
# "token": os.environ["SLACK_USER_TOKEN"],
|
||||
"channel": channel_id,
|
||||
"ts": timestamp,
|
||||
"limit": 200,
|
||||
|
@ -104,6 +154,7 @@ def channel_replies(timestamps, channel_id):
|
|||
"https://slack.com/api/conversations.replies",
|
||||
params,
|
||||
combine_key="messages",
|
||||
response_url=response_url,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
28
slack.yaml
28
slack.yaml
|
@ -1,8 +1,28 @@
|
|||
_metadata:
|
||||
major_version: 1
|
||||
minor_version: 1
|
||||
display_information:
|
||||
name: Slack Exporter
|
||||
description: Export publicly visible channel data as text or JSON.
|
||||
features:
|
||||
app_home:
|
||||
home_tab_enabled: false
|
||||
messages_tab_enabled: true
|
||||
messages_tab_read_only_enabled: false
|
||||
bot_user:
|
||||
display_name: Slack Exporter
|
||||
always_online: true
|
||||
# slash_commands:
|
||||
# - command: /export-channel
|
||||
# description: Export the contents of this channel in either text or JSON format
|
||||
# usage_hint: json|text
|
||||
# url: https://YOUR_HOST_URL_HERE/slack/export-channel
|
||||
# should_escape: false
|
||||
# - command: /export-replies
|
||||
# description: Export reply threads in either text or JSON format
|
||||
# usage_hint: json|text
|
||||
# url: https://YOUR_HOST_URL_HERE/slack/export-replies
|
||||
# should_escape: false
|
||||
oauth_config:
|
||||
scopes:
|
||||
user:
|
||||
|
@ -15,3 +35,11 @@ oauth_config:
|
|||
- im:read
|
||||
- im:history
|
||||
- users:read
|
||||
bot:
|
||||
- commands
|
||||
- chat:write
|
||||
- chat:write.public
|
||||
settings:
|
||||
org_deploy_enabled: false
|
||||
socket_mode_enabled: false
|
||||
token_rotation_enabled: false
|
Loading…
Add table
Add a link
Reference in a new issue