@oneuptime/common 8.0.5237 → 8.0.5283
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/Index.ts +4 -2
- package/Models/DatabaseModels/OnCallDutyPolicy.ts +7 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +7 -0
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +7 -0
- package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +7 -0
- package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +5 -3
- package/Models/DatabaseModels/Project.ts +4 -2
- package/Models/DatabaseModels/ProjectSmtpConfig.ts +4 -2
- package/Models/DatabaseModels/StatusPageDomain.ts +6 -4
- package/Models/DatabaseModels/User.ts +0 -46
- package/Models/DatabaseModels/{UserTwoFactorAuth.ts → UserTotpAuth.ts} +16 -16
- package/Models/DatabaseModels/UserWebAuthn.ts +244 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +21 -0
- package/Server/API/BaseAPI.ts +4 -2
- package/Server/API/GlobalConfigAPI.ts +16 -12
- package/Server/API/MicrosoftTeamsAPI.ts +1240 -0
- package/Server/API/ProjectAPI.ts +4 -2
- package/Server/API/ResellerPlanAPI.ts +4 -2
- package/Server/API/SlackAPI.ts +54 -48
- package/Server/API/StatusPageAPI.ts +5 -3
- package/Server/API/UserOnCallLogTimelineAPI.ts +5 -3
- package/Server/API/{UserTwoFactorAuthAPI.ts → UserTotpAuthAPI.ts} +20 -20
- package/Server/API/UserWebAuthnAPI.ts +103 -0
- package/Server/EnvironmentConfig.ts +6 -0
- package/Server/Images/MicrosoftTeams/color.png +0 -0
- package/Server/Images/MicrosoftTeams/outline.png +0 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1753131488925-AddEnableCustomSubscriberEmailNotificationFooterText.ts +4 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1759175457008-MigrationName.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1759232954703-MigrationName.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1759234532998-MigrationName.ts +15 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +4 -2
- package/Server/Infrastructure/SocketIO.ts +4 -2
- package/Server/Middleware/ProjectAuthorization.ts +5 -3
- package/Server/Middleware/SlackAuthorization.ts +2 -2
- package/Server/Middleware/TelemetryIngest.ts +12 -6
- package/Server/Services/AlertStateTimelineService.ts +34 -18
- package/Server/Services/BillingInvoiceService.ts +8 -4
- package/Server/Services/BillingService.ts +13 -9
- package/Server/Services/DatabaseService.ts +42 -30
- package/Server/Services/IncidentService.ts +5 -3
- package/Server/Services/IncidentStateTimelineService.ts +34 -18
- package/Server/Services/Index.ts +4 -2
- package/Server/Services/MonitorStatusTimelineService.ts +34 -18
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +4 -2
- package/Server/Services/ProjectService.ts +6 -4
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +26 -14
- package/Server/Services/StatusPageService.ts +4 -2
- package/Server/Services/UserService.ts +21 -5
- package/Server/Services/{UserTwoFactorAuthService.ts → UserTotpAuthService.ts} +26 -7
- package/Server/Services/UserWebAuthnService.ts +419 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +257 -77
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +2 -2
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -5
- package/Server/Types/Database/Permissions/BasePermission.ts +4 -2
- package/Server/Types/Database/Permissions/TenantPermission.ts +5 -3
- package/Server/Types/Database/QueryHelper.ts +4 -2
- package/Server/Types/Markdown.ts +6 -4
- package/Server/Types/Workflow/ComponentCode.ts +4 -2
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +5 -3
- package/Server/Types/Workflow/Components/JavaScript.ts +5 -3
- package/Server/Types/Workflow/TriggerCode.ts +4 -2
- package/Server/Utils/AnalyticsDatabase/Statement.ts +4 -2
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +21 -11
- package/Server/Utils/Browser.ts +6 -4
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +4 -2
- package/Server/Utils/LocalFile.ts +14 -0
- package/Server/Utils/Monitor/MonitorResource.ts +17 -9
- package/Server/Utils/Realtime.ts +4 -2
- package/Server/Utils/StartServer.ts +1 -1
- package/Server/Utils/Telemetry.ts +15 -9
- package/Server/Utils/{TwoFactorAuth.ts → TotpAuth.ts} +2 -2
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.ts +75 -16
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +649 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +237 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +1321 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +155 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy.ts +119 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +959 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.ts +16 -14
- package/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.ts +17 -14
- package/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.ts +18 -13
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +2547 -14
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +4 -2
- package/Server/Utils/Workspace/Slack/Actions/Auth.ts +4 -2
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +14 -10
- package/Server/Utils/Workspace/Slack/Actions/Monitor.ts +4 -2
- package/Server/Utils/Workspace/Slack/Actions/OnCallDutyPolicy.ts +4 -2
- package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +14 -10
- package/Server/Utils/Workspace/Slack/Messages/Alert.ts +9 -7
- package/Server/Utils/Workspace/Slack/Messages/Incident.ts +9 -7
- package/Server/Utils/Workspace/Slack/Messages/Monitor.ts +9 -7
- package/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.ts +9 -7
- package/Server/Utils/Workspace/Slack/Slack.ts +6 -0
- package/Server/Utils/Workspace/Workspace.ts +13 -10
- package/Server/Utils/Workspace/WorkspaceBase.ts +9 -0
- package/Tests/Server/API/BaseAPI.test.ts +64 -52
- package/Tests/Server/Services/BillingService.test.ts +4 -4
- package/Tests/Server/Services/TeamMemberService.test.ts +20 -12
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +2 -2
- package/Tests/Types/OnCallDutyPolicy/LayerUtil.test.ts +8 -4
- package/Tests/UI/Components/DictionaryOfStrings.test.tsx +4 -2
- package/Tests/UI/Components/FilePicker.test.tsx +2 -2
- package/Tests/Utils/API.test.ts +9 -8
- package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +5 -3
- package/Types/Html.ts +5 -3
- package/Types/JSONFunctions.ts +5 -5
- package/Types/Metrics/MetricsQuery.ts +6 -4
- package/Types/Monitor/MonitorType.ts +8 -6
- package/Types/OnCallDutyPolicy/Layer.ts +29 -17
- package/Types/Phone.ts +5 -3
- package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +1 -0
- package/Types/Workspace/NotificationRules/CreateChannelNotificationRule.ts +5 -2
- package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
- package/Types/Workspace/WorkspaceType.ts +13 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +8 -6
- package/UI/Components/Detail/Detail.tsx +4 -1
- package/UI/Components/FilePicker/FilePicker.tsx +1 -1
- package/UI/Components/Forms/Types/Field.ts +4 -2
- package/UI/Components/Image/Image.tsx +1 -1
- package/UI/Components/JSONTable/JSONTable.tsx +4 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +5 -3
- package/UI/Components/SideMenu/SideMenu.tsx +4 -2
- package/UI/Components/SideMenu/SideMenuItem.tsx +69 -45
- package/UI/Config.ts +3 -0
- package/UI/Utils/API/API.ts +5 -3
- package/UI/Utils/Countries.ts +5 -3
- package/UI/Utils/Login.ts +6 -1
- package/Utils/Base64.ts +13 -0
- package/Utils/Schema/ModelSchema.ts +4 -2
- package/build/dist/Models/DatabaseModels/Index.js +4 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js +7 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +7 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +7 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +7 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +5 -3
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +4 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSmtpConfig.js +4 -2
- package/build/dist/Models/DatabaseModels/ProjectSmtpConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +6 -4
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +0 -49
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{UserTwoFactorAuth.js → UserTotpAuth.js} +27 -27
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +270 -0
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +4 -2
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +16 -12
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +771 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectAPI.js +4 -2
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ResellerPlanAPI.js +4 -2
- package/build/dist/Server/API/ResellerPlanAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +53 -47
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +5 -3
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +5 -3
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
- package/build/dist/Server/API/{UserTwoFactorAuthAPI.js → UserTotpAuthAPI.js} +16 -16
- package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -0
- package/build/dist/Server/API/UserWebAuthnAPI.js +65 -0
- package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +3 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1753131488925-AddEnableCustomSubscriberEmailNotificationFooterText.js +4 -2
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1753131488925-AddEnableCustomSubscriberEmailNotificationFooterText.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759175457008-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759175457008-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759232954703-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759232954703-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759234532998-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759234532998-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +4 -2
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/SocketIO.js +4 -2
- package/build/dist/Server/Infrastructure/SocketIO.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +5 -3
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +12 -6
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +34 -18
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/BillingInvoiceService.js +8 -4
- package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +13 -9
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +40 -28
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +5 -3
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +34 -18
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +34 -18
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +4 -2
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +6 -4
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +26 -14
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +4 -2
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +16 -3
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/{UserTwoFactorAuthService.js → UserTotpAuthService.js} +22 -8
- package/build/dist/Server/Services/UserTotpAuthService.js.map +1 -0
- package/build/dist/Server/Services/UserWebAuthnService.js +365 -0
- package/build/dist/Server/Services/UserWebAuthnService.js.map +1 -0
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +142 -51
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -5
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +4 -2
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +5 -3
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +4 -2
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +6 -4
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/ComponentCode.js +4 -2
- package/build/dist/Server/Types/Workflow/ComponentCode.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +5 -3
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +5 -3
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +4 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +21 -11
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Browser.js +6 -4
- package/build/dist/Server/Utils/Browser.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +4 -2
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/LocalFile.js +16 -0
- package/build/dist/Server/Utils/LocalFile.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +17 -9
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Realtime.js +4 -2
- package/build/dist/Server/Utils/Realtime.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +6 -4
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/{TwoFactorAuth.js → TotpAuth.js} +8 -8
- package/build/dist/Server/Utils/TotpAuth.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js +86 -36
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +531 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +206 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +1102 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +136 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy.js +107 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +795 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.js +16 -14
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.js +16 -14
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.js +15 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +1982 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +4 -2
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +4 -2
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +14 -10
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Monitor.js +4 -2
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Monitor.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/OnCallDutyPolicy.js +4 -2
- package/build/dist/Server/Utils/Workspace/Slack/Actions/OnCallDutyPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +14 -10
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Messages/Alert.js +9 -7
- package/build/dist/Server/Utils/Workspace/Slack/Messages/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Messages/Incident.js +9 -7
- package/build/dist/Server/Utils/Workspace/Slack/Messages/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Messages/Monitor.js +9 -7
- package/build/dist/Server/Utils/Workspace/Slack/Messages/Monitor.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.js +9 -7
- package/build/dist/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Workspace.js +12 -10
- package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/build/dist/Tests/Server/API/BaseAPI.test.js +59 -47
- package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/BillingService.test.js +4 -4
- package/build/dist/Tests/Server/Services/BillingService.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/TeamMemberService.test.js +20 -12
- package/build/dist/Tests/Server/Services/TeamMemberService.test.js.map +1 -1
- package/build/dist/Tests/Server/TestingUtils/Services/BillingServiceHelper.js +2 -2
- package/build/dist/Tests/Server/TestingUtils/Services/BillingServiceHelper.js.map +1 -1
- package/build/dist/Tests/Types/OnCallDutyPolicy/LayerUtil.test.js +8 -4
- package/build/dist/Tests/Types/OnCallDutyPolicy/LayerUtil.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/DictionaryOfStrings.test.js +4 -2
- package/build/dist/Tests/UI/Components/DictionaryOfStrings.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/FilePicker.test.js +2 -2
- package/build/dist/Tests/UI/Components/FilePicker.test.js.map +1 -1
- package/build/dist/Tests/Utils/API.test.js +8 -7
- package/build/dist/Tests/Utils/API.test.js.map +1 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +5 -3
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
- package/build/dist/Types/Html.js +5 -3
- package/build/dist/Types/Html.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +5 -5
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +8 -6
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +29 -17
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/Phone.js +5 -3
- package/build/dist/Types/Phone.js.map +1 -1
- package/build/dist/Types/Workspace/WorkspaceType.js +9 -0
- package/build/dist/Types/Workspace/WorkspaceType.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +8 -6
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Detail/Detail.js +4 -1
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/FilePicker/FilePicker.js +1 -1
- package/build/dist/UI/Components/FilePicker/FilePicker.js.map +1 -1
- package/build/dist/UI/Components/Image/Image.js +1 -1
- package/build/dist/UI/Components/Image/Image.js.map +1 -1
- package/build/dist/UI/Components/JSONTable/JSONTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -2
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js +62 -38
- package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
- package/build/dist/UI/Config.js +1 -0
- package/build/dist/UI/Config.js.map +1 -1
- package/build/dist/UI/Utils/API/API.js +5 -3
- package/build/dist/UI/Utils/API/API.js.map +1 -1
- package/build/dist/UI/Utils/Countries.js.map +1 -1
- package/build/dist/UI/Utils/Login.js +6 -1
- package/build/dist/UI/Utils/Login.js.map +1 -1
- package/build/dist/Utils/Base64.js +12 -0
- package/build/dist/Utils/Base64.js.map +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js +4 -2
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/package.json +5 -1
- package/build/dist/Models/DatabaseModels/UserTwoFactorAuth.js.map +0 -1
- package/build/dist/Server/API/UserTwoFactorAuthAPI.js.map +0 -1
- package/build/dist/Server/Services/UserTwoFactorAuthService.js.map +0 -1
- package/build/dist/Server/Utils/TwoFactorAuth.js.map +0 -1
|
@@ -8,15 +8,238 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import HTTPErrorResponse from "../../../../Types/API/HTTPErrorResponse";
|
|
11
|
+
import URL from "../../../../Types/API/URL";
|
|
11
12
|
import API from "../../../../Utils/API";
|
|
12
13
|
import logger from "../../Logger";
|
|
13
14
|
import WorkspaceBase from "../WorkspaceBase";
|
|
15
|
+
import WorkspaceType from "../../../../Types/Workspace/WorkspaceType";
|
|
14
16
|
import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
15
|
-
|
|
17
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
18
|
+
import WorkspaceProjectAuthTokenService from "../../../Services/WorkspaceProjectAuthTokenService";
|
|
19
|
+
import OneUptimeDate from "../../../../Types/Date";
|
|
20
|
+
import { MicrosoftTeamsAppClientId, MicrosoftTeamsAppClientSecret, } from "../../../EnvironmentConfig";
|
|
21
|
+
// Import services for bot commands
|
|
22
|
+
import IncidentService from "../../../Services/IncidentService";
|
|
23
|
+
import AlertService from "../../../Services/AlertService";
|
|
24
|
+
import ScheduledMaintenanceService from "../../../Services/ScheduledMaintenanceService";
|
|
25
|
+
import IncidentStateService from "../../../Services/IncidentStateService";
|
|
26
|
+
import AlertStateService from "../../../Services/AlertStateService";
|
|
27
|
+
// Import database utilities
|
|
28
|
+
import QueryHelper from "../../../Types/Database/QueryHelper";
|
|
29
|
+
import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
|
|
30
|
+
// Microsoft Teams apps should always be single-tenant
|
|
31
|
+
const MICROSOFT_TEAMS_APP_TYPE = "SingleTenant";
|
|
32
|
+
// Bot Framework SDK imports
|
|
33
|
+
import { CloudAdapter, ConfigurationBotFrameworkAuthentication, TeamsActivityHandler, MessageFactory, } from "botbuilder";
|
|
34
|
+
// Teams action handlers and types
|
|
35
|
+
import MicrosoftTeamsAuthAction from "./Actions/Auth";
|
|
36
|
+
import MicrosoftTeamsIncidentActions from "./Actions/Incident";
|
|
37
|
+
import MicrosoftTeamsAlertActions from "./Actions/Alert";
|
|
38
|
+
import MicrosoftTeamsMonitorActions from "./Actions/Monitor";
|
|
39
|
+
import MicrosoftTeamsScheduledMaintenanceActions from "./Actions/ScheduledMaintenance";
|
|
40
|
+
import MicrosoftTeamsOnCallDutyActions from "./Actions/OnCallDutyPolicy";
|
|
41
|
+
export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
42
|
+
// Get or create Bot Framework adapter for a specific tenant
|
|
43
|
+
static getBotAdapter(microsoftAppTenantId) {
|
|
44
|
+
if (!MicrosoftTeamsAppClientId || !MicrosoftTeamsAppClientSecret) {
|
|
45
|
+
throw new BadDataException("Microsoft Teams App credentials not configured");
|
|
46
|
+
}
|
|
47
|
+
logger.debug("Creating Bot Framework adapter with authentication configuration");
|
|
48
|
+
logger.debug(`App ID: ${MicrosoftTeamsAppClientId}`);
|
|
49
|
+
logger.debug(`App Type: ${MICROSOFT_TEAMS_APP_TYPE}`);
|
|
50
|
+
logger.debug(`Tenant ID: ${microsoftAppTenantId}`);
|
|
51
|
+
const authConfig = {
|
|
52
|
+
MicrosoftAppId: MicrosoftTeamsAppClientId,
|
|
53
|
+
MicrosoftAppPassword: MicrosoftTeamsAppClientSecret,
|
|
54
|
+
MicrosoftAppType: MICROSOFT_TEAMS_APP_TYPE,
|
|
55
|
+
MicrosoftAppTenantId: microsoftAppTenantId,
|
|
56
|
+
};
|
|
57
|
+
const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(authConfig);
|
|
58
|
+
const adapter = new CloudAdapter(botFrameworkAuthentication);
|
|
59
|
+
logger.debug("Bot Framework adapter created successfully");
|
|
60
|
+
return adapter;
|
|
61
|
+
}
|
|
62
|
+
// Helper method to get a valid access token, refreshing if necessary
|
|
63
|
+
static async getValidAccessToken(data) {
|
|
64
|
+
var _a;
|
|
65
|
+
logger.debug("=== getValidAccessToken called ===");
|
|
66
|
+
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
67
|
+
logger.debug(`Auth token (first 20 chars): ${(_a = data.authToken) === null || _a === void 0 ? void 0 : _a.substring(0, 20)}...`);
|
|
68
|
+
// Get project auth and check token expiration
|
|
69
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
70
|
+
projectId: data.projectId,
|
|
71
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
72
|
+
});
|
|
73
|
+
logger.debug(`Project auth found: ${Boolean(projectAuth)}`);
|
|
74
|
+
if (projectAuth) {
|
|
75
|
+
logger.debug(`Project auth has miscData: ${Boolean(projectAuth.miscData)}`);
|
|
76
|
+
}
|
|
77
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
78
|
+
logger.error("Microsoft Teams integration not found for this project - no project auth or miscData");
|
|
79
|
+
throw new BadDataException("Microsoft Teams integration not found for this project");
|
|
80
|
+
}
|
|
81
|
+
const miscData = projectAuth.miscData;
|
|
82
|
+
logger.debug(`MiscData appAccessToken exists: ${Boolean(miscData.appAccessToken)}`);
|
|
83
|
+
logger.debug(`MiscData appAccessTokenExpiresAt: ${miscData.appAccessTokenExpiresAt}`);
|
|
84
|
+
// Check if token exists and is valid
|
|
85
|
+
if (miscData.appAccessToken && miscData.appAccessToken.includes(".")) {
|
|
86
|
+
logger.debug("Found app access token in miscData");
|
|
87
|
+
// Check if token is expired
|
|
88
|
+
if (miscData.appAccessTokenExpiresAt) {
|
|
89
|
+
const expiryDate = OneUptimeDate.fromString(miscData.appAccessTokenExpiresAt);
|
|
90
|
+
const now = OneUptimeDate.getCurrentDate();
|
|
91
|
+
const isExpired = OneUptimeDate.isAfter(now, expiryDate);
|
|
92
|
+
const secondsToExpiry = OneUptimeDate.getSecondsTo(expiryDate);
|
|
93
|
+
logger.debug(`Token expires in ${secondsToExpiry} seconds`);
|
|
94
|
+
logger.debug(`Token is expired: ${isExpired}`);
|
|
95
|
+
// If token is already expired or expires within the next 5 minutes, refresh it
|
|
96
|
+
if (isExpired || secondsToExpiry <= 300) {
|
|
97
|
+
logger.debug("Access token is expired or expiring soon, attempting to refresh");
|
|
98
|
+
const newToken = await this.refreshAccessToken({
|
|
99
|
+
projectId: data.projectId,
|
|
100
|
+
miscData,
|
|
101
|
+
});
|
|
102
|
+
if (newToken) {
|
|
103
|
+
logger.debug("Successfully refreshed token");
|
|
104
|
+
return newToken;
|
|
105
|
+
}
|
|
106
|
+
logger.warn("Failed to refresh token, falling back to cached token");
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
logger.debug("Using cached appAccessToken from miscData for Microsoft Graph API call");
|
|
110
|
+
return miscData.appAccessToken;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// No expiry information, use the token but it might be expired
|
|
115
|
+
logger.debug("Using appAccessToken from miscData (no expiry info available)");
|
|
116
|
+
return miscData.appAccessToken;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// If we couldn't find a valid token, try to refresh
|
|
120
|
+
logger.debug("No valid app access token found, attempting to refresh");
|
|
121
|
+
const newToken = await this.refreshAccessToken({
|
|
122
|
+
projectId: data.projectId,
|
|
123
|
+
miscData,
|
|
124
|
+
});
|
|
125
|
+
if (newToken) {
|
|
126
|
+
logger.debug("Successfully refreshed token");
|
|
127
|
+
return newToken;
|
|
128
|
+
}
|
|
129
|
+
// If refresh failed, throw error
|
|
130
|
+
logger.error("Could not obtain valid access token for Microsoft Teams");
|
|
131
|
+
throw new BadDataException("Could not obtain valid access token for Microsoft Teams");
|
|
132
|
+
}
|
|
133
|
+
// Method to refresh the Microsoft Teams access token
|
|
134
|
+
static async refreshAccessToken(data) {
|
|
135
|
+
logger.debug("=== refreshAccessToken called ===");
|
|
136
|
+
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
137
|
+
logger.debug(`Tenant ID: ${data.miscData.tenantId}`);
|
|
138
|
+
try {
|
|
139
|
+
// Check if we have the necessary client credentials
|
|
140
|
+
if (!MicrosoftTeamsAppClientId || !MicrosoftTeamsAppClientSecret) {
|
|
141
|
+
logger.error("Microsoft Teams app client credentials are not configured");
|
|
142
|
+
logger.error("Please set MICROSOFT_TEAMS_APP_CLIENT_ID and MICROSOFT_TEAMS_APP_CLIENT_SECRET environment variables");
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
logger.debug("Client credentials are configured");
|
|
146
|
+
if (!data.miscData.tenantId) {
|
|
147
|
+
logger.error("Tenant ID not found in miscData, cannot refresh token");
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
logger.debug(`Attempting to refresh Microsoft Teams access token for project ${data.projectId.toString()}`);
|
|
151
|
+
logger.debug(`Using tenant ID: ${data.miscData.tenantId}`);
|
|
152
|
+
// Use OAuth 2.0 client credentials flow to get a new app access token
|
|
153
|
+
const tokenUrl = `https://login.microsoftonline.com/${data.miscData.tenantId}/oauth2/v2.0/token`;
|
|
154
|
+
logger.debug(`Token URL: ${tokenUrl}`);
|
|
155
|
+
const tokenRequestBody = {
|
|
156
|
+
client_id: MicrosoftTeamsAppClientId,
|
|
157
|
+
client_secret: MicrosoftTeamsAppClientSecret,
|
|
158
|
+
grant_type: "client_credentials",
|
|
159
|
+
scope: "https://graph.microsoft.com/.default",
|
|
160
|
+
};
|
|
161
|
+
logger.debug("Making token refresh request to Microsoft");
|
|
162
|
+
const response = await API.post({
|
|
163
|
+
url: URL.fromString(tokenUrl),
|
|
164
|
+
data: tokenRequestBody,
|
|
165
|
+
headers: {
|
|
166
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
167
|
+
Accept: "application/json",
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
if (response instanceof HTTPErrorResponse) {
|
|
171
|
+
logger.error("Error refreshing Microsoft Teams access token:");
|
|
172
|
+
logger.error(response);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
logger.debug("Token refresh response received successfully");
|
|
176
|
+
const tokenData = response.data;
|
|
177
|
+
const newAccessToken = tokenData["access_token"];
|
|
178
|
+
const expiresIn = tokenData["expires_in"]; // seconds
|
|
179
|
+
logger.debug(`New access token received: ${Boolean(newAccessToken)}`);
|
|
180
|
+
logger.debug(`Token expires in: ${expiresIn} seconds`);
|
|
181
|
+
if (!newAccessToken) {
|
|
182
|
+
logger.error("No access token received in token refresh response");
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
// Calculate expiry time
|
|
186
|
+
const now = OneUptimeDate.getCurrentDate();
|
|
187
|
+
const expiryDate = OneUptimeDate.addRemoveSeconds(now, expiresIn - 300); // Subtrutes buffer
|
|
188
|
+
logger.debug(`Token expiry calculated: ${OneUptimeDate.toString(expiryDate)}`);
|
|
189
|
+
// Update the miscData with new token and expiry
|
|
190
|
+
const updatedMiscData = Object.assign(Object.assign({}, data.miscData), { appAccessToken: newAccessToken, appAccessTokenExpiresAt: OneUptimeDate.toString(expiryDate), lastAppTokenIssuedAt: OneUptimeDate.toString(now) });
|
|
191
|
+
logger.debug("Saving updated token to database");
|
|
192
|
+
// Save the updated token to the database
|
|
193
|
+
await WorkspaceProjectAuthTokenService.refreshAuthToken({
|
|
194
|
+
projectId: data.projectId,
|
|
195
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
196
|
+
authToken: newAccessToken,
|
|
197
|
+
workspaceProjectId: data.miscData.tenantId,
|
|
198
|
+
miscData: updatedMiscData,
|
|
199
|
+
});
|
|
200
|
+
logger.debug("Microsoft Teams access token refreshed successfully");
|
|
201
|
+
logger.debug(`New token expires at: ${updatedMiscData.appAccessTokenExpiresAt}`);
|
|
202
|
+
return newAccessToken;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logger.error("Error refreshing Microsoft Teams access token:");
|
|
206
|
+
logger.error(error);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Extract action type and value from Teams Adaptive Card submit value
|
|
211
|
+
static extractActionFromValue(value) {
|
|
212
|
+
/*
|
|
213
|
+
* Support multiple shapes that Teams may send for Adaptive Card submits
|
|
214
|
+
* 1) { action: "ack-incident", actionValue: "<id>" }
|
|
215
|
+
* 2) { data: { action: "ack-incident", actionValue: "<id>" } }
|
|
216
|
+
* 3) { action: { type: "Action.Submit", data: { action: "ack-incident", actionValue: "<id>" } } }
|
|
217
|
+
*/
|
|
218
|
+
let actionType = value["action"] || "";
|
|
219
|
+
let actionValue = value["actionValue"] || "";
|
|
220
|
+
const valData = value["data"] || undefined;
|
|
221
|
+
if ((!actionType || !actionValue) && valData) {
|
|
222
|
+
actionType = valData["action"] || actionType;
|
|
223
|
+
actionValue = valData["actionValue"] || actionValue;
|
|
224
|
+
}
|
|
225
|
+
const actionObj = value["action"];
|
|
226
|
+
if ((!actionType || !actionValue) &&
|
|
227
|
+
actionObj &&
|
|
228
|
+
typeof actionObj === "object") {
|
|
229
|
+
const embeddedData = actionObj["data"] || undefined;
|
|
230
|
+
if (embeddedData) {
|
|
231
|
+
actionType = embeddedData["action"] || actionType;
|
|
232
|
+
actionValue = embeddedData["actionValue"] || actionValue;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return { actionType: actionType, actionValue };
|
|
236
|
+
}
|
|
16
237
|
static buildMessageCardFromMarkdown(markdown) {
|
|
17
238
|
var _a, _b, _c, _d, _e;
|
|
18
|
-
|
|
19
|
-
|
|
239
|
+
/*
|
|
240
|
+
* Teams MessageCard has limited markdown support. Headings like '##' are not supported
|
|
241
|
+
* and single newlines can collapse. Convert common patterns to a structured card.
|
|
242
|
+
*/
|
|
20
243
|
const lines = markdown
|
|
21
244
|
.split("\n")
|
|
22
245
|
.map((l) => {
|
|
@@ -36,11 +259,29 @@ export default class MicrosoftTeams extends WorkspaceBase {
|
|
|
36
259
|
.replace(/^#+\s*/, "") // remove leading markdown headers like ##
|
|
37
260
|
.replace(/^\*\*|\*\*$/g, "") // remove stray bold markers if any
|
|
38
261
|
.trim();
|
|
262
|
+
// Remove markdown link syntax from title for cleaner rendering
|
|
263
|
+
const titleLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
264
|
+
title = title.replace(titleLinkRegex, "$1");
|
|
265
|
+
// Sanitize unmatched bold markers if any remain
|
|
266
|
+
const boldCountTitle = (title.match(/\*\*/g) || []).length;
|
|
267
|
+
if (boldCountTitle % 2 !== 0) {
|
|
268
|
+
title = title.replace(/\*\*/g, "");
|
|
269
|
+
}
|
|
39
270
|
lines.shift();
|
|
40
271
|
}
|
|
41
272
|
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; // [text](url)
|
|
273
|
+
// Helper to clean up unmatched bold markers that can break rendering
|
|
274
|
+
const sanitizeMarkdownText = (text) => {
|
|
275
|
+
const boldCount = (text.match(/\*\*/g) || []).length;
|
|
276
|
+
// If we have an odd number of **, remove them all to avoid raw markers showing
|
|
277
|
+
if (boldCount % 2 !== 0) {
|
|
278
|
+
text = text.replace(/\*\*/g, "");
|
|
279
|
+
}
|
|
280
|
+
// Collapse multiple spaces introduced by replacements
|
|
281
|
+
return text.replace(/\s{2,}/g, " ");
|
|
282
|
+
};
|
|
42
283
|
for (const line of lines) {
|
|
43
|
-
// Extract links to actions and
|
|
284
|
+
// Extract links to actions and keep link display text in-place (without markdown)
|
|
44
285
|
let lineWithoutLinks = line;
|
|
45
286
|
let match = null;
|
|
46
287
|
while ((match = linkRegex.exec(line))) {
|
|
@@ -56,7 +297,8 @@ export default class MicrosoftTeams extends WorkspaceBase {
|
|
|
56
297
|
},
|
|
57
298
|
],
|
|
58
299
|
});
|
|
59
|
-
|
|
300
|
+
// Replace markdown link with just the display text to preserve sentence flow
|
|
301
|
+
lineWithoutLinks = lineWithoutLinks.replace(match[0], name).trim();
|
|
60
302
|
}
|
|
61
303
|
// Parse facts of the form **Label:** value
|
|
62
304
|
const factMatch = new RegExp("\\*\\*(.*?):\\*\\*\\s*(.*)").exec(lineWithoutLinks);
|
|
@@ -72,7 +314,7 @@ export default class MicrosoftTeams extends WorkspaceBase {
|
|
|
72
314
|
}
|
|
73
315
|
}
|
|
74
316
|
else if (lineWithoutLinks) {
|
|
75
|
-
bodyTextParts.push(lineWithoutLinks);
|
|
317
|
+
bodyTextParts.push(sanitizeMarkdownText(lineWithoutLinks));
|
|
76
318
|
}
|
|
77
319
|
}
|
|
78
320
|
const payload = {
|
|
@@ -81,15 +323,16 @@ export default class MicrosoftTeams extends WorkspaceBase {
|
|
|
81
323
|
title: title,
|
|
82
324
|
summary: title,
|
|
83
325
|
};
|
|
326
|
+
// Build a single section so we can enable markdown explicitly
|
|
327
|
+
const section = { markdown: true };
|
|
84
328
|
if (bodyTextParts.length > 0) {
|
|
85
|
-
|
|
329
|
+
section["text"] = bodyTextParts.join("\n\n");
|
|
86
330
|
}
|
|
87
331
|
if (facts.length > 0) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
];
|
|
332
|
+
section["facts"] = facts;
|
|
333
|
+
}
|
|
334
|
+
if (section["text"] || section["facts"]) {
|
|
335
|
+
payload["sections"] = [section];
|
|
93
336
|
}
|
|
94
337
|
if (actions.length > 0) {
|
|
95
338
|
payload["potentialAction"] = actions;
|
|
@@ -124,11 +367,1737 @@ export default class MicrosoftTeams extends WorkspaceBase {
|
|
|
124
367
|
return (urlString.includes("outlook.office.com") ||
|
|
125
368
|
urlString.includes("office.com"));
|
|
126
369
|
}
|
|
370
|
+
static async getUsernameFromUserId(data) {
|
|
371
|
+
logger.debug("Getting username from user ID with data:");
|
|
372
|
+
logger.debug(data);
|
|
373
|
+
// Get valid access token
|
|
374
|
+
const accessToken = await this.getValidAccessToken({
|
|
375
|
+
authToken: data.authToken,
|
|
376
|
+
projectId: data.projectId,
|
|
377
|
+
});
|
|
378
|
+
const response = await API.get({
|
|
379
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/users/${data.userId}`),
|
|
380
|
+
headers: {
|
|
381
|
+
Authorization: `Bearer ${accessToken}`,
|
|
382
|
+
"Content-Type": "application/json",
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
logger.debug("Response from Microsoft Graph API for getting user info:");
|
|
386
|
+
logger.debug(response);
|
|
387
|
+
if (response instanceof HTTPErrorResponse) {
|
|
388
|
+
logger.error("Error response from Microsoft Graph API:");
|
|
389
|
+
logger.error(response);
|
|
390
|
+
throw response;
|
|
391
|
+
}
|
|
392
|
+
const userData = response.data;
|
|
393
|
+
const username = userData["displayName"] ||
|
|
394
|
+
userData["userPrincipalName"];
|
|
395
|
+
logger.debug("Username obtained:");
|
|
396
|
+
logger.debug(username);
|
|
397
|
+
return username;
|
|
398
|
+
}
|
|
399
|
+
static async sendDirectMessageToUser(data) {
|
|
400
|
+
// Send direct message to user via Microsoft Graph API
|
|
401
|
+
const adaptiveCard = this.buildAdaptiveCardFromMessageBlocks({
|
|
402
|
+
messageBlocks: data.messageBlocks,
|
|
403
|
+
});
|
|
404
|
+
const chatMessage = {
|
|
405
|
+
body: {
|
|
406
|
+
contentType: "html",
|
|
407
|
+
content: this.convertAdaptiveCardToHtml(adaptiveCard),
|
|
408
|
+
},
|
|
409
|
+
attachments: [
|
|
410
|
+
{
|
|
411
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
412
|
+
content: adaptiveCard,
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
};
|
|
416
|
+
await API.post({
|
|
417
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/chats/${data.workspaceUserId}/messages`),
|
|
418
|
+
data: chatMessage,
|
|
419
|
+
headers: {
|
|
420
|
+
Authorization: `Bearer ${data.authToken}`,
|
|
421
|
+
"Content-Type": "application/json",
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
static async createChannelsIfDoesNotExist(data) {
|
|
426
|
+
logger.debug("Creating channels if they do not exist with data:");
|
|
427
|
+
logger.debug(data);
|
|
428
|
+
const workspaceChannels = [];
|
|
429
|
+
for (let channelName of data.channelNames) {
|
|
430
|
+
// Normalize channel name - Teams has different naming requirements
|
|
431
|
+
if (channelName && channelName.startsWith("#")) {
|
|
432
|
+
channelName = channelName.substring(1);
|
|
433
|
+
}
|
|
434
|
+
// Teams channels cannot have spaces in the name for some operations
|
|
435
|
+
const normalizedChannelName = channelName.replace(/\s+/g, "-");
|
|
436
|
+
// Check if channel exists
|
|
437
|
+
const existingChannel = await this.getWorkspaceChannelByName({
|
|
438
|
+
authToken: data.authToken,
|
|
439
|
+
channelName: normalizedChannelName,
|
|
440
|
+
projectId: data.projectId,
|
|
441
|
+
teamId: data.teamId,
|
|
442
|
+
});
|
|
443
|
+
if (existingChannel) {
|
|
444
|
+
logger.debug(`Channel ${channelName} already exists.`);
|
|
445
|
+
workspaceChannels.push(existingChannel);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
logger.debug(`Channel ${channelName} does not exist. Creating channel.`);
|
|
449
|
+
const createChannelData = {
|
|
450
|
+
authToken: data.authToken,
|
|
451
|
+
channelName: normalizedChannelName,
|
|
452
|
+
projectId: data.projectId,
|
|
453
|
+
teamId: data.teamId,
|
|
454
|
+
};
|
|
455
|
+
const channel = await this.createChannel(createChannelData);
|
|
456
|
+
if (channel) {
|
|
457
|
+
logger.debug(`Channel ${channelName} created successfully.`);
|
|
458
|
+
workspaceChannels.push(channel);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
logger.debug("Channels created or found:");
|
|
462
|
+
logger.debug(workspaceChannels);
|
|
463
|
+
return workspaceChannels;
|
|
464
|
+
}
|
|
465
|
+
static async createChannel(data) {
|
|
466
|
+
const teamId = data.teamId;
|
|
467
|
+
// Get valid access token
|
|
468
|
+
const accessToken = await this.getValidAccessToken({
|
|
469
|
+
authToken: data.authToken,
|
|
470
|
+
projectId: data.projectId,
|
|
471
|
+
});
|
|
472
|
+
const channelPayload = {
|
|
473
|
+
displayName: data.channelName,
|
|
474
|
+
description: `OneUptime notifications for ${data.channelName}`,
|
|
475
|
+
membershipType: "standard",
|
|
476
|
+
};
|
|
477
|
+
logger.debug("Creating Teams channel with payload:");
|
|
478
|
+
logger.debug(channelPayload);
|
|
479
|
+
const response = await API.post({
|
|
480
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/teams/${teamId}/channels`),
|
|
481
|
+
data: channelPayload,
|
|
482
|
+
headers: {
|
|
483
|
+
Authorization: `Bearer ${accessToken}`,
|
|
484
|
+
"Content-Type": "application/json",
|
|
485
|
+
},
|
|
486
|
+
});
|
|
487
|
+
if (response instanceof HTTPErrorResponse) {
|
|
488
|
+
logger.error("Error response from Microsoft Graph API:");
|
|
489
|
+
logger.error(response);
|
|
490
|
+
throw response;
|
|
491
|
+
}
|
|
492
|
+
const channelData = response.data;
|
|
493
|
+
const channel = {
|
|
494
|
+
id: channelData["id"],
|
|
495
|
+
name: channelData["displayName"],
|
|
496
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
497
|
+
teamId: data.teamId,
|
|
498
|
+
};
|
|
499
|
+
logger.debug("Channel created successfully:");
|
|
500
|
+
logger.debug(channel);
|
|
501
|
+
return channel;
|
|
502
|
+
}
|
|
503
|
+
static async getWorkspaceChannelFromChannelName(data) {
|
|
504
|
+
const channel = await this.getWorkspaceChannelByName({
|
|
505
|
+
authToken: data.authToken,
|
|
506
|
+
channelName: data.channelName,
|
|
507
|
+
projectId: data.projectId,
|
|
508
|
+
teamId: data.teamId,
|
|
509
|
+
});
|
|
510
|
+
if (!channel) {
|
|
511
|
+
throw new BadDataException("Channel not found.");
|
|
512
|
+
}
|
|
513
|
+
return channel;
|
|
514
|
+
}
|
|
515
|
+
static async getWorkspaceChannelByName(data) {
|
|
516
|
+
logger.debug(`Getting workspace channel by name: ${data.channelName}`);
|
|
517
|
+
// Get project auth to get available teams
|
|
518
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
519
|
+
projectId: data.projectId,
|
|
520
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
521
|
+
});
|
|
522
|
+
if (!(projectAuth === null || projectAuth === void 0 ? void 0 : projectAuth.miscData)) {
|
|
523
|
+
logger.error("Microsoft Teams integration not found for this project");
|
|
524
|
+
throw new BadDataException("Microsoft Teams integration not found for this project");
|
|
525
|
+
}
|
|
526
|
+
// Get valid access token
|
|
527
|
+
const accessToken = await this.getValidAccessToken({
|
|
528
|
+
authToken: data.authToken,
|
|
529
|
+
projectId: data.projectId,
|
|
530
|
+
});
|
|
531
|
+
// Get channels for this team
|
|
532
|
+
const response = await API.get({
|
|
533
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/teams/${data.teamId}/channels`),
|
|
534
|
+
headers: {
|
|
535
|
+
Authorization: `Bearer ${accessToken}`,
|
|
536
|
+
"Content-Type": "application/json",
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
if (response instanceof HTTPErrorResponse) {
|
|
540
|
+
logger.error("Error response from Microsoft Graph API:");
|
|
541
|
+
logger.error(response);
|
|
542
|
+
throw response;
|
|
543
|
+
}
|
|
544
|
+
const channelsData = response.data;
|
|
545
|
+
const channels = channelsData["value"] || [];
|
|
546
|
+
logger.debug(`Found ${channels.length} channels from API`);
|
|
547
|
+
const channelName = data.channelName.toLowerCase();
|
|
548
|
+
for (const channelData of channels) {
|
|
549
|
+
const displayName = channelData["displayName"];
|
|
550
|
+
if (!displayName) {
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
const apiChannelName = displayName.toLowerCase();
|
|
554
|
+
logger.debug(`Comparing channel '${apiChannelName}' with requested '${channelName}'`);
|
|
555
|
+
if (apiChannelName === channelName) {
|
|
556
|
+
const foundChannel = {
|
|
557
|
+
id: `${channelData["id"]}`,
|
|
558
|
+
name: displayName,
|
|
559
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
560
|
+
teamId: data.teamId,
|
|
561
|
+
};
|
|
562
|
+
logger.debug(`Channel match found: ${JSON.stringify(foundChannel)}`);
|
|
563
|
+
return foundChannel;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
logger.debug(`No channel found with name: ${data.channelName}`);
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
static async sendMessage(data) {
|
|
570
|
+
logger.debug("=== MicrosoftTeamsUtil.sendMessage called ===");
|
|
571
|
+
logger.debug("Sending message to Microsoft Teams with data:");
|
|
572
|
+
logger.debug(data);
|
|
573
|
+
const adaptiveCard = this.buildAdaptiveCardFromMessageBlocks({
|
|
574
|
+
messageBlocks: data.workspaceMessagePayload.messageBlocks,
|
|
575
|
+
});
|
|
576
|
+
logger.debug("Adaptive card built successfully:");
|
|
577
|
+
logger.debug(JSON.stringify(adaptiveCard, null, 2));
|
|
578
|
+
const workspaceChannelsToPostTo = [];
|
|
579
|
+
logger.debug(`Processing ${data.workspaceMessagePayload.channelNames.length} channel names`);
|
|
580
|
+
logger.debug(`Channel names: ${JSON.stringify(data.workspaceMessagePayload.channelNames)}`);
|
|
581
|
+
// Resolve channel names
|
|
582
|
+
for (const channelName of data.workspaceMessagePayload.channelNames) {
|
|
583
|
+
logger.debug(`Attempting to resolve channel name: ${channelName}`);
|
|
584
|
+
if (!data.workspaceMessagePayload.teamId) {
|
|
585
|
+
throw new BadDataException("Team ID is required to resolve channel names.");
|
|
586
|
+
}
|
|
587
|
+
const channel = await this.getWorkspaceChannelByName({
|
|
588
|
+
authToken: data.authToken,
|
|
589
|
+
channelName: channelName,
|
|
590
|
+
projectId: data.projectId,
|
|
591
|
+
teamId: data.workspaceMessagePayload.teamId,
|
|
592
|
+
});
|
|
593
|
+
if (channel) {
|
|
594
|
+
logger.debug(`Channel resolved successfully: ${JSON.stringify(channel)}`);
|
|
595
|
+
workspaceChannelsToPostTo.push(channel);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
logger.warn(`Channel not found: ${channelName}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
logger.debug("=== Starting message sending loop ===");
|
|
602
|
+
logger.debug(`Total channels to post to: ${workspaceChannelsToPostTo.length}`);
|
|
603
|
+
logger.debug(`Channels: ${JSON.stringify(workspaceChannelsToPostTo)}`);
|
|
604
|
+
// Add channels by ID
|
|
605
|
+
for (const channelId of data.workspaceMessagePayload.channelIds) {
|
|
606
|
+
if (!data.workspaceMessagePayload.teamId) {
|
|
607
|
+
throw new BadDataException("Team ID is required to resolve channel IDs.");
|
|
608
|
+
}
|
|
609
|
+
try {
|
|
610
|
+
logger.debug(`Getting channel info for channel ID: ${channelId}`);
|
|
611
|
+
const channel = await this.getWorkspaceChannelFromChannelId({
|
|
612
|
+
authToken: data.authToken,
|
|
613
|
+
channelId: channelId,
|
|
614
|
+
projectId: data.projectId,
|
|
615
|
+
teamId: data.workspaceMessagePayload.teamId,
|
|
616
|
+
});
|
|
617
|
+
logger.debug(`Channel info obtained: ${JSON.stringify(channel)}`);
|
|
618
|
+
workspaceChannelsToPostTo.push(channel);
|
|
619
|
+
}
|
|
620
|
+
catch (err) {
|
|
621
|
+
logger.error(`Error getting channel info for channel ID ${channelId}:`);
|
|
622
|
+
logger.error(err);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
logger.debug("=== Starting message sending loop ===");
|
|
626
|
+
logger.debug(`Total channels to post to: ${workspaceChannelsToPostTo.length}`);
|
|
627
|
+
logger.debug(`Channels: ${JSON.stringify(workspaceChannelsToPostTo)}`);
|
|
628
|
+
const workspaceMessageResponse = {
|
|
629
|
+
threads: [],
|
|
630
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
631
|
+
errors: [],
|
|
632
|
+
};
|
|
633
|
+
for (const channel of workspaceChannelsToPostTo) {
|
|
634
|
+
try {
|
|
635
|
+
logger.debug(`Attempting to send message to channel: ${JSON.stringify(channel)}`);
|
|
636
|
+
if (!data.workspaceMessagePayload.teamId) {
|
|
637
|
+
throw new BadDataException("Team ID is required to send messages to channels.");
|
|
638
|
+
}
|
|
639
|
+
const thread = await this.sendAdaptiveCardToChannel({
|
|
640
|
+
authToken: data.authToken,
|
|
641
|
+
teamId: data.workspaceMessagePayload.teamId,
|
|
642
|
+
workspaceChannel: channel,
|
|
643
|
+
adaptiveCard: adaptiveCard,
|
|
644
|
+
projectId: data.projectId,
|
|
645
|
+
});
|
|
646
|
+
logger.debug(`Message sent successfully to channel ${channel.name}, thread: ${JSON.stringify(thread)}`);
|
|
647
|
+
workspaceMessageResponse.threads.push(thread);
|
|
648
|
+
}
|
|
649
|
+
catch (e) {
|
|
650
|
+
logger.error(`Error sending message to channel ID ${channel.id}:`);
|
|
651
|
+
logger.error(e);
|
|
652
|
+
workspaceMessageResponse.errors.push({
|
|
653
|
+
channel: channel,
|
|
654
|
+
error: e instanceof Error ? e.message : String(e),
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
logger.debug("=== Message sending completed ===");
|
|
659
|
+
logger.debug(`Final thread count: ${workspaceMessageResponse.threads.length}`);
|
|
660
|
+
logger.debug(`Final response: ${JSON.stringify(workspaceMessageResponse)}`);
|
|
661
|
+
return workspaceMessageResponse;
|
|
662
|
+
}
|
|
663
|
+
static async sendAdaptiveCardToChannel(data) {
|
|
664
|
+
logger.debug(`Sending adaptive card to channel via Bot Framework: ${data.workspaceChannel.name} (${data.workspaceChannel.id})`);
|
|
665
|
+
logger.debug(`Team ID: ${data.teamId}`);
|
|
666
|
+
logger.debug(`Adaptive card: ${JSON.stringify(data.adaptiveCard)}`);
|
|
667
|
+
try {
|
|
668
|
+
// Get project auth to retrieve bot ID
|
|
669
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
670
|
+
projectId: data.projectId,
|
|
671
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
672
|
+
});
|
|
673
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
674
|
+
throw new BadDataException("Microsoft Teams integration not found for this project");
|
|
675
|
+
}
|
|
676
|
+
const miscData = projectAuth.miscData;
|
|
677
|
+
if (!miscData.botId) {
|
|
678
|
+
throw new BadDataException("Bot ID not found in Microsoft Teams integration");
|
|
679
|
+
}
|
|
680
|
+
// Check if app client ID is configured
|
|
681
|
+
if (!MicrosoftTeamsAppClientId) {
|
|
682
|
+
throw new BadDataException("Microsoft Teams App Client ID not configured");
|
|
683
|
+
}
|
|
684
|
+
logger.debug(`Using bot ID: ${miscData.botId}`);
|
|
685
|
+
// Get Bot Framework adapter
|
|
686
|
+
const adapter = this.getBotAdapter(miscData.tenantId);
|
|
687
|
+
// Create conversation reference for the channel
|
|
688
|
+
const conversationReference = {
|
|
689
|
+
bot: {
|
|
690
|
+
id: MicrosoftTeamsAppClientId,
|
|
691
|
+
name: "OneUptime Bot",
|
|
692
|
+
},
|
|
693
|
+
conversation: {
|
|
694
|
+
id: data.workspaceChannel.id,
|
|
695
|
+
name: data.workspaceChannel.name,
|
|
696
|
+
isGroup: true,
|
|
697
|
+
conversationType: "channel",
|
|
698
|
+
tenantId: miscData.tenantId,
|
|
699
|
+
},
|
|
700
|
+
channelId: "msteams",
|
|
701
|
+
serviceUrl: "https://smba.trafficmanager.net/teams/",
|
|
702
|
+
};
|
|
703
|
+
logger.debug(`Conversation reference: ${JSON.stringify(conversationReference)}`);
|
|
704
|
+
// Send proactive message using Bot Framework
|
|
705
|
+
let messageId = "";
|
|
706
|
+
await adapter.continueConversationAsync(MicrosoftTeamsAppClientId, conversationReference, async (context) => {
|
|
707
|
+
logger.debug("Sending adaptive card as proactive message");
|
|
708
|
+
// Create message with adaptive card attachment
|
|
709
|
+
const message = MessageFactory.attachment({
|
|
710
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
711
|
+
content: data.adaptiveCard,
|
|
712
|
+
});
|
|
713
|
+
const response = await context.sendActivity(message);
|
|
714
|
+
messageId = (response === null || response === void 0 ? void 0 : response.id) || "";
|
|
715
|
+
logger.debug(`Message sent with ID: ${messageId}`);
|
|
716
|
+
});
|
|
717
|
+
const thread = {
|
|
718
|
+
channel: data.workspaceChannel,
|
|
719
|
+
threadId: messageId,
|
|
720
|
+
};
|
|
721
|
+
logger.debug(`Created thread via Bot Framework: ${JSON.stringify(thread)}`);
|
|
722
|
+
return thread;
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
logger.error("Error sending adaptive card via Bot Framework:");
|
|
726
|
+
logger.error(error);
|
|
727
|
+
throw error;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
static async getWorkspaceChannelFromChannelId(data) {
|
|
731
|
+
logger.debug("=== getWorkspaceChannelFromChannelId called ===");
|
|
732
|
+
logger.debug(`Channel ID: ${data.channelId}`);
|
|
733
|
+
logger.debug(`Team ID: ${data.teamId}`);
|
|
734
|
+
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
735
|
+
try {
|
|
736
|
+
// Get valid access token
|
|
737
|
+
const accessToken = await this.getValidAccessToken({
|
|
738
|
+
authToken: data.authToken,
|
|
739
|
+
projectId: data.projectId,
|
|
740
|
+
});
|
|
741
|
+
logger.debug("Access token obtained for channel info retrieval");
|
|
742
|
+
// Fetch channel information from Microsoft Graph API
|
|
743
|
+
const apiUrl = `https://graph.microsoft.com/v1.0/teams/${data.teamId}/channels/${data.channelId}`;
|
|
744
|
+
logger.debug(`Making API call to: ${apiUrl}`);
|
|
745
|
+
const response = await API.get({
|
|
746
|
+
url: URL.fromString(apiUrl),
|
|
747
|
+
headers: {
|
|
748
|
+
Authorization: `Bearer ${accessToken}`,
|
|
749
|
+
"Content-Type": "application/json",
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
if (response instanceof HTTPErrorResponse) {
|
|
753
|
+
logger.error("Error getting channel info from Microsoft Graph API:");
|
|
754
|
+
logger.error(response);
|
|
755
|
+
// Fall back to basic channel object
|
|
756
|
+
logger.debug("Falling back to basic channel object");
|
|
757
|
+
return {
|
|
758
|
+
id: data.channelId,
|
|
759
|
+
name: data.channelId,
|
|
760
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
761
|
+
teamId: data.teamId,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
logger.debug("Channel info API call successful");
|
|
765
|
+
const channelData = response.data;
|
|
766
|
+
const channel = {
|
|
767
|
+
id: data.channelId,
|
|
768
|
+
name: channelData["displayName"],
|
|
769
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
770
|
+
teamId: data.teamId,
|
|
771
|
+
};
|
|
772
|
+
logger.debug(`Channel info retrieved: ${JSON.stringify(channel)}`);
|
|
773
|
+
return channel;
|
|
774
|
+
}
|
|
775
|
+
catch (error) {
|
|
776
|
+
logger.error("Error fetching channel information:");
|
|
777
|
+
logger.error(error);
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
static buildAdaptiveCardFromMessageBlocks(data) {
|
|
782
|
+
logger.debug("=== buildAdaptiveCardFromMessageBlocks called ===");
|
|
783
|
+
logger.debug(`Number of message blocks: ${data.messageBlocks.length}`);
|
|
784
|
+
const card = {
|
|
785
|
+
type: "AdaptiveCard",
|
|
786
|
+
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
|
|
787
|
+
version: "1.5",
|
|
788
|
+
body: [],
|
|
789
|
+
actions: [],
|
|
790
|
+
};
|
|
791
|
+
const body = [];
|
|
792
|
+
const actions = [];
|
|
793
|
+
for (const block of data.messageBlocks) {
|
|
794
|
+
logger.debug(`Processing message block of type: ${block._type}`);
|
|
795
|
+
if (block._type === "WorkspacePayloadMarkdown") {
|
|
796
|
+
const markdownBlock = block;
|
|
797
|
+
logger.debug(`Markdown text: ${markdownBlock.text}`);
|
|
798
|
+
const markdownObj = this.getMarkdownBlock({
|
|
799
|
+
payloadMarkdownBlock: markdownBlock,
|
|
800
|
+
});
|
|
801
|
+
body.push(markdownObj);
|
|
802
|
+
}
|
|
803
|
+
else if (block._type === "WorkspacePayloadHeader") {
|
|
804
|
+
const headerBlock = block;
|
|
805
|
+
logger.debug(`Header text: ${headerBlock.text}`);
|
|
806
|
+
const headerObj = this.getHeaderBlock({
|
|
807
|
+
payloadHeaderBlock: headerBlock,
|
|
808
|
+
});
|
|
809
|
+
body.push(headerObj);
|
|
810
|
+
}
|
|
811
|
+
else if (block._type === "WorkspacePayloadButtons") {
|
|
812
|
+
const buttonsBlock = block;
|
|
813
|
+
logger.debug(`Processing ${buttonsBlock.buttons.length} buttons`);
|
|
814
|
+
for (const button of buttonsBlock.buttons) {
|
|
815
|
+
logger.debug(`Button: ${button.title} -> ${button.url ? button.url.toString() : "invoke"}`);
|
|
816
|
+
const actionObj = this.getButtonBlock({
|
|
817
|
+
payloadButtonBlock: button,
|
|
818
|
+
});
|
|
819
|
+
actions.push(actionObj);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
card["body"] = body;
|
|
824
|
+
card["actions"] = actions;
|
|
825
|
+
logger.debug(`Built adaptive card with ${body.length} body elements and ${actions.length} actions`);
|
|
826
|
+
return card;
|
|
827
|
+
}
|
|
828
|
+
static convertAdaptiveCardToHtml(adaptiveCard) {
|
|
829
|
+
logger.debug("=== convertAdaptiveCardToHtml called ===");
|
|
830
|
+
// Convert adaptive card to basic HTML for fallback
|
|
831
|
+
let html = "";
|
|
832
|
+
const body = adaptiveCard["body"] || [];
|
|
833
|
+
logger.debug(`Converting ${body.length} body elements to HTML`);
|
|
834
|
+
for (const element of body) {
|
|
835
|
+
if (element["type"] === "TextBlock") {
|
|
836
|
+
const text = element["text"];
|
|
837
|
+
const size = element["size"];
|
|
838
|
+
if (size === "Large") {
|
|
839
|
+
html += `<h2>${text}</h2>`;
|
|
840
|
+
logger.debug(`Added header: ${text}`);
|
|
841
|
+
}
|
|
842
|
+
else {
|
|
843
|
+
html += `<p>${text}</p>`;
|
|
844
|
+
logger.debug(`Added paragraph: ${text}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const actions = adaptiveCard["actions"] || [];
|
|
849
|
+
if (actions.length > 0) {
|
|
850
|
+
logger.debug(`Converting ${actions.length} actions to HTML`);
|
|
851
|
+
html += "<div>";
|
|
852
|
+
for (const action of actions) {
|
|
853
|
+
if (action["type"] === "Action.OpenUrl") {
|
|
854
|
+
const title = action["title"];
|
|
855
|
+
const url = action["url"];
|
|
856
|
+
html += `<a href="${url}">${title}</a> `;
|
|
857
|
+
logger.debug(`Added link: ${title} -> ${url}`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
html += "</div>";
|
|
861
|
+
}
|
|
862
|
+
logger.debug(`Generated HTML length: ${html.length} characters`);
|
|
863
|
+
return html;
|
|
864
|
+
}
|
|
865
|
+
// Placeholder implementations for abstract methods
|
|
866
|
+
static async showModalToUser(_data) {
|
|
867
|
+
// Microsoft Teams doesn't support modals in the same way as Slack
|
|
868
|
+
throw new Error("Modals are not supported in Microsoft Teams integration");
|
|
869
|
+
}
|
|
870
|
+
static async archiveChannels(_data) {
|
|
871
|
+
// Microsoft Teams doesn't support archiving channels via API
|
|
872
|
+
throw new Error("Channel archiving is not supported in Microsoft Teams integration");
|
|
873
|
+
}
|
|
874
|
+
static async joinChannel(_data) {
|
|
875
|
+
// Bot automatically has access to channels in Teams
|
|
876
|
+
logger.debug("Bot automatically has access to Teams channels");
|
|
877
|
+
}
|
|
878
|
+
static async inviteUserToChannelByChannelId(_data) {
|
|
879
|
+
// Teams channel membership is managed differently
|
|
880
|
+
logger.debug("Teams channel membership is managed at the team level");
|
|
881
|
+
}
|
|
882
|
+
static async inviteUserToChannelByChannelName(_data) {
|
|
883
|
+
// Teams channel membership is managed differently
|
|
884
|
+
logger.debug("Teams channel membership is managed at the team level");
|
|
885
|
+
}
|
|
886
|
+
static async getAllWorkspaceChannels(data) {
|
|
887
|
+
logger.debug("Getting all workspace channels for team ID: " + data.teamId);
|
|
888
|
+
// Get valid access token
|
|
889
|
+
const accessToken = await this.getValidAccessToken({
|
|
890
|
+
authToken: data.authToken,
|
|
891
|
+
projectId: data.projectId,
|
|
892
|
+
});
|
|
893
|
+
const response = await API.get({
|
|
894
|
+
url: URL.fromString(`https://graph.microsoft.com/v1.0/teams/${data.teamId}/channels`),
|
|
895
|
+
headers: {
|
|
896
|
+
Authorization: `Bearer ${accessToken}`,
|
|
897
|
+
"Content-Type": "application/json",
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
if (response instanceof HTTPErrorResponse) {
|
|
901
|
+
logger.error("Error response from Microsoft Graph API:");
|
|
902
|
+
logger.error(response);
|
|
903
|
+
throw response;
|
|
904
|
+
}
|
|
905
|
+
const channelsData = response.data;
|
|
906
|
+
const channelsArray = channelsData["value"] || [];
|
|
907
|
+
const channelsDict = {};
|
|
908
|
+
for (const channelData of channelsArray) {
|
|
909
|
+
const channel = {
|
|
910
|
+
id: channelData["id"],
|
|
911
|
+
name: channelData["displayName"],
|
|
912
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
913
|
+
};
|
|
914
|
+
channelsDict[channel.id] = channel;
|
|
915
|
+
}
|
|
916
|
+
logger.debug(`Retrieved ${Object.keys(channelsDict).length} channels from API`);
|
|
917
|
+
return channelsDict;
|
|
918
|
+
}
|
|
919
|
+
static async doesChannelExist(data) {
|
|
920
|
+
if (!data.teamId) {
|
|
921
|
+
throw new BadDataException("teamId is required for Microsoft Teams doesChannelExist");
|
|
922
|
+
}
|
|
923
|
+
const channel = await this.getWorkspaceChannelByName({
|
|
924
|
+
authToken: data.authToken,
|
|
925
|
+
channelName: data.channelName,
|
|
926
|
+
projectId: data.projectId,
|
|
927
|
+
teamId: data.teamId,
|
|
928
|
+
});
|
|
929
|
+
return channel !== null;
|
|
930
|
+
}
|
|
931
|
+
static async isUserInDirectMessageChannel(_data) {
|
|
932
|
+
return false; // Placeholder
|
|
933
|
+
}
|
|
934
|
+
static async isUserInChannel(_data) {
|
|
935
|
+
return false; // Placeholder
|
|
936
|
+
}
|
|
937
|
+
// Block generation methods - these create adaptive card elements
|
|
938
|
+
static getDividerBlock() {
|
|
939
|
+
return {
|
|
940
|
+
type: "Container",
|
|
941
|
+
separator: true,
|
|
942
|
+
items: [],
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
static getButtonsBlock(_data) {
|
|
946
|
+
// Return adaptive card actions
|
|
947
|
+
return {
|
|
948
|
+
type: "ActionSet",
|
|
949
|
+
actions: [],
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
static getHeaderBlock(data) {
|
|
953
|
+
return {
|
|
954
|
+
type: "TextBlock",
|
|
955
|
+
text: data.payloadHeaderBlock.text,
|
|
956
|
+
size: "Large",
|
|
957
|
+
weight: "Bolder",
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
static getMarkdownBlock(data) {
|
|
961
|
+
return {
|
|
962
|
+
type: "TextBlock",
|
|
963
|
+
text: data.payloadMarkdownBlock.text,
|
|
964
|
+
wrap: true,
|
|
965
|
+
markdown: true,
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
static getButtonBlock(data) {
|
|
969
|
+
// If URL is present, render as link; otherwise use Action.Submit to post back action/value
|
|
970
|
+
if (data.payloadButtonBlock.url) {
|
|
971
|
+
return {
|
|
972
|
+
type: "Action.OpenUrl",
|
|
973
|
+
title: data.payloadButtonBlock.title,
|
|
974
|
+
url: data.payloadButtonBlock.url.toString(),
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
return {
|
|
978
|
+
type: "Action.Submit",
|
|
979
|
+
title: data.payloadButtonBlock.title,
|
|
980
|
+
data: {
|
|
981
|
+
action: data.payloadButtonBlock.actionId,
|
|
982
|
+
actionValue: data.payloadButtonBlock.value,
|
|
983
|
+
},
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
// Other block methods - placeholders for now
|
|
987
|
+
static getCheckboxBlock(_data) {
|
|
988
|
+
return { type: "Input.Toggle" };
|
|
989
|
+
}
|
|
990
|
+
static getDateTimePickerBlock(_data) {
|
|
991
|
+
return { type: "Input.Date" };
|
|
992
|
+
}
|
|
993
|
+
static getTextAreaBlock(_data) {
|
|
994
|
+
return { type: "Input.Text", isMultiline: true };
|
|
995
|
+
}
|
|
996
|
+
static getTextBoxBlock(_data) {
|
|
997
|
+
return { type: "Input.Text" };
|
|
998
|
+
}
|
|
999
|
+
static getImageBlock(_data) {
|
|
1000
|
+
return { type: "Image" };
|
|
1001
|
+
}
|
|
1002
|
+
static getDropdownBlock(_data) {
|
|
1003
|
+
return { type: "Input.ChoiceSet" };
|
|
1004
|
+
}
|
|
1005
|
+
static getModalBlock(_data) {
|
|
1006
|
+
// Teams doesn't support modals like Slack
|
|
1007
|
+
return {};
|
|
1008
|
+
}
|
|
1009
|
+
static async sendPayloadBlocksToChannel(_data) {
|
|
1010
|
+
// This is handled by sendAdaptiveCardToChannel
|
|
1011
|
+
throw new Error("Use sendAdaptiveCardToChannel instead");
|
|
1012
|
+
}
|
|
1013
|
+
static convertMarkdownToTeamsRichText(markdown) {
|
|
1014
|
+
// Basic markdown to Teams format conversion
|
|
1015
|
+
return markdown
|
|
1016
|
+
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
|
1017
|
+
.replace(/\*(.*?)\*/g, "<em>$1</em>")
|
|
1018
|
+
.replace(/`(.*?)`/g, "<code>$1</code>");
|
|
1019
|
+
}
|
|
1020
|
+
// Bot Framework specific methods
|
|
1021
|
+
static async handleBotMessageActivity(data) {
|
|
1022
|
+
var _a, _b, _c;
|
|
1023
|
+
// Handle direct messages to bot or @mentions via Bot Framework
|
|
1024
|
+
const messageText = data.activity["text"] || "";
|
|
1025
|
+
const possibleActionValue = data.activity["value"] || {};
|
|
1026
|
+
const from = data.activity["from"] || {};
|
|
1027
|
+
const conversation = data.activity["conversation"] || {};
|
|
1028
|
+
const channelData = data.activity["channelData"] || {};
|
|
1029
|
+
const entities = data.activity["entities"] || [];
|
|
1030
|
+
logger.debug(`Bot message from: ${JSON.stringify(from)}`);
|
|
1031
|
+
logger.debug(`Message text: ${messageText}`);
|
|
1032
|
+
logger.debug(`Conversation: ${JSON.stringify(conversation)}`);
|
|
1033
|
+
logger.debug(`Channel data: ${JSON.stringify(channelData)}`);
|
|
1034
|
+
logger.debug(`Entities: ${JSON.stringify(entities)}`);
|
|
1035
|
+
// If this is actually an Adaptive Card submit wrapped as a message, route to invoke handler
|
|
1036
|
+
if (possibleActionValue["action"] ||
|
|
1037
|
+
((_a = possibleActionValue["data"]) === null || _a === void 0 ? void 0 : _a["action"])) {
|
|
1038
|
+
logger.debug("Message activity contains action payload; routing to invoke handler");
|
|
1039
|
+
await this.handleBotInvokeActivity({
|
|
1040
|
+
activity: data.activity,
|
|
1041
|
+
turnContext: data.turnContext,
|
|
1042
|
+
});
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
// Check if the bot was mentioned
|
|
1046
|
+
const recipientId = (_b = data.activity["recipient"]) === null || _b === void 0 ? void 0 : _b["id"];
|
|
1047
|
+
const conversationType = conversation["conversationType"] || "";
|
|
1048
|
+
const isDirectMessage = conversationType === "personal";
|
|
1049
|
+
const isMentioned = entities.some((entity) => {
|
|
1050
|
+
var _a;
|
|
1051
|
+
return (entity["type"] === "mention" &&
|
|
1052
|
+
((_a = entity["mentioned"]) === null || _a === void 0 ? void 0 : _a["id"]) === recipientId);
|
|
1053
|
+
});
|
|
1054
|
+
// Only respond if it's a direct message or the bot was mentioned
|
|
1055
|
+
if (!isDirectMessage && !isMentioned) {
|
|
1056
|
+
logger.debug("Bot not mentioned in channel message, ignoring");
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
// Extract tenant ID to get project ID
|
|
1060
|
+
const tenantId = (_c = channelData["tenant"]) === null || _c === void 0 ? void 0 : _c["id"];
|
|
1061
|
+
if (!tenantId) {
|
|
1062
|
+
logger.error("Tenant ID not found in channelData");
|
|
1063
|
+
await data.turnContext.sendActivity("Sorry, I couldn't identify your organization. Please try again later.");
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
// Get project auth by tenant ID
|
|
1067
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.findOneBy({
|
|
1068
|
+
query: {
|
|
1069
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1070
|
+
miscData: {
|
|
1071
|
+
tenantId: tenantId,
|
|
1072
|
+
},
|
|
1073
|
+
},
|
|
1074
|
+
select: {
|
|
1075
|
+
projectId: true,
|
|
1076
|
+
miscData: true,
|
|
1077
|
+
},
|
|
1078
|
+
props: {
|
|
1079
|
+
isRoot: true,
|
|
1080
|
+
},
|
|
1081
|
+
});
|
|
1082
|
+
if (!projectAuth || !projectAuth.projectId) {
|
|
1083
|
+
logger.error("Project auth not found for tenant ID: " + tenantId);
|
|
1084
|
+
await data.turnContext.sendActivity("Sorry, I couldn't find your project configuration. Please try again later.");
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const projectId = projectAuth.projectId;
|
|
1088
|
+
logger.debug(`Found project ID: ${projectId.toString()} for tenant ID: ${tenantId}`);
|
|
1089
|
+
// Clean the message text by removing bot mentions
|
|
1090
|
+
const cleanText = messageText
|
|
1091
|
+
.replace(/<at[^>]*>.*?<\/at>/g, "")
|
|
1092
|
+
.trim()
|
|
1093
|
+
.toLowerCase();
|
|
1094
|
+
let responseText = "";
|
|
1095
|
+
try {
|
|
1096
|
+
if (cleanText.includes("help") || cleanText === "") {
|
|
1097
|
+
responseText = this.getHelpMessage();
|
|
1098
|
+
}
|
|
1099
|
+
else if (cleanText === "/incident" ||
|
|
1100
|
+
cleanText.startsWith("/incident ")) {
|
|
1101
|
+
// Handle /incident slash command
|
|
1102
|
+
logger.debug("Processing /incident command");
|
|
1103
|
+
const card = await MicrosoftTeamsIncidentActions.buildNewIncidentCard(projectId);
|
|
1104
|
+
await data.turnContext.sendActivity({
|
|
1105
|
+
attachments: [
|
|
1106
|
+
{
|
|
1107
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
1108
|
+
content: card,
|
|
1109
|
+
},
|
|
1110
|
+
],
|
|
1111
|
+
});
|
|
1112
|
+
logger.debug("New incident card sent successfully");
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
else if (cleanText === "/maintenance" ||
|
|
1116
|
+
cleanText.startsWith("/maintenance ")) {
|
|
1117
|
+
// Handle /maintenance slash command
|
|
1118
|
+
logger.debug("Processing /maintenance command");
|
|
1119
|
+
const card = await MicrosoftTeamsScheduledMaintenanceActions.buildNewScheduledMaintenanceCard(projectId);
|
|
1120
|
+
await data.turnContext.sendActivity({
|
|
1121
|
+
attachments: [
|
|
1122
|
+
{
|
|
1123
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
1124
|
+
content: card,
|
|
1125
|
+
},
|
|
1126
|
+
],
|
|
1127
|
+
});
|
|
1128
|
+
logger.debug("New scheduled maintenance card sent successfully");
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
else if (cleanText.includes("show active incidents") ||
|
|
1132
|
+
cleanText.includes("active incidents")) {
|
|
1133
|
+
responseText = await this.getActiveIncidentsMessage(projectId);
|
|
1134
|
+
}
|
|
1135
|
+
else if (cleanText.includes("show scheduled maintenance") ||
|
|
1136
|
+
cleanText.includes("scheduled maintenance")) {
|
|
1137
|
+
responseText = await this.getScheduledMaintenanceMessage(projectId);
|
|
1138
|
+
}
|
|
1139
|
+
else if (cleanText.includes("show ongoing maintenance") ||
|
|
1140
|
+
cleanText.includes("ongoing maintenance")) {
|
|
1141
|
+
responseText = await this.getOngoingMaintenanceMessage(projectId);
|
|
1142
|
+
}
|
|
1143
|
+
else if (cleanText.includes("show active alerts") ||
|
|
1144
|
+
cleanText.includes("active alerts")) {
|
|
1145
|
+
responseText = await this.getActiveAlertsMessage(projectId);
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
responseText = `I received your message: "${cleanText}". Type 'help' to see what I can do for you.`;
|
|
1149
|
+
}
|
|
1150
|
+
// Send response directly using TurnContext - this is the recommended Bot Framework pattern
|
|
1151
|
+
await data.turnContext.sendActivity(responseText);
|
|
1152
|
+
logger.debug("Bot message sent successfully using TurnContext");
|
|
1153
|
+
}
|
|
1154
|
+
catch (error) {
|
|
1155
|
+
logger.error("Error sending bot message via TurnContext: " + error);
|
|
1156
|
+
await data.turnContext.sendActivity("Sorry, I encountered an error processing your request. Please try again later.");
|
|
1157
|
+
throw error;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
// Helper methods for bot commands
|
|
1161
|
+
static getHelpMessage() {
|
|
1162
|
+
return `Hello! I'm the OneUptime bot. I can help you with the following commands:
|
|
1163
|
+
|
|
1164
|
+
**Available Commands:**
|
|
1165
|
+
- **help** — Show this help message
|
|
1166
|
+
- **/incident** — Create a new incident
|
|
1167
|
+
- **/maintenance** — Create a new scheduled maintenance event
|
|
1168
|
+
- **show active incidents** — Display all currently active incidents
|
|
1169
|
+
- **show scheduled maintenance** — Show upcoming scheduled maintenance events
|
|
1170
|
+
- **show ongoing maintenance** — Display currently ongoing maintenance events
|
|
1171
|
+
- **show active alerts** — Display all active alerts
|
|
1172
|
+
|
|
1173
|
+
Just type any of these commands to get the information you need!`;
|
|
1174
|
+
}
|
|
1175
|
+
static async getActiveIncidentsMessage(projectId) {
|
|
1176
|
+
var _a, _b;
|
|
1177
|
+
try {
|
|
1178
|
+
logger.debug("Getting active incidents for project: " + projectId.toString());
|
|
1179
|
+
// Get unresolved incident states
|
|
1180
|
+
const unresolvedIncidentStates = await IncidentStateService.getUnresolvedIncidentStates(projectId, {
|
|
1181
|
+
isRoot: true,
|
|
1182
|
+
});
|
|
1183
|
+
const unresolvedIncidentStateIds = unresolvedIncidentStates.map((state) => {
|
|
1184
|
+
return state.id;
|
|
1185
|
+
});
|
|
1186
|
+
// Find active incidents
|
|
1187
|
+
const activeIncidents = await IncidentService.findBy({
|
|
1188
|
+
query: {
|
|
1189
|
+
projectId: projectId,
|
|
1190
|
+
currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
|
|
1191
|
+
},
|
|
1192
|
+
select: {
|
|
1193
|
+
_id: true,
|
|
1194
|
+
incidentNumber: true,
|
|
1195
|
+
title: true,
|
|
1196
|
+
description: true,
|
|
1197
|
+
currentIncidentState: {
|
|
1198
|
+
name: true,
|
|
1199
|
+
color: true,
|
|
1200
|
+
},
|
|
1201
|
+
incidentSeverity: {
|
|
1202
|
+
name: true,
|
|
1203
|
+
color: true,
|
|
1204
|
+
},
|
|
1205
|
+
createdAt: true,
|
|
1206
|
+
monitors: {
|
|
1207
|
+
name: true,
|
|
1208
|
+
},
|
|
1209
|
+
},
|
|
1210
|
+
sort: {
|
|
1211
|
+
createdAt: SortOrder.Descending,
|
|
1212
|
+
},
|
|
1213
|
+
limit: 10,
|
|
1214
|
+
skip: 0,
|
|
1215
|
+
props: {
|
|
1216
|
+
isRoot: true,
|
|
1217
|
+
},
|
|
1218
|
+
});
|
|
1219
|
+
if (activeIncidents.length === 0) {
|
|
1220
|
+
return `**Active Incidents**
|
|
1221
|
+
|
|
1222
|
+
Currently, there are no active incidents in the system. All services are operating normally.
|
|
1223
|
+
|
|
1224
|
+
If you need to report an incident or check historical incidents, please visit the OneUptime dashboard.`;
|
|
1225
|
+
}
|
|
1226
|
+
let message = `**Active Incidents** (${activeIncidents.length})
|
|
1227
|
+
|
|
1228
|
+
`;
|
|
1229
|
+
for (const incident of activeIncidents) {
|
|
1230
|
+
const severity = ((_a = incident.incidentSeverity) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
|
|
1231
|
+
const state = ((_b = incident.currentIncidentState) === null || _b === void 0 ? void 0 : _b.name) || "Unknown";
|
|
1232
|
+
const createdAt = incident.createdAt
|
|
1233
|
+
? OneUptimeDate.getDateAsFormattedString(incident.createdAt)
|
|
1234
|
+
: "Unknown";
|
|
1235
|
+
const severityIcon = ["Critical", "Major"].includes(severity)
|
|
1236
|
+
? "🔴"
|
|
1237
|
+
: severity === "Minor"
|
|
1238
|
+
? "🟠"
|
|
1239
|
+
: "🟡";
|
|
1240
|
+
const incidentUrl = await IncidentService.getIncidentLinkInDashboard(projectId, incident.id);
|
|
1241
|
+
message += `${severityIcon} **[Incident #${incident.incidentNumber}: ${incident.title}](${incidentUrl.toString()})**
|
|
1242
|
+
• **Severity:** ${severity}
|
|
1243
|
+
• **Status:** ${state}
|
|
1244
|
+
• **Created:** ${createdAt}
|
|
1245
|
+
`;
|
|
1246
|
+
if (incident.monitors && incident.monitors.length > 0) {
|
|
1247
|
+
message += `• **Affected Services:** ${incident.monitors
|
|
1248
|
+
.map((m) => {
|
|
1249
|
+
return m.name;
|
|
1250
|
+
})
|
|
1251
|
+
.join(", ")}\n`;
|
|
1252
|
+
}
|
|
1253
|
+
if (incident.description) {
|
|
1254
|
+
const desc = incident.description.replace(/\s+/g, " ");
|
|
1255
|
+
message += `• **Description:** ${desc.substring(0, 180)}${desc.length > 180 ? "..." : ""}\n`;
|
|
1256
|
+
}
|
|
1257
|
+
message += `• [Open in Dashboard](${incidentUrl.toString()})\n\n`;
|
|
1258
|
+
}
|
|
1259
|
+
return message;
|
|
1260
|
+
}
|
|
1261
|
+
catch (error) {
|
|
1262
|
+
logger.error("Error getting active incidents: " + error);
|
|
1263
|
+
return "Sorry, I couldn't retrieve active incidents information at the moment. Please try again later.";
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
static async getScheduledMaintenanceMessage(projectId) {
|
|
1267
|
+
var _a;
|
|
1268
|
+
try {
|
|
1269
|
+
logger.debug("Getting scheduled maintenance events for project: " +
|
|
1270
|
+
projectId.toString());
|
|
1271
|
+
// Get scheduled maintenance events
|
|
1272
|
+
const scheduledEvents = await ScheduledMaintenanceService.findBy({
|
|
1273
|
+
query: {
|
|
1274
|
+
projectId: projectId,
|
|
1275
|
+
currentScheduledMaintenanceState: {
|
|
1276
|
+
isScheduledState: true,
|
|
1277
|
+
},
|
|
1278
|
+
isVisibleOnStatusPage: true, // Only show events visible on status page
|
|
1279
|
+
},
|
|
1280
|
+
select: {
|
|
1281
|
+
_id: true,
|
|
1282
|
+
title: true,
|
|
1283
|
+
description: true,
|
|
1284
|
+
startsAt: true,
|
|
1285
|
+
endsAt: true,
|
|
1286
|
+
currentScheduledMaintenanceState: {
|
|
1287
|
+
name: true,
|
|
1288
|
+
},
|
|
1289
|
+
monitors: {
|
|
1290
|
+
name: true,
|
|
1291
|
+
},
|
|
1292
|
+
scheduledMaintenanceNumber: true,
|
|
1293
|
+
},
|
|
1294
|
+
sort: {
|
|
1295
|
+
startsAt: SortOrder.Ascending,
|
|
1296
|
+
},
|
|
1297
|
+
limit: 10,
|
|
1298
|
+
skip: 0,
|
|
1299
|
+
props: {
|
|
1300
|
+
isRoot: true,
|
|
1301
|
+
},
|
|
1302
|
+
});
|
|
1303
|
+
if (scheduledEvents.length === 0) {
|
|
1304
|
+
return `**Scheduled Maintenance Events**
|
|
1305
|
+
|
|
1306
|
+
There are currently no scheduled maintenance events.
|
|
1307
|
+
|
|
1308
|
+
When maintenance is scheduled, you'll see details here including:
|
|
1309
|
+
• Event title and description
|
|
1310
|
+
• Scheduled start and end times
|
|
1311
|
+
• Affected services
|
|
1312
|
+
• Status updates
|
|
1313
|
+
|
|
1314
|
+
Check back later for upcoming maintenance windows.`;
|
|
1315
|
+
}
|
|
1316
|
+
let message = `**Scheduled Maintenance Events** (${scheduledEvents.length})
|
|
1317
|
+
|
|
1318
|
+
`;
|
|
1319
|
+
for (const event of scheduledEvents) {
|
|
1320
|
+
const state = ((_a = event.currentScheduledMaintenanceState) === null || _a === void 0 ? void 0 : _a.name) || "Scheduled";
|
|
1321
|
+
const startTime = event.startsAt
|
|
1322
|
+
? OneUptimeDate.getDateAsFormattedString(event.startsAt)
|
|
1323
|
+
: "TBD";
|
|
1324
|
+
const endTime = event.endsAt
|
|
1325
|
+
? OneUptimeDate.getDateAsFormattedString(event.endsAt)
|
|
1326
|
+
: "TBD";
|
|
1327
|
+
const eventUrl = await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(projectId, event.id);
|
|
1328
|
+
message += `🛠️ **[Scheduled Maintenance #${event.scheduledMaintenanceNumber}: ${event.title}](${eventUrl.toString()})**
|
|
1329
|
+
• **Status:** ${state}
|
|
1330
|
+
• **Starts:** ${startTime}
|
|
1331
|
+
• **Ends:** ${endTime}
|
|
1332
|
+
`;
|
|
1333
|
+
if (event.monitors && event.monitors.length > 0) {
|
|
1334
|
+
message += `• **Affected Services:** ${event.monitors
|
|
1335
|
+
.map((m) => {
|
|
1336
|
+
return m.name;
|
|
1337
|
+
})
|
|
1338
|
+
.join(", ")}\n`;
|
|
1339
|
+
}
|
|
1340
|
+
if (event.description) {
|
|
1341
|
+
const desc = event.description.replace(/\s+/g, " ");
|
|
1342
|
+
message += `• **Description:** ${desc.substring(0, 180)}${desc.length > 180 ? "..." : ""}\n`;
|
|
1343
|
+
}
|
|
1344
|
+
message += `• [View Event](${eventUrl.toString()})\n\n`;
|
|
1345
|
+
}
|
|
1346
|
+
return message;
|
|
1347
|
+
}
|
|
1348
|
+
catch (error) {
|
|
1349
|
+
logger.error("Error getting scheduled maintenance: " + error);
|
|
1350
|
+
return "Sorry, I couldn't retrieve scheduled maintenance information at the moment. Please try again later.";
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
static async getOngoingMaintenanceMessage(projectId) {
|
|
1354
|
+
var _a;
|
|
1355
|
+
try {
|
|
1356
|
+
logger.debug("Getting ongoing maintenance events for project: " +
|
|
1357
|
+
projectId.toString());
|
|
1358
|
+
// Get ongoing maintenance events
|
|
1359
|
+
const ongoingEvents = await ScheduledMaintenanceService.findBy({
|
|
1360
|
+
query: {
|
|
1361
|
+
projectId: projectId,
|
|
1362
|
+
currentScheduledMaintenanceState: {
|
|
1363
|
+
isOngoingState: true,
|
|
1364
|
+
},
|
|
1365
|
+
},
|
|
1366
|
+
select: {
|
|
1367
|
+
_id: true,
|
|
1368
|
+
title: true,
|
|
1369
|
+
description: true,
|
|
1370
|
+
startsAt: true,
|
|
1371
|
+
endsAt: true,
|
|
1372
|
+
currentScheduledMaintenanceState: {
|
|
1373
|
+
name: true,
|
|
1374
|
+
},
|
|
1375
|
+
monitors: {
|
|
1376
|
+
name: true,
|
|
1377
|
+
},
|
|
1378
|
+
scheduledMaintenanceNumber: true,
|
|
1379
|
+
},
|
|
1380
|
+
sort: {
|
|
1381
|
+
startsAt: SortOrder.Descending,
|
|
1382
|
+
},
|
|
1383
|
+
limit: 10,
|
|
1384
|
+
skip: 0,
|
|
1385
|
+
props: {
|
|
1386
|
+
isRoot: true,
|
|
1387
|
+
},
|
|
1388
|
+
});
|
|
1389
|
+
if (ongoingEvents.length === 0) {
|
|
1390
|
+
return `**Ongoing Maintenance Events**
|
|
1391
|
+
|
|
1392
|
+
There are currently no ongoing maintenance events.
|
|
1393
|
+
|
|
1394
|
+
When maintenance is in progress, you'll see details here including:
|
|
1395
|
+
• Event title and description
|
|
1396
|
+
• Current status and progress
|
|
1397
|
+
• Affected services
|
|
1398
|
+
• Expected completion time
|
|
1399
|
+
|
|
1400
|
+
All systems are currently operating normally.`;
|
|
1401
|
+
}
|
|
1402
|
+
let message = `**Ongoing Maintenance Events** (${ongoingEvents.length})
|
|
1403
|
+
|
|
1404
|
+
`;
|
|
1405
|
+
for (const event of ongoingEvents) {
|
|
1406
|
+
const state = ((_a = event.currentScheduledMaintenanceState) === null || _a === void 0 ? void 0 : _a.name) || "Ongoing";
|
|
1407
|
+
const startTime = event.startsAt
|
|
1408
|
+
? OneUptimeDate.getDateAsFormattedString(event.startsAt)
|
|
1409
|
+
: "Unknown";
|
|
1410
|
+
const endTime = event.endsAt
|
|
1411
|
+
? OneUptimeDate.getDateAsFormattedString(event.endsAt)
|
|
1412
|
+
: "TBD";
|
|
1413
|
+
const eventUrl = await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(projectId, event.id);
|
|
1414
|
+
message += `🔧 **[Scheduled Maintenance #${event.scheduledMaintenanceNumber}: ${event.title}](${eventUrl.toString()})**
|
|
1415
|
+
• **Status:** ${state}
|
|
1416
|
+
• **Started:** ${startTime}
|
|
1417
|
+
• **Expected End:** ${endTime}
|
|
1418
|
+
`;
|
|
1419
|
+
if (event.monitors && event.monitors.length > 0) {
|
|
1420
|
+
message += `• **Affected Services:** ${event.monitors
|
|
1421
|
+
.map((m) => {
|
|
1422
|
+
return m.name;
|
|
1423
|
+
})
|
|
1424
|
+
.join(", ")}\n`;
|
|
1425
|
+
}
|
|
1426
|
+
if (event.description) {
|
|
1427
|
+
const desc = event.description.replace(/\s+/g, " ");
|
|
1428
|
+
message += `• **Description:** ${desc.substring(0, 180)}${desc.length > 180 ? "..." : ""}\n`;
|
|
1429
|
+
}
|
|
1430
|
+
message += `• [View Event](${eventUrl.toString()})\n\n`;
|
|
1431
|
+
}
|
|
1432
|
+
return message;
|
|
1433
|
+
}
|
|
1434
|
+
catch (error) {
|
|
1435
|
+
logger.error("Error getting ongoing maintenance: " + error);
|
|
1436
|
+
return "Sorry, I couldn't retrieve ongoing maintenance information at the moment. Please try again later.";
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
static async getActiveAlertsMessage(projectId) {
|
|
1440
|
+
var _a, _b, _c;
|
|
1441
|
+
try {
|
|
1442
|
+
logger.debug("Getting active alerts for project: " + projectId.toString());
|
|
1443
|
+
// Get unresolved alert states
|
|
1444
|
+
const unresolvedAlertStates = await AlertStateService.getUnresolvedAlertStates(projectId, {
|
|
1445
|
+
isRoot: true,
|
|
1446
|
+
});
|
|
1447
|
+
const unresolvedAlertStateIds = unresolvedAlertStates.map((state) => {
|
|
1448
|
+
return state.id;
|
|
1449
|
+
});
|
|
1450
|
+
// Find active alerts
|
|
1451
|
+
const activeAlerts = await AlertService.findBy({
|
|
1452
|
+
query: {
|
|
1453
|
+
projectId: projectId,
|
|
1454
|
+
currentAlertStateId: QueryHelper.any(unresolvedAlertStateIds),
|
|
1455
|
+
},
|
|
1456
|
+
select: {
|
|
1457
|
+
_id: true,
|
|
1458
|
+
alertNumber: true,
|
|
1459
|
+
title: true,
|
|
1460
|
+
description: true,
|
|
1461
|
+
currentAlertState: {
|
|
1462
|
+
name: true,
|
|
1463
|
+
color: true,
|
|
1464
|
+
},
|
|
1465
|
+
alertSeverity: {
|
|
1466
|
+
name: true,
|
|
1467
|
+
color: true,
|
|
1468
|
+
},
|
|
1469
|
+
createdAt: true,
|
|
1470
|
+
monitor: {
|
|
1471
|
+
name: true,
|
|
1472
|
+
},
|
|
1473
|
+
},
|
|
1474
|
+
sort: {
|
|
1475
|
+
createdAt: SortOrder.Descending,
|
|
1476
|
+
},
|
|
1477
|
+
limit: 10,
|
|
1478
|
+
skip: 0,
|
|
1479
|
+
props: {
|
|
1480
|
+
isRoot: true,
|
|
1481
|
+
},
|
|
1482
|
+
});
|
|
1483
|
+
if (activeAlerts.length === 0) {
|
|
1484
|
+
return `**Active Alerts**
|
|
1485
|
+
|
|
1486
|
+
Currently, there are no active alerts in the system.
|
|
1487
|
+
|
|
1488
|
+
When alerts are triggered, you'll see details here including:
|
|
1489
|
+
• Alert title and description
|
|
1490
|
+
• Severity level
|
|
1491
|
+
• Affected services or monitors
|
|
1492
|
+
• Time triggered
|
|
1493
|
+
• Current status
|
|
1494
|
+
|
|
1495
|
+
All monitoring checks are passing normally.`;
|
|
1496
|
+
}
|
|
1497
|
+
let message = `**Active Alerts** (${activeAlerts.length})
|
|
1498
|
+
|
|
1499
|
+
`;
|
|
1500
|
+
for (const alert of activeAlerts) {
|
|
1501
|
+
const severity = ((_a = alert.alertSeverity) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
|
|
1502
|
+
const state = ((_b = alert.currentAlertState) === null || _b === void 0 ? void 0 : _b.name) || "Unknown";
|
|
1503
|
+
const createdAt = alert.createdAt
|
|
1504
|
+
? OneUptimeDate.getDateAsFormattedString(alert.createdAt)
|
|
1505
|
+
: "Unknown";
|
|
1506
|
+
const alertUrl = await AlertService.getAlertLinkInDashboard(projectId, alert.id);
|
|
1507
|
+
message += `⚠️ **[Alert #${alert.alertNumber}: ${alert.title}](${alertUrl.toString()})**
|
|
1508
|
+
• **Severity:** ${severity}
|
|
1509
|
+
• **Status:** ${state}
|
|
1510
|
+
• **Triggered:** ${createdAt}
|
|
1511
|
+
`;
|
|
1512
|
+
if ((_c = alert.monitor) === null || _c === void 0 ? void 0 : _c.name) {
|
|
1513
|
+
message += `• **Monitor:** ${alert.monitor.name}\n`;
|
|
1514
|
+
}
|
|
1515
|
+
if (alert.description) {
|
|
1516
|
+
const desc = alert.description.replace(/\s+/g, " ");
|
|
1517
|
+
message += `• **Description:** ${desc.substring(0, 180)}${desc.length > 180 ? "..." : ""}\n`;
|
|
1518
|
+
}
|
|
1519
|
+
message += `• [Open in Dashboard](${alertUrl.toString()})\n\n`;
|
|
1520
|
+
}
|
|
1521
|
+
return message;
|
|
1522
|
+
}
|
|
1523
|
+
catch (error) {
|
|
1524
|
+
logger.error("Error getting active alerts: " + error);
|
|
1525
|
+
return "Sorry, I couldn't retrieve active alerts information at the moment. Please try again later.";
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
static async handleBotInvokeActivity(data) {
|
|
1529
|
+
// Handle adaptive card button clicks via Bot Framework
|
|
1530
|
+
const value = data.activity["value"] || {};
|
|
1531
|
+
// Extract action type and value from the value object
|
|
1532
|
+
const { actionType, actionValue } = this.extractActionFromValue(value);
|
|
1533
|
+
logger.debug(`Bot invoke activity - Action type: ${actionType}`);
|
|
1534
|
+
logger.debug(`Bot invoke value: ${JSON.stringify(value)}`);
|
|
1535
|
+
try {
|
|
1536
|
+
// Resolve project and user context from activity
|
|
1537
|
+
const channelData = data.activity["channelData"] || {};
|
|
1538
|
+
const tenantId = (channelData["tenant"] || {})["id"];
|
|
1539
|
+
if (!tenantId) {
|
|
1540
|
+
logger.error("Tenant ID not found in invoke activity");
|
|
1541
|
+
await data.turnContext.sendActivity("Sorry, I couldn't identify your organization. Please try again later.");
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.findOneBy({
|
|
1545
|
+
query: {
|
|
1546
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1547
|
+
miscData: { tenantId: tenantId },
|
|
1548
|
+
},
|
|
1549
|
+
select: { projectId: true, authToken: true },
|
|
1550
|
+
props: { isRoot: true },
|
|
1551
|
+
});
|
|
1552
|
+
if (!projectAuth || !projectAuth.projectId) {
|
|
1553
|
+
logger.error("Project auth not found for invoke activity tenant: " + tenantId);
|
|
1554
|
+
await data.turnContext.sendActivity("Sorry, I couldn't find your project configuration.");
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
const projectId = projectAuth.projectId;
|
|
1558
|
+
const fromObj = (data.activity["from"] ||
|
|
1559
|
+
{});
|
|
1560
|
+
const teamsUserId = fromObj["aadObjectId"] || undefined;
|
|
1561
|
+
if (!teamsUserId) {
|
|
1562
|
+
logger.error("AAD Object ID (teamsUserId) not found in invoke activity from object");
|
|
1563
|
+
await data.turnContext.sendActivity("Sorry, I couldn't identify you. Please try again later.");
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
const userLookupParamsRes = {
|
|
1567
|
+
teamsUserId: teamsUserId,
|
|
1568
|
+
projectId: projectId,
|
|
1569
|
+
};
|
|
1570
|
+
const oneUptimeUserId = await MicrosoftTeamsAuthAction.getOneUptimeUserIdFromTeamsUserId(userLookupParamsRes);
|
|
1571
|
+
// Handle incident actions
|
|
1572
|
+
if (MicrosoftTeamsIncidentActions.isIncidentAction({ actionType })) {
|
|
1573
|
+
await MicrosoftTeamsIncidentActions.handleBotIncidentAction({
|
|
1574
|
+
actionType,
|
|
1575
|
+
actionValue,
|
|
1576
|
+
value,
|
|
1577
|
+
projectId,
|
|
1578
|
+
oneUptimeUserId,
|
|
1579
|
+
turnContext: data.turnContext,
|
|
1580
|
+
});
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
// Handle alert actions
|
|
1584
|
+
if (MicrosoftTeamsAlertActions.isAlertAction({ actionType })) {
|
|
1585
|
+
await MicrosoftTeamsAlertActions.handleBotAlertAction({
|
|
1586
|
+
actionType,
|
|
1587
|
+
actionValue,
|
|
1588
|
+
value,
|
|
1589
|
+
projectId,
|
|
1590
|
+
oneUptimeUserId,
|
|
1591
|
+
turnContext: data.turnContext,
|
|
1592
|
+
});
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
// Handle monitor actions
|
|
1596
|
+
if (MicrosoftTeamsMonitorActions.isMonitorAction({ actionType })) {
|
|
1597
|
+
await MicrosoftTeamsMonitorActions.handleBotMonitorAction({
|
|
1598
|
+
actionType,
|
|
1599
|
+
actionValue,
|
|
1600
|
+
value,
|
|
1601
|
+
projectId,
|
|
1602
|
+
oneUptimeUserId,
|
|
1603
|
+
turnContext: data.turnContext,
|
|
1604
|
+
});
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
// Handle scheduled maintenance actions
|
|
1608
|
+
if (MicrosoftTeamsScheduledMaintenanceActions.isScheduledMaintenanceAction({
|
|
1609
|
+
actionType,
|
|
1610
|
+
})) {
|
|
1611
|
+
await MicrosoftTeamsScheduledMaintenanceActions.handleBotScheduledMaintenanceAction(actionType, data.turnContext, value, {
|
|
1612
|
+
userId: oneUptimeUserId.toString(),
|
|
1613
|
+
projectId,
|
|
1614
|
+
isAuthorized: true,
|
|
1615
|
+
authToken: "",
|
|
1616
|
+
payloadType: "invoke",
|
|
1617
|
+
});
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
// Handle on-call duty actions
|
|
1621
|
+
if (MicrosoftTeamsOnCallDutyActions.isOnCallDutyAction({ actionType })) {
|
|
1622
|
+
await MicrosoftTeamsOnCallDutyActions.handleBotOnCallDutyAction(actionType, data.turnContext, value);
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
catch (error) {
|
|
1627
|
+
logger.error("Error handling bot invoke activity:");
|
|
1628
|
+
logger.error(error);
|
|
1629
|
+
await data.turnContext.sendActivity("Sorry, that action failed. Please try again later.");
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
static async handleConversationUpdateActivity(data) {
|
|
1633
|
+
// Handle bot added to team/channel or members added/removed
|
|
1634
|
+
const membersAdded = data.activity["membersAdded"] || [];
|
|
1635
|
+
const membersRemoved = data.activity["membersRemoved"] || [];
|
|
1636
|
+
const conversation = data.activity["conversation"] || {};
|
|
1637
|
+
const channelData = data.activity["channelData"] || {};
|
|
1638
|
+
logger.debug(`Conversation update - Members added: ${JSON.stringify(membersAdded)}`);
|
|
1639
|
+
logger.debug(`Conversation update - Members removed: ${JSON.stringify(membersRemoved)}`);
|
|
1640
|
+
logger.debug(`Conversation: ${JSON.stringify(conversation)}`);
|
|
1641
|
+
logger.debug(`Channel data: ${JSON.stringify(channelData)}`);
|
|
1642
|
+
// Check if the bot was added
|
|
1643
|
+
const botWasAdded = membersAdded.some((member) => {
|
|
1644
|
+
return member["id"] === MicrosoftTeamsAppClientId;
|
|
1645
|
+
});
|
|
1646
|
+
if (botWasAdded) {
|
|
1647
|
+
logger.debug("OneUptime bot was added to a Teams conversation");
|
|
1648
|
+
const welcomeText = "🎉 Welcome to OneUptime!\n\nI'm your monitoring and alerting assistant. I'll help you stay on top of your system's health and notify you about any incidents.\n\nType 'help' to see what I can do for you.";
|
|
1649
|
+
try {
|
|
1650
|
+
// Send welcome message directly using TurnContext
|
|
1651
|
+
await data.turnContext.sendActivity(welcomeText);
|
|
1652
|
+
logger.debug("Welcome message sent successfully using TurnContext");
|
|
1653
|
+
}
|
|
1654
|
+
catch (error) {
|
|
1655
|
+
logger.error("Error sending welcome message via TurnContext: " + error);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
static async handleInstallationUpdateActivity(data) {
|
|
1660
|
+
// Handle bot installation/uninstallation
|
|
1661
|
+
const action = data.activity["action"] || "";
|
|
1662
|
+
const conversation = data.activity["conversation"] || {};
|
|
1663
|
+
logger.debug(`Installation update - Action: ${action}`);
|
|
1664
|
+
logger.debug(`Conversation: ${JSON.stringify(conversation)}`);
|
|
1665
|
+
if (action === "add") {
|
|
1666
|
+
logger.debug("OneUptime bot was installed");
|
|
1667
|
+
}
|
|
1668
|
+
else if (action === "remove") {
|
|
1669
|
+
logger.debug("OneUptime bot was uninstalled");
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Process Bot Framework activity using the botbuilder SDK adapter.processActivity
|
|
1674
|
+
* This replaces the manual JWT validation and activity handling with proper SDK methods
|
|
1675
|
+
*/
|
|
1676
|
+
static async processBotActivity(req, res) {
|
|
1677
|
+
var _a, _b, _c;
|
|
1678
|
+
logger.debug("Processing Bot Framework activity using adapter.processActivity");
|
|
1679
|
+
logger.debug("Request body: " + JSON.stringify(req.body, null, 2));
|
|
1680
|
+
try {
|
|
1681
|
+
if (!MicrosoftTeamsAppClientId || !MicrosoftTeamsAppClientSecret) {
|
|
1682
|
+
logger.error("Microsoft Teams App credentials not configured");
|
|
1683
|
+
res.status(500).json({ error: "Bot credentials not configured" });
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
// Extract tenant ID from the activity
|
|
1687
|
+
const tenantId = (_c = (_b = (_a = req.body) === null || _a === void 0 ? void 0 : _a.channelData) === null || _b === void 0 ? void 0 : _b.tenant) === null || _c === void 0 ? void 0 : _c.id;
|
|
1688
|
+
if (!tenantId) {
|
|
1689
|
+
logger.error("Tenant ID not found in activity channelData");
|
|
1690
|
+
res.status(400).json({ error: "Invalid activity: missing tenant ID" });
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
// Get Bot Framework adapter
|
|
1694
|
+
const adapter = this.getBotAdapter(tenantId);
|
|
1695
|
+
// Create custom activity handler class that extends TeamsActivityHandler
|
|
1696
|
+
class OneUptimeTeamsActivityHandler extends TeamsActivityHandler {
|
|
1697
|
+
constructor() {
|
|
1698
|
+
super();
|
|
1699
|
+
// Set up message handlers using the proper API
|
|
1700
|
+
this.onMessage(async (context, next) => {
|
|
1701
|
+
logger.debug("Handling message activity: " +
|
|
1702
|
+
JSON.stringify(context.activity));
|
|
1703
|
+
await MicrosoftTeamsUtil.handleBotMessageActivity({
|
|
1704
|
+
activity: context.activity,
|
|
1705
|
+
turnContext: context,
|
|
1706
|
+
});
|
|
1707
|
+
await next();
|
|
1708
|
+
});
|
|
1709
|
+
this.onMembersAdded(async (context, next) => {
|
|
1710
|
+
logger.debug("Handling members added activity: " +
|
|
1711
|
+
JSON.stringify(context.activity));
|
|
1712
|
+
await MicrosoftTeamsUtil.handleConversationUpdateActivity({
|
|
1713
|
+
activity: context.activity,
|
|
1714
|
+
turnContext: context,
|
|
1715
|
+
});
|
|
1716
|
+
await next();
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
async onInvokeActivity(context) {
|
|
1720
|
+
logger.debug("Handling invoke activity: " + JSON.stringify(context.activity));
|
|
1721
|
+
await MicrosoftTeamsUtil.handleBotInvokeActivity({
|
|
1722
|
+
activity: context.activity,
|
|
1723
|
+
turnContext: context,
|
|
1724
|
+
});
|
|
1725
|
+
// Return empty response for invoke activities
|
|
1726
|
+
return { status: 200 };
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
// Create activity handler instance
|
|
1730
|
+
const activityHandler = new OneUptimeTeamsActivityHandler();
|
|
1731
|
+
// Use the adapter's process method with Express-style req/res
|
|
1732
|
+
await adapter.process(req, res, async (context) => {
|
|
1733
|
+
var _a, _b;
|
|
1734
|
+
logger.debug("Processing activity with TurnContext: " +
|
|
1735
|
+
JSON.stringify({
|
|
1736
|
+
activityType: context.activity.type,
|
|
1737
|
+
activityId: context.activity.id,
|
|
1738
|
+
from: (_a = context.activity.from) === null || _a === void 0 ? void 0 : _a.name,
|
|
1739
|
+
conversationId: (_b = context.activity.conversation) === null || _b === void 0 ? void 0 : _b.id,
|
|
1740
|
+
}));
|
|
1741
|
+
// Run the activity through our activity handler
|
|
1742
|
+
await activityHandler.run(context);
|
|
1743
|
+
});
|
|
1744
|
+
logger.debug("Bot Framework activity processed successfully");
|
|
1745
|
+
}
|
|
1746
|
+
catch (error) {
|
|
1747
|
+
logger.error("Error processing Bot Framework activity: " + error);
|
|
1748
|
+
if (!res.headersSent) {
|
|
1749
|
+
res.status(500).json({ error: "Failed to process bot activity" });
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
// Method to refresh teams list for a user
|
|
1754
|
+
static async refreshTeams(data) {
|
|
1755
|
+
logger.debug("=== refreshTeams called ===");
|
|
1756
|
+
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
1757
|
+
try {
|
|
1758
|
+
// Get project auth to get app access token
|
|
1759
|
+
const projectAuth = await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
1760
|
+
projectId: data.projectId,
|
|
1761
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
1762
|
+
});
|
|
1763
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
1764
|
+
throw new BadDataException("Microsoft Teams integration not found for this project");
|
|
1765
|
+
}
|
|
1766
|
+
// Get a valid app access token
|
|
1767
|
+
const accessToken = await this.refreshAccessToken({
|
|
1768
|
+
projectId: data.projectId,
|
|
1769
|
+
miscData: projectAuth.miscData,
|
|
1770
|
+
});
|
|
1771
|
+
if (!accessToken) {
|
|
1772
|
+
throw new BadDataException("Could not obtain valid access token for Microsoft Teams");
|
|
1773
|
+
}
|
|
1774
|
+
// Fetch all teams from Microsoft Graph API using app permissions
|
|
1775
|
+
const teamsResponse = await API.get({
|
|
1776
|
+
url: URL.fromString("https://graph.microsoft.com/v1.0/teams"),
|
|
1777
|
+
headers: {
|
|
1778
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1779
|
+
},
|
|
1780
|
+
});
|
|
1781
|
+
if (teamsResponse instanceof HTTPErrorResponse) {
|
|
1782
|
+
logger.error("Error fetching teams from Microsoft Teams:");
|
|
1783
|
+
logger.error(teamsResponse);
|
|
1784
|
+
throw new BadDataException("Failed to fetch teams from Microsoft Teams");
|
|
1785
|
+
}
|
|
1786
|
+
const teams = teamsResponse.data["value"] || [];
|
|
1787
|
+
if (teams.length === 0) {
|
|
1788
|
+
logger.debug("No teams found in organization");
|
|
1789
|
+
return {};
|
|
1790
|
+
}
|
|
1791
|
+
// Process teams
|
|
1792
|
+
const availableTeams = teams.reduce((acc, t) => {
|
|
1793
|
+
const team = {
|
|
1794
|
+
id: t["id"],
|
|
1795
|
+
name: t["displayName"] || "Unnamed Team",
|
|
1796
|
+
};
|
|
1797
|
+
acc[team.name] = team;
|
|
1798
|
+
return acc;
|
|
1799
|
+
}, {});
|
|
1800
|
+
logger.debug(`Fetched ${Object.keys(availableTeams).length} teams`);
|
|
1801
|
+
// Update project auth token with new teams
|
|
1802
|
+
const miscData = projectAuth.miscData || {};
|
|
1803
|
+
miscData.availableTeams = availableTeams;
|
|
1804
|
+
await WorkspaceProjectAuthTokenService.updateOneById({
|
|
1805
|
+
id: projectAuth.id,
|
|
1806
|
+
data: {
|
|
1807
|
+
miscData: miscData,
|
|
1808
|
+
},
|
|
1809
|
+
props: {
|
|
1810
|
+
isRoot: true,
|
|
1811
|
+
},
|
|
1812
|
+
});
|
|
1813
|
+
logger.debug("Updated project auth token with refreshed teams");
|
|
1814
|
+
return availableTeams;
|
|
1815
|
+
}
|
|
1816
|
+
catch (error) {
|
|
1817
|
+
logger.error("Error refreshing teams:");
|
|
1818
|
+
logger.error(error);
|
|
1819
|
+
throw error;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
// Method to get user's joined teams using user access token
|
|
1823
|
+
static async getUserJoinedTeams(accessToken) {
|
|
1824
|
+
logger.debug("=== getUserJoinedTeams called ===");
|
|
1825
|
+
try {
|
|
1826
|
+
// Get user's teams
|
|
1827
|
+
const teamsResponse = await API.get({
|
|
1828
|
+
url: URL.fromString("https://graph.microsoft.com/v1.0/me/joinedTeams"),
|
|
1829
|
+
headers: {
|
|
1830
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1831
|
+
},
|
|
1832
|
+
});
|
|
1833
|
+
if (teamsResponse instanceof HTTPErrorResponse) {
|
|
1834
|
+
logger.error("Error getting teams:");
|
|
1835
|
+
logger.error(teamsResponse);
|
|
1836
|
+
throw teamsResponse;
|
|
1837
|
+
}
|
|
1838
|
+
const teamsData = teamsResponse.data;
|
|
1839
|
+
const teams = teamsData["value"] || [];
|
|
1840
|
+
if (teams.length === 0) {
|
|
1841
|
+
logger.debug("No joined teams found for user");
|
|
1842
|
+
return {};
|
|
1843
|
+
}
|
|
1844
|
+
// Process teams
|
|
1845
|
+
const availableTeams = teams.reduce((acc, t) => {
|
|
1846
|
+
const team = {
|
|
1847
|
+
id: t["id"],
|
|
1848
|
+
name: t["displayName"] || "Unnamed Team",
|
|
1849
|
+
};
|
|
1850
|
+
acc[team.name] = team;
|
|
1851
|
+
return acc;
|
|
1852
|
+
}, {});
|
|
1853
|
+
logger.debug(`Fetched ${Object.keys(availableTeams).length} joined teams`);
|
|
1854
|
+
return availableTeams;
|
|
1855
|
+
}
|
|
1856
|
+
catch (error) {
|
|
1857
|
+
logger.error("Error getting user joined teams:");
|
|
1858
|
+
logger.error(error);
|
|
1859
|
+
throw error;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
127
1862
|
}
|
|
128
1863
|
__decorate([
|
|
129
1864
|
CaptureSpan(),
|
|
130
1865
|
__metadata("design:type", Function),
|
|
131
1866
|
__metadata("design:paramtypes", [Object]),
|
|
132
1867
|
__metadata("design:returntype", Promise)
|
|
133
|
-
],
|
|
1868
|
+
], MicrosoftTeamsUtil, "sendMessageToChannelViaIncomingWebhook", null);
|
|
1869
|
+
__decorate([
|
|
1870
|
+
CaptureSpan(),
|
|
1871
|
+
__metadata("design:type", Function),
|
|
1872
|
+
__metadata("design:paramtypes", [Object]),
|
|
1873
|
+
__metadata("design:returntype", Promise)
|
|
1874
|
+
], MicrosoftTeamsUtil, "getUsernameFromUserId", null);
|
|
1875
|
+
__decorate([
|
|
1876
|
+
CaptureSpan(),
|
|
1877
|
+
__metadata("design:type", Function),
|
|
1878
|
+
__metadata("design:paramtypes", [Object]),
|
|
1879
|
+
__metadata("design:returntype", Promise)
|
|
1880
|
+
], MicrosoftTeamsUtil, "sendDirectMessageToUser", null);
|
|
1881
|
+
__decorate([
|
|
1882
|
+
CaptureSpan(),
|
|
1883
|
+
__metadata("design:type", Function),
|
|
1884
|
+
__metadata("design:paramtypes", [Object]),
|
|
1885
|
+
__metadata("design:returntype", Promise)
|
|
1886
|
+
], MicrosoftTeamsUtil, "createChannelsIfDoesNotExist", null);
|
|
1887
|
+
__decorate([
|
|
1888
|
+
CaptureSpan(),
|
|
1889
|
+
__metadata("design:type", Function),
|
|
1890
|
+
__metadata("design:paramtypes", [Object]),
|
|
1891
|
+
__metadata("design:returntype", Promise)
|
|
1892
|
+
], MicrosoftTeamsUtil, "createChannel", null);
|
|
1893
|
+
__decorate([
|
|
1894
|
+
CaptureSpan(),
|
|
1895
|
+
__metadata("design:type", Function),
|
|
1896
|
+
__metadata("design:paramtypes", [Object]),
|
|
1897
|
+
__metadata("design:returntype", Promise)
|
|
1898
|
+
], MicrosoftTeamsUtil, "getWorkspaceChannelFromChannelName", null);
|
|
1899
|
+
__decorate([
|
|
1900
|
+
CaptureSpan(),
|
|
1901
|
+
__metadata("design:type", Function),
|
|
1902
|
+
__metadata("design:paramtypes", [Object]),
|
|
1903
|
+
__metadata("design:returntype", Promise)
|
|
1904
|
+
], MicrosoftTeamsUtil, "getWorkspaceChannelByName", null);
|
|
1905
|
+
__decorate([
|
|
1906
|
+
CaptureSpan(),
|
|
1907
|
+
__metadata("design:type", Function),
|
|
1908
|
+
__metadata("design:paramtypes", [Object]),
|
|
1909
|
+
__metadata("design:returntype", Promise)
|
|
1910
|
+
], MicrosoftTeamsUtil, "sendMessage", null);
|
|
1911
|
+
__decorate([
|
|
1912
|
+
CaptureSpan(),
|
|
1913
|
+
__metadata("design:type", Function),
|
|
1914
|
+
__metadata("design:paramtypes", [Object]),
|
|
1915
|
+
__metadata("design:returntype", Promise)
|
|
1916
|
+
], MicrosoftTeamsUtil, "sendAdaptiveCardToChannel", null);
|
|
1917
|
+
__decorate([
|
|
1918
|
+
CaptureSpan(),
|
|
1919
|
+
__metadata("design:type", Function),
|
|
1920
|
+
__metadata("design:paramtypes", [Object]),
|
|
1921
|
+
__metadata("design:returntype", Promise)
|
|
1922
|
+
], MicrosoftTeamsUtil, "getWorkspaceChannelFromChannelId", null);
|
|
1923
|
+
__decorate([
|
|
1924
|
+
CaptureSpan(),
|
|
1925
|
+
__metadata("design:type", Function),
|
|
1926
|
+
__metadata("design:paramtypes", [Object]),
|
|
1927
|
+
__metadata("design:returntype", Promise)
|
|
1928
|
+
], MicrosoftTeamsUtil, "showModalToUser", null);
|
|
1929
|
+
__decorate([
|
|
1930
|
+
CaptureSpan(),
|
|
1931
|
+
__metadata("design:type", Function),
|
|
1932
|
+
__metadata("design:paramtypes", [Object]),
|
|
1933
|
+
__metadata("design:returntype", Promise)
|
|
1934
|
+
], MicrosoftTeamsUtil, "archiveChannels", null);
|
|
1935
|
+
__decorate([
|
|
1936
|
+
CaptureSpan(),
|
|
1937
|
+
__metadata("design:type", Function),
|
|
1938
|
+
__metadata("design:paramtypes", [Object]),
|
|
1939
|
+
__metadata("design:returntype", Promise)
|
|
1940
|
+
], MicrosoftTeamsUtil, "joinChannel", null);
|
|
1941
|
+
__decorate([
|
|
1942
|
+
CaptureSpan(),
|
|
1943
|
+
__metadata("design:type", Function),
|
|
1944
|
+
__metadata("design:paramtypes", [Object]),
|
|
1945
|
+
__metadata("design:returntype", Promise)
|
|
1946
|
+
], MicrosoftTeamsUtil, "inviteUserToChannelByChannelId", null);
|
|
1947
|
+
__decorate([
|
|
1948
|
+
CaptureSpan(),
|
|
1949
|
+
__metadata("design:type", Function),
|
|
1950
|
+
__metadata("design:paramtypes", [Object]),
|
|
1951
|
+
__metadata("design:returntype", Promise)
|
|
1952
|
+
], MicrosoftTeamsUtil, "inviteUserToChannelByChannelName", null);
|
|
1953
|
+
__decorate([
|
|
1954
|
+
CaptureSpan(),
|
|
1955
|
+
__metadata("design:type", Function),
|
|
1956
|
+
__metadata("design:paramtypes", [Object]),
|
|
1957
|
+
__metadata("design:returntype", Promise)
|
|
1958
|
+
], MicrosoftTeamsUtil, "getAllWorkspaceChannels", null);
|
|
1959
|
+
__decorate([
|
|
1960
|
+
CaptureSpan(),
|
|
1961
|
+
__metadata("design:type", Function),
|
|
1962
|
+
__metadata("design:paramtypes", [Object]),
|
|
1963
|
+
__metadata("design:returntype", Promise)
|
|
1964
|
+
], MicrosoftTeamsUtil, "doesChannelExist", null);
|
|
1965
|
+
__decorate([
|
|
1966
|
+
CaptureSpan(),
|
|
1967
|
+
__metadata("design:type", Function),
|
|
1968
|
+
__metadata("design:paramtypes", [Object]),
|
|
1969
|
+
__metadata("design:returntype", Promise)
|
|
1970
|
+
], MicrosoftTeamsUtil, "isUserInDirectMessageChannel", null);
|
|
1971
|
+
__decorate([
|
|
1972
|
+
CaptureSpan(),
|
|
1973
|
+
__metadata("design:type", Function),
|
|
1974
|
+
__metadata("design:paramtypes", [Object]),
|
|
1975
|
+
__metadata("design:returntype", Promise)
|
|
1976
|
+
], MicrosoftTeamsUtil, "isUserInChannel", null);
|
|
1977
|
+
__decorate([
|
|
1978
|
+
CaptureSpan(),
|
|
1979
|
+
__metadata("design:type", Function),
|
|
1980
|
+
__metadata("design:paramtypes", []),
|
|
1981
|
+
__metadata("design:returntype", Object)
|
|
1982
|
+
], MicrosoftTeamsUtil, "getDividerBlock", null);
|
|
1983
|
+
__decorate([
|
|
1984
|
+
CaptureSpan(),
|
|
1985
|
+
__metadata("design:type", Function),
|
|
1986
|
+
__metadata("design:paramtypes", [Object]),
|
|
1987
|
+
__metadata("design:returntype", Object)
|
|
1988
|
+
], MicrosoftTeamsUtil, "getButtonsBlock", null);
|
|
1989
|
+
__decorate([
|
|
1990
|
+
CaptureSpan(),
|
|
1991
|
+
__metadata("design:type", Function),
|
|
1992
|
+
__metadata("design:paramtypes", [Object]),
|
|
1993
|
+
__metadata("design:returntype", Object)
|
|
1994
|
+
], MicrosoftTeamsUtil, "getHeaderBlock", null);
|
|
1995
|
+
__decorate([
|
|
1996
|
+
CaptureSpan(),
|
|
1997
|
+
__metadata("design:type", Function),
|
|
1998
|
+
__metadata("design:paramtypes", [Object]),
|
|
1999
|
+
__metadata("design:returntype", Object)
|
|
2000
|
+
], MicrosoftTeamsUtil, "getMarkdownBlock", null);
|
|
2001
|
+
__decorate([
|
|
2002
|
+
CaptureSpan(),
|
|
2003
|
+
__metadata("design:type", Function),
|
|
2004
|
+
__metadata("design:paramtypes", [Object]),
|
|
2005
|
+
__metadata("design:returntype", Object)
|
|
2006
|
+
], MicrosoftTeamsUtil, "getButtonBlock", null);
|
|
2007
|
+
__decorate([
|
|
2008
|
+
CaptureSpan(),
|
|
2009
|
+
__metadata("design:type", Function),
|
|
2010
|
+
__metadata("design:paramtypes", [Object]),
|
|
2011
|
+
__metadata("design:returntype", Object)
|
|
2012
|
+
], MicrosoftTeamsUtil, "getCheckboxBlock", null);
|
|
2013
|
+
__decorate([
|
|
2014
|
+
CaptureSpan(),
|
|
2015
|
+
__metadata("design:type", Function),
|
|
2016
|
+
__metadata("design:paramtypes", [Object]),
|
|
2017
|
+
__metadata("design:returntype", Object)
|
|
2018
|
+
], MicrosoftTeamsUtil, "getDateTimePickerBlock", null);
|
|
2019
|
+
__decorate([
|
|
2020
|
+
CaptureSpan(),
|
|
2021
|
+
__metadata("design:type", Function),
|
|
2022
|
+
__metadata("design:paramtypes", [Object]),
|
|
2023
|
+
__metadata("design:returntype", Object)
|
|
2024
|
+
], MicrosoftTeamsUtil, "getTextAreaBlock", null);
|
|
2025
|
+
__decorate([
|
|
2026
|
+
CaptureSpan(),
|
|
2027
|
+
__metadata("design:type", Function),
|
|
2028
|
+
__metadata("design:paramtypes", [Object]),
|
|
2029
|
+
__metadata("design:returntype", Object)
|
|
2030
|
+
], MicrosoftTeamsUtil, "getTextBoxBlock", null);
|
|
2031
|
+
__decorate([
|
|
2032
|
+
CaptureSpan(),
|
|
2033
|
+
__metadata("design:type", Function),
|
|
2034
|
+
__metadata("design:paramtypes", [Object]),
|
|
2035
|
+
__metadata("design:returntype", Object)
|
|
2036
|
+
], MicrosoftTeamsUtil, "getImageBlock", null);
|
|
2037
|
+
__decorate([
|
|
2038
|
+
CaptureSpan(),
|
|
2039
|
+
__metadata("design:type", Function),
|
|
2040
|
+
__metadata("design:paramtypes", [Object]),
|
|
2041
|
+
__metadata("design:returntype", Object)
|
|
2042
|
+
], MicrosoftTeamsUtil, "getDropdownBlock", null);
|
|
2043
|
+
__decorate([
|
|
2044
|
+
CaptureSpan(),
|
|
2045
|
+
__metadata("design:type", Function),
|
|
2046
|
+
__metadata("design:paramtypes", [Object]),
|
|
2047
|
+
__metadata("design:returntype", Object)
|
|
2048
|
+
], MicrosoftTeamsUtil, "getModalBlock", null);
|
|
2049
|
+
__decorate([
|
|
2050
|
+
CaptureSpan(),
|
|
2051
|
+
__metadata("design:type", Function),
|
|
2052
|
+
__metadata("design:paramtypes", [Object]),
|
|
2053
|
+
__metadata("design:returntype", Promise)
|
|
2054
|
+
], MicrosoftTeamsUtil, "sendPayloadBlocksToChannel", null);
|
|
2055
|
+
__decorate([
|
|
2056
|
+
CaptureSpan(),
|
|
2057
|
+
__metadata("design:type", Function),
|
|
2058
|
+
__metadata("design:paramtypes", [String]),
|
|
2059
|
+
__metadata("design:returntype", String)
|
|
2060
|
+
], MicrosoftTeamsUtil, "convertMarkdownToTeamsRichText", null);
|
|
2061
|
+
__decorate([
|
|
2062
|
+
CaptureSpan(),
|
|
2063
|
+
__metadata("design:type", Function),
|
|
2064
|
+
__metadata("design:paramtypes", [Object]),
|
|
2065
|
+
__metadata("design:returntype", Promise)
|
|
2066
|
+
], MicrosoftTeamsUtil, "handleBotMessageActivity", null);
|
|
2067
|
+
__decorate([
|
|
2068
|
+
CaptureSpan(),
|
|
2069
|
+
__metadata("design:type", Function),
|
|
2070
|
+
__metadata("design:paramtypes", [Object]),
|
|
2071
|
+
__metadata("design:returntype", Promise)
|
|
2072
|
+
], MicrosoftTeamsUtil, "handleBotInvokeActivity", null);
|
|
2073
|
+
__decorate([
|
|
2074
|
+
CaptureSpan(),
|
|
2075
|
+
__metadata("design:type", Function),
|
|
2076
|
+
__metadata("design:paramtypes", [Object]),
|
|
2077
|
+
__metadata("design:returntype", Promise)
|
|
2078
|
+
], MicrosoftTeamsUtil, "handleConversationUpdateActivity", null);
|
|
2079
|
+
__decorate([
|
|
2080
|
+
CaptureSpan(),
|
|
2081
|
+
__metadata("design:type", Function),
|
|
2082
|
+
__metadata("design:paramtypes", [Object]),
|
|
2083
|
+
__metadata("design:returntype", Promise)
|
|
2084
|
+
], MicrosoftTeamsUtil, "handleInstallationUpdateActivity", null);
|
|
2085
|
+
__decorate([
|
|
2086
|
+
CaptureSpan(),
|
|
2087
|
+
__metadata("design:type", Function),
|
|
2088
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
2089
|
+
__metadata("design:returntype", Promise)
|
|
2090
|
+
], MicrosoftTeamsUtil, "processBotActivity", null);
|
|
2091
|
+
__decorate([
|
|
2092
|
+
CaptureSpan(),
|
|
2093
|
+
__metadata("design:type", Function),
|
|
2094
|
+
__metadata("design:paramtypes", [Object]),
|
|
2095
|
+
__metadata("design:returntype", Promise)
|
|
2096
|
+
], MicrosoftTeamsUtil, "refreshTeams", null);
|
|
2097
|
+
__decorate([
|
|
2098
|
+
CaptureSpan(),
|
|
2099
|
+
__metadata("design:type", Function),
|
|
2100
|
+
__metadata("design:paramtypes", [String]),
|
|
2101
|
+
__metadata("design:returntype", Promise)
|
|
2102
|
+
], MicrosoftTeamsUtil, "getUserJoinedTeams", null);
|
|
134
2103
|
//# sourceMappingURL=MicrosoftTeams.js.map
|