Add experimental backfills script
This commit is contained in:
parent
d5a7b3256b
commit
ec1550bc97
4 changed files with 89 additions and 10 deletions
79
scripts/backfill.js
Normal file
79
scripts/backfill.js
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// @ts-check
|
||||||
|
|
||||||
|
console.log("-=- This script is experimental. It WILL mess up the room history on Matrix. -=-")
|
||||||
|
console.log()
|
||||||
|
|
||||||
|
const {channel: channelID} = require("minimist")(process.argv.slice(2), {string: ["channel"]})
|
||||||
|
if (!channelID) {
|
||||||
|
console.error("Usage: ./scripts/backfill.js --channel=<channel id here>")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const assert = require("assert/strict")
|
||||||
|
const sqlite = require("better-sqlite3")
|
||||||
|
const backfill = new sqlite("scripts/backfill.db")
|
||||||
|
backfill.prepare("CREATE TABLE IF NOT EXISTS backfill (channel_id TEXT NOT NULL, message_id INTEGER NOT NULL, PRIMARY KEY (channel_id, message_id))").run()
|
||||||
|
|
||||||
|
const HeatSync = require("heatsync")
|
||||||
|
|
||||||
|
const {reg} = require("../src/matrix/read-registration")
|
||||||
|
const passthrough = require("../src/passthrough")
|
||||||
|
|
||||||
|
const sync = new HeatSync({watchFS: false})
|
||||||
|
const db = new sqlite("ooye.db")
|
||||||
|
Object.assign(passthrough, {sync, db})
|
||||||
|
|
||||||
|
const DiscordClient = require("../src/d2m/discord-client")
|
||||||
|
|
||||||
|
const discord = new DiscordClient(reg.ooye.discord_token, "half")
|
||||||
|
passthrough.discord = discord
|
||||||
|
|
||||||
|
const orm = sync.require("../src/db/orm")
|
||||||
|
passthrough.from = orm.from
|
||||||
|
passthrough.select = orm.select
|
||||||
|
|
||||||
|
/** @type {import("../src/d2m/event-dispatcher")}*/
|
||||||
|
const eventDispatcher = sync.require("../src/d2m/event-dispatcher")
|
||||||
|
|
||||||
|
const roomID = passthrough.select("channel_room", "room_id", {channel_id: channelID}).pluck().get()
|
||||||
|
if (!roomID) {
|
||||||
|
console.error("Please choose a channel that's already bridged.")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
await discord.cloud.connect()
|
||||||
|
console.log("Connected, waiting for data about requested channel...")
|
||||||
|
|
||||||
|
discord.cloud.on("event", event)
|
||||||
|
})()
|
||||||
|
|
||||||
|
const preparedInsert = backfill.prepare("INSERT INTO backfill (channel_id, message_id) VALUES (?, ?)")
|
||||||
|
|
||||||
|
async function event(event) {
|
||||||
|
if (event.t !== "GUILD_CREATE") return
|
||||||
|
const channel = event.d.channels.find(c => c.id === channelID)
|
||||||
|
if (!channel) return
|
||||||
|
const guild_id = event.d.id
|
||||||
|
|
||||||
|
let last = backfill.prepare("SELECT cast(max(message_id) as TEXT) FROM backfill WHERE channel_id = ?").pluck().get(channelID) || "0"
|
||||||
|
console.log(`OK, processing messages for #${channel.name}, continuing from ${last}`)
|
||||||
|
|
||||||
|
while (last) {
|
||||||
|
const messages = await discord.snow.channel.getChannelMessages(channelID, {limit: 50, after: String(last)})
|
||||||
|
messages.reverse() // More recent messages come first -> More recent messages come last
|
||||||
|
for (const message of messages) {
|
||||||
|
const simulatedGatewayDispatchData = {
|
||||||
|
guild_id,
|
||||||
|
backfill: true,
|
||||||
|
...message
|
||||||
|
}
|
||||||
|
await eventDispatcher.onMessageCreate(discord, simulatedGatewayDispatchData)
|
||||||
|
preparedInsert.run(channelID, message.id)
|
||||||
|
}
|
||||||
|
last = messages.at(-1)?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit()
|
||||||
|
}
|
|
@ -97,12 +97,12 @@ async function ensureSimJoined(user, roomID) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {DiscordTypes.APIUser} user
|
* @param {DiscordTypes.APIUser} user
|
||||||
* @param {Omit<DiscordTypes.APIGuildMember, "user">} member
|
* @param {Omit<DiscordTypes.APIGuildMember, "user"> | undefined} member
|
||||||
*/
|
*/
|
||||||
async function memberToStateContent(user, member, guildID) {
|
async function memberToStateContent(user, member, guildID) {
|
||||||
let displayname = user.username
|
let displayname = user.username
|
||||||
if (user.global_name) displayname = user.global_name
|
if (user.global_name) displayname = user.global_name
|
||||||
if (member.nick) displayname = member.nick
|
if (member?.nick) displayname = member.nick
|
||||||
|
|
||||||
const content = {
|
const content = {
|
||||||
displayname,
|
displayname,
|
||||||
|
@ -117,7 +117,7 @@ async function memberToStateContent(user, member, guildID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (member.avatar || user.avatar) {
|
if (member?.avatar || user.avatar) {
|
||||||
// const avatarPath = file.userAvatar(user) // the user avatar only
|
// const avatarPath = file.userAvatar(user) // the user avatar only
|
||||||
const avatarPath = file.memberAvatar(guildID, user, member) // the member avatar or the user avatar
|
const avatarPath = file.memberAvatar(guildID, user, member) // the member avatar or the user avatar
|
||||||
content["moe.cadence.ooye.member"].avatar = avatarPath
|
content["moe.cadence.ooye.member"].avatar = avatarPath
|
||||||
|
@ -130,12 +130,14 @@ async function memberToStateContent(user, member, guildID) {
|
||||||
/**
|
/**
|
||||||
* https://gitdab.com/cadence/out-of-your-element/issues/9
|
* https://gitdab.com/cadence/out-of-your-element/issues/9
|
||||||
* @param {DiscordTypes.APIUser} user
|
* @param {DiscordTypes.APIUser} user
|
||||||
* @param {Omit<DiscordTypes.APIGuildMember, "user">} member
|
* @param {Omit<DiscordTypes.APIGuildMember, "user"> | undefined} member
|
||||||
* @param {DiscordTypes.APIGuild} guild
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
* @param {DiscordTypes.APIGuildChannel} channel
|
* @param {DiscordTypes.APIGuildChannel} channel
|
||||||
* @returns {number} 0 to 100
|
* @returns {number} 0 to 100
|
||||||
*/
|
*/
|
||||||
function memberToPowerLevel(user, member, guild, channel) {
|
function memberToPowerLevel(user, member, guild, channel) {
|
||||||
|
if (!member) return 0
|
||||||
|
|
||||||
const permissions = utils.getPermissions(member.roles, guild.roles, user.id, channel.permission_overwrites)
|
const permissions = utils.getPermissions(member.roles, guild.roles, user.id, channel.permission_overwrites)
|
||||||
/*
|
/*
|
||||||
* PL 100 = Administrator = People who can brick the room. RATIONALE:
|
* PL 100 = Administrator = People who can brick the room. RATIONALE:
|
||||||
|
@ -179,7 +181,7 @@ function _hashProfileContent(content, powerLevel) {
|
||||||
* 4. Compare against the previously known state content, which is helpfully stored in the database
|
* 4. Compare against the previously known state content, which is helpfully stored in the database
|
||||||
* 5. If the state content or power level have changed, send them to Matrix and update them in the database for next time
|
* 5. If the state content or power level have changed, send them to Matrix and update them in the database for next time
|
||||||
* @param {DiscordTypes.APIUser} user
|
* @param {DiscordTypes.APIUser} user
|
||||||
* @param {Omit<DiscordTypes.APIGuildMember, "user">} member
|
* @param {Omit<DiscordTypes.APIGuildMember, "user"> | undefined} member
|
||||||
* @param {DiscordTypes.APIGuildChannel} channel
|
* @param {DiscordTypes.APIGuildChannel} channel
|
||||||
* @param {DiscordTypes.APIGuild} guild
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
* @param {string} roomID
|
* @param {string} roomID
|
||||||
|
|
|
@ -31,10 +31,8 @@ async function sendMessage(message, channel, guild, row) {
|
||||||
if (!dUtils.isWebhookMessage(message)) {
|
if (!dUtils.isWebhookMessage(message)) {
|
||||||
if (message.author.id === discord.application.id) {
|
if (message.author.id === discord.application.id) {
|
||||||
// no need to sync the bot's own user
|
// no need to sync the bot's own user
|
||||||
} else if (message.member) { // available on a gateway message create event
|
} else {
|
||||||
senderMxid = await registerUser.syncUser(message.author, message.member, channel, guild, roomID)
|
senderMxid = await registerUser.syncUser(message.author, message.member, channel, guild, roomID)
|
||||||
} else { // well, good enough...
|
|
||||||
senderMxid = await registerUser.ensureSimJoined(message.author, roomID)
|
|
||||||
}
|
}
|
||||||
} else if (row && row.speedbump_webhook_id === message.webhook_id) {
|
} else if (row && row.speedbump_webhook_id === message.webhook_id) {
|
||||||
// Handle the PluralKit public instance
|
// Handle the PluralKit public instance
|
||||||
|
|
|
@ -98,8 +98,8 @@ function userAvatar(user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function memberAvatar(guildID, user, member) {
|
function memberAvatar(guildID, user, member) {
|
||||||
if (!member.avatar) return userAvatar(user)
|
if (!member?.avatar) return userAvatar(user)
|
||||||
return `/guilds/${guildID}/users/${user.id}/avatars/${member.avatar}.png?size=${IMAGE_SIZE}`
|
return `/guilds/${guildID}/users/${user.id}/avatars/${member?.avatar}.png?size=${IMAGE_SIZE}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function emoji(emojiID, animated) {
|
function emoji(emojiID, animated) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue