/
janitor.ts
117 lines (97 loc) · 4.04 KB
/
janitor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { BotConfig, IO, Logger } from 'botpress/sdk'
import { createExpiry } from 'core/misc/expiry'
import { SessionRepository } from 'core/repositories'
import { Event } from 'core/sdk/impl'
import { inject, injectable, tagged } from 'inversify'
import _ from 'lodash'
import { Memoize } from 'lodash-decorators'
import { BotpressConfig } from '../../config/botpress.config'
import { ConfigProvider } from '../../config/config-loader'
import { TYPES } from '../../types'
import { BotService } from '../bot-service'
import { Janitor } from '../janitor'
import { DialogEngine } from './dialog-engine'
import { SessionIdFactory } from './session/id-factory'
const debug = DEBUG('janitor')
const dialogDebug = debug.sub('dialog')
@injectable()
export class DialogJanitor extends Janitor {
constructor(
@inject(TYPES.Logger)
@tagged('name', 'DialogJanitor')
protected logger: Logger,
@inject(TYPES.ConfigProvider) private configProvider: ConfigProvider,
@inject(TYPES.DialogEngine) private dialogEngine: DialogEngine,
@inject(TYPES.BotService) private botService: BotService,
@inject(TYPES.SessionRepository) private sessionRepo: SessionRepository
) {
super(logger)
}
@Memoize
private async getBotpresConfig(): Promise<BotpressConfig> {
return this.configProvider.getBotpressConfig()
}
protected async getInterval(): Promise<string> {
const config = await this.getBotpresConfig()
return config.dialog.janitorInterval
}
/**
* Deletes the sessions that are expired and
* reset the contexts of the sessions that are stale.
* These actions are executed based on two expiries: session_expiry and context_expiry.
*/
protected async runTask(): Promise<void> {
dialogDebug('Running task')
const botsConfigs = await this.botService.getBots()
const botsIds = Array.from(botsConfigs.keys())
for (const botId of botsIds) {
dialogDebug.forBot(botId, 'Deleting expired sessions')
await this.sessionRepo.deleteExpiredSessions(botId)
const sessionsIds = await this.sessionRepo.getExpiredContextSessionIds(botId)
if (sessionsIds.length > 0) {
dialogDebug.forBot(botId, 'Found stale contexts', sessionsIds)
for (const sessionId of sessionsIds) {
await this._processSessionTimeout(sessionId, botId, botsConfigs.get(botId)!)
}
}
}
}
private async _processSessionTimeout(sessionId: string, botId: string, botConfig: BotConfig) {
dialogDebug.forBot(botId, 'Processing timeout', sessionId)
try {
const channel = SessionIdFactory.createChannelFromId(sessionId)
const target = SessionIdFactory.createTargetFromId(sessionId)
const threadId = SessionIdFactory.createThreadIdFromId(sessionId)
const session = await this.sessionRepo.get(sessionId)
// Don't process the timeout when the context is empty.
// This means the conversation has not began.
if (_.isEmpty(session.context)) {
dialogDebug.forBot(botId, 'Skipping. No changes in context', sessionId)
return
}
// This event only exists so that processTimeout can call processEvent
const fakeEvent = Event({
type: 'timeout',
channel: channel,
target: target,
threadId: threadId,
direction: 'incoming',
payload: '',
botId: botId
}) as IO.IncomingEvent
fakeEvent.state.context = session.context as IO.DialogContext
fakeEvent.state.session = session.session_data as IO.CurrentSession
await this.dialogEngine.processTimeout(botId, sessionId, fakeEvent)
const botpressConfig = await this.getBotpresConfig()
const expiry = createExpiry(botConfig!, botpressConfig)
session.context = {}
session.temp_data = {}
session.context_expiry = expiry.context
session.session_expiry = expiry.session
await this.sessionRepo.update(session)
dialogDebug.forBot(botId, `New expiry set for ${session.context_expiry}`, sessionId)
} catch (err) {
this.logger.error(`Could not process the timeout event. ${err.message}`)
}
}
}