mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-13 07:25:10 +00:00
Reformat everything.
This commit is contained in:
parent
3f2213791a
commit
340ff8586d
1008 changed files with 61301 additions and 58309 deletions
|
|
@ -269,22 +269,22 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
from ansible.module_utils.urls import fetch_url
|
||||
from urllib.parse import urlencode
|
||||
|
||||
OLD_SLACK_INCOMING_WEBHOOK = 'https://%s/services/hooks/incoming-webhook?token=%s'
|
||||
SLACK_INCOMING_WEBHOOK = 'https://hooks.%s/services/%s'
|
||||
SLACK_POSTMESSAGE_WEBAPI = 'https://%s/api/chat.postMessage'
|
||||
SLACK_UPDATEMESSAGE_WEBAPI = 'https://%s/api/chat.update'
|
||||
SLACK_CONVERSATIONS_HISTORY_WEBAPI = 'https://%s/api/conversations.history'
|
||||
OLD_SLACK_INCOMING_WEBHOOK = "https://%s/services/hooks/incoming-webhook?token=%s"
|
||||
SLACK_INCOMING_WEBHOOK = "https://hooks.%s/services/%s"
|
||||
SLACK_POSTMESSAGE_WEBAPI = "https://%s/api/chat.postMessage"
|
||||
SLACK_UPDATEMESSAGE_WEBAPI = "https://%s/api/chat.update"
|
||||
SLACK_CONVERSATIONS_HISTORY_WEBAPI = "https://%s/api/conversations.history"
|
||||
|
||||
# Escaping quotes and apostrophes to avoid ending string prematurely in ansible call.
|
||||
# We do not escape other characters used as Slack metacharacters (e.g. &, <, >).
|
||||
escape_table = {
|
||||
'"': "\"",
|
||||
"'": "\'",
|
||||
'"': '"',
|
||||
"'": "'",
|
||||
}
|
||||
|
||||
|
||||
def is_valid_hex_color(color_choice):
|
||||
if re.match(r'^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', color_choice):
|
||||
if re.match(r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", color_choice):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -310,8 +310,21 @@ def recursive_escape_quotes(obj, keys):
|
|||
return escaped
|
||||
|
||||
|
||||
def build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_emoji, link_names,
|
||||
parse, color, attachments, blocks, message_id, prepend_hash):
|
||||
def build_payload_for_slack(
|
||||
text,
|
||||
channel,
|
||||
thread_id,
|
||||
username,
|
||||
icon_url,
|
||||
icon_emoji,
|
||||
link_names,
|
||||
parse,
|
||||
color,
|
||||
attachments,
|
||||
blocks,
|
||||
message_id,
|
||||
prepend_hash,
|
||||
):
|
||||
payload = {}
|
||||
if color == "normal" and text is not None:
|
||||
payload = dict(text=escape_quotes(text))
|
||||
|
|
@ -319,175 +332,178 @@ def build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_e
|
|||
# With a custom color we have to set the message as attachment, and explicitly turn markdown parsing on for it.
|
||||
payload = dict(attachments=[dict(text=escape_quotes(text), color=color, mrkdwn_in=["text"])])
|
||||
if channel is not None:
|
||||
if prepend_hash == 'auto':
|
||||
if channel.startswith(('#', '@', 'C0', 'GF', 'G0', 'CP')):
|
||||
payload['channel'] = channel
|
||||
if prepend_hash == "auto":
|
||||
if channel.startswith(("#", "@", "C0", "GF", "G0", "CP")):
|
||||
payload["channel"] = channel
|
||||
else:
|
||||
payload['channel'] = f"#{channel}"
|
||||
elif prepend_hash == 'always':
|
||||
payload['channel'] = f"#{channel}"
|
||||
elif prepend_hash == 'never':
|
||||
payload['channel'] = channel
|
||||
payload["channel"] = f"#{channel}"
|
||||
elif prepend_hash == "always":
|
||||
payload["channel"] = f"#{channel}"
|
||||
elif prepend_hash == "never":
|
||||
payload["channel"] = channel
|
||||
if thread_id is not None:
|
||||
payload['thread_ts'] = thread_id
|
||||
payload["thread_ts"] = thread_id
|
||||
if username is not None:
|
||||
payload['username'] = username
|
||||
payload["username"] = username
|
||||
if icon_emoji is not None:
|
||||
payload['icon_emoji'] = icon_emoji
|
||||
payload["icon_emoji"] = icon_emoji
|
||||
else:
|
||||
payload['icon_url'] = icon_url
|
||||
payload["icon_url"] = icon_url
|
||||
if link_names is not None:
|
||||
payload['link_names'] = link_names
|
||||
payload["link_names"] = link_names
|
||||
if parse is not None:
|
||||
payload['parse'] = parse
|
||||
payload["parse"] = parse
|
||||
if message_id is not None:
|
||||
payload['ts'] = message_id
|
||||
payload["ts"] = message_id
|
||||
|
||||
if attachments is not None:
|
||||
if 'attachments' not in payload:
|
||||
payload['attachments'] = []
|
||||
if "attachments" not in payload:
|
||||
payload["attachments"] = []
|
||||
|
||||
if attachments is not None:
|
||||
attachment_keys_to_escape = [
|
||||
'title',
|
||||
'text',
|
||||
'author_name',
|
||||
'pretext',
|
||||
'fallback',
|
||||
"title",
|
||||
"text",
|
||||
"author_name",
|
||||
"pretext",
|
||||
"fallback",
|
||||
]
|
||||
for attachment in attachments:
|
||||
for key in attachment_keys_to_escape:
|
||||
if key in attachment:
|
||||
attachment[key] = escape_quotes(attachment[key])
|
||||
|
||||
if 'fallback' not in attachment:
|
||||
attachment['fallback'] = attachment['text']
|
||||
if "fallback" not in attachment:
|
||||
attachment["fallback"] = attachment["text"]
|
||||
|
||||
payload['attachments'].append(attachment)
|
||||
payload["attachments"].append(attachment)
|
||||
|
||||
if blocks is not None:
|
||||
block_keys_to_escape = [
|
||||
'text',
|
||||
'alt_text'
|
||||
]
|
||||
payload['blocks'] = recursive_escape_quotes(blocks, block_keys_to_escape)
|
||||
block_keys_to_escape = ["text", "alt_text"]
|
||||
payload["blocks"] = recursive_escape_quotes(blocks, block_keys_to_escape)
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
def validate_slack_domain(domain):
|
||||
return (domain if domain in ('slack.com', 'slack-gov.com') else 'slack.com')
|
||||
return domain if domain in ("slack.com", "slack-gov.com") else "slack.com"
|
||||
|
||||
|
||||
def get_slack_message(module, domain, token, channel, ts):
|
||||
headers = {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Accept': 'application/json',
|
||||
'Authorization': f"Bearer {token}"
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"Bearer {token}",
|
||||
}
|
||||
qs = urlencode({
|
||||
'channel': channel,
|
||||
'ts': ts,
|
||||
'limit': 1,
|
||||
'inclusive': 'true',
|
||||
})
|
||||
qs = urlencode(
|
||||
{
|
||||
"channel": channel,
|
||||
"ts": ts,
|
||||
"limit": 1,
|
||||
"inclusive": "true",
|
||||
}
|
||||
)
|
||||
domain = validate_slack_domain(domain)
|
||||
url = f"{SLACK_CONVERSATIONS_HISTORY_WEBAPI % domain}?{qs}"
|
||||
response, info = fetch_url(module=module, url=url, headers=headers, method='GET')
|
||||
if info['status'] != 200:
|
||||
response, info = fetch_url(module=module, url=url, headers=headers, method="GET")
|
||||
if info["status"] != 200:
|
||||
module.fail_json(msg="failed to get slack message")
|
||||
data = module.from_json(response.read())
|
||||
if data.get('ok') is False:
|
||||
if data.get("ok") is False:
|
||||
module.fail_json(msg=f"failed to get slack message: {data}")
|
||||
if len(data['messages']) < 1:
|
||||
if len(data["messages"]) < 1:
|
||||
module.fail_json(msg=f"no messages matching ts: {ts}")
|
||||
if len(data['messages']) > 1:
|
||||
if len(data["messages"]) > 1:
|
||||
module.fail_json(msg=f"more than 1 message matching ts: {ts}")
|
||||
return data['messages'][0]
|
||||
return data["messages"][0]
|
||||
|
||||
|
||||
def do_notify_slack(module, domain, token, payload):
|
||||
use_webapi = False
|
||||
if token.count('/') >= 2:
|
||||
if token.count("/") >= 2:
|
||||
# New style webhook token
|
||||
domain = validate_slack_domain(domain)
|
||||
slack_uri = SLACK_INCOMING_WEBHOOK % (domain, token)
|
||||
elif re.match(r'^xox[abp]-\S+$', token):
|
||||
elif re.match(r"^xox[abp]-\S+$", token):
|
||||
domain = validate_slack_domain(domain)
|
||||
slack_uri = (SLACK_UPDATEMESSAGE_WEBAPI if 'ts' in payload else SLACK_POSTMESSAGE_WEBAPI) % domain
|
||||
slack_uri = (SLACK_UPDATEMESSAGE_WEBAPI if "ts" in payload else SLACK_POSTMESSAGE_WEBAPI) % domain
|
||||
use_webapi = True
|
||||
else:
|
||||
if not domain:
|
||||
module.fail_json(msg="Slack has updated its webhook API. You need to specify a token of the form "
|
||||
"XXXX/YYYY/ZZZZ in your playbook")
|
||||
module.fail_json(
|
||||
msg="Slack has updated its webhook API. You need to specify a token of the form "
|
||||
"XXXX/YYYY/ZZZZ in your playbook"
|
||||
)
|
||||
slack_uri = OLD_SLACK_INCOMING_WEBHOOK % (domain, token)
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
'Accept': 'application/json',
|
||||
"Content-Type": "application/json; charset=UTF-8",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
if use_webapi:
|
||||
headers['Authorization'] = f"Bearer {token}"
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
|
||||
data = module.jsonify(payload)
|
||||
response, info = fetch_url(module=module, url=slack_uri, headers=headers, method='POST', data=data)
|
||||
response, info = fetch_url(module=module, url=slack_uri, headers=headers, method="POST", data=data)
|
||||
|
||||
if info['status'] != 200:
|
||||
if info["status"] != 200:
|
||||
if use_webapi:
|
||||
obscured_incoming_webhook = slack_uri
|
||||
else:
|
||||
obscured_incoming_webhook = SLACK_INCOMING_WEBHOOK % (domain, '[obscured]')
|
||||
obscured_incoming_webhook = SLACK_INCOMING_WEBHOOK % (domain, "[obscured]")
|
||||
module.fail_json(msg=f" failed to send {data} to {obscured_incoming_webhook}: {info['msg']}")
|
||||
|
||||
# each API requires different handling
|
||||
if use_webapi:
|
||||
return module.from_json(response.read())
|
||||
else:
|
||||
return {'webhook': 'ok'}
|
||||
return {"webhook": "ok"}
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
domain=dict(type='str'),
|
||||
token=dict(type='str', required=True, no_log=True),
|
||||
msg=dict(type='str'),
|
||||
channel=dict(type='str'),
|
||||
thread_id=dict(type='str'),
|
||||
username=dict(type='str', default='Ansible'),
|
||||
icon_url=dict(type='str', default='https://docs.ansible.com/favicon.ico'),
|
||||
icon_emoji=dict(type='str'),
|
||||
link_names=dict(type='int', default=1, choices=[0, 1]),
|
||||
parse=dict(type='str', choices=['none', 'full']),
|
||||
validate_certs=dict(default=True, type='bool'),
|
||||
color=dict(type='str', default='normal'),
|
||||
attachments=dict(type='list', elements='dict'),
|
||||
blocks=dict(type='list', elements='dict'),
|
||||
message_id=dict(type='str'),
|
||||
prepend_hash=dict(type='str', choices=['always', 'never', 'auto'], default='never'),
|
||||
domain=dict(type="str"),
|
||||
token=dict(type="str", required=True, no_log=True),
|
||||
msg=dict(type="str"),
|
||||
channel=dict(type="str"),
|
||||
thread_id=dict(type="str"),
|
||||
username=dict(type="str", default="Ansible"),
|
||||
icon_url=dict(type="str", default="https://docs.ansible.com/favicon.ico"),
|
||||
icon_emoji=dict(type="str"),
|
||||
link_names=dict(type="int", default=1, choices=[0, 1]),
|
||||
parse=dict(type="str", choices=["none", "full"]),
|
||||
validate_certs=dict(default=True, type="bool"),
|
||||
color=dict(type="str", default="normal"),
|
||||
attachments=dict(type="list", elements="dict"),
|
||||
blocks=dict(type="list", elements="dict"),
|
||||
message_id=dict(type="str"),
|
||||
prepend_hash=dict(type="str", choices=["always", "never", "auto"], default="never"),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
domain = module.params['domain']
|
||||
token = module.params['token']
|
||||
text = module.params['msg']
|
||||
channel = module.params['channel']
|
||||
thread_id = module.params['thread_id']
|
||||
username = module.params['username']
|
||||
icon_url = module.params['icon_url']
|
||||
icon_emoji = module.params['icon_emoji']
|
||||
link_names = module.params['link_names']
|
||||
parse = module.params['parse']
|
||||
color = module.params['color']
|
||||
attachments = module.params['attachments']
|
||||
blocks = module.params['blocks']
|
||||
message_id = module.params['message_id']
|
||||
prepend_hash = module.params['prepend_hash']
|
||||
domain = module.params["domain"]
|
||||
token = module.params["token"]
|
||||
text = module.params["msg"]
|
||||
channel = module.params["channel"]
|
||||
thread_id = module.params["thread_id"]
|
||||
username = module.params["username"]
|
||||
icon_url = module.params["icon_url"]
|
||||
icon_emoji = module.params["icon_emoji"]
|
||||
link_names = module.params["link_names"]
|
||||
parse = module.params["parse"]
|
||||
color = module.params["color"]
|
||||
attachments = module.params["attachments"]
|
||||
blocks = module.params["blocks"]
|
||||
message_id = module.params["message_id"]
|
||||
prepend_hash = module.params["prepend_hash"]
|
||||
|
||||
color_choices = ['normal', 'good', 'warning', 'danger']
|
||||
color_choices = ["normal", "good", "warning", "danger"]
|
||||
if color not in color_choices and not is_valid_hex_color(color):
|
||||
module.fail_json(msg=f"Color value specified should be either one of {color_choices} or any valid hex value with length 3 or 6.")
|
||||
module.fail_json(
|
||||
msg=f"Color value specified should be either one of {color_choices} or any valid hex value with length 3 or 6."
|
||||
)
|
||||
|
||||
changed = True
|
||||
|
||||
|
|
@ -495,35 +511,53 @@ def main():
|
|||
if message_id is not None:
|
||||
changed = False
|
||||
msg = get_slack_message(module, domain, token, channel, message_id)
|
||||
for key in ('icon_url', 'icon_emoji', 'link_names', 'color', 'attachments', 'blocks'):
|
||||
for key in ("icon_url", "icon_emoji", "link_names", "color", "attachments", "blocks"):
|
||||
if msg.get(key) != module.params.get(key):
|
||||
changed = True
|
||||
break
|
||||
# if check mode is active, we shouldn't do anything regardless.
|
||||
# if changed=False, we don't need to do anything, so don't do it.
|
||||
if module.check_mode or not changed:
|
||||
module.exit_json(changed=changed, ts=msg['ts'], channel=msg['channel'])
|
||||
module.exit_json(changed=changed, ts=msg["ts"], channel=msg["channel"])
|
||||
elif module.check_mode:
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
payload = build_payload_for_slack(text, channel, thread_id, username, icon_url, icon_emoji, link_names,
|
||||
parse, color, attachments, blocks, message_id, prepend_hash)
|
||||
payload = build_payload_for_slack(
|
||||
text,
|
||||
channel,
|
||||
thread_id,
|
||||
username,
|
||||
icon_url,
|
||||
icon_emoji,
|
||||
link_names,
|
||||
parse,
|
||||
color,
|
||||
attachments,
|
||||
blocks,
|
||||
message_id,
|
||||
prepend_hash,
|
||||
)
|
||||
slack_response = do_notify_slack(module, domain, token, payload)
|
||||
|
||||
if 'ok' in slack_response:
|
||||
if "ok" in slack_response:
|
||||
# Evaluate WebAPI response
|
||||
if slack_response['ok']:
|
||||
if slack_response["ok"]:
|
||||
# return payload as a string for backwards compatibility
|
||||
payload_json = module.jsonify(payload)
|
||||
module.exit_json(changed=changed, ts=slack_response['ts'], channel=slack_response['channel'],
|
||||
api=slack_response, payload=payload_json)
|
||||
module.exit_json(
|
||||
changed=changed,
|
||||
ts=slack_response["ts"],
|
||||
channel=slack_response["channel"],
|
||||
api=slack_response,
|
||||
payload=payload_json,
|
||||
)
|
||||
else:
|
||||
module.fail_json(msg="Slack API error", error=slack_response['error'])
|
||||
module.fail_json(msg="Slack API error", error=slack_response["error"])
|
||||
else:
|
||||
# Exit with plain OK from WebHook, since we don't have more information
|
||||
# If we get 200 from webhook, the only answer is OK
|
||||
module.exit_json(msg="OK")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue