zulip/web/src/bot_data.ts
Sahil Batra bb15b2d708 users: Send "update" events when deactivating or reactivating users.
We now send "realm_user/update" (and "realm_bot/update" for bots)
events with "is_active" field when deactivating and reactivating
users, including bots.

We would want to use "remove" event for a user losing access
to another user for #10970, so it is better to use "update"
event for deactivation as we only update "is_active" field
in the user objects and the clients still have the data for
deactivated users.

Previously, we used to send "add" event for reactivation along
with complete user objects, but clients should have the data
for deactivated users as well, so an "update" event is enough
like we do when deactivating users.
2023-11-02 08:31:12 -07:00

130 lines
3.4 KiB
TypeScript

import {z} from "zod";
import * as people from "./people";
export type ServerUpdateBotData = z.infer<typeof server_update_bot_schema>;
export type ServerAddBotData = z.infer<typeof server_add_bot_schema>;
export type Bot = Omit<ServerAddBotData, "services">;
export type Services = z.infer<typeof services_schema>;
export type BotDataParams = {
realm_bots: ServerAddBotData[];
};
const bots = new Map<number, Bot>();
const services = new Map<number, Services>();
// Define zod schema for data validation
const basic_bot_schema = z.object({
api_key: z.string(),
avatar_url: z.string(),
bot_type: z.number(),
default_all_public_streams: z.boolean(),
default_events_register_stream: z.union([z.string(), z.null()]),
default_sending_stream: z.union([z.string(), z.null()]),
email: z.string(),
full_name: z.string(),
is_active: z.boolean(),
owner_id: z.number().nullable(),
user_id: z.number(),
});
const outgoing_service_schema = z.object({
base_url: z.string(),
interface: z.number(),
token: z.string(),
});
const embedded_service_schema = z.object({
config_data: z.record(z.string()),
service_name: z.string(),
});
const services_schema = z.union([
z.array(outgoing_service_schema),
z.array(embedded_service_schema),
]);
const server_update_bot_schema = basic_bot_schema.extend({
services: services_schema,
});
const server_add_bot_schema = server_update_bot_schema.extend({
bot_type: z.number(),
email: z.string(),
is_active: z.boolean(),
});
export function all_user_ids(): number[] {
return [...bots.keys()];
}
export function add(bot_data: ServerAddBotData): void {
const {services: bot_services, ...clean_bot} = server_add_bot_schema.parse(bot_data);
bots.set(clean_bot.user_id, clean_bot);
services.set(clean_bot.user_id, bot_services);
}
export function del(bot_id: number): void {
bots.delete(bot_id);
services.delete(bot_id);
}
export function update(bot_id: number, bot_update: ServerUpdateBotData): void {
const bot = bots.get(bot_id)!;
Object.assign(bot, server_update_bot_schema.deepPartial().parse(bot_update));
// We currently only support one service per bot.
const service = services.get(bot_id)![0];
if (bot_update.services !== undefined && bot_update.services.length > 0) {
Object.assign(service, services_schema.parse(bot_update.services)[0]);
}
}
export function get_all_bots_for_current_user(): Bot[] {
const ret = [];
for (const bot of bots.values()) {
if (bot.owner_id !== null && people.is_my_user_id(bot.owner_id)) {
ret.push(bot);
}
}
return ret;
}
export function get_editable(): Bot[] {
const ret = [];
for (const bot of bots.values()) {
if (bot.is_active && bot.owner_id !== null && people.is_my_user_id(bot.owner_id)) {
ret.push(bot);
}
}
return ret;
}
export function get_all_bots_owned_by_user(user_id: number): Bot[] {
const ret = [];
for (const bot of bots.values()) {
if (bot.owner_id === user_id && bot.is_active) {
ret.push(bot);
}
}
return ret;
}
export function get(bot_id: number): Bot | undefined {
return bots.get(bot_id);
}
export function get_services(bot_id: number): Services | undefined {
return services.get(bot_id);
}
export function initialize(params: BotDataParams): void {
bots.clear();
for (const bot of params.realm_bots) {
add(bot);
}
}