Compare commits

...

37 Commits

Author SHA1 Message Date
085385a182 chore: update version string formatting in version.py for consistency 2025-12-01 12:22:18 -06:00
f8b0dd18c5 refactor: clean up code and improve variable naming
- Removed unnecessary blank lines in cx_setup.py.
- Reformatted conditional checks in meshchat.py for better readability.
- Updated callback variable names in NomadnetDownloader for clarity.
- Adjusted version string formatting in version.py for consistency.
- Reordered import statements in prepare_frontend_dir.py for better organization.
2025-12-01 12:18:01 -06:00
3231afb84d fix: improve websocket error logging in ReticulumMeshChat
- Updated error logging in websocket handling to use f-strings for better readability and consistency.
2025-12-01 12:00:00 -06:00
3848613a41 fix: update Python command in README for packaging instructions
- Changed `python` to `python3` in the packaging section to ensure compatibility with Python 3 environments.
2025-12-01 11:54:09 -06:00
284517bdfa chore: add security note to prevent path traversal in get_file_path function 2025-12-01 11:53:13 -06:00
5fc13dc61a Update 2025-12-01 11:50:25 -06:00
f989295773 refactor: update Tailwind CSS configuration for frontend structure
- Introduced a variable for the frontend root path to streamline content paths.
- Updated content paths to reflect the new directory structure for Tailwind CSS.
2025-12-01 11:50:11 -06:00
deepsource-autofix[bot]
d06ede8c5e Migrate to Poetry packaging and restructure codebase
Resolved issues in meshchatx/meshchat.py with DeepSource Autofix
2025-12-01 17:36:17 +00:00
a0047ea8fb refactor: update Docker integration and build process
- Modified docker-compose.yml to allow overriding the image with the MESHCHAT_IMAGE environment variable.
- Enhanced Makefile with new build-docker and run-docker targets for streamlined Docker image creation and execution.
- Updated README.md to reflect changes in Docker build and run commands, providing clearer instructions for users.
2025-12-01 11:30:07 -06:00
c98131f76b refactor: frontend preparation script with error handling
- Added a check to ensure the script is run from the project root by verifying the existence of pyproject.toml.
- Implemented a safeguard against removing the TARGET directory if it is a symlink, raising an appropriate error message.
2025-12-01 11:06:10 -06:00
9b4b8fdfeb grammar corrections 2025-12-01 11:05:55 -06:00
48a0d8697e Remove sync_version.py from poetry include in pyproject.toml 2025-12-01 11:05:38 -06:00
5627ae1640 refactor: test_wheel.sh to dynamically find wheel files
- Updated script to search for wheel files using a pattern.
- Added error handling for cases with no matches or multiple matches.
- Improved output to indicate the found wheel file.
2025-12-01 11:05:32 -06:00
94d91c4934 Update dependencies in pyproject.toml and regenerate poetry.lock 2025-12-01 11:03:28 -06:00
ac839df357 Fix https://github.com/Sudo-Ivan/reticulum-meshchatX/pull/21#discussion_r2575731068 2025-12-01 11:02:45 -06:00
cfad1ddc5f Update 2025-12-01 00:07:30 -06:00
398ab570df update 2025-11-30 23:54:15 -06:00
50bc2cbfc8 Enhance GitHub Actions workflow with conditional execution
- Added conditional checks to ensure build steps only run when necessary.
- Streamlined the installation and build processes for NodeJS and Python based on the new build input logic.
- Improved clarity and efficiency in the workflow by consolidating conditions for Linux-specific tasks.
2025-11-30 23:39:43 -06:00
fe3a01c3c6 Refactor GitHub Actions workflow for conditional builds
- Removed redundant conditional checks for build jobs.
- Introduced a new step to determine if the build should run based on event type and input parameters.
- Updated job definitions to utilize the new build input logic for Windows, macOS, and Linux builds.
2025-11-30 23:39:29 -06:00
0b0a39ea86 Refactor Docker setup and frontend structure
- Updated Dockerfile to copy frontend files to meshchatx/public directory.
- Modified .dockerignore to include meshchatx/public.
- Added a new script to prepare the frontend directory.
- Adjusted Vite configuration to output to the new public directory.
- Updated GitHub Actions workflow to reflect changes in build process and artifact handling.
2025-11-30 23:34:45 -06:00
2e001006c9 Update .gitignore to include 'public/' directory 2025-11-30 23:25:59 -06:00
0beaaaf4b1 Add cx_setup.py for building the ReticulumMeshChatX application
- Introduced a new setup script using cx_Freeze to facilitate building the application.
- Updated version.py to maintain consistency in version string formatting.
- Modified build-backend.js to use poetry for executing the build process.
2025-11-30 23:25:54 -06:00
84f887df90 codebase restructure and organization. 2025-11-30 23:16:57 -06:00
80cf812e54 update 2025-11-30 22:49:46 -06:00
19854e59da Refactor: Adjust formatting and structure in database.py and meshchat.py
- Improved readability by restructuring function arguments and parameters across multiple files.
- Enhanced consistency in the formatting of method signatures and exception handling.
- Minor adjustments to comments for clarity and alignment with code style.
2025-11-30 22:38:07 -06:00
ba47e16b75 Rename package from reticulum-meshchat to reticulum-meshchatx in package-lock.json 2025-11-30 21:30:32 -06:00
578e80023f remove 2025-11-30 21:29:04 -06:00
b7dcee4c06 Update 2025-11-30 21:28:59 -06:00
e44ec59b6e Rename reticulum-meshchat service to reticulum-meshchatx and update image reference in docker-compose.yml 2025-11-30 21:28:46 -06:00
45379e6df1 update version and name 2025-11-30 21:28:39 -06:00
308f1f6459 update 2025-11-30 21:28:31 -06:00
424ff116d1 Merge pull request #20 from Sudo-Ivan/deepsource-autofix-29fa619a
refactor: change methods not using its bound instance to staticmethods
2025-11-30 21:23:46 -06:00
deepsource-autofix[bot]
73f677d319 refactor: change methods not using its bound instance to staticmethods
The method doesn't use its bound instance. Decorate this method with `@staticmethod` decorator, so that Python does not have to instantiate a bound method for every instance of this class thereby saving memory and computation. Read more about staticmethods [here](https://docs.python.org/3/library/functions.html#staticmethod).
2025-12-01 03:22:30 +00:00
4770c21499 update to add manual trigger 2025-11-30 21:18:49 -06:00
720bef90c7 remove old workflow 2025-11-30 21:18:42 -06:00
1c98a231fd Refactor ReticulumMeshChat methods to static
- Updated several instance methods in ReticulumMeshChat to static methods for improved clarity and usability.
- Adjusted method calls to reflect the new static context, enhancing code organization.
2025-11-30 21:17:09 -06:00
f6a1be5e80 Replace backend build script in package.json with a Node.js script for improved compatibility and maintainability. Added new build-backend.js script to handle the backend build process using Python. 2025-11-30 21:16:49 -06:00
130 changed files with 2584 additions and 17689 deletions

View File

@@ -3,24 +3,36 @@ README.md
LICENSE
donate.md
screenshots/
docs/
# Development files
.github/
electron/
scripts/
Makefile
# Build artifacts and cache
build/
dist/
public/
meshchatx/public/
node_modules/
__pycache__/
*.pyc
*.pyo
*.pyd
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
*.egg
python-dist/
# Virtual environments
env/
venv/
ENV/
env.bak/
venv.bak/
.venv/
# IDE and editor files
.vscode/
@@ -47,9 +59,19 @@ Dockerfile*
docker-compose*.yml
.dockerignore
# Local storage and runtime data
storage/
testing/
telemetry_test_lxmf/
# Logs
*.log
# Temporary files
*.tmp
*.temp
*.temp
# Environment variables
.env
.env.local
.env.*.local

View File

@@ -4,94 +4,37 @@ on:
push:
tags:
- "*"
workflow_dispatch:
inputs:
build_windows:
description: 'Build Windows'
required: false
default: 'true'
type: boolean
build_mac:
description: 'Build macOS'
required: false
default: 'true'
type: boolean
build_linux:
description: 'Build Linux'
required: false
default: 'true'
type: boolean
build_docker:
description: 'Build Docker'
required: false
default: 'true'
type: boolean
permissions:
contents: read
jobs:
build_windows:
runs-on: windows-latest
permissions:
contents: write
steps:
- name: Clone Repo
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
- name: Install NodeJS
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1
with:
node-version: 22
- name: Install Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install Python Deps
run: |
python -m venv venv
venv\Scripts\pip install --upgrade pip
venv\Scripts\pip install -r requirements.txt
- name: Install NodeJS Deps
run: npm install
- name: Build Electron App
run: npm run dist
- name: Create Release
id: create_release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
draft: true
allowUpdates: true
replacesArtifacts: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
artifacts: "dist/*-win-installer.exe,dist/*-win-portable.exe"
build_mac:
runs-on: macos-13
permissions:
contents: write
steps:
- name: Clone Repo
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
- name: Install NodeJS
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1
with:
node-version: 18
- name: Install Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: Install Python Deps
run: |
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt
- name: Install NodeJS Deps
run: npm install
- name: Build Electron App
run: npm run dist
- name: Create Release
id: create_release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
draft: true
allowUpdates: true
replacesArtifacts: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
artifacts: "dist/*-mac.dmg"
build_linux:
build_frontend:
runs-on: ubuntu-latest
permissions:
contents: write
contents: read
steps:
- name: Clone Repo
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
@@ -106,34 +49,205 @@ jobs:
with:
python-version: "3.12"
- name: Install patchelf
run: sudo apt-get update && sudo apt-get install -y patchelf
- name: Install Python Deps
run: |
python3 -m venv venv
venv/bin/pip install --upgrade pip
venv/bin/pip install -r requirements.txt
- name: Sync versions
run: python scripts/sync_version.py
- name: Install NodeJS Deps
run: npm install
- name: Build Frontend
run: npm run build-frontend
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-build
path: meshchatx/public
if-no-files-found: error
build_desktop:
name: Build Desktop (${{ matrix.name }})
needs: build_frontend
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: windows
os: windows-latest
node: 22
python: "3.12"
release_artifacts: "dist/*-win-installer.exe,dist/*-win-portable.exe"
build_input: build_windows
- name: mac
os: macos-13
node: 18
python: "3.11"
release_artifacts: "dist/*-mac.dmg"
build_input: build_mac
- name: linux
os: ubuntu-latest
node: 22
python: "3.12"
release_artifacts: "dist/*-linux.AppImage,dist/*-linux.deb,python-dist/*.whl"
build_input: build_linux
permissions:
contents: write
steps:
- name: Clone Repo
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1
- name: Install NodeJS
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1
with:
node-version: ${{ matrix.node }}
- name: Install Python
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python }}
- name: Install Poetry
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python -m pip install --upgrade pip poetry
- name: Sync versions
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python scripts/sync_version.py
- name: Install Python Deps
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python -m poetry install
- name: Install NodeJS Deps
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: npm install
- name: Prepare frontend directory
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: python scripts/prepare_frontend_dir.py
- name: Download frontend artifact
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/download-artifact@v4
with:
name: frontend-build
path: meshchatx/public
- name: Install patchelf
if: |
matrix.name == 'linux' &&
(github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true))
run: sudo apt-get update && sudo apt-get install -y patchelf
- name: Build Python wheel
if: |
matrix.name == 'linux' &&
(github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true))
run: |
python -m poetry build -f wheel
mkdir -p python-dist
mv dist/*.whl python-dist/
rm -rf dist
- name: Build Electron App
run: npm run dist
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
run: npm run dist-prebuilt
- name: Upload build artifacts
if: |
github.event_name == 'push' ||
(github.event_name == 'workflow_dispatch' && inputs[matrix.build_input] == true)
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.name }}
path: |
dist/*-win-installer.exe
dist/*-win-portable.exe
dist/*-mac.dmg
dist/*-linux.AppImage
dist/*-linux.deb
python-dist/*.whl
if-no-files-found: ignore
create_release:
name: Create Release
needs: build_desktop
runs-on: ubuntu-latest
if: github.event_name == 'push'
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Display structure of downloaded files
run: ls -R artifacts
- name: Prepare release assets
run: |
mkdir -p release-assets
find artifacts -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.whl" \) -exec cp {} release-assets/ \;
ls -lh release-assets/
- name: Generate SHA256 checksums
run: |
cd release-assets
echo "## SHA256 Checksums" > release-body.md
echo "" >> release-body.md
for file in *.exe *.dmg *.AppImage *.deb *.whl; do
if [ -f "$file" ]; then
sha256sum "$file" | tee "${file}.sha256"
echo "\`$(cat "${file}.sha256")\`" >> release-body.md
fi
done
echo "" >> release-body.md
echo "Individual \`.sha256\` files are included for each artifact." >> release-body.md
cat release-body.md
echo ""
echo "Generated .sha256 files:"
ls -1 *.sha256 2>/dev/null || echo "No .sha256 files found"
- name: Create Release
id: create_release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
draft: true
allowUpdates: true
replacesArtifacts: true
omitDraftDuringUpdate: true
omitNameDuringUpdate: true
artifacts: "dist/*-linux.AppImage,dist/*-linux.deb"
artifacts: "release-assets/*"
bodyFile: "release-assets/release-body.md"
build_docker:
runs-on: ubuntu-latest
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.build_docker == 'true')
permissions:
packages: write
contents: read
@@ -164,9 +278,9 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
tags: >-
ghcr.io/${{ env.REPO_OWNER_LC }}/reticulum-meshchat:latest,
ghcr.io/${{ env.REPO_OWNER_LC }}/reticulum-meshchat:${{ github.ref_name }}
ghcr.io/${{ env.REPO_OWNER_LC }}/reticulum-meshchatx:latest,
ghcr.io/${{ env.REPO_OWNER_LC }}/reticulum-meshchatx:${{ github.ref_name }}
labels: >-
org.opencontainers.image.title=Reticulum MeshChat,
org.opencontainers.image.description=Docker image for Reticulum MeshChat,
org.opencontainers.image.url=https://github.com/${{ github.repository }}/pkgs/container/reticulum-meshchat/
org.opencontainers.image.title=Reticulum MeshChatX,
org.opencontainers.image.description=Docker image for Reticulum MeshChatX,
org.opencontainers.image.url=https://github.com/${{ github.repository }}/pkgs/container/reticulum-meshchatx/

View File

@@ -1,45 +0,0 @@
name: Temporary manual trigger for Docker build
on:
workflow_dispatch:
jobs:
build_docker:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Clone Repo
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
- name: Set lowercase repository owner
run: echo "REPO_OWNER_LC=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: Log in to the GitHub Container registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker images
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: >-
ghcr.io/${{ env.REPO_OWNER_LC }}/reticulum-meshchat:latest,
ghcr.io/${{ env.REPO_OWNER_LC }}/reticulum-meshchat:${{ github.ref_name }}
labels: >-
org.opencontainers.image.title=Reticulum MeshChat,
org.opencontainers.image.description=Docker image for Reticulum MeshChat,
org.opencontainers.image.url=https://github.com/${{ github.repository }}/pkgs/container/reticulum-meshchat/

54
.gitignore vendored
View File

@@ -1,13 +1,57 @@
# IDE and editor files
.idea
node_modules
.vscode/
*.swp
*.swo
*~
# build files
# Dependencies
node_modules/
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
dist/
*.egg
# Virtual environments
venv/
env/
ENV/
env.bak/
venv.bak/
.venv/
# Build files
/build/
/dist/
/public/
/meshchatx/public/
public/
/electron/build/exe/
python-dist/
# local storage
# Local storage and runtime data
storage/
testing/
telemetry_test_lxmf/
*.pyc
# Logs
*.log
# OS files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Environment variables
.env
.env.local
.env.*.local

View File

@@ -10,9 +10,8 @@ FROM node:${NODE_VERSION}-alpine@${NODE_ALPINE_SHA256} AS build-frontend
WORKDIR /src
# Copy required source files
COPY *.json .
COPY *.js .
COPY src/frontend ./src/frontend
COPY package*.json vite.config.js ./
COPY meshchatx ./meshchatx
# Install NodeJS deps, exluding electron
RUN npm install --omit=dev && \
@@ -34,12 +33,10 @@ RUN apk add --no-cache --virtual .build-deps \
apk del .build-deps
# Copy prebuilt frontend
COPY --from=build-frontend /src/public public
COPY --from=build-frontend /src/meshchatx/public meshchatx/public
# Copy other required source files
COPY *.py .
COPY src/__init__.py ./src/__init__.py
COPY src/backend ./src/backend
COPY *.json .
COPY meshchatx ./meshchatx
COPY pyproject.toml poetry.lock ./
CMD ["python", "meshchat.py", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.meshchat", "--headless"]
CMD ["python", "-m", "meshchatx.meshchat", "--host=0.0.0.0", "--reticulum-config-dir=/config/.reticulum", "--storage-dir=/config/.meshchat", "--headless"]

View File

@@ -1,26 +1,39 @@
.PHONY: install run clean build build-appimage build-exe dist
.PHONY: install run develop clean build build-appimage build-exe dist sync-version wheel node_modules python build-docker run-docker
VENV = venv
PYTHON = $(VENV)/bin/python
PIP = $(VENV)/bin/pip
PYTHON ?= python
POETRY = $(PYTHON) -m poetry
NPM = npm
install: $(VENV) node_modules
DOCKER_COMPOSE_CMD ?= docker compose
DOCKER_COMPOSE_FILE ?= docker-compose.yml
DOCKER_IMAGE ?= reticulum-meshchatx:local
DOCKER_BUILDER ?= meshchatx-builder
DOCKER_PLATFORMS ?= linux/amd64
DOCKER_BUILD_FLAGS ?= --load
DOCKER_BUILD_ARGS ?=
DOCKER_CONTEXT ?= .
DOCKERFILE ?= Dockerfile
$(VENV):
python3 -m venv $(VENV)
$(PIP) install --upgrade pip
$(PIP) install -r requirements.txt
install: sync-version node_modules python
node_modules:
$(NPM) install
python:
$(POETRY) install
run: install
$(PYTHON) meshchat.py
$(POETRY) run meshchat
develop: run
build: install
$(NPM) run build
wheel: install
$(POETRY) build -f wheel
$(PYTHON) scripts/move_wheels.py
build-appimage: build
$(NPM) run electron-postinstall
$(NPM) run dist -- --linux AppImage
@@ -32,10 +45,28 @@ build-exe: build
dist: build-appimage
clean:
rm -rf $(VENV)
rm -rf node_modules
rm -rf build
rm -rf dist
rm -rf python-dist
rm -rf meshchatx/public
sync-version:
$(PYTHON) scripts/sync_version.py
build-docker:
@if ! docker buildx inspect $(DOCKER_BUILDER) >/dev/null 2>&1; then \
docker buildx create --name $(DOCKER_BUILDER) --use >/dev/null; \
else \
docker buildx use $(DOCKER_BUILDER); \
fi
docker buildx build --builder $(DOCKER_BUILDER) --platform $(DOCKER_PLATFORMS) \
$(DOCKER_BUILD_FLAGS) \
-t $(DOCKER_IMAGE) \
$(DOCKER_BUILD_ARGS) \
-f $(DOCKERFILE) \
$(DOCKER_CONTEXT)
run-docker:
MESHCHAT_IMAGE="$(DOCKER_IMAGE)" \
$(DOCKER_COMPOSE_CMD) -f $(DOCKER_COMPOSE_FILE) up --remove-orphans --pull never reticulum-meshchatx

View File

@@ -8,15 +8,25 @@ A heavily customized fork of [Reticulum MeshChat](https://github.com/liamcottle/
- [x] Ability to set inbound and propagation node stamps.
- [x] Better config parsing.
- [x] Cancel page fetching or file downloads
- [x] Block recieving messages from users.
- [x] Block receiving messages from users.
- [ ] Spam filter (based on keywords)
- [ ] Multi-identity support.
- [ ] Multi-language support
- [ ] Offline Reticulum documentation tool
- [ ] More tools (translate, LoRa calculator, LXMFy bots, etc.)
- [x] Codebase reorganization and cleanup.
- [ ] Tests and proper CI/CD pipeline.
- [ ] RNS hot reload
- [ ] Backup/Import identities, messages and interfaces.
- [ ] Full LXST support.
- [x] Poetry for packaging and dependency management.
- [x] More stats on about page.
- [x] Actions are pinned to full-length SHA hashes.
- [x] Docker images are smaller and use SHA256 hashes for the images.
- [x] Electron improvements.
- [x] Electron improvements (ASAR and security).
- [x] Latest updates for NPM and Python dependencies (bleeding edge)
- [x] Ruff linting, CodeQL Advanced and Bearer SAST fixes.
- [x] Numerous Ruff, Deepsource, CodeQL Advanced and Bearer Linting/SAST fixes.
- [x] Some performance improvements.
## Usage
@@ -25,21 +35,76 @@ Check [releases](https://github.com/Sudo-Ivan/reticulum-meshchatX/releases) for
## Building
```bash
make install
make install # installs Python deps via Poetry and Node deps via npm
make build
```
You can run `make run` or `make develop` (a thin alias) to start the backend + frontend loop locally through `poetry run meshchat`.
### Python packaging
The Python build is driven entirely by Poetry now. Run `python3 scripts/sync_version.py` or `make sync-version` before packaging so `pyproject.toml` and `src/version.py` match `package.json`. After that:
```bash
python -m poetry install
make wheel # produces a wheel in python-dist/ that bundles the public assets
```
The wheel includes the frontend `public/` assets, `logo/`, and the CLI entry point, and `python-dist/` keeps the artifact separate from the Electron `dist/` output.
### Building in Docker
```bash
make docker-build
make build-docker
```
The build will be in the `dist` directory.
`build-docker` creates `reticulum-meshchatx:local` (or `$(DOCKER_IMAGE)` if you override it) via `docker buildx`. Set `DOCKER_PLATFORMS` to `linux/amd64,linux/arm64` when you need multi-arch images, and adjust `DOCKER_BUILD_FLAGS`/`DOCKER_BUILD_ARGS` to control `--load`/`--push`.
## Development
### Running with Docker Compose
```bash
make develop
make run-docker
```
`run-docker` feeds the locally-built image into `docker compose -f docker-compose.yml up --remove-orphans --pull never reticulum-meshchatx`. The compose file uses the `MESHCHAT_IMAGE` env var so you can override the target image without editing the YAML (the default still points at `ghcr.io/sudo-ivan/reticulum-meshchatx:latest`). Use `docker compose down` or `Ctrl+C` to stop the container.
The Electron build artifacts will still live under `dist/` for releases.
## Python packaging
The backend uses Poetry with `pyproject.toml` for dependency management and packaging. Before building, run `python3 scripts/sync_version.py` (or `make sync-version`) to ensure the generated `src/version.py` reflects the version from `package.json` that the Electron artifacts use. This keeps the CLI release metadata, wheel packages, and other bundles aligned.
### Build artifact locations
Both `poetry build` and `python -m build` generate wheels inside the default `dist/` directory. The `make wheel` shortcut wraps `poetry build -f wheel` and then runs `python scripts/move_wheels.py` to relocate the generated `.whl` files into `python-dist/` (the layout expected by `scripts/test_wheel.sh` and the release automation). Use `make wheel` if you need the artifacts in `python-dist/`; `poetry build` or `python -m build` alone will leave them in `dist/`.
### Building with Poetry
```bash
# Install dependencies
poetry install
# Build the package (wheels land in dist/)
poetry build
# Install locally for testing (consumes dist/)
pip install dist/*.whl
```
### Building with pip (alternative)
If you prefer pip, you can build/install directly:
```bash
# Build the wheel
pip install build
python -m build
# Install locally
pip install .
```
### cx_Freeze (for AppImage/NSIS)
The `cx_setup.py` script uses cx_Freeze for creating standalone executables (AppImage for Linux, NSIS for Windows). This is separate from the Poetry/pip packaging workflow.

31
TODO.md
View File

@@ -1,31 +0,0 @@
1. for messages fix:
convo goes off edge, near edge should be ... 3 dots
long names push the last message/announced seconds/time to right and nearly off the side, fix please
2. interfaces:
3 dots background circle is a oval, fix to be circle
on 3 dots clicked there is still white background the buttons have dark backgrounds though but main dropdown window is white fix depdning on theme
also on 3 dots drop down it still makes me scroll down in that interfaces window, we can expand that interfaces box os something so this crap doesnt hapen or if dropdown is above it
rework propagation nodes page with new UI/UX please like rest of app.
1. the attachment dropups/popups are white on dark mode, they need a ui/ux rework.
2. for settings add ability to set inbound stamp, ref lxmf via python -c if needed.
3. add multi-identity / account suport and a switcher at bottom with ability to create, delete or import/export identies from other apps.
for all this you will likely need to look at my ren chat app for stamps, multi-identity, /mnt/projects/ren-messenger/
its pretty simple.
translator tool
reticulum documentation tool
lxmfy bot tool
page downloader tool
page snapshots

47
cx_setup.py Normal file
View File

@@ -0,0 +1,47 @@
from pathlib import Path
from cx_Freeze import Executable, setup
from meshchatx.src.version import __version__
ROOT = Path(__file__).resolve().parent
PUBLIC_DIR = ROOT / "meshchatx" / "public"
include_files = [
(str(PUBLIC_DIR), "public"),
("logo", "logo"),
]
setup(
name="ReticulumMeshChatX",
version=__version__,
description="A simple mesh network communications app powered by the Reticulum Network Stack",
executables=[
Executable(
script="meshchatx/meshchat.py",
base=None,
target_name="ReticulumMeshChatX",
shortcut_name="ReticulumMeshChatX",
shortcut_dir="ProgramMenuFolder",
icon="logo/icon.ico",
),
],
options={
"build_exe": {
"packages": [
"RNS",
"RNS.Interfaces",
"LXMF",
],
"include_files": include_files,
"excludes": [
"PIL",
],
"optimize": 2,
"build_exe": "build/exe",
"replace_paths": [
("*", ""),
],
},
},
)

View File

@@ -1,7 +1,7 @@
services:
reticulum-meshchat:
container_name: reticulum-meshchat
image: ghcr.io/sudo-ivan/reticulum-meshchat:latest
reticulum-meshchatx:
container_name: reticulum-meshchatx
image: ${MESHCHAT_IMAGE:-ghcr.io/sudo-ivan/reticulum-meshchatx:latest}
pull_policy: always
restart: unless-stopped
# Make the meshchat web interface accessible from the host on port 8000

View File

@@ -133,6 +133,14 @@ app.whenReady().then(async () => {
webPreferences: {
// used to inject logging over ipc
preload: path.join(__dirname, 'preload.js'),
// Security: disable node integration in renderer
nodeIntegration: false,
// Security: enable context isolation (default in Electron 12+)
contextIsolation: true,
// Security: enable sandbox for additional protection
sandbox: true,
// Security: disable remote module (deprecated but explicit)
enableRemoteModule: false,
},
});

3
meshchatx/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""Reticulum MeshChatX - A mesh network communications app."""
__version__ = "2.41.0"

View File

@@ -1,4 +1,4 @@
from datetime import datetime, timezone
from datetime import UTC, datetime
from peewee import * # noqa: F403
from playhouse.migrate import SqliteMigrator
@@ -17,7 +17,9 @@ def migrate(current_version):
if current_version < 2:
migrate_database(
migrator.add_column(
"lxmf_messages", "delivery_attempts", LxmfMessage.delivery_attempts,
"lxmf_messages",
"delivery_attempts",
LxmfMessage.delivery_attempts,
),
migrator.add_column(
"lxmf_messages",
@@ -66,8 +68,8 @@ class Config(BaseModel):
id = BigAutoField() # noqa: F405
key = CharField(unique=True) # noqa: F405
value = TextField() # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -85,7 +87,7 @@ class Announce(BaseModel):
identity_hash = CharField( # noqa: F405
index=True,
) # identity hash that announced the destination
identity_public_key = ( # noqa: F405
identity_public_key = (
CharField() # noqa: F405
) # base64 encoded public key, incase we want to recreate the identity manually
app_data = TextField(null=True) # noqa: F405 # base64 encoded app data bytes
@@ -93,8 +95,8 @@ class Announce(BaseModel):
snr = FloatField(null=True) # noqa: F405
quality = FloatField(null=True) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -106,8 +108,8 @@ class CustomDestinationDisplayName(BaseModel):
destination_hash = CharField(unique=True) # noqa: F405 # unique destination hash
display_name = CharField() # noqa: F405 # custom display name for the destination hash
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -120,8 +122,8 @@ class FavouriteDestination(BaseModel):
display_name = CharField() # noqa: F405 # custom display name for the destination hash
aspect = CharField() # noqa: F405 # e.g: nomadnetwork.node
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -133,7 +135,7 @@ class LxmfMessage(BaseModel):
hash = CharField(unique=True) # noqa: F405 # unique lxmf message hash
source_hash = CharField(index=True) # noqa: F405
destination_hash = CharField(index=True) # noqa: F405
state = ( # noqa: F405
state = (
CharField() # noqa: F405
) # state is converted from internal int to a human friendly string
progress = FloatField() # noqa: F405 # progress is converted from internal float 0.00-1.00 to float between 0.00/100 (2 decimal places)
@@ -150,15 +152,15 @@ class LxmfMessage(BaseModel):
title = TextField() # noqa: F405
content = TextField() # noqa: F405
fields = TextField() # noqa: F405 # json string
timestamp = ( # noqa: F405
timestamp = (
FloatField() # noqa: F405
) # timestamp of when the message was originally created (before ever being sent)
rssi = IntegerField(null=True) # noqa: F405
snr = FloatField(null=True) # noqa: F405
quality = FloatField(null=True) # noqa: F405
is_spam = BooleanField(default=False) # noqa: F405 # if true, message is marked as spam
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -170,8 +172,8 @@ class LxmfConversationReadState(BaseModel):
destination_hash = CharField(unique=True) # noqa: F405 # unique destination hash
last_read_at = DateTimeField() # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -183,12 +185,12 @@ class LxmfUserIcon(BaseModel):
destination_hash = CharField(unique=True) # noqa: F405 # unique destination hash
icon_name = CharField() # noqa: F405 # material design icon name for the destination hash
foreground_colour = CharField() # noqa: F405 # hex colour to use for foreground (icon colour)
background_colour = ( # noqa: F405
background_colour = (
CharField() # noqa: F405
) # hex colour to use for background (background colour)
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -198,10 +200,11 @@ class LxmfUserIcon(BaseModel):
class BlockedDestination(BaseModel):
id = BigAutoField() # noqa: F405
destination_hash = CharField( # noqa: F405
unique=True, index=True,
unique=True,
index=True,
) # unique destination hash that is blocked
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:
@@ -211,10 +214,11 @@ class BlockedDestination(BaseModel):
class SpamKeyword(BaseModel):
id = BigAutoField() # noqa: F405
keyword = CharField( # noqa: F405
unique=True, index=True,
unique=True,
index=True,
) # keyword to match against message content
created_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(timezone.utc)) # noqa: F405
created_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
updated_at = DateTimeField(default=lambda: datetime.now(UTC)) # noqa: F405
# define table name
class Meta:

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
"""Backend utilities shared by the Reticulum MeshChatX CLI."""

View File

@@ -7,7 +7,11 @@ class AnnounceHandler:
# we will just pass the received announce back to the provided callback
def received_announce(
self, destination_hash, announced_identity, app_data, announce_packet_hash,
self,
destination_hash,
announced_identity,
app_data,
announce_packet_hash,
):
try:
# handle received announce
@@ -18,6 +22,6 @@ class AnnounceHandler:
app_data,
announce_packet_hash,
)
except Exception: # noqa: E722
except Exception:
# ignore failure to handle received announce
pass

View File

@@ -146,7 +146,9 @@ class AudioCallManager:
# attempts to initiate a call to the provided destination and returns the link hash on success
async def initiate(
self, destination_hash: bytes, timeout_seconds: int = 15,
self,
destination_hash: bytes,
timeout_seconds: int = 15,
) -> AudioCall:
# determine when to timeout
timeout_after_seconds = time.time() + timeout_seconds
@@ -240,7 +242,7 @@ class AudioCallReceiver:
)
link.teardown()
return
except Exception: # noqa: E722
except Exception:
# if we can't get identity yet, we'll check later
pass

View File

@@ -71,7 +71,8 @@ class WebsocketClientInterface(Interface):
self.websocket.send(data)
except Exception as e:
RNS.log(
f"Exception occurred while transmitting via {self!s}", RNS.LOG_ERROR,
f"Exception occurred while transmitting via {self!s}",
RNS.LOG_ERROR,
)
RNS.log(f"The contained exception was: {e!s}", RNS.LOG_ERROR)
return
@@ -93,7 +94,9 @@ class WebsocketClientInterface(Interface):
try:
RNS.log(f"Connecting to Websocket for {self!s}...", RNS.LOG_DEBUG)
self.websocket = connect(
f"{self.target_url}", max_size=None, compression=None,
f"{self.target_url}",
max_size=None,
compression=None,
)
RNS.log(f"Connected to Websocket for {self!s}", RNS.LOG_DEBUG)
self.read_loop()

View File

@@ -3,9 +3,8 @@ import time
import RNS
from RNS.Interfaces.Interface import Interface
from websockets.sync.server import Server, ServerConnection, serve
from src.backend.interfaces.WebsocketClientInterface import WebsocketClientInterface
from websockets.sync.server import Server, ServerConnection, serve
class WebsocketServerInterface(Interface):

View File

@@ -0,0 +1 @@
"""Shared transport interfaces for MeshChatX."""

View File

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More