[WIP] threads. #74
14 changed files with 238 additions and 22 deletions
another conflict nooooo
commit
6586188fa3
|
|
@ -916,7 +916,7 @@ async function messageToEvent(message, guild, options = {}, di) {
|
||||||
// Add time
|
// Add time
|
||||||
if (event.scheduled_end_time) {
|
if (event.scheduled_end_time) {
|
||||||
const formatter = new Intl.DateTimeFormat("en-NZ", {weekday: "long", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZone: "UTC"}) // 9 June at 3:00 pm NZT
|
const formatter = new Intl.DateTimeFormat("en-NZ", {weekday: "long", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZone: "UTC"}) // 9 June at 3:00 pm NZT
|
||||||
var displayTime = formatter.formatRange(new Date(event.scheduled_start_time), new Date(event.scheduled_end_time)) + " (UTC)"
|
var displayTime = (formatter.formatRange(new Date(event.scheduled_start_time), new Date(event.scheduled_end_time)) + " (UTC)").replaceAll(/\p{Zs}/gv, " ")
|
||||||
} else {
|
} else {
|
||||||
var displayTime = discordTime.convertDiscordTime(unixSeconds, "F")
|
var displayTime = discordTime.convertDiscordTime(unixSeconds, "F")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1589,12 +1589,12 @@ test("message2event: irl invite event renders embed", async t => {
|
||||||
{
|
{
|
||||||
$type: "m.room.message",
|
$type: "m.room.message",
|
||||||
msgtype: "m.notice",
|
msgtype: "m.notice",
|
||||||
body: `| Scheduled Event - Sunday, 8 June, 10:00 am – 12:00 pm (UTC)`
|
body: `| Scheduled Event - Sunday, 8 June, 10:00 am – 12:00 pm (UTC)`
|
||||||
+ `\n| ## forest exploration`
|
+ `\n| ## forest exploration`
|
||||||
+ `\n| `
|
+ `\n| `
|
||||||
+ `\n| 📍 the dark forest`,
|
+ `\n| 📍 the dark forest`,
|
||||||
format: "org.matrix.custom.html",
|
format: "org.matrix.custom.html",
|
||||||
formatted_body: `<blockquote><p>Scheduled Event - <a href=\"https://bridge.example.org/time/1749376800/F\">Sunday, 8 June, 10:00 am – 12:00 pm (UTC)</a></p>`
|
formatted_body: `<blockquote><p>Scheduled Event - <a href=\"https://bridge.example.org/time/1749376800/F\">Sunday, 8 June, 10:00 am – 12:00 pm (UTC)</a></p>`
|
||||||
+ `<strong>forest exploration</strong>`
|
+ `<strong>forest exploration</strong>`
|
||||||
+ `<p>📍 the dark forest</p></blockquote>`,
|
+ `<p>📍 the dark forest</p></blockquote>`,
|
||||||
"m.mentions": {}
|
"m.mentions": {}
|
||||||
|
|
|
||||||
5
src/db/migrations/0041-room-mention-role.sql
Normal file
5
src/db/migrations/0041-room-mention-role.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE channel_room ADD COLUMN room_mention_role TEXT;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
1
src/db/orm-defs.d.ts
vendored
1
src/db/orm-defs.d.ts
vendored
|
|
@ -21,6 +21,7 @@ export type Models = {
|
||||||
speedbump_checked: number | null
|
speedbump_checked: number | null
|
||||||
guild_id: string | null
|
guild_id: string | null
|
||||||
custom_topic: number
|
custom_topic: number
|
||||||
|
room_mention_role: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_speedbump: {
|
channel_speedbump: {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ function getFormatOptions(style) {
|
||||||
function convertDiscordTime(timestamp, style) {
|
function convertDiscordTime(timestamp, style) {
|
||||||
const formatter = new Intl.DateTimeFormat("en-NZ", getFormatOptions(style))
|
const formatter = new Intl.DateTimeFormat("en-NZ", getFormatOptions(style))
|
||||||
const d = new Date(timestamp * 1000)
|
const d = new Date(timestamp * 1000)
|
||||||
return formatter.format(d) + " (UTC)"
|
return (formatter.format(d) + " (UTC)").replaceAll(/\p{Zs}/gv, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.getFormatOptions = getFormatOptions
|
module.exports.getFormatOptions = getFormatOptions
|
||||||
|
|
|
||||||
|
|
@ -452,23 +452,24 @@ async function handleRoomOrMessageLinks(input, di) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} content
|
* @param {string} content
|
||||||
* @param {string} senderMxid
|
* @param {{content: any, room_id: string, sender: string}} event
|
||||||
* @param {string} roomID
|
|
||||||
* @param {DiscordTypes.APIGuild} guild
|
* @param {DiscordTypes.APIGuild} guild
|
||||||
* @param {{api: import("../../matrix/api"), snow: import("snowtransfer").SnowTransfer}} di
|
* @param {{api: import("../../matrix/api"), snow: import("snowtransfer").SnowTransfer}} di
|
||||||
*/
|
*/
|
||||||
async function checkWrittenMentions(content, senderMxid, roomID, guild, di) {
|
async function checkWrittenMentions(content, event, guild, di) {
|
||||||
let writtenMentionMatch = content.match(/(?:^|[^"[<>/A-Za-z0-9])@([A-Za-z][A-Za-z0-9._\[\]\(\)-]+):?/d) // /d flag for indices requires node.js 16+
|
let writtenMentionMatch = content.match(/(?:^|[^"[<>/A-Za-z0-9])@([A-Za-z][A-Za-z0-9._\[\]\(\)-]+):?/d) // /d flag for indices requires node.js 16+
|
||||||
if (writtenMentionMatch) {
|
if (writtenMentionMatch) {
|
||||||
if (writtenMentionMatch[1] === "room") { // convert @room to @everyone
|
if (writtenMentionMatch[1] === "room") { // convert @room to discord
|
||||||
const {powers: {[senderMxid]: userPower}, powerLevels} = await mxUtils.getEffectivePower(roomID, [senderMxid], di.api)
|
if (("m.mentions" in event.content) && !event.content["m.mentions"].room) return // check it's allowed by m.mentions if present
|
||||||
|
const {powers: {[event.sender]: userPower}, powerLevels} = await mxUtils.getEffectivePower(event.room_id, [event.sender], di.api)
|
||||||
if (userPower >= (powerLevels.notifications?.room ?? 50)) {
|
if (userPower >= (powerLevels.notifications?.room ?? 50)) {
|
||||||
|
const roleID = select("channel_room", "room_mention_role", {room_id: event.room_id}).pluck().get()
|
||||||
return {
|
return {
|
||||||
// @ts-ignore - typescript doesn't know about indices yet
|
// @ts-ignore - typescript doesn't know about indices yet
|
||||||
content: content.slice(0, writtenMentionMatch.indices[1][0]-1) + `@everyone` + content.slice(writtenMentionMatch.indices[1][1]),
|
content: content.slice(0, writtenMentionMatch.indices[1][0]-1) + (roleID ? `<@&${roleID}>` : "@everyone") + content.slice(writtenMentionMatch.indices[1][1]),
|
||||||
ensureJoined: [],
|
ensureJoined: [],
|
||||||
/**@type {DiscordTypes.AllowedMentionsTypes[]}*/ // @ts-ignore - TypeScript is for whatever reason conviced that "everyone" cannot be assigned to AllowedMentionsTypes, but if you „Go to Definition”, you'll see that "everyone" is a valid enum value.
|
/**@type {DiscordTypes.AllowedMentionsTypes[]}*/ // @ts-ignore - TypeScript is for whatever reason convinced that "everyone" cannot be assigned to AllowedMentionsTypes, but if you „Go to Definition”, you'll see that "everyone" is a valid enum value.
|
||||||
allowedMentionsParse: ["everyone"],
|
allowedMentionsParse: roleID ? [] : ["everyone"],
|
||||||
allowedMentionsUsers: []
|
allowedMentionsUsers: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -862,6 +863,8 @@ async function eventToMessage(event, guild, channel, di) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
input = input.replace(/<a\s[^>]*href="https:\/\/matrix\.to\/#\/(?:!|%21|#|%23)[^"\/]*"[^>]*>@room<\/a>/g, "@room")
|
||||||
|
|
||||||
// Handling mentions of rooms and room-messages
|
// Handling mentions of rooms and room-messages
|
||||||
input = await handleRoomOrMessageLinks(input, di)
|
input = await handleRoomOrMessageLinks(input, di)
|
||||||
|
|
||||||
|
|
@ -913,7 +916,7 @@ async function eventToMessage(event, guild, channel, di) {
|
||||||
|
|
||||||
// Check written mentions
|
// Check written mentions
|
||||||
if (node.nodeType === 3 && node.nodeValue.includes("@") && !nodeIsChildOf(node, ["A", "CODE", "PRE"])) {
|
if (node.nodeType === 3 && node.nodeValue.includes("@") && !nodeIsChildOf(node, ["A", "CODE", "PRE"])) {
|
||||||
const result = await checkWrittenMentions(node.nodeValue, event.sender, event.room_id, guild, di)
|
const result = await checkWrittenMentions(node.nodeValue, event, guild, di)
|
||||||
if (result) {
|
if (result) {
|
||||||
node.nodeValue = result.content
|
node.nodeValue = result.content
|
||||||
ensureJoined.push(...result.ensureJoined)
|
ensureJoined.push(...result.ensureJoined)
|
||||||
|
|
@ -1031,7 +1034,7 @@ async function eventToMessage(event, guild, channel, di) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await checkWrittenMentions(content, event.sender, event.room_id, guild, di)
|
const result = await checkWrittenMentions(content, event, guild, di)
|
||||||
if (result) {
|
if (result) {
|
||||||
content = result.content
|
content = result.content
|
||||||
ensureJoined.push(...result.ensureJoined)
|
ensureJoined.push(...result.ensureJoined)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ const DiscordTypes = require("discord-api-types/v10")
|
||||||
const {eventToMessage} = require("./event-to-message")
|
const {eventToMessage} = require("./event-to-message")
|
||||||
const data = require("../../../test/data")
|
const data = require("../../../test/data")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
const {select, discord} = require("../../passthrough")
|
const {select, discord, db} = require("../../passthrough")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} roomID
|
* @param {string} roomID
|
||||||
|
|
@ -5521,6 +5521,9 @@ test("event2message: @room converts to @everyone and is allowed when the room do
|
||||||
type: "m.room.message",
|
type: "m.room.message",
|
||||||
sender: "@cadence:cadence.moe",
|
sender: "@cadence:cadence.moe",
|
||||||
content: {
|
content: {
|
||||||
|
"m.mentions": {
|
||||||
|
room: true
|
||||||
|
},
|
||||||
msgtype: "m.text",
|
msgtype: "m.text",
|
||||||
body: "@room dinner's ready",
|
body: "@room dinner's ready",
|
||||||
},
|
},
|
||||||
|
|
@ -5563,7 +5566,124 @@ test("event2message: @room converts to @everyone and is allowed when the room do
|
||||||
content: "@everyone dinner's ready",
|
content: "@everyone dinner's ready",
|
||||||
avatar_url: "https://bridge.example.org/download/letter-avatar?letter=C&hue=90",
|
avatar_url: "https://bridge.example.org/download/letter-avatar?letter=C&hue=90",
|
||||||
allowed_mentions: {
|
allowed_mentions: {
|
||||||
parse: ["users", "roles", "everyone"]
|
users: [],
|
||||||
|
parse: ["roles", "everyone"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
ensureJoined: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: @room is converted to a role in configured rooms", async t => {
|
||||||
|
let called = 0
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@cadence:cadence.moe",
|
||||||
|
content: {
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "@room dinner's ready",
|
||||||
|
},
|
||||||
|
room_id: "!fGgIymcYWOqjbSRUdV:cadence.moe",
|
||||||
|
event_id: "$SiXetU9h9Dg-M9Frcw_C6ahnoXZ3QPZe3MVJR5tcB9A"
|
||||||
|
}, data.guild.general, data.channel.general, {
|
||||||
|
api: {
|
||||||
|
async getStateEvent(roomID, type, key) {
|
||||||
|
called++
|
||||||
|
t.equal(roomID, "!fGgIymcYWOqjbSRUdV:cadence.moe")
|
||||||
|
t.equal(type, "m.room.power_levels")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
users: {},
|
||||||
|
notifications: {
|
||||||
|
room: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getStateEventOuter(roomID, type, key) {
|
||||||
|
t.equal(roomID, "!fGgIymcYWOqjbSRUdV:cadence.moe")
|
||||||
|
t.equal(type, "m.room.create")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
type: "m.room.create",
|
||||||
|
state_key: "",
|
||||||
|
sender: "@_ooye_bot:cadence.moe",
|
||||||
|
content: {
|
||||||
|
room_version: "11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "cadence [they]",
|
||||||
|
content: "<@&123456> dinner's ready",
|
||||||
|
avatar_url: "https://bridge.example.org/download/matrix/cadence.moe/azCAhThKTojXSZJRoWwZmhvU?preset=avatar",
|
||||||
|
allowed_mentions: {
|
||||||
|
parse: ["users", "roles"]
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
ensureJoined: []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("event2message: @room is not allowed with mismatched m.mentions", async t => {
|
||||||
|
let called = 0
|
||||||
|
t.deepEqual(
|
||||||
|
await eventToMessage({
|
||||||
|
type: "m.room.message",
|
||||||
|
sender: "@cadence:cadence.moe",
|
||||||
|
content: {
|
||||||
|
"m.mentions": {},
|
||||||
|
msgtype: "m.text",
|
||||||
|
body: "@room dinner's ready",
|
||||||
|
},
|
||||||
|
room_id: "!kLRqKKUQXcibIMtOpl:cadence.moe",
|
||||||
|
event_id: "$SiXetU9h9Dg-M9Frcw_C6ahnoXZ3QPZe3MVJR5tcB9A"
|
||||||
|
}, data.guild.general, data.channel.general, {
|
||||||
|
api: {
|
||||||
|
async getStateEvent(roomID, type, key) {
|
||||||
|
called++
|
||||||
|
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||||
|
t.equal(type, "m.room.power_levels")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
users: {},
|
||||||
|
notifications: {
|
||||||
|
room: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getStateEventOuter(roomID, type, key) {
|
||||||
|
t.equal(roomID, "!kLRqKKUQXcibIMtOpl:cadence.moe")
|
||||||
|
t.equal(type, "m.room.create")
|
||||||
|
t.equal(key, "")
|
||||||
|
return {
|
||||||
|
type: "m.room.create",
|
||||||
|
state_key: "",
|
||||||
|
sender: "@_ooye_bot:cadence.moe",
|
||||||
|
content: {
|
||||||
|
room_version: "11"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
messagesToDelete: [],
|
||||||
|
messagesToEdit: [],
|
||||||
|
messagesToSend: [{
|
||||||
|
username: "cadence [they]",
|
||||||
|
content: "@room dinner's ready",
|
||||||
|
avatar_url: "https://bridge.example.org/download/letter-avatar?letter=C&hue=90",
|
||||||
|
allowed_mentions: {
|
||||||
|
users: [],
|
||||||
|
parse: ["roles"]
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
ensureJoined: []
|
ensureJoined: []
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,72 @@ block body
|
||||||
td(colspan="3")
|
td(colspan="3")
|
||||||
.s-empty-state No channels linked between Discord and Matrix yet...
|
.s-empty-state No channels linked between Discord and Matrix yet...
|
||||||
|
|
||||||
|
if space_id && linkedChannelsWithDetails.length
|
||||||
|
- const customMentionRooms = linkedChannelsWithDetails.filter(row => row.room_mention_role)
|
||||||
|
- const defaultMentionRooms = linkedChannelsWithDetails.filter(row => !row.room_mention_role)
|
||||||
|
- const mentionRoles = [...guild.roles].filter(role => role.id !== guild_id).sort((a, b) => b.position - a.position)
|
||||||
|
h3.mt32.fs-category#room-notifications Room notifications
|
||||||
|
.s-card.p0
|
||||||
|
p.s-description.m12 Choose which Discord role receives Matrix @room notifications
|
||||||
|
if customMentionRooms.length
|
||||||
|
.s-table-container(style="container-type: inline-size")
|
||||||
|
table.s-table.s-table__bx-simple(style="table-layout: fixed")
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th(scope="col" style="width: calc(50cqw - 40px)") Channel
|
||||||
|
th.pr12(scope="col") Notify role
|
||||||
|
tbody
|
||||||
|
each row in customMentionRooms
|
||||||
|
tr
|
||||||
|
td.va-middle
|
||||||
|
+discord(row.channel)
|
||||||
|
td.pr12
|
||||||
|
.d-flex.ai-start.g8
|
||||||
|
form.fl-grow1.wmn0.d-flex.ai-stretch.g8(method="post" action=rel("/api/room-mention-role"))
|
||||||
|
input(type="hidden" name="guild_id" value=guild_id)
|
||||||
|
input(type="hidden" name="channel_id" value=row.channel.id)
|
||||||
|
.s-select.fl-grow1.wmn0
|
||||||
|
select.w100(name="room_mention_role" id="mention-role-" + row.channel.id aria-label="Notify role for #" + row.channel.name onchange="this.form.requestSubmit()" style="height: 32px")
|
||||||
|
if !mentionRoles.some(role => role.id === row.room_mention_role)
|
||||||
|
option(value=row.room_mention_role selected disabled) Deleted role (choose a replacement)
|
||||||
|
each role in mentionRoles
|
||||||
|
option(value=role.id selected=row.room_mention_role === role.id)= "@" + role.name
|
||||||
|
button.s-btn.s-btn__outlined.py0.box-border(type="submit" data-room-mention-save style="height: 32px; min-height: 0") Save
|
||||||
|
form.d-flex.ai-stretch(method="post" action=rel("/api/room-mention-role"))
|
||||||
|
input(type="hidden" name="guild_id" value=guild_id)
|
||||||
|
input(type="hidden" name="channel_id" value=row.channel.id)
|
||||||
|
input(type="hidden" name="room_mention_role" value="")
|
||||||
|
button.s-btn.s-btn__danger.s-btn__outlined.py0(type="submit" aria-label="Remove custom role for #" + row.channel.name style="height: 32px") Remove
|
||||||
|
|
||||||
|
script.
|
||||||
|
document.querySelectorAll('[data-room-mention-save]').forEach(button => button.remove())
|
||||||
|
|
||||||
|
if defaultMentionRooms.length && mentionRoles.length
|
||||||
|
details.mt16.mx12.mb12
|
||||||
|
summary.s-label.cursor-pointer Add custom role
|
||||||
|
form.mt16(method="post" action=rel("/api/room-mention-role"))
|
||||||
|
input(type="hidden" name="guild_id" value=guild_id)
|
||||||
|
.d-flex.fw-wrap.ai-end.g16
|
||||||
|
.fl-grow1.wmn0(style="flex-basis: 0; min-width: 180px")
|
||||||
|
label.s-label(for="mention-role-channel") Channel
|
||||||
|
.s-select
|
||||||
|
select.w100.box-border#mention-role-channel(name="channel_id" required style="height: 32px")
|
||||||
|
option(value="" selected disabled) Choose a channel
|
||||||
|
each row in defaultMentionRooms
|
||||||
|
- const parent = discord.channels.get(row.channel.parent_id)
|
||||||
|
option(value=row.channel.id)= "#" + row.channel.name + (parent ? " - " + parent.name : "")
|
||||||
|
.fl-grow1.wmn0(style="flex-basis: 0; min-width: 180px")
|
||||||
|
label.s-label(for="mention-role-new") Notify role
|
||||||
|
.s-select
|
||||||
|
select.w100.box-border#mention-role-new(name="room_mention_role" required style="height: 32px")
|
||||||
|
option(value="" selected disabled) Choose a role
|
||||||
|
each role in mentionRoles
|
||||||
|
option(value=role.id)= "@" + role.name
|
||||||
|
.d-flex.ai-stretch(style="height: 32px")
|
||||||
|
button.s-btn.s-btn__icon.s-btn__filled.d-inline-flex.ai-center.g4.py0.box-border(type="submit" style="min-height: 0")
|
||||||
|
!= icons.Icons.IconPlusSm
|
||||||
|
span Add
|
||||||
|
|
||||||
h3.fs-category.mt32 Auto-create
|
h3.fs-category.mt32 Auto-create
|
||||||
.s-card.d-grid.px0
|
.s-card.d-grid.px0
|
||||||
form.d-flex.ai-center.g16
|
form.d-flex.ai-center.g16
|
||||||
|
|
|
||||||
|
|
@ -140,3 +140,22 @@ as.router.post("/api/default-roles", defineEventHandler(async event => {
|
||||||
return sendRedirect(event, `/guild?guild_id=${guildID}`, 302)
|
return sendRedirect(event, `/guild?guild_id=${guildID}`, 302)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// An empty role restores @everyone
|
||||||
|
as.router.post("/api/room-mention-role", defineEventHandler(async event => {
|
||||||
|
const body = await readValidatedBody(event, z.object({
|
||||||
|
guild_id: z.string(),
|
||||||
|
channel_id: z.string(),
|
||||||
|
room_mention_role: z.string().regex(/^(?:[0-9]+)?$/)
|
||||||
|
}).parse)
|
||||||
|
const managed = await auth.getManagedGuilds(event)
|
||||||
|
if (!managed.has(body.guild_id)) throw createError({status: 403, message: "Forbidden", data: "Can't change settings for a guild you don't have Manage Server permissions in"})
|
||||||
|
const room = select("channel_room", "room_id", {channel_id: body.channel_id, guild_id: body.guild_id}).get()
|
||||||
|
if (!room) throw createError({status: 404, message: "Linked channel not found"})
|
||||||
|
const guild = discord.guilds.get(body.guild_id)
|
||||||
|
if (body.room_mention_role && (!guild?.roles.some(role => role.id === body.room_mention_role) || body.room_mention_role === body.guild_id)) {
|
||||||
|
throw createError({status: 400, message: "Choose a role from this server"})
|
||||||
|
}
|
||||||
|
db.prepare("UPDATE channel_room SET room_mention_role = ? WHERE channel_id = ? AND guild_id = ?").run(body.room_mention_role || null, body.channel_id, body.guild_id)
|
||||||
|
return sendRedirect(event, `/guild?guild_id=${body.guild_id}#room-notifications`, 302)
|
||||||
|
}))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
const {tryToCatch} = require("try-to-catch")
|
const {tryToCatch} = require("try-to-catch")
|
||||||
const {router, test} = require("../../../test/web")
|
const {router, test} = require("../../../test/web")
|
||||||
const {select} = require("../../passthrough")
|
const {select, discord} = require("../../passthrough")
|
||||||
const {MatrixServerError} = require("../../matrix/mreq")
|
const {MatrixServerError} = require("../../matrix/mreq")
|
||||||
|
|
||||||
test("web autocreate: checks permissions", async t => {
|
test("web autocreate: checks permissions", async t => {
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ function getChannelRoomsLinks(guild, rooms, roles, space) {
|
||||||
let channelIDs = discord.guildChannelMap.get(guild.id)
|
let channelIDs = discord.guildChannelMap.get(guild.id)
|
||||||
assert(channelIDs)
|
assert(channelIDs)
|
||||||
|
|
||||||
let linkedChannels = select("channel_room", ["channel_id", "room_id", "name", "nick"], {channel_id: channelIDs}).all()
|
let linkedChannels = select("channel_room", ["channel_id", "room_id", "name", "nick", "room_mention_role"], {channel_id: channelIDs}).all()
|
||||||
let linkedChannelsWithDetails = linkedChannels.map(c => ({
|
let linkedChannelsWithDetails = linkedChannels.map(c => ({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
/** @type {DiscordTypes.APIGuildChannel} */ channel: discord.channels.get(c.channel_id),
|
/** @type {DiscordTypes.APIGuildChannel} */ channel: discord.channels.get(c.channel_id),
|
||||||
|
|
|
||||||
4
start.js
4
start.js
|
|
@ -1,10 +1,8 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
// Temporal API stabilised in Node.js 26. Previous versions can use `--harmony-temporal`, but that's jank, so have a polyfill.
|
// Ensure consistent output from the Intl and Temporal API across Node.js versions
|
||||||
if (!("Temporal" in globalThis)) {
|
|
||||||
require("temporal-polyfill-lite/global")
|
require("temporal-polyfill-lite/global")
|
||||||
}
|
|
||||||
|
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const sqlite = require("better-sqlite3")
|
const sqlite = require("better-sqlite3")
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ INSERT INTO channel_room (channel_id, room_id, name, nick, thread_parent, custom
|
||||||
('1161864271370666075', '!mHmhQQPwXNananMUqq:cadence.moe', 'updates', NULL, NULL, NULL, '112760669178241024'),
|
('1161864271370666075', '!mHmhQQPwXNananMUqq:cadence.moe', 'updates', NULL, NULL, NULL, '112760669178241024'),
|
||||||
('1438284564815548418', '!MHxNpwtgVqWOrmyoTn:cadence.moe', 'sin-cave', NULL, NULL, NULL, '665289423482519565'),
|
('1438284564815548418', '!MHxNpwtgVqWOrmyoTn:cadence.moe', 'sin-cave', NULL, NULL, NULL, '665289423482519565'),
|
||||||
('598707048112193536', '!JBxeGYnzQwLnaooOLD:cadence.moe', 'winners', NULL, NULL, NULL, '1345641201902288987');
|
('598707048112193536', '!JBxeGYnzQwLnaooOLD:cadence.moe', 'winners', NULL, NULL, NULL, '1345641201902288987');
|
||||||
|
UPDATE channel_room SET room_mention_role = '123456' WHERE room_id = '!fGgIymcYWOqjbSRUdV:cadence.moe';
|
||||||
|
|
||||||
INSERT INTO historical_channel_room (reference_channel_id, room_id, upgraded_timestamp) SELECT channel_id, room_id, 0 FROM channel_room;
|
INSERT INTO historical_channel_room (reference_channel_id, room_id, upgraded_timestamp) SELECT channel_id, room_id, 0 FROM channel_room;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
|
// Ensure consistent output from the Intl and Temporal API across Node.js versions
|
||||||
|
require("temporal-polyfill-lite/global")
|
||||||
|
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const {join} = require("path")
|
const {join} = require("path")
|
||||||
const sqlite = require("better-sqlite3")
|
const sqlite = require("better-sqlite3")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue