|
5 | 5 | "context" |
6 | 6 | "encoding/json" |
7 | 7 | "fmt" |
| 8 | + "io" |
8 | 9 | "net/http" |
9 | 10 | "net/url" |
10 | 11 | "sort" |
@@ -144,11 +145,14 @@ func (c *SlackWebhookChannel) Send(ctx context.Context, msg bus.OutboundMessage) |
144 | 145 | defer resp.Body.Close() |
145 | 146 |
|
146 | 147 | if resp.StatusCode >= 400 { |
| 148 | + respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 512)) |
147 | 149 | logger.ErrorCF("slack_webhook", "Slack API error", map[string]any{ |
148 | | - "target": targetName, |
149 | | - "status": resp.StatusCode, |
| 150 | + "target": targetName, |
| 151 | + "status": resp.StatusCode, |
| 152 | + "response": string(respBody), |
150 | 153 | }) |
151 | | - return nil, fmt.Errorf("slack_webhook: send failed: %w", channels.ClassifySendError(resp.StatusCode, fmt.Errorf("status %d", resp.StatusCode))) |
| 154 | + return nil, fmt.Errorf("slack_webhook: send failed (status %d: %s): %w", |
| 155 | + resp.StatusCode, string(respBody), channels.ClassifySendError(resp.StatusCode, nil)) |
152 | 156 | } |
153 | 157 |
|
154 | 158 | logger.DebugCF("slack_webhook", "Message sent successfully", map[string]any{ |
@@ -226,19 +230,47 @@ func splitText(text string, maxLen int) []string { |
226 | 230 | } |
227 | 231 |
|
228 | 232 | var chunks []string |
| 233 | + inFence := false |
| 234 | + |
229 | 235 | for len(runes) > maxLen { |
230 | | - splitAt := maxLen |
231 | | - window := string(runes[:maxLen]) |
232 | | - if idx := strings.LastIndex(window, "\n"); idx > 0 { |
233 | | - splitAt = len([]rune(window[:idx])) + 1 |
234 | | - } else if idx := strings.LastIndex(window, " "); idx > 0 { |
235 | | - splitAt = len([]rune(window[:idx])) + 1 |
236 | | - } |
237 | | - chunks = append(chunks, string(runes[:splitAt])) |
| 236 | + splitAt := findSplitPoint(runes, maxLen, inFence) |
| 237 | + chunk := string(runes[:splitAt]) |
| 238 | + chunks = append(chunks, chunk) |
| 239 | + inFence = endsInsideFence(chunk, inFence) |
238 | 240 | runes = runes[splitAt:] |
239 | 241 | } |
240 | 242 | if len(runes) > 0 { |
241 | 243 | chunks = append(chunks, string(runes)) |
242 | 244 | } |
243 | 245 | return chunks |
244 | 246 | } |
| 247 | + |
| 248 | +func findSplitPoint(runes []rune, maxLen int, inFence bool) int { |
| 249 | + window := string(runes[:maxLen]) |
| 250 | + |
| 251 | + if idx := strings.LastIndex(window, "\n"); idx > 0 { |
| 252 | + splitAt := len([]rune(window[:idx])) + 1 |
| 253 | + if !inFence || !endsInsideFence(string(runes[:splitAt]), inFence) { |
| 254 | + return splitAt |
| 255 | + } |
| 256 | + } |
| 257 | + |
| 258 | + if idx := strings.LastIndex(window, " "); idx > 0 { |
| 259 | + splitAt := len([]rune(window[:idx])) + 1 |
| 260 | + if !inFence { |
| 261 | + return splitAt |
| 262 | + } |
| 263 | + } |
| 264 | + |
| 265 | + if inFence { |
| 266 | + if idx := strings.LastIndex(window, "```"); idx > 0 { |
| 267 | + return len([]rune(window[:idx])) |
| 268 | + } |
| 269 | + } |
| 270 | + |
| 271 | + return maxLen |
| 272 | +} |
| 273 | + |
| 274 | +func endsInsideFence(text string, wasInFence bool) bool { |
| 275 | + return wasInFence != (strings.Count(text, "```")%2 == 1) |
| 276 | +} |
0 commit comments