@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.
- package/Models/DatabaseModels/AlertInternalNote.ts +29 -0
- package/Models/DatabaseModels/IncidentInternalNote.ts +29 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +29 -0
- package/Models/DatabaseModels/Index.ts +3 -11
- package/Models/DatabaseModels/{CopilotPullRequest.ts → LlmProvider.ts} +243 -248
- package/Models/DatabaseModels/ScheduledMaintenanceInternalNote.ts +29 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +29 -0
- package/Server/API/LlmProviderAPI.ts +57 -0
- package/Server/API/MicrosoftTeamsAPI.ts +2 -146
- package/Server/API/SlackAPI.ts +105 -0
- package/Server/API/StatusPageAPI.ts +6 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.ts +71 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/SlackAuthorization.ts +4 -1
- package/Server/Services/AlertInternalNoteService.ts +26 -0
- package/Server/Services/IncidentInternalNoteService.ts +26 -0
- package/Server/Services/IncidentPublicNoteService.ts +26 -0
- package/Server/Services/Index.ts +2 -11
- package/Server/Services/LlmProviderService.ts +100 -0
- package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +26 -0
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +26 -0
- package/Server/Services/StatusPageService.ts +3 -0
- package/Server/Utils/Express.ts +1 -0
- package/Server/Utils/StartServer.ts +5 -0
- package/Server/Utils/StatusPageResource.ts +89 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +96 -55
- package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +16 -0
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +184 -1
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +224 -1
- package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +232 -1
- package/Server/Utils/Workspace/Slack/Slack.ts +113 -0
- package/Server/Utils/Workspace/Slack/app-manifest.json +13 -2
- package/Tests/Server/Utils/StatusPageResource.test.ts +161 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/LLM/Index.ts +4 -0
- package/Types/LLM/LlmType.ts +7 -0
- package/Types/Permission.ts +38 -113
- package/UI/Components/Icon/Icon.tsx +8 -0
- package/UI/Components/Link/Link.tsx +5 -1
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js +30 -0
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +30 -0
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +30 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -10
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{CopilotPullRequest.js → LlmProvider.js} +263 -255
- package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js +30 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +30 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +36 -0
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +2 -91
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +74 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +6 -0
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765477339178-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540325149-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765540549739-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1765544010078-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js +4 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AlertInternalNoteService.js +24 -0
- package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentInternalNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -10
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +85 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +24 -0
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +24 -0
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +3 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +5 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageResource.js +68 -0
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +73 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +13 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +143 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +173 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +173 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +92 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +12 -2
- package/build/dist/Tests/Server/Utils/StatusPageResource.test.js +122 -0
- package/build/dist/Tests/Server/Utils/StatusPageResource.test.js.map +1 -0
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/LLM/Index.js +4 -0
- package/build/dist/Types/LLM/Index.js.map +1 -0
- package/build/dist/Types/LLM/LlmType.js +8 -0
- package/build/dist/Types/LLM/LlmType.js.map +1 -0
- package/build/dist/Types/Permission.js +32 -97
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +3 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Link/Link.js +4 -1
- package/build/dist/UI/Components/Link/Link.js.map +1 -1
- package/package.json +5 -1
- package/Models/DatabaseModels/CopilotAction.ts +0 -772
- package/Models/DatabaseModels/CopilotActionTypePriority.ts +0 -340
- package/Models/DatabaseModels/CopilotCodeRepository.ts +0 -637
- package/Models/DatabaseModels/ServiceCopilotCodeRepository.ts +0 -544
- package/Server/API/CopilotActionAPI.ts +0 -418
- package/Server/API/CopilotCodeRepositoryAPI.ts +0 -127
- package/Server/API/CopilotPullRequestAPI.ts +0 -243
- package/Server/Docs/CodeRepository.md +0 -43
- package/Server/Middleware/CodeRepositoryAuthorization.ts +0 -50
- package/Server/Services/CopilotActionService.ts +0 -10
- package/Server/Services/CopilotActionTypePriorityService.ts +0 -67
- package/Server/Services/CopilotCodeRepositoryService.ts +0 -62
- package/Server/Services/CopilotPullRequestService.ts +0 -10
- package/Server/Services/ServiceCopilotCodeRepositoryService.ts +0 -10
- package/Types/Copilot/CopilotActionProps/DirectoryActionProp.ts +0 -3
- package/Types/Copilot/CopilotActionProps/ExceptionActionProp.ts +0 -4
- package/Types/Copilot/CopilotActionProps/FileActionProp.ts +0 -7
- package/Types/Copilot/CopilotActionProps/FunctionActionProp.ts +0 -5
- package/Types/Copilot/CopilotActionProps/Index.ts +0 -96
- package/Types/Copilot/CopilotActionProps/SpanActionProp.ts +0 -4
- package/Types/Copilot/CopilotActionStatus.ts +0 -114
- package/Types/Copilot/CopilotActionType.ts +0 -212
- package/build/dist/Models/DatabaseModels/CopilotAction.js +0 -793
- package/build/dist/Models/DatabaseModels/CopilotAction.js.map +0 -1
- package/build/dist/Models/DatabaseModels/CopilotActionTypePriority.js +0 -358
- package/build/dist/Models/DatabaseModels/CopilotActionTypePriority.js.map +0 -1
- package/build/dist/Models/DatabaseModels/CopilotCodeRepository.js +0 -656
- package/build/dist/Models/DatabaseModels/CopilotCodeRepository.js.map +0 -1
- package/build/dist/Models/DatabaseModels/CopilotPullRequest.js.map +0 -1
- package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js +0 -561
- package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js.map +0 -1
- package/build/dist/Server/API/CopilotActionAPI.js +0 -295
- package/build/dist/Server/API/CopilotActionAPI.js.map +0 -1
- package/build/dist/Server/API/CopilotCodeRepositoryAPI.js +0 -91
- package/build/dist/Server/API/CopilotCodeRepositoryAPI.js.map +0 -1
- package/build/dist/Server/API/CopilotPullRequestAPI.js +0 -166
- package/build/dist/Server/API/CopilotPullRequestAPI.js.map +0 -1
- package/build/dist/Server/Middleware/CodeRepositoryAuthorization.js +0 -48
- package/build/dist/Server/Middleware/CodeRepositoryAuthorization.js.map +0 -1
- package/build/dist/Server/Services/CopilotActionService.js +0 -9
- package/build/dist/Server/Services/CopilotActionService.js.map +0 -1
- package/build/dist/Server/Services/CopilotActionTypePriorityService.js +0 -61
- package/build/dist/Server/Services/CopilotActionTypePriorityService.js.map +0 -1
- package/build/dist/Server/Services/CopilotCodeRepositoryService.js +0 -61
- package/build/dist/Server/Services/CopilotCodeRepositoryService.js.map +0 -1
- package/build/dist/Server/Services/CopilotPullRequestService.js +0 -9
- package/build/dist/Server/Services/CopilotPullRequestService.js.map +0 -1
- package/build/dist/Server/Services/ServiceCopilotCodeRepositoryService.js +0 -9
- package/build/dist/Server/Services/ServiceCopilotCodeRepositoryService.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/DirectoryActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/DirectoryActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/ExceptionActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/ExceptionActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/FileActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/FileActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/FunctionActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/FunctionActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/Index.js +0 -64
- package/build/dist/Types/Copilot/CopilotActionProps/Index.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionProps/SpanActionProp.js +0 -2
- package/build/dist/Types/Copilot/CopilotActionProps/SpanActionProp.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionStatus.js +0 -96
- package/build/dist/Types/Copilot/CopilotActionStatus.js.map +0 -1
- package/build/dist/Types/Copilot/CopilotActionType.js +0 -175
- package/build/dist/Types/Copilot/CopilotActionType.js.map +0 -1
|
@@ -29,6 +29,8 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
|
29
29
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
30
30
|
import ObjectID from "../../../../Types/ObjectID";
|
|
31
31
|
import WorkspaceProjectAuthTokenService from "../../../Services/WorkspaceProjectAuthTokenService";
|
|
32
|
+
import WorkspaceUserAuthTokenService from "../../../Services/WorkspaceUserAuthTokenService";
|
|
33
|
+
import WorkspaceUserAuthToken from "../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
32
34
|
import WorkspaceProjectAuthToken, {
|
|
33
35
|
MicrosoftTeamsMiscData,
|
|
34
36
|
MicrosoftTeamsTeam,
|
|
@@ -2815,6 +2817,9 @@ All monitoring checks are passing normally.`;
|
|
|
2815
2817
|
@CaptureSpan()
|
|
2816
2818
|
public static async refreshTeams(data: {
|
|
2817
2819
|
projectId: ObjectID;
|
|
2820
|
+
// optional: prefer a user-scoped token when provided
|
|
2821
|
+
userId?: ObjectID;
|
|
2822
|
+
userAccessToken?: string;
|
|
2818
2823
|
}): Promise<Record<string, { id: string; name: string }>> {
|
|
2819
2824
|
logger.debug("=== refreshTeams called ===");
|
|
2820
2825
|
|
|
@@ -2848,75 +2853,111 @@ All monitoring checks are passing normally.`;
|
|
|
2848
2853
|
);
|
|
2849
2854
|
}
|
|
2850
2855
|
|
|
2851
|
-
//
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
miscData: projectAuth.miscData as MicrosoftTeamsMiscData,
|
|
2855
|
-
tenantId,
|
|
2856
|
-
});
|
|
2856
|
+
// Try using user scoped token first when available
|
|
2857
|
+
let allTeams: Array<JSONObject> = [];
|
|
2858
|
+
let usedAccessToken: string | null = null;
|
|
2857
2859
|
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
/*
|
|
2865
|
-
* Fetch all teams from Microsoft Graph API using app permissions
|
|
2866
|
-
* Handle pagination to get all teams
|
|
2867
|
-
*/
|
|
2868
|
-
const allTeams: Array<JSONObject> = [];
|
|
2869
|
-
let nextLink: string | null = "https://graph.microsoft.com/v1.0/teams";
|
|
2870
|
-
let pageCount: number = 0;
|
|
2871
|
-
const MAX_PAGES: number = MICROSOFT_TEAMS_MAX_PAGES; // Prevent infinite loop
|
|
2872
|
-
|
|
2873
|
-
while (nextLink) {
|
|
2874
|
-
pageCount++;
|
|
2875
|
-
if (pageCount > MAX_PAGES) {
|
|
2876
|
-
logger.error(
|
|
2877
|
-
`Maximum page limit (${MAX_PAGES}) reached while paginating teams. Breaking out to prevent infinite loop.`,
|
|
2860
|
+
try {
|
|
2861
|
+
// If caller provided a userAccessToken directly, use it
|
|
2862
|
+
if (data.userAccessToken) {
|
|
2863
|
+
logger.debug(
|
|
2864
|
+
"Using provided user access token to fetch joined teams",
|
|
2878
2865
|
);
|
|
2879
|
-
|
|
2866
|
+
usedAccessToken = data.userAccessToken;
|
|
2867
|
+
const userTeams: Record<string, { id: string; name: string }> =
|
|
2868
|
+
await this.getUserJoinedTeams(usedAccessToken);
|
|
2869
|
+
allTeams = Object.values(userTeams) as any;
|
|
2870
|
+
} else if (data.userId) {
|
|
2871
|
+
// Try to fetch stored user auth for this project + user
|
|
2872
|
+
logger.debug("Looking up stored user auth token for provided userId");
|
|
2873
|
+
const userAuth: WorkspaceUserAuthToken | null =
|
|
2874
|
+
await WorkspaceUserAuthTokenService.getUserAuth({
|
|
2875
|
+
projectId: data.projectId,
|
|
2876
|
+
userId: data.userId,
|
|
2877
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
2878
|
+
});
|
|
2879
|
+
|
|
2880
|
+
if (userAuth && userAuth.authToken) {
|
|
2881
|
+
usedAccessToken = userAuth.authToken;
|
|
2882
|
+
logger.debug(
|
|
2883
|
+
"Found user auth token; using it to fetch joined teams",
|
|
2884
|
+
);
|
|
2885
|
+
const userTeams: Record<string, { id: string; name: string }> =
|
|
2886
|
+
await this.getUserJoinedTeams(usedAccessToken);
|
|
2887
|
+
allTeams = Object.values(userTeams) as any;
|
|
2888
|
+
}
|
|
2880
2889
|
}
|
|
2881
|
-
|
|
2890
|
+
} catch (err) {
|
|
2891
|
+
logger.warn(
|
|
2892
|
+
"Failed to fetch teams using user-scoped token, falling back to app token:",
|
|
2893
|
+
);
|
|
2894
|
+
logger.warn(err);
|
|
2895
|
+
allTeams = [];
|
|
2896
|
+
}
|
|
2882
2897
|
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2898
|
+
// If we couldn't obtain teams via user token, fall back to app-scoped token + existing behavior
|
|
2899
|
+
if (!allTeams || allTeams.length === 0) {
|
|
2900
|
+
// Get a valid app access token
|
|
2901
|
+
const accessToken: string | null = await this.refreshAccessToken({
|
|
2902
|
+
projectId: data.projectId,
|
|
2903
|
+
miscData: projectAuth.miscData as MicrosoftTeamsMiscData,
|
|
2904
|
+
tenantId,
|
|
2905
|
+
});
|
|
2890
2906
|
|
|
2891
|
-
if (
|
|
2892
|
-
logger.error("Error fetching teams from Microsoft Teams:");
|
|
2893
|
-
logger.error(teamsResponse);
|
|
2907
|
+
if (!accessToken) {
|
|
2894
2908
|
throw new BadDataException(
|
|
2895
|
-
"
|
|
2909
|
+
"Could not obtain valid access token for Microsoft Teams",
|
|
2896
2910
|
);
|
|
2897
2911
|
}
|
|
2898
2912
|
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2913
|
+
/*
|
|
2914
|
+
* Fetch all teams from Microsoft Graph API using app permissions
|
|
2915
|
+
* Handle pagination to get all teams
|
|
2916
|
+
*/
|
|
2917
|
+
allTeams = [];
|
|
2918
|
+
let nextLink: string | null = "https://graph.microsoft.com/v1.0/teams";
|
|
2919
|
+
let pageCount: number = 0;
|
|
2920
|
+
const MAX_PAGES: number = MICROSOFT_TEAMS_MAX_PAGES; // Prevent infinite loop
|
|
2921
|
+
|
|
2922
|
+
while (nextLink) {
|
|
2923
|
+
pageCount++;
|
|
2924
|
+
if (pageCount > MAX_PAGES) {
|
|
2925
|
+
logger.error(
|
|
2926
|
+
`Maximum page limit (${MAX_PAGES}) reached while paginating teams. Breaking out to prevent infinite loop.`,
|
|
2927
|
+
);
|
|
2928
|
+
break;
|
|
2929
|
+
}
|
|
2930
|
+
logger.debug(`Fetching teams page ${pageCount}: ${nextLink}`);
|
|
2931
|
+
|
|
2932
|
+
const teamsResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
2933
|
+
await API.get<JSONObject>({
|
|
2934
|
+
url: URL.fromString(nextLink),
|
|
2935
|
+
headers: {
|
|
2936
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2937
|
+
},
|
|
2938
|
+
});
|
|
2939
|
+
|
|
2940
|
+
if (teamsResponse instanceof HTTPErrorResponse) {
|
|
2941
|
+
logger.error("Error fetching teams from Microsoft Teams:");
|
|
2942
|
+
logger.error(teamsResponse);
|
|
2943
|
+
throw new BadDataException(
|
|
2944
|
+
"Failed to fetch teams from Microsoft Teams",
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2902
2947
|
|
|
2903
|
-
|
|
2904
|
-
|
|
2948
|
+
const teams: Array<JSONObject> =
|
|
2949
|
+
(teamsResponse.data as any)["value"] || [];
|
|
2950
|
+
allTeams.push(...teams);
|
|
2905
2951
|
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
);
|
|
2909
|
-
}
|
|
2952
|
+
// Check for next page
|
|
2953
|
+
nextLink = (teamsResponse.data as any)["@odata.nextLink"] || null;
|
|
2910
2954
|
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2955
|
+
logger.debug(
|
|
2956
|
+
`Page ${pageCount}: Fetched ${teams.length} teams. Total so far: ${allTeams.length}`,
|
|
2957
|
+
);
|
|
2958
|
+
}
|
|
2914
2959
|
}
|
|
2915
2960
|
|
|
2916
|
-
logger.debug(
|
|
2917
|
-
`Completed fetching all teams. Total pages: ${pageCount}, Total teams: ${allTeams.length}`,
|
|
2918
|
-
);
|
|
2919
|
-
|
|
2920
2961
|
// Process teams
|
|
2921
2962
|
const availableTeams: Record<string, { id: string; name: string }> =
|
|
2922
2963
|
allTeams.reduce(
|
|
@@ -12,6 +12,9 @@ enum SlackActionType {
|
|
|
12
12
|
NewIncident = "/incident", // new incident slash command
|
|
13
13
|
SubmitNewIncident = "SubmitNewIncident",
|
|
14
14
|
|
|
15
|
+
// Emoji Reaction Actions
|
|
16
|
+
EmojiReactionAdded = "EmojiReactionAdded",
|
|
17
|
+
|
|
15
18
|
// Alert Actions just like Incident Actions
|
|
16
19
|
AcknowledgeAlert = "AcknowledgeAlert",
|
|
17
20
|
ResolveAlert = "ResolveAlert",
|
|
@@ -41,4 +44,17 @@ enum SlackActionType {
|
|
|
41
44
|
ViewOnCallPolicy = "ViewOnCallPolicy",
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
// Emoji names that trigger saving a message as a Private Note (Internal Note)
|
|
48
|
+
export const PrivateNoteEmojis: string[] = ["pushpin", "round_pushpin", "pin"];
|
|
49
|
+
|
|
50
|
+
// Emoji names that trigger saving a message as a Public Note
|
|
51
|
+
export const PublicNoteEmojis: string[] = [
|
|
52
|
+
"mega",
|
|
53
|
+
"loudspeaker",
|
|
54
|
+
"megaphone",
|
|
55
|
+
"announcement",
|
|
56
|
+
"speaking_head_in_silhouette",
|
|
57
|
+
"speaking_head",
|
|
58
|
+
];
|
|
59
|
+
|
|
44
60
|
export default SlackActionType;
|
|
@@ -3,7 +3,7 @@ import ObjectID from "../../../../../Types/ObjectID";
|
|
|
3
3
|
import AlertService from "../../../../Services/AlertService";
|
|
4
4
|
import { ExpressRequest, ExpressResponse } from "../../../Express";
|
|
5
5
|
import SlackUtil from "../Slack";
|
|
6
|
-
import SlackActionType from "./ActionTypes";
|
|
6
|
+
import SlackActionType, { PrivateNoteEmojis } from "./ActionTypes";
|
|
7
7
|
import { SlackAction, SlackRequest } from "./Auth";
|
|
8
8
|
import Response from "../../../Response";
|
|
9
9
|
import {
|
|
@@ -24,7 +24,12 @@ import logger from "../../../Logger";
|
|
|
24
24
|
import AccessTokenService from "../../../../Services/AccessTokenService";
|
|
25
25
|
import CaptureSpan from "../../../Telemetry/CaptureSpan";
|
|
26
26
|
import WorkspaceNotificationLogService from "../../../../Services/WorkspaceNotificationLogService";
|
|
27
|
+
import WorkspaceUserAuthTokenService from "../../../../Services/WorkspaceUserAuthTokenService";
|
|
27
28
|
import WorkspaceType from "../../../../../Types/Workspace/WorkspaceType";
|
|
29
|
+
import WorkspaceProjectAuthTokenService from "../../../../Services/WorkspaceProjectAuthTokenService";
|
|
30
|
+
import WorkspaceNotificationLog from "../../../../../Models/DatabaseModels/WorkspaceNotificationLog";
|
|
31
|
+
import WorkspaceProjectAuthToken from "../../../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
32
|
+
import WorkspaceUserAuthToken from "../../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
28
33
|
|
|
29
34
|
export default class SlackAlertActions {
|
|
30
35
|
@CaptureSpan()
|
|
@@ -773,4 +778,182 @@ export default class SlackAlertActions {
|
|
|
773
778
|
new BadDataException("Invalid Action Type"),
|
|
774
779
|
);
|
|
775
780
|
}
|
|
781
|
+
|
|
782
|
+
@CaptureSpan()
|
|
783
|
+
public static async handleEmojiReaction(data: {
|
|
784
|
+
teamId: string;
|
|
785
|
+
reaction: string;
|
|
786
|
+
userId: string;
|
|
787
|
+
channelId: string;
|
|
788
|
+
messageTs: string;
|
|
789
|
+
}): Promise<void> {
|
|
790
|
+
logger.debug("Handling emoji reaction for Alert with data:");
|
|
791
|
+
logger.debug(data);
|
|
792
|
+
|
|
793
|
+
const { teamId, reaction, userId, channelId, messageTs } = data;
|
|
794
|
+
|
|
795
|
+
// Alerts only support private notes, so only pushpin emojis work
|
|
796
|
+
const isPrivateNoteEmoji: boolean = PrivateNoteEmojis.includes(reaction);
|
|
797
|
+
|
|
798
|
+
if (!isPrivateNoteEmoji) {
|
|
799
|
+
logger.debug(
|
|
800
|
+
`Emoji "${reaction}" is not a supported private note emoji for alerts. Ignoring.`,
|
|
801
|
+
);
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Get the project auth token using the team ID
|
|
806
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
807
|
+
await WorkspaceProjectAuthTokenService.findOneBy({
|
|
808
|
+
query: {
|
|
809
|
+
workspaceProjectId: teamId,
|
|
810
|
+
},
|
|
811
|
+
select: {
|
|
812
|
+
projectId: true,
|
|
813
|
+
authToken: true,
|
|
814
|
+
},
|
|
815
|
+
props: {
|
|
816
|
+
isRoot: true,
|
|
817
|
+
},
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
if (!projectAuth || !projectAuth.projectId || !projectAuth.authToken) {
|
|
821
|
+
logger.debug(
|
|
822
|
+
"No project auth found for team ID. Ignoring emoji reaction.",
|
|
823
|
+
);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const projectId: ObjectID = projectAuth.projectId;
|
|
828
|
+
const authToken: string = projectAuth.authToken;
|
|
829
|
+
|
|
830
|
+
// Find the alert linked to this channel
|
|
831
|
+
const workspaceLog: WorkspaceNotificationLog | null =
|
|
832
|
+
await WorkspaceNotificationLogService.findOneBy({
|
|
833
|
+
query: {
|
|
834
|
+
channelId: channelId,
|
|
835
|
+
workspaceType: WorkspaceType.Slack,
|
|
836
|
+
projectId: projectId,
|
|
837
|
+
},
|
|
838
|
+
select: {
|
|
839
|
+
alertId: true,
|
|
840
|
+
},
|
|
841
|
+
props: {
|
|
842
|
+
isRoot: true,
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
if (!workspaceLog || !workspaceLog.alertId) {
|
|
847
|
+
logger.debug(
|
|
848
|
+
"No alert found linked to this channel. Ignoring emoji reaction.",
|
|
849
|
+
);
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const alertId: ObjectID = workspaceLog.alertId;
|
|
854
|
+
|
|
855
|
+
// Get the alert number for the confirmation message
|
|
856
|
+
const alertNumber: number | null = await AlertService.getAlertNumber({
|
|
857
|
+
alertId: alertId,
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
// Get the user ID in OneUptime based on Slack user ID
|
|
861
|
+
const userAuth: WorkspaceUserAuthToken | null =
|
|
862
|
+
await WorkspaceUserAuthTokenService.findOneBy({
|
|
863
|
+
query: {
|
|
864
|
+
workspaceUserId: userId,
|
|
865
|
+
workspaceType: WorkspaceType.Slack,
|
|
866
|
+
projectId: projectId,
|
|
867
|
+
},
|
|
868
|
+
select: {
|
|
869
|
+
userId: true,
|
|
870
|
+
},
|
|
871
|
+
props: {
|
|
872
|
+
isRoot: true,
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
if (!userAuth || !userAuth.userId) {
|
|
877
|
+
logger.debug(
|
|
878
|
+
"No OneUptime user found for Slack user. Ignoring emoji reaction.",
|
|
879
|
+
);
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const oneUptimeUserId: ObjectID = userAuth.userId;
|
|
884
|
+
|
|
885
|
+
// Fetch the message text using the timestamp
|
|
886
|
+
let messageText: string | null = null;
|
|
887
|
+
try {
|
|
888
|
+
messageText = await SlackUtil.getMessageByTimestamp({
|
|
889
|
+
authToken: authToken,
|
|
890
|
+
channelId: channelId,
|
|
891
|
+
messageTs: messageTs,
|
|
892
|
+
});
|
|
893
|
+
} catch (err) {
|
|
894
|
+
logger.error("Error fetching message text:");
|
|
895
|
+
logger.error(err);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (!messageText) {
|
|
900
|
+
logger.debug("No message text found. Ignoring emoji reaction.");
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Create a unique identifier for this Slack message to prevent duplicate notes
|
|
905
|
+
const postedFromSlackMessageId: string = `${channelId}:${messageTs}`;
|
|
906
|
+
|
|
907
|
+
// Check if a note from this Slack message already exists
|
|
908
|
+
const hasExistingNote: boolean =
|
|
909
|
+
await AlertInternalNoteService.hasNoteFromSlackMessage({
|
|
910
|
+
alertId: alertId,
|
|
911
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
if (hasExistingNote) {
|
|
915
|
+
logger.debug(
|
|
916
|
+
"Private note from this Slack message already exists. Skipping duplicate.",
|
|
917
|
+
);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Save as private note (Alerts only support private notes)
|
|
922
|
+
try {
|
|
923
|
+
await AlertInternalNoteService.addNote({
|
|
924
|
+
alertId: alertId,
|
|
925
|
+
note: messageText,
|
|
926
|
+
projectId: projectId,
|
|
927
|
+
userId: oneUptimeUserId,
|
|
928
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
929
|
+
});
|
|
930
|
+
logger.debug("Private note added to alert successfully.");
|
|
931
|
+
} catch (err) {
|
|
932
|
+
logger.error("Error saving note:");
|
|
933
|
+
logger.error(err);
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Send confirmation message as a reply to the original message thread
|
|
938
|
+
try {
|
|
939
|
+
const alertLink: string = (
|
|
940
|
+
await AlertService.getAlertLinkInDashboard(projectId, alertId)
|
|
941
|
+
).toString();
|
|
942
|
+
|
|
943
|
+
const confirmationMessage: string = `✅ Message saved as *private note* to <${alertLink}|Alert #${alertNumber}>.`;
|
|
944
|
+
|
|
945
|
+
await SlackUtil.sendMessageToThread({
|
|
946
|
+
authToken: authToken,
|
|
947
|
+
channelId: channelId,
|
|
948
|
+
threadTs: messageTs,
|
|
949
|
+
text: confirmationMessage,
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
logger.debug("Confirmation message sent successfully.");
|
|
953
|
+
} catch (err) {
|
|
954
|
+
logger.error("Error sending confirmation message:");
|
|
955
|
+
logger.error(err);
|
|
956
|
+
// Don't throw - note was saved successfully, confirmation is best effort
|
|
957
|
+
}
|
|
958
|
+
}
|
|
776
959
|
}
|
|
@@ -3,7 +3,10 @@ import ObjectID from "../../../../../Types/ObjectID";
|
|
|
3
3
|
import IncidentService from "../../../../Services/IncidentService";
|
|
4
4
|
import { ExpressRequest, ExpressResponse } from "../../../Express";
|
|
5
5
|
import SlackUtil from "../Slack";
|
|
6
|
-
import SlackActionType
|
|
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 {
|
|
@@ -38,6 +41,11 @@ import LabelService from "../../../../Services/LabelService";
|
|
|
38
41
|
import Incident from "../../../../../Models/DatabaseModels/Incident";
|
|
39
42
|
import AccessTokenService from "../../../../Services/AccessTokenService";
|
|
40
43
|
import CaptureSpan from "../../../Telemetry/CaptureSpan";
|
|
44
|
+
import WorkspaceProjectAuthTokenService from "../../../../Services/WorkspaceProjectAuthTokenService";
|
|
45
|
+
import WorkspaceUserAuthTokenService from "../../../../Services/WorkspaceUserAuthTokenService";
|
|
46
|
+
import WorkspaceNotificationLog from "../../../../../Models/DatabaseModels/WorkspaceNotificationLog";
|
|
47
|
+
import WorkspaceProjectAuthToken from "../../../../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
48
|
+
import WorkspaceUserAuthToken from "../../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
41
49
|
|
|
42
50
|
export default class SlackIncidentActions {
|
|
43
51
|
@CaptureSpan()
|
|
@@ -1285,4 +1293,219 @@ export default class SlackIncidentActions {
|
|
|
1285
1293
|
new BadDataException("Invalid Action Type"),
|
|
1286
1294
|
);
|
|
1287
1295
|
}
|
|
1296
|
+
|
|
1297
|
+
@CaptureSpan()
|
|
1298
|
+
public static async handleEmojiReaction(data: {
|
|
1299
|
+
teamId: string;
|
|
1300
|
+
reaction: string;
|
|
1301
|
+
userId: string;
|
|
1302
|
+
channelId: string;
|
|
1303
|
+
messageTs: string;
|
|
1304
|
+
}): Promise<void> {
|
|
1305
|
+
logger.debug("Handling emoji reaction with data:");
|
|
1306
|
+
logger.debug(data);
|
|
1307
|
+
|
|
1308
|
+
const { teamId, reaction, userId, channelId, messageTs } = data;
|
|
1309
|
+
|
|
1310
|
+
// Check if the emoji is a supported private or public note emoji
|
|
1311
|
+
const isPrivateNoteEmoji: boolean = PrivateNoteEmojis.includes(reaction);
|
|
1312
|
+
const isPublicNoteEmoji: boolean = PublicNoteEmojis.includes(reaction);
|
|
1313
|
+
|
|
1314
|
+
if (!isPrivateNoteEmoji && !isPublicNoteEmoji) {
|
|
1315
|
+
logger.debug(
|
|
1316
|
+
`Emoji "${reaction}" is not a supported note emoji. Ignoring.`,
|
|
1317
|
+
);
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
// Get the project auth token using the team ID
|
|
1322
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
1323
|
+
await WorkspaceProjectAuthTokenService.findOneBy({
|
|
1324
|
+
query: {
|
|
1325
|
+
workspaceProjectId: teamId,
|
|
1326
|
+
},
|
|
1327
|
+
select: {
|
|
1328
|
+
projectId: true,
|
|
1329
|
+
authToken: true,
|
|
1330
|
+
},
|
|
1331
|
+
props: {
|
|
1332
|
+
isRoot: true,
|
|
1333
|
+
},
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
if (!projectAuth || !projectAuth.projectId || !projectAuth.authToken) {
|
|
1337
|
+
logger.debug(
|
|
1338
|
+
"No project auth found for team ID. Ignoring emoji reaction.",
|
|
1339
|
+
);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
const projectId: ObjectID = projectAuth.projectId;
|
|
1344
|
+
const authToken: string = projectAuth.authToken;
|
|
1345
|
+
|
|
1346
|
+
// Find the incident linked to this channel
|
|
1347
|
+
const workspaceLog: WorkspaceNotificationLog | null =
|
|
1348
|
+
await WorkspaceNotificationLogService.findOneBy({
|
|
1349
|
+
query: {
|
|
1350
|
+
channelId: channelId,
|
|
1351
|
+
workspaceType: WorkspaceType.Slack,
|
|
1352
|
+
projectId: projectId,
|
|
1353
|
+
},
|
|
1354
|
+
select: {
|
|
1355
|
+
incidentId: true,
|
|
1356
|
+
},
|
|
1357
|
+
props: {
|
|
1358
|
+
isRoot: true,
|
|
1359
|
+
},
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
if (!workspaceLog || !workspaceLog.incidentId) {
|
|
1363
|
+
logger.debug(
|
|
1364
|
+
"No incident found linked to this channel. Ignoring emoji reaction.",
|
|
1365
|
+
);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const incidentId: ObjectID = workspaceLog.incidentId;
|
|
1370
|
+
|
|
1371
|
+
// Get the incident number for the confirmation message
|
|
1372
|
+
const incidentNumber: number | null =
|
|
1373
|
+
await IncidentService.getIncidentNumber({
|
|
1374
|
+
incidentId: incidentId,
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
// Get the user ID in OneUptime based on Slack user ID
|
|
1378
|
+
const userAuth: WorkspaceUserAuthToken | null =
|
|
1379
|
+
await WorkspaceUserAuthTokenService.findOneBy({
|
|
1380
|
+
query: {
|
|
1381
|
+
workspaceUserId: userId,
|
|
1382
|
+
workspaceType: WorkspaceType.Slack,
|
|
1383
|
+
projectId: projectId,
|
|
1384
|
+
},
|
|
1385
|
+
select: {
|
|
1386
|
+
userId: true,
|
|
1387
|
+
},
|
|
1388
|
+
props: {
|
|
1389
|
+
isRoot: true,
|
|
1390
|
+
},
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
if (!userAuth || !userAuth.userId) {
|
|
1394
|
+
logger.debug(
|
|
1395
|
+
"No OneUptime user found for Slack user. Ignoring emoji reaction.",
|
|
1396
|
+
);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
const oneUptimeUserId: ObjectID = userAuth.userId;
|
|
1401
|
+
|
|
1402
|
+
// Fetch the message text using the timestamp
|
|
1403
|
+
let messageText: string | null = null;
|
|
1404
|
+
try {
|
|
1405
|
+
messageText = await SlackUtil.getMessageByTimestamp({
|
|
1406
|
+
authToken: authToken,
|
|
1407
|
+
channelId: channelId,
|
|
1408
|
+
messageTs: messageTs,
|
|
1409
|
+
});
|
|
1410
|
+
} catch (err) {
|
|
1411
|
+
logger.error("Error fetching message text:");
|
|
1412
|
+
logger.error(err);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (!messageText) {
|
|
1417
|
+
logger.debug("No message text found. Ignoring emoji reaction.");
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// Create a unique identifier for this Slack message to prevent duplicate notes
|
|
1422
|
+
const postedFromSlackMessageId: string = `${channelId}:${messageTs}`;
|
|
1423
|
+
|
|
1424
|
+
// Save the note based on the emoji type
|
|
1425
|
+
let noteType: string;
|
|
1426
|
+
try {
|
|
1427
|
+
if (isPrivateNoteEmoji) {
|
|
1428
|
+
noteType = "private";
|
|
1429
|
+
|
|
1430
|
+
// Check if a note from this Slack message already exists
|
|
1431
|
+
const hasExistingNote: boolean =
|
|
1432
|
+
await IncidentInternalNoteService.hasNoteFromSlackMessage({
|
|
1433
|
+
incidentId: incidentId,
|
|
1434
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1437
|
+
if (hasExistingNote) {
|
|
1438
|
+
logger.debug(
|
|
1439
|
+
"Private note from this Slack message already exists. Skipping duplicate.",
|
|
1440
|
+
);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
await IncidentInternalNoteService.addNote({
|
|
1445
|
+
incidentId: incidentId,
|
|
1446
|
+
note: messageText,
|
|
1447
|
+
projectId: projectId,
|
|
1448
|
+
userId: oneUptimeUserId,
|
|
1449
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1450
|
+
});
|
|
1451
|
+
logger.debug("Private note added successfully.");
|
|
1452
|
+
} else if (isPublicNoteEmoji) {
|
|
1453
|
+
noteType = "public";
|
|
1454
|
+
|
|
1455
|
+
// Check if a note from this Slack message already exists
|
|
1456
|
+
const hasExistingNote: boolean =
|
|
1457
|
+
await IncidentPublicNoteService.hasNoteFromSlackMessage({
|
|
1458
|
+
incidentId: incidentId,
|
|
1459
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
if (hasExistingNote) {
|
|
1463
|
+
logger.debug(
|
|
1464
|
+
"Public note from this Slack message already exists. Skipping duplicate.",
|
|
1465
|
+
);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
await IncidentPublicNoteService.addNote({
|
|
1470
|
+
incidentId: incidentId,
|
|
1471
|
+
note: messageText,
|
|
1472
|
+
projectId: projectId,
|
|
1473
|
+
userId: oneUptimeUserId,
|
|
1474
|
+
postedFromSlackMessageId: postedFromSlackMessageId,
|
|
1475
|
+
});
|
|
1476
|
+
logger.debug("Public note added successfully.");
|
|
1477
|
+
} else {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
logger.error("Error saving note:");
|
|
1482
|
+
logger.error(err);
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// Send confirmation message as a reply to the original message thread
|
|
1487
|
+
try {
|
|
1488
|
+
const incidentLink: string = (
|
|
1489
|
+
await IncidentService.getIncidentLinkInDashboard(projectId, incidentId)
|
|
1490
|
+
).toString();
|
|
1491
|
+
|
|
1492
|
+
const confirmationMessage: string =
|
|
1493
|
+
noteType === "private"
|
|
1494
|
+
? `✅ Message saved as *private note* to <${incidentLink}|Incident #${incidentNumber}>.`
|
|
1495
|
+
: `✅ Message saved as *public note* to <${incidentLink}|Incident #${incidentNumber}>. This note will be visible on the status page.`;
|
|
1496
|
+
|
|
1497
|
+
await SlackUtil.sendMessageToThread({
|
|
1498
|
+
authToken: authToken,
|
|
1499
|
+
channelId: channelId,
|
|
1500
|
+
threadTs: messageTs,
|
|
1501
|
+
text: confirmationMessage,
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
logger.debug("Confirmation message sent successfully.");
|
|
1505
|
+
} catch (err) {
|
|
1506
|
+
logger.error("Error sending confirmation message:");
|
|
1507
|
+
logger.error(err);
|
|
1508
|
+
// Don't throw - note was saved successfully, confirmation is best effort
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1288
1511
|
}
|