Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
for (const userRule of list.userRules) {
if (userRule.isMatch(member.userId)) {
// User needs to be banned
// We specifically use sendNotice to avoid having to escape HTML
await logMessage(LogLevel.DEBUG, "ApplyBan", `Banning ${member.userId} in ${roomId} for: ${userRule.reason}`);
if (!config.noop) {
// Always prioritize redactions above bans
if (mjolnir.automaticRedactGlobs.find(g => g.test(userRule.reason.toLowerCase()))) {
await redactUserMessagesIn(mjolnir.client, member.userId, [roomId]);
}
await mjolnir.client.banUser(member.userId, roomId, userRule.reason);
} else {
await logMessage(LogLevel.WARN, "ApplyBan", `Tried to ban ${member.userId} in ${roomId} but Mjolnir is running in no-op mode`);
}
bansApplied++;
banned = true;
break;
}
}
if (banned) break;
}
}
} catch (e) {
const message = e.message || (e.body ? e.body.error : '');
errors.push({
roomId,
errorMessage: message,
errorKind: message.includes("You don't have permission to ban") ? ERROR_KIND_PERMISSION : ERROR_KIND_FATAL,
if (!fs.existsSync("./data")) {
fs.mkdirSync("./data");
}
if (!fs.existsSync(storageDir)) {
fs.mkdirSync(storageDir);
}
// We'll want to make sure the bot doesn't have to do an initial sync every
// time it restarts, so we need to prepare a storage provider. Here we use
// a simple JSON database.
const storage = new SimpleFsStorageProvider(
path.join(storageDir, "matrix.json")
);
// Now we can create the client and set it up to automatically join rooms.
const client = new MatrixClient(
config.homeserverUrl,
config.accessToken,
storage
);
AutojoinRoomsMixin.setupOnClient(client);
// We also want to make sure we can receive events - this is where we will
// handle our command.
client.on("room.message", makeHandleCommand(client, config));
// Now that the client is all set up and the event handler is registered, start the
// client up. This will start it syncing.
await client.start();
console.log("Client started!");
}
const ruleContent = {}; // empty == clear/unban
const stateKey = `rule:${bits.entity}`;
await mjolnir.client.sendStateEvent(bits.list.roomId, bits.ruleType, stateKey, ruleContent);
if (USER_RULE_TYPES.includes(bits.ruleType) && parts.length > 5 && parts[5] === 'true') {
const rule = new MatrixGlob(bits.entity);
await logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.entity);
let unbannedSomeone = false;
for (const protectedRoomId of Object.keys(mjolnir.protectedRooms)) {
const members = await mjolnir.client.getRoomMembers(protectedRoomId, null, ['ban'], null);
for (const member of members) {
const victim = member['state_key'];
if (!member['content'] || member['content']['membership'] !== 'ban') continue;
if (rule.test(victim)) {
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Unbanning ${victim} in ${protectedRoomId}`);
if (!config.noop) {
await mjolnir.client.unbanUser(victim, protectedRoomId);
} else {
await logMessage(LogLevel.WARN, "UnbanBanCommand", `Attempted to unban ${victim} in ${protectedRoomId} but Mjolnir is running in no-op mode`);
}
unbannedSomeone = true;
}
}
}
if (unbannedSomeone) {
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Syncing lists to ensure no users were accidentally unbanned`);
await mjolnir.syncLists(config.verboseLogging);
}
});
}
for (const member of members) {
if (member.membership === 'ban') {
continue; // user already banned
}
let banned = false;
for (const list of lists) {
for (const userRule of list.userRules) {
if (userRule.isMatch(member.userId)) {
// User needs to be banned
// We specifically use sendNotice to avoid having to escape HTML
await logMessage(LogLevel.DEBUG, "ApplyBan", `Banning ${member.userId} in ${roomId} for: ${userRule.reason}`);
if (!config.noop) {
// Always prioritize redactions above bans
if (mjolnir.automaticRedactGlobs.find(g => g.test(userRule.reason.toLowerCase()))) {
await redactUserMessagesIn(mjolnir.client, member.userId, [roomId]);
}
await mjolnir.client.banUser(member.userId, roomId, userRule.reason);
} else {
await logMessage(LogLevel.WARN, "ApplyBan", `Tried to ban ${member.userId} in ${roomId} but Mjolnir is running in no-op mode`);
}
bansApplied++;
banned = true;
break;
}
export async function execUnbanCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
const bits = await parseArguments(roomId, event, mjolnir, parts);
if (!bits) return; // error already handled
const ruleContent = {}; // empty == clear/unban
const stateKey = `rule:${bits.entity}`;
await mjolnir.client.sendStateEvent(bits.list.roomId, bits.ruleType, stateKey, ruleContent);
if (USER_RULE_TYPES.includes(bits.ruleType) && parts.length > 5 && parts[5] === 'true') {
const rule = new MatrixGlob(bits.entity);
await logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.entity);
let unbannedSomeone = false;
for (const protectedRoomId of Object.keys(mjolnir.protectedRooms)) {
const members = await mjolnir.client.getRoomMembers(protectedRoomId, null, ['ban'], null);
for (const member of members) {
const victim = member['state_key'];
if (!member['content'] || member['content']['membership'] !== 'ban') continue;
if (rule.test(victim)) {
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Unbanning ${victim} in ${protectedRoomId}`);
if (!config.noop) {
await mjolnir.client.unbanUser(victim, protectedRoomId);
} else {
await logMessage(LogLevel.WARN, "UnbanBanCommand", `Attempted to unban ${victim} in ${protectedRoomId} but Mjolnir is running in no-op mode`);
}
}
// We'll want to make sure the bot doesn't have to do an initial sync every
// time it restarts, so we need to prepare a storage provider. Here we use
// a simple JSON database.
const storage = new SimpleFsStorageProvider(
path.join(storageDir, "matrix.json")
);
// Now we can create the client and set it up to automatically join rooms.
const client = new MatrixClient(
config.homeserverUrl,
config.accessToken,
storage
);
AutojoinRoomsMixin.setupOnClient(client);
// We also want to make sure we can receive events - this is where we will
// handle our command.
client.on("room.message", makeHandleCommand(client, config));
// Now that the client is all set up and the event handler is registered, start the
// client up. This will start it syncing.
await client.start();
console.log("Client started!");
}
export async function execUnbanCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) {
const bits = await parseArguments(roomId, event, mjolnir, parts);
if (!bits) return; // error already handled
const ruleContent = {}; // empty == clear/unban
const stateKey = `rule:${bits.entity}`;
await mjolnir.client.sendStateEvent(bits.list.roomId, bits.ruleType, stateKey, ruleContent);
if (USER_RULE_TYPES.includes(bits.ruleType) && parts.length > 5 && parts[5] === 'true') {
const rule = new MatrixGlob(bits.entity);
await logMessage(LogLevel.INFO, "UnbanBanCommand", "Unbanning users that match glob: " + bits.entity);
let unbannedSomeone = false;
for (const protectedRoomId of Object.keys(mjolnir.protectedRooms)) {
const members = await mjolnir.client.getRoomMembers(protectedRoomId, null, ['ban'], null);
for (const member of members) {
const victim = member['state_key'];
if (!member['content'] || member['content']['membership'] !== 'ban') continue;
if (rule.test(victim)) {
await logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Unbanning ${victim} in ${protectedRoomId}`);
if (!config.noop) {
await mjolnir.client.unbanUser(victim, protectedRoomId);
} else {
await logMessage(LogLevel.WARN, "UnbanBanCommand", `Attempted to unban ${victim} in ${protectedRoomId} but Mjolnir is running in no-op mode`);
}
unbannedSomeone = true;
client.on("room.message", async (roomId, event) => {
if (roomId !== config.managementRoom) return;
if (!event['content']) return;
const content = event['content'];
if (content['msgtype'] === "m.text" && content['body']) {
const prefixes = [COMMAND_PREFIX, this.localpart + ":", this.displayName + ":", await client.getUserId() + ":"];
const prefixUsed = prefixes.find(p => content['body'].startsWith(p));
if (!prefixUsed) return;
// rewrite the event body to make the prefix uniform (in case the bot has spaces in its display name)
event['content']['body'] = COMMAND_PREFIX + content['body'].substring(prefixUsed.length);
LogService.info("Mjolnir", `Command being run by ${event['sender']}: ${event['content']['body']}`);
await client.sendReadReceipt(roomId, event['event_id']);
return handleCommand(roomId, event, this);
}
});
for (const topic of Object.keys(this.listeners)) await this.doBind(topic);
LogService.info("mq", `Asserting dead letter queue ${VoyagerConfig.rabbitmq.deadLetterQueue} exists...`);
await this.channel.assertQueue(VoyagerConfig.rabbitmq.deadLetterQueue, {
durable: true,
messageTtl: 2 * 60 * 1000, // 2 minutes
deadLetterExchange: VoyagerConfig.rabbitmq.exchange,
});
LogService.info("mq", "Binding dead letter exchange to dead letter queue...");
await this.channel.bindQueue(VoyagerConfig.rabbitmq.deadLetterQueue, VoyagerConfig.rabbitmq.deadLetterExchange, "*");
LogService.info("mq", "Listening for events...");
await this.channel.consume(this.queueName, this.onMessage.bind(this));
LogService.info("mq", "RabbitMQ ready");
}
LogService.info("mq", `Asserting queue ${this.queueName} exists...`);
await this.channel.assertQueue(this.queueName, {
exclusive: true,
autoDelete: true,
deadLetterExchange: VoyagerConfig.rabbitmq.deadLetterExchange,
});
for (const topic of Object.keys(this.listeners)) await this.doBind(topic);
LogService.info("mq", `Asserting dead letter queue ${VoyagerConfig.rabbitmq.deadLetterQueue} exists...`);
await this.channel.assertQueue(VoyagerConfig.rabbitmq.deadLetterQueue, {
durable: true,
messageTtl: 2 * 60 * 1000, // 2 minutes
deadLetterExchange: VoyagerConfig.rabbitmq.exchange,
});
LogService.info("mq", "Binding dead letter exchange to dead letter queue...");
await this.channel.bindQueue(VoyagerConfig.rabbitmq.deadLetterQueue, VoyagerConfig.rabbitmq.deadLetterExchange, "*");
LogService.info("mq", "Listening for events...");
await this.channel.consume(this.queueName, this.onMessage.bind(this));
LogService.info("mq", "RabbitMQ ready");
}