@@ -19,6 +19,14 @@ import {
19
19
} from "../../../core/string.js" ;
20
20
import { isIncidentActive } from "../../../data/incident.js" ;
21
21
import { LogType } from "../../../data/schema/log-entry-schema.js" ;
22
+ import {
23
+ handleCancelSelection ,
24
+ handlePrioritySelection ,
25
+ } from "../handlers/incident-start.js" ;
26
+ import {
27
+ priorityCanceledConfirmationBlocks ,
28
+ prioritySelectedConfirmationBlocks ,
29
+ } from "./slack/blocks.js" ;
22
30
import {
23
31
affectedAddedBlocks ,
24
32
affectedBlocks ,
@@ -41,6 +49,7 @@ import {
41
49
newBreakingBlocks ,
42
50
notesBlocks ,
43
51
priorityBlocks ,
52
+ prioritySelectionBlocks ,
44
53
resolvedBlocks ,
45
54
richTextBlock ,
46
55
statusAllActiveBlocks ,
@@ -89,7 +98,11 @@ import type { DatetimeIso9075 } from "../../../core/date.js";
89
98
import type { Blocker } from "../../../data/blocker.js" ;
90
99
import type { Incident , IncidentOverview } from "../../../data/incident.js" ;
91
100
import type { LogEntry } from "../../../data/log.js" ;
92
- import type { ChatRoomUid , ChatUserId } from "../../../types/index.js" ;
101
+ import type {
102
+ BreakingBot ,
103
+ ChatRoomUid ,
104
+ ChatUserId ,
105
+ } from "../../../types/index.js" ;
93
106
import type { CommPlatform } from "../../comm-platform.js" ;
94
107
import type { IssueTracker } from "../../issue-tracker.js" ;
95
108
@@ -128,18 +141,42 @@ export class Slack extends Adapter implements CommPlatform {
128
141
`SLACK: creating #${ channelName } with users ${ initialUserIds . join ( "," ) } ` ,
129
142
) ;
130
143
131
- const response = await this . #webClient. conversations . create ( {
132
- name : channelName ,
133
- } ) ;
144
+ let finalChannelName = channelName ;
145
+ let response : unknown ;
146
+
147
+ try {
148
+ response = await this . #webClient. conversations . create ( {
149
+ name : finalChannelName ,
150
+ } ) ;
151
+ } catch ( error : unknown ) {
152
+ // Handle name_taken error with timestamp fallback
153
+ const slackError = error as { data ?: { error ?: string } } ;
154
+ if ( slackError ?. data ?. error === "name_taken" ) {
155
+ finalChannelName = `${ channelName } -${ Date . now ( ) } ` ;
156
+ this . robot . logger . warn (
157
+ `SLACK: Channel #${ channelName } already exists, retrying with #${ finalChannelName } ` ,
158
+ ) ;
159
+
160
+ response = await this . #webClient. conversations . create ( {
161
+ name : finalChannelName ,
162
+ } ) ;
163
+ } else {
164
+ throw error ;
165
+ }
166
+ }
134
167
135
- const { channel } = response ;
168
+ const { channel } = response as {
169
+ channel ?: { id ?: string ; name ?: string } ;
170
+ } ;
136
171
137
172
if ( ! channel || ! channel . id || ! channel . name ) {
138
- this . robot . logger . error ( `SLACK: failed create channel ${ channelName } ` ) ;
173
+ this . robot . logger . error (
174
+ `SLACK: failed create channel ${ finalChannelName } ` ,
175
+ ) ;
139
176
return { } ;
140
177
}
141
178
142
- this . robot . logger . debug ( `SLACK: created channel ${ channelName } ` ) ;
179
+ this . robot . logger . debug ( `SLACK: created channel ${ finalChannelName } ` ) ;
143
180
144
181
if ( initialUserIds . length > 0 ) {
145
182
this . #webClient. conversations . invite ( {
@@ -939,6 +976,97 @@ export class Slack extends Adapter implements CommPlatform {
939
976
) ;
940
977
}
941
978
979
+ async sendPrioritySelection (
980
+ channel : string ,
981
+ config : PriorityConfig ,
982
+ timestamp : string ,
983
+ ) : Promise < { ts ?: string } > {
984
+ const result = await this . #replyThreaded(
985
+ channel ,
986
+ prioritySelectionBlocks ( config ) ,
987
+ "Breaking process started. Please select a priority using the buttons below." ,
988
+ timestamp ,
989
+ ) ;
990
+ return result || { } ;
991
+ }
992
+
993
+ async updatePrioritySelectionMessage (
994
+ channel : string ,
995
+ messageTimestamp : string ,
996
+ selectedPriority : number ,
997
+ config : PriorityConfig ,
998
+ success = true ,
999
+ errorMessage ?: string ,
1000
+ ) : Promise < unknown > {
1001
+ const blocks = prioritySelectedConfirmationBlocks (
1002
+ selectedPriority ,
1003
+ config ,
1004
+ success ,
1005
+ errorMessage ,
1006
+ ) ;
1007
+ const text = success
1008
+ ? `Priority ${ selectedPriority } selected. Incident created successfully!`
1009
+ : `Priority ${ selectedPriority } selected. Error: ${ errorMessage || "Failed to create incident" } ` ;
1010
+
1011
+ return this . #webClient. chat . update ( {
1012
+ channel,
1013
+ ts : messageTimestamp ,
1014
+ blocks,
1015
+ text,
1016
+ } ) ;
1017
+ }
1018
+
1019
+ async updatePrioritySelectionMessageCanceled (
1020
+ channel : string ,
1021
+ messageTimestamp : string ,
1022
+ ) : Promise < unknown > {
1023
+ const blocks = priorityCanceledConfirmationBlocks ( ) ;
1024
+ const text = "Incident creation canceled." ;
1025
+
1026
+ try {
1027
+ return await this . #webClient. chat . update ( {
1028
+ channel,
1029
+ ts : messageTimestamp ,
1030
+ blocks,
1031
+ text,
1032
+ } ) ;
1033
+ } catch ( error : unknown ) {
1034
+ // If we can't update the message (too old, not our message, etc.),
1035
+ // just log the error and continue - the emoji reaction will still indicate cancellation
1036
+ const slackError = error as {
1037
+ data ?: { error ?: string } ;
1038
+ message ?: string ;
1039
+ } ;
1040
+ this . robot . logger . warn (
1041
+ `Failed to update priority selection message: ${ slackError ?. data ?. error || slackError ?. message || "Unknown error" } ` ,
1042
+ ) ;
1043
+ return Promise . resolve ( ) ;
1044
+ }
1045
+ }
1046
+
1047
+ async deletePrioritySelectionMessage (
1048
+ channel : string ,
1049
+ messageTimestamp : string ,
1050
+ ) : Promise < unknown > {
1051
+ try {
1052
+ return await this . #webClient. chat . delete ( {
1053
+ channel,
1054
+ ts : messageTimestamp ,
1055
+ } ) ;
1056
+ } catch ( error : unknown ) {
1057
+ // If we can't delete the message, just log the error and continue
1058
+ // The emoji reaction and thread reply will still indicate what happened
1059
+ const slackError = error as {
1060
+ data ?: { error ?: string } ;
1061
+ message ?: string ;
1062
+ } ;
1063
+ this . robot . logger . warn (
1064
+ `Failed to delete priority selection message: ${ slackError ?. data ?. error || slackError ?. message || "Unknown error" } ` ,
1065
+ ) ;
1066
+ return Promise . resolve ( ) ;
1067
+ }
1068
+ }
1069
+
942
1070
sendPriorityUpdated (
943
1071
channel : string ,
944
1072
newPriority : number ,
@@ -1507,6 +1635,96 @@ export class Slack extends Adapter implements CommPlatform {
1507
1635
this . robot . receive ( new LeaveMessage ( user , null , event . ts ) ) ;
1508
1636
} ) ;
1509
1637
1638
+ this . #socketClient. on ( "interactive" , async ( payload ) => {
1639
+ await payload . ack ( ) ;
1640
+
1641
+ this . robot . logger . debug (
1642
+ "Interactive payload received:" ,
1643
+ JSON . stringify ( payload , null , 2 ) ,
1644
+ ) ;
1645
+
1646
+ const { body } = payload ;
1647
+ if ( ! body ) {
1648
+ this . robot . logger . error ( "No body in interactive payload" ) ;
1649
+ return ;
1650
+ }
1651
+
1652
+ if ( body . type === "block_actions" ) {
1653
+ for ( const action of body . actions || [ ] ) {
1654
+ if ( action . action_id ?. startsWith ( "select_priority_" ) ) {
1655
+ const priority = Number ( action . value ) ;
1656
+ const channel = body . channel ?. id ;
1657
+ const userId = body . user ?. id ;
1658
+ const messageTs = body . message ?. ts ;
1659
+
1660
+ this . robot . logger . debug (
1661
+ `Priority ${ priority } selected by user ${ userId } ` ,
1662
+ ) ;
1663
+
1664
+ try {
1665
+ // Update message to show selection immediately
1666
+ await this . updatePrioritySelectionMessage (
1667
+ channel ,
1668
+ messageTs ,
1669
+ priority ,
1670
+ ( this . robot as BreakingBot ) . config . priorities ,
1671
+ true ,
1672
+ ) ;
1673
+
1674
+ // Create the incident
1675
+ await handlePrioritySelection (
1676
+ this . robot as BreakingBot ,
1677
+ channel ,
1678
+ priority ,
1679
+ userId ,
1680
+ messageTs ,
1681
+ ) ;
1682
+ } catch ( error ) {
1683
+ this . robot . logger . error (
1684
+ "Error handling priority selection:" ,
1685
+ error ,
1686
+ ) ;
1687
+
1688
+ // Update message to show error
1689
+ await this . updatePrioritySelectionMessage (
1690
+ channel ,
1691
+ messageTs ,
1692
+ priority ,
1693
+ ( this . robot as BreakingBot ) . config . priorities ,
1694
+ false ,
1695
+ error instanceof Error
1696
+ ? error . message
1697
+ : "Unknown error occurred" ,
1698
+ ) ;
1699
+ }
1700
+ } else if ( action . action_id === "cancel_priority_selection" ) {
1701
+ const channel = body . channel ?. id ;
1702
+ const userId = body . user ?. id ;
1703
+ const messageTs = body . message ?. ts ;
1704
+
1705
+ this . robot . logger . debug (
1706
+ `Priority selection cancelled by user ${ userId } ` ,
1707
+ ) ;
1708
+
1709
+ try {
1710
+ // Use consistent cancellation behavior (same as timeout)
1711
+ await handleCancelSelection (
1712
+ this . robot as BreakingBot ,
1713
+ channel ,
1714
+ userId ,
1715
+ messageTs ,
1716
+ ) ;
1717
+ } catch ( error ) {
1718
+ this . robot . logger . error (
1719
+ "Error handling priority cancellation:" ,
1720
+ error ,
1721
+ ) ;
1722
+ }
1723
+ }
1724
+ }
1725
+ }
1726
+ } ) ;
1727
+
1510
1728
await this . #socketClient. start ( ) ;
1511
1729
}
1512
1730
0 commit comments