Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various improvements #205

Merged
merged 9 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
name: Docker build CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
push:
branches: ['master']
pull_request:
branches: ['master']

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run setup
- run: echo $USER; id -u $USER; id -g $USER
- run: cp .github/workflows/.env.github .env
- run: docker-compose up -d
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run setup
- run: echo $USER; id -u $USER; id -g $USER
- run: cp .github/workflows/.env.github .env
- run: docker compose up -d
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ The `config/preferences.json` file specifies application preferences. The availa
| minRightForPublicMessages | number | -1 | Min. right to send public messages |
| minRightForPrivateMessages | number | -1 | Min. right to send private messages |
| minRightForMessageQuoting | number | -1 | Min. right to quote messages |
| minRightForUserMention | number | -1 | Min. right to mention users |
| minRightForShortTermMessageHistory | number | -1 | Min. right to access short term room message history |
| minRightForMessageHistory | number | -1 | Min. right to access full room message history |
| minRightForUserModeration | number | 'op' | Min. right to ban, kick and access user ips |
Expand Down Expand Up @@ -99,6 +100,10 @@ The `config/preferences.json` file specifies application preferences. The availa

`minRightForMessageHistory` defines who can quote old messages and navigate room old history.

### Customize the welcome message

By default, guests are welcomes with a welcome message that you can change in `config/welcome.txt`. If you remove this file, there won't be a welcome message anymore.

### Customize guest names

`config/guestnames.txt` is the pool of non-logged usernames.
Expand Down
7 changes: 7 additions & 0 deletions app/script/debug-db.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

source .env

DOCKER_CONTAINER_ID=$(docker ps | grep skychat_db | awk '{print $1}')

docker exec -it "$DOCKER_CONTAINER_ID" /usr/local/bin/psql "--user=$POSTGRES_USER" "$POSTGRES_DB"
2 changes: 2 additions & 0 deletions app/script/log.sh → app/script/debug-log.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#!/bin/bash

# Log web server
docker container logs -n 1000 -f $(docker container list | grep skychat_app | cut -d' ' -f1) | pino-pretty
5 changes: 5 additions & 0 deletions app/script/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,8 @@ fi
if [[ ! -e config/fakemessages.txt ]]; then
cp app/template/fakemessages.txt.template config/fakemessages.txt;
fi

# Initialize welcome.txt.template
if [[ ! -e config/welcome.txt.template ]]; then
cp app/template/welcome.txt.template config/welcome.txt;
fi
2 changes: 2 additions & 0 deletions app/server/plugins/core/CorePluginGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ReactionPlugin } from './global/ReactionPlugin.js';
import { SetRightPlugin } from './global/SetRightPlugin.js';
import { StickerPlugin } from './global/StickerPlugin.js';
import { VoidPlugin } from './global/VoidPlugin.js';
import { WelcomePlugin } from './global/WelcomePlugin.js';
import { XpTickerPlugin } from './global/XpTickerPlugin.js';
import { HelpPlugin } from './room/HelpPlugin.js';
import { MentionPlugin } from './room/MentionPlugin.js';
Expand Down Expand Up @@ -64,6 +65,7 @@ export class CorePluginGroup extends PluginGroup {
SetRightPlugin,
StickerPlugin,
VoidPlugin,
WelcomePlugin,
XpTickerPlugin,
];
}
2 changes: 1 addition & 1 deletion app/server/plugins/core/global/BanPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export class BanPlugin extends GlobalPlugin {
// If access ban, just close connection
if (this.isBanned(connection, BAN_TYPES.ACCESS)) {
connection.close(Connection.CLOSE_KICKED, 'You have been disconnected');
throw new Error();
return '/void';
}
// If shadow banned
if (this.isBanned(connection, BAN_TYPES.SHADOW)) {
Expand Down
27 changes: 27 additions & 0 deletions app/server/plugins/core/global/WelcomePlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Config } from '../../../skychat/Config.js';
import { Connection } from '../../../skychat/Connection.js';
import { UserController } from '../../../skychat/UserController.js';
import { GlobalPlugin } from '../../GlobalPlugin.js';

export class WelcomePlugin extends GlobalPlugin {
static readonly commandName = 'welcome';

readonly callable = false;

async run() {
throw new Error('Method not implemented.');
}

async onNewConnection(connection: Connection) {
const message = Config.WELCOME_MESSAGE;
if (!message) {
return;
}

if (connection.session.user.id > 0) {
return;
}

connection.send('message', UserController.createNeutralMessage({ content: message, room: connection.roomId, id: 0 }).sanitized());
}
}
14 changes: 12 additions & 2 deletions app/server/plugins/core/room/MentionPlugin.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Config } from '../../../skychat/Config.js';
import { Connection } from '../../../skychat/Connection.js';
import { Message } from '../../../skychat/Message.js';
import { Session } from '../../../skychat/Session.js';
Expand All @@ -12,7 +13,16 @@ export class MentionPlugin extends RoomPlugin {
async run(): Promise<void> {}

// Intercept quotes in messages
public async onBeforeMessageBroadcastHook(message: Message, connection: Connection) {
public async onBeforeMessageBroadcastHook(message: Message, connection?: Connection) {
// It is currently not possible to mention an user if there's not connection the mention originates from
if (!connection) {
return message;
}

if (connection.session.user.right < Config.PREFERENCES.minRightForUserMention) {
return message;
}

const mentions = message.content.match(/@[a-zA-Z0-9-_]+/g);

// Note quote detected
Expand Down Expand Up @@ -42,7 +52,7 @@ export class MentionPlugin extends RoomPlugin {
continue;
}
// Skip if in a room where the mentioned user is not allowed
if (!connection.room || !connection.room.accepts(session)) {
if (!connection.room?.accepts(session)) {
continue;
}
session.send('mention', {
Expand Down
4 changes: 2 additions & 2 deletions app/server/skychat/AuthBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export class AuthBridge extends EventEmitter {

static readonly MAX_REGISTER_PER_HOUR = 1;

static readonly MAX_LOGIN_PER_MINUTE = 10;
static readonly MAX_LOGIN_PER_MINUTE = 20;

static readonly MAX_GUEST_PER_MINUTE = 8;
static readonly MAX_GUEST_PER_MINUTE = 20;

static readonly MAX_TOKEN_AUTH_PER_MINUTE = 20;

Expand Down
15 changes: 13 additions & 2 deletions app/server/skychat/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type Preferences = {
minRightForPublicMessages: number;
minRightForPrivateMessages: number;
minRightForMessageQuoting: number;
minRightForUserMention: number;
minRightForShortTermMessageHistory: number;
minRightForMessageHistory: number;
minRightForUserModeration: number | 'op';
Expand Down Expand Up @@ -45,6 +46,8 @@ export class Config {

public static FAKE_MESSAGES: string[] = [];

public static WELCOME_MESSAGE: string | null = null;

public static getRandomGuestName(): string {
const index = Math.floor(Math.random() * Config.GUEST_NAMES.length);
return Config.GUEST_NAMES[index];
Expand Down Expand Up @@ -84,13 +87,21 @@ export class Config {
Logging.warn('No fake messages found (fakemessages.txt file is empty). Using a single empty fake message.');
Config.GUEST_NAMES.push('');
}
// Load welcome message
try {
Config.WELCOME_MESSAGE = fs.readFileSync('config/welcome.txt').toString().trim();
} catch (e) {
Logging.info('No welcome message found (welcome.txt file is missing). Will not display a welcome message.');
Config.WELCOME_MESSAGE = null;
}
// Load preferences.json
Config.PREFERENCES = JSON.parse(fs.readFileSync('config/preferences.json').toString());
const keys: string[] = [
'minRightForPrivateMessages',
'minRightForMessageQuoting',
'minRightForShortTermMessageHistory',
'minRightForMessageHistory',
'minRightForPrivateMessages',
'minRightForMessageQuoting',
'minRightForUserMention',
'minRightForUserModeration',
'minRightForSetRight',
'minRightForAudioRecording',
Expand Down
11 changes: 3 additions & 8 deletions app/server/skychat/Connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,10 @@ import { Session } from './Session.js';
* A client represents an open connection to the server
*/
export class Connection extends EventEmitter implements IBroadcaster {
static readonly PING_INTERVAL_MS = 10 * 1000;

// TODO: This limit is also used for the AudioRecorderPlugin, so we currently have to keep it high. The audio limit should be different.
static readonly MAX_RECEIVED_BYTES_PER_10_SEC = 1024 * 400;

static readonly MAX_EVENTS_PER_SEC = 16;

static readonly MAXIMUM_MISSED_PING = 1;

static readonly CLOSE_PING_TIMEOUT = 4504;
static readonly MAX_EVENTS_PER_SEC = 30;

static readonly CLOSE_KICKED = 4403;

Expand Down Expand Up @@ -149,7 +143,8 @@ export class Connection extends EventEmitter implements IBroadcaster {

// Check that the event name is valid and is registered
if (typeof eventName !== 'string') {
throw new Error('Event could not be parsed');
Logging.error(`Event could not be parsed: "${eventName}" from event "${dataAsString}"`);
throw new Error(`Event could not be parsed: "${eventName}"`);
}

if (!Connection.ACCEPTED_EVENTS.includes(eventName)) {
Expand Down
4 changes: 2 additions & 2 deletions app/server/skychat/HttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type ConnectionUpgradeEvent = {
export class HttpServer extends EventEmitter {
public static readonly UPLOADED_FILE_REGEXP: RegExp = new RegExp('^' + Config.LOCATION + '/uploads/all/([-\\/._a-zA-Z0-9]+)$');

static readonly MAX_UPGRADES_PER_SECOND = 3;
static readonly MAX_UPGRADES_PER_SECOND = 4;

static readonly MAX_UPGRADES_PER_MINUTE = 20;

Expand All @@ -39,7 +39,7 @@ export class HttpServer extends EventEmitter {

private readonly wsCreateSecLimiter: RateLimiterMemory = new RateLimiterMemory({
points: HttpServer.MAX_UPGRADES_PER_SECOND,
duration: 60,
duration: 1,
});

private readonly wsCreateMinLimiter: RateLimiterMemory = new RateLimiterMemory({
Expand Down
5 changes: 4 additions & 1 deletion app/server/skychat/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { GlobalPlugin } from '../plugins/GlobalPlugin.js';
import { globalPluginGroup } from '../plugins/GlobalPluginGroup.js';
import { ConnectionAcceptedEvent } from './AuthBridge.js';
import { Connection } from './Connection.js';
import { Logging } from './Logging.js';
import { MessageFormatter } from './MessageFormatter.js';
import { RoomManager } from './RoomManager.js';

Expand Down Expand Up @@ -63,10 +64,12 @@ export class PluginManager {
private async onConnectionMessage(connection: Connection, payload: string): Promise<void> {
try {
// Handle default command (/message)
if (payload[0] !== '/') {
if (!payload.startsWith('/')) {
payload = '/message ' + payload;
}

Logging.info(`Command received: ${connection.session.identifier} ${payload.split(' ')[0]} (length: ${payload.length})`);

payload = await this.executeNewMessageHook(payload, connection);

// Parse command name and message content
Expand Down
1 change: 1 addition & 0 deletions app/template/preferences.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"minRightForPublicMessages": -1,
"minRightForPrivateMessages": -1,
"minRightForMessageQuoting": -1,
"minRightForUserMention": -1,
"minRightForUserModeration": "op",
"minRightForSetRight": "op",
"minRightForAudioRecording": -1,
Expand Down
1 change: 1 addition & 0 deletions app/template/welcome.txt.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Welcome on the SkyChat!
Loading