Compare commits
85
Commits
v0.9.1
..
20251011-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
530264f747 | ||
|
|
ae1d4dd264 | ||
|
|
4789dd4847 | ||
|
|
f91d3bbfdd | ||
|
|
5b50532d15 | ||
|
|
938480ccec | ||
|
|
f422e8e917 | ||
|
|
1632e3dacf | ||
|
|
cc9a68b9c5 | ||
|
|
77a23239ea | ||
|
|
d03e6be2eb | ||
|
|
0a4d77ffd3 | ||
|
|
c6f121f585 | ||
|
|
96ddeb79d2 | ||
|
|
a36b138978 | ||
|
|
860c47fbad | ||
|
|
a770d5822a | ||
|
|
10c9682bba | ||
|
|
eb48975428 | ||
|
|
28ed85a779 | ||
|
|
95ca735b7b | ||
|
|
901ac56154 | ||
|
|
3dbf3bb7a0 | ||
|
|
45df5724b3 | ||
|
|
ea1c165d80 | ||
|
|
48e0b48ea9 | ||
|
|
61038ddcf1 | ||
|
|
b96f07e615 | ||
|
|
d830fb0911 | ||
|
|
cc9ff9a632 | ||
|
|
02f18705f4 | ||
|
|
a841df3b36 | ||
|
|
2a36f5912d | ||
|
|
a110a087be | ||
|
|
21b9164ee7 | ||
|
|
2f3d1f0c7e | ||
|
|
62204fe50b | ||
|
|
773b890012 | ||
|
|
9c3b2fb364 | ||
|
|
5fe19d9815 | ||
|
|
c1c5b0fd29 | ||
|
|
6737100b4b | ||
|
|
6f0e80eef9 | ||
|
|
bfc6f50ee5 | ||
|
|
7abbad700c | ||
|
|
a671ba6308 | ||
|
|
1d385ef2bc | ||
|
|
6558e9f7c4 | ||
|
|
92f4b44cb3 | ||
|
|
370442bf4e | ||
|
|
d54c5e80fa | ||
|
|
a42746c55b | ||
|
|
9a98c53aff | ||
|
|
4ea08a189d | ||
|
|
ae15a0b4c7 | ||
|
|
9e5fbec9b0 | ||
|
|
0b02c84f04 | ||
|
|
8bfe3ec6b0 | ||
|
|
a7345a7b90 | ||
|
|
12854d0f1b | ||
|
|
9a2a7901e7 | ||
|
|
e8cb0bb88a | ||
|
|
343b90b9af | ||
|
|
a4246f39e4 | ||
|
|
38380db6e7 | ||
|
|
091aa4705b | ||
|
|
8b5e085232 | ||
|
|
9c2cc8736c | ||
|
|
951128032b | ||
|
|
367ac887f4 | ||
|
|
060b4c43f4 | ||
|
|
2593a4272b | ||
|
|
742eb3bb7d | ||
|
|
41a6de67cd | ||
|
|
c8ae34ba73 | ||
|
|
c55b5bf75f | ||
|
|
9de87a01ab | ||
|
|
2f7294705e | ||
|
|
120ca449b8 | ||
|
|
ffbc899792 | ||
|
|
675c7d6c0d | ||
|
|
67569f932e | ||
|
|
7a4421089c | ||
|
|
ae87bc0b67 | ||
|
|
ad0dcad497 |
@@ -0,0 +1,37 @@
|
|||||||
|
name: Build documentation
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write # To push a branch
|
||||||
|
pages: write # To push to a GitHub Pages site
|
||||||
|
id-token: write # To update the deployment status
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install latest mdbook
|
||||||
|
run: |
|
||||||
|
tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name')
|
||||||
|
url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz"
|
||||||
|
mkdir mdbook
|
||||||
|
curl -sSL $url | tar -xz --directory=./mdbook
|
||||||
|
echo `pwd`/mdbook >> $GITHUB_PATH
|
||||||
|
- name: Build Book
|
||||||
|
run: |
|
||||||
|
cd doc
|
||||||
|
mdbook build
|
||||||
|
- name: Setup Pages
|
||||||
|
uses: actions/configure-pages@v4
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: "doc/book"
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
@@ -5,22 +5,19 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- "*"
|
- "*"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
paths:
|
paths:
|
||||||
- "Dockerfile"
|
- "Dockerfile"
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.head_ref || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
base:
|
base:
|
||||||
name: Test base image build
|
name: Test base image build
|
||||||
runs-on: arc-runner-set
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
# Configure build environment
|
# Configure build environment
|
||||||
@@ -41,7 +38,7 @@ jobs:
|
|||||||
cache-to: type=gha,scope=buildx-base-multi-arch,mode=max
|
cache-to: type=gha,scope=buildx-base-multi-arch,mode=max
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
runs-on: arc-runner-set
|
runs-on: self-hosted
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
name: Publish Docker images
|
name: Publish Docker images
|
||||||
steps:
|
steps:
|
||||||
@@ -52,6 +49,13 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
# Authenticate with Docker Hub and GHCR
|
# Authenticate with Docker Hub and GHCR
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: docker.io
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Login to Github Container Registry
|
- name: Login to Github Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -68,13 +72,14 @@ jobs:
|
|||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
tags: ghcr.io/${{ github.repository_owner }}/base:latest
|
tags: ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
|
|
||||||
# stoatchat/api
|
# revoltchat/server
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-delta
|
id: meta-delta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/api
|
docker.io/revoltchat/server
|
||||||
|
ghcr.io/revoltchat/server
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -87,13 +92,14 @@ jobs:
|
|||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-delta.outputs.labels }}
|
labels: ${{ steps.meta-delta.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/events
|
# revoltchat/bonfire
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-bonfire
|
id: meta-bonfire
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/events
|
docker.io/revoltchat/bonfire
|
||||||
|
ghcr.io/revoltchat/bonfire
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -106,13 +112,14 @@ jobs:
|
|||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-bonfire.outputs.labels }}
|
labels: ${{ steps.meta-bonfire.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/file-server
|
# revoltchat/autumn
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-autumn
|
id: meta-autumn
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/file-server
|
docker.io/revoltchat/autumn
|
||||||
|
ghcr.io/revoltchat/autumn
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -125,13 +132,14 @@ jobs:
|
|||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-autumn.outputs.labels }}
|
labels: ${{ steps.meta-autumn.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/proxy
|
# revoltchat/january
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-january
|
id: meta-january
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/proxy
|
docker.io/revoltchat/january
|
||||||
|
ghcr.io/revoltchat/january
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -144,13 +152,14 @@ jobs:
|
|||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-january.outputs.labels }}
|
labels: ${{ steps.meta-january.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/gifbox
|
# revoltchat/gifbox
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-gifbox
|
id: meta-gifbox
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/gifbox
|
docker.io/revoltchat/gifbox
|
||||||
|
ghcr.io/revoltchat/gifbox
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -163,13 +172,14 @@ jobs:
|
|||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-gifbox.outputs.labels }}
|
labels: ${{ steps.meta-gifbox.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/crond
|
# revoltchat/crond
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-crond
|
id: meta-crond
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/crond
|
docker.io/revoltchat/crond
|
||||||
|
ghcr.io/revoltchat/crond
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -182,13 +192,14 @@ jobs:
|
|||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-crond.outputs.labels }}
|
labels: ${{ steps.meta-crond.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/pushd
|
# revoltchat/pushd
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta-pushd
|
id: meta-pushd
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stoatchat/pushd
|
docker.io/revoltchat/pushd
|
||||||
|
ghcr.io/revoltchat/pushd
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
@@ -200,22 +211,3 @@ jobs:
|
|||||||
build-args: |
|
build-args: |
|
||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
||||||
labels: ${{ steps.meta-pushd.outputs.labels }}
|
labels: ${{ steps.meta-pushd.outputs.labels }}
|
||||||
|
|
||||||
# stoatchat/voice-ingress
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta-voice-ingress
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
ghcr.io/stoatchat/voice-ingress
|
|
||||||
- name: Publish
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
file: crates/daemons/voice-ingress/Dockerfile
|
|
||||||
tags: ${{ steps.meta-voice-ingress.outputs.tags }}
|
|
||||||
build-args: |
|
|
||||||
BASE_IMAGE=ghcr.io/${{ github.repository_owner }}/base:latest
|
|
||||||
labels: ${{ steps.meta-voice-ingress.outputs.labels }}
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
name: Documentation (test)
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test-deploy:
|
|
||||||
name: Test deployment
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./docs
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
package_json_file: ./docs/package.json
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: lts/*
|
|
||||||
cache: pnpm
|
|
||||||
cache-dependency-path: ./docs/pnpm-lock.yaml
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
- name: Test build website
|
|
||||||
run: pnpm run build
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
name: Documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build Docusaurus
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./docs
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
package_json_file: ./docs/package.json
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: lts/*
|
|
||||||
cache: pnpm
|
|
||||||
cache-dependency-path: ./docs/pnpm-lock.yaml
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install
|
|
||||||
- name: Build website
|
|
||||||
run: pnpm run build
|
|
||||||
|
|
||||||
- name: Upload Build Artifact
|
|
||||||
uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: ./docs/build
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
name: Deploy to GitHub Pages
|
|
||||||
needs: build
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pages: write # to deploy to Pages
|
|
||||||
id-token: write # to verify the deployment originates from an appropriate source
|
|
||||||
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Deploy to GitHub Pages
|
|
||||||
id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
name: Release Please
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main] # updates/opens the release PR when commits land on main
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: release-please
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release-please:
|
|
||||||
name: Release Please
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
release_created: ${{ steps.rp.outputs.release_created }}
|
|
||||||
tag_name: ${{ steps.rp.outputs.tag_name }}
|
|
||||||
steps:
|
|
||||||
- id: app-token
|
|
||||||
uses: actions/create-github-app-token@v2
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.GH_STOAT_RELEASE_APP_ID }}
|
|
||||||
private-key: ${{ secrets.GH_STOAT_RELEASE_APP_PRIVATE_KEY }}
|
|
||||||
- id: rp
|
|
||||||
uses: googleapis/release-please-action@v4
|
|
||||||
with:
|
|
||||||
token: ${{ steps.app-token.outputs.token }}
|
|
||||||
config-file: release-please-config.json
|
|
||||||
|
|
||||||
publish-please:
|
|
||||||
name: Publish Please
|
|
||||||
needs: release-please
|
|
||||||
if: needs.release-please.outputs.release_created == 'true'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- id: app-token
|
|
||||||
uses: actions/create-github-app-token@v2
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.GH_STOAT_RELEASE_APP_ID }}
|
|
||||||
private-key: ${{ secrets.GH_STOAT_RELEASE_APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Send release notification webhook
|
|
||||||
run: |
|
|
||||||
RELEASE_URL="https://github.com/revoltchat/backend/releases/tag/${{ needs.release-please.outputs.tag_name }}"
|
|
||||||
curl -X POST "${{ secrets.STOAT_WEBHOOK_UPDATES_URL }}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"content\": \"$RELEASE_URL\"}"
|
|
||||||
|
|
||||||
- name: Publish crates
|
|
||||||
uses: katyo/publish-crates@v2
|
|
||||||
with:
|
|
||||||
registry-token: ${{ secrets.CRATES_IO_PUBLISH_TOKEN }}
|
|
||||||
+26
-25
@@ -2,12 +2,16 @@ name: Rust build, test, and generate specification
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
paths-ignore:
|
||||||
|
- ".github/**"
|
||||||
|
- "!.github/workflows/rust.yaml"
|
||||||
|
- ".vscode/**"
|
||||||
|
- "doc/**"
|
||||||
|
- ".gitignore"
|
||||||
|
- "LICENSE"
|
||||||
|
- "README"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
concurrency:
|
|
||||||
group: ${{ github.head_ref || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -15,28 +19,25 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
name: Rust project
|
name: Rust project
|
||||||
runs-on: arc-runner-set
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
# Using our own runners for now:
|
|
||||||
# - name: Free up disk space
|
|
||||||
# run: |
|
|
||||||
# sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
|
|
||||||
|
|
||||||
- name: Install latest stable
|
- name: Install latest stable
|
||||||
uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- name: Install cargo-nextest
|
- name: Install cargo-nextest
|
||||||
uses: taiki-e/install-action@a58ae9526b2c3acee9ad8e1926b950b7863305d4 # 2.65.16
|
uses: baptiste0928/cargo-install@v1
|
||||||
with:
|
with:
|
||||||
tool: cargo-nextest@0.9.119
|
crate: cargo-nextest
|
||||||
|
locked: true
|
||||||
|
|
||||||
- name: Run cargo build
|
- name: Run cargo build
|
||||||
run: cargo build
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
|
||||||
- name: Run services in background
|
- name: Run services in background
|
||||||
run: |
|
run: |
|
||||||
@@ -64,17 +65,17 @@ jobs:
|
|||||||
|
|
||||||
- name: Wait for API to go up
|
- name: Wait for API to go up
|
||||||
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||||
uses: nev7n/wait_for_response@7fef3c1a6e8939d0b09062f14fec50d3c5d15fa1 # v1.0.1
|
uses: nev7n/wait_for_response@v1
|
||||||
with:
|
with:
|
||||||
url: "http://localhost:14702/"
|
url: "http://localhost:14702/"
|
||||||
|
|
||||||
- name: Checkout API repository
|
- name: Checkout API repository
|
||||||
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: stoatchat/javascript-client-api
|
repository: revoltchat/api
|
||||||
path: api
|
path: api
|
||||||
ssh-key: ${{ secrets.DEPLOY_KEY_JAVASCRIPT_CLIENT_API }}
|
token: ${{ secrets.PAT }}
|
||||||
|
|
||||||
- name: Download OpenAPI specification
|
- name: Download OpenAPI specification
|
||||||
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||||
@@ -82,10 +83,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
uses: EndBug/add-and-commit@v4
|
||||||
with:
|
with:
|
||||||
cwd: "api"
|
cwd: "api"
|
||||||
add: "*.json"
|
add: "*.json"
|
||||||
author_name: Stoat CI
|
author_name: Revolt CI
|
||||||
author_email: stoat-ci@users.noreply.github.com
|
author_email: revolt-ci@users.noreply.github.com
|
||||||
message: "chore: generate OpenAPI specification"
|
message: "chore: generate OpenAPI specification"
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
name: Add Issue to Board
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
track_issue:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get project data
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||||
|
run: |
|
||||||
|
gh api graphql -f query='
|
||||||
|
query {
|
||||||
|
organization(login: "revoltchat"){
|
||||||
|
projectV2(number: 3) {
|
||||||
|
id
|
||||||
|
fields(first:20) {
|
||||||
|
nodes {
|
||||||
|
... on ProjectV2SingleSelectField {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
options {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' > project_data.json
|
||||||
|
|
||||||
|
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
|
||||||
|
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||||
|
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Add issue to project
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||||
|
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||||
|
run: |
|
||||||
|
item_id="$( gh api graphql -f query='
|
||||||
|
mutation($project:ID!, $issue:ID!) {
|
||||||
|
addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
|
||||||
|
item {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectV2ItemById.item.id')"
|
||||||
|
|
||||||
|
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
name: Add PR to Board
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, synchronize, ready_for_review, review_requested]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
track_pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get project data
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||||
|
run: |
|
||||||
|
gh api graphql -f query='
|
||||||
|
query {
|
||||||
|
organization(login: "revoltchat"){
|
||||||
|
projectV2(number: 5) {
|
||||||
|
id
|
||||||
|
fields(first:20) {
|
||||||
|
nodes {
|
||||||
|
... on ProjectV2SingleSelectField {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
options {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' > project_data.json
|
||||||
|
|
||||||
|
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
|
||||||
|
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
|
||||||
|
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="🆕 Untriaged") |.id' project_data.json) >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Add PR to project
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||||
|
PR_ID: ${{ github.event.pull_request.node_id }}
|
||||||
|
run: |
|
||||||
|
item_id="$( gh api graphql -f query='
|
||||||
|
mutation($project:ID!, $pr:ID!) {
|
||||||
|
addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
|
||||||
|
item {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectV2ItemById.item.id')"
|
||||||
|
|
||||||
|
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set fields
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||||
|
run: |
|
||||||
|
gh api graphql -f query='
|
||||||
|
mutation (
|
||||||
|
$project: ID!
|
||||||
|
$item: ID!
|
||||||
|
$status_field: ID!
|
||||||
|
$status_value: String!
|
||||||
|
) {
|
||||||
|
set_status: updateProjectV2ItemFieldValue(input: {
|
||||||
|
projectId: $project
|
||||||
|
itemId: $item
|
||||||
|
fieldId: $status_field
|
||||||
|
value: {
|
||||||
|
singleSelectOptionId: $status_value
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
projectV2Item {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
name: "Lint PR"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
- reopened
|
|
||||||
- edited
|
|
||||||
- synchronize
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
main:
|
|
||||||
name: Validate PR title
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
pull-requests: read
|
|
||||||
steps:
|
|
||||||
- uses: amannn/action-semantic-pull-request@v6
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
+2
-1
@@ -12,4 +12,5 @@ venv/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
livekit.yml
|
livekit.yml
|
||||||
.idea
|
.idea
|
||||||
|
start
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
".": "0.9.1"
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## [0.9.1](https://github.com/stoatchat/stoatchat/compare/v0.9.0...v0.9.1) (2026-01-10)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **ci:** pipeline fixes (marked as fix to force release) ([#483](https://github.com/stoatchat/stoatchat/issues/483)) ([303e52b](https://github.com/stoatchat/stoatchat/commit/303e52b476585eea81c33837f1b01506ce387684))
|
|
||||||
|
|
||||||
## [0.9.0](https://github.com/stoatchat/stoatchat/compare/v0.8.8...v0.9.0) (2026-01-10)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* add id field to role ([#470](https://github.com/stoatchat/stoatchat/issues/470)) ([2afea56](https://github.com/stoatchat/stoatchat/commit/2afea56e56017f02de98e67316b4457568ad5b26))
|
|
||||||
* add ratelimits to gifbox ([1542047](https://github.com/stoatchat/stoatchat/commit/154204742d21cbeff6e2577b00f50b495ea44631))
|
|
||||||
* include groups and dms in fetch mutuals ([caa8607](https://github.com/stoatchat/stoatchat/commit/caa86074680d46223cebc20f41e9c91c41ec825d))
|
|
||||||
* include member payload in ServerMemberJoin event ([480f210](https://github.com/stoatchat/stoatchat/commit/480f210ce85271e13d1dac58a5dae08de108579d))
|
|
||||||
* initial work on tenor gif searching ([b0c977b](https://github.com/stoatchat/stoatchat/commit/b0c977b324b8144c1152589546eb8fec5954c3e7))
|
|
||||||
* make message lexer use unowned string ([1561481](https://github.com/stoatchat/stoatchat/commit/1561481eb4cdc0f385fbf0a81e4950408050e11f))
|
|
||||||
* ready payload field customisation ([db57706](https://github.com/stoatchat/stoatchat/commit/db577067948f13e830b5fb773034e9713a1abaff))
|
|
||||||
* require auth for search ([b5cd5e3](https://github.com/stoatchat/stoatchat/commit/b5cd5e30ef7d5e56e8964fb7c543965fa6bf5a4a))
|
|
||||||
* trending and categories routes ([5885e06](https://github.com/stoatchat/stoatchat/commit/5885e067a627b8fff1c8ce2bf9e852ff8cf3f07a))
|
|
||||||
* voice chats v2 ([#414](https://github.com/stoatchat/stoatchat/issues/414)) ([d567155](https://github.com/stoatchat/stoatchat/commit/d567155f124e4da74115b1a8f810062f7c6559d9))
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* add license to revolt-parser ([5335124](https://github.com/stoatchat/stoatchat/commit/53351243064cac8d499dd74284be73928fa78a43))
|
|
||||||
* allow for disabling default features ([65fbd36](https://github.com/stoatchat/stoatchat/commit/65fbd3662462aed1333b79e59155fa6377e83fcc))
|
|
||||||
* apple music to use original url instead of metadata url ([bfe4018](https://github.com/stoatchat/stoatchat/commit/bfe4018e436a4075bae780dd4d35a9b58315e12f))
|
|
||||||
* apply uname fix to january and autumn ([8f9015a](https://github.com/stoatchat/stoatchat/commit/8f9015a6ff181d208d9269ab8691bd417d39811a))
|
|
||||||
* **ci:** publish images under stoatchat and remove docker hub ([d65c1a1](https://github.com/stoatchat/stoatchat/commit/d65c1a1ab3bdc7e5684b03f280af77d881661a3d))
|
|
||||||
* correct miniz_oxide in lockfile ([#478](https://github.com/stoatchat/stoatchat/issues/478)) ([5d27a91](https://github.com/stoatchat/stoatchat/commit/5d27a91e901dd2ea3e860aeaed8468db6c5f3214))
|
|
||||||
* correct shebang for try-tag-and-release ([050ba16](https://github.com/stoatchat/stoatchat/commit/050ba16d4adad5d0fb247867aa3e94e3d42bd12d))
|
|
||||||
* correct string_cache in lockfile ([#479](https://github.com/stoatchat/stoatchat/issues/479)) ([0b178fc](https://github.com/stoatchat/stoatchat/commit/0b178fc791583064bf9ca94b1d39b42d021e1d79))
|
|
||||||
* don't remove timeouts when a member leaves a server ([#409](https://github.com/stoatchat/stoatchat/issues/409)) ([e635bc2](https://github.com/stoatchat/stoatchat/commit/e635bc23ec857d648d5705e1a3875d7bc3402b0d))
|
|
||||||
* don't update the same field while trying to remove it ([f4ee35f](https://github.com/stoatchat/stoatchat/commit/f4ee35fb093ca49f0a64ff4b17fd61587df28145)), closes [#392](https://github.com/stoatchat/stoatchat/issues/392)
|
|
||||||
* github webhook incorrect payload and formatting ([#468](https://github.com/stoatchat/stoatchat/issues/468)) ([dc9c82a](https://github.com/stoatchat/stoatchat/commit/dc9c82aa4e9667ea6639256c65ac8de37a24d1f7))
|
|
||||||
* implement Serialize to ClientMessage ([dea0f67](https://github.com/stoatchat/stoatchat/commit/dea0f675dde7a63c7a59b38d469f878b7a8a3af4))
|
|
||||||
* newly created roles should be ranked the lowest ([947eb15](https://github.com/stoatchat/stoatchat/commit/947eb15771ed6785b3dcd16c354c03ded5e4cbe0))
|
|
||||||
* permit empty `remove` array in edit requests ([6ad3da5](https://github.com/stoatchat/stoatchat/commit/6ad3da5f35f989a2e7d8e29718b98374248e76af))
|
|
||||||
* preserve order of replies in message ([#447](https://github.com/stoatchat/stoatchat/issues/447)) ([657a3f0](https://github.com/stoatchat/stoatchat/commit/657a3f08e5d652814bbf0647e089ed9ebb139bbf))
|
|
||||||
* prevent timing out members which have TimeoutMembers permission ([e36fc97](https://github.com/stoatchat/stoatchat/commit/e36fc9738bac0de4f3fcbccba521f1e3754f7ae7))
|
|
||||||
* relax settings name regex ([3a34159](https://github.com/stoatchat/stoatchat/commit/3a3415915f0d0fdce1499d47a2b7fa097f5946ea))
|
|
||||||
* remove authentication tag bytes from attachment download ([32e6600](https://github.com/stoatchat/stoatchat/commit/32e6600272b885c595c094f0bc69459250220dcb))
|
|
||||||
* rename openapi operation ids ([6048587](https://github.com/stoatchat/stoatchat/commit/6048587d348fbca0dc3a9b47690c56df8fece576)), closes [#406](https://github.com/stoatchat/stoatchat/issues/406)
|
|
||||||
* respond with 201 if no body in requests ([#465](https://github.com/stoatchat/stoatchat/issues/465)) ([24fedf8](https://github.com/stoatchat/stoatchat/commit/24fedf8c4d9cd3160bdec97aa451520f8beaa739))
|
|
||||||
* swap to using reqwest for query building ([38dd4d1](https://github.com/stoatchat/stoatchat/commit/38dd4d10797b3e6e397fc219e818f379bdff19f2))
|
|
||||||
* use `trust_cloudflare` config value instead of env var ([cc7a796](https://github.com/stoatchat/stoatchat/commit/cc7a7962a882e1627fcd0bc75858a017415e8cfc))
|
|
||||||
* use our own result types instead of tenors types ([a92152d](https://github.com/stoatchat/stoatchat/commit/a92152d86da136997817e797c7af8e38731cdde8))
|
|
||||||
Generated
+17
-18
@@ -6519,7 +6519,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-autumn"
|
name = "revolt-autumn"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-macros",
|
"axum-macros",
|
||||||
@@ -6557,7 +6557,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel 2.5.0",
|
"async-channel 2.5.0",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -6588,7 +6588,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-coalesced"
|
name = "revolt-coalesced"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.11.4",
|
"indexmap 2.11.4",
|
||||||
"lru 0.7.8",
|
"lru 0.7.8",
|
||||||
@@ -6597,7 +6597,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"cached",
|
"cached",
|
||||||
@@ -6614,7 +6614,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-crond"
|
name = "revolt-crond"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"revolt-config",
|
"revolt-config",
|
||||||
@@ -6626,7 +6626,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"amqprs",
|
"amqprs",
|
||||||
"async-lock 2.8.0",
|
"async-lock 2.8.0",
|
||||||
@@ -6676,7 +6676,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"amqprs",
|
"amqprs",
|
||||||
"async-channel 1.9.0",
|
"async-channel 1.9.0",
|
||||||
@@ -6726,7 +6726,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-files"
|
name = "revolt-files"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"aws-config",
|
"aws-config",
|
||||||
@@ -6749,7 +6749,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-gifbox"
|
name = "revolt-gifbox"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
@@ -6771,7 +6771,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-january"
|
name = "revolt-january"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -6799,7 +6799,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"iso8601-timestamp",
|
"iso8601-timestamp",
|
||||||
@@ -6818,14 +6818,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-parser"
|
name = "revolt-parser"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"logos",
|
"logos",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -6840,7 +6840,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"log",
|
"log",
|
||||||
@@ -6852,7 +6852,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-pushd"
|
name = "revolt-pushd"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"amqprs",
|
"amqprs",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -6880,7 +6880,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-ratelimits"
|
name = "revolt-ratelimits"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"authifier",
|
"authifier",
|
||||||
@@ -6897,7 +6897,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.8.8"
|
version = "0.8.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"log",
|
"log",
|
||||||
@@ -6905,7 +6905,6 @@ dependencies = [
|
|||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
"rocket",
|
"rocket",
|
||||||
"schemars 0.8.22",
|
"schemars 0.8.22",
|
||||||
"sentry",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"utoipa",
|
"utoipa",
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ The services and libraries that power the Revolt service.<br/>
|
|||||||
|
|
||||||
Rust 1.86.0 or higher.
|
Rust 1.86.0 or higher.
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> The events server has a significant performance regression between Rust 1.77.2 and 1.78.0 onwards, see [issue #341](https://github.com/revoltchat/backend/issues/341). This is currently solved by build time options but we are looking for a proper fix.
|
||||||
|
|
||||||
## Development Guide
|
## Development Guide
|
||||||
|
|
||||||
Before contributing, make yourself familiar with [our contribution guidelines](https://developers.revolt.chat/contrib.html) and the [technical documentation for this project](https://revoltchat.github.io/backend/).
|
Before contributing, make yourself familiar with [our contribution guidelines](https://developers.revolt.chat/contrib.html) and the [technical documentation for this project](https://revoltchat.github.io/backend/).
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ use_starttls = false
|
|||||||
url = "http://livekit"
|
url = "http://livekit"
|
||||||
lat = 0.0
|
lat = 0.0
|
||||||
lon = 0.0
|
lon = 0.0
|
||||||
key = "worldwide"
|
key = "worldwide_key"
|
||||||
secret = "ZjCofRlfm6GGtjlifmNpCDkcQbEIIVC0"
|
secret = "ZjCofRlfm6GGtjlifmNpCDkcQbEIIVC0"
|
||||||
|
|
||||||
[files.s3]
|
[files.s3]
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Code Style Guide
|
||||||
|
|
||||||
|
Beyond using Cargo format and Clippy, there are some specific code style guidelines laid out in this document for different parts of the project.
|
||||||
|
|
||||||
|
## Writing Style
|
||||||
|
|
||||||
|
- Shorten "identifier" to "Id" with that exact casing, i.e. Server Id.
|
||||||
|
|
||||||
|
## `core/database` crate
|
||||||
|
|
||||||
|
w.r.t. `model.rs` files
|
||||||
|
|
||||||
|
- All struct definitions must be commented.
|
||||||
|
```rust
|
||||||
|
/// Server
|
||||||
|
pub struct Server {
|
||||||
|
/// Name of the server
|
||||||
|
pub name: String,
|
||||||
|
```
|
||||||
|
- Struct definitions should not include derives unless necessary (if additional traits such as Hash are required) and instead use `auto_derived!` and `auto_derived_partial!`.
|
||||||
|
```rust
|
||||||
|
auto_derived_partial!(
|
||||||
|
/// Server
|
||||||
|
pub struct Server { .. },
|
||||||
|
"PartialServer"
|
||||||
|
);
|
||||||
|
```
|
||||||
|
- `auto_derived!` macro accepts multiple entries and should be used as such:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
auto_derived!(
|
||||||
|
/// Optional fields on server object
|
||||||
|
pub enum FieldsServer { .. }
|
||||||
|
|
||||||
|
/// Optional fields on server object
|
||||||
|
pub enum FieldsRole { .. }
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
- If special serialisation conditions are required, such as checking if a boolean is false, use the existing definitions for these functions from the crate root:
|
||||||
|
```rust
|
||||||
|
#[serde(skip_serializing_if = "crate::if_false", default)]
|
||||||
|
```
|
||||||
|
- `impl` blocks may be defined below the struct definitions and should be ordered in the same order of definition. Methods in the block must follow the same guidelines as traits where-in: methods are ordered in terms of CRUD, there are empty line breaks, and methods are commented.
|
||||||
|
|
||||||
|
w.r.t. `ops` module for models
|
||||||
|
|
||||||
|
- All traits must use a the name format `AbstractPlural` where Plural is the plural form of the collection. e.g. Servers
|
||||||
|
- Traits defined must follow these guidelines:
|
||||||
|
|
||||||
|
- Methods are ordered in terms of CRUD, create-read-update-delete ordering.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractServerMembers: Sync + Send {
|
||||||
|
/// Insert a new server member into the database
|
||||||
|
async fn insert_member(&self, member: &Member) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch a server member by their id
|
||||||
|
async fn fetch_member(&self, server_id: &str, user_id: &str) -> Result<Member>;
|
||||||
|
|
||||||
|
/// Update information for a server member
|
||||||
|
async fn update_member(&self, .. ) -> Result<()>;
|
||||||
|
|
||||||
|
/// Delete a server member by their id
|
||||||
|
async fn delete_member(&self, id: &MemberCompositeKey) -> Result<()>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- There should be an empty line break between each method declaration.
|
||||||
|
- All methods must have an appropriate comment.
|
||||||
|
|
||||||
|
- When implementing the trait defined in `ops.rs` with each driver, the method declaration style should be the same for ease of searching: same ordering, same comments, same line breaks.
|
||||||
@@ -64,10 +64,3 @@ services:
|
|||||||
MAILDEV_WEB_PORT: 8080
|
MAILDEV_WEB_PORT: 8080
|
||||||
MAILDEV_INCOMING_USER: smtp
|
MAILDEV_INCOMING_USER: smtp
|
||||||
MAILDEV_INCOMING_PASS: smtp
|
MAILDEV_INCOMING_PASS: smtp
|
||||||
|
|
||||||
livekit:
|
|
||||||
image: ghcr.io/stoatchat/livekit-server:v1.9.9
|
|
||||||
command: --config /etc/livekit.yml
|
|
||||||
network_mode: "host"
|
|
||||||
volumes:
|
|
||||||
- ./livekit.yml:/etc/livekit.yml
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ authifier = { version = "1.0.15" }
|
|||||||
revolt-result = { path = "../core/result" }
|
revolt-result = { path = "../core/result" }
|
||||||
revolt-models = { path = "../core/models" }
|
revolt-models = { path = "../core/models" }
|
||||||
revolt-config = { path = "../core/config" }
|
revolt-config = { path = "../core/config" }
|
||||||
revolt-database = { path = "../core/database", features = ["voice"] }
|
revolt-database = { path = "../core/database" }
|
||||||
revolt-permissions = { path = "../core/permissions" }
|
revolt-permissions = { version = "0.8.9", path = "../core/permissions" }
|
||||||
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
revolt-presence = { path = "../core/presence", features = ["redis-is-patched"] }
|
||||||
|
|
||||||
# redis
|
# redis
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
@@ -124,7 +124,12 @@ impl State {
|
|||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Fetch all memberships with their corresponding servers.
|
// Fetch all memberships with their corresponding servers.
|
||||||
let mut members: Vec<Member> = db.fetch_all_memberships(&user.id).await?;
|
let members: Vec<Member> = db.fetch_all_memberships(&user.id).await?;
|
||||||
|
self.cache.members = members
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| (x.id.server.clone(), x))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let server_ids: Vec<String> = members.iter().map(|x| x.id.server.clone()).collect();
|
let server_ids: Vec<String> = members.iter().map(|x| x.id.server.clone()).collect();
|
||||||
let servers = db.fetch_servers(&server_ids).await?;
|
let servers = db.fetch_servers(&server_ids).await?;
|
||||||
@@ -153,51 +158,6 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let voice_states = if fields.voice_states {
|
|
||||||
let mut voice_state_server_members: HashMap<String, HashSet<String>> = HashMap::new();
|
|
||||||
|
|
||||||
// fetch voice states for all the channels we can see
|
|
||||||
let mut voice_states = Vec::new();
|
|
||||||
|
|
||||||
for channel in channels.iter().filter(|c| {
|
|
||||||
matches!(
|
|
||||||
c,
|
|
||||||
Channel::DirectMessage { .. }
|
|
||||||
| Channel::Group { .. }
|
|
||||||
| Channel::TextChannel { voice: Some(_), .. }
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await {
|
|
||||||
if let Some(server) = channel.server() {
|
|
||||||
let set = voice_state_server_members.entry(server.to_string()).or_default();
|
|
||||||
|
|
||||||
for participant in &voice_state.participants {
|
|
||||||
user_ids.insert(participant.id.clone());
|
|
||||||
set.insert(participant.id.clone());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for participant in &voice_state.participants {
|
|
||||||
user_ids.insert(participant.id.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
voice_states.push(voice_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch all the members for for the participants who are in a server
|
|
||||||
for (server, user_ids) in voice_state_server_members {
|
|
||||||
let user_ids = user_ids.into_iter().collect::<Vec<_>>();
|
|
||||||
let voice_members = db.fetch_members(&server, &user_ids).await?;
|
|
||||||
|
|
||||||
members.extend(voice_members);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(voice_states)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch presence data for known users.
|
// Fetch presence data for known users.
|
||||||
let online_ids = filter_online(&user_ids.iter().cloned().collect::<Vec<String>>()).await;
|
let online_ids = filter_online(&user_ids.iter().cloned().collect::<Vec<String>>()).await;
|
||||||
|
|
||||||
@@ -211,12 +171,6 @@ impl State {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.cache.members = members
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|x| (x.id.server.clone(), x))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Fetch customisations.
|
// Fetch customisations.
|
||||||
let emojis = if fields.emojis {
|
let emojis = if fields.emojis {
|
||||||
Some(
|
Some(
|
||||||
@@ -258,6 +212,28 @@ impl State {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let voice_states = if fields.voice_states {
|
||||||
|
// fetch voice states for all the channels we can see
|
||||||
|
let mut voice_states = Vec::new();
|
||||||
|
|
||||||
|
for channel in channels.iter().filter(|c| {
|
||||||
|
matches!(
|
||||||
|
c,
|
||||||
|
Channel::DirectMessage { .. }
|
||||||
|
| Channel::Group { .. }
|
||||||
|
| Channel::TextChannel { voice: Some(_), .. }
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await {
|
||||||
|
voice_states.push(voice_state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(voice_states)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Copy data into local state cache.
|
// Copy data into local state cache.
|
||||||
self.cache.users = users.iter().cloned().map(|x| (x.id.clone(), x)).collect();
|
self.cache.users = users.iter().cloned().map(|x| (x.id.clone(), x)).collect();
|
||||||
self.cache
|
self.cache
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::env;
|
use std::{env, sync::Arc};
|
||||||
|
|
||||||
use async_std::net::TcpListener;
|
use async_std::net::TcpListener;
|
||||||
use revolt_presence::clear_region;
|
use revolt_presence::clear_region;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use revolt_database::voice::VoiceClient;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
@@ -12,6 +14,15 @@ pub mod events;
|
|||||||
mod database;
|
mod database;
|
||||||
mod websocket;
|
mod websocket;
|
||||||
|
|
||||||
|
pub static VOICE_CLIENT: OnceCell<Arc<VoiceClient>> = OnceCell::new();
|
||||||
|
|
||||||
|
pub fn get_voice_client() -> Arc<VoiceClient> {
|
||||||
|
VOICE_CLIENT
|
||||||
|
.get()
|
||||||
|
.expect("get_voice_client called before set")
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
// Configure requirements for Bonfire.
|
// Configure requirements for Bonfire.
|
||||||
@@ -24,6 +35,8 @@ async fn main() {
|
|||||||
clear_region(None).await;
|
clear_region(None).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOICE_CLIENT.set(Arc::new(VoiceClient::from_revolt_config().await)).unwrap();
|
||||||
|
|
||||||
// Setup a TCP listener to accept WebSocket connections on.
|
// Setup a TCP listener to accept WebSocket connections on.
|
||||||
// By default, we bind to port 14703 on all interfaces.
|
// By default, we bind to port 14703 on all interfaces.
|
||||||
let bind = env::var("HOST").unwrap_or_else(|_| "0.0.0.0:14703".into());
|
let bind = env::var("HOST").unwrap_or_else(|_| "0.0.0.0:14703".into());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-coalesced"
|
name = "revolt-coalesced"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>", "Zomatree <me@zomatree.live>"]
|
authors = ["Paul Makles <me@insrt.uk>", "Zomatree <me@zomatree.live>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-config"
|
name = "revolt-config"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -37,4 +37,4 @@ sentry = { version = "0.31.5", optional = true }
|
|||||||
sentry-anyhow = { version = "0.38.1", optional = true }
|
sentry-anyhow = { version = "0.38.1", optional = true }
|
||||||
|
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { version = "0.9.1", path = "../result", optional = true }
|
revolt-result = { version = "0.8.9", path = "../result", optional = true }
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ message_queue = "notifications.origin.message"
|
|||||||
mass_mention_queue = "notifications.origin.mass_mention" # handles messages that contain role or everyone mentions
|
mass_mention_queue = "notifications.origin.mass_mention" # handles messages that contain role or everyone mentions
|
||||||
fr_accepted_queue = "notifications.ingest.fr_accepted" # friend request accepted
|
fr_accepted_queue = "notifications.ingest.fr_accepted" # friend request accepted
|
||||||
fr_received_queue = "notifications.ingest.fr_received" # friend request received
|
fr_received_queue = "notifications.ingest.fr_received" # friend request received
|
||||||
dm_call_queue = "notifications.ingest.dm_call" # direct message voice call
|
dm_call_queue = "notifications.ingest.dm_call" # friend request received
|
||||||
generic_queue = "notifications.ingest.generic" # generic messages (title + body)
|
generic_queue = "notifications.ingest.generic" # generic messages (title + body)
|
||||||
ack_queue = "notifications.process.ack" # updates badges for apple devices
|
ack_queue = "notifications.process.ack" # updates badges for apple devices
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-database"
|
name = "revolt-database"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -18,26 +18,25 @@ async-std-runtime = ["async-std", "authifier/async-std-runtime"]
|
|||||||
rocket-impl = ["rocket", "schemars", "revolt_okapi", "revolt_rocket_okapi", "authifier/rocket_impl"]
|
rocket-impl = ["rocket", "schemars", "revolt_okapi", "revolt_rocket_okapi", "authifier/rocket_impl"]
|
||||||
axum-impl = ["axum"]
|
axum-impl = ["axum"]
|
||||||
redis-is-patched = ["revolt-presence/redis-is-patched"]
|
redis-is-patched = ["revolt-presence/redis-is-patched"]
|
||||||
voice = ["livekit-api", "livekit-protocol", "livekit-runtime"]
|
|
||||||
|
|
||||||
# Default Features
|
# Default Features
|
||||||
default = ["mongodb", "async-std-runtime", "tasks"]
|
default = ["mongodb", "async-std-runtime", "tasks"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.9.1", path = "../config", features = [
|
revolt-config = { version = "0.8.9", path = "../config", features = [
|
||||||
"report-macros",
|
"report-macros",
|
||||||
] }
|
] }
|
||||||
revolt-result = { version = "0.9.1", path = "../result" }
|
revolt-result = { version = "0.8.9", path = "../result" }
|
||||||
revolt-models = { version = "0.9.1", path = "../models", features = [
|
revolt-models = { version = "0.8.9", path = "../models", features = [
|
||||||
"validator",
|
"validator",
|
||||||
] }
|
] }
|
||||||
revolt-presence = { version = "0.9.1", path = "../presence" }
|
revolt-presence = { version = "0.8.9", path = "../presence" }
|
||||||
revolt-permissions = { version = "0.9.1", path = "../permissions", features = [
|
revolt-permissions = { version = "0.8.9", path = "../permissions", features = [
|
||||||
"serde",
|
"serde",
|
||||||
"bson",
|
"bson",
|
||||||
] }
|
] }
|
||||||
revolt-parser = { version = "0.9.1", path = "../parser" }
|
revolt-parser = { version = "0.8.9", path = "../parser" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
@@ -99,6 +98,6 @@ authifier = { version = "1.0.15" }
|
|||||||
amqprs = { version = "1.7.0" }
|
amqprs = { version = "1.7.0" }
|
||||||
|
|
||||||
# Voice
|
# Voice
|
||||||
livekit-api = { version = "0.4.4", optional = true}
|
livekit-api = "0.4.4"
|
||||||
livekit-protocol = { version = "0.4.0", optional = true }
|
livekit-protocol = "0.4.0"
|
||||||
livekit-runtime = { version = "0.3.1", features = ["tokio"], optional = true }
|
livekit-runtime = { version = "0.3.1", features = ["tokio"] }
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
],
|
],
|
||||||
"roles": {
|
"roles": {
|
||||||
"__ID:5__": {
|
"__ID:5__": {
|
||||||
"_id": "__ID:5__",
|
|
||||||
"name": "Moderator",
|
"name": "Moderator",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"a": 545270208,
|
"a": 545270208,
|
||||||
@@ -56,7 +55,6 @@
|
|||||||
"rank": 1
|
"rank": 1
|
||||||
},
|
},
|
||||||
"__ID:6__": {
|
"__ID:6__": {
|
||||||
"_id": "__ID:6__",
|
|
||||||
"name": "Owner",
|
"name": "Owner",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"a": 0,
|
"a": 0,
|
||||||
|
|||||||
@@ -295,9 +295,7 @@ pub enum EventV1 {
|
|||||||
},
|
},
|
||||||
UserMoveVoiceChannel {
|
UserMoveVoiceChannel {
|
||||||
node: String,
|
node: String,
|
||||||
from: String,
|
token: String
|
||||||
to: String,
|
|
||||||
token: String,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ pub mod tasks;
|
|||||||
mod amqp;
|
mod amqp;
|
||||||
pub use amqp::amqp::AMQP;
|
pub use amqp::amqp::AMQP;
|
||||||
|
|
||||||
#[cfg(feature = "voice")]
|
|
||||||
pub mod voice;
|
pub mod voice;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ struct MigrationInfo {
|
|||||||
revision: i32,
|
revision: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LATEST_REVISION: i32 = 50; // MUST BE +1 to last migration
|
pub const LATEST_REVISION: i32 = 48; // MUST BE +1 to last migration
|
||||||
|
|
||||||
pub async fn migrate_database(db: &MongoDb) {
|
pub async fn migrate_database(db: &MongoDb) {
|
||||||
let migrations = db.col::<Document>("migrations");
|
let migrations = db.col::<Document>("migrations");
|
||||||
@@ -1246,8 +1246,8 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.expect("Failed to update voice channels");
|
.expect("Failed to update voice channels");
|
||||||
};
|
};
|
||||||
|
|
||||||
if revision <= 48 {
|
if revision <= 47 {
|
||||||
info!("Running migration [revision 48 / 22-10-2025]: Add Video + Listen to default permissions");
|
info!("Running migration [revision 47 / 29-04-2025]: Add Video to default permissions");
|
||||||
|
|
||||||
db.col::<Document>("servers")
|
db.col::<Document>("servers")
|
||||||
.update_many(
|
.update_many(
|
||||||
@@ -1255,7 +1255,7 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
doc! {
|
doc! {
|
||||||
"$bit": {
|
"$bit": {
|
||||||
"default_permissions": {
|
"default_permissions": {
|
||||||
"or": (ChannelPermission::Video + ChannelPermission::Speak + ChannelPermission::Listen) as i64
|
"or": ChannelPermission::Video as i64
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1264,48 +1264,6 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
.expect("Failed to update default_permissions");
|
.expect("Failed to update default_permissions");
|
||||||
};
|
};
|
||||||
|
|
||||||
if revision <= 49 {
|
|
||||||
info!("Running migration [revision 49 / 12-12-2025]: Add _id key to roles");
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
struct Server {
|
|
||||||
#[serde(rename = "_id")]
|
|
||||||
pub id: String,
|
|
||||||
#[serde(default = "HashMap::<String, Document>::new")]
|
|
||||||
pub roles: HashMap<String, Document>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut servers = db
|
|
||||||
.db()
|
|
||||||
.collection::<Server>("servers")
|
|
||||||
.find(doc! {
|
|
||||||
"roles": {
|
|
||||||
"$exists": true,
|
|
||||||
"$ne": {}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.map(|res| res.expect("Failed to decode Server { id, roles }"));
|
|
||||||
|
|
||||||
while let Some(server) = servers.next().await {
|
|
||||||
let mut doc = doc! {};
|
|
||||||
|
|
||||||
for id in server.roles.keys() {
|
|
||||||
doc.insert(
|
|
||||||
format!("roles.{id}._id"),
|
|
||||||
id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.db()
|
|
||||||
.collection::<Server>("servers")
|
|
||||||
.update_one(doc! { "_id": &server.id }, doc! { "$set": doc })
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reminder to update LATEST_REVISION when adding new migrations.
|
// Reminder to update LATEST_REVISION when adding new migrations.
|
||||||
LATEST_REVISION.max(revision)
|
LATEST_REVISION.max(revision)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -443,8 +443,7 @@ impl Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify replies are valid.
|
// Verify replies are valid.
|
||||||
let mut replies = Vec::new();
|
let mut replies = HashSet::new();
|
||||||
|
|
||||||
if let Some(entries) = data.replies {
|
if let Some(entries) = data.replies {
|
||||||
if entries.len() > config.features.limits.global.message_replies {
|
if entries.len() > config.features.limits.global.message_replies {
|
||||||
return Err(create_error!(TooManyReplies {
|
return Err(create_error!(TooManyReplies {
|
||||||
@@ -452,8 +451,6 @@ impl Message {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
replies.reserve(entries.len());
|
|
||||||
|
|
||||||
for ReplyIntent {
|
for ReplyIntent {
|
||||||
id,
|
id,
|
||||||
mention,
|
mention,
|
||||||
@@ -467,12 +464,7 @@ impl Message {
|
|||||||
user_mentions.insert(message.author.to_owned());
|
user_mentions.insert(message.author.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is O(n^2), but this is faster than a HashSet
|
replies.insert(message.id);
|
||||||
// when n < 20; as long as the message_replies limit
|
|
||||||
// is reasonable, this will be fast.
|
|
||||||
if !replies.contains(&message.id) {
|
|
||||||
replies.push(message.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the referenced message doesn't exist and fail_if_not_exists
|
// If the referenced message doesn't exist and fail_if_not_exists
|
||||||
// is set to false, send the message without the reply.
|
// is set to false, send the message without the reply.
|
||||||
@@ -545,7 +537,9 @@ impl Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !replies.is_empty() {
|
if !replies.is_empty() {
|
||||||
message.replies.replace(replies);
|
message
|
||||||
|
.replies
|
||||||
|
.replace(replies.into_iter().collect::<Vec<String>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate final message flags
|
// Calculate final message flags
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use iso8601_timestamp::Timestamp;
|
use iso8601_timestamp::Timestamp;
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
|
use crate::voice::get_channel_voice_state;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel,
|
events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel,
|
||||||
@@ -147,12 +148,10 @@ impl Member {
|
|||||||
|
|
||||||
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut voice_states = Vec::new();
|
let mut voice_states = Vec::new();
|
||||||
|
|
||||||
#[cfg(feature = "voice")]
|
|
||||||
for channel in &channels {
|
for channel in &channels {
|
||||||
if let Ok(Some(voice_state)) = crate::voice::get_channel_voice_state(channel).await {
|
if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await {
|
||||||
voice_states.push(voice_state)
|
voice_states.push(voice_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,8 +336,8 @@ impl IntoDocumentPath for FieldsMember {
|
|||||||
FieldsMember::Nickname => "nickname",
|
FieldsMember::Nickname => "nickname",
|
||||||
FieldsMember::Roles => "roles",
|
FieldsMember::Roles => "roles",
|
||||||
FieldsMember::Timeout => "timeout",
|
FieldsMember::Timeout => "timeout",
|
||||||
FieldsMember::CanPublish => "can_publish",
|
FieldsMember::CanPublish => "is_publishing",
|
||||||
FieldsMember::CanReceive => "can_receive",
|
FieldsMember::CanReceive => "is_receiving",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,9 +68,6 @@ auto_derived_partial!(
|
|||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// Role
|
/// Role
|
||||||
pub struct Role {
|
pub struct Role {
|
||||||
/// Unique Id
|
|
||||||
#[serde(rename = "_id")]
|
|
||||||
pub id: String,
|
|
||||||
/// Role name
|
/// Role name
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Permissions available to this role
|
/// Permissions available to this role
|
||||||
@@ -249,6 +246,7 @@ impl Server {
|
|||||||
role.update(
|
role.update(
|
||||||
db,
|
db,
|
||||||
&self.id,
|
&self.id,
|
||||||
|
role_id,
|
||||||
PartialRole {
|
PartialRole {
|
||||||
permissions: Some(permissions),
|
permissions: Some(permissions),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -299,7 +297,6 @@ impl Role {
|
|||||||
/// Into optional struct
|
/// Into optional struct
|
||||||
pub fn into_optional(self) -> PartialRole {
|
pub fn into_optional(self) -> PartialRole {
|
||||||
PartialRole {
|
PartialRole {
|
||||||
id: Some(self.id),
|
|
||||||
name: Some(self.name),
|
name: Some(self.name),
|
||||||
permissions: Some(self.permissions),
|
permissions: Some(self.permissions),
|
||||||
colour: self.colour,
|
colour: self.colour,
|
||||||
@@ -309,29 +306,20 @@ impl Role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a role
|
/// Create a role
|
||||||
pub async fn create(db: &Database, server: &Server, name: String) -> Result<Self> {
|
pub async fn create(&self, db: &Database, server_id: &str) -> Result<String> {
|
||||||
let role = Role {
|
let role_id = Ulid::new().to_string();
|
||||||
id: Ulid::new().to_string(),
|
db.insert_role(server_id, &role_id, self).await?;
|
||||||
name,
|
|
||||||
// Rank of the new role should be below the lowest role
|
|
||||||
rank: server.roles.len() as i64,
|
|
||||||
colour: None,
|
|
||||||
hoist: false,
|
|
||||||
permissions: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
db.insert_role(&server.id, &role).await?;
|
|
||||||
|
|
||||||
EventV1::ServerRoleUpdate {
|
EventV1::ServerRoleUpdate {
|
||||||
id: server.id.clone(),
|
id: server_id.to_string(),
|
||||||
role_id: role.id.clone(),
|
role_id: role_id.to_string(),
|
||||||
data: role.clone().into_optional().into(),
|
data: self.clone().into_optional().into(),
|
||||||
clear: vec![],
|
clear: vec![],
|
||||||
}
|
}
|
||||||
.p(server.id.clone())
|
.p(server_id.to_string())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(role)
|
Ok(role_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update server data
|
/// Update server data
|
||||||
@@ -339,6 +327,7 @@ impl Role {
|
|||||||
&mut self,
|
&mut self,
|
||||||
db: &Database,
|
db: &Database,
|
||||||
server_id: &str,
|
server_id: &str,
|
||||||
|
role_id: &str,
|
||||||
partial: PartialRole,
|
partial: PartialRole,
|
||||||
remove: Vec<FieldsRole>,
|
remove: Vec<FieldsRole>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -348,14 +337,14 @@ impl Role {
|
|||||||
|
|
||||||
self.apply_options(partial.clone());
|
self.apply_options(partial.clone());
|
||||||
|
|
||||||
db.update_role(server_id, &self.id, &partial, remove.clone())
|
db.update_role(server_id, role_id, &partial, remove.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
EventV1::ServerRoleUpdate {
|
EventV1::ServerRoleUpdate {
|
||||||
id: server_id.to_string(),
|
id: server_id.to_string(),
|
||||||
role_id: self.id.clone(),
|
role_id: role_id.to_string(),
|
||||||
data: partial.into(),
|
data: partial.into(),
|
||||||
clear: remove.into_iter().map(Into::into).collect(),
|
clear: vec![],
|
||||||
}
|
}
|
||||||
.p(server_id.to_string())
|
.p(server_id.to_string())
|
||||||
.await;
|
.await;
|
||||||
@@ -371,15 +360,15 @@ impl Role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a role
|
/// Delete a role
|
||||||
pub async fn delete(self, db: &Database, server_id: &str) -> Result<()> {
|
pub async fn delete(self, db: &Database, server_id: &str, role_id: &str) -> Result<()> {
|
||||||
EventV1::ServerRoleDelete {
|
EventV1::ServerRoleDelete {
|
||||||
id: server_id.to_string(),
|
id: server_id.to_string(),
|
||||||
role_id: self.id.clone(),
|
role_id: role_id.to_string(),
|
||||||
}
|
}
|
||||||
.p(server_id.to_string())
|
.p(server_id.to_string())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
db.delete_role(server_id, &self.id).await
|
db.delete_role(server_id, role_id).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ pub trait AbstractServers: Sync + Send {
|
|||||||
async fn delete_server(&self, id: &str) -> Result<()>;
|
async fn delete_server(&self, id: &str) -> Result<()>;
|
||||||
|
|
||||||
/// Insert a new role into server object
|
/// Insert a new role into server object
|
||||||
async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()>;
|
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()>;
|
||||||
|
|
||||||
/// Update an existing role on a server
|
/// Update an existing role on a server
|
||||||
async fn update_role(
|
async fn update_role(
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ impl AbstractServers for MongoDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new role into server object
|
/// Insert a new role into server object
|
||||||
async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()> {
|
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
|
||||||
self.col::<Document>(COL)
|
self.col::<Document>(COL)
|
||||||
.update_one(
|
.update_one(
|
||||||
doc! {
|
doc! {
|
||||||
@@ -77,7 +77,7 @@ impl AbstractServers for MongoDb {
|
|||||||
},
|
},
|
||||||
doc! {
|
doc! {
|
||||||
"$set": {
|
"$set": {
|
||||||
"roles.".to_owned() + &role.id: to_document(role)
|
"roles.".to_owned() + role_id: to_document(role)
|
||||||
.map_err(|_| create_database_error!("to_document", "role"))?
|
.map_err(|_| create_database_error!("to_document", "role"))?
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,10 +72,10 @@ impl AbstractServers for ReferenceDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new role into server object
|
/// Insert a new role into server object
|
||||||
async fn insert_role(&self, server_id: &str, role: &Role) -> Result<()> {
|
async fn insert_role(&self, server_id: &str, role_id: &str, role: &Role) -> Result<()> {
|
||||||
let mut servers = self.servers.lock().await;
|
let mut servers = self.servers.lock().await;
|
||||||
if let Some(server) = servers.get_mut(server_id) {
|
if let Some(server) = servers.get_mut(server_id) {
|
||||||
server.roles.insert(role.id.clone(), role.clone());
|
server.roles.insert(role_id.to_string(), role.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(create_error!(NotFound))
|
Err(create_error!(NotFound))
|
||||||
|
|||||||
@@ -896,7 +896,6 @@ impl From<SystemMessageChannels> for crate::SystemMessageChannels {
|
|||||||
impl From<crate::Role> for Role {
|
impl From<crate::Role> for Role {
|
||||||
fn from(value: crate::Role) -> Self {
|
fn from(value: crate::Role) -> Self {
|
||||||
Role {
|
Role {
|
||||||
id: value.id,
|
|
||||||
name: value.name,
|
name: value.name,
|
||||||
permissions: value.permissions,
|
permissions: value.permissions,
|
||||||
colour: value.colour,
|
colour: value.colour,
|
||||||
@@ -909,7 +908,6 @@ impl From<crate::Role> for Role {
|
|||||||
impl From<Role> for crate::Role {
|
impl From<Role> for crate::Role {
|
||||||
fn from(value: Role) -> crate::Role {
|
fn from(value: Role) -> crate::Role {
|
||||||
crate::Role {
|
crate::Role {
|
||||||
id: value.id,
|
|
||||||
name: value.name,
|
name: value.name,
|
||||||
permissions: value.permissions,
|
permissions: value.permissions,
|
||||||
colour: value.colour,
|
colour: value.colour,
|
||||||
@@ -922,7 +920,6 @@ impl From<Role> for crate::Role {
|
|||||||
impl From<crate::PartialRole> for PartialRole {
|
impl From<crate::PartialRole> for PartialRole {
|
||||||
fn from(value: crate::PartialRole) -> Self {
|
fn from(value: crate::PartialRole) -> Self {
|
||||||
PartialRole {
|
PartialRole {
|
||||||
id: value.id,
|
|
||||||
name: value.name,
|
name: value.name,
|
||||||
permissions: value.permissions,
|
permissions: value.permissions,
|
||||||
colour: value.colour,
|
colour: value.colour,
|
||||||
@@ -935,7 +932,6 @@ impl From<crate::PartialRole> for PartialRole {
|
|||||||
impl From<PartialRole> for crate::PartialRole {
|
impl From<PartialRole> for crate::PartialRole {
|
||||||
fn from(value: PartialRole) -> crate::PartialRole {
|
fn from(value: PartialRole) -> crate::PartialRole {
|
||||||
crate::PartialRole {
|
crate::PartialRole {
|
||||||
id: value.id,
|
|
||||||
name: value.name,
|
name: value.name,
|
||||||
permissions: value.permissions,
|
permissions: value.permissions,
|
||||||
colour: value.colour,
|
colour: value.colour,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ mod voice_client;
|
|||||||
pub use voice_client::VoiceClient;
|
pub use voice_client::VoiceClient;
|
||||||
|
|
||||||
async fn get_connection() -> Result<Conn> {
|
async fn get_connection() -> Result<Conn> {
|
||||||
_get_connection().await.map_err(|_| create_error!(InternalError))
|
_get_connection().await.to_internal_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn raise_if_in_voice(user: &User, channel_id: &str) -> Result<()> {
|
pub async fn raise_if_in_voice(user: &User, channel_id: &str) -> Result<()> {
|
||||||
@@ -35,7 +35,7 @@ pub async fn raise_if_in_voice(user: &User, channel_id: &str) -> Result<()> {
|
|||||||
.to_internal_error()?
|
.to_internal_error()?
|
||||||
> 0
|
> 0
|
||||||
{
|
{
|
||||||
Err(create_error!(AlreadyConnected))
|
Err(create_error!(NotConnected))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -564,35 +564,3 @@ pub async fn get_call_notification_recipients(
|
|||||||
.await
|
.await
|
||||||
.to_internal_error()
|
.to_internal_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_user_from_voice_channels(db: &Database, voice_client: &VoiceClient, user_id: &str) -> Result<()> {
|
|
||||||
for channel_id in get_user_voice_channels(user_id).await? {
|
|
||||||
remove_user_from_voice_channel(db, voice_client, &channel_id, user_id).await?;
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn remove_user_from_voice_channel(db: &Database, voice_client: &VoiceClient, channel_id: &str, user_id: &str) -> Result<()> {
|
|
||||||
if let Some(node) = get_channel_node(channel_id).await? {
|
|
||||||
let _ = voice_client.remove_user(&node, user_id, channel_id).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
|
||||||
|
|
||||||
delete_voice_state(channel_id, channel.server(), user_id).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_voice_channel(voice_client: &VoiceClient, channel_id: &str, server_id: Option<&str>) -> Result<()> {
|
|
||||||
if let Some(users) = get_voice_channel_members(channel_id).await? {
|
|
||||||
let node = get_channel_node(channel_id).await?.unwrap();
|
|
||||||
|
|
||||||
voice_client.delete_room(&node, channel_id).await?;
|
|
||||||
|
|
||||||
delete_channel_voice_state(channel_id, server_id, &users).await?;
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- Compiled with Bootstrap Email version: 1.6.0 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
<!-- Compiled with Bootstrap Email version: 1.5.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
<img alt="Stoat Logo" class="w-24" src="https://stoat.chat/favicon.svg" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
<img alt="Revolt Logo" class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||||
This email is intended for {{email}}<br>
|
This email is intended for {{email}}<br>
|
||||||
Sent from Stoat<br>
|
Sent from Revolt<br>
|
||||||
Made in Europe
|
Made in Europe
|
||||||
</div>
|
</div>
|
||||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
|||||||
@@ -1,37 +1,38 @@
|
|||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<style>
|
||||||
<style>
|
/* Add custom classes and styles that you want inlined here */
|
||||||
/* Add custom classes and styles that you want inlined here */
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body class="bg-light">
|
||||||
|
<div class="container">
|
||||||
<body class="bg-light">
|
<img
|
||||||
<div class="container">
|
alt="Revolt Logo"
|
||||||
<img alt="Stoat Logo" class="ax-center my-10 w-24" src="https://stoat.chat/favicon.svg" />
|
class="ax-center my-10 w-24"
|
||||||
<div class="card p-6 p-lg-10 space-y-4">
|
src="https://app.revolt.chat/assets/logo_round.png"
|
||||||
<h1 class="h3 fw-700">Account Deletion</h1>
|
/>
|
||||||
<p>
|
<div class="card p-6 p-lg-10 space-y-4">
|
||||||
You requested to have your account deleted, if you did not perform
|
<h1 class="h3 fw-700">Account Deletion</h1>
|
||||||
this action please take measures to secure your account immediately.
|
<p>
|
||||||
</p>
|
You requested to have your account deleted, if you did not perform
|
||||||
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Confirm</a>
|
this action please take measures to secure your account immediately.
|
||||||
|
</p>
|
||||||
|
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Confirm</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-center my-6">
|
||||||
|
This email is intended for {{email}}<br />
|
||||||
|
Sent from Revolt<br />
|
||||||
|
Made in Europe
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-xs my-6 ax-center">
|
||||||
|
Revolt Platforms Ltd. is a company incorporated and registered under the
|
||||||
|
laws of England and Wales.<br />
|
||||||
|
Registered Company Number: 16260658<br />
|
||||||
|
Registered Office:<br />
|
||||||
|
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
||||||
|
Holborn, United Kingdom, EC1N 8DX
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted text-center my-6">
|
</body>
|
||||||
This email is intended for {{email}}<br />
|
</html>
|
||||||
Sent from Stoat<br />
|
|
||||||
Made in Europe
|
|
||||||
</div>
|
|
||||||
<div class="text-muted text-xs my-6 ax-center">
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the
|
|
||||||
laws of England and Wales.<br />
|
|
||||||
Registered Company Number: 16260658<br />
|
|
||||||
Registered Office:<br />
|
|
||||||
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
|
||||||
Holborn, United Kingdom, EC1N 8DX
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ You requested to have your account deleted, if you did not perform this action p
|
|||||||
Please navigate to: {{url}}
|
Please navigate to: {{url}}
|
||||||
|
|
||||||
This email is intended for {{email}}
|
This email is intended for {{email}}
|
||||||
Sent by Stoat
|
Sent by Revolt
|
||||||
Made in Europe
|
Made in Europe
|
||||||
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ Please navigate to: {{url}}
|
|||||||
|
|
||||||
This email is intended for {{email}}
|
This email is intended for {{email}}
|
||||||
|
|
||||||
This email has no association with Stoat or Revolt Platforms Ltd.
|
This email has no association with Revolt or Revolt Platforms Ltd.
|
||||||
Learn more about third party instances here:
|
Learn more about third party instances here:
|
||||||
https://developers.stoat.chat/faq.html
|
https://developers.revolt.chat/faq.html
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- Compiled with Bootstrap Email version: 1.6.0 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
<!-- Compiled with Bootstrap Email version: 1.5.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
<img class="w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
<img class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||||
This email is intended for {{email}}<br>
|
This email is intended for {{email}}<br>
|
||||||
Sent from Stoat<br>
|
Sent from Revolt<br>
|
||||||
Made in Europe
|
Made in Europe
|
||||||
</div>
|
</div>
|
||||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<img
|
<img
|
||||||
class="ax-center my-10 w-24"
|
class="ax-center my-10 w-24"
|
||||||
src="https://stoat.chat/favicon.svg"
|
src="https://app.revolt.chat/assets/logo_round.png"
|
||||||
alt="Stoat Logo"
|
|
||||||
/>
|
/>
|
||||||
<div class="card p-6 p-lg-10 space-y-4">
|
<div class="card p-6 p-lg-10 space-y-4">
|
||||||
<h1 class="h3 fw-700">Password Reset</h1>
|
<h1 class="h3 fw-700">Password Reset</h1>
|
||||||
@@ -26,7 +25,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-muted text-center my-6">
|
<div class="text-muted text-center my-6">
|
||||||
This email is intended for {{email}}<br />
|
This email is intended for {{email}}<br />
|
||||||
Sent from Stoat<br />
|
Sent from Revolt<br />
|
||||||
Made in Europe
|
Made in Europe
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted text-xs my-6 ax-center">
|
<div class="text-muted text-xs my-6 ax-center">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ password on it, click below to continue.
|
|||||||
Please navigate to: {{url}}
|
Please navigate to: {{url}}
|
||||||
|
|
||||||
This email is intended for {{email}}
|
This email is intended for {{email}}
|
||||||
Sent by Stoat
|
Sent by Revolt
|
||||||
Made in Europe
|
Made in Europe
|
||||||
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- Compiled with Bootstrap Email version: 1.6.0 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
<!-- Compiled with Bootstrap Email version: 1.5.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
<img class="w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
<img class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||||
This email is intended for {{email}}<br>
|
This email is intended for {{email}}<br>
|
||||||
Sent from Stoat<br>
|
Sent from Revolt<br>
|
||||||
Made in Europe
|
Made in Europe
|
||||||
</div>
|
</div>
|
||||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<style>
|
||||||
<style>
|
/* Add custom classes and styles that you want inlined here */
|
||||||
/* Add custom classes and styles that you want inlined here */
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body class="bg-light">
|
||||||
|
<div class="container">
|
||||||
<body class="bg-light">
|
<img
|
||||||
<div class="container">
|
class="ax-center my-10 w-24"
|
||||||
<img class="ax-center my-10 w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" />
|
src="https://app.revolt.chat/assets/logo_round.png"
|
||||||
<div class="card p-6 p-lg-10 space-y-4">
|
/>
|
||||||
<h1 class="h3 fw-700">Password Reset</h1>
|
<div class="card p-6 p-lg-10 space-y-4">
|
||||||
<p>You requested a password reset, click below to continue.</p>
|
<h1 class="h3 fw-700">Password Reset</h1>
|
||||||
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Reset</a>
|
<p>You requested a password reset, click below to continue.</p>
|
||||||
|
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Reset</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-center my-6">
|
||||||
|
This email is intended for {{email}}<br />
|
||||||
|
Sent from Revolt<br />
|
||||||
|
Made in Europe
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-xs my-6 ax-center">
|
||||||
|
Revolt Platforms Ltd. is a company incorporated and registered under the
|
||||||
|
laws of England and Wales.<br />
|
||||||
|
Registered Company Number: 16260658<br />
|
||||||
|
Registered Office:<br />
|
||||||
|
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
||||||
|
Holborn, United Kingdom, EC1N 8DX
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted text-center my-6">
|
</body>
|
||||||
This email is intended for {{email}}<br />
|
</html>
|
||||||
Sent from Stoat<br />
|
|
||||||
Made in Europe
|
|
||||||
</div>
|
|
||||||
<div class="text-muted text-xs my-6 ax-center">
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the
|
|
||||||
laws of England and Wales.<br />
|
|
||||||
Registered Company Number: 16260658<br />
|
|
||||||
Registered Office:<br />
|
|
||||||
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
|
||||||
Holborn, United Kingdom, EC1N 8DX
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ You requested a password reset, if you did not perform this action you can safel
|
|||||||
Please navigate to: {{url}}
|
Please navigate to: {{url}}
|
||||||
|
|
||||||
This email is intended for {{email}}
|
This email is intended for {{email}}
|
||||||
Sent by Stoat
|
Sent by Revolt
|
||||||
Made in Europe
|
Made in Europe
|
||||||
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- Compiled with Bootstrap Email version: 1.6.0 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
<!-- Compiled with Bootstrap Email version: 1.5.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
<img class="w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
<img alt="Revolt Logo" class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||||
This email is intended for {{email}}<br>
|
This email is intended for {{email}}<br>
|
||||||
Sent from Stoat<br>
|
Sent from Revolt<br>
|
||||||
Made in Europe
|
Made in Europe
|
||||||
</div>
|
</div>
|
||||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
|||||||
@@ -1,45 +1,46 @@
|
|||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<style>
|
||||||
<style>
|
/* Add custom classes and styles that you want inlined here */
|
||||||
/* Add custom classes and styles that you want inlined here */
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body class="bg-light">
|
||||||
|
<div class="container">
|
||||||
<body class="bg-light">
|
<img
|
||||||
<div class="container">
|
alt="Revolt Logo"
|
||||||
<img class="ax-center my-10 w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" />
|
class="ax-center my-10 w-24"
|
||||||
<div class="card p-6 p-lg-10 space-y-4">
|
src="https://app.revolt.chat/assets/logo_round.png"
|
||||||
<h1 class="h3 fw-700">Account Suspended</h1>
|
/>
|
||||||
<p>Your account has been suspended, for one or more reasons:</p>
|
<div class="card p-6 p-lg-10 space-y-4">
|
||||||
<ul>
|
<h1 class="h3 fw-700">Account Suspended</h1>
|
||||||
{{list}}
|
<p>Your account has been suspended, for one or more reasons:</p>
|
||||||
</ul>
|
<ul>
|
||||||
<p style="display:{{duration_display}}">
|
{{list}}
|
||||||
You will be able to use your account again in {{duration}} days.
|
</ul>
|
||||||
</p>
|
<p style="display:{{duration_display}}">
|
||||||
<p>
|
You will be able to use your account again in {{duration}} days.
|
||||||
Further violations may result in a permanent ban depending on
|
</p>
|
||||||
severity, please abide by the
|
<p>
|
||||||
<a href="https://revolt.chat/aup">Acceptable Usage Policy</a>.
|
Further violations may result in a permanent ban depending on
|
||||||
</p>
|
severity, please abide by the
|
||||||
<p>Ban evasion is prohibited and will be dealt with accordingly.</p>
|
<a href="https://revolt.chat/aup">Acceptable Usage Policy</a>.
|
||||||
|
</p>
|
||||||
|
<p>Ban evasion is prohibited and will be dealt with accordingly.</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-center my-6">
|
||||||
|
This email is intended for {{email}}<br />
|
||||||
|
Sent from Revolt<br />
|
||||||
|
Made in Europe
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-xs my-6 ax-center">
|
||||||
|
Revolt Platforms Ltd. is a company incorporated and registered under the
|
||||||
|
laws of England and Wales.<br />
|
||||||
|
Registered Company Number: 16260658<br />
|
||||||
|
Registered Office:<br />
|
||||||
|
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
||||||
|
Holborn, United Kingdom, EC1N 8DX
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted text-center my-6">
|
</body>
|
||||||
This email is intended for {{email}}<br />
|
</html>
|
||||||
Sent from Stoat<br />
|
|
||||||
Made in Europe
|
|
||||||
</div>
|
|
||||||
<div class="text-muted text-xs my-6 ax-center">
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the
|
|
||||||
laws of England and Wales.<br />
|
|
||||||
Registered Company Number: 16260658<br />
|
|
||||||
Registered Office:<br />
|
|
||||||
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
|
||||||
Holborn, United Kingdom, EC1N 8DX
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Further violations may result in a permanent ban depending on severity, please a
|
|||||||
Ban evasion is prohibited and will be dealt with accordingly.
|
Ban evasion is prohibited and will be dealt with accordingly.
|
||||||
|
|
||||||
This email is intended for {{email}}
|
This email is intended for {{email}}
|
||||||
Sent by Stoat
|
Sent by Revolt
|
||||||
Made in Europe
|
Made in Europe
|
||||||
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<!-- Compiled with Bootstrap Email version: 1.6.0 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
<!-- Compiled with Bootstrap Email version: 1.5.1 --><meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||||
<meta name="x-apple-disable-message-reformatting">
|
<meta name="x-apple-disable-message-reformatting">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
<td style="line-height: 24px; font-size: 16px; margin: 0;" align="left">
|
||||||
<img class="w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
<img alt="Revolt Logo" class="w-24" src="https://app.revolt.chat/assets/logo_round.png" style="height: auto; line-height: 100%; outline: none; text-decoration: none; display: block; width: 96px; border-style: none; border-width: 0;" width="96">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<div class="text-muted text-center" style="color: #718096;" align="center">
|
<div class="text-muted text-center" style="color: #718096;" align="center">
|
||||||
This email is intended for {{email}}<br>
|
This email is intended for {{email}}<br>
|
||||||
Sent from Stoat<br>
|
Sent from Revolt<br>
|
||||||
Made in Europe
|
Made in Europe
|
||||||
</div>
|
</div>
|
||||||
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
<table class="s-6 w-full" role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%;" width="100%">
|
||||||
|
|||||||
@@ -1,34 +1,35 @@
|
|||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<style>
|
||||||
<style>
|
/* Add custom classes and styles that you want inlined here */
|
||||||
/* Add custom classes and styles that you want inlined here */
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body class="bg-light">
|
||||||
|
<div class="container">
|
||||||
<body class="bg-light">
|
<img
|
||||||
<div class="container">
|
alt="Revolt Logo"
|
||||||
<img class="ax-center my-10 w-24" src="https://stoat.chat/favicon.svg" alt="Stoat Logo" />
|
class="ax-center my-10 w-24"
|
||||||
<div class="card p-6 p-lg-10 space-y-4">
|
src="https://app.revolt.chat/assets/logo_round.png"
|
||||||
<h1 class="h3 fw-700">Almost there!</h1>
|
/>
|
||||||
<p>To complete your sign up, we just need to verify your email.</p>
|
<div class="card p-6 p-lg-10 space-y-4">
|
||||||
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Confirm</a>
|
<h1 class="h3 fw-700">Almost there!</h1>
|
||||||
|
<p>To complete your sign up, we just need to verify your email.</p>
|
||||||
|
<a class="btn btn-primary p-3 fw-700" href="{{url}}">Confirm</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-center my-6">
|
||||||
|
This email is intended for {{email}}<br />
|
||||||
|
Sent from Revolt<br />
|
||||||
|
Made in Europe
|
||||||
|
</div>
|
||||||
|
<div class="text-muted text-xs my-6 ax-center">
|
||||||
|
Revolt Platforms Ltd. is a company incorporated and registered under the
|
||||||
|
laws of England and Wales.<br />
|
||||||
|
Registered Company Number: 16260658<br />
|
||||||
|
Registered Office:<br />
|
||||||
|
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
||||||
|
Holborn, United Kingdom, EC1N 8DX
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-muted text-center my-6">
|
</body>
|
||||||
This email is intended for {{email}}<br />
|
</html>
|
||||||
Sent from Stoat<br />
|
|
||||||
Made in Europe
|
|
||||||
</div>
|
|
||||||
<div class="text-muted text-xs my-6 ax-center">
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the
|
|
||||||
laws of England and Wales.<br />
|
|
||||||
Registered Company Number: 16260658<br />
|
|
||||||
Registered Office:<br />
|
|
||||||
Suite 5703 Unit 3A, 34-35 Hatton Garden,<br />
|
|
||||||
Holborn, United Kingdom, EC1N 8DX
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ To complete your sign up, we just need to verify your email.
|
|||||||
Please navigate to: {{url}}
|
Please navigate to: {{url}}
|
||||||
|
|
||||||
This email is intended for {{email}}
|
This email is intended for {{email}}
|
||||||
Sent by Stoat
|
Sent by Revolt
|
||||||
Made in Europe
|
Made in Europe
|
||||||
|
|
||||||
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
Revolt Platforms Ltd. is a company incorporated and registered under the laws of England and Wales.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-files"
|
name = "revolt-files"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -20,10 +20,10 @@ typenum = "1.17.0"
|
|||||||
aws-config = "1.5.5"
|
aws-config = "1.5.5"
|
||||||
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
|
aws-sdk-s3 = { version = "1.46.0", features = ["behavior-version-latest"] }
|
||||||
|
|
||||||
revolt-config = { version = "0.9.1", path = "../config", features = [
|
revolt-config = { version = "0.8.9", path = "../config", features = [
|
||||||
"report-macros",
|
"report-macros",
|
||||||
] }
|
] }
|
||||||
revolt-result = { version = "0.9.1", path = "../result" }
|
revolt-result = { version = "0.8.9", path = "../result" }
|
||||||
|
|
||||||
# image processing
|
# image processing
|
||||||
jxl-oxide = "0.8.1"
|
jxl-oxide = "0.8.1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-models"
|
name = "revolt-models"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -20,8 +20,8 @@ default = ["serde", "partials", "rocket"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-config = { version = "0.9.1", path = "../config" }
|
revolt-config = { version = "0.8.9", path = "../config" }
|
||||||
revolt-permissions = { version = "0.9.1", path = "../permissions" }
|
revolt-permissions = { version = "0.8.9", path = "../permissions" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
regex = "1.11"
|
regex = "1.11"
|
||||||
|
|||||||
@@ -85,9 +85,6 @@ auto_derived_partial!(
|
|||||||
auto_derived_partial!(
|
auto_derived_partial!(
|
||||||
/// Role
|
/// Role
|
||||||
pub struct Role {
|
pub struct Role {
|
||||||
/// Unique Id
|
|
||||||
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
|
|
||||||
pub id: String,
|
|
||||||
/// Role name
|
/// Role name
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Permissions available to this role
|
/// Permissions available to this role
|
||||||
@@ -184,7 +181,6 @@ auto_derived!(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Response after creating new role
|
/// Response after creating new role
|
||||||
// TODO: remove this in favor of just Role
|
|
||||||
pub struct NewRoleResponse {
|
pub struct NewRoleResponse {
|
||||||
/// Id of the role
|
/// Id of the role
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-parser"
|
name = "revolt-parser"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]
|
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-permissions"
|
name = "revolt-permissions"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -21,7 +21,7 @@ async-std = { version = "1.8.0", features = ["attributes"] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Core
|
# Core
|
||||||
revolt-result = { version = "0.9.1", path = "../result" }
|
revolt-result = { version = "0.8.9", path = "../result" }
|
||||||
|
|
||||||
# Utility
|
# Utility
|
||||||
auto_ops = "0.3.0"
|
auto_ops = "0.3.0"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-presence"
|
name = "revolt-presence"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -16,7 +16,7 @@ redis-is-patched = []
|
|||||||
async-std = { version = "1.8.0", features = ["attributes"] }
|
async-std = { version = "1.8.0", features = ["attributes"] }
|
||||||
|
|
||||||
# Config for loading Redis URI
|
# Config for loading Redis URI
|
||||||
revolt-config = { version = "0.9.1", path = "../config" }
|
revolt-config = { version = "0.8.9", path = "../config" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Utility
|
# Utility
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-ratelimits"
|
name = "revolt-ratelimits"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@@ -10,9 +10,9 @@ axum = ["dep:axum", "revolt-database/axum-impl"]
|
|||||||
default = ["rocket", "axum"]
|
default = ["rocket", "axum"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
revolt-database = { version = "0.9.1", path = "../database"}
|
revolt-database = { version = "0.8.9", path = "../database"}
|
||||||
revolt-result = { version = "0.9.1", path = "../result" }
|
revolt-result = { version = "0.8.9", path = "../result" }
|
||||||
revolt-config = { version = "0.9.1", path = "../config" }
|
revolt-config = { version = "0.8.9", path = "../config" }
|
||||||
|
|
||||||
rocket = { version = "0.5.1", optional = true }
|
rocket = { version = "0.5.1", optional = true }
|
||||||
revolt_rocket_okapi = { version = "0.10.0", optional = true }
|
revolt_rocket_okapi = { version = "0.10.0", optional = true }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
@@ -15,9 +15,8 @@ utoipa = ["dep:utoipa"]
|
|||||||
rocket = ["dep:rocket", "dep:serde_json"]
|
rocket = ["dep:rocket", "dep:serde_json"]
|
||||||
axum = ["dep:axum", "dep:serde_json"]
|
axum = ["dep:axum", "dep:serde_json"]
|
||||||
okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi", "schemas"]
|
okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi", "schemas"]
|
||||||
sentry = ["dep:sentry"]
|
|
||||||
|
|
||||||
default = ["serde", "sentry"]
|
default = ["serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Serialisation
|
# Serialisation
|
||||||
@@ -37,5 +36,3 @@ revolt_okapi = { version = "0.9.1", optional = true }
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
# Axum
|
# Axum
|
||||||
axum = { version = "0.7.5", optional = true }
|
axum = { version = "0.7.5", optional = true }
|
||||||
|
|
||||||
sentry = { version = "0.31.5", optional = true }
|
|
||||||
@@ -62,7 +62,6 @@ impl IntoResponse for Error {
|
|||||||
ErrorType::NotPrivileged => StatusCode::FORBIDDEN,
|
ErrorType::NotPrivileged => StatusCode::FORBIDDEN,
|
||||||
ErrorType::CannotGiveMissingPermissions => StatusCode::FORBIDDEN,
|
ErrorType::CannotGiveMissingPermissions => StatusCode::FORBIDDEN,
|
||||||
ErrorType::NotOwner => StatusCode::FORBIDDEN,
|
ErrorType::NotOwner => StatusCode::FORBIDDEN,
|
||||||
ErrorType::IsElevated => StatusCode::FORBIDDEN,
|
|
||||||
|
|
||||||
ErrorType::DatabaseError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
|
ErrorType::DatabaseError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
ErrorType::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
|
ErrorType::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ pub enum ErrorType {
|
|||||||
NotPrivileged,
|
NotPrivileged,
|
||||||
CannotGiveMissingPermissions,
|
CannotGiveMissingPermissions,
|
||||||
NotOwner,
|
NotOwner,
|
||||||
IsElevated,
|
|
||||||
|
|
||||||
// ? General errors
|
// ? General errors
|
||||||
DatabaseError {
|
DatabaseError {
|
||||||
@@ -227,7 +226,7 @@ pub trait ToRevoltError<T> {
|
|||||||
fn to_internal_error(self) -> Result<T, Error>;
|
fn to_internal_error(self) -> Result<T, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E: std::fmt::Debug + std::error::Error> ToRevoltError<T> for Result<T, E> {
|
impl<T, E: std::fmt::Debug> ToRevoltError<T> for Result<T, E> {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn to_internal_error(self) -> Result<T, Error> {
|
fn to_internal_error(self) -> Result<T, Error> {
|
||||||
let loc = Location::caller();
|
let loc = Location::caller();
|
||||||
@@ -235,8 +234,6 @@ impl<T, E: std::fmt::Debug + std::error::Error> ToRevoltError<T> for Result<T, E
|
|||||||
self
|
self
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
#[cfg(feature = "sentry")]
|
|
||||||
sentry::capture_error(&e);
|
|
||||||
|
|
||||||
Error {
|
Error {
|
||||||
error_type: ErrorType::InternalError,
|
error_type: ErrorType::InternalError,
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ impl<'r> Responder<'r, 'static> for Error {
|
|||||||
ErrorType::NotPrivileged => Status::Forbidden,
|
ErrorType::NotPrivileged => Status::Forbidden,
|
||||||
ErrorType::CannotGiveMissingPermissions => Status::Forbidden,
|
ErrorType::CannotGiveMissingPermissions => Status::Forbidden,
|
||||||
ErrorType::NotOwner => Status::Forbidden,
|
ErrorType::NotOwner => Status::Forbidden,
|
||||||
ErrorType::IsElevated => Status::Forbidden,
|
|
||||||
|
|
||||||
ErrorType::DatabaseError { .. } => Status::InternalServerError,
|
ErrorType::DatabaseError { .. } => Status::InternalServerError,
|
||||||
ErrorType::InternalError => Status::InternalServerError,
|
ErrorType::InternalError => Status::InternalServerError,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-crond"
|
name = "revolt-crond"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <me@insrt.uk>"]
|
authors = ["Paul Makles <me@insrt.uk>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -16,7 +16,7 @@ log = "0.4"
|
|||||||
tokio = { version = "1" }
|
tokio = { version = "1" }
|
||||||
|
|
||||||
# Core
|
# Core
|
||||||
revolt-database = { version = "0.9.1", path = "../../core/database" }
|
revolt-database = { version = "0.8.9", path = "../../core/database" }
|
||||||
revolt-result = { version = "0.9.1", path = "../../core/result" }
|
revolt-result = { version = "0.8.9", path = "../../core/result" }
|
||||||
revolt-config = { version = "0.9.1", path = "../../core/config" }
|
revolt-config = { version = "0.8.9", path = "../../core/config" }
|
||||||
revolt-files = { version = "0.9.1", path = "../../core/files" }
|
revolt-files = { version = "0.8.9", path = "../../core/files" }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-pushd"
|
name = "revolt-pushd"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
revolt-result = { version = "0.9.1", path = "../../core/result" }
|
revolt-result = { version = "0.8.9", path = "../../core/result" }
|
||||||
revolt-config = { version = "0.9.1", path = "../../core/config", features = [
|
revolt-config = { version = "0.8.9", path = "../../core/config", features = [
|
||||||
"report-macros",
|
"report-macros",
|
||||||
"anyhow"
|
"anyhow"
|
||||||
] }
|
] }
|
||||||
revolt-database = { version = "0.9.1", path = "../../core/database" }
|
revolt-database = { version = "0.8.9", path = "../../core/database" }
|
||||||
revolt-models = { version = "0.9.1", path = "../../core/models", features = [
|
revolt-models = { version = "0.8.9", path = "../../core/models", features = [
|
||||||
"validator",
|
"validator",
|
||||||
] }
|
] }
|
||||||
revolt-presence = { version = "0.9.1", path = "../../core/presence", features = [
|
revolt-presence = { version = "0.8.9", path = "../../core/presence", features = [
|
||||||
"redis-is-patched",
|
"redis-is-patched",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-voice-ingress"
|
name = "revolt-voice-ingress"
|
||||||
version = "0.9.1"
|
version = "0.7.1"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ async-std = { version = "1.8.0", features = [
|
|||||||
revolt-result = { path = "../../core/result" }
|
revolt-result = { path = "../../core/result" }
|
||||||
revolt-models = { path = "../../core/models" }
|
revolt-models = { path = "../../core/models" }
|
||||||
revolt-config = { path = "../../core/config" }
|
revolt-config = { path = "../../core/config" }
|
||||||
revolt-database = { path = "../../core/database", features = ["voice"] }
|
revolt-database = { path = "../../core/database" }
|
||||||
revolt-permissions = { path = "../../core/permissions" }
|
revolt-permissions = { path = "../../core/permissions" }
|
||||||
|
|
||||||
# Voice
|
# Voice
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use chrono::DateTime;
|
||||||
use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver};
|
use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver};
|
||||||
use livekit_protocol::TrackType;
|
use livekit_protocol::TrackType;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
@@ -5,15 +6,18 @@ use revolt_database::{
|
|||||||
iso8601_timestamp::{Duration, Timestamp},
|
iso8601_timestamp::{Duration, Timestamp},
|
||||||
util::reference::Reference,
|
util::reference::Reference,
|
||||||
voice::{
|
voice::{
|
||||||
create_voice_state, delete_voice_state,
|
create_voice_state, delete_voice_state, get_call_notification_recipients,
|
||||||
get_user_moved_from_voice, get_user_moved_to_voice,
|
get_user_moved_from_voice, get_user_moved_to_voice, get_voice_channel_members,
|
||||||
|
set_channel_call_started_system_message, take_channel_call_started_system_message,
|
||||||
update_voice_state_tracks, VoiceClient,
|
update_voice_state_tracks, VoiceClient,
|
||||||
},
|
},
|
||||||
Database, AMQP,
|
Database, PartialMessage, SystemMessage, AMQP,
|
||||||
};
|
};
|
||||||
|
use revolt_models::v0;
|
||||||
use revolt_result::{Result, ToRevoltError};
|
use revolt_result::{Result, ToRevoltError};
|
||||||
use rocket::{post, State};
|
use rocket::{post, State};
|
||||||
use rocket_empty::EmptyResponse;
|
use rocket_empty::EmptyResponse;
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
use crate::guard::AuthHeader;
|
use crate::guard::AuthHeader;
|
||||||
|
|
||||||
@@ -21,7 +25,7 @@ use crate::guard::AuthHeader;
|
|||||||
pub async fn ingress(
|
pub async fn ingress(
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
voice_client: &State<VoiceClient>,
|
voice_client: &State<VoiceClient>,
|
||||||
_amqp: &State<AMQP>,
|
amqp: &State<AMQP>,
|
||||||
node: &str,
|
node: &str,
|
||||||
auth_header: AuthHeader<'_>,
|
auth_header: AuthHeader<'_>,
|
||||||
body: &str,
|
body: &str,
|
||||||
@@ -86,51 +90,50 @@ pub async fn ingress(
|
|||||||
.await;
|
.await;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: fix `num_participants` being incorrect sometimes see (#457)
|
|
||||||
// First user who joined - send call started system message.
|
// First user who joined - send call started system message.
|
||||||
// if event.room.as_ref().unwrap().num_participants == 1 {
|
if event.room.as_ref().unwrap().num_participants == 1 {
|
||||||
// let user = Reference::from_unchecked(user_id).as_user(db).await?;
|
let user = Reference::from_unchecked(user_id).as_user(db).await?;
|
||||||
|
|
||||||
// let message_id =
|
let message_id =
|
||||||
// Ulid::from_datetime(DateTime::from_timestamp_secs(event.created_at).unwrap())
|
Ulid::from_datetime(DateTime::from_timestamp_secs(event.created_at).unwrap())
|
||||||
// .to_string();
|
.to_string();
|
||||||
|
|
||||||
// let mut call_started_message = SystemMessage::CallStarted {
|
let mut call_started_message = SystemMessage::CallStarted {
|
||||||
// by: user_id.to_string(),
|
by: user_id.to_string(),
|
||||||
// finished_at: None,
|
finished_at: None,
|
||||||
// }
|
}
|
||||||
// .into_message(channel.id().to_string());
|
.into_message(channel.id().to_string());
|
||||||
|
|
||||||
// call_started_message.id = message_id;
|
call_started_message.id = message_id;
|
||||||
|
|
||||||
// set_channel_call_started_system_message(channel.id(), &call_started_message.id)
|
set_channel_call_started_system_message(channel.id(), &call_started_message.id)
|
||||||
// .await?;
|
.await?;
|
||||||
|
|
||||||
// call_started_message
|
call_started_message
|
||||||
// .send(
|
.send(
|
||||||
// db,
|
db,
|
||||||
// Some(amqp),
|
Some(amqp),
|
||||||
// v0::MessageAuthor::System {
|
v0::MessageAuthor::System {
|
||||||
// username: &user.username,
|
username: &user.username,
|
||||||
// avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
|
avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
|
||||||
// },
|
},
|
||||||
// None,
|
None,
|
||||||
// None,
|
None,
|
||||||
// &channel,
|
&channel,
|
||||||
// false,
|
false,
|
||||||
// )
|
)
|
||||||
// .await?;
|
.await?;
|
||||||
|
|
||||||
// let recipients = get_call_notification_recipients(&channel_id, &user_id).await?;
|
let recipients = get_call_notification_recipients(&channel_id, &user_id).await?;
|
||||||
// let now = joined_at.format_short().to_string();
|
let now = joined_at.format_short().to_string();
|
||||||
|
|
||||||
// if let Err(e) = amqp
|
if let Err(e) = amqp
|
||||||
// .dm_call_updated(&user.id, channel.id(), Some(&now), false, recipients)
|
.dm_call_updated(&user.id, channel.id(), Some(&now), false, recipients)
|
||||||
// .await
|
.await
|
||||||
// {
|
{
|
||||||
// revolt_config::capture_error(&e);
|
revolt_config::capture_error(&e);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
// User left a channel
|
// User left a channel
|
||||||
"participant_left" => {
|
"participant_left" => {
|
||||||
@@ -154,52 +157,50 @@ pub async fn ingress(
|
|||||||
.await;
|
.await;
|
||||||
};
|
};
|
||||||
|
|
||||||
// See above for why this is commented out
|
// Update CallStarted system message if everyone has left with the end time
|
||||||
|
let members = get_voice_channel_members(channel_id).await?;
|
||||||
|
|
||||||
// // Update CallStarted system message if everyone has left with the end time
|
if members.is_none_or(|m| m.is_empty()) {
|
||||||
// let members = get_voice_channel_members(channel_id).await?;
|
// The channel is empty so send out an "end" message for ringing
|
||||||
|
if let Err(e) = amqp
|
||||||
|
.dm_call_updated(user_id, channel_id, None, true, None)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
revolt_config::capture_internal_error!(&e);
|
||||||
|
}
|
||||||
|
|
||||||
// if members.is_none_or(|m| m.is_empty()) {
|
if let Some(system_message_id) =
|
||||||
// // The channel is empty so send out an "end" message for ringing
|
take_channel_call_started_system_message(channel_id).await?
|
||||||
// if let Err(e) = amqp
|
{
|
||||||
// .dm_call_updated(user_id, channel_id, None, true, None)
|
// Could have been deleted
|
||||||
// .await
|
if let Ok(mut message) = Reference::from_unchecked(&system_message_id)
|
||||||
// {
|
.as_message(db)
|
||||||
// revolt_config::capture_internal_error!(&e);
|
.await
|
||||||
// }
|
{
|
||||||
|
if let Some(SystemMessage::CallStarted { finished_at, .. }) =
|
||||||
|
&mut message.system
|
||||||
|
{
|
||||||
|
*finished_at = Some(Timestamp::now_utc());
|
||||||
|
|
||||||
// if let Some(system_message_id) =
|
message
|
||||||
// take_channel_call_started_system_message(channel_id).await?
|
.update(
|
||||||
// {
|
db,
|
||||||
// // Could have been deleted
|
PartialMessage {
|
||||||
// if let Ok(mut message) = Reference::from_unchecked(&system_message_id)
|
system: message.system.clone(),
|
||||||
// .as_message(db)
|
..Default::default()
|
||||||
// .await
|
},
|
||||||
// {
|
Vec::new(),
|
||||||
// if let Some(SystemMessage::CallStarted { finished_at, .. }) =
|
)
|
||||||
// &mut message.system
|
.await?;
|
||||||
// {
|
} else {
|
||||||
// *finished_at = Some(Timestamp::now_utc());
|
log::error!("Broken State: Call started message ID ({}) does not contain a CallStarted system message.", &message.id)
|
||||||
|
}
|
||||||
// message
|
};
|
||||||
// .update(
|
};
|
||||||
// db,
|
}
|
||||||
// PartialMessage {
|
|
||||||
// system: message.system.clone(),
|
|
||||||
// ..Default::default()
|
|
||||||
// },
|
|
||||||
// Vec::new(),
|
|
||||||
// )
|
|
||||||
// .await?;
|
|
||||||
// } else {
|
|
||||||
// log::error!("Broken State: Call started message ID ({}) does not contain a CallStarted system message.", &message.id)
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
// Audio/video track was started/stopped/unmuted/muted
|
// Audio/video track was started/stopped
|
||||||
"track_published" | "track_unpublished" | "track_unmuted" | "track_muted" => {
|
"track_published" | "track_unpublished" => {
|
||||||
let channel_id = channel_id.to_internal_error()?;
|
let channel_id = channel_id.to_internal_error()?;
|
||||||
let user_id = user_id.to_internal_error()?;
|
let user_id = user_id.to_internal_error()?;
|
||||||
let track = event.track.as_ref().to_internal_error()?;
|
let track = event.track.as_ref().to_internal_error()?;
|
||||||
@@ -213,59 +214,40 @@ pub async fn ingress(
|
|||||||
// forbid any size which goes over the limit and also limit the aspect ratio to stop people from making too tall or too wide and bypassing the limit.
|
// forbid any size which goes over the limit and also limit the aspect ratio to stop people from making too tall or too wide and bypassing the limit.
|
||||||
// TODO: figure out how to track audio stream quality
|
// TODO: figure out how to track audio stream quality
|
||||||
|
|
||||||
if event.event == "track_published" {
|
if event.event == "track_published"
|
||||||
let mut disconnect = false;
|
&& (track.r#type == TrackType::Data as i32
|
||||||
|
|| (track.r#type == TrackType::Video as i32
|
||||||
|
&& (user_limits.video_resolution[0] != 0
|
||||||
|
&& user_limits.video_resolution[1] != 0
|
||||||
|
&& track.width * track.height
|
||||||
|
> user_limits.video_resolution[0]
|
||||||
|
* user_limits.video_resolution[1])
|
||||||
|
|| (user_limits.video_aspect_ratio[0]
|
||||||
|
!= user_limits.video_aspect_ratio[1]
|
||||||
|
&& !(user_limits.video_aspect_ratio[0]
|
||||||
|
..=user_limits.video_aspect_ratio[1])
|
||||||
|
.contains(&(track.width as f32 / track.height as f32)))))
|
||||||
|
{
|
||||||
|
voice_client.remove_user(node, user_id, channel_id).await?;
|
||||||
|
delete_voice_state(channel_id, channel.server(), user_id).await?;
|
||||||
|
} else {
|
||||||
|
let partial = update_voice_state_tracks(
|
||||||
|
channel_id,
|
||||||
|
channel.server(),
|
||||||
|
user_id,
|
||||||
|
event.event == "track_published", // to avoid duplicating this entire case twice
|
||||||
|
track.source,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
if track.r#type == TrackType::Data as i32 {
|
EventV1::UserVoiceStateUpdate {
|
||||||
log::debug!("User published data");
|
id: user_id.clone(),
|
||||||
disconnect = true;
|
channel_id: channel_id.clone(),
|
||||||
};
|
data: partial,
|
||||||
|
}
|
||||||
if track.r#type == TrackType::Video as i32 {
|
.p(channel_id.clone())
|
||||||
if user_limits.video_resolution[0] != 0
|
.await;
|
||||||
&& user_limits.video_resolution[1] != 0
|
|
||||||
&& track.width * track.height
|
|
||||||
> user_limits.video_resolution[0] * user_limits.video_resolution[1]
|
|
||||||
{
|
|
||||||
log::debug!("User published video with out of bounds resolution");
|
|
||||||
disconnect = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
if user_limits.video_aspect_ratio[0] != user_limits.video_aspect_ratio[1]
|
|
||||||
&& !(user_limits.video_aspect_ratio[0]..=user_limits.video_aspect_ratio[1])
|
|
||||||
.contains(&(track.width as f32 / track.height as f32))
|
|
||||||
{
|
|
||||||
log::debug!("User published video with out of bounds aspect ratio");
|
|
||||||
disconnect = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
if disconnect {
|
|
||||||
log::debug!("Removing user {user_id} from channel {channel_id} {event:?} due to forbidden track.");
|
|
||||||
|
|
||||||
let _ = voice_client.remove_user(node, user_id, channel_id).await;
|
|
||||||
delete_voice_state(channel_id, channel.server(), user_id).await?;
|
|
||||||
|
|
||||||
return Ok(EmptyResponse);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let partial = update_voice_state_tracks(
|
|
||||||
channel_id,
|
|
||||||
channel.server(),
|
|
||||||
user_id,
|
|
||||||
event.event == "track_published" || event.event == "track_unmuted", // to avoid duplicating this entire case twice
|
|
||||||
track.source,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
EventV1::UserVoiceStateUpdate {
|
|
||||||
id: user_id.clone(),
|
|
||||||
channel_id: channel_id.clone(),
|
|
||||||
data: partial,
|
|
||||||
}
|
}
|
||||||
.p(channel_id.clone())
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -72,7 +72,6 @@ revolt-config = { path = "../core/config" }
|
|||||||
revolt-database = { path = "../core/database", features = [
|
revolt-database = { path = "../core/database", features = [
|
||||||
"rocket-impl",
|
"rocket-impl",
|
||||||
"redis-is-patched",
|
"redis-is-patched",
|
||||||
"voice",
|
|
||||||
] }
|
] }
|
||||||
revolt-models = { path = "../core/models", features = [
|
revolt-models = { path = "../core/models", features = [
|
||||||
"schemas",
|
"schemas",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
],
|
],
|
||||||
"roles": {
|
"roles": {
|
||||||
"__ID:5__": {
|
"__ID:5__": {
|
||||||
"_id": "__ID:5__",
|
|
||||||
"name": "Moderator",
|
"name": "Moderator",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"a": 545270216,
|
"a": 545270216,
|
||||||
@@ -56,7 +55,6 @@
|
|||||||
"rank": 1
|
"rank": 1
|
||||||
},
|
},
|
||||||
"__ID:6__": {
|
"__ID:6__": {
|
||||||
"_id": "__ID:6__",
|
|
||||||
"name": "Owner",
|
"name": "Owner",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"a": 0,
|
"a": 0,
|
||||||
@@ -65,7 +63,6 @@
|
|||||||
"rank": 0
|
"rank": 0
|
||||||
},
|
},
|
||||||
"__ID:7__": {
|
"__ID:7__": {
|
||||||
"_id": "__ID:7__",
|
|
||||||
"name": "Lower Rank 1",
|
"name": "Lower Rank 1",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"a": 0,
|
"a": 0,
|
||||||
@@ -74,7 +71,6 @@
|
|||||||
"rank": 2
|
"rank": 2
|
||||||
},
|
},
|
||||||
"__ID:8__": {
|
"__ID:8__": {
|
||||||
"_id": "__ID:8__",
|
|
||||||
"name": "Lower Rank 2",
|
"name": "Lower Rank 2",
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"a": 0,
|
"a": 0,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use revolt_database::{Database, User, util::reference::Reference, voice::{VoiceClient, remove_user_from_voice_channels}};
|
use revolt_database::{util::reference::Reference, voice::{delete_voice_state, get_channel_node, get_user_voice_channels, VoiceClient}, Database, User};
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
use rocket_empty::EmptyResponse;
|
use rocket_empty::EmptyResponse;
|
||||||
@@ -21,7 +21,14 @@ pub async fn delete_bot(
|
|||||||
|
|
||||||
bot.delete(db).await?;
|
bot.delete(db).await?;
|
||||||
|
|
||||||
remove_user_from_voice_channels(db, voice_client, &bot.id).await?;
|
for channel_id in get_user_voice_channels(&bot.id).await? {
|
||||||
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
let channel = Reference::from_unchecked(&channel_id).as_channel(db).await?;
|
||||||
|
|
||||||
|
voice_client.remove_user(&node, &bot.id, &channel_id).await?;
|
||||||
|
|
||||||
|
delete_voice_state(&channel_id, channel.server(), &bot.id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(EmptyResponse)
|
Ok(EmptyResponse)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
AMQP, Channel, Database, PartialChannel, User, util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
VoiceClient, delete_voice_channel, is_in_voice_channel, remove_user_from_voice_channel
|
voice::{
|
||||||
}
|
delete_channel_voice_state, delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel, VoiceClient
|
||||||
|
},
|
||||||
|
Channel, Database, PartialChannel, User, AMQP,
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
@@ -55,14 +57,26 @@ pub async fn delete(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if is_in_voice_channel(&user.id, channel.id()).await? {
|
if is_in_voice_channel(&user.id, channel.id()).await? {
|
||||||
remove_user_from_voice_channel(db, voice_client, channel.id(), &user.id).await?;
|
let node = get_channel_node(channel.id()).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client
|
||||||
|
.remove_user(&node, &user.id, channel.id())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
delete_voice_state(channel.id(), None, &user.id).await?;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Channel::TextChannel { .. } => {
|
Channel::TextChannel { .. } => {
|
||||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
|
||||||
channel.delete(db).await?;
|
channel.delete(db).await?;
|
||||||
|
|
||||||
delete_voice_channel(voice_client, channel.id(), channel.server()).await?;
|
if let Some(users) = get_voice_channel_members(channel.id()).await? {
|
||||||
|
let node = get_channel_node(channel.id()).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.delete_room(&node, channel.id()).await?;
|
||||||
|
|
||||||
|
delete_channel_voice_state(channel.id(), channel.server(), &users).await?;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_channel_voice_state, get_channel_node, get_voice_channel_members, VoiceClient}, Channel, Database, File, PartialChannel, SystemMessage, User, AMQP
|
||||||
voice::{delete_voice_channel, VoiceClient},
|
|
||||||
Channel, Database, File, PartialChannel, SystemMessage, User, AMQP,
|
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
@@ -255,12 +253,21 @@ pub async fn edit(
|
|||||||
.update(
|
.update(
|
||||||
db,
|
db,
|
||||||
partial,
|
partial,
|
||||||
data.remove.into_iter().map(|f| f.into()).collect(),
|
data.remove
|
||||||
|
.into_iter()
|
||||||
|
.map(|f| f.into())
|
||||||
|
.collect(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if channel.voice().is_none() {
|
if channel.voice().is_none() {
|
||||||
delete_voice_channel(voice_client, channel.id(), channel.server()).await?;
|
if let Some(users) = get_voice_channel_members(channel.id()).await? {
|
||||||
|
let node = get_channel_node(channel.id()).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.delete_room(&node, channel.id()).await?;
|
||||||
|
|
||||||
|
delete_channel_voice_state(channel.id(), channel.server(), &users).await?;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(channel.into()))
|
Ok(Json(channel.into()))
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{util::reference::Reference, voice::{delete_voice_state, get_channel_node, is_in_voice_channel, VoiceClient}, Channel, Database, User, AMQP};
|
||||||
util::reference::Reference,
|
|
||||||
voice::{is_in_voice_channel, remove_user_from_voice_channel, VoiceClient},
|
|
||||||
Channel, Database, User, AMQP,
|
|
||||||
};
|
|
||||||
use revolt_permissions::ChannelPermission;
|
use revolt_permissions::ChannelPermission;
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
|
|
||||||
@@ -28,10 +24,7 @@ pub async fn remove_member(
|
|||||||
|
|
||||||
let channel = target.as_channel(db).await?;
|
let channel = target.as_channel(db).await?;
|
||||||
|
|
||||||
if let Channel::Group {
|
if let Channel::Group { owner, recipients, .. } = &channel {
|
||||||
owner, recipients, ..
|
|
||||||
} = &channel
|
|
||||||
{
|
|
||||||
if &user.id != owner {
|
if &user.id != owner {
|
||||||
return Err(create_error!(MissingPermission {
|
return Err(create_error!(MissingPermission {
|
||||||
permission: ChannelPermission::ManageChannel.to_string()
|
permission: ChannelPermission::ManageChannel.to_string()
|
||||||
@@ -51,11 +44,14 @@ pub async fn remove_member(
|
|||||||
.remove_user_from_group(db, amqp, &member, Some(&user.id), false)
|
.remove_user_from_group(db, amqp, &member, Some(&user.id), false)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
return Err(create_error!(InvalidOperation));
|
return Err(create_error!(InvalidOperation))
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_in_voice_channel(&member.id, channel.id()).await? {
|
if is_in_voice_channel(&user.id, channel.id()).await? {
|
||||||
remove_user_from_voice_channel(db, voice_client, channel.id(), &member.id).await?;
|
let node = get_channel_node(channel.id()).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.remove_user(&node, &user.id, channel.id()).await?;
|
||||||
|
delete_voice_state(channel.id(), None, &user.id).await?;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(EmptyResponse)
|
Ok(EmptyResponse)
|
||||||
|
|||||||
@@ -151,20 +151,29 @@ mod test {
|
|||||||
name: "Hidden Channel".to_string(),
|
name: "Hidden Channel".to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
nsfw: Some(false),
|
nsfw: Some(false),
|
||||||
voice: None,
|
voice: None
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to make new channel");
|
.expect("Failed to make new channel");
|
||||||
|
|
||||||
let role = Role::create(&harness.db, &server, "Show Hidden Channel".to_string())
|
let role = Role {
|
||||||
|
name: "Show Hidden Channel".to_string(),
|
||||||
|
permissions: OverrideField { a: 0, d: 0 },
|
||||||
|
colour: None,
|
||||||
|
hoist: false,
|
||||||
|
rank: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
let role_id = role
|
||||||
|
.create(&harness.db, &server.id)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create the role");
|
.expect("Failed to create the role");
|
||||||
|
|
||||||
let mut overrides = HashMap::new();
|
let mut overrides = HashMap::new();
|
||||||
overrides.insert(
|
overrides.insert(
|
||||||
role.id.clone(),
|
role_id.clone(),
|
||||||
OverrideField {
|
OverrideField {
|
||||||
a: (ChannelPermission::ViewChannel) as i64,
|
a: (ChannelPermission::ViewChannel) as i64,
|
||||||
d: 0,
|
d: 0,
|
||||||
@@ -272,7 +281,7 @@ mod test {
|
|||||||
"Mention failed to be scrubbed when the user cannot see the channel"
|
"Mention failed to be scrubbed when the user cannot see the channel"
|
||||||
);
|
);
|
||||||
|
|
||||||
let second_member_roles = vec![role.id.clone()];
|
let second_member_roles = vec![role_id.clone()];
|
||||||
let partial = PartialMember {
|
let partial = PartialMember {
|
||||||
id: None,
|
id: None,
|
||||||
joined_at: None,
|
joined_at: None,
|
||||||
@@ -281,7 +290,7 @@ mod test {
|
|||||||
timeout: None,
|
timeout: None,
|
||||||
roles: Some(second_member_roles),
|
roles: Some(second_member_roles),
|
||||||
can_publish: None,
|
can_publish: None,
|
||||||
can_receive: None,
|
can_receive: None
|
||||||
};
|
};
|
||||||
second_member
|
second_member
|
||||||
.update(&harness.db, partial, vec![])
|
.update(&harness.db, partial, vec![])
|
||||||
@@ -487,7 +496,7 @@ mod test {
|
|||||||
let (_, _, other_user) = harness.new_user().await;
|
let (_, _, other_user) = harness.new_user().await;
|
||||||
let (server, _) = harness.new_server(&user).await;
|
let (server, _) = harness.new_server(&user).await;
|
||||||
let channel = harness.new_channel(&server).await;
|
let channel = harness.new_channel(&server).await;
|
||||||
let role = harness
|
let (role_id, _role) = harness
|
||||||
.new_role(
|
.new_role(
|
||||||
&server,
|
&server,
|
||||||
1,
|
1,
|
||||||
@@ -509,7 +518,7 @@ mod test {
|
|||||||
Some(&harness.amqp),
|
Some(&harness.amqp),
|
||||||
channel.clone(),
|
channel.clone(),
|
||||||
v0::DataMessageSend {
|
v0::DataMessageSend {
|
||||||
content: Some(format!("Mentioning @everyone and role <%{}>", &role.id)),
|
content: Some(format!("Mentioning @everyone and role <%{}>", &role_id)),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
attachments: None,
|
attachments: None,
|
||||||
replies: None,
|
replies: None,
|
||||||
@@ -554,7 +563,7 @@ mod test {
|
|||||||
Some(&harness.amqp),
|
Some(&harness.amqp),
|
||||||
channel.clone(),
|
channel.clone(),
|
||||||
v0::DataMessageSend {
|
v0::DataMessageSend {
|
||||||
content: Some(format!("Mentioning `@everyone` and role `<%{}>`", &role.id)),
|
content: Some(format!("Mentioning `@everyone` and role `<%{}>`", &role_id)),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
attachments: None,
|
attachments: None,
|
||||||
replies: None,
|
replies: None,
|
||||||
@@ -596,7 +605,7 @@ mod test {
|
|||||||
"Role mentions detected when inside codeblock"
|
"Role mentions detected when inside codeblock"
|
||||||
);
|
);
|
||||||
|
|
||||||
other_member.roles.push(role.id.clone());
|
other_member.roles.push(role_id.clone());
|
||||||
harness
|
harness
|
||||||
.db
|
.db
|
||||||
.update_member(
|
.update_member(
|
||||||
@@ -606,10 +615,10 @@ mod test {
|
|||||||
id: None,
|
id: None,
|
||||||
joined_at: None,
|
joined_at: None,
|
||||||
nickname: None,
|
nickname: None,
|
||||||
roles: Some(vec![role.id.clone()]),
|
roles: Some(vec![role_id.clone()]),
|
||||||
timeout: None,
|
timeout: None,
|
||||||
can_publish: None,
|
can_publish: None,
|
||||||
can_receive: None,
|
can_receive: None
|
||||||
},
|
},
|
||||||
vec![],
|
vec![],
|
||||||
)
|
)
|
||||||
@@ -623,7 +632,7 @@ mod test {
|
|||||||
Some(&harness.amqp),
|
Some(&harness.amqp),
|
||||||
channel.clone(),
|
channel.clone(),
|
||||||
v0::DataMessageSend {
|
v0::DataMessageSend {
|
||||||
content: Some(format!("Mentioning @everyone and role <%{}>", &role.id)),
|
content: Some(format!("Mentioning @everyone and role <%{}>", &role_id)),
|
||||||
nonce: None,
|
nonce: None,
|
||||||
attachments: None,
|
attachments: None,
|
||||||
replies: None,
|
replies: None,
|
||||||
|
|||||||
@@ -79,15 +79,15 @@ pub async fn call(
|
|||||||
// should only ever loop once but just to cover our backs.
|
// should only ever loop once but just to cover our backs.
|
||||||
|
|
||||||
for channel_id in get_user_voice_channels(&user.id).await? {
|
for channel_id in get_user_voice_channels(&user.id).await? {
|
||||||
if let Some(node) = get_channel_node(&channel_id).await? {
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
// if this errors its just a mismatching state - ignore and proceed to still delete our state
|
|
||||||
let _ = voice_client.remove_user(&node, &user.id, &channel_id).await;
|
|
||||||
};
|
|
||||||
|
|
||||||
let channel = Reference::from_unchecked(&channel_id)
|
let channel = Reference::from_unchecked(&channel_id)
|
||||||
.as_channel(db)
|
.as_channel(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
voice_client
|
||||||
|
.remove_user(&node, &user.id, &channel_id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
delete_voice_state(&channel_id, channel.server(), &user.id).await?;
|
delete_voice_state(&channel_id, channel.server(), &user.id).await?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -103,7 +103,7 @@ pub async fn call(
|
|||||||
log::debug!("Created room {}", room.name);
|
log::debug!("Created room {}", room.name);
|
||||||
|
|
||||||
if let Some(recipients) = recipients {
|
if let Some(recipients) = recipients {
|
||||||
if room.num_participants == 0 && !recipients.is_empty() {
|
if room.num_participants == 0 {
|
||||||
set_call_notification_recipients(channel.id(), &user.id, &recipients).await?;
|
set_call_notification_recipients(channel.id(), &user.id, &recipients).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ use revolt_database::{Database, User};
|
|||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
use rocket_empty::EmptyResponse;
|
|
||||||
|
|
||||||
/// # Acknowledge Policy Changes
|
/// # Acknowledge Policy Changes
|
||||||
///
|
///
|
||||||
/// Accept/acknowledge changes to platform policy.
|
/// Accept/acknowledge changes to platform policy.
|
||||||
#[openapi(tag = "Policy")]
|
#[openapi(tag = "Policy")]
|
||||||
#[post("/acknowledge")]
|
#[post("/acknowledge")]
|
||||||
pub async fn acknowledge_policy_changes(db: &State<Database>, user: User) -> Result<EmptyResponse> {
|
pub async fn acknowledge_policy_changes(db: &State<Database>, user: User) -> Result<()> {
|
||||||
db.acknowledge_policy_changes(&user.id).await?;
|
db.acknowledge_policy_changes(&user.id).await
|
||||||
Ok(EmptyResponse)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use revolt_database::{events::client::EventV1, Database, Report, Snapshot, SnapshotContent, User};
|
use revolt_database::{events::client::EventV1, Database, Report, Snapshot, SnapshotContent, User};
|
||||||
use revolt_models::v0::{ReportStatus, ReportedContent};
|
use revolt_models::v0::{ReportStatus, ReportedContent};
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
use rocket_empty::EmptyResponse;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
@@ -28,7 +27,7 @@ pub async fn report_content(
|
|||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
user: User,
|
user: User,
|
||||||
data: Json<DataReportContent>,
|
data: Json<DataReportContent>,
|
||||||
) -> Result<EmptyResponse> {
|
) -> Result<()> {
|
||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
data.validate().map_err(|error| {
|
data.validate().map_err(|error| {
|
||||||
create_error!(FailedValidation {
|
create_error!(FailedValidation {
|
||||||
@@ -130,5 +129,5 @@ pub async fn report_content(
|
|||||||
|
|
||||||
EventV1::ReportCreate(report.into()).global().await;
|
EventV1::ReportCreate(report.into()).global().await;
|
||||||
|
|
||||||
Ok(EmptyResponse)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
voice::{get_user_voice_channel_in_server, remove_user_from_voice_channel, VoiceClient},
|
voice::{delete_voice_state, get_channel_node, get_user_voice_channel_in_server, VoiceClient},
|
||||||
Database, RemovalIntention, ServerBan, User,
|
Database, RemovalIntention, ServerBan, User,
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
@@ -58,8 +58,12 @@ pub async fn ban(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// If the member is in a voice channel while banned kick them from the voice channel
|
// If the member is in a voice channel while banned kick them from the voice channel
|
||||||
if let Some(channel_id) = get_user_voice_channel_in_server(&target.id, &server.id).await? {
|
if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? {
|
||||||
remove_user_from_voice_channel(db, voice_client, &channel_id, &target.id).await?;
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
||||||
|
|
||||||
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ pub async fn edit(
|
|||||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
let permissions = calculate_server_permissions(&mut query).await;
|
let permissions = calculate_server_permissions(&mut query).await;
|
||||||
|
|
||||||
// Fetch target permissions
|
|
||||||
let mut target_query = DatabasePermissionQuery::new(db, &target_user).server(&server).member(&member);
|
|
||||||
let target_permissions = calculate_server_permissions(&mut target_query).await;
|
|
||||||
|
|
||||||
// Check permissions in server
|
// Check permissions in server
|
||||||
if data.nickname.is_some() || data.remove.contains(&v0::FieldsMember::Nickname) {
|
if data.nickname.is_some() || data.remove.contains(&v0::FieldsMember::Nickname) {
|
||||||
if user.id == member.id.user {
|
if user.id == member.id.user {
|
||||||
@@ -77,14 +73,8 @@ pub async fn edit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data.timeout.is_some() || data.remove.contains(&v0::FieldsMember::Timeout) {
|
if data.timeout.is_some() || data.remove.contains(&v0::FieldsMember::Timeout) {
|
||||||
if data.timeout.is_some() {
|
if data.timeout.is_some() && member.id.user == user.id {
|
||||||
if member.id.user == user.id {
|
return Err(create_error!(CannotTimeoutYourself));
|
||||||
return Err(create_error!(CannotTimeoutYourself));
|
|
||||||
}
|
|
||||||
|
|
||||||
if target_permissions.has_channel_permission(ChannelPermission::TimeoutMembers) {
|
|
||||||
return Err(create_error!(IsElevated))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::TimeoutMembers)?;
|
permissions.throw_if_lacking_channel_permission(ChannelPermission::TimeoutMembers)?;
|
||||||
@@ -227,11 +217,9 @@ pub async fn edit(
|
|||||||
|
|
||||||
EventV1::UserMoveVoiceChannel {
|
EventV1::UserMoveVoiceChannel {
|
||||||
node: new_node,
|
node: new_node,
|
||||||
from: channel,
|
|
||||||
to: new_voice_channel.id().to_string(),
|
|
||||||
token,
|
token,
|
||||||
}
|
}
|
||||||
.private(target_user.id.clone())
|
.p_user(target_user.id.clone(), db)
|
||||||
.await;
|
.await;
|
||||||
};
|
};
|
||||||
} else if can_publish.is_some() || can_receive.is_some() || remove_contains_voice {
|
} else if can_publish.is_some() || can_receive.is_some() || remove_contains_voice {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
voice::{get_user_voice_channel_in_server, remove_user_from_voice_channel, VoiceClient},
|
voice::{delete_voice_state, get_channel_node, get_user_voice_channel_in_server, VoiceClient},
|
||||||
Database, RemovalIntention, User,
|
Database, RemovalIntention, User,
|
||||||
};
|
};
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
@@ -46,8 +46,12 @@ pub async fn kick(
|
|||||||
.remove(db, &server, RemovalIntention::Kick, false)
|
.remove(db, &server, RemovalIntention::Kick, false)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Some(channel_id) = get_user_voice_channel_in_server(&target.id, &server.id).await? {
|
if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? {
|
||||||
remove_user_from_voice_channel(db, voice_client, &channel_id, &target.id).await?;
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
||||||
|
|
||||||
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?;
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(EmptyResponse)
|
Ok(EmptyResponse)
|
||||||
|
|||||||
@@ -40,10 +40,17 @@ pub async fn create(
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
let role = Role::create(db, &server, data.name).await?;
|
let role = Role {
|
||||||
|
name: data.name,
|
||||||
|
// Rank of the new role should be below the lowest role
|
||||||
|
rank: server.roles.len() as i64,
|
||||||
|
colour: None,
|
||||||
|
hoist: false,
|
||||||
|
permissions: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Json(v0::NewRoleResponse {
|
Ok(Json(v0::NewRoleResponse {
|
||||||
id: role.id.clone(),
|
id: role.create(db, &server.id).await?,
|
||||||
role: role.into(),
|
role: role.into(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ pub async fn delete(
|
|||||||
return Err(create_error!(NotElevated));
|
return Err(create_error!(NotElevated));
|
||||||
}
|
}
|
||||||
|
|
||||||
role.delete(db, &server.id).await?;
|
role.delete(db, &server.id, &role_id).await?;
|
||||||
|
|
||||||
for channel_id in &server.channels {
|
for channel_id in &server.channels {
|
||||||
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ pub async fn edit(
|
|||||||
role.update(
|
role.update(
|
||||||
db,
|
db,
|
||||||
&server.id,
|
&server.id,
|
||||||
|
&role_id,
|
||||||
partial,
|
partial,
|
||||||
remove.into_iter().map(Into::into).collect(),
|
remove.into_iter().map(Into::into).collect(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::reference::Reference,
|
util::reference::Reference,
|
||||||
voice::{
|
voice::{delete_channel_voice_state, delete_voice_state, get_channel_node, get_user_voice_channel_in_server, get_voice_channel_members, VoiceClient},
|
||||||
delete_voice_channel, get_user_voice_channel_in_server, remove_user_from_voice_channel,
|
|
||||||
VoiceClient,
|
|
||||||
},
|
|
||||||
Database, RemovalIntention, User,
|
Database, RemovalIntention, User,
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
@@ -29,13 +26,25 @@ pub async fn delete(
|
|||||||
|
|
||||||
if server.owner == user.id {
|
if server.owner == user.id {
|
||||||
for channel_id in &server.channels {
|
for channel_id in &server.channels {
|
||||||
delete_voice_channel(voice_client, channel_id, Some(&server.id)).await?;
|
if let Some(users) = get_voice_channel_members(channel_id).await? {
|
||||||
|
let node = get_channel_node(channel_id).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.delete_room(&node, channel_id).await?;
|
||||||
|
|
||||||
|
delete_channel_voice_state(channel_id, Some(&server.id), &users).await?;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
server.delete(db).await
|
server.delete(db).await
|
||||||
} else {
|
} else {
|
||||||
if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? {
|
if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? {
|
||||||
remove_user_from_voice_channel(db, voice_client, &channel_id, &user.id).await?;
|
if server.channels.iter().any(|c| c == &channel_id) {
|
||||||
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
|
||||||
|
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
||||||
|
|
||||||
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
member
|
member
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ pub struct GithubComment {
|
|||||||
position: Option<u32>,
|
position: Option<u32>,
|
||||||
reactions: Option<GithubReactions>,
|
reactions: Option<GithubReactions>,
|
||||||
updated_at: Value,
|
updated_at: Value,
|
||||||
|
url: String,
|
||||||
user: GithubUser,
|
user: GithubUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,7 +701,7 @@ fn safe_from_str<T: for<'de> Deserialize<'de>>(data: &str) -> Result<T> {
|
|||||||
match serde_json::from_str(data) {
|
match serde_json::from_str(data) {
|
||||||
Ok(output) => Ok(output),
|
Ok(output) => Ok(output),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
revolt_config::capture_internal_error!(err);
|
log::error!("{err:?}");
|
||||||
Err(create_error!(InvalidOperation))
|
Err(create_error!(InvalidOperation))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -752,12 +753,12 @@ pub async fn webhook_execute_github(
|
|||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
amqp: &State<AMQP>,
|
amqp: &State<AMQP>,
|
||||||
webhook_id: Reference<'_>,
|
webhook_id: Reference<'_>,
|
||||||
token: &str,
|
token: String,
|
||||||
event: EventHeader<'_>,
|
event: EventHeader<'_>,
|
||||||
data: String,
|
data: String,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let webhook = webhook_id.as_webhook(db).await?;
|
let webhook = webhook_id.as_webhook(db).await?;
|
||||||
webhook.assert_token(token)?;
|
webhook.assert_token(&token)?;
|
||||||
|
|
||||||
let channel = db.fetch_channel(&webhook.channel_id).await?;
|
let channel = db.fetch_channel(&webhook.channel_id).await?;
|
||||||
let event = convert_event(&data, &event)?;
|
let event = convert_event(&data, &event)?;
|
||||||
@@ -896,11 +897,10 @@ pub async fn webhook_execute_github(
|
|||||||
url: Some(event.sender.html_url),
|
url: Some(event.sender.html_url),
|
||||||
title: Some(event.sender.login),
|
title: Some(event.sender.login),
|
||||||
description: Some(format!(
|
description: Some(format!(
|
||||||
"#### [[{}] New discussion #{}: {}]({})\n{}",
|
"#### [{}] New discussion #{}: {}\n{}",
|
||||||
event.repository.full_name,
|
event.repository.full_name,
|
||||||
discussion.number,
|
discussion.number,
|
||||||
discussion.title,
|
discussion.title,
|
||||||
discussion.html_url,
|
|
||||||
shorten_text(&discussion.body, 450)
|
shorten_text(&discussion.body, 450)
|
||||||
)),
|
)),
|
||||||
colour: Some(LIGHT_ORANGE.to_string()),
|
colour: Some(LIGHT_ORANGE.to_string()),
|
||||||
@@ -911,11 +911,10 @@ pub async fn webhook_execute_github(
|
|||||||
url: Some(answer.comment.user.html_url),
|
url: Some(answer.comment.user.html_url),
|
||||||
title: Some(answer.comment.user.login),
|
title: Some(answer.comment.user.login),
|
||||||
description: Some(format!(
|
description: Some(format!(
|
||||||
"#### [[{}] Discussion #{} marked answered: {}]({})\n{}",
|
"#### [{}] discussion #{} marked answered: {}\n{}",
|
||||||
event.repository.full_name,
|
event.repository.full_name,
|
||||||
discussion.number,
|
discussion.number,
|
||||||
discussion.title,
|
discussion.title,
|
||||||
answer.comment.html_url,
|
|
||||||
shorten_text(&answer.comment.body, 450)
|
shorten_text(&answer.comment.body, 450)
|
||||||
)),
|
)),
|
||||||
colour: Some(LIGHT_ORANGE.to_string()),
|
colour: Some(LIGHT_ORANGE.to_string()),
|
||||||
@@ -931,11 +930,10 @@ pub async fn webhook_execute_github(
|
|||||||
url: Some(comment.comment.user.html_url),
|
url: Some(comment.comment.user.html_url),
|
||||||
title: Some(comment.comment.user.login),
|
title: Some(comment.comment.user.login),
|
||||||
description: Some(format!(
|
description: Some(format!(
|
||||||
"#### [[{}] New comment on discussion #{}: {}]({})\n{}",
|
"[{}] New comment on discussion #{}: {}\n{}",
|
||||||
event.repository.full_name,
|
event.repository.full_name,
|
||||||
discussion.number,
|
discussion.number,
|
||||||
discussion.title,
|
discussion.title,
|
||||||
comment.comment.html_url,
|
|
||||||
shorten_text(&comment.comment.body, 450)
|
shorten_text(&comment.comment.body, 450)
|
||||||
)),
|
)),
|
||||||
colour: Some(LIGHT_ORANGE.to_string()),
|
colour: Some(LIGHT_ORANGE.to_string()),
|
||||||
@@ -1004,7 +1002,7 @@ pub async fn webhook_execute_github(
|
|||||||
event.repository.full_name,
|
event.repository.full_name,
|
||||||
issue.number,
|
issue.number,
|
||||||
issue.title,
|
issue.title,
|
||||||
comment.html_url,
|
issue.html_url,
|
||||||
shorten_text(&comment.body, 450)
|
shorten_text(&comment.body, 450)
|
||||||
)),
|
)),
|
||||||
colour: Some(LIGHT_ORANGE.to_string()),
|
colour: Some(LIGHT_ORANGE.to_string()),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use futures::StreamExt;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use redis_kiss::redis::aio::PubSub;
|
use redis_kiss::redis::aio::PubSub;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
events::client::EventV1, Channel, Database, Member, Message, PartialRole, Server, User, AMQP,
|
events::client::EventV1, Channel, Database, Member, Message, Server, User, AMQP,
|
||||||
};
|
};
|
||||||
use revolt_database::{util::idempotency::IdempotencyKey, Role};
|
use revolt_database::{util::idempotency::IdempotencyKey, Role};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
@@ -139,26 +139,21 @@ impl TestHarness {
|
|||||||
server: &Server,
|
server: &Server,
|
||||||
rank: i64,
|
rank: i64,
|
||||||
overrides: Option<OverrideField>,
|
overrides: Option<OverrideField>,
|
||||||
) -> Role {
|
) -> (String, Role) {
|
||||||
let mut role = Role::create(&self.db, &server, TestHarness::rand_string())
|
let role = Role {
|
||||||
|
name: TestHarness::rand_string(),
|
||||||
|
permissions: overrides.unwrap_or(OverrideField { a: 0, d: 0 }),
|
||||||
|
rank,
|
||||||
|
colour: None,
|
||||||
|
hoist: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = role
|
||||||
|
.create(&self.db, &server.id)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create test role");
|
.expect("Failed to create test role");
|
||||||
|
|
||||||
if let Some(overrides) = overrides {
|
(id, role)
|
||||||
role.update(
|
|
||||||
&self.db,
|
|
||||||
&server.id,
|
|
||||||
PartialRole {
|
|
||||||
permissions: Some(overrides),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.expect("Failed to set test role overrides");
|
|
||||||
};
|
|
||||||
|
|
||||||
role
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_channel(&self, server: &Server) -> Channel {
|
pub async fn new_channel(&self, server: &Server) -> Channel {
|
||||||
@@ -170,7 +165,7 @@ impl TestHarness {
|
|||||||
name: "Test Channel".to_string(),
|
name: "Test Channel".to_string(),
|
||||||
description: None,
|
description: None,
|
||||||
nsfw: Some(false),
|
nsfw: Some(false),
|
||||||
voice: None,
|
voice: None
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-autumn"
|
name = "revolt-autumn"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
@@ -43,16 +43,16 @@ tracing = "0.1"
|
|||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
# Core crates
|
# Core crates
|
||||||
revolt-files = { version = "0.9.1", path = "../../core/files" }
|
revolt-files = { version = "0.8.9", path = "../../core/files" }
|
||||||
revolt-config = { version = "0.9.1", path = "../../core/config" }
|
revolt-config = { version = "0.8.9", path = "../../core/config" }
|
||||||
revolt-database = { version = "0.9.1", path = "../../core/database", features = [
|
revolt-database = { version = "0.8.9", path = "../../core/database", features = [
|
||||||
"axum-impl",
|
"axum-impl",
|
||||||
] }
|
] }
|
||||||
revolt-result = { version = "0.9.1", path = "../../core/result", features = [
|
revolt-result = { version = "0.8.9", path = "../../core/result", features = [
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"axum",
|
"axum",
|
||||||
] }
|
] }
|
||||||
revolt-ratelimits = { version = "0.9.1", path = "../../core/ratelimits", features = ["axum"] }
|
revolt-ratelimits = { version = "0.8.9", path = "../../core/ratelimits", features = ["axum"] }
|
||||||
|
|
||||||
# Axum / web server
|
# Axum / web server
|
||||||
tempfile = "3.12.0"
|
tempfile = "3.12.0"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-gifbox"
|
name = "revolt-gifbox"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
@@ -16,19 +16,19 @@ tokio = { version = "1.0", features = ["full"] }
|
|||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
|
|
||||||
# Core crates
|
# Core crates
|
||||||
revolt-config = { version = "0.9.1", path = "../../core/config" }
|
revolt-config = { version = "0.8.9", path = "../../core/config" }
|
||||||
revolt-models = { version = "0.9.1", path = "../../core/models" }
|
revolt-models = { version = "0.8.9", path = "../../core/models" }
|
||||||
revolt-result = { version = "0.9.1", path = "../../core/result", features = [
|
revolt-result = { version = "0.8.9", path = "../../core/result", features = [
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"axum",
|
"axum",
|
||||||
] }
|
] }
|
||||||
revolt-coalesced = { version = "0.9.1", path = "../../core/coalesced", features = [
|
revolt-coalesced = { version = "0.8.9", path = "../../core/coalesced", features = [
|
||||||
"queue",
|
"queue",
|
||||||
] }
|
] }
|
||||||
revolt-database = { version = "0.9.1", path = "../../core/database", features = [
|
revolt-database = { version = "0.8.9", path = "../../core/database", features = [
|
||||||
"axum-impl",
|
"axum-impl",
|
||||||
] }
|
] }
|
||||||
revolt-ratelimits = { version = "0.9.1", path = "../../core/ratelimits", features = [
|
revolt-ratelimits = { version = "0.8.9", path = "../../core/ratelimits", features = [
|
||||||
"axum",
|
"axum",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
FROM gcr.io/distroless/cc-debian12:nonroot
|
FROM gcr.io/distroless/cc-debian12:nonroot
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-january"
|
name = "revolt-january"
|
||||||
version = "0.9.1"
|
version = "0.8.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
||||||
@@ -32,13 +32,13 @@ tracing = "0.1"
|
|||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
# Core crates
|
# Core crates
|
||||||
revolt-config = { version = "0.9.1", path = "../../core/config" }
|
revolt-config = { version = "0.8.9", path = "../../core/config" }
|
||||||
revolt-models = { version = "0.9.1", path = "../../core/models" }
|
revolt-models = { version = "0.8.9", path = "../../core/models" }
|
||||||
revolt-result = { version = "0.9.1", path = "../../core/result", features = [
|
revolt-result = { version = "0.8.9", path = "../../core/result", features = [
|
||||||
"utoipa",
|
"utoipa",
|
||||||
"axum",
|
"axum",
|
||||||
] }
|
] }
|
||||||
revolt-files = { version = "0.9.1", path = "../../core/files" }
|
revolt-files = { version = "0.8.9", path = "../../core/files" }
|
||||||
|
|
||||||
# Axum / web server
|
# Axum / web server
|
||||||
axum = { version = "0.7.5" }
|
axum = { version = "0.7.5" }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/stoatchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
FROM debian:12 AS debian
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
book
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[book]
|
||||||
|
authors = []
|
||||||
|
language = "en"
|
||||||
|
multilingual = false
|
||||||
|
src = "src"
|
||||||
|
title = "Revolt Backend"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user