mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-27 22:34:51 +00:00
Merge branch 'master' into chat-relays
This commit is contained in:
@@ -605,6 +605,9 @@ jobs:
|
||||
- name: Checkout current repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install packages for archiving
|
||||
run: sudo apt install -y msitools gcc-mingw-w64
|
||||
|
||||
- name: Build archives
|
||||
run: |
|
||||
INIT_DIR='${{ runner.temp }}/artifacts'
|
||||
@@ -612,6 +615,8 @@ jobs:
|
||||
TAG='${{ github.ref_name }}'
|
||||
URL='https://github.com/${{ github.repository }}/releases/download'
|
||||
PREFIX='${{ github.event.repository.name }}-libs'
|
||||
# Windows-specific
|
||||
FILE_URL='https://raw.githubusercontent.com/${{ github.repository }}/refs/tags/${{ github.ref_name }}'
|
||||
|
||||
# Setup directories
|
||||
mkdir "$INIT_DIR" "$RELEASE_DIR" && cd "$INIT_DIR"
|
||||
@@ -620,6 +625,7 @@ jobs:
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L "${URL}/${TAG}/simplex-desktop-ubuntu-22_04-x86_64.deb" -o linux.deb
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L "${URL}/${TAG}/simplex-desktop-macos-aarch64.dmg" -o macos-aarch64.dmg
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L "${URL}/${TAG}/simplex-desktop-macos-x86_64.dmg" -o macos-x86_64.dmg
|
||||
curl --proto '=https' --tlsv1.2 -sSf -L "${URL}/${TAG}/simplex-desktop-windows-x86_64.msi" -o windows-x86_64.msi
|
||||
|
||||
# Linux
|
||||
# -----
|
||||
@@ -646,6 +652,18 @@ jobs:
|
||||
zip -r "${PREFIX}-macos-x86_64.zip" libs
|
||||
mv "${PREFIX}-macos-x86_64.zip" "$RELEASE_DIR" && cd "$INIT_DIR"
|
||||
|
||||
# Windows: x86_64
|
||||
# ---------------
|
||||
msiextract windows-x86_64.msi -C windows-out && cd windows-out/SimpleX/app/resources
|
||||
|
||||
# We need to generate library that exports symbols from Windows dll
|
||||
curl --proto '=https' --tlsv1.2 -sSf -LO "${FILE_URL}/libsimplex.dll.def"
|
||||
x86_64-w64-mingw32-dlltool -d libsimplex.dll.def -l libsimplex.lib -D libsimplex.dll
|
||||
|
||||
mkdir libs && cp *.dll *.lib libs/
|
||||
zip -r "${PREFIX}-windows-x86_64.zip" libs
|
||||
mv "${PREFIX}-windows-x86_64.zip" "$RELEASE_DIR" && cd "$INIT_DIR"
|
||||
|
||||
- name: Create release in libs repo and upload artifacts
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
## Connect to the team
|
||||
|
||||
You can connect to the team via the app using "chat with the developers button" available when you have no conversations in the profile, "Send questions and ideas" in the app settings or via our [SimpleX address](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). Please connect to:
|
||||
You can connect to the team via the app using "chat with the developers button" available when you have no conversations in the profile, "Send questions and ideas" in the app settings or via our [SimpleX address](https://smp6.simplex.im/a#lrdvu2d8A1GumSmoKb2krQmtKhWXq-tyGpHuM7aMwsw). Please connect to:
|
||||
|
||||
- to ask any questions
|
||||
- to suggest any improvements
|
||||
@@ -54,7 +54,7 @@ If you are interested in helping us to integrate open-source language models, an
|
||||
|
||||
## Join user groups
|
||||
|
||||
You can find the groups created by users in [SimpleX Directory](https://simplex.chat/directory/). It is also available as [SimpleX bot](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) that allows to add your own groups and communities to the directory. We are not responsible for the content shared in these groups.
|
||||
You can find the groups created by users in [SimpleX Directory](https://simplex.chat/directory/). It is also available as [SimpleX bot](https://smp4.simplex.im/a#lXUjJW5vHYQzoLYgmi8GbxkGP41_kjefFvBrdwg-0Ok) that allows to add your own groups and communities to the directory. We are not responsible for the content shared in these groups.
|
||||
|
||||
**Please note**: The groups below are created for the users to be able to ask questions, make suggestions and ask questions about SimpleX Chat only.
|
||||
|
||||
|
||||
@@ -94,6 +94,5 @@ mkChatOpts BroadcastBotOpts {coreOptions, botDisplayName} =
|
||||
autoAcceptFileSize = 0,
|
||||
muteNotifications = True,
|
||||
markRead = False,
|
||||
createBot = Just CreateBotOpts {botDisplayName, allowFiles = False},
|
||||
maintenance = False
|
||||
createBot = Just CreateBotOpts {botDisplayName, allowFiles = False}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ data DirectoryOpts = DirectoryOpts
|
||||
adminUsers :: [KnownContact],
|
||||
superUsers :: [KnownContact],
|
||||
ownersGroup :: Maybe KnownGroup,
|
||||
noAddress :: Bool, -- skip creating address
|
||||
blockedWordsFile :: Maybe FilePath,
|
||||
blockedFragmentsFile :: Maybe FilePath,
|
||||
blockedExtensionRules :: Maybe FilePath,
|
||||
@@ -70,6 +71,11 @@ directoryOpts appDir defaultDbName = do
|
||||
<> metavar "OWNERS_GROUP"
|
||||
<> help "The group of group owners in the format GROUP_ID:DISPLAY_NAME - owners of listed groups will be invited automatically"
|
||||
)
|
||||
noAddress <-
|
||||
switch
|
||||
( long "no-address"
|
||||
<> help "skip checking and creating service address"
|
||||
)
|
||||
blockedWordsFile <-
|
||||
optional $
|
||||
strOption
|
||||
@@ -153,6 +159,7 @@ directoryOpts appDir defaultDbName = do
|
||||
adminUsers,
|
||||
superUsers,
|
||||
ownersGroup,
|
||||
noAddress,
|
||||
blockedWordsFile,
|
||||
blockedFragmentsFile,
|
||||
blockedExtensionRules,
|
||||
@@ -194,8 +201,7 @@ mkChatOpts DirectoryOpts {coreOptions, serviceName} =
|
||||
autoAcceptFileSize = 0,
|
||||
muteNotifications = True,
|
||||
markRead = False,
|
||||
createBot = Just CreateBotOpts {botDisplayName = serviceName, allowFiles = False},
|
||||
maintenance = False
|
||||
createBot = Just CreateBotOpts {botDisplayName = serviceName, allowFiles = False}
|
||||
}
|
||||
|
||||
parseMigrateLog :: ReadM MigrateLog
|
||||
|
||||
@@ -184,10 +184,11 @@ directoryPreStartHook :: DirectoryOpts -> ChatController -> IO ()
|
||||
directoryPreStartHook opts ChatController {config, chatStore} = runDirectoryMigrations opts config chatStore
|
||||
|
||||
directoryPostStartHook :: DirectoryOpts -> ServiceState -> ChatController -> IO ()
|
||||
directoryPostStartHook opts env cc =
|
||||
directoryPostStartHook opts@DirectoryOpts {noAddress, testing} env cc =
|
||||
readTVarIO (currentUser cc) >>= \case
|
||||
Nothing -> putStrLn "No current user" >> exitFailure
|
||||
Just User {userId, profile = p@LocalProfile {preferences}} -> do
|
||||
unless noAddress $ initializeBotAddress' (not testing) cc
|
||||
listingsUpdated env cc
|
||||
let cmds = fromMaybe [] $ preferences >>= commands_
|
||||
unless (cmds == directoryCommands) $ do
|
||||
@@ -216,7 +217,7 @@ directoryCommands =
|
||||
idParam = Just "<ID>"
|
||||
|
||||
directoryService :: DirectoryLog -> DirectoryOpts -> ChatConfig -> IO ()
|
||||
directoryService st opts@DirectoryOpts {testing} cfg = do
|
||||
directoryService st opts cfg = do
|
||||
env <- newServiceState opts
|
||||
let chatHooks =
|
||||
defaultChatHooks
|
||||
@@ -224,8 +225,7 @@ directoryService st opts@DirectoryOpts {testing} cfg = do
|
||||
postStartHook = Just $ directoryPostStartHook opts env,
|
||||
acceptMember = Just $ acceptMemberHook opts env
|
||||
}
|
||||
simplexChatCore cfg {chatHooks} (mkChatOpts opts) $ \user cc -> do
|
||||
initializeBotAddress' (not testing) cc
|
||||
simplexChatCore cfg {chatHooks} (mkChatOpts opts) $ \user cc ->
|
||||
raceAny_ $
|
||||
[ forever $ void getLine,
|
||||
forever $ do
|
||||
|
||||
@@ -14,7 +14,7 @@ Please share your use cases and implementations.
|
||||
## Quick start: a simple bot
|
||||
|
||||
```
|
||||
npm i simplex-chat@6.5.0-beta.4.2
|
||||
npm i simplex-chat@6.5.0-beta.4.4
|
||||
```
|
||||
|
||||
Simple bot that replies with squares of numbers you send to it:
|
||||
@@ -28,7 +28,7 @@ Simple bot that replies with squares of numbers you send to it:
|
||||
profile: {displayName: "Squaring bot example", fullName: ""},
|
||||
dbOpts: {dbFilePrefix: "./squaring_bot", dbKey: ""},
|
||||
options: {
|
||||
addressSettings: {welcomeMessage: "If you send me a number, I will calculate its square."},
|
||||
addressSettings: {welcomeMessage: "Send a number, I will square it.",
|
||||
},
|
||||
onMessage: async (ci, content) => {
|
||||
const n = +content.text
|
||||
@@ -44,9 +44,11 @@ Simple bot that replies with squares of numbers you send to it:
|
||||
If you installed this package as dependency, you can run this example with:
|
||||
|
||||
```sh
|
||||
node ./node_modules/simplex-chat/examples/squaring-bot-readme.js`
|
||||
node ./node_modules/simplex-chat/examples/squaring-bot-readme.js
|
||||
```
|
||||
|
||||
If you run it on Mac, the first time it will take 20-30 seconds for MacOS to verify the library.
|
||||
|
||||
If you cloned this repository, you can:
|
||||
|
||||
```
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
"dependencies": [
|
||||
"<!(node -p \"require('node-addon-api').gyp\")"
|
||||
],
|
||||
"libraries": [
|
||||
"-L<(module_root_dir)/libs",
|
||||
"-lsimplex"
|
||||
],
|
||||
"cflags!": [ "-fno-exceptions" ],
|
||||
"cflags_cc!": [ "-fno-exceptions" ],
|
||||
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ],
|
||||
"conditions": [
|
||||
["OS=='mac'", {
|
||||
"libraries": [
|
||||
"-L<(module_root_dir)/libs",
|
||||
"-lsimplex"
|
||||
],
|
||||
"xcode_settings": {
|
||||
"OTHER_LDFLAGS": [
|
||||
"-Wl,-rpath,@loader_path/../../libs"
|
||||
@@ -25,9 +25,24 @@
|
||||
}
|
||||
}],
|
||||
["OS=='linux'", {
|
||||
"libraries": [
|
||||
"-L<(module_root_dir)/libs",
|
||||
"-lsimplex"
|
||||
],
|
||||
"ldflags": [
|
||||
"-Wl,-rpath,'$$ORIGIN'/../../libs"
|
||||
]
|
||||
}],
|
||||
["OS=='win'", {
|
||||
"libraries": [
|
||||
"<(module_root_dir)/libs/libsimplex.lib"
|
||||
],
|
||||
"copies": [{
|
||||
"destination": "<(PRODUCT_DIR)",
|
||||
"files": [
|
||||
"<(module_root_dir)/libs/*"
|
||||
]
|
||||
}]
|
||||
}]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,17 @@ namespace simplex {
|
||||
using namespace Napi;
|
||||
|
||||
void haskell_init() {
|
||||
#ifdef _WIN32
|
||||
// non-moving GC is broken on windows with GHC 9.4-9.6.3
|
||||
int argc = 5;
|
||||
const char *argv[] = {
|
||||
"simplex",
|
||||
"+RTS", // requires `hs_init_with_rtsopts`
|
||||
"-A64m", // chunk size for new allocations
|
||||
"-H64m", // initial heap size
|
||||
"--install-signal-handlers=no",
|
||||
nullptr};
|
||||
#else
|
||||
int argc = 6;
|
||||
const char *argv[] = {
|
||||
"simplex",
|
||||
@@ -19,6 +30,7 @@ void haskell_init() {
|
||||
"-xn", // non-moving GC
|
||||
"--install-signal-handlers=no",
|
||||
nullptr};
|
||||
#endif
|
||||
char **pargv = const_cast<char **>(argv);
|
||||
hs_init_with_rtsopts(&argc, &pargv);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
profile: {displayName: "Squaring bot example", fullName: ""},
|
||||
dbOpts: {dbFilePrefix: "./squaring_bot", dbKey: ""},
|
||||
options: {
|
||||
addressSettings: {welcomeMessage: "If you send me a number, I will calculate its square."},
|
||||
addressSettings: {welcomeMessage: "Send a number, I will square it."},
|
||||
},
|
||||
onMessage: async (ci, content) => {
|
||||
const n = +content.text
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simplex-chat",
|
||||
"version": "6.5.0-beta.4.2",
|
||||
"version": "6.5.0-beta.4.4",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
|
||||
@@ -18,6 +18,7 @@ CMDS="curl git docker"
|
||||
|
||||
INIT_DIR="$PWD"
|
||||
TEMPDIR="$(mktemp -d)"
|
||||
PID_MAX_ORIGINAL="$(sysctl -n kernel.pid_max)"
|
||||
|
||||
ARCHES="${ARCHES:-aarch64 armv7a}"
|
||||
|
||||
@@ -32,6 +33,15 @@ cleanup() {
|
||||
rm -rf -- "${TEMPDIR}"
|
||||
docker rm --force "${CONTAINER_NAME}" 2>/dev/null || :
|
||||
docker image rm "${IMAGE_NAME}" 2>/dev/null || :
|
||||
|
||||
if [ "$(sysctl -n kernel.pid_max)" != "$PID_MAX_ORIGINAL" ]; then
|
||||
printf 'Adjusting kernel.pid_max back to original value...\n'
|
||||
if $SUDO sysctl kernel.pid_max="$PID_MAX_ORIGINAL"; then
|
||||
printf 'Successfully adjusted kernel.pid_max\n'
|
||||
else
|
||||
printf 'Failed to adjust kernel.pid_max. Please set the value manually with: %s sysctl kernel.pid_max=%s\n' "$SUDO" "$PID_MAX_ORIGINAL"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
trap 'cleanup' EXIT INT
|
||||
|
||||
@@ -52,6 +62,19 @@ check() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$PID_MAX_ORIGINAL" -gt 65535 ]; then
|
||||
SUDO=$(command -v sudo || command -v doas) || { echo "No sudo or doas"; exit 1; }
|
||||
|
||||
printf 'Adjusting kernel.pid_max value to 65535...\n'
|
||||
|
||||
if $SUDO sysctl kernel.pid_max=65535; then
|
||||
printf 'Successfully adjusted kernel.pid_max\n'
|
||||
else
|
||||
printf 'Failed to adjust kernel.pid_max, aborting.\n'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
set -u
|
||||
}
|
||||
|
||||
@@ -182,6 +205,9 @@ main() {
|
||||
3) build core library with nix (12-24 hours).
|
||||
4) build APK and compare with downloaded one
|
||||
|
||||
The script will ask for sudo password to adjust kernel.pid_max (needed for armv7a build)
|
||||
and set it back to otiginal value when the build is done.
|
||||
|
||||
Continue?'
|
||||
|
||||
read _
|
||||
|
||||
@@ -34,8 +34,8 @@ import Simplex.Chat.Types
|
||||
import Simplex.Chat.Types.Preferences (FeatureAllowed (..), FilesPreference (..), Preferences (..), emptyChatPrefs)
|
||||
import Simplex.Chat.View (ChatResponseEvent, serializeChatError, serializeChatResponse, simplexChatContact)
|
||||
import Simplex.Messaging.Agent.Protocol
|
||||
import Simplex.Messaging.Agent.Store.Shared (MigrationConfig (..), MigrationConfirmation (..))
|
||||
import Simplex.Messaging.Agent.Store.Common (DBStore, withTransaction)
|
||||
import Simplex.Messaging.Agent.Store.Shared (MigrationConfig (..), MigrationConfirmation (..))
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import System.Exit (exitFailure)
|
||||
import System.IO (hFlush, stdout)
|
||||
@@ -43,7 +43,7 @@ import Text.Read (readMaybe)
|
||||
import UnliftIO.Async
|
||||
|
||||
simplexChatCore :: ChatConfig -> ChatOpts -> (User -> ChatController -> IO ()) -> IO ()
|
||||
simplexChatCore cfg@ChatConfig {confirmMigrations, testView, chatHooks} opts@ChatOpts {coreOptions = coreOptions@CoreChatOpts {dbOptions, logAgent, yesToUpMigrations, migrationBackupPath}, createBot, maintenance} chat =
|
||||
simplexChatCore cfg@ChatConfig {confirmMigrations, testView, chatHooks} opts@ChatOpts {coreOptions = coreOptions@CoreChatOpts {dbOptions, logAgent, yesToUpMigrations, migrationBackupPath, maintenance}, createBot} chat =
|
||||
case logAgent of
|
||||
Just level -> do
|
||||
setLogLevel level
|
||||
@@ -60,13 +60,16 @@ simplexChatCore cfg@ChatConfig {confirmMigrations, testView, chatHooks} opts@Cha
|
||||
u_ <- selectActiveUser coreOptions chatStore users
|
||||
let backgroundMode = maintenance
|
||||
cc <- newChatController db u_ cfg opts backgroundMode
|
||||
u <- maybe (createActiveUser cc coreOptions createBot) pure u_
|
||||
forM_ (preStartHook chatHooks) ($ cc)
|
||||
u <- maybe (noMaintenance >> createActiveUser cc coreOptions createBot) pure u_
|
||||
unless testView $ putStrLn $ "Current user: " <> userStr u
|
||||
unless maintenance $ forM_ (preStartHook chatHooks) ($ cc)
|
||||
runSimplexChat cfg opts u cc chat
|
||||
noMaintenance = when maintenance $ do
|
||||
putStrLn "exiting: no active user in maintenance mode"
|
||||
exitFailure
|
||||
|
||||
runSimplexChat :: ChatConfig -> ChatOpts -> User -> ChatController -> (User -> ChatController -> IO ()) -> IO ()
|
||||
runSimplexChat ChatConfig {testView} ChatOpts {coreOptions = CoreChatOpts {chatRelay}, maintenance} u cc@ChatController {config = ChatConfig {chatHooks}} chat
|
||||
runSimplexChat ChatConfig {testView} ChatOpts {coreOptions = CoreChatOpts {chatRelay, maintenance}} u cc@ChatController {config = ChatConfig {chatHooks}} chat
|
||||
| maintenance = wait =<< async (chat u cc)
|
||||
| otherwise = do
|
||||
a1 <- runReaderT (startChatController True True) cc
|
||||
|
||||
@@ -256,7 +256,8 @@ mobileChatOpts dbOptions =
|
||||
chatRelay = False,
|
||||
highlyAvailable = False,
|
||||
yesToUpMigrations = False,
|
||||
migrationBackupPath = Just ""
|
||||
migrationBackupPath = Just "",
|
||||
maintenance = True
|
||||
},
|
||||
chatCmd = "",
|
||||
chatCmdDelay = 3,
|
||||
@@ -269,8 +270,7 @@ mobileChatOpts dbOptions =
|
||||
autoAcceptFileSize = 0,
|
||||
muteNotifications = True,
|
||||
markRead = False,
|
||||
createBot = Nothing,
|
||||
maintenance = True
|
||||
createBot = Nothing
|
||||
}
|
||||
|
||||
defaultMobileConfig :: ChatConfig
|
||||
|
||||
+12
-12
@@ -50,8 +50,7 @@ data ChatOpts = ChatOpts
|
||||
autoAcceptFileSize :: Integer,
|
||||
muteNotifications :: Bool,
|
||||
markRead :: Bool,
|
||||
createBot :: Maybe CreateBotOpts,
|
||||
maintenance :: Bool
|
||||
createBot :: Maybe CreateBotOpts
|
||||
}
|
||||
|
||||
data CoreChatOpts = CoreChatOpts
|
||||
@@ -69,7 +68,8 @@ data CoreChatOpts = CoreChatOpts
|
||||
chatRelay :: Bool,
|
||||
highlyAvailable :: Bool,
|
||||
yesToUpMigrations :: Bool,
|
||||
migrationBackupPath :: Maybe FilePath
|
||||
migrationBackupPath :: Maybe FilePath,
|
||||
maintenance :: Bool
|
||||
}
|
||||
|
||||
data CreateBotOpts = CreateBotOpts
|
||||
@@ -251,6 +251,12 @@ coreChatOptsP appDir defaultDbName = do
|
||||
<> help "Automatically confirm \"up\" database migrations"
|
||||
)
|
||||
migrationBackupPath <- migrationBackupPathP
|
||||
maintenance <-
|
||||
switch
|
||||
( long "maintenance"
|
||||
<> short 'm'
|
||||
<> help "Run in maintenance mode (/_start to start chat)"
|
||||
)
|
||||
pure
|
||||
CoreChatOpts
|
||||
{ dbOptions,
|
||||
@@ -278,7 +284,8 @@ coreChatOptsP appDir defaultDbName = do
|
||||
chatRelay,
|
||||
highlyAvailable,
|
||||
yesToUpMigrations,
|
||||
migrationBackupPath
|
||||
migrationBackupPath,
|
||||
maintenance
|
||||
}
|
||||
where
|
||||
useTcpTimeout p t = 1000000 * if t > 0 then t else maybe 7 (const 15) p
|
||||
@@ -383,12 +390,6 @@ chatOptsP appDir defaultDbName = do
|
||||
( long "create-bot-allow-files"
|
||||
<> help "Flag for created bot to allow files (only allowed together with --create-bot option)"
|
||||
)
|
||||
maintenance <-
|
||||
switch
|
||||
( long "maintenance"
|
||||
<> short 'm'
|
||||
<> help "Run in maintenance mode (/_start to start chat)"
|
||||
)
|
||||
pure
|
||||
ChatOpts
|
||||
{ coreOptions,
|
||||
@@ -407,8 +408,7 @@ chatOptsP appDir defaultDbName = do
|
||||
Just botDisplayName -> Just CreateBotOpts {botDisplayName, allowFiles = createBotAllowFiles}
|
||||
Nothing
|
||||
| createBotAllowFiles -> error "--create-bot-allow-files option requires --create-bot-name option"
|
||||
| otherwise -> Nothing,
|
||||
maintenance
|
||||
| otherwise -> Nothing
|
||||
}
|
||||
|
||||
parseProtocolServers :: ProtocolTypeI p => ReadM [ProtoServerWithAuth p]
|
||||
|
||||
@@ -95,6 +95,7 @@ mkDirectoryOpts TestParams {tmpPath = ps} superUsers ownersGroup webFolder =
|
||||
adminUsers = [],
|
||||
superUsers,
|
||||
ownersGroup,
|
||||
noAddress = False,
|
||||
blockedFragmentsFile = Nothing,
|
||||
blockedWordsFile = Nothing,
|
||||
blockedExtensionRules = Nothing,
|
||||
|
||||
+5
-5
@@ -117,8 +117,7 @@ testOpts =
|
||||
autoAcceptFileSize = 0,
|
||||
muteNotifications = True,
|
||||
markRead = True,
|
||||
createBot = Nothing,
|
||||
maintenance = False
|
||||
createBot = Nothing
|
||||
}
|
||||
|
||||
testCoreOpts :: CoreChatOpts
|
||||
@@ -153,7 +152,8 @@ testCoreOpts =
|
||||
chatRelay = False,
|
||||
highlyAvailable = False,
|
||||
yesToUpMigrations = False,
|
||||
migrationBackupPath = Nothing
|
||||
migrationBackupPath = Nothing,
|
||||
maintenance = False
|
||||
}
|
||||
|
||||
relayTestOpts :: ChatOpts
|
||||
@@ -161,7 +161,7 @@ relayTestOpts = testOpts {coreOptions = testCoreOpts {chatRelay = True}}
|
||||
|
||||
#if !defined(dbPostgres)
|
||||
getTestOpts :: Bool -> ScrubbedBytes -> ChatOpts
|
||||
getTestOpts maintenance dbKey = testOpts {maintenance, coreOptions = testCoreOpts {dbOptions = (dbOptions testCoreOpts) {dbKey}}}
|
||||
getTestOpts maintenance dbKey = testOpts {coreOptions = testCoreOpts {dbOptions = (dbOptions testCoreOpts) {dbKey}, maintenance}}
|
||||
#endif
|
||||
|
||||
termSettings :: VirtualTerminalSettings
|
||||
@@ -307,7 +307,7 @@ insertUser st = withTransaction st (`DB.execute_` "INSERT INTO users (user_id) V
|
||||
#endif
|
||||
|
||||
startTestChat_ :: TestParams -> ChatDatabase -> ChatConfig -> ChatOpts -> User -> IO TestCC
|
||||
startTestChat_ TestParams {printOutput} db cfg opts@ChatOpts {maintenance} user = do
|
||||
startTestChat_ TestParams {printOutput} db cfg opts@ChatOpts {coreOptions = CoreChatOpts {maintenance}} user = do
|
||||
t <- withVirtualTerminal termSettings pure
|
||||
ct <- newChatTerminal t opts
|
||||
cc <- newChatController db (Just user) cfg opts False
|
||||
|
||||
@@ -1352,7 +1352,7 @@ testNegotiateCall =
|
||||
testMaintenanceMode :: HasCallStack => TestParams -> IO ()
|
||||
testMaintenanceMode ps = do
|
||||
withNewTestChat ps "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatOpts ps testOpts {maintenance = True} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatOpts ps testOpts {coreOptions = testCoreOpts {maintenance = True}} "alice" aliceProfile $ \alice -> do
|
||||
alice ##> "/c"
|
||||
alice <## "error: chat not started"
|
||||
alice ##> "/_start"
|
||||
@@ -1397,7 +1397,7 @@ testChatWorking alice bob = do
|
||||
testMaintenanceModeWithFiles :: HasCallStack => TestParams -> IO ()
|
||||
testMaintenanceModeWithFiles ps = withXFTPServer $ do
|
||||
withNewTestChat ps "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatOpts ps testOpts {maintenance = True} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatOpts ps testOpts {coreOptions = testCoreOpts {maintenance = True}} "alice" aliceProfile $ \alice -> do
|
||||
alice ##> "/_start"
|
||||
alice <## "chat started"
|
||||
alice ##> "/_files_folder ./tests/tmp/alice_files"
|
||||
@@ -1443,7 +1443,7 @@ testMaintenanceModeWithFiles ps = withXFTPServer $ do
|
||||
testDatabaseEncryption :: HasCallStack => TestParams -> IO ()
|
||||
testDatabaseEncryption ps = do
|
||||
withNewTestChat ps "bob" bobProfile $ \bob -> do
|
||||
withNewTestChatOpts ps testOpts {maintenance = True} "alice" aliceProfile $ \alice -> do
|
||||
withNewTestChatOpts ps testOpts {coreOptions = testCoreOpts {maintenance = True}} "alice" aliceProfile $ \alice -> do
|
||||
alice ##> "/_start"
|
||||
alice <## "chat started"
|
||||
connectUsers alice bob
|
||||
@@ -1655,7 +1655,7 @@ testSubscribeAppNSE :: HasCallStack => TestParams -> IO ()
|
||||
testSubscribeAppNSE ps =
|
||||
withNewTestChat ps "bob" bobProfile $ \bob -> do
|
||||
withNewTestChat ps "alice" aliceProfile $ \alice -> do
|
||||
withTestChatOpts ps testOpts {maintenance = True} "alice" $ \nseAlice -> do
|
||||
withTestChatOpts ps testOpts {coreOptions = testCoreOpts {maintenance = True}} "alice" $ \nseAlice -> do
|
||||
alice ##> "/_app suspend 1"
|
||||
alice <## "ok"
|
||||
alice <## "chat suspended"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"textColor": "white",
|
||||
"iconBg": "green",
|
||||
"enabled": true,
|
||||
"home": true,
|
||||
"rtl": true
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
{% endfor %}
|
||||
|
||||
<header id="navbar">
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo flex dark:hidden ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-light.png" alt="logo"></a>
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo hidden dark:flex ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-dark.png" alt="logo"></a>
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo logo-light ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-light.png" alt="logo"></a>
|
||||
<a href="/{{ '' if lang == 'en' else lang }}" class="logo logo-dark ltr:mr-auto rtl:ml-auto"><img src="/img/new/logo-dark.png" alt="logo"></a>
|
||||
|
||||
<nav id="menu">
|
||||
<ul>
|
||||
|
||||
@@ -15,9 +15,17 @@ body.change-nav-color {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
[dir="ltr"] .logo {
|
||||
border-bottom-right-radius: 12px;
|
||||
padding-right: 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
[dir="rtl"] .logo {
|
||||
border-bottom-left-radius: 12px;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.dark .logo {
|
||||
@@ -42,9 +50,17 @@ body.change-nav-color {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
[dir="ltr"] .right-links {
|
||||
border-bottom-left-radius: 12px;
|
||||
padding-left: 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
[dir="rtl"] .right-links {
|
||||
border-bottom-right-radius: 12px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.dark .right-links {
|
||||
@@ -69,14 +85,46 @@ header#navbar {
|
||||
|
||||
header#navbar>a.logo {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
padding-left: 30px;
|
||||
padding-top: 2px;
|
||||
height: 100%;
|
||||
/* display: flex; */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[dir="ltr"] header#navbar>a.logo {
|
||||
left: 0px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
[dir="rtl"] header#navbar>a.logo {
|
||||
right: 0px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
header#navbar>a.logo.logo-light {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
header#navbar>a.logo.logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark header#navbar>a.logo.logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark header#navbar>a.logo.logo-dark {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[dir="rtl"].dark .change-nav-color header#navbar>a.logo.logo-light {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[dir="rtl"].dark .change-nav-color header#navbar>a.logo.logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header#navbar>a.logo img {
|
||||
height: 40px;
|
||||
width: auto;
|
||||
@@ -183,7 +231,7 @@ header#navbar ul a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.change-nav-color .box-btn.token {
|
||||
.dark .change-nav-color .box-btn.token {
|
||||
background: linear-gradient(90deg, #019bfe 0%, #2e3fa0 100%);
|
||||
color: white;
|
||||
}
|
||||
@@ -219,9 +267,13 @@ header#navbar ul a span svg {
|
||||
fill: var(--nav-color);
|
||||
}
|
||||
|
||||
header#navbar nav#menu li.nav-link:hover span svg {
|
||||
[dir="ltr"] header#navbar nav#menu li.nav-link:hover span svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
[dir="rtl"] header#navbar nav#menu li.nav-link:hover span svg {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
|
||||
header#navbar ul.sub-menu {
|
||||
@@ -348,6 +400,11 @@ header#navbar .nav-link:focus-within .sub-menu {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
[dir="rtl"] .flag-container .sub-menu {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
header#navbar button.theme-switch-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -380,14 +437,22 @@ header#navbar button.theme-switch-btn svg path {
|
||||
.right-links {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0px;
|
||||
padding-right: 20px;
|
||||
height: 54px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
[dir="ltr"] .right-links {
|
||||
right: 0px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .right-links {
|
||||
left: 0px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
button#cross-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
@@ -454,9 +519,16 @@ button#cross-btn {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: all .3s ease;
|
||||
}
|
||||
|
||||
[dir="ltr"] header#navbar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
[dir="rtl"] header#navbar {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.dark header#navbar {
|
||||
background: #0a0f2b;
|
||||
}
|
||||
@@ -494,13 +566,20 @@ button#cross-btn {
|
||||
header#navbar nav#menu {
|
||||
position: fixed !important;
|
||||
top: 54px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
overflow: auto;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
[dir="ltr"] header#navbar nav#menu {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
[dir="rtl"] header#navbar nav#menu {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
header#navbar nav#menu .nav-link {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -433,7 +433,6 @@ section.cover div.content p {
|
||||
.publications-btns {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 30px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
@@ -441,6 +440,14 @@ section.cover div.content p {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
[dir="ltr"] .publications-btns {
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .publications-btns {
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.publications-btns img {
|
||||
width: 32px;
|
||||
}
|
||||
@@ -456,8 +463,6 @@ section.cover div.content p {
|
||||
.security-btns {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 0;
|
||||
transform: translateX(calc((100vw / 2) - 50%));
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
@@ -465,6 +470,16 @@ section.cover div.content p {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
[dir="ltr"] .security-btns {
|
||||
left: 0;
|
||||
transform: translateX(calc((100vw / 2) - 50%));
|
||||
}
|
||||
|
||||
[dir="rtl"] .security-btns {
|
||||
right: 0;
|
||||
transform: translateX(calc(-1 * ((100vw / 2) - 50%)));
|
||||
}
|
||||
|
||||
.security-btns img {
|
||||
height: 36px;
|
||||
}
|
||||
@@ -473,27 +488,38 @@ section.cover div.content p {
|
||||
font-size: 14px !important;
|
||||
font-family: 'Manrope', 'GT-Walsheim', sans-serif !important;
|
||||
font-weight: 300 !important;
|
||||
text-align: left;
|
||||
color: white;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
[dir="ltr"] .security-audits {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[dir="rtl"] .security-audits {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1279px) {
|
||||
.publications-btns {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.security-btns {
|
||||
[dir="ltr"] .security-btns {
|
||||
transform: translateX(0);
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .security-btns {
|
||||
transform: translateX(0);
|
||||
right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.socials {
|
||||
position: absolute;
|
||||
bottom: 22px;
|
||||
right: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -501,6 +527,14 @@ section.cover div.content p {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
[dir="ltr"] .socials {
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .socials {
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.socials a img {
|
||||
width: auto;
|
||||
height: 40px;
|
||||
@@ -783,6 +817,12 @@ main .section-bg {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
[dir="rtl"] .page-2 .text-container,
|
||||
[dir="rtl"] .page-5 .text-container,
|
||||
[dir="rtl"] .page-6 .text-container {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.page-4 .text-container h2 {
|
||||
max-width: calc(var(--sec-vwu)*26) !important;
|
||||
}
|
||||
@@ -929,6 +969,10 @@ main .section-bg {
|
||||
float: left;
|
||||
}
|
||||
|
||||
[dir="rtl"] .roadmap>p:first-child {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.roadmap p.title span {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
@@ -166,17 +166,17 @@ active_home: true
|
||||
<h2>{{ "index-roadmap-h2" | i18n({}, lang) }}</h2>
|
||||
<div class="roadmap">
|
||||
<p>{{ "index-roadmap-2025" | i18n({}, lang) }} </p>
|
||||
<p class="title"><span>: </span>{{ "index-roadmap-2025-title" | i18n({}, lang) }}</p>
|
||||
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2025-title" | i18n({}, lang) }}</p>
|
||||
<p>{{ "index-roadmap-2025-desc" | i18n({}, lang) }}</p>
|
||||
</div>
|
||||
<div class="roadmap">
|
||||
<p>{{ "index-roadmap-2026" | i18n({}, lang) }} </p>
|
||||
<p class="title"><span>: </span>{{ "index-roadmap-2026-title" | i18n({}, lang) }}</p>
|
||||
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2026-title" | i18n({}, lang) }}</p>
|
||||
<p>{{ "index-roadmap-2026-desc" | i18n({}, lang) }}</p>
|
||||
</div>
|
||||
<div class="roadmap">
|
||||
<p>{{ "index-roadmap-2027" | i18n({}, lang) }} </p>
|
||||
<p class="title"><span>: </span>{{ "index-roadmap-2027-title" | i18n({}, lang) }}</p>
|
||||
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2027-title" | i18n({}, lang) }}</p>
|
||||
<p>{{ "index-roadmap-2027-desc" | i18n({}, lang) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,19 +235,19 @@ active_home: true
|
||||
(function navColorOnScroll(){
|
||||
const header = document.querySelector('header#navbar');
|
||||
const cover = document.querySelector('.page-1.cover');
|
||||
if (!header || !cover || !('IntersectionObserver' in window)) {
|
||||
const footer = document.querySelector('.footer.page');
|
||||
if (!header || !cover || !footer || !('IntersectionObserver' in window)) {
|
||||
window.addEventListener('load', () => header && document.body.classList.toggle('change-nav-color', window.scrollY > 10));
|
||||
window.addEventListener('scroll', () => header && document.body.classList.toggle('change-nav-color', window.scrollY > 10));
|
||||
return;
|
||||
}
|
||||
const io = new IntersectionObserver((entries) => {
|
||||
for (const e of entries) {
|
||||
// if cover is mostly visible, keep white nav; else switch to #001796
|
||||
const onCover = e.isIntersecting && e.intersectionRatio >= 0.02;
|
||||
document.body.classList.toggle('change-nav-color', !onCover);
|
||||
}
|
||||
// if cover or footer is mostly visible, keep white nav; else switch to #001796
|
||||
const onObserved = entries.some((e) => e.isIntersecting && e.intersectionRatio >= 0.02);
|
||||
document.body.classList.toggle('change-nav-color', !onObserved);
|
||||
}, { threshold: [0, 0.02, 1] });
|
||||
io.observe(cover);
|
||||
io.observe(footer);
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user