Typescript v5 + eslint 8.57 WIP

This commit also improves some type handling in the project.
This commit is contained in:
John Livingston
2024-09-07 14:49:27 +02:00
parent 64a9c7be21
commit 7b3d93b290
41 changed files with 2652 additions and 3054 deletions

View File

@ -189,7 +189,7 @@ class BotConfiguration {
})).toString()
config = JSON.parse(content)
} catch (err) {
} catch (_err) {
this.logger.info('Error reading the moderation bot global configuration file, assuming it does not exists.')
config = undefined
}
@ -275,7 +275,7 @@ class BotConfiguration {
content = (await fs.promises.readFile(filePath, {
encoding: 'utf-8'
})).toString()
} catch (err) {
} catch (_err) {
this.logger.debug('Failed to read room conf file, assuming it does not exists')
this.roomConfCache.set(roomJID, null)
return null
@ -284,7 +284,7 @@ class BotConfiguration {
let json: RoomConf
try {
json = JSON.parse(content) as RoomConf
} catch (err) {
} catch (_err) {
this.logger.error(`Error parsing JSON file ${filePath}, assuming empty`)
this.roomConfCache.set(roomJID, null)
return null

View File

@ -59,7 +59,7 @@ async function initChannelConfiguration (options: RegisterServerOptions): Promis
logger.info(`Channel ${channelId} deleted, removing 'custom emojis' related stuff.`)
try {
Emojis.singletonSafe()?.deleteChannelDefinition(channelId)
await Emojis.singletonSafe()?.deleteChannelDefinition(channelId)
} catch (err) {
logger.error(err)
}
@ -87,7 +87,7 @@ async function initChannelConfiguration (options: RegisterServerOptions): Promis
])
await fillVideoCustomFields(options, video)
const hasChat = await videoHasWebchat({
const hasChat = videoHasWebchat({
'chat-per-live-video': !!settings['chat-per-live-video'],
'chat-all-lives': !!settings['chat-all-lives'],
'chat-all-non-lives': !!settings['chat-all-non-lives'],

View File

@ -17,31 +17,36 @@ import { channelTermsMaxLength } from '../../../../shared/lib/constants'
async function sanitizeChannelConfigurationOptions (
_options: RegisterServerOptions,
_channelId: number | string,
data: any
data: unknown
): Promise<ChannelConfigurationOptions> {
if (typeof data !== 'object') {
if (!_assertObjectType(data)) {
throw new Error('Invalid data type')
}
const botData = data.bot
if (typeof botData !== 'object') {
const botData = data.bot ?? {}
if (!_assertObjectType(botData)) {
throw new Error('Invalid data.bot data type')
}
// slowMode not present in livechat <= 8.2.0:
const slowModeData = data.slowMode ?? {}
slowModeData.duration ??= slowModeData.defaultDuration ?? 0 // v8.3.0 to 8.3.2: was in defaultDuration
if (typeof slowModeData !== 'object') {
if (!_assertObjectType(slowModeData)) {
throw new Error('Invalid data.slowMode data type')
}
slowModeData.duration ??= slowModeData.defaultDuration ?? 0 // v8.3.0 to 8.3.2: was in defaultDuration
const moderationData = data.moderation ?? {} // comes with livechat 10.3.0
if (!_assertObjectType(moderationData)) {
throw new Error('Invalid data.moderation data type')
}
moderationData.delay ??= 0
moderationData.anonymize ??= false // comes with livechat 11.0.0
// mute not present in livechat <= 10.2.0
const mute = data.mute ?? {}
if (!_assertObjectType(mute)) {
throw new Error('Invalid data.mute data type')
}
mute.anonymous ??= false
// forbidSpecialChars comes with livechat 11.1.0
@ -51,13 +56,12 @@ async function sanitizeChannelConfigurationOptions (
tolerance: 0,
applyToModerators: false
}
if (typeof mute !== 'object') {
throw new Error('Invalid data.mute data type')
if (!_assertObjectType(botData.forbidSpecialChars)) {
throw new Error('Invalid data.forbidSpecialChars data type')
}
// terms not present in livechat <= 10.2.0
let terms = data.terms
let terms = data.terms ?? undefined
if (terms !== undefined && (typeof terms !== 'string')) {
throw new Error('Invalid data.terms data type')
}
@ -94,7 +98,11 @@ async function sanitizeChannelConfigurationOptions (
return result
}
function _readBoolean (data: any, f: string): boolean {
function _assertObjectType (data: unknown): data is Record<string, unknown> {
return !!data && (typeof data === 'object') && Object.keys(data).every(k => typeof k === 'string')
}
function _readBoolean (data: Record<string, unknown>, f: string): boolean {
if (!(f in data)) {
return false
}
@ -104,11 +112,11 @@ function _readBoolean (data: any, f: string): boolean {
return data[f]
}
function _readInteger (data: any, f: string, min: number, max: number): number {
function _readInteger (data: Record<string, unknown>, f: string, min: number, max: number): number {
if (!(f in data)) {
throw new Error('Missing integer value for field ' + f)
}
const v = parseInt(data[f])
const v = typeof data[f] === 'number' ? Math.trunc(data[f]) : parseInt(data[f] as string)
if (isNaN(v)) {
throw new Error('Invalid value type for field ' + f)
}
@ -121,7 +129,7 @@ function _readInteger (data: any, f: string, min: number, max: number): number {
return v
}
function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boolean): string {
function _readSimpleInput (data: Record<string, unknown>, f: string, strict?: boolean, noSpace?: boolean): string {
if (!(f in data)) {
return ''
}
@ -130,7 +138,7 @@ function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boo
}
// Removing control characters.
// eslint-disable-next-line no-control-regex
let s = (data[f] as string).replace(/[\u0000-\u001F\u007F-\u009F]/g, '')
let s = data[f].replace(/[\u0000-\u001F\u007F-\u009F]/g, '')
if (strict) {
// Replacing all invalid characters, no need to throw an error..
s = s.replace(/[^\p{L}\p{N}\p{Z}_-]$/gu, '')
@ -141,7 +149,7 @@ function _readSimpleInput (data: any, f: string, strict?: boolean, noSpace?: boo
return s
}
function _readStringArray (data: any, f: string): string[] {
function _readStringArray (data: Record<string, unknown>, f: string): string[] {
if (!(f in data)) {
return []
}
@ -162,7 +170,7 @@ function _readStringArray (data: any, f: string): string[] {
return result
}
function _readMultiLineString (data: any, f: string): string {
function _readMultiLineString (data: Record<string, unknown>, f: string): string {
if (!(f in data)) {
return ''
}
@ -171,11 +179,11 @@ function _readMultiLineString (data: any, f: string): string {
}
// Removing control characters (must authorize \u001A: line feed)
// eslint-disable-next-line no-control-regex
const s = (data[f] as string).replace(/[\u0000-\u0009\u001B-\u001F\u007F-\u009F]/g, '')
const s = data[f].replace(/[\u0000-\u0009\u001B-\u001F\u007F-\u009F]/g, '')
return s
}
async function _readRegExpArray (data: any, f: string): Promise<string[]> {
async function _readRegExpArray (data: Record<string, unknown>, f: string): Promise<string[]> {
// Note: this function can instanciate a lot of RegExp.
// To avoid freezing the server, we make it async, and will validate each regexp in a separate tick.
if (!(f in data)) {
@ -195,11 +203,11 @@ async function _readRegExpArray (data: any, f: string): Promise<string[]> {
}
// value must be a valid regexp
try {
async function _validate (): Promise<void> {
async function _validate (v: string): Promise<void> {
// eslint-disable-next-line no-new
new RegExp(v)
}
await _validate()
await _validate(v)
} catch (_err) {
throw new Error('Invalid value in field ' + f)
}
@ -208,12 +216,17 @@ async function _readRegExpArray (data: any, f: string): Promise<string[]> {
return result
}
async function _readForbiddenWords (botData: any): Promise<ChannelConfigurationOptions['bot']['forbiddenWords']> {
async function _readForbiddenWords (
botData: Record<string, unknown>
): Promise<ChannelConfigurationOptions['bot']['forbiddenWords']> {
if (!Array.isArray(botData.forbiddenWords)) {
throw new Error('Invalid forbiddenWords data')
}
const result: ChannelConfigurationOptions['bot']['forbiddenWords'] = []
for (const fw of botData.forbiddenWords) {
if (!_assertObjectType(fw)) {
throw new Error('Invalid entry in botData.forbiddenWords')
}
const regexp = !!fw.regexp
let entries
if (regexp) {
@ -239,9 +252,9 @@ async function _readForbiddenWords (botData: any): Promise<ChannelConfigurationO
}
async function _readForbidSpecialChars (
botData: any
botData: Record<string, unknown>
): Promise<ChannelConfigurationOptions['bot']['forbidSpecialChars']> {
if (typeof botData.forbidSpecialChars !== 'object') {
if (!_assertObjectType(botData.forbidSpecialChars)) {
throw new Error('Invalid forbidSpecialChars data')
}
const result: ChannelConfigurationOptions['bot']['forbidSpecialChars'] = {
@ -253,12 +266,15 @@ async function _readForbidSpecialChars (
return result
}
function _readQuotes (botData: any): ChannelConfigurationOptions['bot']['quotes'] {
function _readQuotes (botData: Record<string, unknown>): ChannelConfigurationOptions['bot']['quotes'] {
if (!Array.isArray(botData.quotes)) {
throw new Error('Invalid quotes data')
}
const result: ChannelConfigurationOptions['bot']['quotes'] = []
for (const qs of botData.quotes) {
if (!_assertObjectType(qs)) {
throw new Error('Invalid entry in botData.quotes')
}
const messages = _readStringArray(qs, 'messages')
const delay = _readInteger(qs, 'delay', 1, 6000)
@ -270,12 +286,15 @@ function _readQuotes (botData: any): ChannelConfigurationOptions['bot']['quotes'
return result
}
function _readCommands (botData: any): ChannelConfigurationOptions['bot']['commands'] {
function _readCommands (botData: Record<string, unknown>): ChannelConfigurationOptions['bot']['commands'] {
if (!Array.isArray(botData.commands)) {
throw new Error('Invalid commands data')
}
const result: ChannelConfigurationOptions['bot']['commands'] = []
for (const cs of botData.commands) {
if (!_assertObjectType(cs)) {
throw new Error('Invalid entry in botData.commands')
}
const message = _readSimpleInput(cs, 'message')
const command = _readSimpleInput(cs, 'command', false, true)

View File

@ -140,7 +140,7 @@ function channelConfigurationOptionsToBotRoomConf (
for (const handler of previousRoomConf.handlers) {
if (!handlersIds.has(handler.id)) {
// cloning to avoid issues...
const disabledHandler = JSON.parse(JSON.stringify(handler))
const disabledHandler = JSON.parse(JSON.stringify(handler)) as typeof handler
disabledHandler.enabled = false
handlers.push(disabledHandler)
}