kopia lustrzana https://codeberg.org/nmkj/audon
Porównaj commity
19 Commity
Autor | SHA1 | Data |
---|---|---|
Namekuji | 7adba191e6 | |
Namekuji | 3cc4ee4f84 | |
Namekuji | a606f7f4fb | |
Namekuji | 1bb384a10c | |
Namekuji | 0fc4e5fbec | |
Namekuji | 30cc7930fc | |
Namekuji | 6c4ef49085 | |
Namekuji | 85979a7ebf | |
Namekuji | 6f7b58f2e2 | |
Namekuji | 03e5cb4f02 | |
Namekuji | 87300ea112 | |
Namekuji | 7b837651dd | |
Namekuji | c40fb71147 | |
Namekuji | d52ae18c44 | |
Namekuji | 3138e2a596 | |
Namekuji | 80963c96e6 | |
Namekuji | 32dbfbc205 | |
Namekuji | e2a5fb6fd6 | |
Namekuji | f11d4f016a |
|
@ -16,7 +16,7 @@ audon.localhost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
livekit.audon.localhost {
|
livekit.localhost {
|
||||||
tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem
|
tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem
|
||||||
encode zstd gzip
|
encode zstd gzip
|
||||||
reverse_proxy livekit:7880
|
reverse_proxy livekit:7880
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// "image": "mcr.microsoft.com/devcontainers/universal:2-linux"
|
// "image": "mcr.microsoft.com/devcontainers/universal:2-linux"
|
||||||
"dockerComposeFile": "docker-compose.dev.yaml",
|
"dockerComposeFile": "docker-compose.dev.yaml",
|
||||||
"service": "devcontainer",
|
"service": "devcontainer",
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/audon-go",
|
||||||
|
|
||||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
"features": {
|
"features": {
|
||||||
|
@ -33,8 +33,8 @@
|
||||||
"EditorConfig.EditorConfig"
|
"EditorConfig.EditorConfig"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
// "remoteUser": "root"
|
"remoteUser": "root"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ version: '3.1'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
devcontainer:
|
devcontainer:
|
||||||
image: "mcr.microsoft.com/devcontainers/base:jammy"
|
image: "mcr.microsoft.com/devcontainers/base:debian"
|
||||||
volumes:
|
volumes:
|
||||||
- ../..:/workspaces:cached
|
- ..:/audon-go:cached
|
||||||
command: sleep infinity
|
command: sleep infinity
|
||||||
|
environment:
|
||||||
|
- "DOCKER_HOST=unix:///run/user/1000/docker.sock"
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: mongo:6
|
image: mongo:6
|
||||||
|
|
|
@ -66,6 +66,7 @@ build/Release
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
|
.pnpm-store/
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
web_modules/
|
web_modules/
|
||||||
|
|
|
@ -4,8 +4,9 @@ WORKDIR /workspace
|
||||||
|
|
||||||
COPY audon-fe/ /workspace/
|
COPY audon-fe/ /workspace/
|
||||||
|
|
||||||
RUN npm install && \
|
RUN npm install -g pnpm && \
|
||||||
npm run build
|
pnpm install && \
|
||||||
|
pnpm run build
|
||||||
|
|
||||||
FROM golang:1.19-bullseye
|
FROM golang:1.19-bullseye
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,19 @@
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
This repository is archived and will no longer be updated. Successor: [Hamabē](https://codeberg.org/hamabe/hamabe)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
<div align="right">
|
<div align="right">
|
||||||
<img src="audon-fe/src/assets/img/mascot.webp" alt="Mascot" width="150" align="right" title="Mascot designed by Taiyo Fujii" />
|
<img src="audon-fe/src/assets/img/mascot.webp" alt="Mascot" width="150" align="right" title="Mascot designed by Taiyo Fujii" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Audio + Mastodon = Audon
|
Audio + Mastodon = Audon
|
||||||
|
|
||||||
Audon is a service of realtime audio streaming for Mastodon.
|
Audon is a service of realtime audio chat for Mastodon, Akkoma, GoToSocial, and Calckey.
|
||||||
|
|
||||||
|
Other Fediverse platforms supporting Mastodon API may work, but not tested (yet).
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
{% end %}
|
{% end %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app" data-version='0.3.0'></div>
|
<div id="app" data-version='0.3.2'></div>
|
||||||
<script type="module" src="/src/main.js"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "audon-fe",
|
"name": "audon-fe",
|
||||||
"version": "0.3.0",
|
"version": "0.3.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cp -v index.dev.html index.html && vite",
|
"dev": "cp -v index.dev.html index.html && vite",
|
||||||
|
@ -9,34 +9,35 @@
|
||||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.2",
|
||||||
"@picmo/popup-picker": "^5.7.2",
|
"@picmo/popup-picker": "^5.7.6",
|
||||||
|
"@picmo/renderer-twemoji": "^5.7.6",
|
||||||
"@uriopass/nosleep.js": "^0.12.2",
|
"@uriopass/nosleep.js": "^0.12.2",
|
||||||
"@vuelidate/core": "^2.0.0",
|
"@vuelidate/core": "^2.0.0",
|
||||||
"@vuelidate/validators": "^2.0.0",
|
"@vuelidate/validators": "^2.0.0",
|
||||||
"@vueuse/core": "^9.6.0",
|
"@vueuse/core": "^9.13.0",
|
||||||
"axios": "^1.2.0",
|
"axios": "^1.3.3",
|
||||||
"howler": "^2.2.3",
|
"howler": "^2.2.3",
|
||||||
"livekit-client": "^1.6.0",
|
"livekit-client": "^1.6.5",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"luxon": "^3.1.1",
|
"luxon": "^3.2.1",
|
||||||
"masto": "^5.6.0",
|
"masto": "^5.10.0",
|
||||||
"picmo": "^5.7.2",
|
"picmo": "^5.7.6",
|
||||||
"pinia": "^2.0.26",
|
"pinia": "^2.0.31",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.47",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vuetify": "^3.0.3"
|
"vuetify": "^3.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mdi/js": "^7.0.96",
|
"@mdi/js": "^7.1.96",
|
||||||
"@rushstack/eslint-patch": "^1.1.4",
|
"@rushstack/eslint-patch": "^1.2.0",
|
||||||
"@vitejs/plugin-vue": "^3.2.0",
|
"@vitejs/plugin-vue": "^3.2.0",
|
||||||
"@vue/eslint-config-prettier": "^7.0.0",
|
"@vue/eslint-config-prettier": "^7.0.0",
|
||||||
"eslint": "^8.22.0",
|
"eslint": "^8.34.0",
|
||||||
"eslint-plugin-vue": "^9.3.0",
|
"eslint-plugin-vue": "^9.9.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.8.4",
|
||||||
"vite": "^3.2.4",
|
"vite": "^3.2.5",
|
||||||
"vite-plugin-vuetify": "^1.0.0"
|
"vite-plugin-vuetify": "^1.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -28,7 +28,8 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
uploadEnabled() {
|
uploadEnabled() {
|
||||||
return this.roomToken?.original && this.roomToken?.indicator;
|
// return this.roomToken?.original && this.roomToken?.indicator;
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
|
@ -77,7 +77,9 @@ export default {
|
||||||
scrim="#000000"
|
scrim="#000000"
|
||||||
class="align-center justify-center reaction"
|
class="align-center justify-center reaction"
|
||||||
>
|
>
|
||||||
<span>{{ emoji }}</span>
|
<div class="d-flex align-center justify-center">
|
||||||
|
<img class="emoji" :src="emoji" />
|
||||||
|
</div>
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
<v-img
|
<v-img
|
||||||
:class="{ cursorPointer: enableMenu }"
|
:class="{ cursorPointer: enableMenu }"
|
||||||
|
@ -100,7 +102,9 @@ export default {
|
||||||
scrim="#000000"
|
scrim="#000000"
|
||||||
class="align-center justify-center reaction"
|
class="align-center justify-center reaction"
|
||||||
>
|
>
|
||||||
<span>{{ emoji }}</span>
|
<div class="d-flex align-center justify-center">
|
||||||
|
<img class="emoji" :src="emoji" />
|
||||||
|
</div>
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
<v-img
|
<v-img
|
||||||
:class="{ cursorPointer: enableMenu }"
|
:class="{ cursorPointer: enableMenu }"
|
||||||
|
@ -147,10 +151,8 @@ export default {
|
||||||
outline: 3px solid cornflowerblue;
|
outline: 3px solid cornflowerblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reaction span {
|
.reaction img {
|
||||||
font-size: 2rem;
|
height: 2rem;
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cursorPointer {
|
.cursorPointer {
|
||||||
|
|
|
@ -17,6 +17,21 @@ enterRoom: "Enter"
|
||||||
leaveRoom: "Leave but keep this room open"
|
leaveRoom: "Leave but keep this room open"
|
||||||
closeRoom: "Close this room"
|
closeRoom: "Close this room"
|
||||||
close: "Close"
|
close: "Close"
|
||||||
|
emojiReaction: "Open emoji reaction picker"
|
||||||
|
micStatus:
|
||||||
|
mute: "Mute microphone"
|
||||||
|
unmute: "Unmute microphone"
|
||||||
|
retry: "Retry enabling microphone"
|
||||||
|
request: "Send speaker request"
|
||||||
|
roomOperation:
|
||||||
|
operation: "Leave or close"
|
||||||
|
leave: "Leave this room"
|
||||||
|
close: "Close this room"
|
||||||
|
openRequests: "Open list of speaker requests"
|
||||||
|
edit: "Edit room information"
|
||||||
|
requestOperation:
|
||||||
|
decline: "Decline this speaker request"
|
||||||
|
accept: "Accept this speaker request"
|
||||||
connecting: "Connecting"
|
connecting: "Connecting"
|
||||||
server: "Your Mastodon instance"
|
server: "Your Mastodon instance"
|
||||||
addressRequired: "Enter your instance address"
|
addressRequired: "Enter your instance address"
|
||||||
|
@ -40,9 +55,9 @@ form:
|
||||||
advertise: "Allow the bot ({bot}) to advertise your room"
|
advertise: "Allow the bot ({bot}) to advertise your room"
|
||||||
relationships:
|
relationships:
|
||||||
everyone: "Everyone"
|
everyone: "Everyone"
|
||||||
following: "Followed accounts only"
|
following: "Followees only (Accounts you're following)"
|
||||||
follower: "Followers-only"
|
follower: "Followers only (Accounts following you)"
|
||||||
knowing: "Followed accounts and/or followers"
|
knowing: "Followees and/or followers"
|
||||||
mutual: "Your mutuals"
|
mutual: "Your mutuals"
|
||||||
private: "CoHosts only"
|
private: "CoHosts only"
|
||||||
shareRoomMessage: "Join my Audon room!\n{link}\n\nTitle: {title}"
|
shareRoomMessage: "Join my Audon room!\n{link}\n\nTitle: {title}"
|
||||||
|
|
|
@ -17,6 +17,18 @@ enterRoom: "入室"
|
||||||
leaveRoom: "部屋を閉じずに退室"
|
leaveRoom: "部屋を閉じずに退室"
|
||||||
closeRoom: "部屋を閉じる"
|
closeRoom: "部屋を閉じる"
|
||||||
close: "閉じる"
|
close: "閉じる"
|
||||||
|
emojiReaction: "絵文字ピッカーを開く"
|
||||||
|
micStatus:
|
||||||
|
mute: "マイクをミュート"
|
||||||
|
unmute: "マイクのミュートを解除"
|
||||||
|
retry: "マイク有効化を再試行"
|
||||||
|
request: "発言リクエストを送る"
|
||||||
|
roomOperation:
|
||||||
|
operation: "退室または閉室"
|
||||||
|
leave: "部屋から退室する"
|
||||||
|
close: "部屋を閉室する"
|
||||||
|
openRequests: "発言リクエストの一覧を開く"
|
||||||
|
edit: "部屋の情報を編集する"
|
||||||
connecting: "接続中"
|
connecting: "接続中"
|
||||||
server: "Mastodon サーバー"
|
server: "Mastodon サーバー"
|
||||||
addressRequired: "アドレスを入力してください"
|
addressRequired: "アドレスを入力してください"
|
||||||
|
|
|
@ -47,11 +47,14 @@ export const useMastodonStore = defineStore("mastodon", {
|
||||||
this.authorized = true;
|
this.authorized = true;
|
||||||
},
|
},
|
||||||
async updateAvatar(img, filename) {
|
async updateAvatar(img, filename) {
|
||||||
|
return;
|
||||||
|
/*
|
||||||
if (this.client === null) return;
|
if (this.client === null) return;
|
||||||
const avatarBlob = await (await fetch(img)).blob();
|
const avatarBlob = await (await fetch(img)).blob();
|
||||||
this.userinfo = await this.client.v1.accounts.updateCredentials({
|
this.userinfo = await this.client.v1.accounts.updateCredentials({
|
||||||
avatar: new File([avatarBlob], `${Date.now()}_${filename}`),
|
avatar: new File([avatarBlob], `${Date.now()}_${filename}`),
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
async revertAvatar() {
|
async revertAvatar() {
|
||||||
const token = await axios.get("/api/token");
|
const token = await axios.get("/api/token");
|
||||||
|
|
|
@ -381,10 +381,8 @@ export default {
|
||||||
<template v-slot:label>
|
<template v-slot:label>
|
||||||
<i18n-t keypath="form.advertise" tag="div">
|
<i18n-t keypath="form.advertise" tag="div">
|
||||||
<template v-slot:bot>
|
<template v-slot:bot>
|
||||||
<a
|
<a href="https://i.audon.space/@now" target="_blank"
|
||||||
href="https://akkoma.audon.space/users/now"
|
>now@i.audon.space</a
|
||||||
target="_blank"
|
|
||||||
>now@audon.space</a
|
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import axios from "axios";
|
||||||
import { pushNotFound, webfinger } from "../assets/utils";
|
import { pushNotFound, webfinger } from "../assets/utils";
|
||||||
import { useMastodonStore } from "../stores/mastodon";
|
import { useMastodonStore } from "../stores/mastodon";
|
||||||
import { map, some, omit, filter, trim, clone, differenceBy } from "lodash-es";
|
import { map, some, omit, filter, trim, clone, differenceBy } from "lodash-es";
|
||||||
import { darkTheme } from "picmo";
|
import { darkTheme, NativeRenderer } from "picmo";
|
||||||
import { createPopup } from "@picmo/popup-picker";
|
import { createPopup } from "@picmo/popup-picker";
|
||||||
import { Howl } from "howler";
|
import { Howl } from "howler";
|
||||||
import Participant from "../components/Participant.vue";
|
import Participant from "../components/Participant.vue";
|
||||||
|
@ -76,7 +76,7 @@ export default {
|
||||||
sounds: {
|
sounds: {
|
||||||
boop: new Howl({
|
boop: new Howl({
|
||||||
src: [boopSound],
|
src: [boopSound],
|
||||||
volume: 0.7,
|
volume: 0.5,
|
||||||
}),
|
}),
|
||||||
message: new Howl({
|
message: new Howl({
|
||||||
src: [messageSound],
|
src: [messageSound],
|
||||||
|
@ -84,7 +84,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
request: new Howl({
|
request: new Howl({
|
||||||
src: [requestSound],
|
src: [requestSound],
|
||||||
volume: 0.7,
|
volume: 0.5,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -256,6 +256,18 @@ export default {
|
||||||
}
|
}
|
||||||
return mdiMicrophone;
|
return mdiMicrophone;
|
||||||
},
|
},
|
||||||
|
micStatusLabel() {
|
||||||
|
if (!(this.iamHost || this.iamCohost || this.iamSpeaker)) {
|
||||||
|
return this.$t("micStatus.request");
|
||||||
|
}
|
||||||
|
if (!this.micGranted) {
|
||||||
|
return this.$t("micStatus.retry");
|
||||||
|
}
|
||||||
|
if (this.iamMuted) {
|
||||||
|
return this.$t("micStatus.unmute");
|
||||||
|
}
|
||||||
|
return this.$t("micStatus.mute");
|
||||||
|
},
|
||||||
titleErrors() {
|
titleErrors() {
|
||||||
const errors = this.v$.editingRoomInfo.title.$errors;
|
const errors = this.v$.editingRoomInfo.title.$errors;
|
||||||
const messages = map(errors, (e) => e.$message);
|
const messages = map(errors, (e) => e.$message);
|
||||||
|
@ -544,6 +556,7 @@ export default {
|
||||||
emojiSize: "1.8rem",
|
emojiSize: "1.8rem",
|
||||||
autoFocus: "none",
|
autoFocus: "none",
|
||||||
showPreview: false,
|
showPreview: false,
|
||||||
|
renderer: new NativeRenderer(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
referenceElement: btn,
|
referenceElement: btn,
|
||||||
|
@ -553,8 +566,8 @@ export default {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const self = this;
|
const self = this;
|
||||||
picker.addEventListener("emoji:select", ({ emoji }) => {
|
picker.addEventListener("emoji:select", ({ url }) => {
|
||||||
self.onEmojiSelected(emoji);
|
self.onEmojiSelected(url);
|
||||||
});
|
});
|
||||||
this.emojiPicker = picker;
|
this.emojiPicker = picker;
|
||||||
}
|
}
|
||||||
|
@ -795,6 +808,7 @@ export default {
|
||||||
:icon="mdiCheck"
|
:icon="mdiCheck"
|
||||||
:disabled="isRequestLoading"
|
:disabled="isRequestLoading"
|
||||||
@click.once="onModerate(id, 'speaker')"
|
@click.once="onModerate(id, 'speaker')"
|
||||||
|
:aria-label="$t('requestOperation.accept')"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -802,6 +816,7 @@ export default {
|
||||||
:icon="mdiClose"
|
:icon="mdiClose"
|
||||||
:disabled="isRequestLoading"
|
:disabled="isRequestLoading"
|
||||||
@click="onDeclineRequest(id)"
|
@click="onDeclineRequest(id)"
|
||||||
|
:aria-label="$t('requestOperation.decline')"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-subtitle>
|
<v-list-item-subtitle>
|
||||||
|
@ -837,6 +852,7 @@ export default {
|
||||||
@click="showRequestedNotification = false"
|
@click="showRequestedNotification = false"
|
||||||
:icon="mdiClose"
|
:icon="mdiClose"
|
||||||
size="small"
|
size="small"
|
||||||
|
:aria-label="$t('close')"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-snackbar>
|
</v-snackbar>
|
||||||
|
@ -861,6 +877,7 @@ export default {
|
||||||
@click="showRequestNotification = false"
|
@click="showRequestNotification = false"
|
||||||
:icon="mdiClose"
|
:icon="mdiClose"
|
||||||
size="small"
|
size="small"
|
||||||
|
:aria-label="$('close')"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-snackbar>
|
</v-snackbar>
|
||||||
|
@ -877,6 +894,7 @@ export default {
|
||||||
size="small"
|
size="small"
|
||||||
variant="text"
|
variant="text"
|
||||||
color="white"
|
color="white"
|
||||||
|
:aria-label="$t('roomOperation.edit')"
|
||||||
:icon="mdiPencil"
|
:icon="mdiPencil"
|
||||||
@click="showEditDialog = true"
|
@click="showEditDialog = true"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
|
@ -953,6 +971,7 @@ export default {
|
||||||
<v-card-actions v-else class="justify-center" style="gap: 20px">
|
<v-card-actions v-else class="justify-center" style="gap: 20px">
|
||||||
<v-btn
|
<v-btn
|
||||||
:icon="mdiEmoticon"
|
:icon="mdiEmoticon"
|
||||||
|
:aria-label="$t('emojiReaction')"
|
||||||
color="white"
|
color="white"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@click="onPickerPopup"
|
@click="onPickerPopup"
|
||||||
|
@ -961,6 +980,7 @@ export default {
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn
|
<v-btn
|
||||||
:icon="micStatusIcon"
|
:icon="micStatusIcon"
|
||||||
|
:aria-label="micStatusLabel"
|
||||||
color="white"
|
color="white"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@click="onToggleMute"
|
@click="onToggleMute"
|
||||||
|
@ -970,6 +990,7 @@ export default {
|
||||||
<v-btn
|
<v-btn
|
||||||
:icon="mdiLogout"
|
:icon="mdiLogout"
|
||||||
color="red"
|
color="red"
|
||||||
|
:aria-label="$t('roomOperation.operation')"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
v-bind="props"
|
v-bind="props"
|
||||||
|
@ -978,6 +999,7 @@ export default {
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
:title="$t('closeRoom')"
|
:title="$t('closeRoom')"
|
||||||
|
:aria-label="$t('roomOperation.close')"
|
||||||
:prepend-icon="mdiCloseBoxOutline"
|
:prepend-icon="mdiCloseBoxOutline"
|
||||||
@click="onRoomClose"
|
@click="onRoomClose"
|
||||||
class="text-red"
|
class="text-red"
|
||||||
|
@ -985,6 +1007,7 @@ export default {
|
||||||
<v-list-item
|
<v-list-item
|
||||||
:disabled="isLastHost"
|
:disabled="isLastHost"
|
||||||
:title="$t('leaveRoom')"
|
:title="$t('leaveRoom')"
|
||||||
|
:aria-label="$t('roomOperation.leave')"
|
||||||
:prepend-icon="mdiExitRun"
|
:prepend-icon="mdiExitRun"
|
||||||
@click="onLeave"
|
@click="onLeave"
|
||||||
>
|
>
|
||||||
|
@ -996,6 +1019,7 @@ export default {
|
||||||
:icon="mdiLogout"
|
:icon="mdiLogout"
|
||||||
color="red"
|
color="red"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
|
:aria-label="$t('roomOperation.leave')"
|
||||||
@click="onLeave"
|
@click="onLeave"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
></v-btn>
|
></v-btn>
|
||||||
|
@ -1007,6 +1031,7 @@ export default {
|
||||||
>
|
>
|
||||||
<v-btn
|
<v-btn
|
||||||
:icon="mdiAccountVoice"
|
:icon="mdiAccountVoice"
|
||||||
|
:aria-label="$t('roomOperation.openRequests')"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
color="white"
|
color="white"
|
||||||
@click="
|
@click="
|
||||||
|
|
21
auth.go
21
auth.go
|
@ -66,7 +66,7 @@ func loginHandler(c echo.Context) (err error) {
|
||||||
req.Redirect = "/"
|
req.Redirect = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
appConfig, err := getAppConfig(serverURL.String(), req.Redirect)
|
appConfig, err := getAppConfig(serverURL.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrInvalidRequestFormat
|
return ErrInvalidRequestFormat
|
||||||
}
|
}
|
||||||
|
@ -89,15 +89,24 @@ func loginHandler(c echo.Context) (err error) {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusCreated, mastApp.AuthURI)
|
redirURL, err := url.Parse(mastApp.AuthURI)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Warn(err)
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, "invalid_auth_uri")
|
||||||
|
}
|
||||||
|
q := redirURL.Query()
|
||||||
|
q.Add("state", req.Redirect)
|
||||||
|
redirURL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
return c.String(http.StatusCreated, redirURL.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.NoContent(http.StatusNoContent)
|
return c.NoContent(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuthRequest struct {
|
type OAuthRequest struct {
|
||||||
Code string `query:"code"`
|
Code string `query:"code"`
|
||||||
Redirect string `query:"redir"`
|
State string `query:"state"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// handler for GET to /app/oauth?code=****
|
// handler for GET to /app/oauth?code=****
|
||||||
|
@ -122,7 +131,7 @@ func oauthHandler(c echo.Context) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
appConf, err := getAppConfig(data.MastodonConfig.Server, req.Redirect)
|
appConf, err := getAppConfig(data.MastodonConfig.Server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -178,7 +187,7 @@ func oauthHandler(c echo.Context) (err error) {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusFound, req.Redirect)
|
return c.Redirect(http.StatusFound, req.State)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserTokenHandler(c echo.Context) (err error) {
|
func getUserTokenHandler(c echo.Context) (err error) {
|
||||||
|
|
|
@ -93,10 +93,10 @@ func (u *AudonUser) GetIndicator(ctx context.Context, fnew []byte, room *Room) (
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
indicator, err = u.createGIF(newImg, room.IsHost(u) || room.IsCoHost(u))
|
// indicator, err = u.createGIF(newImg, room.IsHost(u) || room.IsCoHost(u))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
return indicator, origImg, isGIF, nil
|
return indicator, origImg, isGIF, nil
|
||||||
}
|
}
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -3,10 +3,12 @@ module audon
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.1
|
||||||
github.com/go-playground/validator/v10 v10.11.1
|
github.com/go-playground/validator/v10 v10.11.1
|
||||||
github.com/go-redis/redis/v9 v9.0.0-rc.2
|
github.com/go-redis/redis/v9 v9.0.0-rc.2
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/jaevor/go-nanoid v1.3.0
|
github.com/jaevor/go-nanoid v1.3.0
|
||||||
|
github.com/jellydator/ttlcache/v3 v3.0.1
|
||||||
github.com/joho/godotenv v1.4.0
|
github.com/joho/godotenv v1.4.0
|
||||||
github.com/labstack/echo-contrib v0.13.0
|
github.com/labstack/echo-contrib v0.13.0
|
||||||
github.com/labstack/echo/v4 v4.9.1
|
github.com/labstack/echo/v4 v4.9.1
|
||||||
|
@ -33,7 +35,6 @@ require (
|
||||||
github.com/eapache/channels v1.1.0 // indirect
|
github.com/eapache/channels v1.1.0 // indirect
|
||||||
github.com/eapache/queue v1.1.0 // indirect
|
github.com/eapache/queue v1.1.0 // indirect
|
||||||
github.com/frostbyte73/go-throttle v0.0.0-20210621200530-8018c891361d // indirect
|
github.com/frostbyte73/go-throttle v0.0.0-20210621200530-8018c891361d // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.1 // indirect
|
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-logr/zapr v1.2.3 // indirect
|
github.com/go-logr/zapr v1.2.3 // indirect
|
||||||
|
@ -47,7 +48,6 @@ require (
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
github.com/gorilla/context v1.1.1 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/jellydator/ttlcache/v3 v3.0.1 // indirect
|
|
||||||
github.com/jxskiss/base62 v1.1.0 // indirect
|
github.com/jxskiss/base62 v1.1.0 // indirect
|
||||||
github.com/klauspost/compress v1.13.6 // indirect
|
github.com/klauspost/compress v1.13.6 // indirect
|
||||||
github.com/labstack/gommon v0.4.0 // indirect
|
github.com/labstack/gommon v0.4.0 // indirect
|
||||||
|
|
5
room.go
5
room.go
|
@ -383,7 +383,8 @@ func joinRoomHandler(c echo.Context) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate indicator GIF
|
// Generate indicator GIF
|
||||||
indicator, original, isGIF, err := user.GetIndicator(c.Request().Context(), fnew, room)
|
// indicator, original, isGIF, err := user.GetIndicator(c.Request().Context(), fnew, room)
|
||||||
|
_, original, isGIF, err := user.GetIndicator(c.Request().Context(), fnew, room)
|
||||||
origMime := "image/png"
|
origMime := "image/png"
|
||||||
if isGIF {
|
if isGIF {
|
||||||
origMime = "image/gif"
|
origMime = "image/gif"
|
||||||
|
@ -393,7 +394,7 @@ func joinRoomHandler(c echo.Context) (err error) {
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
return echo.NewHTTPError(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
resp.Original = fmt.Sprintf("data:%s;base64,%s", origMime, base64.StdEncoding.EncodeToString(original))
|
resp.Original = fmt.Sprintf("data:%s;base64,%s", origMime, base64.StdEncoding.EncodeToString(original))
|
||||||
resp.Indicator = fmt.Sprintf("data:image/gif;base64,%s", base64.StdEncoding.EncodeToString(indicator))
|
// resp.Indicator = fmt.Sprintf("data:image/gif;base64,%s", base64.StdEncoding.EncodeToString(indicator))
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
13
server.go
13
server.go
|
@ -225,25 +225,20 @@ func (cv *CustomValidator) Validate(i interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAppConfig(server string, redirPath string) (*mastodon.AppConfig, error) {
|
func getAppConfig(server string) (*mastodon.AppConfig, error) {
|
||||||
if redirPath == "" {
|
|
||||||
redirPath = "/"
|
|
||||||
}
|
|
||||||
redirectURI := "urn:ietf:wg:oauth:2.0:oob"
|
redirectURI := "urn:ietf:wg:oauth:2.0:oob"
|
||||||
u := &url.URL{
|
u := &url.URL{
|
||||||
Host: mainConfig.LocalDomain,
|
Host: mainConfig.LocalDomain,
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
Path: "/",
|
Path: "/",
|
||||||
}
|
}
|
||||||
q := u.Query()
|
|
||||||
q.Add("redir", redirPath)
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
u = u.JoinPath("app", "oauth")
|
u = u.JoinPath("app", "oauth")
|
||||||
redirectURI = u.String()
|
redirectURI = u.String()
|
||||||
|
|
||||||
conf := &mastodon.AppConfig{
|
conf := &mastodon.AppConfig{
|
||||||
ClientName: "Audon",
|
ClientName: "Audon",
|
||||||
Scopes: "read:accounts read:follows write:accounts",
|
// Scopes: "read:accounts read:follows write:accounts",
|
||||||
|
Scopes: "read:accounts read:follows",
|
||||||
Website: "https://codeberg.org/nmkj/audon",
|
Website: "https://codeberg.org/nmkj/audon",
|
||||||
RedirectURIs: redirectURI,
|
RedirectURIs: redirectURI,
|
||||||
}
|
}
|
||||||
|
|
54
webhooks.go
54
webhooks.go
|
@ -1,19 +1,18 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jellydator/ttlcache/v3"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/livekit/protocol/auth"
|
"github.com/livekit/protocol/auth"
|
||||||
"github.com/livekit/protocol/webhook"
|
"github.com/livekit/protocol/webhook"
|
||||||
mastodon "github.com/mattn/go-mastodon"
|
mastodon "github.com/mattn/go-mastodon"
|
||||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func livekitWebhookHandler(c echo.Context) error {
|
func livekitWebhookHandler(c echo.Context) error {
|
||||||
|
@ -38,7 +37,6 @@ func livekitWebhookHandler(c echo.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if event.GetEvent() == webhook.EventParticipantLeft {
|
} else if event.GetEvent() == webhook.EventParticipantLeft {
|
||||||
// Revert user's avatar
|
|
||||||
audonID := event.GetParticipant().GetIdentity()
|
audonID := event.GetParticipant().GetIdentity()
|
||||||
user, err := findUserByID(c.Request().Context(), audonID)
|
user, err := findUserByID(c.Request().Context(), audonID)
|
||||||
if user == nil || err != nil {
|
if user == nil || err != nil {
|
||||||
|
@ -51,51 +49,13 @@ func livekitWebhookHandler(c echo.Context) error {
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return echo.NewHTTPError(http.StatusGone)
|
return echo.NewHTTPError(http.StatusGone)
|
||||||
}
|
}
|
||||||
mastoClient := getMastodonClient(data.Value())
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
if mastoClient == nil {
|
defer cancel()
|
||||||
c.Logger().Errorf("unable to get mastodon client: %v", data.Value().MastodonConfig)
|
nextUser, err := findUserByID(ctx, audonID)
|
||||||
return echo.NewHTTPError(http.StatusInternalServerError)
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
}
|
}
|
||||||
cached := webhookTimerCache.Get(audonID)
|
nextUser.ClearUserAvatar(ctx)
|
||||||
if cached != nil {
|
|
||||||
oldTimer := cached.Value()
|
|
||||||
if !oldTimer.Stop() {
|
|
||||||
<-oldTimer.C
|
|
||||||
}
|
|
||||||
}
|
|
||||||
countdown := time.NewTimer(60 * time.Second)
|
|
||||||
webhookTimerCache.Set(audonID, countdown, ttlcache.DefaultTTL)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-countdown.C
|
|
||||||
webhookTimerCache.Delete(audonID)
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
stillAgain, err := user.InLivekit(ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
if stillAgain {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nextUser, err := findUserByID(ctx, audonID)
|
|
||||||
if err == nil && nextUser.AvatarFile != "" {
|
|
||||||
log.Printf("Recovering avatar: %s --> (%s) %s\n", nextUser.AvatarFile, audonID, nextUser.Webfinger)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
avatar := nextUser.getAvatarImagePath(nextUser.AvatarFile)
|
|
||||||
_, err = updateAvatar(ctx, mastoClient, avatar)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
nextUser.ClearUserAvatar(ctx)
|
|
||||||
} else if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
} else if event.GetEvent() == webhook.EventRoomStarted {
|
} else if event.GetEvent() == webhook.EventRoomStarted {
|
||||||
// Have the bot advertise the room
|
// Have the bot advertise the room
|
||||||
|
|
Ładowanie…
Reference in New Issue