1"""
2Library for interacting with Slack API
3
4.. versionadded:: 2016.3.0
5
6:configuration: This module can be used by specifying the name of a
7    configuration profile in the minion config, minion pillar, or master
8    config.
9
10    For example:
11
12    .. code-block:: yaml
13
14        slack:
15          api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15
16"""
17
18import http.client
19import logging
20import urllib.parse
21
22import salt.utils.http
23
24log = logging.getLogger(__name__)
25
26
27def query(
28    function,
29    api_key=None,
30    args=None,
31    method="GET",
32    header_dict=None,
33    data=None,
34    opts=None,
35):
36    """
37    Slack object method function to construct and execute on the API URL.
38
39    :param api_key:     The Slack api key.
40    :param function:    The Slack api function to perform.
41    :param method:      The HTTP method, e.g. GET or POST.
42    :param data:        The data to be sent for POST method.
43    :return:            The json response from the API call or False.
44    """
45
46    ret = {"message": "", "res": True}
47
48    slack_functions = {
49        "rooms": {"request": "channels.list", "response": "channels"},
50        "users": {"request": "users.list", "response": "members"},
51        "message": {"request": "chat.postMessage", "response": "channel"},
52    }
53
54    if not api_key:
55        api_key = __salt__["config.get"]("slack.api_key") or __salt__["config.get"](
56            "slack:api_key"
57        )
58
59        if not api_key:
60            log.error("No Slack api key found.")
61            ret["message"] = "No Slack api key found."
62            ret["res"] = False
63            return ret
64
65    api_url = "https://slack.com"
66    base_url = urllib.parse.urljoin(api_url, "/api/")
67    path = slack_functions.get(function).get("request")
68    url = urllib.parse.urljoin(base_url, path, False)
69
70    if not isinstance(args, dict):
71        query_params = {}
72    else:
73        query_params = args.copy()
74
75    if header_dict is None:
76        header_dict = {}
77
78    if method != "POST":
79        header_dict["Accept"] = "application/json"
80
81    # https://api.slack.com/changelog/2020-11-no-more-tokens-in-querystrings-for
82    # -newly-created-apps
83    # Apps created after February 24, 2021 may no longer send tokens as query
84    # parameters and must instead use an HTTP authorization header or
85    # send the token in an HTTP POST body.
86    # Apps created before February 24, 2021 will continue functioning no
87    # matter which way you pass your token.
88    header_dict["Authorization"] = "Bearer {}".format(api_key)
89    result = salt.utils.http.query(
90        url,
91        method,
92        params=query_params,
93        data=data,
94        decode=True,
95        status=True,
96        header_dict=header_dict,
97        opts=opts,
98    )
99
100    if result.get("status", None) == http.client.OK:
101        _result = result["dict"]
102        response = slack_functions.get(function).get("response")
103        if "error" in _result:
104            ret["message"] = _result["error"]
105            ret["res"] = False
106            return ret
107        ret["message"] = _result.get(response)
108        return ret
109    elif result.get("status", None) == http.client.NO_CONTENT:
110        return True
111    else:
112        log.debug(url)
113        log.debug(query_params)
114        log.debug(data)
115        log.debug(result)
116        if "dict" in result:
117            _result = result["dict"]
118            if "error" in _result:
119                ret["message"] = result["error"]
120                ret["res"] = False
121                return ret
122            ret["message"] = "Unknown response"
123            ret["res"] = False
124        else:
125            ret["message"] = "invalid_auth"
126            ret["res"] = False
127        return ret
128