75 lines
2.7 KiB
TypeScript
75 lines
2.7 KiB
TypeScript
/**
|
|
* Prepares the chat's `@everyone`: it adds `chat_messages.mentions_everyone`,
|
|
* and frees the name `everyone`, which no account may have any more.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import type { DatabaseSync } from "node:sqlite";
|
|
import { EVERYONE, mentionsEveryone } from "../../../shared/mention.ts";
|
|
import { ROLE_PERMISSIONS } from "../../../shared/permissions.ts";
|
|
|
|
/**
|
|
* Run once by `runMigrations`. The messages that a moderator or an admin
|
|
* already sent with `@everyone` get the flag, so that editing one later
|
|
* doesn't notify everyone of an old message; none is notified now. A user
|
|
* named `everyone`, in any case, is renamed to the first free `everyone_2`,
|
|
* `everyone_3`…, with no alias left behind, and an alias `everyone` is
|
|
* dropped. Both are logged.
|
|
*/
|
|
export function up(db: DatabaseSync): void {
|
|
const cols = db.prepare(`PRAGMA table_info(chat_messages);`).all() as {
|
|
name: string;
|
|
}[];
|
|
if (!cols.some((c) => c.name === "mentions_everyone")) {
|
|
db.exec(
|
|
`ALTER TABLE chat_messages ADD COLUMN mentions_everyone INTEGER NOT NULL DEFAULT 0;`,
|
|
);
|
|
}
|
|
|
|
const roles = Object.entries(ROLE_PERMISSIONS)
|
|
.filter(([, perms]) => perms.includes("chat:everyone"))
|
|
.map(([role]) => role);
|
|
const messages = db.prepare(
|
|
`SELECT m.id, m.body FROM chat_messages m JOIN users u ON u.id = m.user_id
|
|
WHERE m.mentions_everyone = 0
|
|
AND u.role IN (${roles.map(() => "?").join(", ")})
|
|
AND m.body LIKE '%@%';`,
|
|
).all(...roles) as { id: string; body: string }[];
|
|
const flag = db.prepare(
|
|
`UPDATE chat_messages SET mentions_everyone = 1 WHERE id = ?;`,
|
|
);
|
|
for (const m of messages) {
|
|
if (mentionsEveryone(m.body)) flag.run(m.id);
|
|
}
|
|
|
|
const taken = (name: string) =>
|
|
!!db.prepare(
|
|
`SELECT 1 FROM users WHERE lower(username) = lower(?)
|
|
UNION ALL SELECT 1 FROM user_aliases WHERE lower(username) = lower(?);`,
|
|
).get(name, name);
|
|
const user = db.prepare(
|
|
`SELECT id, username FROM users WHERE lower(username) = ?;`,
|
|
).get(EVERYONE) as { id: string; username: string } | undefined;
|
|
if (user) {
|
|
let n = 2;
|
|
while (taken(`${EVERYONE}_${n}`)) n++;
|
|
const renamed = `${EVERYONE}_${n}`;
|
|
db.prepare(`UPDATE users SET username = ? WHERE id = ?;`).run(
|
|
renamed,
|
|
user.id,
|
|
);
|
|
console.log(
|
|
`[migrate] user "${user.username}" is now "${renamed}": @${EVERYONE} is reserved`,
|
|
);
|
|
}
|
|
const alias = db.prepare(
|
|
`DELETE FROM user_aliases WHERE lower(username) = ? RETURNING user_id;`,
|
|
).get(EVERYONE) as { user_id: string } | undefined;
|
|
if (alias) {
|
|
console.log(
|
|
`[migrate] the former name "${EVERYONE}" of user ${alias.user_id} is dropped: @${EVERYONE} is reserved`,
|
|
);
|
|
}
|
|
}
|