allow discord users to opt out of the bridge

This commit is contained in:
JandereDev 2022-05-26 19:08:19 +02:00
parent 800b9ed492
commit 8acd65159d
No known key found for this signature in database
GPG key ID: 5D5E18ACB990F57A
6 changed files with 100 additions and 7 deletions

View file

@ -3,7 +3,7 @@
import { client } from "./client"; import { client } from "./client";
import { REST } from '@discordjs/rest'; import { REST } from '@discordjs/rest';
import { Routes } from 'discord-api-types/v9'; import { Routes } from 'discord-api-types/v9';
import { BRIDGED_MESSAGES, BRIDGE_CONFIG, BRIDGE_REQUESTS, logger } from ".."; import { BRIDGED_MESSAGES, BRIDGE_CONFIG, BRIDGE_REQUESTS, BRIDGE_USER_CONFIG, logger } from "..";
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";
@ -36,7 +36,20 @@ const COMMANDS: any[] = [
name: 'help', name: 'help',
description: 'Usage instructions', description: 'Usage instructions',
type: 1, type: 1,
} },
{
name: 'opt_out',
description: 'Opt out of having your messages bridged',
type: 1,
options: [
{
name: 'opt_out',
description: 'Whether you wish to opt out of having your messages bridged',
optional: true,
type: 5 // Boolean
},
],
},
], ],
}, },
{ {
@ -168,6 +181,46 @@ client.on('interactionCreate', async interaction => {
await interaction.reply({ embeds: [ embed ], ephemeral: true }); await interaction.reply({ embeds: [ embed ], ephemeral: true });
break; break;
case 'opt_out':
const optOut = interaction.options.getBoolean('opt_out', false);
if (optOut == null) {
const userConfig = await BRIDGE_USER_CONFIG.findOne({ id: interaction.user.id });
if (userConfig?.optOut) {
return await interaction.reply({
ephemeral: true,
content: 'You are currently **opted out** of message bridging. ' +
'Users on Revolt **will not** see your username, avatar or message content.'
});
} else {
return await interaction.reply({
ephemeral: true,
content: '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 {
await BRIDGE_USER_CONFIG.update(
{ id: interaction.user.id },
{
$setOnInsert: { id: interaction.user.id },
$set: { optOut },
},
{ upsert: true }
);
return await interaction.reply({
ephemeral: true,
content: `You have **opted ${optOut ? 'out of' : 'into'}** message bridging. `
+ (
optOut
? '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;
default: await interaction.reply('Unknown subcommand'); default: await interaction.reply('Unknown subcommand');
} }

View file

@ -1,4 +1,4 @@
import { BRIDGED_MESSAGES, BRIDGE_CONFIG, logger } from ".."; import { BRIDGED_MESSAGES, BRIDGE_CONFIG, BRIDGE_USER_CONFIG, logger } from "..";
import { client } from "./client"; import { client } from "./client";
import { AUTUMN_URL, client as revoltClient } from "../revolt/client"; import { AUTUMN_URL, client as revoltClient } from "../revolt/client";
import axios from 'axios'; import axios from 'axios';
@ -27,6 +27,7 @@ client.on('messageDelete', async message => {
]); ]);
if (!bridgedMsg?.revolt) return logger.debug(`Discord: Message has not been bridged; ignoring deletion`); if (!bridgedMsg?.revolt) return logger.debug(`Discord: Message has not been bridged; ignoring deletion`);
if (!bridgedMsg.ignore) return logger.debug(`Discord: Message marked as ignore`);
if (!bridgeCfg?.revolt) return logger.debug(`Discord: No Revolt channel associated`); if (!bridgeCfg?.revolt) return logger.debug(`Discord: No Revolt channel associated`);
const targetMsg = await revoltFetchMessage(bridgedMsg.revolt.messageId, revoltClient.channels.get(bridgeCfg.revolt)); const targetMsg = await revoltFetchMessage(bridgedMsg.revolt.messageId, revoltClient.channels.get(bridgeCfg.revolt));
@ -51,6 +52,7 @@ client.on('messageUpdate', async (oldMsg, newMsg) => {
]); ]);
if (!bridgedMsg) return logger.debug(`Discord: Message has not been bridged; ignoring edit`); if (!bridgedMsg) return logger.debug(`Discord: Message has not been bridged; ignoring edit`);
if (!bridgedMsg.ignore) return logger.debug(`Discord: Message marked as ignore`);
if (!bridgeCfg?.revolt) return logger.debug(`Discord: No Revolt channel associated`); if (!bridgeCfg?.revolt) return logger.debug(`Discord: No Revolt channel associated`);
if (newMsg.webhookId && newMsg.webhookId == bridgeCfg.discordWebhook?.id) { if (newMsg.webhookId && newMsg.webhookId == bridgeCfg.discordWebhook?.id) {
return logger.debug(`Discord: Message was sent by bridge; ignoring edit`); return logger.debug(`Discord: Message was sent by bridge; ignoring edit`);
@ -69,12 +71,13 @@ client.on('messageUpdate', async (oldMsg, newMsg) => {
client.on('messageCreate', async message => { client.on('messageCreate', async message => {
try { try {
logger.debug(`[M] Discord: ${message.content}`); logger.debug(`[M] Discord: ${message.content}`);
const [ bridgeCfg, bridgedReply ] = await Promise.all([ const [ bridgeCfg, bridgedReply, userConfig ] = await Promise.all([
BRIDGE_CONFIG.findOne({ discord: message.channelId }), BRIDGE_CONFIG.findOne({ discord: message.channelId }),
(message.reference?.messageId (message.reference?.messageId
? BRIDGED_MESSAGES.findOne({ "discord.messageId": message.reference.messageId }) ? BRIDGED_MESSAGES.findOne({ "discord.messageId": message.reference.messageId })
: undefined : undefined
), ),
BRIDGE_USER_CONFIG.findOne({ id: message.author.id }),
]); ]);
if (message.webhookId && bridgeCfg?.discordWebhook?.id == message.webhookId) { if (message.webhookId && bridgeCfg?.discordWebhook?.id == message.webhookId) {
@ -98,6 +101,11 @@ client.on('messageCreate', async message => {
} }
} }
if (bridgeCfg.disallowIfOptedOut && userConfig?.optOut && message.deletable) {
await message.delete();
return;
}
// Setting a known nonce allows us to ignore bridged // Setting a known nonce allows us to ignore bridged
// messages while still letting other AutoMod messages pass. // messages while still letting other AutoMod messages pass.
const nonce = ulid(); const nonce = ulid();
@ -105,7 +113,7 @@ client.on('messageCreate', async message => {
await BRIDGED_MESSAGES.update( await BRIDGED_MESSAGES.update(
{ "discord.messageId": message.id }, { "discord.messageId": message.id },
{ {
$setOnInsert: { $setOnInsert: userConfig?.optOut ? {} : {
origin: 'discord', origin: 'discord',
discord: { discord: {
messageId: message.id, messageId: message.id,
@ -116,12 +124,32 @@ client.on('messageCreate', async message => {
channels: { channels: {
discord: message.channelId, discord: message.channelId,
revolt: bridgeCfg.revolt, revolt: bridgeCfg.revolt,
} },
ignore: userConfig?.optOut,
} }
}, },
{ upsert: true } { upsert: true }
); );
if (userConfig?.optOut) {
const msg = await channel.sendMessage({
content: `$\\color{#565656}\\small{\\textsf{Message content redacted}}$`,
masquerade: {
name: 'AutoMod Bridge',
},
nonce: nonce,
});
await BRIDGED_MESSAGES.update(
{ "discord.messageId": message.id },
{
$set: { "revolt.messageId": msg._id },
}
);
return;
}
const autumnUrls: string[] = []; const autumnUrls: string[] = [];
// todo: upload all attachments at once instead of sequentially // todo: upload all attachments at once instead of sequentially

View file

@ -8,6 +8,7 @@ import BridgeConfig from './types/BridgeConfig';
import BridgedMessage from './types/BridgedMessage'; import BridgedMessage from './types/BridgedMessage';
import BridgeRequest from './types/BridgeRequest'; import BridgeRequest from './types/BridgeRequest';
import DiscordBridgedEmoji from './types/DiscordBridgedEmoji'; import DiscordBridgedEmoji from './types/DiscordBridgedEmoji';
import BridgeUserConfig from './types/BridgeUserConfig';
config(); config();
@ -17,6 +18,7 @@ const BRIDGED_MESSAGES: ICollection<BridgedMessage> = db.get('bridged_messages')
const BRIDGE_CONFIG: ICollection<BridgeConfig> = db.get('bridge_config'); const BRIDGE_CONFIG: ICollection<BridgeConfig> = db.get('bridge_config');
const BRIDGE_REQUESTS: ICollection<BridgeRequest> = db.get('bridge_requests'); const BRIDGE_REQUESTS: ICollection<BridgeRequest> = db.get('bridge_requests');
const BRIDGED_EMOJIS: ICollection<DiscordBridgedEmoji> = db.get('bridged_emojis'); const BRIDGED_EMOJIS: ICollection<DiscordBridgedEmoji> = db.get('bridged_emojis');
const BRIDGE_USER_CONFIG: ICollection<BridgeUserConfig> = db.get('bridge_user_config');
for (const v of [ 'REVOLT_TOKEN', 'DISCORD_TOKEN', 'DB_STRING' ]) { for (const v of [ 'REVOLT_TOKEN', 'DISCORD_TOKEN', 'DB_STRING' ]) {
if (!process.env[v]) { if (!process.env[v]) {
@ -33,4 +35,4 @@ for (const v of [ 'REVOLT_TOKEN', 'DISCORD_TOKEN', 'DB_STRING' ]) {
]); ]);
})(); })();
export { logger, db, BRIDGED_MESSAGES, BRIDGE_CONFIG, BRIDGE_REQUESTS, BRIDGED_EMOJIS } export { logger, db, BRIDGED_MESSAGES, BRIDGE_CONFIG, BRIDGE_REQUESTS, BRIDGED_EMOJIS, BRIDGE_USER_CONFIG }

View file

@ -10,4 +10,7 @@ export default class {
id: string; id: string;
token: string; token: string;
} }
// If true, messages by users who have opted out of bridging will be deleted.
disallowIfOptedOut?: boolean;
} }

View file

@ -0,0 +1,5 @@
export default class {
platform: 'discord'; // Todo: Revolt users too?
id: string;
optOut?: boolean;
}

View file

@ -15,4 +15,6 @@ export default class {
discord: string; discord: string;
revolt: string; revolt: string;
} }
ignore?: boolean;
} }