forked from XshubhamX/Rocket.Chat.OpenAI.Completions.App
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathOpenAiChatApp.ts
237 lines (217 loc) · 8.06 KB
/
OpenAiChatApp.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import {
IAppAccessors,
IConfigurationExtend,
IHttp,
ILogger,
IModify,
IPersistence,
IRead,
} from "@rocket.chat/apps-engine/definition/accessors";
import { App } from "@rocket.chat/apps-engine/definition/App";
import {
IMessage,
IPostMessageSent,
} from "@rocket.chat/apps-engine/definition/messages";
import { IAppInfo } from "@rocket.chat/apps-engine/definition/metadata";
import { RoomType } from "@rocket.chat/apps-engine/definition/rooms";
import {
IUIKitResponse,
UIKitActionButtonInteractionContext,
UIKitViewSubmitInteractionContext,
} from "@rocket.chat/apps-engine/definition/uikit";
import { OpenAIChatCommand } from "./commands/OpenAIChatCommand";
import { OpenAIChatHelpCommand } from "./commands/OpenAIChatHelpCommand";
import { buttons } from "./config/Buttons";
import { AppSetting, settings } from "./config/Settings";
import { ActionButtonHandler } from "./handlers/ActionButtonHandler";
import { ViewSubmitHandler } from "./handlers/ViewSubmit";
import { OpenAiCompletionRequest } from "./lib/RequestOpenAiChat";
import { sendDirect } from "./lib/SendDirect";
import { sendMessage } from "./lib/SendMessage";
import { sendNotification } from "./lib/SendNotification";
import { DirectContext } from "./persistence/DirectContext";
import { addReactions, removeReactions } from "./lib/SendReactions";
export class OpenAiChatApp extends App implements IPostMessageSent {
constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) {
super(info, logger, accessors);
}
public async extendConfiguration(configuration: IConfigurationExtend) {
await configuration.slashCommands.provideSlashCommand(
new OpenAIChatCommand(this)
);
await configuration.slashCommands.provideSlashCommand(
new OpenAIChatHelpCommand(this)
);
// Providing persistant app settings
await Promise.all(
settings.map((setting) =>
configuration.settings.provideSetting(setting)
)
);
// Registering Action Buttons
await Promise.all(
buttons.map((button) => configuration.ui.registerButton(button))
);
}
// register ActionButton Handler
public async executeActionButtonHandler(
context: UIKitActionButtonInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify
): Promise<IUIKitResponse> {
// lets just move this execution to another file to keep DemoApp.ts clean.
return new ActionButtonHandler().executor(
this,
context,
read,
http,
persistence,
modify,
this.getLogger()
);
}
// register SubmitView Handler
public async executeViewSubmitHandler(
context: UIKitViewSubmitInteractionContext,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify
) {
// same for View SubmitHandler, moving to another Class
return new ViewSubmitHandler().executor(
this,
context,
read,
http,
persistence,
modify,
this.getLogger()
);
}
public async checkPostMessageSent(
message: IMessage,
read: IRead,
http: IHttp,
): Promise<boolean> {
const { id: messageId, text, editedAt, room, sender } = message;
// we only want direct with the app username
var bot_user = await read.getUserReader().getAppUser();
const { username: botUsername, id: botId } = bot_user || {};
if (!messageId || !botId) return false;
const msgRead = await read.getMessageReader().getById(messageId);
if (!msgRead) return false;
const { value: ENABLE_MENTION } = await read
.getEnvironmentReader()
.getSettings()
.getById(AppSetting.ENABLE_MENTION);
const mentionedUsers = msgRead['_unmappedProperties_']?.mentions || [];
const botMentioned = Boolean(mentionedUsers.find(user => user.username === botUsername) && ENABLE_MENTION);
const isDirectMessageBot = Boolean(room.type === RoomType.DIRECT_MESSAGE && room.userIds && room.userIds.includes(botId));
const isChannel = room.type === RoomType.CHANNEL;
const isPrivateGroup = room.type === RoomType.PRIVATE_GROUP;
const isMentionedInAChannel = botMentioned && isChannel;
const isMentionedInAPrivateRoom = botMentioned && isPrivateGroup;
const { value: ENABLE_IN_PRIVATE_ROOM } = await read
.getEnvironmentReader()
.getSettings()
.getById(AppSetting.ENABLE_IN_PRIVATE_ROOM);
if (isDirectMessageBot) return isDirectMessageBot;
if (isMentionedInAChannel) return isMentionedInAChannel;
if (isMentionedInAPrivateRoom) return ENABLE_IN_PRIVATE_ROOM;
return false;
}
// register hook to answer directs
public async executePostMessageSent(
message: IMessage,
read: IRead,
http: IHttp,
persistence: IPersistence,
modify: IModify
): Promise<void> {
const { room, sender } = message;
var context: any;
var bot_user = await read.getUserReader().getAppUser();
// this the bot answer, get the actual context and store it
if (bot_user?.id == sender.id && message.threadId) {
context_data = await DirectContext.get(read, message.threadId);
context = context_data[0]["context"];
context.push({ role: "assistant", content: message.text });
await DirectContext.update(
persistence,
message.threadId,
context
);
return;
}
// get thread id
// discover is there any context for this thread
if (message.threadId) {
// message from inside a thread
// get context, and push to it
var context_data = await DirectContext.get(
read,
message.threadId
);
context = context_data[0]["context"];
context.push({ role: "user", content: message.text });
// update context on persistence
await DirectContext.update(
persistence,
message.threadId,
context
);
} else {
// no thread id, first message, initiating context
var context = [{ role: "user", content: message.text }] as any;
// update context
if (message.id) {
await DirectContext.update(
persistence,
message.id,
context
);
}
}
const { value: ENABLE_REACTION } = await read
.getEnvironmentReader()
.getSettings()
.getById(AppSetting.ENABLE_REACTION);
if (ENABLE_REACTION) await addReactions(modify, message, read, [':thinking_face:']);
const result = await OpenAiCompletionRequest(
this,
http,
read,
context,
sender
);
if (ENABLE_REACTION) await removeReactions(modify, message, read);
if (result.success) {
var markdown_message =
result.content.choices[0].message.content.replace(
/^\s*/gm,
""
);
sendDirect(
sender,
read,
modify,
markdown_message,
message.threadId || message.id
);
if (ENABLE_REACTION) await addReactions(modify, message, read, [':checkered_flag:']);
} else {
sendNotification(
modify,
room,
sender,
`**Error!** Could not Request Completion:\n\n` +
result.content.error.message
);
if (ENABLE_REACTION) await addReactions(modify, message, read, [':interrobang:']);
}
return;
}
}