add option to bridge nicknames/masquerade data

This commit is contained in:
Jan 2022-11-07 21:17:18 +01:00
parent 0408c4b0a5
commit 55f5e339e1
Signed by: Lea
GPG key ID: 5D5E18ACB990F57A
5 changed files with 392 additions and 179 deletions

View file

@ -7,18 +7,20 @@ import { BRIDGED_MESSAGES, BRIDGE_CONFIG, BRIDGE_REQUESTS, BRIDGE_USER_CONFIG, l
import { MessageEmbed, TextChannel } from "discord.js"; import { MessageEmbed, TextChannel } from "discord.js";
import { revoltFetchMessage, revoltFetchUser } from "../util"; import { revoltFetchMessage, revoltFetchUser } from "../util";
import { client as revoltClient } from "../revolt/client"; import { client as revoltClient } from "../revolt/client";
import { CONFIG_KEYS } from "../types/ConfigKeys";
const PRIVACY_POLICY_URL = 'https://github.com/janderedev/automod/wiki/Privacy-Policy'; const PRIVACY_POLICY_URL =
"https://github.com/janderedev/automod/wiki/Privacy-Policy";
const COMMANDS: any[] = [ const COMMANDS: any[] = [
{ {
name: 'bridge', name: "bridge",
description: 'Confirm or delete Revolt bridges', description: "Confirm or delete Revolt bridges",
type: 1, // Slash command type: 1, // Slash command
options: [ options: [
{ {
name: 'confirm', name: "confirm",
description: 'Confirm a bridge initiated from Revolt', description: "Confirm a bridge initiated from Revolt",
type: 1, // Subcommand type: 1, // Subcommand
options: [ options: [
{ {
@ -26,99 +28,153 @@ const COMMANDS: any[] = [
description: "The bridge request ID", description: "The bridge request ID",
required: true, required: true,
type: 3, type: 3,
} },
], ],
}, },
{ {
name: 'unlink', name: "unlink",
description: 'Unbridge the current channel', description: "Unbridge the current channel",
type: 1, type: 1,
}, },
{ {
name: 'help', name: "help",
description: 'Usage instructions', description: "Usage instructions",
type: 1, type: 1,
}, },
{ {
name: 'opt_out', name: "opt_out",
description: 'Opt out of having your messages bridged', description: "Opt out of having your messages bridged",
type: 1, type: 1,
options: [ options: [
{ {
name: 'opt_out', name: "opt_out",
description: 'Whether you wish to opt out of having your messages bridged', description:
"Whether you wish to opt out of having your messages bridged",
optional: true, optional: true,
type: 5 // Boolean type: 5, // Boolean
}, },
], ],
}, },
{ {
name: 'status', name: "status",
description: 'Find out whether this channel is bridged to Revolt', description:
type: 1 "Find out whether this channel is bridged to Revolt",
} type: 1,
},
{
name: "config",
description: "Bridge configuration options for this channel",
type: 1,
options: [
{
name: "key",
description: "The configuration option to change",
type: 3, // String
required: true,
choices: Object.entries(CONFIG_KEYS).map((conf) => ({
name: conf[1].friendlyName,
value: conf[0],
})),
},
{
name: "value",
description:
"The new value for the option. Leave empty to get current state",
type: 5, // Boolean
required: false,
},
],
},
], ],
}, },
{ {
name: 'Message Info', name: "Message Info",
description: '', description: "",
type: 3, // Message context menu type: 3, // Message context menu
} },
]; ];
const rest = new REST({ version: '9' }).setToken(process.env['DISCORD_TOKEN']!); const rest = new REST({ version: "9" }).setToken(process.env["DISCORD_TOKEN"]!);
client.once('ready', async () => { client.once("ready", async () => {
try { try {
logger.info(`Refreshing application commands.`); logger.info(`Refreshing application commands.`);
if (process.env.NODE_ENV != 'production' && process.env.DEV_GUILD) { if (process.env.NODE_ENV != "production" && process.env.DEV_GUILD) {
await rest.put( await rest.put(
Routes.applicationGuildCommands(client.user!.id, process.env.DEV_GUILD), Routes.applicationGuildCommands(
{ body: COMMANDS }, client.user!.id,
process.env.DEV_GUILD
),
{ body: COMMANDS }
);
logger.done(
`Application commands for ${process.env.DEV_GUILD} have been updated.`
); );
logger.done(`Application commands for ${process.env.DEV_GUILD} have been updated.`);
} else { } else {
await rest.put( await rest.put(Routes.applicationCommands(client.user!.id), {
Routes.applicationCommands(client.user!.id), body: COMMANDS,
{ body: COMMANDS }, });
);
logger.done(`Global application commands have been updated.`); logger.done(`Global application commands have been updated.`);
} }
} catch(e) { } catch (e) {
console.error(e); console.error(e);
} }
}); });
client.on('interactionCreate', async interaction => { client.on("interactionCreate", async (interaction) => {
try { try {
if (interaction.isCommand()) { if (interaction.isCommand()) {
logger.debug(`Command received: /${interaction.commandName}`); logger.debug(`Command received: /${interaction.commandName}`);
// The revolutionary Jan command handler // The revolutionary Jan command handler
switch(interaction.commandName) { switch (interaction.commandName) {
case 'bridge': case "bridge":
if (!interaction.memberPermissions?.has('MANAGE_GUILD') && if (
['confirm', 'unlink'].includes(interaction.options.getSubcommand(true)) !interaction.memberPermissions?.has("MANAGE_GUILD") &&
["confirm", "unlink"].includes(
interaction.options.getSubcommand(true)
)
) { ) {
return await interaction.reply({ content: `\`MANAGE_GUILD\` permission is required for this.`, ephemeral: true }); return await interaction.reply({
content: `\`MANAGE_GUILD\` permission is required for this.`,
ephemeral: true,
});
} }
const ownPerms = (interaction.channel as TextChannel).permissionsFor(client.user!)!; const ownPerms = (
switch(interaction.options.getSubcommand(true)) { interaction.channel as TextChannel
case 'confirm': ).permissionsFor(client.user!)!;
if (!ownPerms.has('MANAGE_WEBHOOKS')) switch (interaction.options.getSubcommand(true)) {
return interaction.reply('Sorry, I lack permission to manage webhooks in this channel.'); case "confirm": {
if (!ownPerms.has("MANAGE_WEBHOOKS"))
return interaction.reply(
"Sorry, I lack permission to manage webhooks in this channel."
);
const id = interaction.options.getString('id', true); const id = interaction.options.getString(
const request = await BRIDGE_REQUESTS.findOne({ id: id }); "id",
if (!request || request.expires < Date.now()) return await interaction.reply('Unknown ID.'); true
);
const request = await BRIDGE_REQUESTS.findOne({
id: id,
});
if (!request || request.expires < Date.now())
return await interaction.reply("Unknown ID.");
const bridgedCount = await BRIDGE_CONFIG.count({ discord: interaction.channelId }); const bridgedCount = await BRIDGE_CONFIG.count({
if (bridgedCount > 0) return await interaction.reply('This channel is already bridged.'); discord: interaction.channelId,
});
if (bridgedCount > 0)
return await interaction.reply(
"This channel is already bridged."
);
const webhook = await (interaction.channel as TextChannel) const webhook = await(
.createWebhook('AutoMod Bridge', { avatar: client.user?.avatarURL() }); interaction.channel as TextChannel
).createWebhook("AutoMod Bridge", {
avatar: client.user?.avatarURL(),
});
await BRIDGE_REQUESTS.remove({ id: id }); await BRIDGE_REQUESTS.remove({ id: id });
await BRIDGE_CONFIG.insert({ await BRIDGE_CONFIG.insert({
@ -126,58 +182,133 @@ client.on('interactionCreate', async interaction => {
revolt: request.revolt, revolt: request.revolt,
discordWebhook: { discordWebhook: {
id: webhook.id, id: webhook.id,
token: webhook.token || '', token: webhook.token || "",
} },
}); });
return await interaction.reply(`✅ Channel bridged!`); return await interaction.reply(
case 'unlink': `✅ Channel bridged!`
const res = await BRIDGE_CONFIG.findOneAndDelete({ discord: interaction.channelId }); );
}
case "unlink": {
const res = await BRIDGE_CONFIG.findOneAndDelete({
discord: interaction.channelId,
});
if (res?._id) { if (res?._id) {
await interaction.reply('Channel unbridged.'); await interaction.reply("Channel unbridged.");
if (ownPerms.has('MANAGE_WEBHOOKS') && res.discordWebhook) { if (
ownPerms.has("MANAGE_WEBHOOKS") &&
res.discordWebhook
) {
try { try {
const hooks = await (interaction.channel as TextChannel).fetchWebhooks(); const hooks = await(
if (hooks.get(res?.discordWebhook?.id)) await hooks.get(res?.discordWebhook?.id) interaction.channel as TextChannel
?.delete('Channel has been unbridged'); ).fetchWebhooks();
} catch(_) {} if (hooks.get(res?.discordWebhook?.id))
await hooks
.get(res?.discordWebhook?.id)
?.delete(
"Channel has been unbridged"
);
} catch (_) {}
} }
} } else
else await interaction.reply('This channel is not bridged.'); await interaction.reply(
"This channel is not bridged."
);
break; break;
}
case 'help': case "config": {
const isPrivileged = !!interaction.memberPermissions?.has('MANAGE_GUILD'); const configKey = interaction.options.getString(
"key",
true
) as keyof typeof CONFIG_KEYS;
const newValue = interaction.options.getBoolean(
"value",
false
);
if (newValue == null) {
const currentState =
(
await BRIDGE_CONFIG.findOne({
discord: interaction.channelId,
})
)?.config?.[configKey] ?? false;
return await interaction.reply({
ephemeral: true,
embeds: [
new MessageEmbed()
.setAuthor({
name: "Bridge Configuration",
})
.setTitle(configKey)
.setDescription(
`${CONFIG_KEYS[configKey].description}\n\nCurrent state: \`${currentState}\``
)
.toJSON(),
],
});
}
await BRIDGE_CONFIG.update(
{ discord: interaction.channelId },
{
$set: { [`config.${configKey}`]: newValue },
$setOnInsert: {
discord: interaction.channelId,
},
},
{ upsert: true }
);
return await interaction.reply({
ephemeral: true,
content: `Option \`${configKey}\` has been updated to \`${newValue}\`.`,
});
}
case "help": {
const isPrivileged =
!!interaction.memberPermissions?.has(
"MANAGE_GUILD"
);
const INVITE_URL = `https://discord.com/api/oauth2/authorize?client_id=${client.user?.id}&permissions=536996864&scope=bot%20applications.commands`; const INVITE_URL = `https://discord.com/api/oauth2/authorize?client_id=${client.user?.id}&permissions=536996864&scope=bot%20applications.commands`;
const embed = new MessageEmbed() const embed = new MessageEmbed()
.setColor('#ff6e6d') .setColor("#ff6e6d")
.setAuthor({ name: 'AutoMod Revolt Bridge', iconURL: client.user?.displayAvatarURL() }); .setAuthor({
name: "AutoMod Revolt Bridge",
iconURL: client.user?.displayAvatarURL(),
});
embed.setDescription( embed.setDescription(
'[AutoMod](https://automod.me) is a utility and moderation bot for [Revolt](https://revolt.chat). ' + "[AutoMod](https://automod.me) is a utility and moderation bot for [Revolt](https://revolt.chat). " +
'This Discord bot allows you to link your Discord servers to your Revolt servers ' + "This Discord bot allows you to link your Discord servers to your Revolt servers " +
'by mirroring messages between text channels.' "by mirroring messages between text channels."
); );
embed.addField( embed.addField(
'Setting up a bridge', "Setting up a bridge",
isPrivileged isPrivileged
? 'The bridge process is initialized by running the `/bridge link` command in the Revolt ' + ? "The bridge process is initialized by running the `/bridge link` command in the Revolt " +
'channel you wish to bridge.\n' + "channel you wish to bridge.\n" +
'Afterwards you can run the `/bridge confirm` command in the correct Discord channel to finish the link.' "Afterwards you can run the `/bridge confirm` command in the correct Discord channel to finish the link."
: 'You don\'t have `Manage Messages` permission - Please ask a moderator to configure the bridge.' : "You don't have `Manage Messages` permission - Please ask a moderator to configure the bridge."
); );
embed.addField( embed.addField(
'Adding AutoMod to your server', "Adding AutoMod to your server",
`You can add the Revolt bot to your server ` + `You can add the Revolt bot to your server ` +
`[here](https://app.revolt.chat/bot/${revoltClient.user?._id} "Open Revolt"). To add the Discord counterpart, ` + `[here](https://app.revolt.chat/bot/${revoltClient.user?._id} "Open Revolt"). To add the Discord counterpart, ` +
`click [here](${INVITE_URL} "Add Discord bot").` `click [here](${INVITE_URL} "Add Discord bot").`
); );
embed.addField( embed.addField(
'Contact', "Contact",
`If you have any questions regarding this bot or the Revolt counterpart, feel free to join ` + `If you have any questions regarding this bot or the Revolt counterpart, feel free to join ` +
`[this](https://discord.gg/4pZgvqgYJ8) Discord server or [this](https://rvlt.gg/jan) Revolt server.\n` + `[this](https://discord.gg/4pZgvqgYJ8) Discord server or [this](https://rvlt.gg/jan) Revolt server.\n` +
`If you want to report a bug, suggest a feature or browse the source code, ` + `If you want to report a bug, suggest a feature or browse the source code, ` +
@ -186,31 +317,45 @@ client.on('interactionCreate', async interaction => {
`Before using this bot, please read the [Privacy Policy](${PRIVACY_POLICY_URL})!` `Before using this bot, please read the [Privacy Policy](${PRIVACY_POLICY_URL})!`
); );
await interaction.reply({ embeds: [ embed ], ephemeral: true }); await interaction.reply({
embeds: [embed],
ephemeral: true,
});
break; break;
}
case 'opt_out': case "opt_out": {
const optOut = interaction.options.getBoolean('opt_out', false); const optOut = interaction.options.getBoolean(
"opt_out",
false
);
if (optOut == null) { if (optOut == null) {
const userConfig = await BRIDGE_USER_CONFIG.findOne({ id: interaction.user.id }); const userConfig =
await BRIDGE_USER_CONFIG.findOne({
id: interaction.user.id,
});
if (userConfig?.optOut) { if (userConfig?.optOut) {
return await interaction.reply({ return await interaction.reply({
ephemeral: true, ephemeral: true,
content: 'You are currently **opted out** of message bridging. ' + content:
'Users on Revolt **will not** see your username, avatar or message content.' "You are currently **opted out** of message bridging. " +
"Users on Revolt **will not** see your username, avatar or message content.",
}); });
} else { } else {
return await interaction.reply({ return await interaction.reply({
ephemeral: true, ephemeral: true,
content: 'You are currently **not** opted out of message bridging. ' + content:
'All your messages in a bridged channel will be sent to the associated Revolt channel.' "You are currently **not** opted out of message bridging. " +
"All your messages in a bridged channel will be sent to the associated Revolt channel.",
}); });
} }
} else { } else {
await BRIDGE_USER_CONFIG.update( await BRIDGE_USER_CONFIG.update(
{ id: interaction.user.id }, { id: interaction.user.id },
{ {
$setOnInsert: { id: interaction.user.id }, $setOnInsert: {
id: interaction.user.id,
},
$set: { optOut }, $set: { optOut },
}, },
{ upsert: true } { upsert: true }
@ -218,104 +363,135 @@ client.on('interactionCreate', async interaction => {
return await interaction.reply({ return await interaction.reply({
ephemeral: true, ephemeral: true,
content: `You have **opted ${optOut ? 'out of' : 'into'}** message bridging. ` content:
+ ( `You have **opted ${
optOut optOut ? "out of" : "into"
? 'Your username, avatar and message content will no longer be visible on Revolt.\n' + }** message bridging. ` +
'Please note that some servers may be configured to automatically delete your messages.' (optOut
: 'All your messages in a bridged channel will be sent to the associated Revolt channel.' ? "Your username, avatar and message content will no longer be visible on Revolt.\n" +
), "Please note that some servers may be configured to automatically delete your messages."
: "All your messages in a bridged channel will be sent to the associated Revolt channel."),
}); });
} }
break; }
case 'status': case "status": {
const bridgeConfig = await BRIDGE_CONFIG.findOne({ discord: interaction.channelId }); const bridgeConfig = await BRIDGE_CONFIG.findOne({
discord: interaction.channelId,
});
if (!bridgeConfig?.revolt) { if (!bridgeConfig?.revolt) {
return await interaction.reply({ return await interaction.reply({
ephemeral: true, ephemeral: true,
content: 'This channel is **not** bridged. No message content data will be processed.', content:
"This channel is **not** bridged. No message content data will be processed.",
}); });
} } else {
else {
return await interaction.reply({ return await interaction.reply({
ephemeral: true, ephemeral: true,
content: 'This channel is **bridged to Revolt**. Your messages will ' + content:
'be processed and sent to [Revolt](<https://revolt.chat>) according to AutoMod\'s ' + "This channel is **bridged to Revolt**. Your messages will " +
"be processed and sent to [Revolt](<https://revolt.chat>) according to AutoMod's " +
`[Privacy Policy](<${PRIVACY_POLICY_URL}>).`, `[Privacy Policy](<${PRIVACY_POLICY_URL}>).`,
}); });
} }
break; break;
}
default: await interaction.reply('Unknown subcommand'); default:
await interaction.reply("Unknown subcommand");
} }
break; break;
} }
} } else if (interaction.isMessageContextMenu()) {
else if (interaction.isMessageContextMenu()) { logger.debug(
logger.debug(`Received context menu: ${interaction.targetMessage.id}`); `Received context menu: ${interaction.targetMessage.id}`
);
switch(interaction.commandName) { switch (interaction.commandName) {
case 'Message Info': case "Message Info":
const message = interaction.targetMessage; const message = interaction.targetMessage;
const bridgeInfo = await BRIDGED_MESSAGES.findOne({ "discord.messageId": message.id }); const bridgeInfo = await BRIDGED_MESSAGES.findOne({
"discord.messageId": message.id,
});
const messageUrl = `https://discord.com/channels/${interaction.guildId}/${interaction.channelId}/${message.id}`; const messageUrl = `https://discord.com/channels/${interaction.guildId}/${interaction.channelId}/${message.id}`;
if (!bridgeInfo) return await interaction.reply({ if (!bridgeInfo)
return await interaction.reply({
ephemeral: true, ephemeral: true,
embeds: [ embeds: [
new MessageEmbed() new MessageEmbed()
.setAuthor({ name: 'Message info', url: messageUrl }) .setAuthor({
.setDescription('This message has not been bridged.') name: "Message info",
.setColor('#7e96ff'), url: messageUrl,
})
.setDescription(
"This message has not been bridged."
)
.setColor("#7e96ff"),
], ],
}); });
else { else {
const embed = new MessageEmbed(); const embed = new MessageEmbed();
embed.setColor('#7e96ff'); embed.setColor("#7e96ff");
embed.setAuthor({ name: 'Message info', url: messageUrl }); embed.setAuthor({
name: "Message info",
url: messageUrl,
});
embed.addField('Origin', bridgeInfo.origin == 'discord' ? 'Discord' : 'Revolt', true);
if (bridgeInfo.origin == 'discord') {
embed.addField( embed.addField(
'Bridge Status', "Origin",
bridgeInfo.origin == "discord"
? "Discord"
: "Revolt",
true
);
if (bridgeInfo.origin == "discord") {
embed.addField(
"Bridge Status",
bridgeInfo.revolt.messageId bridgeInfo.revolt.messageId
? 'Bridged' ? "Bridged"
: bridgeInfo.revolt.nonce : bridgeInfo.revolt.nonce
? 'ID unknown' ? "ID unknown"
: 'Unbridged', : "Unbridged",
true true
); );
} else { } else {
embed.addField( embed.addField(
'Bridge Status', "Bridge Status",
bridgeInfo.discord.messageId bridgeInfo.discord.messageId
? 'Bridged' ? "Bridged"
: 'Unbridged', : "Unbridged",
true true
); );
if (bridgeInfo.channels?.revolt) { if (bridgeInfo.channels?.revolt) {
const channel = await revoltClient.channels.get(bridgeInfo.channels.revolt); const channel = await revoltClient.channels.get(
const revoltMsg = await revoltFetchMessage(bridgeInfo.revolt.messageId, channel); bridgeInfo.channels.revolt
);
const revoltMsg = await revoltFetchMessage(
bridgeInfo.revolt.messageId,
channel
);
if (revoltMsg) { if (revoltMsg) {
const author = await revoltFetchUser(revoltMsg.author_id); const author = await revoltFetchUser(
revoltMsg.author_id
);
embed.addField( embed.addField(
'Message Author', "Message Author",
`**@${author?.username}** (${revoltMsg.author_id})`, `**@${author?.username}** (${revoltMsg.author_id})`
); );
} }
} }
} }
embed.addField( embed.addField(
'Bridge Data', "Bridge Data",
`Origin: \`${bridgeInfo.origin}\`\n` + `Origin: \`${bridgeInfo.origin}\`\n` +
`Discord ID: \`${bridgeInfo.discord.messageId}\`\n` + `Discord ID: \`${bridgeInfo.discord.messageId}\`\n` +
`Revolt ID: \`${bridgeInfo.revolt.messageId}\`\n` + `Revolt ID: \`${bridgeInfo.revolt.messageId}\`\n` +
@ -326,13 +502,14 @@ client.on('interactionCreate', async interaction => {
return await interaction.reply({ return await interaction.reply({
ephemeral: true, ephemeral: true,
embeds: [ embed ], embeds: [embed],
}); });
} }
} }
} }
} catch(e) { } catch (e) {
console.error(e); console.error(e);
if (interaction.isCommand()) interaction.reply('An error has occurred: ' + e).catch(() => {}); if (interaction.isCommand())
interaction.reply("An error has occurred: " + e).catch(() => {});
} }
}); });

View file

@ -192,18 +192,24 @@ client.on('messageCreate', async message => {
//attachments: [], //attachments: [],
//embeds: [], //embeds: [],
nonce: nonce, nonce: nonce,
replies: reply ? [ { id: reply, mention: !!message.mentions.repliedUser } ] : undefined, replies: reply
? [{ id: reply, mention: !!message.mentions.repliedUser }]
: undefined,
masquerade: { masquerade: {
name: message.author.username, name: bridgeCfg.config?.bridge_nicknames
avatar: message.author.displayAvatarURL({ size: 128 }), ? message.member?.nickname ?? message.author.username
colour: channel.server?.havePermission('ManageRole') : message.author.username,
avatar: bridgeCfg.config?.bridge_nicknames
? message.member?.displayAvatarURL({ size: 128 })
: message.author.displayAvatarURL({ size: 128 }),
colour: channel.server?.havePermission("ManageRole")
? message.member?.displayColor // Discord.js returns black or 0 instead of undefined when no role color is set ? message.member?.displayColor // Discord.js returns black or 0 instead of undefined when no role color is set
? message.member?.displayHexColor ? message.member?.displayHexColor
: 'var(--foreground)' : "var(--foreground)"
: undefined, : undefined,
}, },
embeds: message.embeds.length embeds: message.embeds.length
? message.embeds.map(e => new GenericEmbed(e).toRevolt()) ? message.embeds.map((e) => new GenericEmbed(e).toRevolt())
: undefined, : undefined,
attachments: autumnUrls.length ? autumnUrls : undefined, attachments: autumnUrls.length ? autumnUrls : undefined,
}; };

View file

@ -150,16 +150,29 @@ client.on('message', async message => {
token: bridgeCfg.discordWebhook!.token, token: bridgeCfg.discordWebhook!.token,
}); });
const payload: MessagePayload|WebhookMessageOptions = { const payload: MessagePayload | WebhookMessageOptions = {
content: message.content ? await renderMessageBody(message.content) : undefined, content: message.content
username: message.author?.username ?? 'Unknown user', ? await renderMessageBody(message.content)
avatarURL: message.author?.generateAvatarURL({ max_side: 128 }), : undefined,
username:
(bridgeCfg.config?.bridge_nicknames
? message.masquerade?.name ??
message.member?.nickname ??
message.author?.username
: message.author?.username) ?? "Unknown user",
avatarURL: bridgeCfg.config?.bridge_nicknames
? message.masquerade?.avatar ??
message.member?.generateAvatarURL({ max_side: 128 }) ??
message.author?.generateAvatarURL({ max_side: 128 })
: message.author?.generateAvatarURL({ max_side: 128 }),
embeds: message.embeds?.length embeds: message.embeds?.length
? message.embeds ? message.embeds
.filter(e => e.type == "Text") .filter((e) => e.type == "Text")
.map(e => new GenericEmbed(e as SendableEmbed).toDiscord()) .map((e) =>
new GenericEmbed(e as SendableEmbed).toDiscord()
)
: undefined, : undefined,
allowedMentions: { parse: [ ] }, allowedMentions: { parse: [] },
}; };
if (repliedMessages.length) { if (repliedMessages.length) {

View file

@ -1,3 +1,5 @@
import { CONFIG_KEYS } from "./ConfigKeys";
export default class { export default class {
// Revolt channel ID // Revolt channel ID
revolt?: string; revolt?: string;
@ -9,7 +11,9 @@ export default class {
discordWebhook?: { discordWebhook?: {
id: string; id: string;
token: string; token: string;
} };
config?: { [key in keyof typeof CONFIG_KEYS]: boolean | undefined };
// If true, messages by users who have opted out of bridging will be deleted. // If true, messages by users who have opted out of bridging will be deleted.
disallowIfOptedOut?: boolean; disallowIfOptedOut?: boolean;

View file

@ -0,0 +1,13 @@
export const CONFIG_KEYS = {
bridge_nicknames: {
friendlyName: "Bridge Nicknames",
description:
"If enabled, nicknames and avatar overrides will be bridged.",
},
// disallow_opt_out: {
// friendlyName: "Disallow users who opted out of message bridging",
// description:
// "If enabled, all messages by users who opted out of their messages being bridged (`/bridge opt_out`) will be deleted. " +
// "You should enable this if your Revolt server is bridged to a mostly unmoderated Discord server.",
// },
};