From bbdd7dbf37671fdbeb92d33bbb4f6f5683e43480 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:17:54 -0600 Subject: [PATCH] Add Telegram as a macOS and Windows FMA (#35968) Introduces Telegram as a maintained app for both macOS and Windows platforms. Adds input definitions, install/uninstall scripts, output manifests, icon component, and app icon asset. Updates the apps.json output to include Telegram with appropriate metadata and descriptions. **Related issue:** Resolves # # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [ ] Added/updated automated tests - [ ] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [ ] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [ ] Confirmed that the fix is not expected to adversely impact load test results - [ ] Alerted the release DRI if additional load testing is needed ## Database migrations - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). ## New Fleet configuration settings - [ ] Setting(s) is/are explicitly excluded from GitOps If you didn't check the box above, follow this checklist for GitOps-enabled settings: - [ ] Verified that the setting is exported via `fleetctl generate-gitops` - [ ] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [ ] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) - [ ] Verified that any relevant UI is disabled when GitOps mode is enabled ## fleetd/orbit/Fleet Desktop - [ ] Verified compatibility with the latest released version of Fleet (see [Must rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md)) - [ ] If the change applies to only one platform, confirmed that `runtime.GOOS` is used as needed to isolate changes - [ ] Verified that fleetd runs on macOS, Linux and Windows - [ ] Verified auto-update works from the released version of component to the new version (see [tools/tuf/test](../tools/tuf/test/README.md)) --- .../inputs/homebrew/telegram.json | 8 ++ .../winget/scripts/telegram_install.ps1 | 68 ++++++++++++ .../winget/scripts/telegram_uninstall.ps1 | 97 ++++++++++++++++++ .../inputs/winget/telegram.json | 12 +++ ee/maintained-apps/outputs/apps.json | 14 +++ .../outputs/telegram/darwin.json | 21 ++++ .../outputs/telegram/windows.json | 21 ++++ .../components/icons/Telegram.tsx | 14 +++ .../images/app-icon-telegram-60x60@2x.png | Bin 0 -> 14143 bytes 9 files changed, 255 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/telegram.json create mode 100644 ee/maintained-apps/inputs/winget/scripts/telegram_install.ps1 create mode 100644 ee/maintained-apps/inputs/winget/scripts/telegram_uninstall.ps1 create mode 100644 ee/maintained-apps/inputs/winget/telegram.json create mode 100644 ee/maintained-apps/outputs/telegram/darwin.json create mode 100644 ee/maintained-apps/outputs/telegram/windows.json create mode 100644 frontend/pages/SoftwarePage/components/icons/Telegram.tsx create mode 100644 website/assets/images/app-icon-telegram-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/telegram.json b/ee/maintained-apps/inputs/homebrew/telegram.json new file mode 100644 index 0000000000..70d545675d --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/telegram.json @@ -0,0 +1,8 @@ +{ + "name": "Telegram", + "unique_identifier": "ru.keepcoder.Telegram", + "token": "telegram", + "installer_format": "zip", + "slug": "telegram/darwin", + "default_categories": ["Communication"] +} diff --git a/ee/maintained-apps/inputs/winget/scripts/telegram_install.ps1 b/ee/maintained-apps/inputs/winget/scripts/telegram_install.ps1 new file mode 100644 index 0000000000..bb8e2891fe --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/telegram_install.ps1 @@ -0,0 +1,68 @@ +# Learn more about .exe install scripts: +# http://fleetdm.com/learn-more-about/exe-install-scripts + +$exeFilePath = "${env:INSTALLER_PATH}" + +try { + # Verify installer file exists + if (-not (Test-Path $exeFilePath)) { + Write-Host "Error: Installer file not found at: $exeFilePath" + Exit 1 + } + + Write-Host "Installing Telegram Desktop from: $exeFilePath" + + # Add arguments to install silently + # Telegram uses an Inno Setup-based installer + # Try /VERYSILENT first (more reliable for Inno Setup), fall back to /S if needed + $processOptions = @{ + FilePath = "$exeFilePath" + ArgumentList = "/VERYSILENT /SUPPRESSMSGBOXES /NORESTART" + PassThru = $true + Wait = $true + NoNewWindow = $true + } + + # Start process and track exit code + Write-Host "Starting installation with arguments: $($processOptions.ArgumentList)" + $process = Start-Process @processOptions + + if ($null -eq $process) { + Write-Host "Error: Failed to start installer process" + Exit 1 + } + + $exitCode = $process.ExitCode + + # Prints the exit code + Write-Host "Install exit code: $exitCode" + + if ($exitCode -ne 0) { + Write-Host "Warning: Installer exited with non-zero code: $exitCode" + # Try with /S as fallback for user-scope installs + Write-Host "Attempting fallback with /S switch..." + $fallbackOptions = @{ + FilePath = "$exeFilePath" + ArgumentList = "/S" + PassThru = $true + Wait = $true + NoNewWindow = $true + } + $fallbackProcess = Start-Process @fallbackOptions + if ($null -ne $fallbackProcess) { + $fallbackExitCode = $fallbackProcess.ExitCode + Write-Host "Fallback install exit code: $fallbackExitCode" + Exit $fallbackExitCode + } + } + + Exit $exitCode + +} catch { + Write-Host "Error: $_" + Write-Host "Error details: $($_.Exception.Message)" + if ($_.Exception.InnerException) { + Write-Host "Inner exception: $($_.Exception.InnerException.Message)" + } + Exit 1 +} diff --git a/ee/maintained-apps/inputs/winget/scripts/telegram_uninstall.ps1 b/ee/maintained-apps/inputs/winget/scripts/telegram_uninstall.ps1 new file mode 100644 index 0000000000..199d53c943 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/scripts/telegram_uninstall.ps1 @@ -0,0 +1,97 @@ +# Attempts to locate Telegram Desktop's uninstaller from registry and execute it silently + +$displayName = "Telegram Desktop" +$publisher = "Telegram FZ-LLC" + +$paths = @( + 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', + 'HKCU:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall' +) + +$uninstall = $null +foreach ($p in $paths) { + $items = Get-ItemProperty "$p\*" -ErrorAction SilentlyContinue | Where-Object { + $_.DisplayName -and ($_.DisplayName -eq $displayName -or $_.DisplayName -like "$displayName*") -and ($publisher -eq "" -or $_.Publisher -eq $publisher) + } + if ($items) { $uninstall = $items | Select-Object -First 1; break } +} + +if (-not $uninstall -or -not $uninstall.UninstallString) { + Write-Host "Uninstall entry not found" + Exit 0 +} + +# Kill any running Telegram processes before uninstalling +Stop-Process -Name "Telegram" -Force -ErrorAction SilentlyContinue + +$uninstallString = $uninstall.UninstallString +$exePath = "" +$arguments = "" + +# Parse the uninstall string to extract executable path and existing arguments +# Handles both quoted and unquoted paths +if ($uninstallString -match '^"([^"]+)"(.*)') { + $exePath = $matches[1] + $arguments = $matches[2].Trim() +} elseif ($uninstallString -match '^([^\s]+)(.*)') { + $exePath = $matches[1] + $arguments = $matches[2].Trim() +} else { + Write-Host "Error: Could not parse uninstall string: $uninstallString" + Exit 1 +} + +# Build argument list array, preserving existing arguments and adding /S for silent (Inno Setup) +$baseArgumentList = @() +if ($arguments -ne '') { + # Split existing arguments and add them + $baseArgumentList += $arguments -split '\s+' +} + +function Invoke-Uninstall { + param( + [string]$Executable, + [array]$BaseArgs, + [array]$ExtraArgs + ) + + $finalArgs = @() + if ($BaseArgs) { + $finalArgs += $BaseArgs + } + if ($ExtraArgs) { + $finalArgs += $ExtraArgs + } + + Write-Host "Uninstall executable: $Executable" + Write-Host "Uninstall arguments: $($finalArgs -join ' ')" + + try { + $processOptions = @{ + FilePath = $Executable + ArgumentList = $finalArgs + NoNewWindow = $true + PassThru = $true + Wait = $true + WorkingDirectory = (Split-Path -Path $Executable -Parent) + } + + $process = Start-Process @processOptions + return $process.ExitCode + } catch { + Write-Host "Error running uninstaller: $_" + return 1 + } +} + +$preferredSilentArgs = @("/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART") +$exitCode = Invoke-Uninstall -Executable $exePath -BaseArgs $baseArgumentList -ExtraArgs $preferredSilentArgs + +if ($exitCode -ne 0) { + Write-Host "Preferred silent uninstall failed with exit code $exitCode. Retrying with /S." + $exitCode = Invoke-Uninstall -Executable $exePath -BaseArgs $baseArgumentList -ExtraArgs @("/S") +} + +Exit $exitCode diff --git a/ee/maintained-apps/inputs/winget/telegram.json b/ee/maintained-apps/inputs/winget/telegram.json new file mode 100644 index 0000000000..5147d6e559 --- /dev/null +++ b/ee/maintained-apps/inputs/winget/telegram.json @@ -0,0 +1,12 @@ +{ + "name": "Telegram", + "slug": "telegram/windows", + "package_identifier": "Telegram.TelegramDesktop", + "unique_identifier": "Telegram Desktop", + "install_script_path": "ee/maintained-apps/inputs/winget/scripts/telegram_install.ps1", + "uninstall_script_path": "ee/maintained-apps/inputs/winget/scripts/telegram_uninstall.ps1", + "installer_arch": "x64", + "installer_type": "exe", + "installer_scope": "user", + "default_categories": ["Communication"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 8c149bfb41..6315f67eef 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -498,6 +498,20 @@ "unique_identifier": "TeamViewer", "description": "TeamViewer is a versatile remote access and connectivity platform trusted for secure remote desktop control, support, and collaboration." }, + { + "name": "Telegram", + "slug": "telegram/darwin", + "platform": "darwin", + "unique_identifier": "ru.keepcoder.Telegram", + "description": "Telegram is a messaging app with a focus on speed and security." + }, + { + "name": "Telegram", + "slug": "telegram/windows", + "platform": "windows", + "unique_identifier": "Telegram Desktop", + "description": "Telegram is a messaging app with a focus on speed and security." + }, { "name": "Thunderbird", "slug": "thunderbird/darwin", diff --git a/ee/maintained-apps/outputs/telegram/darwin.json b/ee/maintained-apps/outputs/telegram/darwin.json new file mode 100644 index 0000000000..534276ef68 --- /dev/null +++ b/ee/maintained-apps/outputs/telegram/darwin.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "12.2", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'ru.keepcoder.Telegram';" + }, + "installer_url": "https://osx.telegram.org/updates/Telegram-12.2.277101.app.zip", + "install_script_ref": "8e5525d9", + "uninstall_script_ref": "a39fa3ea", + "sha256": "583a3dd9600445059a4a6829a51f026b0451bc8679599a5b653781e99959beed", + "default_categories": [ + "Communication" + ] + } + ], + "refs": { + "8e5525d9": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\n# extract contents\nunzip \"$INSTALLER_PATH\" -d \"$TMPDIR\"\n# copy to the applications folder\nquit_application 'ru.keepcoder.Telegram'\nif [ -d \"$APPDIR/Telegram.app\" ]; then\n\tsudo mv \"$APPDIR/Telegram.app\" \"$TMPDIR/Telegram.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Telegram.app\" \"$APPDIR\"\n", + "a39fa3ea": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 && \"$console_user\" == \"root\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nquit_application 'ru.keepcoder.Telegram'\nsudo rm -rf \"$APPDIR/Telegram.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/*.ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/*.ru.keepcoder.Telegram.TelegramShare'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/ru.keepcoder.Telegram.TelegramShare'\ntrash $LOGGED_IN_USER '~/Library/Application Support/ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Caches/ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Containers/ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Containers/ru.keepcoder.Telegram.TelegramShare'\ntrash $LOGGED_IN_USER '~/Library/Cookies/ru.keepcoder.Telegram.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/*.ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/*.ru.keepcoder.Telegram.TelegramShare'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/ru.keepcoder.Telegram'\ntrash $LOGGED_IN_USER '~/Library/Preferences/ru.keepcoder.Telegram.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/ru.keepcoder.Telegram.savedState'\n" + } +} diff --git a/ee/maintained-apps/outputs/telegram/windows.json b/ee/maintained-apps/outputs/telegram/windows.json new file mode 100644 index 0000000000..7c7b1b3398 --- /dev/null +++ b/ee/maintained-apps/outputs/telegram/windows.json @@ -0,0 +1,21 @@ +{ + "versions": [ + { + "version": "6.3.1", + "queries": { + "exists": "SELECT 1 FROM programs WHERE name = 'Telegram Desktop' AND publisher = 'Telegram FZ-LLC';" + }, + "installer_url": "https://td.telegram.org/tx64/tsetup-x64.6.3.1.exe", + "install_script_ref": "0b88a95e", + "uninstall_script_ref": "42311f1b", + "sha256": "507487cd543d04282bab696465703b06b86eb742355fbc4e73c5a7a97937b8e9", + "default_categories": [ + "Communication" + ] + } + ], + "refs": { + "0b88a95e": "# Learn more about .exe install scripts:\n# http://fleetdm.com/learn-more-about/exe-install-scripts\n\n$exeFilePath = \"${env:INSTALLER_PATH}\"\n\ntry {\n # Verify installer file exists\n if (-not (Test-Path $exeFilePath)) {\n Write-Host \"Error: Installer file not found at: $exeFilePath\"\n Exit 1\n }\n\n Write-Host \"Installing Telegram Desktop from: $exeFilePath\"\n \n # Add arguments to install silently\n # Telegram uses an Inno Setup-based installer\n # Try /VERYSILENT first (more reliable for Inno Setup), fall back to /S if needed\n $processOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/VERYSILENT /SUPPRESSMSGBOXES /NORESTART\"\n PassThru = $true\n Wait = $true\n NoNewWindow = $true\n }\n \n # Start process and track exit code\n Write-Host \"Starting installation with arguments: $($processOptions.ArgumentList)\"\n $process = Start-Process @processOptions\n \n if ($null -eq $process) {\n Write-Host \"Error: Failed to start installer process\"\n Exit 1\n }\n \n $exitCode = $process.ExitCode\n \n # Prints the exit code\n Write-Host \"Install exit code: $exitCode\"\n \n if ($exitCode -ne 0) {\n Write-Host \"Warning: Installer exited with non-zero code: $exitCode\"\n # Try with /S as fallback for user-scope installs\n Write-Host \"Attempting fallback with /S switch...\"\n $fallbackOptions = @{\n FilePath = \"$exeFilePath\"\n ArgumentList = \"/S\"\n PassThru = $true\n Wait = $true\n NoNewWindow = $true\n }\n $fallbackProcess = Start-Process @fallbackOptions\n if ($null -ne $fallbackProcess) {\n $fallbackExitCode = $fallbackProcess.ExitCode\n Write-Host \"Fallback install exit code: $fallbackExitCode\"\n Exit $fallbackExitCode\n }\n }\n \n Exit $exitCode\n\n} catch {\n Write-Host \"Error: $_\"\n Write-Host \"Error details: $($_.Exception.Message)\"\n if ($_.Exception.InnerException) {\n Write-Host \"Inner exception: $($_.Exception.InnerException.Message)\"\n }\n Exit 1\n}\n", + "42311f1b": "# Attempts to locate Telegram Desktop's uninstaller from registry and execute it silently\n\n$displayName = \"Telegram Desktop\"\n$publisher = \"Telegram FZ-LLC\"\n\n$paths = @(\n 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',\n 'HKCU:\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall'\n)\n\n$uninstall = $null\nforeach ($p in $paths) {\n $items = Get-ItemProperty \"$p\\*\" -ErrorAction SilentlyContinue | Where-Object {\n $_.DisplayName -and ($_.DisplayName -eq $displayName -or $_.DisplayName -like \"$displayName*\") -and ($publisher -eq \"\" -or $_.Publisher -eq $publisher)\n }\n if ($items) { $uninstall = $items | Select-Object -First 1; break }\n}\n\nif (-not $uninstall -or -not $uninstall.UninstallString) {\n Write-Host \"Uninstall entry not found\"\n Exit 0\n}\n\n# Kill any running Telegram processes before uninstalling\nStop-Process -Name \"Telegram\" -Force -ErrorAction SilentlyContinue\n\n$uninstallString = $uninstall.UninstallString\n$exePath = \"\"\n$arguments = \"\"\n\n# Parse the uninstall string to extract executable path and existing arguments\n# Handles both quoted and unquoted paths\nif ($uninstallString -match '^\"([^\"]+)\"(.*)') {\n $exePath = $matches[1]\n $arguments = $matches[2].Trim()\n} elseif ($uninstallString -match '^([^\\s]+)(.*)') {\n $exePath = $matches[1]\n $arguments = $matches[2].Trim()\n} else {\n Write-Host \"Error: Could not parse uninstall string: $uninstallString\"\n Exit 1\n}\n\n# Build argument list array, preserving existing arguments and adding /S for silent (Inno Setup)\n$baseArgumentList = @()\nif ($arguments -ne '') {\n # Split existing arguments and add them\n $baseArgumentList += $arguments -split '\\s+'\n}\n\nfunction Invoke-Uninstall {\n param(\n [string]$Executable,\n [array]$BaseArgs,\n [array]$ExtraArgs\n )\n\n $finalArgs = @()\n if ($BaseArgs) {\n $finalArgs += $BaseArgs\n }\n if ($ExtraArgs) {\n $finalArgs += $ExtraArgs\n }\n\n Write-Host \"Uninstall executable: $Executable\"\n Write-Host \"Uninstall arguments: $($finalArgs -join ' ')\"\n\n try {\n $processOptions = @{\n FilePath = $Executable\n ArgumentList = $finalArgs\n NoNewWindow = $true\n PassThru = $true\n Wait = $true\n WorkingDirectory = (Split-Path -Path $Executable -Parent)\n }\n\n $process = Start-Process @processOptions\n return $process.ExitCode\n } catch {\n Write-Host \"Error running uninstaller: $_\"\n return 1\n }\n}\n\n$preferredSilentArgs = @(\"/VERYSILENT\", \"/SUPPRESSMSGBOXES\", \"/NORESTART\")\n$exitCode = Invoke-Uninstall -Executable $exePath -BaseArgs $baseArgumentList -ExtraArgs $preferredSilentArgs\n\nif ($exitCode -ne 0) {\n Write-Host \"Preferred silent uninstall failed with exit code $exitCode. Retrying with /S.\"\n $exitCode = Invoke-Uninstall -Executable $exePath -BaseArgs $baseArgumentList -ExtraArgs @(\"/S\")\n}\n\nExit $exitCode\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/Telegram.tsx b/frontend/pages/SoftwarePage/components/icons/Telegram.tsx new file mode 100644 index 0000000000..cf8109ca21 --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/Telegram.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const Telegram = (props: SVGProps) => ( + + + +); +export default Telegram; diff --git a/website/assets/images/app-icon-telegram-60x60@2x.png b/website/assets/images/app-icon-telegram-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ad94eda4daf9792fc15f9886cddb70b375fda89d GIT binary patch literal 14143 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 literal 0 HcmV?d00001