snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,45 @@
{
"$schema": "https://json.schemastore.org/package",
"name": "@rocket.chat/core-typings",
"version": "8.9.0-develop",
"repository": {
"type": "git",
"url": "git+https://github.com/RocketChat/Rocket.Chat.git",
"directory": "packages/core-typings"
},
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"files": [
"/dist"
],
"scripts": {
".:build:build": "tsc -p tsconfig.json",
".:build:clean": "rimraf dist",
".:build:prepare": "ts-patch install && typia patch",
"build": "run-s .:build:prepare .:build:clean .:build:build",
"dev": "tsc --watch --preserveWatchOutput -p tsconfig.json",
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"test": "echo \"no tests\" && exit 1"
},
"dependencies": {
"@rocket.chat/icons": "^0.49.0",
"@rocket.chat/message-parser": "workspace:^",
"@rocket.chat/ui-kit": "workspace:~",
"typia": "patch:typia@npm%3A9.7.2#~/.yarn/patches/typia-npm-9.7.2-5c5d9c80b4.patch",
"zod": "~4.3.6"
},
"devDependencies": {
"@rocket.chat/apps-engine": "workspace:^",
"@types/express": "^4.17.25",
"eslint": "~9.39.5",
"mongodb": "6.16.0",
"npm-run-all": "~4.1.5",
"rimraf": "^6.0.1",
"ts-patch": "^3.3.0",
"typescript": "~5.9.3"
},
"volta": {
"extends": "../../package.json"
}
}

View File

@@ -0,0 +1,22 @@
import type { AbacAttributeStoreType, AbacPdpType } from './ServerAudit/IAuditServerAbacAction';
export enum AbacAccessOperation {
READ = 'read',
WRITE = 'write',
}
export enum AbacObjectType {
ROOM = 'room',
// Just room for now :)
}
export const isAbacPdpType = (value: unknown): value is AbacPdpType => value === 'local' || value === 'virtru';
export const isAbacAttributeStoreType = (value: unknown): value is AbacAttributeStoreType => value === 'local' || value === 'virtru';
export type AbacUserIdentifiers = {
usernames?: string[];
ids?: string[];
emails?: string[];
ldapIds?: string[];
};

View File

@@ -0,0 +1,76 @@
import typia from 'typia';
import type { IBanner } from './IBanner';
import type { ICalendarEvent } from './ICalendarEvent';
import type { CallHistoryItem } from './ICallHistoryItem';
import type { CloudConfirmationPollData, CloudRegistrationIntentData, CloudRegistrationStatus } from './ICloud';
import type { ICustomSound } from './ICustomSound';
import type { ICustomUserStatus } from './ICustomUserStatus';
import type { IEmailInbox } from './IEmailInbox';
import type { IEmojiCustom } from './IEmojiCustom';
import type { IIntegration } from './IIntegration';
import type { IIntegrationHistory } from './IIntegrationHistory';
import type { IInvite } from './IInvite';
import type { IMeApiUser } from './IMeApiUser';
import type { IMessage } from './IMessage';
import type { IModerationAudit, IModerationReport } from './IModerationReport';
import type { IOAuthApps } from './IOAuthApps';
import type { IPermission } from './IPermission';
import type { IReadReceiptWithUser } from './IReadReceipt';
import type { IRole } from './IRole';
import type { IRoom, IDirectoryChannelResult, IRoomAdmin } from './IRoom';
import type { DeviceManagementSession, DeviceManagementPopulatedSession } from './ISession';
import type { ISubscription } from './ISubscription';
import type { ITeam } from './ITeam';
import type { IUploadWithUser } from './IUpload';
import type { IUser, IDirectoryUserResult } from './IUser';
import type { VideoConference, VideoConferenceInstructions } from './IVideoConference';
import type { SlashCommand } from './SlashCommands';
import type { VideoConferenceCapabilities } from './VideoConferenceCapabilities';
import type { IImport } from './import/IImport';
import type { IMediaCall } from './mediaCalls/IMediaCall';
export const schemas = typia.json.schemas<
[
(
| ISubscription
| IInvite
| ICustomSound
| IEmojiCustom
| IMessage
| IOAuthApps
| IPermission
| IMediaCall
| IEmailInbox
| IImport
| ICalendarEvent
| IRole
| IRoom
| IRoomAdmin
| IDirectoryChannelResult
| IUser
| IDirectoryUserResult
| VideoConference
| VideoConferenceCapabilities
| VideoConferenceInstructions
| CloudConfirmationPollData
| CloudRegistrationIntentData
| CloudRegistrationStatus
| IModerationAudit
| IModerationReport
| IBanner
| IIntegration
| IIntegrationHistory
| IMeApiUser
| IReadReceiptWithUser
| ITeam
| IUploadWithUser
| DeviceManagementSession
| DeviceManagementPopulatedSession
),
CallHistoryItem,
ICustomUserStatus,
SlashCommand,
],
'3.0'
>();

View File

@@ -0,0 +1,253 @@
import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
import type { ExternalModuleName } from './license';
export type AppScreenshot = {
id: string;
appId: string;
fileName: string;
accessUrl: string;
thumbnailUrl: string;
createdAt: string;
modifiedAt: string;
};
export type AppPricingPlan = {
id: string;
enabled: boolean;
price: number;
trialDays: number;
strategy: string;
isPerSeat: boolean;
tiers?: {
perUnit: boolean;
minimum: number;
maximum: number;
price: number;
}[];
};
export enum AppSubscriptionStatus {
Trialing = 'trialing',
Active = 'active',
Cancelled = 'cancelled',
Cancelling = 'cancelling',
PastDue = 'pastDue',
}
export type AppSubscriptionInfo = {
typeOf: string;
status: AppSubscriptionStatus;
statusFromBilling: boolean;
isSeatBased: boolean;
seats: number;
maxSeats: number;
license: {
license: string;
version: number;
expireDate: string;
};
startDate: string;
periodEnd: string;
endDate: string;
isSubscribedViaBundle: boolean;
};
export type AppPermission = {
name: string;
required?: boolean;
};
export type PurchaseType = 'buy' | 'subscription';
export type AppRequestStats = {
appId: string;
totalSeen: number;
totalUnseen: number;
};
export type App = {
id: string;
iconFileData: string;
name: string;
appRequestStats: AppRequestStats;
author: {
name: string;
homepage: string;
support: string;
};
description: string;
shortDescription?: string;
privacyPolicySummary: string;
detailedDescription: {
raw: string;
rendered: string;
};
detailedChangelog: {
raw: string;
rendered: string;
};
categories: string[];
version: string;
versionIncompatible?: boolean;
addon?: ExternalModuleName;
installedAddon?: ExternalModuleName;
price: number;
purchaseType: PurchaseType;
pricingPlans: AppPricingPlan[];
iconFileContent: string;
installed?: boolean;
isEnterpriseOnly?: boolean;
isPurchased?: boolean;
isSubscribed: boolean;
bundledIn: {
bundleId: string;
bundleName: string;
apps: App[];
addonTierId?: string;
}[];
marketplaceVersion: string;
latest: App;
status?: AppStatus;
subscriptionInfo: AppSubscriptionInfo;
licenseValidation?: {
errors: { [key: string]: string };
warnings: { [key: string]: string };
};
tosLink: string;
privacyLink: string;
marketplace?: unknown;
modifiedAt: string;
permissions: AppPermission[];
languages: string[];
createdDate: string;
requestedEndUser?: boolean;
private: boolean;
documentationUrl: string;
migrated: boolean;
// Status of the app across the cluster (when deployment includes multiple instances)
clusterStatus?: {
instanceId: string;
status: AppStatus;
}[];
};
export type AppCategory = {
id: string;
title: string;
description: string;
hidden: boolean;
createdDate: string;
modifiedDate: string;
};
export type AppOverview = {
appId: string;
latest: {
internalId: string;
id: string;
name: string;
nameSlug: string;
version: string;
categories: string[];
languages: string[];
description: string;
privacyPolicySummary: string;
detailedDescription: {
raw: string;
rendered: string;
};
detailedChangelog: {
raw: string;
rendered: string;
};
requiredApiVersion: string;
permissions: {
name: string;
scopes?: string[];
}[];
author: {
name: string;
support: string;
homepage: string;
};
classFile: string;
iconFile: string;
iconFileData: string;
status: AppStatus;
isVisible: boolean;
createdDate: string;
modifiedDate: string;
isPurchased: boolean;
isSubscribed: boolean;
subscriptionInfo: AppSubscriptionInfo & { externallyManaged: boolean };
compileJobId: string;
compiled: boolean;
tosLink: string;
privacyLink: string;
};
isAddon: boolean;
addonId: string;
isEnterpriseOnly: boolean;
isBundle: boolean;
bundledAppIds: any[];
bundledIn: {
bundleId: string;
bundleName: string;
apps: App[];
addonTierId: string;
}[];
isPurchased: boolean;
isSubscribed: boolean;
subscriptionInfo: AppSubscriptionInfo & { externallyManaged: boolean };
price: number;
purchaseType: string;
pricingPlans: {
id: string;
enabled: boolean;
price: number;
trialDays: number;
strategy: string;
isPerSeat: boolean;
}[];
isUsageBased: boolean;
createdAt: string;
modifiedAt: string;
iconFileContent?: string;
marketplaceVersion?: string;
marketplace?: unknown;
};
export type FeaturedAppsSection = {
i18nLabel: string;
slug: string;
apps: AppOverview[];
};
export type AppRequestFilter = 'unseen' | 'seen' | 'notification-sent' | 'notification-not-sent' | '';
type AppRequestEndUser = {
id: string;
username: string;
name: string;
nickname: string;
emails: string[];
};
export type AppRequest = {
id: string;
appId: string;
requester: AppRequestEndUser;
admins: AppRequestEndUser[];
workspaceId: string;
message: string;
seen: boolean;
seenAt: string;
notificationSent: boolean;
notificationSentAt: string;
createdDate: string;
};

View File

@@ -0,0 +1,12 @@
import type { SelectOption } from '@rocket.chat/fuselage';
export type CustomFieldMetadata = {
name: string;
label?: string;
type: 'select' | 'text';
required?: boolean;
defaultValue?: any;
minLength?: number;
maxLength?: number;
options?: SelectOption[];
};

View File

@@ -0,0 +1,16 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IAbacAttributeDefinition {
/**
* Validation expectation (NOT enforced here, must be enforced by caller):
* /^[A-Za-z0-9_-]+$/
*/
key: string;
/**
* List of string values for this attribute key.
*/
values: string[];
}
export interface IAbacAttribute extends IRocketChatRecord, IAbacAttributeDefinition {}

View File

@@ -0,0 +1,27 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
interface IAnalyticsBase extends IRocketChatRecord {
type: 'messages' | 'users' | 'seat-request';
date: number;
}
interface IAnalyticsMessages extends IAnalyticsBase {
type: 'messages';
room: {
_id: string;
name?: string;
t: string;
usernames: string[];
};
}
interface IAnalyticsUsers extends IAnalyticsBase {
type: 'users';
}
export interface IAnalyticsSeatRequest extends IAnalyticsBase {
type: 'seat-request';
count: number;
}
export type IAnalytics = IAnalyticsMessages | IAnalyticsUsers | IAnalyticsSeatRequest;

View File

@@ -0,0 +1,3 @@
import type { IUpload } from './IUpload';
export interface IAvatar extends IUpload {}

View File

@@ -0,0 +1,49 @@
import type * as UiKit from '@rocket.chat/ui-kit';
import * as z from 'zod';
import { IRocketChatRecordSchema, type IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
import { TimestampSchema } from './utils';
export enum BannerPlatform {
Web = 'web',
Mobile = 'mobile',
}
export const IBannerSchema = IRocketChatRecordSchema.extend({
platform: z.array(z.enum(BannerPlatform)), // pĺatforms a banner could be shown
expireAt: TimestampSchema, // date when banner should not be shown anymore
startAt: TimestampSchema, // start date a banner should be presented
/** @deprecated a new `selector` field should be created for filtering instead */
roles: z.array(z.string()).optional().meta({ deprecated: true }),
createdBy: z.union([
z.object({
_id: z.string(),
username: z.string().optional(),
}),
z.enum(['cloud', 'system']),
]),
createdAt: TimestampSchema,
view: z.custom<UiKit.BannerView>(),
active: z.boolean().optional(),
inactivedAt: TimestampSchema.optional(),
snapshot: z.string().optional(),
dictionary: z.record(z.string(), z.record(z.string(), z.string())).optional(),
surface: z.enum(['banner', 'modal']),
});
export interface IBanner extends z.infer<typeof IBannerSchema>, IRocketChatRecord {}
export type InactiveBanner = IBanner & {
active: false;
inactivedAt: Date;
};
export const isInactiveBanner = (banner: IBanner): banner is InactiveBanner => banner.active === false;
export interface IBannerDismiss extends IRocketChatRecord {
userId: IUser['_id']; // user receiving the banner dismissed
bannerId: IBanner['_id']; // banner dismissed
dismissedAt: Date; // when is was dismissed
dismissedBy: Pick<IUser, '_id' | 'username'>; // who dismissed (usually the same as userId)
}

View File

@@ -0,0 +1,20 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
export interface ICalendarEvent extends IRocketChatRecord {
startTime: Date;
endTime?: Date;
uid: IUser['_id'];
subject: string;
description: string;
notificationSent: boolean;
externalId?: string | null;
meetingUrl?: string | null;
reminderMinutesBeforeStart?: number;
reminderTime?: Date;
busy?: boolean;
}

View File

@@ -0,0 +1,53 @@
import type { IMessage } from './IMessage/IMessage';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
export type CallHistoryItemState =
/** One of the users ended the call */
| 'ended'
/** Call was not answered */
| 'not-answered'
/** The call could not be established */
| 'failed'
/** The call was established, but it ended due to an error */
| 'error'
/** The call ended due to a transfer */
| 'transferred';
interface ICallHistoryItem extends IRocketChatRecord {
uid: IUser['_id'];
ts: Date;
callId: string;
direction: 'inbound' | 'outbound';
state: CallHistoryItemState;
}
interface IMediaCallHistoryItem extends ICallHistoryItem {
type: 'media-call';
external: boolean;
/* The call's duration, in seconds */
duration: number;
endedAt: Date;
}
export interface IInternalMediaCallHistoryItem extends IMediaCallHistoryItem {
external: false;
contactId: IUser['_id'];
contactName?: IUser['name'];
contactUsername?: IUser['username'];
rid?: IRoom['_id'];
messageId?: IMessage['_id']; // Id of the message that was sent after the call ended
}
export interface IExternalMediaCallHistoryItem extends IMediaCallHistoryItem {
external: true;
contactExtension: string;
}
export type CallHistoryItem = IInternalMediaCallHistoryItem | IExternalMediaCallHistoryItem;

View File

@@ -0,0 +1,43 @@
export type CloudRegistrationStatus = {
email: string;
token: string;
uniqueId: string;
workspaceId: string;
workspaceRegistered: boolean;
};
export type CloudRegistrationIntentData = {
device_code: string;
user_code: string;
verification_url?: string;
interval: number;
expires_in: number;
};
type CloudConfirmationPollDataPending = {
status: 'authorization_pending';
};
type CloudConfirmationPollDataSuccess = {
successful: boolean;
payload: {
workspaceId: string;
client_name: string;
client_id: string;
client_secret: string;
redirect_uris: Array<string>;
publicKey: string;
client_secret_expires_at: number;
registration_client_uri: string;
licenseData: {
version: number;
address: string;
license: string;
updatedAt: string;
modules?: string;
expireAt: string;
};
};
};
export type CloudConfirmationPollData = CloudConfirmationPollDataPending | CloudConfirmationPollDataSuccess;

View File

@@ -0,0 +1,10 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ICredentialToken extends IRocketChatRecord {
userInfo: {
username?: string;
attributes?: any;
profile?: Record<string, any>;
};
expireAt: Date;
}

View File

@@ -0,0 +1,10 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ICronHistoryItem extends IRocketChatRecord {
name: string;
intendedAt: Date;
startedAt: Date;
finishedAt?: Date;
result?: any;
error?: any;
}

View File

@@ -0,0 +1,7 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ICustomEmojiDescriptor extends IRocketChatRecord {
name: string;
aliases: string;
extension: string;
}

View File

@@ -0,0 +1,8 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ICustomSound extends IRocketChatRecord {
name: string;
extension: string;
src?: string;
random?: unknown;
}

View File

@@ -0,0 +1,4 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUserStatus } from './IUserStatus';
export interface ICustomUserStatus extends IUserStatus, IRocketChatRecord {}

View File

@@ -0,0 +1,32 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IEmailInbox extends IRocketChatRecord {
active: boolean;
name: string;
email: string;
description?: string;
senderInfo?: string;
department?: string;
smtp: {
server: string;
port: number;
username: string;
password: string;
secure: boolean;
};
imap: {
server: string;
port: number;
username: string;
password: string;
secure: boolean;
maxRetries: number;
};
_createdAt: Date;
_createdBy: {
_id: string;
username?: string;
} | null;
}
export type IEmailInboxPayload = Omit<IEmailInbox, '_createdAt' | '_createdBy' | '_updatedAt'>;

View File

@@ -0,0 +1,6 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IEmailMessageHistory extends IRocketChatRecord {
email: string;
createdAt?: Date;
}

View File

@@ -0,0 +1,3 @@
export interface IEmoji {
[x: string]: any;
}

View File

@@ -0,0 +1,8 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IEmojiCustom extends IRocketChatRecord {
name: string;
aliases: string[];
extension: string;
etag?: string;
}

View File

@@ -0,0 +1,32 @@
import type { FileProp } from './IMessage/MessageAttachment/Files/FileProp';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
import type { RoomType } from './RoomType';
export interface IExportOperation extends IRocketChatRecord {
roomList?: (
| {
roomId: string;
roomName: string;
userId: string | undefined;
exportedCount: number;
status: string;
type: RoomType;
targetFile: string;
}
| Record<string, never>
)[];
status: string;
fileList: FileProp[];
generatedFile?: string;
fileId: string;
userNameTable: Record<string, string>;
userData: IUser;
generatedUserFile: boolean;
generatedAvatar: boolean;
exportPath: string;
assetsPath: string;
createdAt: Date;
fullExport: boolean;
userId: IUser['_id'];
}

View File

@@ -0,0 +1,6 @@
import type { IncomingMessage } from 'node:http';
export interface IIncomingMessage extends IncomingMessage {
query: Record<string, any>;
body: Record<string, any>;
}

View File

@@ -0,0 +1,67 @@
import type { ILivechatPriority } from './ILivechatPriority';
import type { ILivechatVisitor } from './ILivechatVisitor';
import type { IMessage } from './IMessage';
import type { IOmnichannelServiceLevelAgreements } from './IOmnichannelServiceLevelAgreements';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IOmnichannelRoom, OmnichannelSourceType } from './IRoom';
import type { SelectedAgent } from './omnichannel/routing';
export enum LivechatInquiryStatus {
VERIFYING = 'verifying',
QUEUED = 'queued',
TAKEN = 'taken',
READY = 'ready',
OPEN = 'open',
}
// This is a subset of the IVisitor interface + channel related fields
// IMPORTANT: If you're adding a new field here, make sure to update the
// apps-engine's room converter to include it too
export interface IVisitor {
_id: string;
username: string;
token: string;
status: 'online' | 'busy' | 'away' | 'offline';
phone?: string | null;
lastMessageTs?: Date;
}
export interface ILivechatInquiryRecord extends IRocketChatRecord {
rid: string;
name: string;
ts: Date;
message: string;
status: LivechatInquiryStatus;
v: Pick<ILivechatVisitor, '_id' | 'username' | 'status' | 'name' | 'token' | 'phone' | 'activity'> & {
lastMessageTs?: Date;
};
t: 'l';
department?: string;
estimatedInactivityCloseTimeAt?: Date;
locked?: boolean;
lockedAt?: Date;
lastMessage?: IMessage & { token?: string };
defaultAgent?: SelectedAgent;
source?: {
type: OmnichannelSourceType;
};
// Note: for the sort order to be maintained, we're making priorityWeight and estimatedWaitingTimeQueue required
priorityId?: IOmnichannelRoom['priorityId'];
priorityWeight: ILivechatPriority['sortItem'];
slaId?: string;
estimatedWaitingTimeQueue: IOmnichannelServiceLevelAgreements['dueTimeInMinutes'];
}
export const isLivechatInquiryRecord = (record: unknown): record is ILivechatInquiryRecord =>
typeof record === 'object' &&
record !== null &&
'status' in record &&
typeof record.status === 'string' &&
['verifying', 'queued', 'taken', 'ready', 'open'].includes(record.status);
export type InquiryWithAgentInfo = Pick<ILivechatInquiryRecord, '_id' | 'rid' | 'name' | 'ts' | 'status' | 'department' | 'v'> & {
position?: number;
defaultAgent?: SelectedAgent;
};

View File

@@ -0,0 +1,23 @@
export interface IInstanceStatus {
_id: string;
_createdAt: Date;
name: string;
pid: number;
extraInformation: {
host: string;
nodeVersion: string;
port: string;
tcpPort: number;
os: {
type: string;
platform: string;
arch: string;
release: string;
uptime: number;
loadavg: number[];
totalmem: number;
freemem: number;
cpus: number;
};
};
}

View File

@@ -0,0 +1,115 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
export type IntegrationScriptEngine = 'isolated-vm';
export interface IIncomingIntegration extends IRocketChatRecord {
type: 'webhook-incoming';
_createdBy: Pick<IUser, 'username' | '_id'> | null;
_createdAt: Date;
userId: IUser['_id'];
username: string;
channel: string[];
token?: string;
scriptEnabled: boolean;
script?: string;
scriptCompiled?: string;
scriptError?: Pick<Error, 'name' | 'message' | 'stack'>;
/**
* When `true`, the integration script is stored as-is without Babel
* transpilation — matching the 9.0.0 default where Babel is removed
* entirely. Defaults to `false` (transpile with Babel for backwards
* compatibility). Deprecated field — removed in 9.0.0 together with
* the Babel transpilation path.
*/
skipTranspile?: boolean;
name: string;
enabled: boolean;
overrideDestinationChannelEnabled?: boolean;
alias?: string;
avatar?: string;
emoji?: string;
scriptEngine?: IntegrationScriptEngine;
}
export type OutgoingIntegrationEvent =
'sendMessage' | 'fileUploaded' | 'roomArchived' | 'roomCreated' | 'roomJoined' | 'roomLeft' | 'userCreated';
export interface IOutgoingIntegration extends IRocketChatRecord {
type: 'webhook-outgoing';
_createdBy: Pick<IUser, 'username' | '_id'> | null;
_createdAt: Date;
userId: IUser['_id'];
username: string;
channel: string[];
event: OutgoingIntegrationEvent;
targetRoom?: string;
urls?: string[];
triggerWords?: string[];
triggerWordAnywhere?: boolean;
token?: string;
scriptEnabled: boolean;
script?: string;
scriptCompiled?: string;
scriptError?: Pick<Error, 'name' | 'message' | 'stack'>;
/**
* When `true`, the integration script is stored as-is without Babel
* transpilation — matching the 9.0.0 default where Babel is removed
* entirely. Defaults to `false` (transpile with Babel for backwards
* compatibility). Deprecated field — removed in 9.0.0 together with
* the Babel transpilation path.
*/
skipTranspile?: boolean;
runOnEdits?: boolean;
retryFailedCalls?: boolean;
retryCount?: number;
retryDelay?: string;
impersonateUser?: boolean;
name: string;
enabled: boolean;
alias?: string;
avatar?: string;
emoji?: string;
scriptEngine?: IntegrationScriptEngine;
}
export type IIntegration = IIncomingIntegration | IOutgoingIntegration;
export type INewIncomingIntegration = Omit<
IIncomingIntegration,
'channel' | 'scriptCompiled' | 'scriptError' | '_createdBy' | '_createdAt' | 'userId' | 'token'
> & {
channel: string;
};
export type INewOutgoingIntegration = Omit<
IOutgoingIntegration,
'channel' | 'scriptCompiled' | 'scriptError' | '_createdAt' | '_createdBy' | 'userId'
> & {
channel?: string;
token?: string;
};
export type IUpdateIncomingIntegration = Omit<
IIncomingIntegration,
'type' | 'channel' | 'scriptCompiled' | 'scriptError' | '_createdBy' | '_createdAt' | 'userId' | 'token' | 'username'
> & {
channel?: string;
};
export type IUpdateOutgoingIntegration = Omit<
IOutgoingIntegration,
'type' | 'channel' | 'scriptCompiled' | 'scriptError' | '_createdAt' | '_createdBy' | 'userId'
> & {
channel?: string | string[];
};

View File

@@ -0,0 +1,33 @@
import type { OutgoingIntegrationEvent } from './IIntegration';
import type { IMessage } from './IMessage';
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IIntegrationHistory extends IRocketChatRecord {
type: string;
step: string;
integration: {
_id: string;
};
event: OutgoingIntegrationEvent;
_createdAt: Date;
data?: {
user?: any;
room?: any;
owner?: any;
message_id?: string;
channel_id?: string;
user_id?: string;
};
ranPrepareScript: boolean;
finished: boolean;
triggerWord?: string;
prepareSentMessage?: { channel: string; message: Partial<IMessage> }[];
processSentMessage?: { channel: string; message: Partial<IMessage> }[];
url?: string;
httpCallData?: Record<string, any>;
httpError?: any;
httpResult?: string;
error?: any;
errorStack?: any;
}

View File

@@ -0,0 +1,12 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IInvite extends IRocketChatRecord {
days: number;
maxUses: number;
rid: string;
userId: string;
createdAt: Date;
expires: Date | null;
uses: number;
url: string;
}

View File

@@ -0,0 +1,26 @@
import type { IUser } from './IUser';
export enum ILivechatAgentStatus {
AVAILABLE = 'available',
NOT_AVAILABLE = 'not-available',
}
export interface ILivechatAgent extends IUser {
statusLivechat: ILivechatAgentStatus;
livechat?: {
maxNumberSimultaneousChat: number;
};
livechatCount: number;
lastRoutingTime: Date;
livechatStatusSystemModified?: boolean;
openBusinessHours?: string[];
agentLockedAt?: Date;
agentLocked?: boolean;
}
export type AvailableAgentsAggregation = {
agentId: string;
username: string;
maxChatsForAgent: number;
queueInfo: { chats: number; chatsForDepartment?: number };
};

View File

@@ -0,0 +1,15 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatAgentActivity extends IRocketChatRecord {
agentId: string;
date: number;
lastStartedAt: Date;
availableTime: number;
serviceHistory: IServiceHistory[];
lastStoppedAt?: Date;
}
export interface IServiceHistory {
startedAt: Date;
stoppedAt: Date;
}

View File

@@ -0,0 +1,42 @@
import type { ILivechatDepartment } from './ILivechatDepartment';
import type { IRocketChatRecord } from './IRocketChatRecord';
export enum LivechatBusinessHourTypes {
DEFAULT = 'default',
CUSTOM = 'custom',
}
export enum LivechatBusinessHourBehaviors {
SINGLE = 'Single',
MULTIPLE = 'Multiple',
}
interface IBusinessHourTime {
time: string;
utc: { dayOfWeek: string; time: string };
cron: { dayOfWeek: string; time: string };
}
export interface IBusinessHourWorkHour {
day: string;
start: IBusinessHourTime;
finish: IBusinessHourTime;
open: boolean;
code: unknown;
}
export interface IBusinessHourTimezone {
name: string;
utc: string;
}
export interface ILivechatBusinessHour extends IRocketChatRecord {
name: string;
active: boolean;
type: LivechatBusinessHourTypes;
timezone: IBusinessHourTimezone;
ts: Date;
workHours: IBusinessHourWorkHour[];
/** Populated on the fly by the business-hours endpoints; never persisted with the record. */
departments?: Pick<ILivechatDepartment, '_id' | 'name'>[];
}

View File

@@ -0,0 +1,55 @@
import type { IVisitorEmail, IVisitorPhone } from './ILivechatVisitor';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IOmnichannelSource, OmnichannelSourceType } from './IRoom';
export interface ILivechatContactVisitorAssociation {
visitorId: string;
source: {
type: OmnichannelSourceType;
id?: IOmnichannelSource['id'];
};
}
export interface ILivechatContactChannel {
name: string;
verified: boolean;
visitor: ILivechatContactVisitorAssociation;
blocked: boolean;
field?: string;
value?: string;
verifiedAt?: Date;
details: IOmnichannelSource;
lastChat?: {
_id: string;
ts: Date;
};
}
export interface ILivechatContactConflictingField {
field: 'name' | 'manager' | `customFields.${string}`;
value: string;
}
export interface ILivechatContact extends IRocketChatRecord {
name: string;
phones?: IVisitorPhone[];
emails?: IVisitorEmail[];
contactManager?: string;
unknown?: boolean;
conflictingFields?: ILivechatContactConflictingField[];
customFields?: Record<string, string | unknown>;
channels: ILivechatContactChannel[];
createdAt: Date;
lastChat?: {
_id: string;
ts: Date;
};
importIds?: string[];
// Expected string format is YYYY-MM
activity?: string[];
// When preRegistration is true, the contact was added by an admin and it doesn't have any visitor association yet
// This contact may then be linked to new visitors that use the same email address or phone number
preRegistration?: boolean;
}

View File

@@ -0,0 +1,14 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatCustomField extends IRocketChatRecord {
label: string;
scope: 'visitor' | 'room';
visibility: string;
type?: string;
regexp?: string;
required?: boolean;
defaultValue?: string;
options?: string;
public?: boolean;
searchable?: boolean;
}

View File

@@ -0,0 +1,41 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatDepartment extends IRocketChatRecord {
name: string;
enabled: boolean;
description?: string;
showOnRegistration: boolean;
showOnOfflineForm: boolean;
requestTagBeforeClosingChat?: boolean;
email: string;
chatClosingTags?: string[];
offlineMessageChannelName: string;
numAgents: number;
businessHourId?: string;
fallbackForwardDepartment?: string;
archived?: boolean;
departmentsAllowedToForward?: string[];
maxNumberSimultaneousChat?: number;
parentId?: string;
ancestors?: string[];
allowReceiveForwardOffline?: boolean;
// extra optional fields
[k: string]: any;
}
export type LivechatDepartmentDTO = {
enabled: boolean;
name: string;
description?: string | undefined;
showOnRegistration: boolean;
email: string;
showOnOfflineForm: boolean;
requestTagBeforeClosingChat?: boolean | undefined;
chatClosingTags?: string[] | undefined;
fallbackForwardDepartment?: string | undefined;
departmentsAllowedToForward?: string[] | undefined;
allowReceiveForwardOffline?: boolean;
offlineMessageChannelName?: string | undefined;
abandonedRoomsCloseCustomMessage?: string | undefined;
waitingQueueMessage?: string | undefined;
};

View File

@@ -0,0 +1,10 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatDepartmentAgents extends IRocketChatRecord {
departmentId: string;
departmentEnabled: boolean;
agentId: string;
username: string;
count: number;
order: number;
}

View File

@@ -0,0 +1,20 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
/** @deprecated */
export interface ILivechatDepartmentRecord extends IRocketChatRecord {
name: string;
enabled: boolean;
description?: string;
showOnRegistration: boolean;
showOnOfflineForm: boolean;
requestTagBeforeClosingChat?: boolean;
email: string;
chatClosingTags?: string[];
offlineMessageChannelName: string;
numAgents: number;
businessHourId?: string;
fallbackForwardDepartment?: string;
departmentsAllowedToForward?: string[];
// extra optional fields
[k: string]: any;
}

View File

@@ -0,0 +1,10 @@
export interface ILivechatMonitor {
_id: string;
name: string;
username: string;
email?: string;
enabled: boolean;
numMonitors: number;
type: string;
visibility: string;
}

View File

@@ -0,0 +1,19 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export enum LivechatPriorityWeight {
LOWEST = 5,
LOW = 4,
MEDIUM = 3,
HIGH = 2,
HIGHEST = 1,
NOT_SPECIFIED = 99,
}
export interface ILivechatPriority extends IRocketChatRecord {
name?: string;
i18n: string;
sortItem: LivechatPriorityWeight;
// Whether the priority has been modified by the user or not
dirty: boolean;
}

View File

@@ -0,0 +1,15 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatTag extends IRocketChatRecord {
name: string;
description?: string;
numDepartments: number;
departments: Array<string>;
}
export type FindTagsResult = {
tags: ILivechatTag[];
count: number;
offset: number;
total: number;
};

View File

@@ -0,0 +1,8 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatTagRecord extends IRocketChatRecord {
name: string;
description: string;
numDepartments: number;
departments: Array<string>;
}

View File

@@ -0,0 +1,56 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export type ILivechatTriggerType = 'time-on-site' | 'page-url' | 'chat-opened-by-visitor' | 'after-guest-registration';
export interface ILivechatTriggerCondition {
name: ILivechatTriggerType;
value?: string | number;
}
export interface ILivechatSendMessageAction {
name: 'send-message';
params?: {
sender: 'queue' | 'custom';
msg: string;
name: string;
};
}
export interface ILivechatUseExternalServiceAction {
name: 'use-external-service';
params?: {
sender: 'queue' | 'custom';
name: string;
serviceUrl: string;
serviceTimeout: number;
serviceFallbackMessage: string;
};
}
export const isExternalServiceTrigger = (
trigger: ILivechatTrigger,
): trigger is ILivechatTrigger & { actions: ILivechatUseExternalServiceAction[] } => {
return trigger.actions.every((action) => action.name === 'use-external-service');
};
export type ILivechatTriggerAction = ILivechatSendMessageAction | ILivechatUseExternalServiceAction;
export interface ILivechatTrigger extends IRocketChatRecord {
name: string;
description: string;
enabled: boolean;
runOnce: boolean;
conditions: ILivechatTriggerCondition[];
actions: ILivechatTriggerAction[];
}
export interface ILivechatTriggerActionResponse {
_id: string;
response: {
statusCode: number;
contents: {
msg: string;
order: number;
}[];
};
}

View File

@@ -0,0 +1,7 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ILivechatUnitMonitor extends IRocketChatRecord {
monitorId: string;
unitId: string;
username: string;
}

View File

@@ -0,0 +1,71 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { UserStatus } from './UserStatus';
export interface IVisitorPhone {
phoneNumber: string;
}
export interface IVisitorLastChat {
_id: string;
ts: Date;
}
export interface IVisitorEmail {
address: string;
}
export interface IVisitorExternalIdentifier {
appId: string;
entityId: string;
metadata?: Record<string, unknown>;
}
export interface ILivechatVisitor extends IRocketChatRecord {
username: string;
ts: Date;
token: string;
department?: string;
name?: string;
phone?: IVisitorPhone[] | null;
lastChat?: IVisitorLastChat;
userAgent?: string;
ip?: string;
host?: string;
visitorEmails?: IVisitorEmail[];
externalIds?: IVisitorExternalIdentifier[];
status?: UserStatus;
lastAgent?: {
username: string;
agentId: string;
ts: Date;
};
livechatData?: Record<string, unknown>;
contactManager?: {
_id?: string;
username: string;
name?: string;
emails?: { address: string }[];
};
activity?: string[];
disabled?: boolean;
}
export interface ILivechatVisitorDTO {
id?: string;
token: string;
name?: string;
email?: string;
department?: string;
phone?: string;
username?: string;
customFields?: {
key: string;
value: string;
overwrite: boolean;
}[];
connectionData?: {
httpHeaders: Record<string, string | string[] | undefined>;
};
}
export const isILivechatVisitor = (a: any): a is ILivechatVisitor => typeof a?.token === 'string';

View File

@@ -0,0 +1,18 @@
export interface ILogItem {
appId: string;
endTime: string;
entries: {
args: string[];
caller: string;
severity: string;
timestamp: string;
method?: string;
}[];
instanceId: string;
method: string;
startTime: string;
totalTime: number;
_createdAt: string;
_id: string;
_updatedAt: string;
}

View File

@@ -0,0 +1,6 @@
export interface ILoginCode {
_id: string;
userId: string;
createdAt: Date;
expireAt: Date;
}

View File

@@ -0,0 +1,123 @@
export interface ILoginServiceConfiguration {
_id: string;
service: string;
}
export type OAuthConfiguration = {
custom: boolean;
clientId: string;
secret: string;
serverURL: string;
tokenPath: string;
identityPath: string;
authorizePath: string;
scope: string;
accessTokenParam: string;
buttonLabelText: string;
buttonLabelColor: string;
loginStyle: '' | 'redirect' | 'popup';
buttonColor: string;
tokenSentVia: 'header' | 'payload';
identityTokenSentVia: 'default' | 'header' | 'payload';
keyField: 'username' | 'email';
usernameField: string;
emailField: string;
nameField: string;
avatarField: string;
rolesClaim: string;
groupsClaim: string;
channelsMap: string;
channelsAdmin: string;
mergeUsers: boolean;
mergeUsersDistinctServices: boolean;
mapChannels: boolean;
mergeRoles: boolean;
rolesToSync: string;
showButton: boolean;
addAutopublishFields?: {
forLoggedInUser: string[];
forOtherUsers: string[];
};
pkce?: boolean;
};
export type FacebookOAuthConfiguration = Omit<Partial<OAuthConfiguration>, 'clientId'> & {
appId: OAuthConfiguration['clientId'];
};
export type TwitterOAuthConfiguration = Omit<Partial<OAuthConfiguration>, 'clientId'> & {
consumerKey: OAuthConfiguration['clientId'];
};
export type LinkedinOAuthConfiguration = Partial<OAuthConfiguration> & {
clientConfig: {
requestPermissions?: string[];
};
};
export type CASConfiguration = {
enabled: boolean;
base_url: string;
login_url: string;
buttonLabelText: string;
buttonLabelColor: string;
buttonColor: string;
width: number;
height: number;
autoclose: boolean;
};
export type SAMLSignatureAlgorithm = 'SHA1' | 'SHA256' | 'SHA384' | 'SHA512';
export type SAMLConfiguration = {
buttonLabelText: string;
buttonLabelColor: string;
buttonColor: string;
clientConfig: {
provider?: string;
};
entryPoint: string;
idpSLORedirectURL: string;
usernameNormalize: 'None' | 'Lowercase';
immutableProperty: 'Username' | 'EMail';
generateUsername: boolean;
debug: boolean;
nameOverwrite: boolean;
mailOverwrite: boolean;
issuer: string;
logoutBehaviour: 'SAML' | 'Local';
defaultUserRole: string;
secret: {
privateKey: string;
publicCert: string;
cert: string;
algorithm: SAMLSignatureAlgorithm;
};
signatureValidationType: 'All' | 'Response' | 'Assertion' | 'Either' | 'None';
validateLogoutRequestSignature: boolean;
validateLogoutResponseSignature: boolean;
userDataFieldMap: string;
allowedClockDrift: number;
channelsAttributeUpdate: boolean;
includePrivateChannelsInUpdate: boolean;
customAuthnContext: string;
authnContextComparison: 'better' | 'exact' | 'maximum' | 'minimum';
identifierFormat: string;
nameIDPolicyTemplate: string;
authnContextTemplate: string;
authRequestTemplate: string;
logoutResponseTemplate: string;
logoutRequestTemplate: string;
metadataCertificateTemplate: string;
metadataTemplate: string;
};
export type LoginServiceConfiguration = ILoginServiceConfiguration &
(
| Partial<OAuthConfiguration>
| Partial<FacebookOAuthConfiguration>
| Partial<TwitterOAuthConfiguration>
| Partial<LinkedinOAuthConfiguration>
| Partial<CASConfiguration>
| Partial<SAMLConfiguration>
);

View File

@@ -0,0 +1,17 @@
export interface IMatrixFederationStatistics {
enabled: boolean;
maximumSizeOfPublicRoomsUsers: number;
biggestRoom: {
_id: string;
name: string;
usersCount: number;
} | null;
smallestRoom: {
_id: string;
name: string;
usersCount: number;
} | null;
amountOfExternalUsers: number;
amountOfFederatedRooms: number;
externalConnectedServers: { quantity: number; servers: string[] };
}

View File

@@ -0,0 +1,72 @@
import type { IUser, IUserCalendar } from './IUser';
/**
* Public service fields exposed by getUserInfo (password hash is never leaked).
*/
export type IMeApiUserServices = {
github?: Record<string, unknown>;
gitlab?: Record<string, unknown>;
email2fa?: { enabled: boolean; changedAt?: Date };
totp?: { enabled: boolean };
password: { exists: boolean };
email?: { verificationTokens?: Array<{ token: string; address: string; when: Date }> };
cloud?: { accessToken?: string; refreshToken?: string; expiresAt: Date };
resume?: { loginTokens?: Array<Record<string, unknown>> };
emailCode?: Array<{ code: string; expire: Date }>;
};
/**
* User document fields projected by getBaseUserFields() plus full `services` (e.g. GET /api/v1/me),
* after getUserInfo() reshapes `settings`, `services`, `email`, and adds `avatarUrl` / `isOAuthUser`.
*/
type MeProjectedUserFields = Pick<
IUser,
| 'name'
| 'username'
| 'nickname'
| 'emails'
| 'status'
| 'statusDefault'
| 'statusText'
| 'statusSource'
| 'statusExpiresAt'
| 'statusConnection'
| 'bio'
| 'avatarOrigin'
| 'utcOffset'
| 'language'
| 'roles'
| 'active'
| 'defaultRoom'
| 'customFields'
| 'requirePasswordChange'
| 'requirePasswordChangeReason'
| 'banners'
| '_updatedAt'
| 'avatarETag'
| 'abacAttributes'
| 'oauth'
| 'createdAt'
| 'lastLogin'
| 'ldap'
>;
export type IMeApiUser = { _id: IUser['_id'] } & Partial<MeProjectedUserFields> & {
avatarUrl: string;
isOAuthUser: boolean;
settings: {
profile: Record<string, unknown>;
preferences?: Record<string, unknown>;
calendar: IUserCalendar;
};
email?: string;
services?: IMeApiUserServices;
/** Present when projected via getBaseUserFields (not on IUser). */
enableAutoAway?: boolean;
/** Present when projected via getBaseUserFields (not on IUser). */
idleTimeLimit?: number;
/** Present when projected via getBaseUserFields (not on IUser). */
statusLivechat?: string;
/** Present when projected via getBaseUserFields (not on IUser). */
openBusinessHours?: string[];
};

View File

@@ -0,0 +1,413 @@
import type { UrlWithStringQuery } from 'node:url';
import type Icons from '@rocket.chat/icons';
import type { Root } from '@rocket.chat/message-parser';
import type { MessageSurfaceLayout } from '@rocket.chat/ui-kit';
import type { ILivechatPriority } from '../ILivechatPriority';
import type { ILivechatVisitor } from '../ILivechatVisitor';
import type { IOmnichannelServiceLevelAgreements } from '../IOmnichannelServiceLevelAgreements';
import type { IRocketChatRecord } from '../IRocketChatRecord';
import type { IRoom } from '../IRoom';
import type { IUser } from '../IUser';
import type { FileProp } from './MessageAttachment/Files/FileProp';
import type { MessageAttachment } from './MessageAttachment/MessageAttachment';
export type MessageUrl = {
url: string;
source?: string;
meta: Record<string, string>;
headers?: { contentLength?: string; contentType?: string };
ignoreParse?: boolean;
parsedUrl?: Pick<UrlWithStringQuery, 'host' | 'hash' | 'pathname' | 'protocol' | 'port' | 'query' | 'search' | 'hostname'>;
};
const TeamMessageTypesValues = [
'removed-user-from-team',
'added-user-to-team',
'ult',
'user-converted-to-team',
'user-converted-to-channel',
'user-removed-room-from-team',
'user-deleted-room-from-team',
'user-added-room-to-team',
'ujt',
] as const;
const LivechatMessageTypesValues = [
'livechat_navigation_history',
'livechat_transfer_history',
'livechat_transcript_history',
'livechat_video_call',
'livechat_transfer_history_fallback',
'livechat-close',
'livechat-started',
'omnichannel_priority_change_history',
'omnichannel_sla_change_history',
'omnichannel_placed_chat_on_hold',
'omnichannel_on_hold_chat_resumed',
] as const;
const MessageTypes = [
'e2e',
'uj',
'ui',
'uir',
'ul',
'ru',
'au',
'mute_unmute',
'r',
'ut',
'wm',
'rm',
'subscription-role-added',
'subscription-role-removed',
'room-archived',
'room-unarchived',
'room_changed_privacy',
'room_changed_description',
'room_changed_announcement',
'room_changed_avatar',
'room_changed_topic',
'room_e2e_enabled',
'room_e2e_disabled',
'user-muted',
'user-unmuted',
'user-banned',
'user-unbanned',
'room-removed-read-only',
'room-set-read-only',
'room-allowed-reacting',
'room-disallowed-reacting',
'command',
'videoconf',
'message_pinned',
'message_pinned_e2e',
'new-moderator',
'moderator-removed',
'new-owner',
'owner-removed',
'new-leader',
'leader-removed',
'discussion-created',
'abac-removed-user-from-room',
...TeamMessageTypesValues,
...LivechatMessageTypesValues,
] as const;
export type MessageTypesValues = (typeof MessageTypes)[number];
export type TokenType = 'code' | 'inlinecode' | 'bold' | 'italic' | 'strike' | 'link';
export type Token = {
token: string;
text: string;
type?: TokenType;
noHtml?: string;
} & TokenExtra;
export type TokenExtra = {
highlight?: boolean;
noHtml?: string;
};
export type MessageMention = {
type?: 'user' | 'team'; // mentions for 'all' and 'here' doesn't have type
_id: string;
name?: string;
username?: string;
fname?: string; // incase of channel mentions
};
interface IEncryptedContent {
algorithm: string;
ciphertext: string;
}
interface IEncryptedContentV1 extends IEncryptedContent {
algorithm: 'rc.v1.aes-sha2';
ciphertext: string;
}
interface IEncryptedContentV2 extends IEncryptedContent {
algorithm: 'rc.v2.aes-sha2';
ciphertext: string;
iv: string; // Initialization Vector
kid: string; // ID of the key used to encrypt the message
}
interface IEncryptedContentFederation extends IEncryptedContent {
algorithm: 'm.megolm.v1.aes-sha2';
ciphertext: string;
}
export type EncryptedContent = IEncryptedContentV1 | IEncryptedContentV2 | IEncryptedContentFederation;
export interface IMessage extends IRocketChatRecord {
rid: IRoom['_id'];
msg: string;
tmid?: string;
tshow?: boolean;
ts: Date;
mentions?: MessageMention[];
groupable?: boolean;
channels?: Pick<IRoom, '_id' | 'name'>[];
u: Required<Pick<IUser, '_id' | 'username'>> & Pick<IUser, 'name'>;
blocks?: MessageSurfaceLayout;
alias?: string;
md?: Root;
_hidden?: boolean;
imported?: boolean;
replies?: IUser['_id'][];
location?: {
type: 'Point';
coordinates: [number, number];
};
starred?: { _id: IUser['_id'] }[];
pinned?: boolean;
pinnedAt?: Date;
pinnedBy?: Pick<IUser, '_id' | 'username'>;
unread?: boolean;
temp?: boolean;
drid?: IRoom['_id'];
tlm?: Date;
dcount?: number;
tcount?: number;
t?: MessageTypesValues;
e2e?: 'pending' | 'done';
e2eMentions?: { e2eUserMentions?: string[]; e2eChannelMentions?: string[] };
urls?: MessageUrl[];
/** @deprecated Deprecated */
actionLinks?: {
icon: keyof typeof Icons;
i18nLabel: unknown;
label: string;
method_id: string;
params: string;
}[];
/** @deprecated Deprecated in favor of files */
file?: FileProp;
fileUpload?: {
publicFilePath: string;
type?: string;
size?: number;
};
files?: FileProp[];
attachments?: MessageAttachment[];
reactions?: {
[key: string]: { names?: string[]; usernames: string[]; federationReactionEventIds?: Record<string, string> };
};
private?: boolean;
/* @deprecated */
bot?: Record<string, any>;
sentByEmail?: boolean;
webRtcCallEndTs?: Date;
role?: string;
avatar?: string;
emoji?: string;
// Tokenization fields
tokens?: Token[];
html?: string;
// Messages sent from visitors have this field
token?: string;
federation?: {
eventId: string;
version?: number;
};
/* used when message type is "omnichannel_sla_change_history" */
slaData?: {
definedBy: Pick<IUser, '_id' | 'username'>;
sla?: Pick<IOmnichannelServiceLevelAgreements, 'name'>;
};
/* used when message type is "omnichannel_priority_change_history" */
priorityData?: {
definedBy: Pick<IUser, '_id' | 'username'>;
priority?: Pick<ILivechatPriority, 'name' | 'i18n'>;
};
customFields?: Record<string, any>;
content?: EncryptedContent;
// Read receipts migration flag
receiptsArchived?: boolean;
}
export type EncryptedMessageContent = Required<Pick<IMessage, 'content'>>;
export function isEncryptedMessageContent(value: unknown): value is EncryptedMessageContent {
return (
typeof value === 'object' &&
value !== null &&
'content' in value &&
typeof value.content === 'object' &&
value.content !== null &&
'algorithm' in value.content &&
(value.content.algorithm === 'rc.v1.aes-sha2' || value.content.algorithm === 'rc.v2.aes-sha2') &&
'ciphertext' in value.content &&
typeof value.content.ciphertext === 'string' &&
(value.content.algorithm === 'rc.v1.aes-sha2' ||
(value.content.algorithm === 'rc.v2.aes-sha2' &&
'iv' in value.content &&
typeof value.content.iv === 'string' &&
'kid' in value.content &&
typeof value.content.kid === 'string'))
);
}
interface ISystemMessage extends IMessage {
t: MessageTypesValues;
}
export interface IEditedMessage extends IMessage {
editedAt: Date;
editedBy: Pick<IUser, '_id' | 'username'>;
}
export const isEditedMessage = (message: IMessage): message is IEditedMessage =>
'editedAt' in message &&
(message as { editedAt?: unknown }).editedAt instanceof Date &&
'editedBy' in message &&
typeof (message as { editedBy?: unknown }).editedBy === 'object' &&
(message as { editedBy?: unknown }).editedBy !== null &&
'_id' in (message as IEditedMessage).editedBy &&
typeof (message as IEditedMessage).editedBy._id === 'string';
export const isSystemMessage = (message: IMessage): message is ISystemMessage =>
message.t !== undefined && MessageTypes.includes(message.t);
export const isDeletedMessage = (message: IMessage): message is IEditedMessage => isEditedMessage(message) && message.t === 'rm';
interface IFederatedMessage extends IMessage {
federation: {
eventId: string;
};
}
export const isMessageFromMatrixFederation = (message: IMessage): message is IFederatedMessage =>
'federation' in message && Boolean(message.federation?.eventId);
export interface ITranslatedMessage extends IMessage {
translations: { [key: string]: string } & { original?: string };
translationProvider: string;
autoTranslateShowInverse?: boolean;
autoTranslateFetching?: boolean;
}
export const isTranslatedMessage = (message: IMessage): message is ITranslatedMessage => 'translations' in message;
export interface IThreadMainMessage extends IMessage {
tcount: number;
tlm: Date;
replies: IUser['_id'][];
}
export interface IThreadMessage extends IMessage {
tmid: string;
}
export const isThreadMainMessage = (message: IMessage): message is IThreadMainMessage => 'tcount' in message && 'tlm' in message;
export const isThreadMessage = (message: IMessage): message is IThreadMessage => !!message.tmid;
export interface IDiscussionMessage extends IMessage {
drid: string;
dlm?: Date;
dcount: number;
}
export const isDiscussionMessage = (message: IMessage): message is IDiscussionMessage => !!message.drid;
export interface IOmnichannelSystemMessage extends IMessage {
navigation?: {
page: {
title: string;
location: {
href: string;
};
token?: string;
};
};
transferData?: {
comment: string;
transferredBy: {
name?: string;
username: string;
};
transferredTo: {
name?: string;
username: string;
};
nextDepartment?: {
_id: string;
name?: string;
};
scope: 'department' | 'agent' | 'queue';
};
requestData?: {
type: 'visitor' | 'user';
visitor?: ILivechatVisitor;
user?: Pick<IUser, '_id' | 'name' | 'username' | 'utcOffset'> | null;
};
webRtcCallEndTs?: Date;
comment?: string;
}
export interface IMessageInbox extends IMessage {
// email inbox fields
email?: {
references?: string[];
messageId?: string;
thread?: string[];
};
}
export const isIMessageInbox = (message: IMessage): message is IMessageInbox => 'email' in message;
export interface IE2EEMessage extends IMessage {
t: 'e2e';
e2e: 'pending' | 'done';
content: EncryptedContent;
}
export interface IE2EEPinnedMessage extends IMessage {
t: 'message_pinned_e2e';
attachments: [MessageAttachment & { content: EncryptedContent }];
}
interface IVideoConfMessage extends IMessage {
t: 'videoconf';
}
export const isE2EEMessage = (message: IMessage): message is IE2EEMessage => message.t === 'e2e';
export const isE2EEPinnedMessage = (message: IMessage): message is IE2EEPinnedMessage => message.t === 'message_pinned_e2e';
export const isVideoConfMessage = (message: IMessage): message is IVideoConfMessage => message.t === 'videoconf';
export type IMessageWithPendingFileImport = IMessage & {
_importFile: {
downloadUrl: string;
id: string;
size: number;
name: string;
external: boolean;
source: 'slack' | 'hipchat-enterprise';
original: Record<string, any>;
rocketChatUrl?: string;
downloaded?: boolean;
};
};
interface IMessageFromVisitor extends IMessage {
token: string;
}
export const isMessageFromVisitor = (message: IMessage): message is IMessageFromVisitor => 'token' in message;

View File

@@ -0,0 +1,13 @@
import type { MessageAttachmentBase } from '../MessageAttachmentBase';
import type { FileAttachmentProps } from './FileAttachmentProps';
import type { FileProp } from './FileProp';
export type AudioAttachmentProps = {
audio_url: string;
audio_type: string;
audio_size?: number;
file?: FileProp;
} & MessageAttachmentBase;
export const isFileAudioAttachment = (attachment: FileAttachmentProps): attachment is AudioAttachmentProps & { type: 'file' } =>
'audio_url' in attachment;

View File

@@ -0,0 +1,19 @@
import type { MessageAttachmentBase } from '../MessageAttachmentBase';
import type { AudioAttachmentProps } from './AudioAttachmentProps';
import type { FileProp } from './FileProp';
import type { ImageAttachmentProps } from './ImageAttachmentProps';
import type { VideoAttachmentProps } from './VideoAttachmentProps';
type CommonFileProps = {
type: 'file';
fileId?: FileProp['_id'];
};
export type FileAttachmentProps =
| (CommonFileProps & VideoAttachmentProps)
| (CommonFileProps & ImageAttachmentProps)
| (CommonFileProps & AudioAttachmentProps)
| (CommonFileProps & MessageAttachmentBase);
export const isFileAttachment = (attachment: MessageAttachmentBase): attachment is FileAttachmentProps =>
'type' in attachment && (attachment as any).type === 'file';

View File

@@ -0,0 +1,10 @@
export type FileProp = {
_id: string;
name: string;
type: string;
format: string;
size: number;
// used to filter out thumbnails in email notifications
// may not exist in old messages
typeGroup?: string;
};

View File

@@ -0,0 +1,20 @@
import type { MessageAttachmentBase } from '../MessageAttachmentBase';
import type { FileAttachmentProps } from './FileAttachmentProps';
import type { FileProp } from './FileProp';
export type ImageAttachmentProps = {
image_dimensions?: {
width: number;
height: number;
};
image_preview?: string;
image_url: string;
image_type?: string;
image_size?: number;
/** Accessibility alternative text for the image. Kept separate from `description` so it is not rendered as a visible caption. */
image_alt?: string;
file?: FileProp;
} & MessageAttachmentBase;
export const isFileImageAttachment = (attachment: FileAttachmentProps): attachment is ImageAttachmentProps & { type: 'file' } =>
'image_url' in attachment;

View File

@@ -0,0 +1,11 @@
import type { MessageAttachmentBase } from '../MessageAttachmentBase';
import type { FileProp } from './FileProp';
export type RemovedFileAttachmentProps = MessageAttachmentBase & {
type: 'removed-file';
/* If fileId is missing, then every file in the message has been removed */
fileId?: FileProp['_id'];
};
export const isRemovedFileAttachment = (attachment: MessageAttachmentBase): attachment is RemovedFileAttachmentProps =>
'type' in attachment && attachment.type === 'removed-file';

View File

@@ -0,0 +1,13 @@
import type { MessageAttachmentBase } from '../MessageAttachmentBase';
import type { FileAttachmentProps } from './FileAttachmentProps';
import type { FileProp } from './FileProp';
export type VideoAttachmentProps = {
video_url: string;
video_type: string;
video_size: number;
file?: FileProp;
} & MessageAttachmentBase;
export const isFileVideoAttachment = (attachment: FileAttachmentProps): attachment is VideoAttachmentProps & { type: 'file' } =>
'video_url' in attachment;

View File

@@ -0,0 +1,6 @@
export * from './AudioAttachmentProps';
export * from './FileAttachmentProps';
export type * from './FileProp';
export * from './ImageAttachmentProps';
export * from './RemovedFileAttachmentProps';
export * from './VideoAttachmentProps';

View File

@@ -0,0 +1,14 @@
import type { FileProp } from './Files';
import type { FileAttachmentProps } from './Files/FileAttachmentProps';
import type { RemovedFileAttachmentProps } from './Files/RemovedFileAttachmentProps';
import type { MessageAttachmentAction } from './MessageAttachmentAction';
import type { MessageAttachmentDefault } from './MessageAttachmentDefault';
import type { MessageQuoteAttachment } from './MessageQuoteAttachment';
export type MessageAttachment =
MessageAttachmentAction | MessageAttachmentDefault | FileAttachmentProps | MessageQuoteAttachment | RemovedFileAttachmentProps;
export type FilesAndAttachments = {
files: FileProp[];
attachments: MessageAttachment[];
};

View File

@@ -0,0 +1,25 @@
// DEPRECATED
import type { Root } from '@rocket.chat/message-parser';
import type { MessageAttachmentBase } from './MessageAttachmentBase';
type Action = {
msgId?: string;
type: 'button';
text: string;
md?: Root;
msg?: string;
url?: string;
image_url?: string;
is_webview?: true;
msg_in_chat_window?: true;
msg_processing_type?: 'sendMessage' | 'respondWithMessage' | 'respondWithQuotedMessage';
};
export type MessageAttachmentAction = {
button_alignment?: 'horizontal' | 'vertical';
actions: Array<Action>;
} & MessageAttachmentBase;
export const isActionAttachment = (attachment: MessageAttachmentBase): attachment is MessageAttachmentAction => 'actions' in attachment;

View File

@@ -0,0 +1,32 @@
import type { Root } from '@rocket.chat/message-parser';
export type MessageAttachmentBase = {
id?: string;
title?: string;
ts?: Date;
collapsed?: boolean;
/** description isn't being used on client for non-image attachments, we're keeping it for backward compatibility */
description?: string;
descriptionMd?: Root;
text?: string;
md?: Root;
size?: number;
format?: string;
title_link?: string;
title_link_download?: boolean;
encryption?: {
iv: string;
key: JsonWebKey;
};
hashes?: {
sha256: string;
};
};
export type EncryptedMessageAttachment = MessageAttachmentBase & {
encryption: Required<MessageAttachmentBase>['encryption'];
};
export const isEncryptedMessageAttachment = (attachment: MessageAttachmentBase): attachment is EncryptedMessageAttachment => {
return attachment?.encryption !== undefined && typeof attachment.encryption === 'object';
};

View File

@@ -0,0 +1,43 @@
import type { Root } from '@rocket.chat/message-parser';
import type { MessageAttachment } from './MessageAttachment';
import type { MessageAttachmentBase } from './MessageAttachmentBase';
export type MarkdownFields = 'text' | 'pretext' | 'fields';
export type MessageAttachmentDefault = {
author_icon?: string;
author_link?: string;
author_name?: string;
fields?: {
short?: boolean;
title: string;
value: string | number | boolean;
}[];
// footer
// footer_icon
image_url?: string;
/** Accessibility alternative text for the image. Kept separate from `description` so it is not rendered as a visible caption. */
image_alt?: string;
image_dimensions?: {
width: number;
height: number;
};
mrkdwn_in?: Array<MarkdownFields>;
pretext?: string;
text?: string;
md?: Root;
thumb_url?: string;
color?: string;
attachments?: MessageAttachment[];
/** Encrypted content from e2e messages, preserved in pin attachments */
content?: object; // TODO: check if MessageAttachmentDefault[content] is a valid type it does not seem to be used anywhere
} & MessageAttachmentBase;

View File

@@ -0,0 +1,17 @@
import type { Root } from '@rocket.chat/message-parser';
import type { MessageAttachment } from './MessageAttachment';
import type { MessageAttachmentBase } from './MessageAttachmentBase';
export type MessageQuoteAttachment = {
author_name: string;
author_link?: string;
author_icon: string;
message_link: string;
text: string;
md?: Root;
attachments?: Array<MessageAttachment>; // TODO this is causing issues to define a model, see @ts-expect-error at apps/meteor/app/api/server/v1/channels.ts:274
} & MessageAttachmentBase;
export const isQuoteAttachment = (attachment: MessageAttachment): attachment is MessageQuoteAttachment =>
'message_link' in attachment && attachment.message_link !== undefined;

View File

@@ -0,0 +1,12 @@
import type { MessageAttachment } from './MessageAttachment';
import type { MessageAttachmentDefault } from './MessageAttachmentDefault';
interface ITranslatedMessageAttachment extends MessageAttachmentDefault {
translations: { [language: string]: string };
}
export const isTranslatedAttachment = (attachment: MessageAttachment): attachment is ITranslatedMessageAttachment =>
'translations' in attachment;
export const isTranslatedMessageAttachment = (attachments: MessageAttachment[]): attachments is ITranslatedMessageAttachment[] =>
attachments?.some(isTranslatedAttachment);

View File

@@ -0,0 +1,7 @@
export * from './Files';
export type * from './MessageAttachment';
export * from './MessageAttachmentAction';
export * from './MessageAttachmentBase';
export type * from './MessageAttachmentDefault';
export * from './MessageQuoteAttachment';
export * from './TranslatedMessageAttachment';

View File

@@ -0,0 +1,2 @@
export * from './IMessage';
export * from './MessageAttachment';

View File

@@ -0,0 +1,13 @@
export interface IMethodConnection {
id: string;
close(fn: (...args: any[]) => void): void;
onClose(fn: (...args: any[]) => void): void;
clientAddress: string;
httpHeaders: Record<string, any>;
/**
* Hashed login token carried by REST connections. Unlike DDP connections, REST requests do not
* register the login token in `Accounts._accountData`, so it is exposed here for the two-factor
* authorization check to resolve the token without mutating global account data.
*/
token?: string;
}

View File

@@ -0,0 +1,47 @@
import type { IMessage } from './IMessage/IMessage';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
export interface IModerationReport extends IRocketChatRecord {
message?: IMessage;
room?: Pick<IRoom, '_id' | 'name' | 'fname' | 't' | 'federated' | 'prid'>;
reportedUser?: Pick<IUser, '_id' | 'username' | 'name' | 'emails' | 'createdAt'>;
description: string;
ts: Date | string;
reportedBy: Pick<IUser, '_id' | 'username' | 'name' | 'createdAt'>;
/**
* Right now we're assuming neither Room Info or User Info changes.
* There's no update method referencing reports as of now (6.3.0-develop).
* This means that a possible user or room change will not be reflected in the report.
*/
moderationInfo?: {
moderatedBy: IUser['_id'];
hiddenAt: Date;
action: string;
reason: string;
};
_hidden?: boolean;
}
export type MessageReport = Omit<IModerationReport, 'reportedUser'> & {
room: Exclude<IModerationReport['room'], undefined>;
message: Exclude<IModerationReport['message'], undefined>;
};
export type UserReport = Omit<IModerationReport, 'message' | 'room'> & {
reportedUser: Exclude<IModerationReport['reportedUser'], undefined>;
};
export interface IModerationAudit {
userId: IUser['_id'];
username: IUser['username'];
name: IUser['name'];
message: IMessage['msg'];
msgId: IMessage['_id'];
roomIds: IRoom['_id'][];
ts: IModerationReport['ts'];
rooms: MessageReport['room'][];
count: number;
isUserDeleted: boolean;
}

View File

@@ -0,0 +1,80 @@
import type { ICalendarEvent } from './ICalendarEvent';
import type { IMessage } from './IMessage';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRoom } from './IRoom';
import type { ISubscription } from './ISubscription';
export interface INotificationItemPush {
type: 'push';
data: {
payload: {
sender: {
_id: string;
username: string;
name?: string;
};
type: string;
};
roomName: string;
username: string;
message: string;
badge: number;
category: string;
};
}
export interface INotificationItemEmail {
type: 'email';
data: {
to: string;
subject: string;
html: string;
data: {
room_path: string;
};
from: string;
};
}
export type NotificationItem = INotificationItemPush | INotificationItemEmail;
export interface INotification extends IRocketChatRecord {
uid: string;
rid: string;
mid: string;
ts: Date;
schedule?: Date;
sending?: Date;
error?: string;
items: NotificationItem[];
}
export interface INotificationDesktop {
title: string;
text: string;
icon?: string;
duration?: number;
payload: {
_id: IMessage['_id'];
rid: IMessage['rid'];
tmid?: IMessage['tmid'];
sender: IMessage['u'];
type: IRoom['t'];
name: IRoom['name'];
message: {
msg: IMessage['msg'];
t?: IMessage['t'];
content?: IMessage['content'];
};
audioNotificationValue: ISubscription['audioNotificationValue'];
};
}
export interface ICalendarNotification {
title: string;
text: string;
payload: {
_id: ICalendarEvent['_id'];
startTimeUtc?: string;
};
}

View File

@@ -0,0 +1,34 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
export enum NPSStatus {
OPEN = 'open',
SENDING = 'sending',
SENT = 'sent',
CLOSED = 'closed',
}
export interface INps extends IRocketChatRecord {
startAt: Date; // start date a banner should be presented
expireAt: Date; // date when banner should not be shown anymore
createdBy: Pick<IUser, '_id' | 'username'>;
createdAt: Date;
status: NPSStatus;
}
export enum INpsVoteStatus {
NEW = 'new',
SENDING = 'sending',
SENT = 'sent',
}
export interface INpsVote extends IRocketChatRecord {
npsId: INps['_id'];
ts: Date;
identifier: string; // voter identifier
roles?: IUser['roles']; // voter roles
score: number;
comment: string;
status: INpsVoteStatus;
sentAt?: Date;
}

View File

@@ -0,0 +1,10 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IOAuthAccessToken extends IRocketChatRecord {
accessToken: string;
expires?: Date;
clientId: string;
userId: string;
refreshToken?: string;
refreshTokenExpiresAt?: Date;
}

View File

@@ -0,0 +1,19 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IOAuthApps extends IRocketChatRecord {
name: string;
active: boolean;
clientId: string;
clientSecret?: string;
redirectUri: string;
_createdAt: Date;
_createdBy: {
_id: string;
username: string;
};
_updatedBy?: {
_id: string;
username?: string;
} | null;
appId?: string;
}

View File

@@ -0,0 +1,9 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IOAuthAuthCode extends IRocketChatRecord {
authCode: string;
clientId: string;
userId: string;
expires: Date;
redirectUri: string;
}

View File

@@ -0,0 +1,8 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IOAuthRefreshToken extends IRocketChatRecord {
refreshToken: string;
expires?: Date;
clientId: string;
userId: string;
}

View File

@@ -0,0 +1,6 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IOEmbedCache extends IRocketChatRecord {
data: any;
updatedAt: Date; // TODO: this field name differs from `_updatedAt` on `IRocketChatRecord`, should we unify this?
}

View File

@@ -0,0 +1,37 @@
import type { MessageAttachment } from './IMessage';
export type OEmbedMeta = {
[key: string]: string;
} & {
oembedHtml: string | undefined;
oembedUrl: string | string[];
};
export type OEmbedUrlContent = {
urlObj: URL;
headers: { [k: string]: string };
body: string;
statusCode: number;
};
export type OEmbedProvider = {
urls: RegExp[];
endPoint?: string;
getHeaderOverrides?: () => { [k: string]: string };
};
export type OEmbedUrlContentResult = {
headers: { [key: string]: string };
body: string;
statusCode: number;
attachments?: MessageAttachment[];
};
export type OEmbedUrlWithMetadata = {
url: string;
meta: OEmbedMeta;
headers: { [k: string]: string };
content: OEmbedUrlContent;
};
export const isOEmbedUrlWithMetadata = (value: any): value is OEmbedUrlWithMetadata => 'meta' in value;

View File

@@ -0,0 +1,7 @@
import type { IUser } from './IUser';
export interface IOmnichannelAgent extends IUser {
statusLivechat: OmnichannelAgentStatus;
}
export type OmnichannelAgentStatus = 'available' | 'not-available';

View File

@@ -0,0 +1,24 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IOmnichannelBusinessUnit extends IRocketChatRecord {
name: string;
visibility: 'public' | 'private';
type: string;
numMonitors: number;
numDepartments: number;
// Units don't have ancestors per se, but we need TS to know it can access the property from the collection
ancestors?: string[];
}
export type OmnichannelBusinessUnitPayload = {
unitData: {
name: string;
visibility: string;
enabled?: boolean;
description?: string;
email?: string;
showOnOfflineForm?: boolean;
};
unitMonitors: { monitorId: string; username: string }[];
unitDepartments: { departmentId: string }[];
};

View File

@@ -0,0 +1,18 @@
import type { ILivechatDepartment } from './ILivechatDepartment';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
export interface IOmnichannelCannedResponse extends IRocketChatRecord {
shortcut: string;
text: string;
scope: string;
tags: string[];
// userId is optional, its only required when scope === 'user'
userId?: IUser['_id'];
departmentId?: ILivechatDepartment['_id'];
createdBy: {
_id: IUser['_id'];
username: string;
};
_createdAt: Date;
}

View File

@@ -0,0 +1,4 @@
export interface IOmnichannelCustomAgent {
agentId: string;
username: string;
}

View File

@@ -0,0 +1,11 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export const DEFAULT_SLA_CONFIG = {
ESTIMATED_WAITING_TIME_QUEUE: 9999999,
};
export interface IOmnichannelServiceLevelAgreements extends IRocketChatRecord {
name: string;
description?: string;
dueTimeInMinutes: number;
}

View File

@@ -0,0 +1,4 @@
export interface IPassword {
plain?: string;
sha256?: string;
}

View File

@@ -0,0 +1,13 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IPermission extends IRocketChatRecord {
roles: string[];
// TODO: migrate settings with group and section with null to undefined
group?: string | null;
section?: string | null;
groupPermissionId?: string;
level?: 'settings';
sectionPermissionId?: string;
settingId?: string;
sorter?: number;
}

View File

@@ -0,0 +1,23 @@
import type { IPushToken } from './IPushToken';
export interface IPushNotificationConfig {
from?: string;
badge?: number;
sound?: string;
priority?: number;
title?: string;
text?: string;
payload?: Record<string, any>;
userId: string;
notId?: number;
gcm?: {
style: string;
image: string;
};
apn?: {
category?: string;
expirationSeconds?: number;
};
useVoipToken?: boolean;
skipTokenId?: IPushToken['_id'];
}

View File

@@ -0,0 +1,17 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { ILoginToken } from './IUser';
export const pushTokenTypes = ['gcm', 'apn'] as const;
export type IPushTokenTypes = (typeof pushTokenTypes)[number];
export interface IPushToken extends IRocketChatRecord {
token: Partial<Record<IPushTokenTypes, string>>;
appName: string;
userId: string;
enabled: boolean;
authToken: ILoginToken['hashedToken'];
metadata?: Record<string, unknown>;
createdAt: Date;
voipToken?: string;
}

View File

@@ -0,0 +1,16 @@
import type { IMessage } from './IMessage/IMessage';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
export interface IReadReceipt extends IRocketChatRecord {
token?: string;
messageId: IMessage['_id'];
roomId: IRoom['_id'];
userId: IUser['_id'];
ts: Date;
}
export interface IReadReceiptWithUser extends IReadReceipt {
user?: Pick<IUser, '_id' | 'name' | 'username'> | undefined;
}

View File

@@ -0,0 +1,9 @@
import type { IMessage } from './IMessage/IMessage';
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IReport extends IRocketChatRecord {
message: IMessage;
description: string;
ts: Date;
userId: string;
}

View File

@@ -0,0 +1,55 @@
export interface IRocketChatAssetConstraint {
type: string;
extensions: string[];
width?: number;
height?: number;
}
export interface IRocketChatAssetCache {
path: string;
cacheable: boolean;
where: string;
type: string;
content?: Buffer;
extension?: string;
url: string;
size?: number;
uploadDate?: Date;
contentType?: string;
hash: string;
sourceMapUrl?: string;
}
export interface IRocketChatAsset {
label: string;
constraints: IRocketChatAssetConstraint;
defaultUrl?: string;
url?: string;
wizard?: {
step: number;
order: number;
};
cache?: IRocketChatAssetCache;
}
export interface IRocketChatAssets {
logo: IRocketChatAsset;
logo_dark: IRocketChatAsset;
background: IRocketChatAsset;
background_dark: IRocketChatAsset;
favicon_ico: IRocketChatAsset;
favicon: IRocketChatAsset;
favicon_16: IRocketChatAsset;
favicon_32: IRocketChatAsset;
favicon_192: IRocketChatAsset;
favicon_512: IRocketChatAsset;
touchicon_180: IRocketChatAsset;
touchicon_180_pre: IRocketChatAsset;
tile_70: IRocketChatAsset;
tile_144: IRocketChatAsset;
tile_150: IRocketChatAsset;
tile_310_square: IRocketChatAsset;
tile_310_wide: IRocketChatAsset;
safari_pinned: IRocketChatAsset;
livechat_widget_logo: IRocketChatAsset;
}

View File

@@ -0,0 +1,17 @@
import type { WithId } from 'mongodb';
import * as z from 'zod';
import { TimestampSchema } from './utils';
export const IRocketChatRecordSchema = z.object({
_id: z.string(),
_updatedAt: TimestampSchema,
});
export interface IRocketChatRecord extends z.infer<typeof IRocketChatRecordSchema> {}
export type RocketChatRecordDeleted<T> = WithId<T> & {
_updatedAt: Date;
_deletedAt: Date;
__collection__: string;
};

View File

@@ -0,0 +1,9 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface IRole extends IRocketChatRecord {
description: string;
mandatory2fa?: boolean;
name: string;
protected: boolean;
scope: 'Users' | 'Subscriptions';
}

View File

@@ -0,0 +1,412 @@
import type { IAbacAttributeDefinition } from './IAbacAttribute';
import type { ILivechatDepartment } from './ILivechatDepartment';
import type { ILivechatPriority } from './ILivechatPriority';
import type { ILivechatVisitor } from './ILivechatVisitor';
import type { IMessage, MessageTypesValues } from './IMessage';
import type { IOmnichannelServiceLevelAgreements } from './IOmnichannelServiceLevelAgreements';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
import type { RoomType } from './RoomType';
import type { Branded } from './utils';
export interface IRoom extends IRocketChatRecord {
t: RoomType;
name?: string;
fname?: string;
msgs: number;
default?: boolean;
broadcast?: boolean;
featured?: true;
announcement?: string;
joinCodeRequired?: boolean;
announcementDetails?: {
style?: string;
} | null;
encrypted?: boolean;
// The existence of an abac attribute definition indicates that ABAC is enabled for the room
abacAttributes?: IAbacAttributeDefinition[];
topic?: string;
reactWhenReadOnly?: boolean;
// TODO: this boolean might be an accident
sysMes?: MessageTypesValues[] | boolean;
u: Pick<IUser, '_id' | 'username' | 'name'>;
uids?: Array<string>;
lastMessage?: IMessage;
lm?: Date;
usersCount: number;
webRtcCallStartTime?: Date;
servedBy?: {
_id: string;
};
prid?: string;
avatarETag?: string;
teamMain?: boolean;
teamId?: string;
teamDefault?: boolean;
open?: boolean;
autoTranslateLanguage?: string;
autoTranslate?: boolean;
unread?: number;
alert?: boolean;
hideUnreadStatus?: boolean;
hideMentionStatus?: boolean;
muted?: string[];
unmuted?: string[];
usernames?: string[];
ts?: Date;
cl?: boolean;
ro?: boolean;
favorite?: boolean;
archived?: boolean;
description?: string;
e2eKeyId?: string;
/* @deprecated */
federated?: boolean;
/* @deprecated */
customFields?: Record<string, any>;
usersWaitingForE2EKeys?: { userId: IUser['_id']; ts: Date }[];
/**
* @deprecated Using `boolean` is deprecated. Use `number` instead.
*/
rolePrioritiesCreated?: number | boolean;
}
export type IDirectoryChannelResult = Pick<
IRoom,
| '_id'
| 't'
| 'usersCount'
| 'name'
| 'fname'
| 'description'
| 'topic'
| 'lastMessage'
| 'ts'
| 'archived'
| 'default'
| 'featured'
| 'prid'
| 'teamId'
| 'teamMain'
| 'federated'
> & {
belongsTo?: string;
roomsCount?: number;
};
export const isRoomWithJoinCode = (room: Partial<IRoom>): room is IRoomWithJoinCode =>
'joinCodeRequired' in room && (room as any).joinCodeRequired === true;
export interface IRoomWithJoinCode extends IRoom {
joinCodeRequired: true;
joinCode: string;
}
export interface IRoomFederated extends IRoom {
_id: Branded<string, 'IRoomFederated'>;
federated: true;
}
export interface IRoomNativeFederated extends IRoomFederated {
federation: {
version: number;
// Matrix's room ID. Example: !XqJXqZxXqJXq:matrix.org
mrid: string;
origin: string;
};
}
export const isRoomFederated = (room: Partial<IRoom>): room is IRoomFederated => 'federated' in room && room.federated === true;
export const isRoomNativeFederated = (room: Partial<IRoom>): room is IRoomNativeFederated =>
isRoomFederated(room) && 'federation' in room && room.federation !== undefined;
export interface ICreatedRoom extends IRoom {
rid: string;
inserted: boolean;
}
export interface ITeamRoom extends IRoom {
teamMain: boolean;
teamId: string;
}
export const isTeamRoom = (room: Partial<IRoom>): room is ITeamRoom => !!room.teamMain;
export const isPrivateTeamRoom = (room: Partial<IRoom>): room is ITeamRoom => isTeamRoom(room) && room.t === 'p';
export const isPublicTeamRoom = (room: Partial<IRoom>): room is ITeamRoom => isTeamRoom(room) && room.t === 'c';
export const isDiscussion = (room: Partial<IRoom>): room is IRoom => !!room.prid;
export const isPrivateDiscussion = (room: Partial<IRoom>): room is IRoom => isDiscussion(room) && room.t === 'p';
export const isPublicDiscussion = (room: Partial<IRoom>): room is IRoom => isDiscussion(room) && room.t === 'c';
export const isPublicRoom = (room: Partial<IRoom>): room is IRoom => room.t === 'c';
export const isPrivateRoom = (room: Partial<IRoom>): room is IRoom => room.t === 'p';
export const isABACManagedRoom = (room: Partial<IRoom>): room is IRoom & { abacAttributes: IAbacAttributeDefinition[] } =>
room?.t === 'p' && Array.isArray(room?.abacAttributes) && room.abacAttributes.length > 0;
export interface IDirectMessageRoom extends Omit<IRoom, 'default' | 'featured' | 'u' | 'name'> {
t: 'd';
uids: Array<string>;
usernames: Array<NonNullable<IUser['username']>>;
}
export const isDirectMessageRoom = (room: Partial<IRoom> | IDirectMessageRoom): room is IDirectMessageRoom => room.t === 'd';
export const isMultipleDirectMessageRoom = (room: IRoom | IDirectMessageRoom): room is IDirectMessageRoom =>
isDirectMessageRoom(room) && room.uids.length > 2;
export enum OmnichannelSourceType {
WIDGET = 'widget',
EMAIL = 'email',
SMS = 'sms',
APP = 'app',
API = 'api',
OTHER = 'other', // catch-all source type
}
export interface IOmnichannelSource {
// TODO: looks like this is not so required as the definition suggests
// The source, or client, which created the Omnichannel room
type: OmnichannelSourceType;
// An optional identification of external sources, such as an App
id?: string;
// A human readable alias that goes with the ID, for post analytical purposes
alias?: string;
// A label to be shown in the room info
label?: string;
// The sidebar icon
sidebarIcon?: string;
// The default sidebar icon
defaultIcon?: string;
// The destination of the message (e.g widget host, email address, whatsapp number, etc)
destination?: string;
}
export interface IOmnichannelSourceFromApp extends IOmnichannelSource {
type: OmnichannelSourceType.APP;
id: string;
label: string;
sidebarIcon?: string;
defaultIcon?: string;
alias?: string;
destination?: string;
}
export interface IOmnichannelGenericRoom extends Omit<IRoom, 'default' | 'featured' | 'broadcast'> {
t: 'l';
v: Pick<ILivechatVisitor, '_id' | 'username' | 'status' | 'name' | 'token' | 'activity'> & {
lastMessageTs?: Date;
phone?: string;
};
email?: {
// Data used when the room is created from an email, via email Integration.
inbox: string;
thread: string[];
replyTo: string;
subject?: string;
};
source: IOmnichannelSource;
// Note: this field is used only for email transcripts. For Pdf transcripts, we have a separate field.
transcriptRequest?: {
email: string; // the email address to send the transcript to
subject: string; // the subject of the email
requestedAt: Date;
requestedBy: Pick<IUser, '_id' | 'username' | 'name' | 'utcOffset'>;
};
servedBy?: {
_id: string;
ts: Date;
username: IUser['username'];
};
onHold?: boolean;
departmentId?: string;
lastMessage?: IMessage & { token?: string };
tags?: string[];
closedAt?: Date;
metrics?: {
serviceTimeDuration?: number;
};
// set to true when the room is waiting for a response from the visitor
waitingResponse: any;
// contains information about the last response from an agent
responseBy?: {
_id: string;
username: string;
// when the agent first responded to the visitor after the latest message from visitor
// this will reset when the visitor sends a new message
firstResponseTs: Date;
// when the agent last responded to the visitor
// This is almost the same as firstResponseTs, but here we hold the timestamp of the last response
// and it gets updated after each message from agent
// So if an agent sends multiple messages to visitor, then firstResponseTs will store timestamp
// of their first reply, and lastMessageTs will store timestamp of their latest response
lastMessageTs: Date;
};
livechatData: any;
queuedAt?: Date;
status?: 'queued' | 'taken' | 'ready'; // TODO: missing types for this
ts: Date;
label?: string;
crmData?: unknown;
// optional keys for closed rooms
closer?: 'user' | 'visitor';
closedBy?: {
_id: string;
username: IUser['username'];
};
closingMessage?: IMessage;
departmentAncestors?: string[];
contactId?: string;
}
export interface IOmnichannelRoom extends IOmnichannelGenericRoom {
t: 'l';
omnichannel?: {
predictedVisitorAbandonmentAt: Date;
};
// sms field is used when the room is created from one of the internal SMS integrations (e.g. Twilio)
sms?: {
from: string;
};
// Following props are used for priorities feature
priorityId?: string;
priorityWeight: ILivechatPriority['sortItem']; // It should always have a default value for sorting mechanism to work
// Following props are used for SLA feature
slaId?: string;
estimatedWaitingTimeQueue: IOmnichannelServiceLevelAgreements['dueTimeInMinutes']; // It should always have a default value for sorting mechanism to work
// The ID of the pdf file generated for the transcript
// This will help if we want to have this file shown on other places of the UI
pdfTranscriptFileId?: string;
metrics?: {
serviceTimeDuration?: number;
chatDuration?: number;
v?: {
lq: Date;
};
servedBy?: {
lr: Date;
};
response?: {
tt: number;
total: number;
avg: number;
ft: number;
fd?: number;
};
reaction?: {
tt: number;
ft: number;
fd?: number;
};
};
// Both fields are being used for the auto transfer feature for unanswered chats
// which is controlled by Livechat_auto_transfer_chat_timeout setting
autoTransferredAt?: Date;
autoTransferOngoing?: boolean;
verified?: boolean;
}
export type IOmnichannelRoomClosingInfo = Pick<IOmnichannelGenericRoom, 'closer' | 'closedBy' | 'closedAt' | 'tags'> & {
serviceTimeDuration?: number;
chatDuration: number;
};
export type IOmnichannelRoomWithDepartment = IOmnichannelRoom & { department?: ILivechatDepartment };
export const isOmnichannelRoom = (room: Pick<IRoom, 't'>): room is IOmnichannelRoom & IRoom => room.t === 'l';
export const isOmnichannelSourceFromApp = (source: IOmnichannelSource): source is IOmnichannelSourceFromApp => {
return source?.type === OmnichannelSourceType.APP;
};
export type IOmnichannelRoomInfo = Pick<Partial<IOmnichannelRoom>, 'sms' | 'email'> & Pick<IOmnichannelRoom, 'source'>;
export type IOmnichannelRoomExtraData = Pick<Partial<IOmnichannelRoom>, 'customFields' | 'source'> & { sla?: string };
export type IOmnichannelInquiryExtraData = IOmnichannelRoomExtraData & { priority?: string };
export type RoomAdminFieldsType =
| '_id'
| 'prid'
| 'fname'
| 'name'
| 't'
| 'cl'
| 'u'
| 'usernames'
| 'ts'
| 'usersCount'
| 'muted'
| 'unmuted'
| 'ro'
| 'default'
| 'favorite'
| 'featured'
| 'reactWhenReadOnly'
| 'topic'
| 'msgs'
| 'archived'
| 'teamId'
| 'teamMain'
| 'announcement'
| 'description'
| 'customFields'
| 'broadcast'
| 'uids'
| 'avatarETag'
| 'abacAttributes';
export type IRoomAdmin = Pick<IRoom, RoomAdminFieldsType>;
export type IRoomAbacRedaction = { abacAttributesRedacted?: boolean };
export type IRoomAdminWithAbacRedaction = IRoomAdmin & IRoomAbacRedaction;
export interface IRoomWithRetentionPolicy extends IRoom {
retention: {
enabled?: boolean;
maxAge: number;
filesOnly: boolean;
excludePinned: boolean;
ignoreThreads: boolean;
overrideGlobal?: boolean;
};
}
export const ROOM_ROLE_PRIORITY_MAP = {
owner: 0,
leader: 250,
moderator: 500,
default: 10000,
};

View File

@@ -0,0 +1,7 @@
export interface ISamlUsedAssertions {
_id: string;
assertionId: string;
issuer: string;
expireAt: Date;
createdAt: Date;
}

View File

@@ -0,0 +1,88 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IUser } from './IUser';
import type {
IServerEventAbacActionPerformed,
IServerEventAbacAttributeChanged,
IServerEventAbacAttributeStoreSwitched,
IServerEventAbacObjectAttributeChanged,
IServerEventAbacObjectAttributesRemoved,
IServerEventAbacSubjectAttributeChanged,
} from './ServerAudit/IAuditServerAbacAction';
import type { IServerEventSettingsChanged } from './ServerAudit/IAuditServerSettingEvent';
import type { IServerEventUserChanged } from './ServerAudit/IAuditUserChangedEvent';
export enum ServerEventType {
FAILED_LOGIN_ATTEMPT = 'failed-login-attempt',
LOGIN = 'login',
}
export interface IServerEvent extends IRocketChatRecord {
t: ServerEventType | keyof IServerEvents;
ts: Date;
// @deprecated
ip: string;
// @deprecated
u?: Partial<Pick<IUser, '_id' | 'username'>>;
actor?: IAuditServerActor;
data?: AuditServerEventPayloadItem[];
}
export interface IAuditServerUserActor {
type: 'user';
_id: string;
username: string;
ip: string;
useragent: string;
}
export interface IAuditServerSystemActor {
type: 'system';
reason?: string;
}
export interface IAuditServerAppActor {
type: 'app';
_id: string;
reason?: string;
}
export type IAuditServerActor = IAuditServerUserActor | IAuditServerSystemActor | IAuditServerAppActor;
interface IAuditServerEvent {
_id: string;
t: string;
ts: Date;
actor: IAuditServerActor;
}
type AuditServerEventPayloadItem = {
key: string;
value: unknown;
};
export interface IAuditServerEventType<E extends AuditServerEventPayloadItem> extends IAuditServerEvent {
data: E[];
}
export interface IServerEvents {
'abac.subject.attribute.changed': IServerEventAbacSubjectAttributeChanged;
'abac.object.attribute.changed': IServerEventAbacObjectAttributeChanged;
'abac.attribute.changed': IServerEventAbacAttributeChanged;
'abac.action.performed': IServerEventAbacActionPerformed;
'abac.object.attributes.removed': IServerEventAbacObjectAttributesRemoved;
'abac.attribute.store.switched': IServerEventAbacAttributeStoreSwitched;
'settings.changed': IServerEventSettingsChanged;
'user.changed': IServerEventUserChanged;
}
type KeyValuePair = { key: string; value: any };
type ArrayToObject<T extends readonly KeyValuePair[]> = {
[K in T[number] as K['key']]: K['value'];
};
export type ExtractDataToParams<T extends IAuditServerEventType<any>> =
T extends IAuditServerEventType<any> ? ArrayToObject<T['data']> : never;

View File

@@ -0,0 +1,22 @@
export interface IServerInfo {
build: {
arch: string;
cpus: number;
date: string;
freeMemory: number;
nodeVersion: string;
osRelease: string;
platform: string;
totalMemory: number;
};
commit: {
author?: string;
branch?: string;
date?: string;
hash?: string;
subject?: string;
tag?: string;
};
marketplaceApiVersion: string;
version: string;
}

View File

@@ -0,0 +1,86 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRole } from './IRole';
import type { IUser } from './IUser';
export interface ISessionDevice {
type: string;
name: string;
longVersion: string;
os: {
name: string;
// Empty values are stripped at capture time (SAUMonitor.removeEmptyProps), so version may be absent at runtime.
version?: string;
};
version: string;
}
export interface ISession extends IRocketChatRecord {
type: 'session' | 'computed-session' | 'user_daily';
mostImportantRole: IRole['_id'];
userId: string;
lastActivityAt?: Date;
device?: ISessionDevice;
roles: string[];
year: number;
month: number;
day: number;
instanceId: string;
sessionId: string;
createdAt: Date;
host: string;
ip: string;
loginAt: Date;
logoutAt?: Date;
closedAt?: Date;
logoutBy?: IUser['_id'];
loginToken?: string;
searchTerm: string;
}
type SessionAggregationResult = {
year: number;
month: number;
day: number;
};
export type UserSessionAggregationResult = SessionAggregationResult & {
data: UserSessionAggregation[];
};
export type DeviceSessionAggregationResult = SessionAggregationResult & {
data: DeviceSessionAggregation[];
};
export type OSSessionAggregationResult = SessionAggregationResult & {
data: OSSessionAggregation[];
};
export type UserSessionAggregation = Pick<ISession, '_id'> & {
count: number;
sessions: number;
roles: { role: string; count: number; sessions: number; time: number }[];
};
export type DeviceSessionAggregation = Pick<ISession, '_id'> & {
type: string;
name: string;
version: string;
count: number;
time: number;
};
export type OSSessionAggregation = Pick<ISession, '_id'> & {
name: string;
version: string;
count: number;
time: number;
};
export type DeviceManagementSession = Pick<ISession, '_id' | 'sessionId' | 'device' | 'host' | 'ip' | 'logoutAt' | 'userId' | 'loginAt'> & {
current?: boolean;
};
export type DeviceManagementPopulatedSession = DeviceManagementSession & {
// The users $lookup uses preserveNullAndEmptyArrays, so an unmatched session has no _user.
_user?: Pick<IUser, 'name' | 'username' | 'avatarETag' | 'avatarOrigin'>;
};

View File

@@ -0,0 +1,281 @@
import type { IRocketChatAssetConstraint } from './IRocketChatAssets';
import type { IRocketChatRecord } from './IRocketChatRecord';
export enum SettingEditor {
COLOR = 'color',
EXPRESSION = 'expression',
}
export type SettingValueMultiSelect = (string | number)[];
export type SettingValueRoomPick = { _id: string; name?: string }[];
export type SettingValueAction = { method: string; path: string };
export type SettingValue =
| string
| boolean
| number
| SettingValueMultiSelect
| SettingValueRoomPick
| SettingValueAction
| Date
| { url?: string; defaultUrl?: string }
| { method: 'GET' | 'POST' | 'PUT' | 'DELETE'; path: string }
| undefined
| null;
export interface ISettingSelectOption {
key: string | number;
i18nLabel: string;
}
export type ISetting = ISettingBase | ISettingEnterprise | ISettingColor | ISettingCode | ISettingAction | ISettingAsset | ISettingRange;
type EnableQuery = string | { _id: string; value: any } | { _id: string; value: any }[];
export type SettingValidationRule = {
query: Record<string, unknown>;
appliesWhen?: { _id: string; value: unknown } | { _id: string; value: unknown }[];
};
type SettingValidation = SettingValidationRule[] | string;
export interface ISettingBase extends IRocketChatRecord {
type:
| 'boolean'
| 'timezone'
| 'string'
| 'relativeUrl'
| 'password'
| 'int'
| 'select'
| 'multiSelect'
| 'language'
| 'color'
| 'font'
| 'code'
| 'action'
| 'asset'
| 'roomPick'
| 'group'
| 'date'
| 'lookup'
| 'range'
| 'timespan';
public: boolean;
env: boolean;
// TODO: migrate settings with group and section with null to undefined
group?: string | null;
section?: string;
tab?: string;
i18nLabel: string;
value: SettingValue;
packageValue: SettingValue;
blocked: boolean;
enableQuery?: EnableQuery;
displayQuery?: EnableQuery;
validation?: SettingValidation;
sorter: number;
properties?: unknown;
enterprise?: boolean;
requiredOnWizard?: boolean;
hidden?: boolean;
modules?: Array<string>;
invalidValue?: SettingValue;
valueSource?: 'packageValue' | 'processEnvValue';
secret?: boolean;
i18nDescription?: string;
autocomplete?: boolean;
processEnvValue?: SettingValue;
meteorSettingsValue?: SettingValue;
ts: Date;
createdAt: Date;
multiline?: boolean;
values?: Array<ISettingSelectOption>;
placeholder?: string;
lookupEndpoint?: string;
wizard?: {
step: number;
order: number;
} | null;
persistent?: boolean; // todo: remove
readonly?: boolean; // todo: remove
alert?: string; // todo: check if this is still used
private?: boolean; // todo: remove
}
export interface ISettingGroup {
_id: string;
hidden: boolean;
blocked: boolean;
ts?: Date;
sorter: number;
i18nLabel: string;
displayQuery?: EnableQuery;
i18nDescription: string;
value?: undefined;
type: 'group';
alert?: string; // todo: check if this is needed
}
interface ISettingEnterprise extends ISettingBase {
enterprise: true;
invalidValue: SettingValue;
}
export interface ISettingColor extends ISettingBase {
type: 'color';
editor: SettingEditor;
packageEditor?: SettingEditor;
}
interface ISettingCode extends ISettingBase {
type: 'code';
code?: string;
}
type SettingActionEndpoint = { method: 'GET' | 'POST' | 'PUT' | 'DELETE'; path: string };
interface ISettingAction extends ISettingBase {
type: 'action';
value: string | SettingActionEndpoint;
actionText?: string;
}
export interface ISettingAsset extends ISettingBase {
type: 'asset';
value: { url?: string; defaultUrl?: string };
fileConstraints: IRocketChatAssetConstraint;
asset: string;
}
interface ISettingRange extends ISettingBase {
type: 'range';
minValue: number;
maxValue: number;
}
// Checks if setting has at least the required properties
export const isSetting = (setting: any): setting is ISetting =>
'_id' in setting &&
'type' in setting &&
'public' in setting &&
'value' in setting &&
'packageValue' in setting &&
'blocked' in setting &&
'sorter' in setting &&
'i18nLabel' in setting;
export const isSettingEnterprise = (setting: ISettingBase): setting is ISettingEnterprise => setting.enterprise === true;
export function isSettingColor(setting: ISettingBase): setting is ISettingColor;
export function isSettingColor<T extends Pick<ISettingBase, 'type'>>(setting: T): setting is T & { type: 'color' };
export function isSettingColor(setting: Pick<ISettingBase, 'type'>): boolean {
return setting.type === 'color';
}
export const isSettingCode = (setting: ISettingBase): setting is ISettingCode => setting.type === 'code';
export const isSettingAction = (setting: ISettingBase): setting is ISettingAction => setting.type === 'action';
export const isActionSettingWithEndpoint = (value: ISettingAction['value']): value is SettingActionEndpoint =>
typeof value === 'object' && value !== null && 'method' in value && 'path' in value;
export const isSettingRange = (setting: ISettingBase): setting is ISettingRange => setting.type === 'range';
export interface ISettingStatistics {
account2fa?: boolean;
cannedResponsesEnabled?: boolean;
e2e?: boolean;
e2eDefaultDirectRoom?: boolean;
e2eDefaultPrivateRoom?: boolean;
smtpHost?: string;
smtpPort?: string;
fromEmail?: string;
fileUploadEnable?: boolean;
frameworkDevMode?: boolean;
frameworkEnable?: boolean;
surveyEnabled?: boolean;
updateChecker?: boolean;
liveStream?: boolean;
broadcasting?: boolean;
allowEditing?: boolean;
allowDeleting?: boolean;
allowUnrecognizedSlashCommand?: boolean;
allowBadWordsFilter?: boolean;
readReceiptEnabled?: boolean;
readReceiptStoreUsers?: boolean;
pushEnable?: boolean;
globalSearchEnabled?: boolean;
threadsEnabled?: boolean;
webRTCEnableChannel?: boolean;
webRTCEnablePrivate?: boolean;
webRTCEnableDirect?: boolean;
}
export interface ISettingStatisticsObject {
accounts?: {
account2fa?: boolean;
};
cannedResponses?: {
cannedResponsesEnabled?: boolean;
};
e2ee?: {
e2e?: boolean;
e2eDefaultDirectRoom?: boolean;
e2eDefaultPrivateRoom?: boolean;
};
email?: {
smtp?: {
smtpHost?: string;
smtpPort?: string;
fromEmail?: string;
};
};
fileUpload?: {
fileUploadEnable?: boolean;
};
general?: {
apps?: {
frameworkDevMode?: boolean;
frameworkEnable?: boolean;
};
nps?: {
surveyEnabled?: boolean;
};
update?: {
updateChecker?: boolean;
};
};
liveStreamAndBroadcasting?: {
liveStream?: boolean;
broadcasting?: boolean;
};
message?: {
allowEditing?: boolean;
allowDeleting?: boolean;
allowUnrecognizedSlashCommand?: boolean;
allowBadWordsFilter?: boolean;
readReceiptEnabled?: boolean;
readReceiptStoreUsers?: boolean;
};
push?: {
pushEnable?: boolean;
};
search?: {
defaultProvider?: {
globalSearchEnabled?: boolean;
};
};
threads?: {
threadsEnabled?: boolean;
};
videoConference?: {
bigBlueButton?: boolean;
jitsiEnabled?: boolean;
};
webRTC?: {
webRTCEnableChannel?: boolean;
webRTCEnablePrivate?: boolean;
webRTCEnableDirect?: boolean;
};
}

View File

@@ -0,0 +1,6 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
export interface ISmarshHistory extends IRocketChatRecord {
lastRan: Date;
lastResult: string;
}

View File

@@ -0,0 +1,15 @@
import type { IncomingHttpHeaders } from 'node:http';
export interface ISocketConnection {
id: string;
instanceId: string;
loginToken?: string;
livechatToken?: string;
onClose(fn: (...args: any[]) => void): void;
clientAddress: string | undefined;
httpHeaders: IncomingHttpHeaders;
}
export interface ISocketConnectionLogged extends ISocketConnection {
loginToken?: string;
}

View File

@@ -0,0 +1,277 @@
import type { CpuInfo } from 'node:os';
import type { IMatrixFederationStatistics } from './IMatrixFederationStatistics';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { DeviceSessionAggregationResult, OSSessionAggregationResult, UserSessionAggregationResult } from './ISession';
import type { ISettingStatisticsObject } from './ISetting';
import type { ITeamStats } from './ITeam';
import type { MACStats } from './omnichannel';
interface IVoIPPeriodStats {
calls?: number;
externalInboundCalls?: number;
externalOutboundCalls?: number;
internalCalls?: number;
externalCalls?: number;
successfulCalls?: number;
failedCalls?: number;
callsDuration?: number;
}
export interface IStats extends IRocketChatRecord {
wizard: {
organizationType?: string;
industry?: string;
size?: string;
country?: string;
language?: string;
serverType?: string;
registerServer?: boolean;
};
uniqueId: string;
deploymentFingerprintHash: string;
deploymentFingerprintVerified: boolean;
installedAt?: string;
version?: string;
tag?: string;
branch?: string;
totalUsers: number;
activeUsers: number;
activeGuests: number;
nonActiveUsers: number;
appUsers: number;
onlineUsers: number;
awayUsers: number;
busyUsers: number;
totalConnectedUsers: number;
offlineUsers: number;
userLanguages: Record<string, number>;
totalRooms: number;
totalChannels: number;
totalPrivateGroups: number;
totalDirect: number;
totalLivechat: number;
totalDiscussions: number;
totalThreads: number;
teams: ITeamStats;
totalLivechatVisitors: number;
totalLivechatAgents: number;
totalLivechatManagers: number;
totalCustomFields: number;
totalLivechatPublicCustomFields: number;
livechatAutomaticForwardingUnansweredChats: boolean;
livechatEnabled: boolean;
isDepartmentRemovalEnabled: boolean;
totalChannelMessages: number;
totalPrivateGroupMessages: number;
totalDirectMessages: number;
totalDiscussionsMessages: number;
totalLivechatMessages: number;
totalLivechatRoomsWithPriority: number;
totalLivechatRoomsWithDepartment: number;
totalTriggers: number;
totalMessages: number;
federatedServers: number;
federatedUsers: number;
lastLogin: string;
lastMessageSentAt: Date | undefined;
lastSeenSubscription: string;
os: {
type: string;
platform: NodeJS.Platform;
arch: string;
release: string;
uptime: number;
loadavg: number[];
totalmem: number;
freemem: number;
cpus: CpuInfo[];
};
process: {
nodeVersion: string;
pid: number;
uptime: number;
};
deploy: {
method: string;
platform: string;
};
enterpriseReady: boolean;
uploadsTotal: number;
uploadsTotalSize: number;
fileStoreType: string;
migration: {
_id?: string;
locked: boolean;
version: number;
buildAt?: string | Date;
lockedAt?: string | Date;
};
instanceCount: number;
oplogEnabled: boolean;
msEnabled: boolean;
mongoVersion: string;
mongoStorageEngine: string;
pushQueue: number;
omnichannelSources: { [key: string]: number | string }[];
omnichannelContactsBySource: MACStats;
uniqueContactsOfLastMonth: MACStats;
uniqueContactsOfLastWeek: MACStats;
uniqueContactsOfYesterday: MACStats;
departments: number;
archivedDepartments: number;
routingAlgorithm: string;
onHoldEnabled: boolean;
emailInboxes: number;
BusinessHours: { [key: string]: number | string };
lastChattedAgentPreferred: boolean;
assignNewConversationsToContactManager: boolean;
visitorAbandonment: string;
chatsOnHold: number;
voipEnabled: boolean;
voipCalls: number;
voipExtensions: number;
voipSuccessfulCalls: number;
voipErrorCalls: number;
voipOnHoldCalls: number;
federationOverviewData: {
numberOfEvents: number;
numberOfFederatedUsers: number;
numberOfServers: number;
};
readReceiptsEnabled: boolean;
readReceiptsDetailed: boolean;
uniqueUsersOfLastWeek: UserSessionAggregationResult;
uniqueUsersOfLastMonth: UserSessionAggregationResult;
uniqueUsersOfYesterday: UserSessionAggregationResult;
uniqueDevicesOfYesterday: DeviceSessionAggregationResult;
uniqueDevicesOfLastWeek: DeviceSessionAggregationResult;
uniqueDevicesOfLastMonth: DeviceSessionAggregationResult;
uniqueOSOfYesterday: OSSessionAggregationResult;
uniqueOSOfLastWeek: OSSessionAggregationResult;
uniqueOSOfLastMonth: OSSessionAggregationResult;
apps: {
engineVersion: string;
totalInstalled: number | false;
totalActive: number | false;
totalFailed: number | false;
};
services: Record<string, unknown>;
importer: Record<string, unknown>;
settings: ISettingStatisticsObject;
integrations: {
totalIntegrations: number;
totalIncoming: number;
totalIncomingActive: number;
totalOutgoing: number;
totalOutgoingActive: number;
totalWithScriptEnabled: number;
};
enterprise: {
modules: string[];
tags: string[];
seatRequests: number;
livechatTags?: number;
cannedResponses?: number;
priorities?: number;
slas?: number;
businessUnits?: number;
omnichannelPdfTranscriptSucceeded?: number;
omnichannelRoomsWithSlas?: number;
omnichannelRoomsWithPriorities?: number;
livechatMonitors?: number;
voip?: {
total?: IVoIPPeriodStats;
lastMonth?: IVoIPPeriodStats;
lastWeek?: IVoIPPeriodStats;
lastDay?: IVoIPPeriodStats;
};
};
createdAt: Date | string;
slashCommandsJitsi: number;
messageAuditApply: number;
messageAuditLoad: number;
dashboardCount: number;
joinJitsiButton: number;
totalBroadcastRooms: number;
totalTriggeredEmails: number;
totalRoomsWithStarred: number;
totalRoomsWithPinned: number;
totalUserEmail2fa: number;
totalUserTOTP: number;
totalStarred: number;
totalPinned: number;
totalLinkInvitation: number;
totalEmailInvitation: number;
totalE2ERooms: number;
logoChange: boolean;
homeTitleChanged: boolean;
homeBodyChanged: boolean;
customCSSChanged: boolean;
onLogoutCustomScriptChanged: boolean;
loggedOutCustomScriptChanged: boolean;
loggedInCustomScriptChanged: boolean;
roomsInsideTeams: number;
showHomeButton: boolean;
totalEncryptedMessages: number;
totalLinkInvitationUses: number;
totalManuallyAddedUsers: number;
videoConf: {
videoConference: {
started: number;
ended: number;
};
direct: {
calling: number;
started: number;
ended: number;
};
livechat: {
started: number;
ended: number;
};
settings: {
provider: string;
dms: boolean;
channels: boolean;
groups: boolean;
teams: boolean;
};
};
totalSubscriptionRoles: number;
totalUserRoles: number;
totalCustomRoles: number;
totalWebRTCCalls: number;
uncaughtExceptionsCount: number;
push: number;
pushSecured: boolean;
dailyPeakConnections: number;
maxMonthlyPeakConnections: number;
matrixFederation: IMatrixFederationStatistics;
webRTCEnabled: boolean;
webRTCEnabledForOmnichannel: boolean;
omnichannelWebRTCCalls: number;
statsToken?: string;
contactVerification: {
totalContacts: number;
totalUnknownContacts: number;
totalMergedContacts: number;
totalConflicts: number;
totalResolvedConflicts: number;
totalBlockedContacts: number;
totalPartiallyBlockedContacts: number;
totalFullyBlockedContacts: number;
totalVerifiedContacts: number;
avgChannelsPerContact: number;
totalContactsWithoutChannels: number;
totalImportedContacts: number;
totalUpsellViews: number;
totalUpsellClicks: number;
};
abacEnabled?: boolean;
abacTotalAttributes?: number;
abacTotalAttributeValues?: number;
abacRoomsEnrolled?: number;
allowUnsafeQueryAndFieldsApiParamsEnabled?: boolean;
fips?: boolean;
}

View File

@@ -0,0 +1,104 @@
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRole } from './IRole';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
import type { RoomType } from './RoomType';
type OldKey = { e2eKeyId: string; ts: Date; E2EKey: string };
export type SubscriptionStatus = 'INVITED' | 'BANNED';
export interface ISubscription extends IRocketChatRecord {
u: Pick<IUser, '_id' | 'username' | 'name'>;
v?: Pick<IUser, '_id' | 'username' | 'name' | 'status'> & { token?: string };
rid: IRoom['_id'];
open: boolean;
ts: Date;
name: string;
alert?: boolean;
unread: number;
t: RoomType;
ls?: Date;
f?: boolean;
category?: string;
lr?: Date;
hideUnreadStatus?: true;
hideMentionStatus?: true;
teamMain?: boolean;
teamId?: string;
userMentions: number;
groupMentions: number;
broadcast?: true;
tunread?: Array<string>;
tunreadGroup?: Array<string>;
tunreadUser?: Array<string>;
prid?: IRoom['_id'];
roles?: IRole['_id'][];
onHold?: boolean;
encrypted?: boolean;
E2EKey?: string;
E2ESuggestedKey?: string;
unreadAlert?: 'default' | 'all' | 'mentions' | 'nothing';
fname?: string;
code?: unknown;
archived?: boolean;
audioNotificationValue?: string;
desktopNotifications?: 'all' | 'mentions' | 'nothing';
mobilePushNotifications?: 'all' | 'mentions' | 'nothing';
emailNotifications?: 'all' | 'mentions' | 'nothing';
userHighlights?: string[];
blocked?: unknown;
blocker?: unknown;
autoTranslate?: boolean;
autoTranslateLanguage?: string;
disableNotifications?: boolean;
muteGroupMentions?: boolean;
ignored?: IUser['_id'][];
department?: unknown;
draft?: string;
threadDrafts?: Record<string, string>;
desktopPrefOrigin?: 'subscription' | 'user';
mobilePrefOrigin?: 'subscription' | 'user';
emailPrefOrigin?: 'subscription' | 'user';
/* @deprecated */
customFields?: Record<string, any>;
oldRoomKeys?: OldKey[];
suggestedOldRoomKeys?: OldKey[];
status?: SubscriptionStatus;
inviter?: Required<Pick<IUser, '_id' | 'username'>> & Pick<IUser, 'name'>;
abacLastTimeChecked?: Date;
}
export interface IInviteSubscription extends ISubscription {
status: 'INVITED';
inviter: NonNullable<ISubscription['inviter']>;
}
export const isInviteSubscription = (subscription: ISubscription): subscription is IInviteSubscription => {
return subscription?.status === 'INVITED' && !!subscription.inviter;
};
export interface IBannedSubscription extends ISubscription {
status: 'BANNED';
}
export const isBannedSubscription = <T extends Pick<ISubscription, 'status'>>(
subscription: T,
): subscription is T & { status: 'BANNED' } => {
return subscription?.status === 'BANNED';
};

View File

@@ -0,0 +1,59 @@
import type { Document, FindOptions, Filter, SchemaMember } from 'mongodb';
import type { IRocketChatRecord } from './IRocketChatRecord';
import type { IRole } from './IRole';
import type { IUser } from './IUser';
export enum TeamType {
PUBLIC = 0,
PRIVATE = 1,
}
export type SortType = -1 | 1;
export interface ITeam extends IRocketChatRecord {
name: string;
type: TeamType;
roomId: string;
createdBy: Pick<IUser, '_id' | 'username'>;
createdAt: Date;
}
export interface ITeamMember extends IRocketChatRecord {
teamId: string;
userId: string;
roles?: Array<IRole['_id']>;
createdBy: Pick<IUser, '_id' | 'username'>;
createdAt: Date;
}
// TODO move this definition to a more broader file
export interface IPaginationOptions {
offset: number;
count: number;
}
// TODO move this definition to a more broader file
export interface IQueryOptions<T extends Document> {
sort?: FindOptions<T>['sort'];
query?: Filter<T>;
fields?: SchemaMember<T, number | boolean>;
}
// TODO move this definition to a more broader file
export interface IRecordsWithTotal<T> {
records: Array<T>;
total: number;
}
export interface ITeamStats {
totalTeams: number;
totalRoomsInsideTeams: number;
totalDefaultRoomsInsideTeams: number;
}
// TODO: move to service sdk package
export interface ITeamMemberParams {
userId: string;
roles?: Array<IRole['_id']> | null;
}

Some files were not shown because too many files have changed in this diff Show More