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
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:
521
apps/meteor/server/api/v1/commands.ts
Normal file
521
apps/meteor/server/api/v1/commands.ts
Normal file
@@ -0,0 +1,521 @@
|
||||
import { Apps } from '@rocket.chat/apps';
|
||||
import type { SlashCommand, SlashCommandPreviewItem } from '@rocket.chat/core-typings';
|
||||
import { Messages } from '@rocket.chat/models';
|
||||
import { Random } from '@rocket.chat/random';
|
||||
import {
|
||||
ajv,
|
||||
ajvQuery,
|
||||
validateUnauthorizedErrorResponse,
|
||||
validateBadRequestErrorResponse,
|
||||
validateForbiddenErrorResponse,
|
||||
} from '@rocket.chat/rest-typings';
|
||||
import objectPath from 'object-path';
|
||||
|
||||
import { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSlashCommandPreview } from '../../meteor-methods/messages/executeSlashCommandPreview';
|
||||
import { getSlashCommandPreviews } from '../../meteor-methods/messages/getSlashCommandPreviews';
|
||||
import type { ExtractRoutesFromAPI } from '../ApiClass';
|
||||
import { API } from '../api';
|
||||
import { getPaginationItems } from '../lib/getPaginationItems';
|
||||
|
||||
type CommandsGetParams = { command: string };
|
||||
|
||||
const CommandsGetParamsSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string' },
|
||||
},
|
||||
required: ['command'],
|
||||
additionalProperties: false,
|
||||
};
|
||||
|
||||
const isCommandsGetParams = ajvQuery.compile<CommandsGetParams>(CommandsGetParamsSchema);
|
||||
|
||||
const commandsListResponseSchema = ajv.compile<{
|
||||
commands: SlashCommand[];
|
||||
appsLoaded: boolean;
|
||||
offset: number;
|
||||
count: number;
|
||||
total: number;
|
||||
}>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
commands: {
|
||||
type: 'array',
|
||||
items: { $ref: '#/components/schemas/SlashCommand' },
|
||||
},
|
||||
appsLoaded: { type: 'boolean' },
|
||||
offset: { type: 'number' },
|
||||
count: { type: 'number' },
|
||||
total: { type: 'number' },
|
||||
success: { type: 'boolean', enum: [true] },
|
||||
},
|
||||
required: ['commands', 'appsLoaded', 'offset', 'count', 'total', 'success'],
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
type CommandsListParams = {
|
||||
offset?: number;
|
||||
count?: number;
|
||||
sort?: string;
|
||||
query?: string;
|
||||
fields?: string;
|
||||
};
|
||||
|
||||
const isCommandsListParams = ajvQuery.compile<CommandsListParams>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
offset: { type: 'number', nullable: true },
|
||||
count: { type: 'number', nullable: true },
|
||||
sort: { type: 'string', nullable: true },
|
||||
query: { type: 'string', nullable: true },
|
||||
fields: { type: 'string', nullable: true },
|
||||
},
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
const commandsEndpoints = API.v1
|
||||
.get(
|
||||
'commands.get',
|
||||
{
|
||||
authRequired: true,
|
||||
query: isCommandsGetParams,
|
||||
response: {
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
200: ajv.compile<{
|
||||
command: Pick<SlashCommand, 'clientOnly' | 'command' | 'description' | 'params' | 'providesPreview'>;
|
||||
success: true;
|
||||
}>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
clientOnly: { type: 'boolean' },
|
||||
command: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
params: { type: 'string' },
|
||||
providesPreview: { type: 'boolean' },
|
||||
},
|
||||
required: ['command', 'providesPreview'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
success: {
|
||||
type: 'boolean',
|
||||
enum: [true],
|
||||
},
|
||||
},
|
||||
required: ['command', 'success'],
|
||||
additionalProperties: false,
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
async function action() {
|
||||
const params = this.queryParams;
|
||||
|
||||
const cmd = slashCommands.commands[params.command.toLowerCase()];
|
||||
|
||||
if (!cmd) {
|
||||
return API.v1.failure(`There is no command in the system by the name of: ${params.command}`);
|
||||
}
|
||||
|
||||
return API.v1.success({
|
||||
command: {
|
||||
command: cmd.command,
|
||||
description: cmd.description,
|
||||
params: cmd.params,
|
||||
clientOnly: cmd.clientOnly,
|
||||
providesPreview: cmd.providesPreview,
|
||||
},
|
||||
});
|
||||
},
|
||||
)
|
||||
.get(
|
||||
'commands.list',
|
||||
{
|
||||
authRequired: true,
|
||||
query: isCommandsListParams,
|
||||
response: {
|
||||
200: commandsListResponseSchema,
|
||||
202: commandsListResponseSchema,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
},
|
||||
},
|
||||
async function action() {
|
||||
if (!Apps.self?.isLoaded()) {
|
||||
return API.v1.success(
|
||||
{
|
||||
commands: [],
|
||||
appsLoaded: false as const,
|
||||
offset: 0,
|
||||
count: 0,
|
||||
total: 0,
|
||||
success: true as const,
|
||||
},
|
||||
202,
|
||||
);
|
||||
}
|
||||
|
||||
const { offset, count } = await getPaginationItems(this.queryParams);
|
||||
const { sort, query } = await this.parseJsonQuery();
|
||||
|
||||
let commands = Object.values(slashCommands.commands);
|
||||
|
||||
if (query?.command) {
|
||||
commands = commands.filter((command) => command.command === query.command);
|
||||
}
|
||||
|
||||
const totalCount = commands.length;
|
||||
|
||||
return API.v1.success({
|
||||
commands: processQueryOptionsOnResult(commands, {
|
||||
sort: sort || { name: 1 },
|
||||
skip: offset,
|
||||
limit: count,
|
||||
}),
|
||||
appsLoaded: true as const,
|
||||
offset,
|
||||
count: commands.length,
|
||||
total: totalCount,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/* @deprecated */
|
||||
const processQueryOptionsOnResult = <T extends { _id?: string } & Record<string, any>, F extends keyof T>(
|
||||
result: T[],
|
||||
options: {
|
||||
fields?: {
|
||||
[key in F]?: 1 | 0;
|
||||
};
|
||||
sort?: {
|
||||
[key: string]: 1 | -1;
|
||||
};
|
||||
limit?: number;
|
||||
skip?: number;
|
||||
} = {},
|
||||
): Pick<T, F>[] => {
|
||||
if (result === undefined || result === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
if (options.sort) {
|
||||
result = result.sort((a, b) => {
|
||||
let r = 0;
|
||||
for (const field in options.sort) {
|
||||
if (options.sort.hasOwnProperty(field)) {
|
||||
const direction = options.sort[field];
|
||||
let valueA;
|
||||
let valueB;
|
||||
if (field.indexOf('.') > -1) {
|
||||
valueA = objectPath.get(a, field);
|
||||
valueB = objectPath.get(b, field);
|
||||
} else {
|
||||
valueA = a[field];
|
||||
valueB = b[field];
|
||||
}
|
||||
if (valueA > valueB) {
|
||||
r = direction;
|
||||
break;
|
||||
}
|
||||
if (valueA < valueB) {
|
||||
r = -direction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof options.skip === 'number') {
|
||||
result.splice(0, options.skip);
|
||||
}
|
||||
|
||||
if (typeof options.limit === 'number' && options.limit !== 0) {
|
||||
result.splice(options.limit);
|
||||
}
|
||||
}
|
||||
|
||||
const fieldsToRemove: F[] = [];
|
||||
const fieldsToGet: F[] = [];
|
||||
|
||||
if (options.fields) {
|
||||
for (const field in Object.keys(options.fields)) {
|
||||
if (options.fields.hasOwnProperty(field as F)) {
|
||||
if (options.fields[field as F] === 0) {
|
||||
fieldsToRemove.push(field as F);
|
||||
} else if (options.fields[field as F] === 1) {
|
||||
fieldsToGet.push(field as F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldsToGet.length > 0 && fieldsToGet.indexOf('_id' as F) === -1) {
|
||||
fieldsToGet.push('_id' as F);
|
||||
}
|
||||
|
||||
const pickFields = <F extends keyof T>(obj: T, fields: F[]): Pick<T, F> => {
|
||||
const picked: Partial<T> = {};
|
||||
fields.forEach((field: F) => {
|
||||
if (String(field).indexOf('.') !== -1) {
|
||||
objectPath.set(picked, String(field), objectPath.get(obj, String(field)));
|
||||
} else {
|
||||
picked[field] = obj[field];
|
||||
}
|
||||
});
|
||||
return picked as Pick<T, F>;
|
||||
};
|
||||
|
||||
if (fieldsToRemove.length > 0 && fieldsToGet.length > 0) {
|
||||
console.warn("Can't mix remove and get fields");
|
||||
fieldsToRemove.splice(0, fieldsToRemove.length);
|
||||
}
|
||||
|
||||
if (fieldsToRemove.length > 0 || fieldsToGet.length > 0) {
|
||||
return result.map((record) => {
|
||||
if (fieldsToRemove.length > 0) {
|
||||
return Object.fromEntries(Object.entries(record).filter(([key]) => !fieldsToRemove.includes(key as F))) as Pick<T, F>;
|
||||
}
|
||||
|
||||
return pickFields(record, fieldsToGet);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const isCommandsRunProps = ajv.compile<{ command: string; params?: string; roomId: string; tmid?: string; triggerId?: string }>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', minLength: 1 },
|
||||
params: { type: 'string', nullable: true },
|
||||
roomId: { type: 'string', minLength: 1 },
|
||||
tmid: { type: 'string', nullable: true },
|
||||
triggerId: { type: 'string', nullable: true },
|
||||
},
|
||||
required: ['command', 'roomId'],
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
const commandsRunResponseSchema = ajv.compile<{ result: unknown }>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
result: {},
|
||||
success: { type: 'boolean', enum: [true] },
|
||||
},
|
||||
required: ['success'],
|
||||
additionalProperties: true,
|
||||
});
|
||||
|
||||
const isCommandsPreviewGetProps = ajvQuery.compile<{ command: string; params?: string; roomId: string }>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', minLength: 1 },
|
||||
params: { type: 'string', nullable: true },
|
||||
roomId: { type: 'string', minLength: 1 },
|
||||
},
|
||||
required: ['command', 'roomId'],
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
const commandsPreviewGetResponseSchema = ajv.compile<{ preview: Record<string, unknown> | null }>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
preview: { type: 'object', nullable: true },
|
||||
success: { type: 'boolean', enum: [true] },
|
||||
},
|
||||
required: ['success'],
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
const isCommandsPreviewPostProps = ajv.compile<{
|
||||
command: string;
|
||||
params?: string;
|
||||
roomId: string;
|
||||
tmid?: string;
|
||||
triggerId?: string;
|
||||
previewItem: SlashCommandPreviewItem;
|
||||
}>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', minLength: 1 },
|
||||
params: { type: 'string', nullable: true },
|
||||
roomId: { type: 'string', minLength: 1 },
|
||||
tmid: { type: 'string', nullable: true },
|
||||
triggerId: { type: 'string', nullable: true },
|
||||
previewItem: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
type: { type: 'string', enum: ['image', 'video', 'audio', 'text', 'other'] },
|
||||
value: { type: 'string' },
|
||||
},
|
||||
required: ['id', 'type', 'value'],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
required: ['command', 'roomId', 'previewItem'],
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
const commandsPreviewPostResponseSchema = ajv.compile<void>({
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: { type: 'boolean', enum: [true] },
|
||||
},
|
||||
required: ['success'],
|
||||
additionalProperties: false,
|
||||
});
|
||||
|
||||
// Expects a body of: { command: 'gimme', params: 'any string value', roomId: 'value', triggerId: 'value' }
|
||||
API.v1.post(
|
||||
'commands.run',
|
||||
{
|
||||
authRequired: true,
|
||||
body: isCommandsRunProps,
|
||||
response: {
|
||||
200: commandsRunResponseSchema,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
403: validateForbiddenErrorResponse,
|
||||
},
|
||||
},
|
||||
async function action() {
|
||||
const body = this.bodyParams;
|
||||
|
||||
const cmd = body.command.toLowerCase();
|
||||
if (!slashCommands.commands[cmd]) {
|
||||
return API.v1.failure('The command provided does not exist (or is disabled).');
|
||||
}
|
||||
|
||||
if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) {
|
||||
return API.v1.forbidden();
|
||||
}
|
||||
|
||||
const params = body.params ? body.params : '';
|
||||
if (body.tmid) {
|
||||
const thread = await Messages.findOneById(body.tmid);
|
||||
if (thread?.rid !== body.roomId) {
|
||||
return API.v1.failure('Invalid thread.');
|
||||
}
|
||||
}
|
||||
|
||||
const message = {
|
||||
_id: Random.id(),
|
||||
rid: body.roomId,
|
||||
msg: `/${cmd} ${params}`,
|
||||
...(body.tmid && { tmid: body.tmid }),
|
||||
};
|
||||
|
||||
const { triggerId } = body;
|
||||
|
||||
const result = await slashCommands.run({ command: cmd, params, message, triggerId, userId: this.userId });
|
||||
|
||||
return API.v1.success({ result });
|
||||
},
|
||||
);
|
||||
|
||||
// Expects these query params: command: 'giphy', params: 'mine', roomId: 'value'
|
||||
API.v1.get(
|
||||
'commands.preview',
|
||||
{
|
||||
authRequired: true,
|
||||
query: isCommandsPreviewGetProps,
|
||||
response: {
|
||||
200: commandsPreviewGetResponseSchema,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
403: validateForbiddenErrorResponse,
|
||||
},
|
||||
},
|
||||
async function action() {
|
||||
const query = this.queryParams;
|
||||
|
||||
const cmd = query.command.toLowerCase();
|
||||
if (!slashCommands.commands[cmd]) {
|
||||
return API.v1.failure('The command provided does not exist (or is disabled).');
|
||||
}
|
||||
|
||||
if (!(await canAccessRoomIdAsync(query.roomId, this.userId))) {
|
||||
return API.v1.forbidden();
|
||||
}
|
||||
|
||||
const params = query.params ? query.params : '';
|
||||
|
||||
const preview = await getSlashCommandPreviews({
|
||||
cmd,
|
||||
params,
|
||||
msg: { rid: query.roomId },
|
||||
userId: this.userId,
|
||||
});
|
||||
|
||||
return API.v1.success({ preview: preview ?? null });
|
||||
},
|
||||
);
|
||||
|
||||
// Expects a body format of: { command: 'giphy', params: 'mine', roomId: 'value', tmid: 'value', triggerId: 'value', previewItem: { id: 'sadf8' type: 'image', value: 'https://dev.null/gif' } }
|
||||
API.v1.post(
|
||||
'commands.preview',
|
||||
{
|
||||
authRequired: true,
|
||||
body: isCommandsPreviewPostProps,
|
||||
response: {
|
||||
200: commandsPreviewPostResponseSchema,
|
||||
400: validateBadRequestErrorResponse,
|
||||
401: validateUnauthorizedErrorResponse,
|
||||
403: validateForbiddenErrorResponse,
|
||||
},
|
||||
},
|
||||
async function action() {
|
||||
const body = this.bodyParams;
|
||||
|
||||
const cmd = body.command.toLowerCase();
|
||||
if (!slashCommands.commands[cmd]) {
|
||||
return API.v1.failure('The command provided does not exist (or is disabled).');
|
||||
}
|
||||
|
||||
if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) {
|
||||
return API.v1.forbidden();
|
||||
}
|
||||
|
||||
const { params = '' } = body;
|
||||
if (body.tmid) {
|
||||
const thread = await Messages.findOneById(body.tmid);
|
||||
if (thread?.rid !== body.roomId) {
|
||||
return API.v1.failure('Invalid thread.');
|
||||
}
|
||||
}
|
||||
|
||||
const msg = {
|
||||
rid: body.roomId,
|
||||
...(body.tmid && { tmid: body.tmid }),
|
||||
};
|
||||
|
||||
await executeSlashCommandPreview(
|
||||
{
|
||||
cmd,
|
||||
params,
|
||||
msg,
|
||||
triggerId: body.triggerId,
|
||||
},
|
||||
body.previewItem,
|
||||
this.userId,
|
||||
);
|
||||
|
||||
return API.v1.success();
|
||||
},
|
||||
);
|
||||
|
||||
export type CommandsEndpoints = ExtractRoutesFromAPI<typeof commandsEndpoints>;
|
||||
|
||||
declare module '@rocket.chat/rest-typings' {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
|
||||
interface Endpoints extends CommandsEndpoints {}
|
||||
}
|
||||
Reference in New Issue
Block a user