1
0
Fork 0
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:
Felix Fontein 2025-11-01 12:08:41 +01:00
parent 3f2213791a
commit 340ff8586d
1008 changed files with 61301 additions and 58309 deletions

View file

@ -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()