|
11 | 11 | from prometrix import PrometheusNotFound |
12 | 12 | from rich.console import Console |
13 | 13 | from slack_sdk import WebClient |
| 14 | +from slack_sdk.errors import SlackApiError |
14 | 15 | from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type |
15 | 16 | import requests |
16 | 17 | import json |
@@ -135,14 +136,50 @@ def _process_result(self, result: Result) -> None: |
135 | 136 | if settings.slack_output: |
136 | 137 | client = WebClient(os.environ["SLACK_BOT_TOKEN"]) |
137 | 138 | warnings.filterwarnings("ignore", category=UserWarning) |
138 | | - client.files_upload( |
139 | | - channels=f"#{settings.slack_output}", |
| 139 | + |
| 140 | + # Resolve channel name to ID if needed (files_upload_v2 requires channel ID) |
| 141 | + channel_id = self._resolve_slack_channel_id(client, settings.slack_output) |
| 142 | + |
| 143 | + client.files_upload_v2( |
| 144 | + channels=channel_id, |
140 | 145 | title="KRR Report", |
141 | 146 | file=f"./{file_name}", |
142 | 147 | initial_comment=f'Kubernetes Resource Report for {(" ".join(settings.namespaces))}', |
143 | 148 | ) |
144 | 149 | os.remove(file_name) |
145 | 150 |
|
| 151 | + def _resolve_slack_channel_id(self, client: WebClient, channel_input: str) -> str: |
| 152 | + """Resolve channel name to ID if needed. files_upload_v2 requires channel ID.""" |
| 153 | + # If it already looks like a channel ID (starts with C), return as-is |
| 154 | + if channel_input.startswith('C'): |
| 155 | + return channel_input |
| 156 | + |
| 157 | + # Remove # if present |
| 158 | + channel_name = channel_input.lstrip('#') |
| 159 | + |
| 160 | + # Search through all pages of channels |
| 161 | + cursor = None |
| 162 | + while True: |
| 163 | + response = client.conversations_list( |
| 164 | + types="public_channel,private_channel", |
| 165 | + cursor=cursor |
| 166 | + ) |
| 167 | + |
| 168 | + # Check channels on this page |
| 169 | + for channel in response.get("channels", []): |
| 170 | + if channel.get("name") == channel_name: |
| 171 | + return channel["id"] |
| 172 | + |
| 173 | + # Move to next page if available |
| 174 | + cursor = response.get("response_metadata", {}).get("next_cursor") |
| 175 | + if not cursor: |
| 176 | + break |
| 177 | + |
| 178 | + raise ValueError( |
| 179 | + f"Channel '{channel_input}' not found. " |
| 180 | + f"Ensure: 1) Channel exists, 2) Bot is added to channel, 3) Bot has 'channels:read' permission" |
| 181 | + ) |
| 182 | + |
146 | 183 | def __get_resource_minimal(self, resource: ResourceType) -> float: |
147 | 184 | if resource == ResourceType.CPU: |
148 | 185 | return 1 / 1000 * settings.cpu_min_value |
|
0 commit comments