@oneuptime/common 9.2.12 → 9.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/Models/DatabaseModels/AlertInternalNote.ts +29 -0
  2. package/Models/DatabaseModels/IncidentInternalNote.ts +29 -0
  3. package/Models/DatabaseModels/IncidentPublicNote.ts +29 -0
  4. package/Models/DatabaseModels/Index.ts +3 -11
  5. package/Models/DatabaseModels/{CopilotPullRequest.ts → LlmProvider.ts} +243 -248
  6. package/Models/DatabaseModels/ScheduledMaintenanceInternalNote.ts +29 -0
  7. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +29 -0
  8. package/Server/API/LlmProviderAPI.ts +57 -0
  9. package/Server/API/MicrosoftTeamsAPI.ts +2 -146
  10. package/Server/API/SlackAPI.ts +105 -0
  11. package/Server/API/StatusPageAPI.ts +6 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.ts +71 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.ts +45 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.ts +61 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.ts +35 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  17. package/Server/Middleware/SlackAuthorization.ts +4 -1
  18. package/Server/Services/AlertInternalNoteService.ts +26 -0
  19. package/Server/Services/IncidentInternalNoteService.ts +26 -0
  20. package/Server/Services/IncidentPublicNoteService.ts +26 -0
  21. package/Server/Services/Index.ts +2 -11
  22. package/Server/Services/LlmProviderService.ts +100 -0
  23. package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +26 -0
  24. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +26 -0
  25. package/Server/Services/StatusPageService.ts +3 -0
  26. package/Server/Utils/Express.ts +1 -0
  27. package/Server/Utils/StartServer.ts +5 -0
  28. package/Server/Utils/StatusPageResource.ts +89 -0
  29. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +96 -55
  30. package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +16 -0
  31. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +184 -1
  32. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +224 -1
  33. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +232 -1
  34. package/Server/Utils/Workspace/Slack/Slack.ts +113 -0
  35. package/Server/Utils/Workspace/Slack/app-manifest.json +13 -2
  36. package/Tests/Server/Utils/StatusPageResource.test.ts +161 -0
  37. package/Types/Icon/IconProp.ts +1 -0
  38. package/Types/LLM/Index.ts +4 -0
  39. package/Types/LLM/LlmType.ts +7 -0
  40. package/Types/Permission.ts +38 -113
  41. package/UI/Components/Icon/Icon.tsx +8 -0
  42. package/UI/Components/Link/Link.tsx +5 -1
  43. package/build/dist/Models/DatabaseModels/AlertInternalNote.js +30 -0
  44. package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
  45. package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +30 -0
  46. package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
  47. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +30 -0
  48. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  49. package/build/dist/Models/DatabaseModels/Index.js +2 -10
  50. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  51. package/build/dist/Models/DatabaseModels/{CopilotPullRequest.js → LlmProvider.js} +263 -255
  52. package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -0
  53. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js +30 -0
  54. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js.map +1 -1
  55. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +30 -0
  56. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  57. package/build/dist/Server/API/LlmProviderAPI.js +36 -0
  58. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -0
  59. package/build/dist/Server/API/MicrosoftTeamsAPI.js +2 -91
  60. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  61. package/build/dist/Server/API/SlackAPI.js +74 -0
  62. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  63. package/build/dist/Server/API/StatusPageAPI.js +6 -0
  64. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  65. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.js +30 -0
  66. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.js.map +1 -0
  67. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.js +22 -0
  68. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.js.map +1 -0
  69. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.js +39 -0
  70. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.js.map +1 -0
  71. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.js +18 -0
  72. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.js.map +1 -0
  73. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  74. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  75. package/build/dist/Server/Middleware/SlackAuthorization.js +4 -1
  76. package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
  77. package/build/dist/Server/Services/AlertInternalNoteService.js +24 -0
  78. package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
  79. package/build/dist/Server/Services/IncidentInternalNoteService.js +24 -0
  80. package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
  81. package/build/dist/Server/Services/IncidentPublicNoteService.js +24 -0
  82. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  83. package/build/dist/Server/Services/Index.js +2 -10
  84. package/build/dist/Server/Services/Index.js.map +1 -1
  85. package/build/dist/Server/Services/LlmProviderService.js +85 -0
  86. package/build/dist/Server/Services/LlmProviderService.js.map +1 -0
  87. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +24 -0
  88. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
  89. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +24 -0
  90. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  91. package/build/dist/Server/Services/StatusPageService.js +3 -0
  92. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  93. package/build/dist/Server/Utils/Express.js.map +1 -1
  94. package/build/dist/Server/Utils/StartServer.js +5 -0
  95. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  96. package/build/dist/Server/Utils/StatusPageResource.js +68 -0
  97. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -0
  98. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +73 -42
  99. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  100. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +13 -0
  101. package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
  102. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +143 -1
  103. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  104. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +173 -1
  105. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  106. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +173 -1
  107. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  108. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +92 -0
  109. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  110. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +12 -2
  111. package/build/dist/Tests/Server/Utils/StatusPageResource.test.js +122 -0
  112. package/build/dist/Tests/Server/Utils/StatusPageResource.test.js.map +1 -0
  113. package/build/dist/Types/Icon/IconProp.js +1 -0
  114. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  115. package/build/dist/Types/LLM/Index.js +4 -0
  116. package/build/dist/Types/LLM/Index.js.map +1 -0
  117. package/build/dist/Types/LLM/LlmType.js +8 -0
  118. package/build/dist/Types/LLM/LlmType.js.map +1 -0
  119. package/build/dist/Types/Permission.js +32 -97
  120. package/build/dist/Types/Permission.js.map +1 -1
  121. package/build/dist/UI/Components/Icon/Icon.js +3 -0
  122. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  123. package/build/dist/UI/Components/Link/Link.js +4 -1
  124. package/build/dist/UI/Components/Link/Link.js.map +1 -1
  125. package/package.json +5 -1
  126. package/Models/DatabaseModels/CopilotAction.ts +0 -772
  127. package/Models/DatabaseModels/CopilotActionTypePriority.ts +0 -340
  128. package/Models/DatabaseModels/CopilotCodeRepository.ts +0 -637
  129. package/Models/DatabaseModels/ServiceCopilotCodeRepository.ts +0 -544
  130. package/Server/API/CopilotActionAPI.ts +0 -418
  131. package/Server/API/CopilotCodeRepositoryAPI.ts +0 -127
  132. package/Server/API/CopilotPullRequestAPI.ts +0 -243
  133. package/Server/Docs/CodeRepository.md +0 -43
  134. package/Server/Middleware/CodeRepositoryAuthorization.ts +0 -50
  135. package/Server/Services/CopilotActionService.ts +0 -10
  136. package/Server/Services/CopilotActionTypePriorityService.ts +0 -67
  137. package/Server/Services/CopilotCodeRepositoryService.ts +0 -62
  138. package/Server/Services/CopilotPullRequestService.ts +0 -10
  139. package/Server/Services/ServiceCopilotCodeRepositoryService.ts +0 -10
  140. package/Types/Copilot/CopilotActionProps/DirectoryActionProp.ts +0 -3
  141. package/Types/Copilot/CopilotActionProps/ExceptionActionProp.ts +0 -4
  142. package/Types/Copilot/CopilotActionProps/FileActionProp.ts +0 -7
  143. package/Types/Copilot/CopilotActionProps/FunctionActionProp.ts +0 -5
  144. package/Types/Copilot/CopilotActionProps/Index.ts +0 -96
  145. package/Types/Copilot/CopilotActionProps/SpanActionProp.ts +0 -4
  146. package/Types/Copilot/CopilotActionStatus.ts +0 -114
  147. package/Types/Copilot/CopilotActionType.ts +0 -212
  148. package/build/dist/Models/DatabaseModels/CopilotAction.js +0 -793
  149. package/build/dist/Models/DatabaseModels/CopilotAction.js.map +0 -1
  150. package/build/dist/Models/DatabaseModels/CopilotActionTypePriority.js +0 -358
  151. package/build/dist/Models/DatabaseModels/CopilotActionTypePriority.js.map +0 -1
  152. package/build/dist/Models/DatabaseModels/CopilotCodeRepository.js +0 -656
  153. package/build/dist/Models/DatabaseModels/CopilotCodeRepository.js.map +0 -1
  154. package/build/dist/Models/DatabaseModels/CopilotPullRequest.js.map +0 -1
  155. package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js +0 -561
  156. package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js.map +0 -1
  157. package/build/dist/Server/API/CopilotActionAPI.js +0 -295
  158. package/build/dist/Server/API/CopilotActionAPI.js.map +0 -1
  159. package/build/dist/Server/API/CopilotCodeRepositoryAPI.js +0 -91
  160. package/build/dist/Server/API/CopilotCodeRepositoryAPI.js.map +0 -1
  161. package/build/dist/Server/API/CopilotPullRequestAPI.js +0 -166
  162. package/build/dist/Server/API/CopilotPullRequestAPI.js.map +0 -1
  163. package/build/dist/Server/Middleware/CodeRepositoryAuthorization.js +0 -48
  164. package/build/dist/Server/Middleware/CodeRepositoryAuthorization.js.map +0 -1
  165. package/build/dist/Server/Services/CopilotActionService.js +0 -9
  166. package/build/dist/Server/Services/CopilotActionService.js.map +0 -1
  167. package/build/dist/Server/Services/CopilotActionTypePriorityService.js +0 -61
  168. package/build/dist/Server/Services/CopilotActionTypePriorityService.js.map +0 -1
  169. package/build/dist/Server/Services/CopilotCodeRepositoryService.js +0 -61
  170. package/build/dist/Server/Services/CopilotCodeRepositoryService.js.map +0 -1
  171. package/build/dist/Server/Services/CopilotPullRequestService.js +0 -9
  172. package/build/dist/Server/Services/CopilotPullRequestService.js.map +0 -1
  173. package/build/dist/Server/Services/ServiceCopilotCodeRepositoryService.js +0 -9
  174. package/build/dist/Server/Services/ServiceCopilotCodeRepositoryService.js.map +0 -1
  175. package/build/dist/Types/Copilot/CopilotActionProps/DirectoryActionProp.js +0 -2
  176. package/build/dist/Types/Copilot/CopilotActionProps/DirectoryActionProp.js.map +0 -1
  177. package/build/dist/Types/Copilot/CopilotActionProps/ExceptionActionProp.js +0 -2
  178. package/build/dist/Types/Copilot/CopilotActionProps/ExceptionActionProp.js.map +0 -1
  179. package/build/dist/Types/Copilot/CopilotActionProps/FileActionProp.js +0 -2
  180. package/build/dist/Types/Copilot/CopilotActionProps/FileActionProp.js.map +0 -1
  181. package/build/dist/Types/Copilot/CopilotActionProps/FunctionActionProp.js +0 -2
  182. package/build/dist/Types/Copilot/CopilotActionProps/FunctionActionProp.js.map +0 -1
  183. package/build/dist/Types/Copilot/CopilotActionProps/Index.js +0 -64
  184. package/build/dist/Types/Copilot/CopilotActionProps/Index.js.map +0 -1
  185. package/build/dist/Types/Copilot/CopilotActionProps/SpanActionProp.js +0 -2
  186. package/build/dist/Types/Copilot/CopilotActionProps/SpanActionProp.js.map +0 -1
  187. package/build/dist/Types/Copilot/CopilotActionStatus.js +0 -96
  188. package/build/dist/Types/Copilot/CopilotActionStatus.js.map +0 -1
  189. package/build/dist/Types/Copilot/CopilotActionType.js +0 -175
  190. package/build/dist/Types/Copilot/CopilotActionType.js.map +0 -1
@@ -3,7 +3,10 @@ import ObjectID from "../../../../../Types/ObjectID";
3
3
  import ScheduledMaintenanceService from "../../../../Services/ScheduledMaintenanceService";
4
4
  import { ExpressRequest, ExpressResponse } from "../../../Express";
5
5
  import SlackUtil from "../Slack";
6
- import SlackActionType from "./ActionTypes";
6
+ import SlackActionType, {
7
+ PrivateNoteEmojis,
8
+ PublicNoteEmojis,
9
+ } from "./ActionTypes";
7
10
  import { SlackAction, SlackRequest } from "./Auth";
8
11
  import Response from "../../../Response";
9
12
  import {
@@ -34,7 +37,12 @@ import OneUptimeDate from "../../../../../Types/Date";
34
37
  import AccessTokenService from "../../../../Services/AccessTokenService";
35
38
  import CaptureSpan from "../../../Telemetry/CaptureSpan";
36
39
  import WorkspaceType from "../../../../../Types/Workspace/WorkspaceType";
40
+ import WorkspaceUserAuthTokenService from "../../../../Services/WorkspaceUserAuthTokenService";
37
41
  import WorkspaceNotificationLogService from "../../../../Services/WorkspaceNotificationLogService";
42
+ import WorkspaceProjectAuthTokenService from "../../../../Services/WorkspaceProjectAuthTokenService";
43
+ import WorkspaceNotificationLog from "../../../../../Models/DatabaseModels/WorkspaceNotificationLog";
44
+ import WorkspaceProjectAuthToken from "../../../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
45
+ import WorkspaceUserAuthToken from "../../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
38
46
 
39
47
  export default class SlackScheduledMaintenanceActions {
40
48
  @CaptureSpan()
@@ -1110,4 +1118,227 @@ export default class SlackScheduledMaintenanceActions {
1110
1118
  new BadDataException("Invalid Action Type"),
1111
1119
  );
1112
1120
  }
1121
+
1122
+ @CaptureSpan()
1123
+ public static async handleEmojiReaction(data: {
1124
+ teamId: string;
1125
+ reaction: string;
1126
+ userId: string;
1127
+ channelId: string;
1128
+ messageTs: string;
1129
+ }): Promise<void> {
1130
+ logger.debug(
1131
+ "Handling emoji reaction for Scheduled Maintenance with data:",
1132
+ );
1133
+ logger.debug(data);
1134
+
1135
+ const { teamId, reaction, userId, channelId, messageTs } = data;
1136
+
1137
+ // Check if the emoji is a supported private or public note emoji
1138
+ const isPrivateNoteEmoji: boolean = PrivateNoteEmojis.includes(reaction);
1139
+ const isPublicNoteEmoji: boolean = PublicNoteEmojis.includes(reaction);
1140
+
1141
+ if (!isPrivateNoteEmoji && !isPublicNoteEmoji) {
1142
+ logger.debug(
1143
+ `Emoji "${reaction}" is not a supported note emoji. Ignoring.`,
1144
+ );
1145
+ return;
1146
+ }
1147
+
1148
+ // Get the project auth token using the team ID
1149
+ const projectAuth: WorkspaceProjectAuthToken | null =
1150
+ await WorkspaceProjectAuthTokenService.findOneBy({
1151
+ query: {
1152
+ workspaceProjectId: teamId,
1153
+ },
1154
+ select: {
1155
+ projectId: true,
1156
+ authToken: true,
1157
+ },
1158
+ props: {
1159
+ isRoot: true,
1160
+ },
1161
+ });
1162
+
1163
+ if (!projectAuth || !projectAuth.projectId || !projectAuth.authToken) {
1164
+ logger.debug(
1165
+ "No project auth found for team ID. Ignoring emoji reaction.",
1166
+ );
1167
+ return;
1168
+ }
1169
+
1170
+ const projectId: ObjectID = projectAuth.projectId;
1171
+ const authToken: string = projectAuth.authToken;
1172
+
1173
+ // Find the scheduled maintenance linked to this channel
1174
+ const workspaceLog: WorkspaceNotificationLog | null =
1175
+ await WorkspaceNotificationLogService.findOneBy({
1176
+ query: {
1177
+ channelId: channelId,
1178
+ workspaceType: WorkspaceType.Slack,
1179
+ projectId: projectId,
1180
+ },
1181
+ select: {
1182
+ scheduledMaintenanceId: true,
1183
+ },
1184
+ props: {
1185
+ isRoot: true,
1186
+ },
1187
+ });
1188
+
1189
+ if (!workspaceLog || !workspaceLog.scheduledMaintenanceId) {
1190
+ logger.debug(
1191
+ "No scheduled maintenance found linked to this channel. Ignoring emoji reaction.",
1192
+ );
1193
+ return;
1194
+ }
1195
+
1196
+ const scheduledMaintenanceId: ObjectID =
1197
+ workspaceLog.scheduledMaintenanceId;
1198
+
1199
+ // Get the scheduled maintenance number for the confirmation message
1200
+ const scheduledMaintenanceNumber: number | null =
1201
+ await ScheduledMaintenanceService.getScheduledMaintenanceNumber({
1202
+ scheduledMaintenanceId: scheduledMaintenanceId,
1203
+ });
1204
+
1205
+ // Get the user ID in OneUptime based on Slack user ID
1206
+ const userAuth: WorkspaceUserAuthToken | null =
1207
+ await WorkspaceUserAuthTokenService.findOneBy({
1208
+ query: {
1209
+ workspaceUserId: userId,
1210
+ workspaceType: WorkspaceType.Slack,
1211
+ projectId: projectId,
1212
+ },
1213
+ select: {
1214
+ userId: true,
1215
+ },
1216
+ props: {
1217
+ isRoot: true,
1218
+ },
1219
+ });
1220
+
1221
+ if (!userAuth || !userAuth.userId) {
1222
+ logger.debug(
1223
+ "No OneUptime user found for Slack user. Ignoring emoji reaction.",
1224
+ );
1225
+ return;
1226
+ }
1227
+
1228
+ const oneUptimeUserId: ObjectID = userAuth.userId;
1229
+
1230
+ // Fetch the message text using the timestamp
1231
+ let messageText: string | null = null;
1232
+ try {
1233
+ messageText = await SlackUtil.getMessageByTimestamp({
1234
+ authToken: authToken,
1235
+ channelId: channelId,
1236
+ messageTs: messageTs,
1237
+ });
1238
+ } catch (err) {
1239
+ logger.error("Error fetching message text:");
1240
+ logger.error(err);
1241
+ return;
1242
+ }
1243
+
1244
+ if (!messageText) {
1245
+ logger.debug("No message text found. Ignoring emoji reaction.");
1246
+ return;
1247
+ }
1248
+
1249
+ // Create a unique identifier for this Slack message to prevent duplicate notes
1250
+ const postedFromSlackMessageId: string = `${channelId}:${messageTs}`;
1251
+
1252
+ // Save the note based on the emoji type
1253
+ let noteType: string;
1254
+ try {
1255
+ if (isPrivateNoteEmoji) {
1256
+ noteType = "private";
1257
+
1258
+ // Check if a note from this Slack message already exists
1259
+ const hasExistingNote: boolean =
1260
+ await ScheduledMaintenanceInternalNoteService.hasNoteFromSlackMessage(
1261
+ {
1262
+ scheduledMaintenanceId: scheduledMaintenanceId,
1263
+ postedFromSlackMessageId: postedFromSlackMessageId,
1264
+ },
1265
+ );
1266
+
1267
+ if (hasExistingNote) {
1268
+ logger.debug(
1269
+ "Private note from this Slack message already exists. Skipping duplicate.",
1270
+ );
1271
+ return;
1272
+ }
1273
+
1274
+ await ScheduledMaintenanceInternalNoteService.addNote({
1275
+ scheduledMaintenanceId: scheduledMaintenanceId,
1276
+ note: messageText,
1277
+ projectId: projectId,
1278
+ userId: oneUptimeUserId,
1279
+ postedFromSlackMessageId: postedFromSlackMessageId,
1280
+ });
1281
+ logger.debug("Private note added successfully.");
1282
+ } else if (isPublicNoteEmoji) {
1283
+ noteType = "public";
1284
+
1285
+ // Check if a note from this Slack message already exists
1286
+ const hasExistingNote: boolean =
1287
+ await ScheduledMaintenancePublicNoteService.hasNoteFromSlackMessage({
1288
+ scheduledMaintenanceId: scheduledMaintenanceId,
1289
+ postedFromSlackMessageId: postedFromSlackMessageId,
1290
+ });
1291
+
1292
+ if (hasExistingNote) {
1293
+ logger.debug(
1294
+ "Public note from this Slack message already exists. Skipping duplicate.",
1295
+ );
1296
+ return;
1297
+ }
1298
+
1299
+ await ScheduledMaintenancePublicNoteService.addNote({
1300
+ scheduledMaintenanceId: scheduledMaintenanceId,
1301
+ note: messageText,
1302
+ projectId: projectId,
1303
+ userId: oneUptimeUserId,
1304
+ postedFromSlackMessageId: postedFromSlackMessageId,
1305
+ });
1306
+ logger.debug("Public note added successfully.");
1307
+ } else {
1308
+ return;
1309
+ }
1310
+ } catch (err) {
1311
+ logger.error("Error saving note:");
1312
+ logger.error(err);
1313
+ return;
1314
+ }
1315
+
1316
+ // Send confirmation message as a reply to the original message thread
1317
+ try {
1318
+ const scheduledMaintenanceLink: string = (
1319
+ await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(
1320
+ projectId,
1321
+ scheduledMaintenanceId,
1322
+ )
1323
+ ).toString();
1324
+
1325
+ const confirmationMessage: string =
1326
+ noteType === "private"
1327
+ ? `✅ Message saved as *private note* to <${scheduledMaintenanceLink}|Scheduled Maintenance #${scheduledMaintenanceNumber}>.`
1328
+ : `✅ Message saved as *public note* to <${scheduledMaintenanceLink}|Scheduled Maintenance #${scheduledMaintenanceNumber}>. This note will be visible on the status page.`;
1329
+
1330
+ await SlackUtil.sendMessageToThread({
1331
+ authToken: authToken,
1332
+ channelId: channelId,
1333
+ threadTs: messageTs,
1334
+ text: confirmationMessage,
1335
+ });
1336
+
1337
+ logger.debug("Confirmation message sent successfully.");
1338
+ } catch (err) {
1339
+ logger.error("Error sending confirmation message:");
1340
+ logger.error(err);
1341
+ // Don't throw - note was saved successfully, confirmation is best effort
1342
+ }
1343
+ }
1113
1344
  }
@@ -1156,6 +1156,119 @@ export default class SlackUtil extends WorkspaceBase {
1156
1156
  };
1157
1157
  }
1158
1158
 
1159
+ @CaptureSpan()
1160
+ public static async sendMessageToThread(data: {
1161
+ authToken: string;
1162
+ channelId: string;
1163
+ threadTs: string;
1164
+ text: string;
1165
+ }): Promise<void> {
1166
+ logger.debug("Sending message to thread with data:");
1167
+ logger.debug(data);
1168
+
1169
+ const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
1170
+ await API.post({
1171
+ url: URL.fromString("https://slack.com/api/chat.postMessage"),
1172
+ data: {
1173
+ channel: data.channelId,
1174
+ thread_ts: data.threadTs,
1175
+ text: data.text,
1176
+ },
1177
+ headers: {
1178
+ Authorization: `Bearer ${data.authToken}`,
1179
+ ["Content-Type"]: "application/json",
1180
+ },
1181
+ options: {
1182
+ retries: 3,
1183
+ exponentialBackoff: true,
1184
+ },
1185
+ });
1186
+
1187
+ logger.debug("Response from Slack API for sending thread message:");
1188
+ logger.debug(response);
1189
+
1190
+ if (response instanceof HTTPErrorResponse) {
1191
+ logger.error("Error response from Slack API:");
1192
+ logger.error(response);
1193
+ throw response;
1194
+ }
1195
+
1196
+ if ((response.jsonData as JSONObject)?.["ok"] !== true) {
1197
+ logger.error("Invalid response from Slack API:");
1198
+ logger.error(response.jsonData);
1199
+ const messageFromSlack: string = (response.jsonData as JSONObject)?.[
1200
+ "error"
1201
+ ] as string;
1202
+ throw new BadRequestException("Error from Slack " + messageFromSlack);
1203
+ }
1204
+
1205
+ logger.debug("Thread message sent successfully.");
1206
+ }
1207
+
1208
+ @CaptureSpan()
1209
+ public static async getMessageByTimestamp(data: {
1210
+ authToken: string;
1211
+ channelId: string;
1212
+ messageTs: string;
1213
+ }): Promise<string | null> {
1214
+ logger.debug("Getting message by timestamp with data:");
1215
+ logger.debug(data);
1216
+
1217
+ const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
1218
+ await API.post({
1219
+ url: URL.fromString("https://slack.com/api/conversations.history"),
1220
+ data: {
1221
+ channel: data.channelId,
1222
+ latest: data.messageTs,
1223
+ oldest: data.messageTs,
1224
+ inclusive: true,
1225
+ limit: 1,
1226
+ },
1227
+ headers: {
1228
+ Authorization: `Bearer ${data.authToken}`,
1229
+ ["Content-Type"]: "application/x-www-form-urlencoded",
1230
+ },
1231
+ options: {
1232
+ retries: 3,
1233
+ exponentialBackoff: true,
1234
+ },
1235
+ });
1236
+
1237
+ logger.debug("Response from Slack API for getting message:");
1238
+ logger.debug(response);
1239
+
1240
+ if (response instanceof HTTPErrorResponse) {
1241
+ logger.error("Error response from Slack API:");
1242
+ logger.error(response);
1243
+ throw response;
1244
+ }
1245
+
1246
+ if ((response.jsonData as JSONObject)?.["ok"] !== true) {
1247
+ logger.error("Invalid response from Slack API:");
1248
+ logger.error(response.jsonData);
1249
+ const messageFromSlack: string = (response.jsonData as JSONObject)?.[
1250
+ "error"
1251
+ ] as string;
1252
+ throw new BadRequestException("Error from Slack " + messageFromSlack);
1253
+ }
1254
+
1255
+ const messages: Array<JSONObject> = (response.jsonData as JSONObject)?.[
1256
+ "messages"
1257
+ ] as Array<JSONObject>;
1258
+
1259
+ if (!messages || messages.length === 0) {
1260
+ logger.debug("No messages found for timestamp.");
1261
+ return null;
1262
+ }
1263
+
1264
+ const messageText: string | undefined = messages[0]?.["text"] as string;
1265
+
1266
+ logger.debug("Message text retrieved:");
1267
+ logger.debug(messageText);
1268
+
1269
+ return messageText || null;
1270
+ }
1271
+
1159
1272
  @CaptureSpan()
1160
1273
  public static override getButtonsBlock(data: {
1161
1274
  payloadButtonsBlock: WorkspacePayloadButtons;
@@ -5,6 +5,7 @@
5
5
  "background_color": "#000000",
6
6
  "long_description": "OneUptime is a comprehensive solution for monitoring and managing your online services. Whether you need to check the availability of your website, dashboard, API, or any other online resource, OneUptime can alert your team when downtime happens and keep your customers informed with a status page. OneUptime also helps you handle incidents, set up on-call rotations, run tests, secure your services, analyze logs, track performance, and debug errors."
7
7
  },
8
+
8
9
  "features": {
9
10
  "app_home": {
10
11
  "home_tab_enabled": false,
@@ -66,10 +67,14 @@
66
67
  "users:read",
67
68
  "groups:read",
68
69
  "groups:write",
70
+ "groups:history",
69
71
  "im:read",
70
72
  "im:write",
73
+ "im:history",
71
74
  "mpim:read",
72
- "mpim:write"
75
+ "mpim:write",
76
+ "mpim:history",
77
+ "reactions:read"
73
78
  ]
74
79
  }
75
80
  },
@@ -81,6 +86,12 @@
81
86
  },
82
87
  "org_deploy_enabled": true,
83
88
  "socket_mode_enabled": false,
84
- "token_rotation_enabled": false
89
+ "token_rotation_enabled": false,
90
+ "event_subscriptions": {
91
+ "request_url": "{{SERVER_URL}}/api/slack/events",
92
+ "bot_events": [
93
+ "reaction_added"
94
+ ]
95
+ }
85
96
  }
86
97
  }
@@ -0,0 +1,161 @@
1
+ import StatusPageResourceUtil from "../../../Server/Utils/StatusPageResource";
2
+ import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResource";
3
+ import ObjectID from "../../../Types/ObjectID";
4
+
5
+ describe("StatusPageResourceUtil", () => {
6
+ describe("getResourcesGroupedByGroupName", () => {
7
+ it("should return empty string for empty resources array", () => {
8
+ const result: string =
9
+ StatusPageResourceUtil.getResourcesGroupedByGroupName([]);
10
+ expect(result).toBe("");
11
+ });
12
+
13
+ it("should return custom default value for empty resources array", () => {
14
+ const result: string =
15
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(
16
+ [],
17
+ "No resources",
18
+ );
19
+ expect(result).toBe("No resources");
20
+ });
21
+
22
+ it("should return simple comma-separated list when no resources have groups", () => {
23
+ const resources: Array<StatusPageResource> = [
24
+ createResource("API"),
25
+ createResource("Website"),
26
+ createResource("Database"),
27
+ ];
28
+
29
+ const result: string =
30
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
31
+ expect(result).toBe("API, Website, Database");
32
+ });
33
+
34
+ it("should group resources by their resource group name", () => {
35
+ const resources: Array<StatusPageResource> = [
36
+ createResourceWithGroup("Infrastructure", "EU"),
37
+ createResourceWithGroup("Website", "EU"),
38
+ createResourceWithGroup("Infrastructure", "UK"),
39
+ createResourceWithGroup("API", "UK"),
40
+ ];
41
+
42
+ const result: string =
43
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
44
+ /*
45
+ * Groups should be formatted as "GroupName: Resource1, Resource2"
46
+ * Multiple groups separated by <br/> for HTML rendering
47
+ */
48
+ expect(result).toContain("EU: Infrastructure, Website");
49
+ expect(result).toContain("UK: Infrastructure, API");
50
+ expect(result).toContain("<br/>");
51
+ });
52
+
53
+ it("should handle mixed grouped and ungrouped resources", () => {
54
+ const resources: Array<StatusPageResource> = [
55
+ createResourceWithGroup("Infrastructure", "EU"),
56
+ createResource("Global API"),
57
+ createResourceWithGroup("Website", "UK"),
58
+ ];
59
+
60
+ const result: string =
61
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
62
+ expect(result).toContain("EU: Infrastructure");
63
+ expect(result).toContain("UK: Website");
64
+ // Ungrouped resources should appear on their own line without "Other" label
65
+ expect(result).toContain("Global API");
66
+ expect(result).not.toContain("Other:");
67
+ });
68
+
69
+ it("should handle single grouped resource", () => {
70
+ const resources: Array<StatusPageResource> = [
71
+ createResourceWithGroup("Infrastructure", "EU"),
72
+ ];
73
+
74
+ const result: string =
75
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
76
+ expect(result).toBe("EU: Infrastructure");
77
+ });
78
+
79
+ it("should handle single ungrouped resource", () => {
80
+ const resources: Array<StatusPageResource> = [createResource("API")];
81
+
82
+ const result: string =
83
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
84
+ expect(result).toBe("API");
85
+ });
86
+
87
+ it("should skip resources without displayName", () => {
88
+ const resources: Array<StatusPageResource> = [
89
+ createResource("API"),
90
+ createResourceWithoutDisplayName(),
91
+ createResource("Website"),
92
+ ];
93
+
94
+ const result: string =
95
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
96
+ expect(result).toBe("API, Website");
97
+ });
98
+
99
+ it("should handle null resources array", () => {
100
+ const result: string =
101
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(
102
+ null as unknown as Array<StatusPageResource>,
103
+ );
104
+ expect(result).toBe("");
105
+ });
106
+
107
+ it("should return empty string as default when specified", () => {
108
+ const result: string =
109
+ StatusPageResourceUtil.getResourcesGroupedByGroupName([], "");
110
+ expect(result).toBe("");
111
+ });
112
+
113
+ it("should handle multiple resources in same group", () => {
114
+ const resources: Array<StatusPageResource> = [
115
+ createResourceWithGroup("Infrastructure", "EU"),
116
+ createResourceWithGroup("Website", "EU"),
117
+ createResourceWithGroup("API", "EU"),
118
+ ];
119
+
120
+ const result: string =
121
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(resources);
122
+ expect(result).toBe("EU: Infrastructure, Website, API");
123
+ });
124
+ });
125
+ });
126
+
127
+ /**
128
+ * Helper function to create a StatusPageResource without a group
129
+ */
130
+ function createResource(displayName: string): StatusPageResource {
131
+ const resource: StatusPageResource = new StatusPageResource();
132
+ resource._id = ObjectID.generate().toString();
133
+ resource.displayName = displayName;
134
+ return resource;
135
+ }
136
+
137
+ /**
138
+ * Helper function to create a StatusPageResource with a group
139
+ */
140
+ function createResourceWithGroup(
141
+ displayName: string,
142
+ groupName: string,
143
+ ): StatusPageResource {
144
+ const resource: StatusPageResource = new StatusPageResource();
145
+ resource._id = ObjectID.generate().toString();
146
+ resource.displayName = displayName;
147
+ resource.statusPageGroupId = ObjectID.generate();
148
+ resource.statusPageGroup = {
149
+ name: groupName,
150
+ } as any;
151
+ return resource;
152
+ }
153
+
154
+ /**
155
+ * Helper function to create a StatusPageResource without a displayName
156
+ */
157
+ function createResourceWithoutDisplayName(): StatusPageResource {
158
+ const resource: StatusPageResource = new StatusPageResource();
159
+ resource._id = ObjectID.generate().toString();
160
+ return resource;
161
+ }
@@ -140,6 +140,7 @@ enum IconProp {
140
140
  SquareStack3D = "SquareStack3D",
141
141
  ExclaimationCircle = "ExclaimationCircle",
142
142
  WhatsApp = "WhatsApp",
143
+ Brain = "Brain",
143
144
  }
144
145
 
145
146
  export default IconProp;
@@ -0,0 +1,4 @@
1
+ import LlmType from "./LlmType";
2
+
3
+ export default LlmType;
4
+ export { LlmType };
@@ -0,0 +1,7 @@
1
+ enum LlmType {
2
+ OpenAI = "OpenAI",
3
+ Anthropic = "Anthropic",
4
+ Ollama = "Ollama",
5
+ }
6
+
7
+ export default LlmType;