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:
1
apps/meteor/server/slashcommands/archiveroom/index.ts
Normal file
1
apps/meteor/server/slashcommands/archiveroom/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
86
apps/meteor/server/slashcommands/archiveroom/server.ts
Normal file
86
apps/meteor/server/slashcommands/archiveroom/server.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { isRegisterUser } from '@rocket.chat/core-typings';
|
||||
import { Users, Rooms } from '@rocket.chat/models';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { RoomMemberActions } from '../../../definition/IRoomTypeConfig';
|
||||
import { hasPermissionAsync } from '../../lib/authorization/hasPermission';
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { archiveRoom } from '../../lib/rooms/archiveRoom';
|
||||
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'archive',
|
||||
callback: async function Archive({ params, message, userId }: SlashCommandCallbackParams<'archive'>): Promise<void> {
|
||||
let channel = params.trim();
|
||||
|
||||
let room;
|
||||
|
||||
if (channel === '') {
|
||||
room = await Rooms.findOneById(message.rid);
|
||||
if (room?.name) {
|
||||
channel = room.name;
|
||||
}
|
||||
} else {
|
||||
channel = channel.replace('#', '');
|
||||
room = await Rooms.findOneByName(channel);
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await Users.findOneById(userId, { projection: { username: 1, name: 1 } });
|
||||
if (!user || !isRegisterUser(user)) {
|
||||
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
|
||||
}
|
||||
|
||||
if (!room) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.ARCHIVE, userId))) {
|
||||
throw new Meteor.Error('error-room-type-not-archivable', `Room type: ${room.t} can not be archived`);
|
||||
}
|
||||
|
||||
if (!(await hasPermissionAsync(userId, 'archive-room', room._id))) {
|
||||
throw new Meteor.Error('error-not-authorized', 'Not authorized');
|
||||
}
|
||||
|
||||
if (room.archived) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Duplicate_archived_channel_name', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await archiveRoom(room._id, user);
|
||||
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_Archived', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
},
|
||||
options: {
|
||||
description: 'Archive',
|
||||
params: '#channel',
|
||||
permission: 'archive-room',
|
||||
},
|
||||
});
|
||||
24
apps/meteor/server/slashcommands/asciiarts/gimme.ts
Normal file
24
apps/meteor/server/slashcommands/asciiarts/gimme.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
/*
|
||||
* Gimme is a named function that will replace /gimme commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
async function Gimme({ message, params, userId }: SlashCommandCallbackParams<'gimme'>): Promise<void> {
|
||||
const msg = message;
|
||||
msg.msg = `༼ つ ◕_◕ ༽つ ${params}`;
|
||||
await executeSendMessage(userId, msg);
|
||||
}
|
||||
|
||||
slashCommands.add({
|
||||
command: 'gimme',
|
||||
callback: Gimme,
|
||||
options: {
|
||||
description: 'Slash_Gimme_Description',
|
||||
params: 'your_message_optional',
|
||||
clientOnly: true,
|
||||
},
|
||||
});
|
||||
5
apps/meteor/server/slashcommands/asciiarts/index.ts
Normal file
5
apps/meteor/server/slashcommands/asciiarts/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import './gimme';
|
||||
import './lenny';
|
||||
import './shrug';
|
||||
import './tableflip';
|
||||
import './unflip';
|
||||
24
apps/meteor/server/slashcommands/asciiarts/lenny.ts
Normal file
24
apps/meteor/server/slashcommands/asciiarts/lenny.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
/*
|
||||
* Lenny is a named function that will replace /lenny commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
async function LennyFace({ message, params, userId }: SlashCommandCallbackParams<'lenny'>): Promise<void> {
|
||||
const msg = message;
|
||||
msg.msg = `${params} ( ͡° ͜ʖ ͡°)`;
|
||||
await executeSendMessage(userId, msg);
|
||||
}
|
||||
|
||||
slashCommands.add({
|
||||
command: 'lennyface',
|
||||
callback: LennyFace,
|
||||
options: {
|
||||
description: 'Slash_LennyFace_Description',
|
||||
params: 'your_message_optional',
|
||||
clientOnly: true,
|
||||
},
|
||||
});
|
||||
22
apps/meteor/server/slashcommands/asciiarts/shrug.ts
Normal file
22
apps/meteor/server/slashcommands/asciiarts/shrug.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
/*
|
||||
* Shrug is a named function that will replace /shrug commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'shrug',
|
||||
callback: async ({ message, params, userId }: SlashCommandCallbackParams<'shrug'>): Promise<void> => {
|
||||
const msg = message;
|
||||
msg.msg = `${params} ¯\\\\_(ツ)_/¯`;
|
||||
await executeSendMessage(userId, msg);
|
||||
},
|
||||
options: {
|
||||
description: 'Slash_Shrug_Description',
|
||||
params: 'your_message_optional',
|
||||
clientOnly: true,
|
||||
},
|
||||
});
|
||||
22
apps/meteor/server/slashcommands/asciiarts/tableflip.ts
Normal file
22
apps/meteor/server/slashcommands/asciiarts/tableflip.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
/*
|
||||
* Tableflip is a named function that will replace /Tableflip commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'tableflip',
|
||||
callback: async ({ message, params, userId }: SlashCommandCallbackParams<'tableflip'>): Promise<void> => {
|
||||
const msg = message;
|
||||
msg.msg = `${params} (╯°□°)╯︵ ┻━┻`;
|
||||
await executeSendMessage(userId, msg);
|
||||
},
|
||||
options: {
|
||||
description: 'Slash_Tableflip_Description',
|
||||
params: 'your_message_optional',
|
||||
clientOnly: true,
|
||||
},
|
||||
});
|
||||
22
apps/meteor/server/slashcommands/asciiarts/unflip.ts
Normal file
22
apps/meteor/server/slashcommands/asciiarts/unflip.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
/*
|
||||
* Unflip is a named function that will replace /unflip commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'unflip',
|
||||
callback: async ({ message, params, userId }: SlashCommandCallbackParams<'unflip'>): Promise<void> => {
|
||||
const msg = message;
|
||||
msg.msg = `${params} ┬─┬ ノ( ゜-゜ノ)`;
|
||||
await executeSendMessage(userId, msg);
|
||||
},
|
||||
options: {
|
||||
description: 'Slash_TableUnflip_Description',
|
||||
params: 'your_message_optional',
|
||||
clientOnly: true,
|
||||
},
|
||||
});
|
||||
39
apps/meteor/server/slashcommands/ban/ban.ts
Normal file
39
apps/meteor/server/slashcommands/ban/ban.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { banUserFromRoomMethod } from '../../lib/banUserFromRoom';
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { sanitizeUsername } from '../../meteor-methods/rooms/addUsersToRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'ban',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'ban'>): Promise<void> => {
|
||||
const username = sanitizeUsername(params.trim());
|
||||
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await Users.findOneByUsernameIgnoringCase(username);
|
||||
if (!user) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Username_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await banUserFromRoomMethod(userId, { rid: message.rid, username });
|
||||
},
|
||||
options: {
|
||||
description: 'Ban_user_from_room',
|
||||
params: '@username',
|
||||
permission: 'ban-user',
|
||||
},
|
||||
});
|
||||
2
apps/meteor/server/slashcommands/ban/index.ts
Normal file
2
apps/meteor/server/slashcommands/ban/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './ban';
|
||||
import './unban';
|
||||
39
apps/meteor/server/slashcommands/ban/unban.ts
Normal file
39
apps/meteor/server/slashcommands/ban/unban.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { unbanUserFromRoom } from '../../lib/unbanUserFromRoom';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { sanitizeUsername } from '../../meteor-methods/rooms/addUsersToRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'unban',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'unban'>): Promise<void> => {
|
||||
const username = sanitizeUsername(params.trim());
|
||||
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await Users.findOneByUsernameIgnoringCase(username);
|
||||
if (!user) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Username_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await unbanUserFromRoom(userId, { rid: message.rid, username });
|
||||
},
|
||||
options: {
|
||||
description: 'Unban_user_from_room',
|
||||
params: '@username',
|
||||
permission: 'ban-user',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/create/index.ts
Normal file
1
apps/meteor/server/slashcommands/create/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
68
apps/meteor/server/slashcommands/create/server.ts
Normal file
68
apps/meteor/server/slashcommands/create/server.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Rooms, Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { createChannelMethod } from '../../meteor-methods/rooms/createChannel';
|
||||
import { createPrivateGroupMethod } from '../../meteor-methods/rooms/createPrivateGroup';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'create',
|
||||
callback: async function Create({ params, message, userId }: SlashCommandCallbackParams<'create'>): Promise<void> {
|
||||
function getParams(str: string): string[] {
|
||||
const regex = /(--(\w+))+/g;
|
||||
const result = [];
|
||||
let m;
|
||||
while ((m = regex.exec(str)) !== null) {
|
||||
if (m.index === regex.lastIndex) {
|
||||
regex.lastIndex++;
|
||||
}
|
||||
result.push(m[2]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const regexp = new RegExp(settings.get('UTF8_Channel_Names_Validation') as string);
|
||||
|
||||
const channel = regexp.exec(params.trim());
|
||||
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const channelStr: string = channel ? channel[0] : '';
|
||||
if (channelStr === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const room = await Rooms.findOneByName(channelStr);
|
||||
if (room != null) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_already_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channelStr,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (getParams(params).indexOf('private') > -1) {
|
||||
const user = await Users.findOneById(userId);
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
await createPrivateGroupMethod(user, channelStr, []);
|
||||
return;
|
||||
}
|
||||
|
||||
await createChannelMethod(userId, channelStr, []);
|
||||
},
|
||||
options: {
|
||||
description: 'Create_A_New_Channel',
|
||||
params: '#channel',
|
||||
permission: ['create-c', 'create-p'],
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/help/index.ts
Normal file
1
apps/meteor/server/slashcommands/help/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
74
apps/meteor/server/slashcommands/help/server.ts
Normal file
74
apps/meteor/server/slashcommands/help/server.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
/*
|
||||
* Help is a named function that will replace /help commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
interface IHelpCommand {
|
||||
key: string;
|
||||
command: string;
|
||||
}
|
||||
|
||||
slashCommands.add({
|
||||
command: 'help',
|
||||
callback: async function Help({ message, userId }: SlashCommandCallbackParams<'help'>): Promise<void> {
|
||||
const user = await Users.findOneById(userId);
|
||||
|
||||
const keys: IHelpCommand[] = [
|
||||
{
|
||||
key: 'Open_channel_user_search',
|
||||
command: 'Command (or Ctrl) + p OR Command (or Ctrl) + k',
|
||||
},
|
||||
{
|
||||
key: 'Mark_all_as_read',
|
||||
command: 'Shift (or Ctrl) + ESC',
|
||||
},
|
||||
{
|
||||
key: 'Edit_previous_message',
|
||||
command: 'Up Arrow',
|
||||
},
|
||||
{
|
||||
key: 'Move_beginning_message',
|
||||
command: 'Command (or Alt) + Left Arrow',
|
||||
},
|
||||
{
|
||||
key: 'Move_beginning_message',
|
||||
command: 'Command (or Alt) + Up Arrow',
|
||||
},
|
||||
{
|
||||
key: 'Move_end_message',
|
||||
command: 'Command (or Alt) + Right Arrow',
|
||||
},
|
||||
{
|
||||
key: 'Move_end_message',
|
||||
command: 'Command (or Alt) + Down Arrow',
|
||||
},
|
||||
{
|
||||
key: 'New_line_message_compose_input',
|
||||
command: 'Shift + Enter',
|
||||
},
|
||||
];
|
||||
let msg = '';
|
||||
keys.forEach((key) => {
|
||||
msg = `${msg}\n${i18n.t(key.key, {
|
||||
interpolation: { escapeValue: false },
|
||||
shortcut: key.command,
|
||||
lng: user?.language || settings.get('language') || 'en',
|
||||
})}`;
|
||||
});
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg,
|
||||
...(message.tmid && { tmid: message.tmid }),
|
||||
});
|
||||
},
|
||||
options: {
|
||||
description: 'Show_the_keyboard_shortcut_list',
|
||||
},
|
||||
});
|
||||
75
apps/meteor/server/slashcommands/hide/hide.ts
Normal file
75
apps/meteor/server/slashcommands/hide/hide.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { IRoom, SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Rooms, Subscriptions, Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { hideRoomMethod } from '../../meteor-methods/rooms/hideRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
/*
|
||||
* Hide is a named function that will replace /hide commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'hide',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'hide'>): Promise<void> => {
|
||||
const room = params.trim();
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await Users.findOneById(userId);
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lng = user.language || settings.get('Language') || 'en';
|
||||
|
||||
// if there is not a param, hide the current room
|
||||
let { rid } = message;
|
||||
if (room !== '') {
|
||||
const [strippedRoom] = room.replace(/#|@/, '').split(' ');
|
||||
|
||||
const [type] = room;
|
||||
|
||||
const roomObject: IRoom | null =
|
||||
type === '#'
|
||||
? await Rooms.findOneByName(strippedRoom)
|
||||
: await Rooms.findOne({
|
||||
t: 'd',
|
||||
usernames: { $all: [user.username, strippedRoom] },
|
||||
});
|
||||
if (!roomObject) {
|
||||
void api.broadcast('notify.ephemeralMessage', user._id, message.rid, {
|
||||
msg: i18n.t('Channel_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: strippedRoom,
|
||||
lng,
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (!(await Subscriptions.findOneByRoomIdAndUserId(roomObject ? roomObject._id : '', user._id, { projection: { _id: 1 } }))) {
|
||||
void api.broadcast('notify.ephemeralMessage', user._id, message.rid, {
|
||||
msg: i18n.t('error-logged-user-not-in-room', {
|
||||
interpolation: { escapeValue: false },
|
||||
roomName: room,
|
||||
lng,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
rid = roomObject?._id || message.rid;
|
||||
}
|
||||
try {
|
||||
await hideRoomMethod(userId, rid);
|
||||
} catch (error: any) {
|
||||
await api.broadcast('notify.ephemeralMessage', user._id, message.rid, {
|
||||
msg: i18n.t(error, { lng }),
|
||||
});
|
||||
}
|
||||
},
|
||||
options: { description: 'Hide_room', params: '#room' },
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/hide/index.ts
Normal file
1
apps/meteor/server/slashcommands/hide/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './hide';
|
||||
1
apps/meteor/server/slashcommands/invite/index.ts
Normal file
1
apps/meteor/server/slashcommands/invite/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
153
apps/meteor/server/slashcommands/invite/server.ts
Normal file
153
apps/meteor/server/slashcommands/invite/server.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { api, FederationMatrix, isMeteorError } from '@rocket.chat/core-services';
|
||||
import type { IUser, SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { isBannedSubscription } from '@rocket.chat/core-typings';
|
||||
import { validateFederatedUsername } from '@rocket.chat/federation-matrix';
|
||||
import { Subscriptions, Users, Rooms } from '@rocket.chat/models';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { addUsersToRoomMethod, sanitizeUsername } from '../../meteor-methods/rooms/addUsersToRoom';
|
||||
import { FederationActions } from '../../services/room/hooks/BeforeFederationActions';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
// Type guards for the error
|
||||
function isStringError(error: unknown): error is { error: string } {
|
||||
return typeof (error as any)?.error === 'string';
|
||||
}
|
||||
|
||||
/*
|
||||
* Invite is a named function that will replace /invite commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
slashCommands.add({
|
||||
command: 'invite',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'invite'>): Promise<void> => {
|
||||
let usernames = params
|
||||
.split(/[\s,]/)
|
||||
.map((username) => sanitizeUsername(username))
|
||||
.filter((a) => a !== '');
|
||||
if (usernames.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get room information for federation check
|
||||
const room = await Rooms.findOneById(message.rid);
|
||||
if (!room) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('error-invalid-room', { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure federated users exist locally before looking them up
|
||||
const federatedUsernames = usernames.filter((u) => validateFederatedUsername(u)) as string[];
|
||||
if (federatedUsernames.length > 0) {
|
||||
if (FederationActions.shouldPerformFederationAction(room)) {
|
||||
await FederationMatrix.ensureFederatedUsersExistLocally(federatedUsernames);
|
||||
} else {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('You_cannot_add_external_users_to_non_federated_room', { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
// These federated users shouldn't be invited and we already broadcasted the error message
|
||||
usernames = usernames.filter((username) => {
|
||||
return !federatedUsernames.includes(username);
|
||||
});
|
||||
if (usernames.length === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const users = await Users.findByUsernames(usernames).toArray();
|
||||
if (users.length === 0) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('User_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username: usernames.join(' @'),
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const usersFiltered: IUser[] = [];
|
||||
|
||||
for await (const user of users) {
|
||||
const subscription = await Subscriptions.findOneByRoomIdAndUserId(message.rid, user._id, {
|
||||
projection: { _id: 1, status: 1 },
|
||||
});
|
||||
if (subscription == null || isBannedSubscription(subscription)) {
|
||||
usersFiltered.push(user);
|
||||
continue;
|
||||
}
|
||||
const usernameStr = user.username as string;
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Username_is_already_in_here', {
|
||||
interpolation: { escapeValue: false },
|
||||
username: usernameStr,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const inviter = await Users.findOneById(userId);
|
||||
|
||||
if (!inviter) {
|
||||
throw new Meteor.Error('error-user-not-found', 'Inviter not found', {
|
||||
method: 'slashcommand-invite',
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
usersFiltered.map(async (user) => {
|
||||
try {
|
||||
// TODO: Refactor this to return an error if some user fails to be added
|
||||
return await addUsersToRoomMethod(
|
||||
userId,
|
||||
{
|
||||
rid: message.rid,
|
||||
users: [user.username || ''],
|
||||
},
|
||||
inviter,
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
if (isMeteorError(e)) {
|
||||
if (e.error === 'error-only-compliant-users-can-be-added-to-abac-rooms') {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t(e.error, { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
} else {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t(e.message, { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isStringError(e)) {
|
||||
const { error } = e;
|
||||
if (error === 'error-federated-users-in-non-federated-rooms') {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('You_cannot_add_external_users_to_non_federated_room', { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
} else if (error === 'cant-invite-for-direct-room') {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Cannot_invite_users_to_direct_rooms', { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
} else {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t(error, { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
options: {
|
||||
description: 'Invite_user_to_join_channel',
|
||||
params: '@username',
|
||||
permission: 'add-user-to-joined-room',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/inviteall/index.ts
Normal file
1
apps/meteor/server/slashcommands/inviteall/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
128
apps/meteor/server/slashcommands/inviteall/server.ts
Normal file
128
apps/meteor/server/slashcommands/inviteall/server.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Invite is a named function that will replace /invite commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { ISubscription, SlashCommand, SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Rooms, Subscriptions, Users } from '@rocket.chat/models';
|
||||
import { isTruthy } from '@rocket.chat/tools';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { canAccessRoomAsync } from '../../lib/authorization';
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { addUsersToRoomMethod } from '../../meteor-methods/rooms/addUsersToRoom';
|
||||
import { createChannelMethod } from '../../meteor-methods/rooms/createChannel';
|
||||
import { createPrivateGroupMethod } from '../../meteor-methods/rooms/createPrivateGroup';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
function inviteAll<T extends string>(type: T): SlashCommand<T>['callback'] {
|
||||
return async function inviteAll({ command, params, message, userId }: SlashCommandCallbackParams<T>): Promise<void> {
|
||||
if (!/invite\-all-(to|from)/.test(command)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let channel = params.trim();
|
||||
if (channel === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
channel = channel.replace('#', '');
|
||||
|
||||
if (!channel) {
|
||||
return;
|
||||
}
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await Users.findOneById(userId);
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
const lng = user?.language || settings.get('Language') || 'en';
|
||||
|
||||
const baseChannel = type === 'to' ? await Rooms.findOneById(message.rid) : await Rooms.findOneByName(channel);
|
||||
const targetChannel = type === 'from' ? await Rooms.findOneById(message.rid) : await Rooms.findOneByName(channel);
|
||||
|
||||
if (!baseChannel) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await canAccessRoomAsync(baseChannel, user))) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Room_not_exist_or_not_permission', { lng }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const APIsettings = settings.get<number>('API_User_Limit');
|
||||
if (!APIsettings) {
|
||||
return;
|
||||
}
|
||||
if ((await Subscriptions.countByRoomIdWhenUsernameExists(baseChannel._id)) > APIsettings) {
|
||||
throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', {
|
||||
method: 'addAllToRoom',
|
||||
});
|
||||
}
|
||||
|
||||
const cursor = Subscriptions.findByRoomIdWhenUsernameExists(baseChannel._id, {
|
||||
projection: { 'u.username': 1 },
|
||||
});
|
||||
const users = (await cursor.toArray()).map((s: ISubscription) => s.u.username).filter(isTruthy);
|
||||
|
||||
if (!targetChannel && ['c', 'p'].indexOf(baseChannel.t) > -1) {
|
||||
baseChannel.t === 'c' ? await createChannelMethod(userId, channel, users) : await createPrivateGroupMethod(user, channel, users);
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_created', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
await addUsersToRoomMethod(userId, {
|
||||
rid: targetChannel?._id ?? '',
|
||||
users,
|
||||
});
|
||||
}
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Users_added', { lng }),
|
||||
});
|
||||
} catch (e: any) {
|
||||
const msg = e.error === 'cant-invite-for-direct-room' ? 'Cannot_invite_users_to_direct_rooms' : e.error;
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t(msg, { lng }),
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
slashCommands.add({
|
||||
command: 'invite-all-to',
|
||||
callback: inviteAll('to'),
|
||||
options: {
|
||||
description: 'Invite_user_to_join_channel_all_to',
|
||||
params: '#room',
|
||||
permission: ['add-user-to-joined-room', 'add-user-to-any-c-room', 'add-user-to-any-p-room'],
|
||||
},
|
||||
});
|
||||
slashCommands.add({
|
||||
command: 'invite-all-from',
|
||||
callback: inviteAll('from'),
|
||||
options: {
|
||||
description: 'Invite_user_to_join_channel_all_from',
|
||||
params: '#room',
|
||||
permission: 'add-user-to-joined-room',
|
||||
},
|
||||
});
|
||||
module.exports = inviteAll;
|
||||
1
apps/meteor/server/slashcommands/join/index.ts
Normal file
1
apps/meteor/server/slashcommands/join/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
59
apps/meteor/server/slashcommands/join/server.ts
Normal file
59
apps/meteor/server/slashcommands/join/server.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { api, Room } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Rooms, Subscriptions, Users } from '@rocket.chat/models';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'join',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'join'>): Promise<void> => {
|
||||
let channel = params.trim();
|
||||
if (channel === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
channel = channel.replace('#', '');
|
||||
|
||||
const room = await Rooms.findOneByNameAndType(channel, 'c');
|
||||
if (!room) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, userId, {
|
||||
projection: { _id: 1 },
|
||||
});
|
||||
|
||||
if (subscription) {
|
||||
throw new Meteor.Error('error-user-already-in-room', 'You are already in the channel', {
|
||||
method: 'slashCommands',
|
||||
});
|
||||
}
|
||||
|
||||
const user = await Users.findOneById(userId);
|
||||
if (!user) {
|
||||
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
|
||||
method: 'slashCommands',
|
||||
});
|
||||
}
|
||||
await Room.join({ room, user });
|
||||
},
|
||||
options: {
|
||||
description: 'Join_the_given_channel',
|
||||
params: '#channel',
|
||||
permission: 'view-c-room',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/kick/index.ts
Normal file
1
apps/meteor/server/slashcommands/kick/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
44
apps/meteor/server/slashcommands/kick/server.ts
Normal file
44
apps/meteor/server/slashcommands/kick/server.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// Kick is a named function that will replace /kick commands
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { sanitizeUsername } from '../../meteor-methods/rooms/addUsersToRoom';
|
||||
import { removeUserFromRoomMethod } from '../../meteor-methods/rooms/removeUserFromRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'kick',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'kick'>): Promise<void> => {
|
||||
const username = sanitizeUsername(params.trim());
|
||||
if (username === '') {
|
||||
return;
|
||||
}
|
||||
const user = await Users.findOneById(userId);
|
||||
const lng = user?.language || settings.get('Language') || 'en';
|
||||
|
||||
const kickedUser = await Users.findOneByUsernameIgnoringCase(username);
|
||||
|
||||
if (kickedUser == null) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Username_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username,
|
||||
lng,
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { rid } = message;
|
||||
|
||||
await removeUserFromRoomMethod(userId, { rid, username });
|
||||
},
|
||||
options: {
|
||||
description: 'Remove_someone_from_room',
|
||||
params: '@username',
|
||||
permission: 'remove-user',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/leave/index.ts
Normal file
1
apps/meteor/server/slashcommands/leave/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './leave';
|
||||
49
apps/meteor/server/slashcommands/leave/leave.ts
Normal file
49
apps/meteor/server/slashcommands/leave/leave.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { leaveRoomMethod } from '../../meteor-methods/rooms/leaveRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
/*
|
||||
* Leave is a named function that will replace /leave commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
const Leave = async function Leave({ message, userId }: SlashCommandCallbackParams<'leave'>): Promise<void> {
|
||||
try {
|
||||
const user = await Users.findOneById(userId);
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
await leaveRoomMethod(user, message.rid);
|
||||
} catch (error: any) {
|
||||
if (typeof error.error !== 'string') {
|
||||
throw error;
|
||||
}
|
||||
const user = await Users.findOneById(userId);
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t(error, { lng: user?.language || settings.get('Language') || 'en' }),
|
||||
});
|
||||
throw new Meteor.Error(error.error, error.message);
|
||||
}
|
||||
};
|
||||
|
||||
slashCommands.add({
|
||||
command: 'leave',
|
||||
callback: Leave,
|
||||
options: {
|
||||
description: 'Leave_the_current_channel',
|
||||
permission: ['leave-c', 'leave-p'],
|
||||
},
|
||||
});
|
||||
slashCommands.add({
|
||||
command: 'part',
|
||||
callback: Leave,
|
||||
options: {
|
||||
description: 'Leave_the_current_channel',
|
||||
permission: ['leave-c', 'leave-p'],
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/me/index.ts
Normal file
1
apps/meteor/server/slashcommands/me/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './me';
|
||||
23
apps/meteor/server/slashcommands/me/me.ts
Normal file
23
apps/meteor/server/slashcommands/me/me.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
|
||||
/*
|
||||
* Me is a named function that will replace /me commands
|
||||
* @param {Object} message - The message object
|
||||
*/
|
||||
slashCommands.add({
|
||||
command: 'me',
|
||||
callback: async function Me({ params, message, userId }: SlashCommandCallbackParams<'me'>): Promise<void> {
|
||||
if (params.trim()) {
|
||||
const msg = message;
|
||||
msg.msg = `_${params}_`;
|
||||
await executeSendMessage(userId, msg);
|
||||
}
|
||||
},
|
||||
options: {
|
||||
description: 'Displays_action_text',
|
||||
params: 'your_message',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/msg/index.ts
Normal file
1
apps/meteor/server/slashcommands/msg/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
55
apps/meteor/server/slashcommands/msg/server.ts
Normal file
55
apps/meteor/server/slashcommands/msg/server.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
import { Random } from '@rocket.chat/random';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { createDirectMessage } from '../../meteor-methods/messages/createDirectMessage';
|
||||
import { executeSendMessage } from '../../meteor-methods/messages/sendMessage';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
/*
|
||||
* Msg is a named function that will replace /msg commands
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'msg',
|
||||
callback: async function Msg({ params, message: item, userId }: SlashCommandCallbackParams<'msg'>): Promise<void> {
|
||||
const trimmedParams = params.trim();
|
||||
const separator = trimmedParams.indexOf(' ');
|
||||
if (separator === -1) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, item.rid, {
|
||||
msg: i18n.t('Username_and_message_must_not_be_empty', { lng: settings.get('Language') || 'en' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const message = trimmedParams.slice(separator + 1);
|
||||
const targetUsernameOrig = trimmedParams.slice(0, separator);
|
||||
const targetUsername = targetUsernameOrig.replace('@', '');
|
||||
const targetUser = await Users.findOneByUsernameIgnoringCase(targetUsername);
|
||||
if (targetUser == null) {
|
||||
const user = await Users.findOneById(userId, { projection: { language: 1 } });
|
||||
void api.broadcast('notify.ephemeralMessage', userId, item.rid, {
|
||||
msg: i18n.t('Username_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username: targetUsernameOrig,
|
||||
lng: user?.language || settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { rid } = await createDirectMessage([targetUsername], userId);
|
||||
const msgObject = {
|
||||
_id: Random.id(),
|
||||
rid,
|
||||
msg: message,
|
||||
};
|
||||
await executeSendMessage(userId, msgObject);
|
||||
},
|
||||
options: {
|
||||
description: 'Direct_message_someone',
|
||||
params: '@username <message>',
|
||||
permission: 'create-d',
|
||||
},
|
||||
});
|
||||
2
apps/meteor/server/slashcommands/mute/index.ts
Normal file
2
apps/meteor/server/slashcommands/mute/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import './mute';
|
||||
import './unmute';
|
||||
43
apps/meteor/server/slashcommands/mute/mute.ts
Normal file
43
apps/meteor/server/slashcommands/mute/mute.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { muteUserInRoom } from '../../meteor-methods/rooms/muteUserInRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
/*
|
||||
* Mute is a named function that will replace /mute commands
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'mute',
|
||||
callback: async function Mute({ params, message, userId }: SlashCommandCallbackParams<'mute'>): Promise<void> {
|
||||
const username = params.trim().replace('@', '');
|
||||
if (username === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const mutedUser = await Users.findOneByUsernameIgnoringCase(username);
|
||||
if (mutedUser == null) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Username_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
await muteUserInRoom(userId, {
|
||||
rid: message.rid,
|
||||
username,
|
||||
});
|
||||
},
|
||||
options: {
|
||||
description: 'Mute_someone_in_room',
|
||||
params: '@username',
|
||||
permission: 'mute-user',
|
||||
},
|
||||
});
|
||||
43
apps/meteor/server/slashcommands/mute/unmute.ts
Normal file
43
apps/meteor/server/slashcommands/mute/unmute.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { unmuteUserInRoom } from '../../meteor-methods/rooms/unmuteUserInRoom';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
/*
|
||||
* Unmute is a named function that will replace /unmute commands
|
||||
*/
|
||||
|
||||
slashCommands.add({
|
||||
command: 'unmute',
|
||||
callback: async function Unmute({ params, message, userId }: SlashCommandCallbackParams<'unmute'>): Promise<void> {
|
||||
const username = params.trim().replace('@', '');
|
||||
if (username === '') {
|
||||
return;
|
||||
}
|
||||
const unmutedUser = await Users.findOneByUsernameIgnoringCase(username);
|
||||
if (unmutedUser == null) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Username_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
username,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await unmuteUserInRoom(userId, {
|
||||
rid: message.rid,
|
||||
username,
|
||||
});
|
||||
},
|
||||
options: {
|
||||
description: 'Unmute_someone_in_room',
|
||||
params: '@username',
|
||||
permission: 'mute-user',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/status/index.ts
Normal file
1
apps/meteor/server/slashcommands/status/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './status';
|
||||
47
apps/meteor/server/slashcommands/status/status.ts
Normal file
47
apps/meteor/server/slashcommands/status/status.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import type { SlashCommandCallbackParams, IUser } from '@rocket.chat/core-typings';
|
||||
import { Users } from '@rocket.chat/models';
|
||||
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { setUserStatusMethod } from '../../meteor-methods/users/setUserStatus';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'status',
|
||||
callback: async function Status({ params, message, userId }: SlashCommandCallbackParams<'status'>): Promise<void> {
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await Users.findOneById<
|
||||
Pick<IUser, '_id' | 'username' | 'name' | 'status' | 'statusDefault' | 'roles' | 'statusText' | 'language'>
|
||||
>(userId, {
|
||||
projection: { language: 1, username: 1, name: 1, status: 1, statusDefault: 1, roles: 1, statusText: 1 },
|
||||
});
|
||||
const lng = user?.language || settings.get('Language') || 'en';
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await setUserStatusMethod(user, undefined, params);
|
||||
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('StatusMessage_Changed_Successfully', { lng }),
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err.error === 'error-not-allowed') {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('StatusMessage_Change_Disabled', { lng }),
|
||||
});
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
options: {
|
||||
description: 'Slash_Status_Description',
|
||||
params: 'Slash_Status_Params',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/topic/index.ts
Normal file
1
apps/meteor/server/slashcommands/topic/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './topic';
|
||||
19
apps/meteor/server/slashcommands/topic/topic.ts
Normal file
19
apps/meteor/server/slashcommands/topic/topic.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
|
||||
import { hasPermissionAsync } from '../../lib/authorization/hasPermission';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { saveRoomSettings } from '../../meteor-methods/rooms/saveRoomSettings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'topic',
|
||||
callback: async ({ params, message, userId }: SlashCommandCallbackParams<'topic'>): Promise<void> => {
|
||||
if (userId && (await hasPermissionAsync(userId, 'edit-room', message.rid))) {
|
||||
await saveRoomSettings(userId, message.rid, 'roomTopic', params);
|
||||
}
|
||||
},
|
||||
options: {
|
||||
description: 'Slash_Topic_Description',
|
||||
params: 'Slash_Topic_Params',
|
||||
permission: 'edit-room',
|
||||
},
|
||||
});
|
||||
1
apps/meteor/server/slashcommands/unarchiveroom/index.ts
Normal file
1
apps/meteor/server/slashcommands/unarchiveroom/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './server';
|
||||
85
apps/meteor/server/slashcommands/unarchiveroom/server.ts
Normal file
85
apps/meteor/server/slashcommands/unarchiveroom/server.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import { isRegisterUser } from '@rocket.chat/core-typings';
|
||||
import type { SlashCommandCallbackParams } from '@rocket.chat/core-typings';
|
||||
import { Users, Rooms } from '@rocket.chat/models';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { RoomMemberActions } from '../../../definition/IRoomTypeConfig';
|
||||
import { hasPermissionAsync } from '../../lib/authorization/hasPermission';
|
||||
import { i18n } from '../../lib/i18n';
|
||||
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
|
||||
import { unarchiveRoom } from '../../lib/rooms/unarchiveRoom';
|
||||
import { slashCommands } from '../../lib/utils/slashCommand';
|
||||
import { settings } from '../../settings';
|
||||
|
||||
slashCommands.add({
|
||||
command: 'unarchive',
|
||||
callback: async function Unarchive({ params, message, userId }: SlashCommandCallbackParams<'unarchive'>): Promise<void> {
|
||||
let channel = params.trim();
|
||||
let room;
|
||||
|
||||
if (channel === '') {
|
||||
room = await Rooms.findOneById(message.rid);
|
||||
if (room?.name) {
|
||||
channel = room.name;
|
||||
}
|
||||
} else {
|
||||
channel = channel.replace('#', '');
|
||||
room = await Rooms.findOneByName(channel);
|
||||
}
|
||||
|
||||
if (!userId) {
|
||||
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
|
||||
}
|
||||
|
||||
const user = await Users.findOneById(userId, { projection: { username: 1, name: 1 } });
|
||||
if (!user || !isRegisterUser(user)) {
|
||||
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'archiveRoom' });
|
||||
}
|
||||
|
||||
if (!room) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_doesnt_exist', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await roomCoordinator.getRoomDirectives(room.t).allowMemberAction(room, RoomMemberActions.ARCHIVE, userId))) {
|
||||
throw new Meteor.Error('error-room-type-not-unarchivable', `Room type: ${room.t} can not be unarchived`);
|
||||
}
|
||||
|
||||
if (!(await hasPermissionAsync(userId, 'unarchive-room', room._id))) {
|
||||
throw new Meteor.Error('error-not-authorized', 'Not authorized');
|
||||
}
|
||||
|
||||
if (!room.archived) {
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_already_Unarchived', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await unarchiveRoom(room._id, user);
|
||||
|
||||
void api.broadcast('notify.ephemeralMessage', userId, message.rid, {
|
||||
msg: i18n.t('Channel_Unarchived', {
|
||||
interpolation: { escapeValue: false },
|
||||
channelName: channel,
|
||||
lng: settings.get('Language') || 'en',
|
||||
}),
|
||||
});
|
||||
},
|
||||
options: {
|
||||
description: 'Unarchive',
|
||||
params: '#channel',
|
||||
permission: 'unarchive-room',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user