Implement the speedbump
This commit is contained in:
parent
5ef5dbb2e8
commit
e49dc18e67
9 changed files with 90 additions and 13 deletions
|
@ -4,21 +4,25 @@ const passthrough = require("../../passthrough")
|
||||||
const {sync, db, select, from} = passthrough
|
const {sync, db, select, from} = passthrough
|
||||||
/** @type {import("../../matrix/api")} */
|
/** @type {import("../../matrix/api")} */
|
||||||
const api = sync.require("../../matrix/api")
|
const api = sync.require("../../matrix/api")
|
||||||
|
/** @type {import("./speedbump")} */
|
||||||
|
const speedbump = sync.require("./speedbump")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("discord-api-types/v10").GatewayMessageDeleteDispatchData} data
|
* @param {import("discord-api-types/v10").GatewayMessageDeleteDispatchData} data
|
||||||
*/
|
*/
|
||||||
async function deleteMessage(data) {
|
async function deleteMessage(data) {
|
||||||
const roomID = select("channel_room", "room_id", {channel_id: data.channel_id}).pluck().get()
|
const row = select("channel_room", ["room_id", "speedbump_id", "speedbump_checked"], {channel_id: data.channel_id}).get()
|
||||||
if (!roomID) return
|
if (!row) return
|
||||||
|
|
||||||
const eventsToRedact = select("event_message", "event_id", {message_id: data.id}).pluck().all()
|
const eventsToRedact = select("event_message", "event_id", {message_id: data.id}).pluck().all()
|
||||||
db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(data.id)
|
db.prepare("DELETE FROM message_channel WHERE message_id = ?").run(data.id)
|
||||||
db.prepare("DELETE FROM event_message WHERE message_id = ?").run(data.id)
|
db.prepare("DELETE FROM event_message WHERE message_id = ?").run(data.id)
|
||||||
for (const eventID of eventsToRedact) {
|
for (const eventID of eventsToRedact) {
|
||||||
// Unfortunately, we can't specify a sender to do the redaction as, unless we find out that info via the audit logs
|
// Unfortunately, we can't specify a sender to do the redaction as, unless we find out that info via the audit logs
|
||||||
await api.redactEvent(roomID, eventID)
|
await api.redactEvent(row.room_id, eventID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speedbump.updateCache(data.channel_id, row.speedbump_id, row.speedbump_checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
51
d2m/actions/speedbump.js
Normal file
51
d2m/actions/speedbump.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
const DiscordTypes = require("discord-api-types/v10")
|
||||||
|
const passthrough = require("../../passthrough")
|
||||||
|
const {discord, db} = passthrough
|
||||||
|
|
||||||
|
const SPEEDBUMP_SPEED = 4000 // 4 seconds delay
|
||||||
|
const SPEEDBUMP_UPDATE_FREQUENCY = 2 * 60 * 60 // 2 hours
|
||||||
|
|
||||||
|
/** @type {Set<any>} */
|
||||||
|
const KNOWN_BOTS = new Set([
|
||||||
|
"466378653216014359" // PluralKit
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch new speedbump data for the channel and put it in the database as cache
|
||||||
|
* @param {string} channelID
|
||||||
|
* @param {string?} speedbumpID
|
||||||
|
* @param {number?} speedbumpChecked
|
||||||
|
*/
|
||||||
|
async function updateCache(channelID, speedbumpID, speedbumpChecked) {
|
||||||
|
const now = Math.floor(Date.now() / 1000)
|
||||||
|
if (speedbumpChecked && now - speedbumpChecked < SPEEDBUMP_UPDATE_FREQUENCY) return
|
||||||
|
const webhooks = await discord.snow.webhook.getChannelWebhooks(channelID)
|
||||||
|
const found = webhooks.find(b => KNOWN_BOTS.has(b.application_id))?.application_id || null
|
||||||
|
db.prepare("UPDATE channel_room SET speedbump_id = ?, speedbump_checked = ? WHERE channel_id = ?").run(found, now, channelID)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {Set<string>} set of messageID */
|
||||||
|
const bumping = new Set()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slow down a message. After it passes the speedbump, return whether it's okay or if it's been deleted.
|
||||||
|
* @param {string} messageID
|
||||||
|
*/
|
||||||
|
async function doSpeedbump(messageID) {
|
||||||
|
bumping.add(messageID)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, SPEEDBUMP_SPEED))
|
||||||
|
return !bumping.delete(messageID)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} messageID
|
||||||
|
*/
|
||||||
|
function onMessageDelete(messageID) {
|
||||||
|
bumping.delete(messageID)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.updateCache = updateCache
|
||||||
|
module.exports.doSpeedbump = doSpeedbump
|
||||||
|
module.exports.onMessageDelete = onMessageDelete
|
|
@ -47,7 +47,16 @@ class DiscordClient {
|
||||||
if (listen !== "no") {
|
if (listen !== "no") {
|
||||||
this.cloud.on("event", message => discordPackets.onPacket(this, message, listen))
|
this.cloud.on("event", message => discordPackets.onPacket(this, message, listen))
|
||||||
}
|
}
|
||||||
this.cloud.on("error", console.error)
|
|
||||||
|
const addEventLogger = (eventName, logName) => {
|
||||||
|
this.cloud.on(eventName, (...args) => {
|
||||||
|
const d = new Date().toISOString().slice(0, 19)
|
||||||
|
console.error(`[${d} Client ${logName}]`, ...args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
addEventLogger("error", "Error")
|
||||||
|
addEventLogger("disconnected", "Disconnected")
|
||||||
|
addEventLogger("ready", "Ready")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ const dUtils = sync.require("../discord/utils")
|
||||||
const discordCommandHandler = sync.require("../discord/discord-command-handler")
|
const discordCommandHandler = sync.require("../discord/discord-command-handler")
|
||||||
/** @type {import("../m2d/converters/utils")} */
|
/** @type {import("../m2d/converters/utils")} */
|
||||||
const mxUtils = require("../m2d/converters/utils")
|
const mxUtils = require("../m2d/converters/utils")
|
||||||
|
/** @type {import("./actions/speedbump")} */
|
||||||
|
const speedbump = sync.require("./actions/speedbump")
|
||||||
|
|
||||||
/** @type {any} */ // @ts-ignore bad types from semaphore
|
/** @type {any} */ // @ts-ignore bad types from semaphore
|
||||||
const Semaphore = require("@chriscdn/promise-semaphore")
|
const Semaphore = require("@chriscdn/promise-semaphore")
|
||||||
|
@ -236,9 +238,12 @@ module.exports = {
|
||||||
if (message.author.username === "Deleted User") return // Nothing we can do for deleted users.
|
if (message.author.username === "Deleted User") return // Nothing we can do for deleted users.
|
||||||
if (message.webhook_id) {
|
if (message.webhook_id) {
|
||||||
const row = select("webhook", "webhook_id", {webhook_id: message.webhook_id}).pluck().get()
|
const row = select("webhook", "webhook_id", {webhook_id: message.webhook_id}).pluck().get()
|
||||||
if (row) {
|
if (row) return // The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
|
||||||
// The message was sent by the bridge's own webhook on discord. We don't want to reflect this back, so just drop it.
|
} else {
|
||||||
return
|
const speedbumpID = select("channel_room", "speedbump_id", {channel_id: message.channel_id}).pluck().get()
|
||||||
|
if (speedbumpID) {
|
||||||
|
const affected = await speedbump.doSpeedbump(message.id)
|
||||||
|
if (affected) return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const channel = client.channels.get(message.channel_id)
|
const channel = client.channels.get(message.channel_id)
|
||||||
|
@ -299,6 +304,7 @@ module.exports = {
|
||||||
* @param {DiscordTypes.GatewayMessageDeleteDispatchData} data
|
* @param {DiscordTypes.GatewayMessageDeleteDispatchData} data
|
||||||
*/
|
*/
|
||||||
async onMessageDelete(client, data) {
|
async onMessageDelete(client, data) {
|
||||||
|
speedbump.onMessageDelete(data.id)
|
||||||
await deleteMessage.deleteMessage(data)
|
await deleteMessage.deleteMessage(data)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
6
db/migrations/0009-add-speedbump-id.sql
Normal file
6
db/migrations/0009-add-speedbump-id.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE channel_room ADD COLUMN speedbump_id TEXT;
|
||||||
|
ALTER TABLE channel_room ADD COLUMN speedbump_checked INTEGER;
|
||||||
|
|
||||||
|
COMMIT;
|
2
db/orm-defs.d.ts
vendored
2
db/orm-defs.d.ts
vendored
|
@ -7,6 +7,8 @@ export type Models = {
|
||||||
thread_parent: string | null
|
thread_parent: string | null
|
||||||
custom_avatar: string | null
|
custom_avatar: string | null
|
||||||
last_bridged_pin_timestamp: number | null
|
last_bridged_pin_timestamp: number | null
|
||||||
|
speedbump_id: string | null
|
||||||
|
speedbump_checked: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
event_message: {
|
event_message: {
|
||||||
|
|
|
@ -85,7 +85,9 @@ OOYE's speedbump will prevent the edit command appearing at all on Matrix-side,
|
||||||
|
|
||||||
## Database schema
|
## Database schema
|
||||||
|
|
||||||
TBD
|
* channel_room
|
||||||
|
+ speedbump_id - the ID of the webhook that may be proxying in this channel
|
||||||
|
+ speedbump_checked - time in unix seconds when the webhooks were last queried
|
||||||
|
|
||||||
## Unsolved problems
|
## Unsolved problems
|
||||||
|
|
||||||
|
|
|
@ -581,10 +581,6 @@ test("event2message: ordered list start attribute works", async t => {
|
||||||
room_id: '!cBxtVRxDlZvSVhJXVK:cadence.moe',
|
room_id: '!cBxtVRxDlZvSVhJXVK:cadence.moe',
|
||||||
sender: '@Milan:tchncs.de',
|
sender: '@Milan:tchncs.de',
|
||||||
type: 'm.room.message',
|
type: 'm.room.message',
|
||||||
}, {}, {
|
|
||||||
api: {
|
|
||||||
getStateEvent: async () => ({displayname: "Milan"})
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
ensureJoined: [],
|
ensureJoined: [],
|
||||||
|
@ -2194,7 +2190,7 @@ test("event2message: mentioning events falls back to original link when the chan
|
||||||
}
|
}
|
||||||
}, {}, {
|
}, {}, {
|
||||||
api: {
|
api: {
|
||||||
/* c8 skip next 3 */
|
/* c8 ignore next 3 */
|
||||||
async getEvent() {
|
async getEvent() {
|
||||||
t.fail("getEvent should not be called because it should quit early due to no channel-guild")
|
t.fail("getEvent should not be called because it should quit early due to no channel-guild")
|
||||||
}
|
}
|
||||||
|
|
1
stdin.js
1
stdin.js
|
@ -17,6 +17,7 @@ const file = sync.require("./matrix/file")
|
||||||
const sendEvent = sync.require("./m2d/actions/send-event")
|
const sendEvent = sync.require("./m2d/actions/send-event")
|
||||||
const eventDispatcher = sync.require("./d2m/event-dispatcher")
|
const eventDispatcher = sync.require("./d2m/event-dispatcher")
|
||||||
const updatePins = sync.require("./d2m/actions/update-pins")
|
const updatePins = sync.require("./d2m/actions/update-pins")
|
||||||
|
const speedbump = sync.require("./d2m/actions/speedbump")
|
||||||
const ks = sync.require("./matrix/kstate")
|
const ks = sync.require("./matrix/kstate")
|
||||||
const guildID = "112760669178241024"
|
const guildID = "112760669178241024"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue