Skip to content

Commit

Permalink
feat: hooks module with remove empty rooms hook
Browse files Browse the repository at this point in the history
Closes #29
  • Loading branch information
Jordy Cabannes committed Apr 12, 2024
1 parent 042f8fc commit 8e9a6ec
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 13 deletions.
11 changes: 11 additions & 0 deletions packages/matrix-identity-server/src/matrixDb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ type Match = (
*/
type GetAll = (table: Collections, fields: string[]) => Promise<DbGetResult>

type Insert = (
table: Collections,
values: Record<string, string | number>
) => Promise<DbGetResult>

export interface MatrixDBBackend {
ready: Promise<void>
get: Get
getAll: GetAll
insert: Insert
// match: Match
close: () => void
}
Expand Down Expand Up @@ -79,6 +85,11 @@ class MatrixDB implements MatrixDBBackend {
return await this.db.get(table, fields, filterFields)
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/promise-function-async
insert(table: Collections, values: Record<string, string | number>) {
return this.db.insert(table, values)
}

close(): void {
this.db.close()
}
Expand Down
7 changes: 3 additions & 4 deletions packages/matrix-identity-server/src/matrixDb/sql/sqlite.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Collections } from '../../db'
import { type MatrixDBBackend } from '../'
import { type Config } from '../../types'
import { type Collections } from '../../db'
import SQLite from '../../db/sql/sqlite'
import { type Config } from '../../types'

class MatrixDBSQLite extends SQLite implements MatrixDBBackend {
// eslint-disable-next-line @typescript-eslint/promise-function-async
Expand All @@ -21,8 +21,7 @@ class MatrixDBSQLite extends SQLite implements MatrixDBBackend {
/* istanbul ignore next */ // @ts-ignore
if (sqlite3.Database == null) sqlite3 = sqlite3.default
const db = (this.db = new sqlite3.Database(
conf.matrix_database_host as string,
sqlite3.OPEN_READONLY
conf.matrix_database_host as string
))
/* istanbul ignore if */
if (db == null) {
Expand Down
59 changes: 59 additions & 0 deletions packages/tom-server/src/hooks/callback/on-room-membership-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { type TwakeLogger } from '@twake/logger'
import type MatrixApplicationServer from '@twake/matrix-application-server'
import { type ClientEvent } from '@twake/matrix-application-server'
import fetch from 'node-fetch'
import { type Config } from '../../types'
import { type IMatrixDBRepository } from '../repositories/interfaces/matrix-db-repository.interface'

export class OnRoomMembershipEvent {
constructor(
private readonly _applicationServer: MatrixApplicationServer,
matrixDb: IMatrixDBRepository,
conf: Config,
logger: TwakeLogger
) {
this._applicationServer.on(
'state event | type: m.room.member',
(event: ClientEvent) => {
if (event.content.membership === 'leave') {
let statusCode: number
const deleteRequestUrl = `https://${conf.matrix_server}/_synapse/admin/v1/rooms/${event.room_id}`
matrixDb
.hasRoomLocalServerUsers(event.room_id)
// eslint-disable-next-line @typescript-eslint/promise-function-async
.then((hasLocalServerUsers) => {
if (!hasLocalServerUsers) {
return fetch(encodeURI(deleteRequestUrl), {
method: 'DELETE',
headers: {
Authorization: `Bearer ${this._applicationServer.appServiceRegistration.asToken}`
},
body: JSON.stringify({})
})
}
})
// eslint-disable-next-line @typescript-eslint/promise-function-async
.then((response) => {
if (response != null) {
statusCode = response.status
return response.json()
}
})
.then((body) => {
if (body != null) {
logger.info(JSON.stringify(body), {
matrixUserId: `@${this._applicationServer.appServiceRegistration.senderLocalpart}:${conf.server_name}`,
httpMethod: 'DELETE',
requestUrl: deleteRequestUrl,
status: statusCode
})
}
})
.catch((e) => {
logger.error(e)
})
}
}
)
}
}
45 changes: 45 additions & 0 deletions packages/tom-server/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { type TwakeLogger } from '@twake/logger'
import type MatrixApplicationServer from '@twake/matrix-application-server'
import { type MatrixDB } from '@twake/matrix-identity-server'
import { type Config } from '../types'
import { OnRoomMembershipEvent } from './callback/on-room-membership-event'
import { MatrixDBRepository } from './repositories/matrix-db.repository'

export type CallbackHooks = OnRoomMembershipEvent

export class TwakeServerHooks {
callbackHooks: CallbackHooks[]
ready: Promise<void>
constructor(
applicationServer: MatrixApplicationServer,
matrixDb: MatrixDB,
conf: Config,
logger: TwakeLogger
) {
this.callbackHooks = []
const matrixDbRepository = new MatrixDBRepository(
matrixDb,
conf.server_name
)
this.ready = new Promise<void>((resolve, reject) => {
matrixDbRepository
.addMatrixUserAdmin(
`@${applicationServer.appServiceRegistration.senderLocalpart}:${conf.server_name}`
)
.then(() => {
this.callbackHooks.push(
new OnRoomMembershipEvent(
applicationServer,
matrixDbRepository,
conf,
logger
)
)
resolve()
})
.catch((e) => {
reject(e)
})
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { type DbGetResult } from '@twake/matrix-identity-server'

export interface IMatrixDBRepository {
addMatrixUserAdmin: (matrixAddress: string) => Promise<DbGetResult>
hasRoomLocalServerUsers: (roomId: string) => Promise<boolean>
}
47 changes: 47 additions & 0 deletions packages/tom-server/src/hooks/repositories/matrix-db.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { type DbGetResult, type MatrixDB } from '@twake/matrix-identity-server'
import lodash from 'lodash'
import { type IMatrixDBRepository } from './interfaces/matrix-db-repository.interface'

const { groupBy, mapValues, escapeRegExp } = lodash

export class MatrixDBRepository implements IMatrixDBRepository {
constructor(
private readonly _matrixDb: MatrixDB,
private readonly _matrixServer: string
) {}

async addMatrixUserAdmin(matrixAddress: string): Promise<DbGetResult> {
const result = await this._matrixDb.insert('users', {
name: matrixAddress,
admin: 1
})
if (result.length === 0) {
throw new Error(`Set ${matrixAddress} as Matrix homeserver admin failed`)
}
return result
}

async hasRoomLocalServerUsers(roomId: string): Promise<boolean> {
const userIdLocalServerRegExp = new RegExp(
`^@[^:]*:${escapeRegExp(this._matrixServer)}$`
)
const localServerUsers = (
(await this._matrixDb.get('room_memberships', ['user_id', 'membership'], {
room_id: roomId
})) as Array<{ user_id: string; membership: string }>
).filter(
(membershipDetail) =>
membershipDetail.user_id.match(userIdLocalServerRegExp) != null
)

const currentMembershipByUserId = mapValues(
groupBy(localServerUsers, 'user_id'),
(membershipsDetails) =>
membershipsDetails.map((detail) => detail.membership).pop()
)

return Object.keys(currentMembershipByUserId).some(
(userId) => currentMembershipByUserId[userId] !== 'leave'
)
}
}
16 changes: 9 additions & 7 deletions packages/tom-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import fs from 'fs'
import AdministrationConsoleAPI from './administration-console-api'
import defaultConfig from './config.json'
import initializeDb, { type TwakeDB } from './db'
import { TwakeServerHooks } from './hooks'
import IdServer from './identity-server'
import mutualRoomsAPIRouter from './mutual-rooms-api'
import privateNoteApiRouter from './private-note-api'
Expand All @@ -30,6 +31,7 @@ export default class TwakeServer {
ready!: Promise<boolean>
idServer!: TwakeIdentityServer
applicationServer: MatrixApplicationServer
hooks!: TwakeServerHooks

constructor(
conf?: Partial<Config>,
Expand Down Expand Up @@ -164,13 +166,13 @@ export default class TwakeServer {
Object.keys(wellKnown.api.get).forEach((k) => {
this.endpoints.get(k, wellKnown.api.get[k])
})

return true
} catch (error) {
/* istanbul ignore next */
this.logger.error(`Unable to initialize server`, { error })
/* istanbul ignore next */
throw Error('Unable to initialize server', { cause: error })
this.hooks = new TwakeServerHooks(
this.applicationServer,
this.matrixDb,
this.conf,
this.logger
)
await this.hooks.ready
return true
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sqlite3 from 'sqlite3'
import { type Config } from '../../types'
import { type tokenDetail } from '../middlewares/auth'
import sqlite3 from 'sqlite3'

const token: tokenDetail = {
value: 'accessTokenddddddddddddddddddddddddddddddddddddddddddddddddddddd',
Expand Down Expand Up @@ -36,7 +36,7 @@ const buildTokenTable = (conf: Config): Promise<void> => {
)
)

matrixDbManager.run('CREATE TABLE users (uid varchar(8), name varchar(32), mobile varchar(12), mail varchar(32))')
matrixDbManager.run('CREATE TABLE users (uid varchar(8), name varchar(32), mobile varchar(12), mail varchar(32), admin SMALLINT DEFAULT 0 NOT NULL)')
})
}

Expand Down

0 comments on commit 8e9a6ec

Please sign in to comment.