allow discord users to opt out of the bridge
This commit is contained in:
parent
800b9ed492
commit
8acd65159d
6 changed files with 100 additions and 7 deletions
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
5
bridge/src/types/BridgeUserConfig.ts
Normal file
5
bridge/src/types/BridgeUserConfig.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export default class {
|
||||||
|
platform: 'discord'; // Todo: Revolt users too?
|
||||||
|
id: string;
|
||||||
|
optOut?: boolean;
|
||||||
|
}
|
|
@ -15,4 +15,6 @@ export default class {
|
||||||
discord: string;
|
discord: string;
|
||||||
revolt: string;
|
revolt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignore?: boolean;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue