Add //invite command
This commit is contained in:
		
							parent
							
								
									0ed74feb66
								
							
						
					
					
						commit
						c9dec3ba75
					
				
					 3 changed files with 122 additions and 4 deletions
				
			
		| 
						 | 
					@ -18,8 +18,8 @@ const createRoom = sync.require("./actions/create-room")
 | 
				
			||||||
const createSpace = sync.require("./actions/create-space")
 | 
					const createSpace = sync.require("./actions/create-space")
 | 
				
			||||||
/** @type {import("../matrix/api")}) */
 | 
					/** @type {import("../matrix/api")}) */
 | 
				
			||||||
const api = sync.require("../matrix/api")
 | 
					const api = sync.require("../matrix/api")
 | 
				
			||||||
/** @type {import("./discord-command-handler")}) */
 | 
					/** @type {import("../discord/discord-command-handler")}) */
 | 
				
			||||||
const discordCommandHandler = sync.require("./discord-command-handler")
 | 
					const discordCommandHandler = sync.require("../discord/discord-command-handler")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let lastReportedEvent = 0
 | 
					let lastReportedEvent = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,8 @@ const {discord, sync, db} = require("../passthrough")
 | 
				
			||||||
const api = sync.require("../matrix/api")
 | 
					const api = sync.require("../matrix/api")
 | 
				
			||||||
/** @type {import("../matrix/file")} */
 | 
					/** @type {import("../matrix/file")} */
 | 
				
			||||||
const file = sync.require("../matrix/file")
 | 
					const file = sync.require("../matrix/file")
 | 
				
			||||||
 | 
					/** @type {import("./utils")} */
 | 
				
			||||||
 | 
					const utils = sync.require("./utils")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PREFIX = "//"
 | 
					const PREFIX = "//"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,9 +123,75 @@ const commands = [{
 | 
				
			||||||
	aliases: ["invite"],
 | 
						aliases: ["invite"],
 | 
				
			||||||
	execute: replyctx(
 | 
						execute: replyctx(
 | 
				
			||||||
		async (message, channel, guild, ctx) => {
 | 
							async (message, channel, guild, ctx) => {
 | 
				
			||||||
 | 
								// Check guild is bridged
 | 
				
			||||||
 | 
								const spaceID = db.prepare("SELECT space_id FROM guild_space WHERE guild_id = ?").pluck().get(guild.id)
 | 
				
			||||||
 | 
								const roomID = db.prepare("SELECT room_id FROM channel_room WHERE channel_id = ?").pluck().get(channel.id)
 | 
				
			||||||
 | 
								if (!spaceID || !roomID) return discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
									...ctx,
 | 
				
			||||||
 | 
									content: "This server isn't bridged to Matrix, so you can't invite Matrix users."
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Check CREATE_INSTANT_INVITE permission
 | 
				
			||||||
 | 
								assert(message.member)
 | 
				
			||||||
 | 
								const guildPermissions = utils.getPermissions(message.member.roles, guild.roles)
 | 
				
			||||||
 | 
								if (!(guildPermissions & BigInt(1))) {
 | 
				
			||||||
				return discord.snow.channel.createMessage(channel.id, {
 | 
									return discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
					...ctx,
 | 
										...ctx,
 | 
				
			||||||
				content: "This command isn't implemented yet."
 | 
										content: "You don't have permission to invite people to this Discord server."
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Get named MXID
 | 
				
			||||||
 | 
								const mxid = message.content.match(/@([^:]+):([a-z0-9:-]+\.[a-z0-9.:-]+)/)?.[0]
 | 
				
			||||||
 | 
								if (!mxid) return discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
									...ctx,
 | 
				
			||||||
 | 
									content: "You have to say the Matrix ID of the person you want to invite. Matrix IDs look like this: `@username:example.org`"
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Check for existing invite to the space
 | 
				
			||||||
 | 
								let spaceMember
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									spaceMember = await api.getStateEvent(spaceID, "m.room.member", mxid)
 | 
				
			||||||
 | 
								} catch (e) {}
 | 
				
			||||||
 | 
								if (spaceMember && spaceMember.membership === "invite") {
 | 
				
			||||||
 | 
									return discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
										...ctx,
 | 
				
			||||||
 | 
										content: `\`${mxid}\` already has an invite, which they haven't accepted yet.`
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Invite Matrix user if not in space
 | 
				
			||||||
 | 
								if (!spaceMember || spaceMember.membership !== "join") {
 | 
				
			||||||
 | 
									await api.inviteToRoom(spaceID, mxid)
 | 
				
			||||||
 | 
									return discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
										...ctx,
 | 
				
			||||||
 | 
										content: `You invited \`${mxid}\` to the server.`
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// The Matrix user *is* in the space, maybe we want to invite them to this channel?
 | 
				
			||||||
 | 
								let roomMember
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									roomMember = await api.getStateEvent(roomID, "m.room.member", mxid)
 | 
				
			||||||
 | 
								} catch (e) {}
 | 
				
			||||||
 | 
								if (!roomMember || (roomMember.membership !== "join" && roomMember.membership !== "invite")) {
 | 
				
			||||||
 | 
									const sent = await discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
										...ctx,
 | 
				
			||||||
 | 
										content: `\`${mxid}\` is already in this server. Would you like to additionally invite them to this specific channel?\nHit ✅ to make it happen.`
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									return addButton(channel.id, sent.id, "✅", message.author.id).then(async data => {
 | 
				
			||||||
 | 
										await api.inviteToRoom(roomID, mxid)
 | 
				
			||||||
 | 
										await discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
											...ctx,
 | 
				
			||||||
 | 
											content: `You invited \`${mxid}\` to the channel.`
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// The Matrix user *is* in the space and in the channel.
 | 
				
			||||||
 | 
								await discord.snow.channel.createMessage(channel.id, {
 | 
				
			||||||
 | 
									...ctx,
 | 
				
			||||||
 | 
									content: `\`${mxid}\` is already in this server and this channel.`
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
							
								
								
									
										50
									
								
								discord/utils.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								discord/utils.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					// @ts-check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const DiscordTypes = require("discord-api-types/v10")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @param {string[]} userRoles
 | 
				
			||||||
 | 
					 * @param {DiscordTypes.APIGuild["roles"]} guildRoles
 | 
				
			||||||
 | 
					 * @param {string} [userID]
 | 
				
			||||||
 | 
					 * @param {DiscordTypes.APIGuildChannel["permission_overwrites"]} [channelOverwrites]
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function getPermissions(userRoles, guildRoles, userID, channelOverwrites) {
 | 
				
			||||||
 | 
						let allowed = BigInt(0)
 | 
				
			||||||
 | 
						let everyoneID
 | 
				
			||||||
 | 
						// Guild allows
 | 
				
			||||||
 | 
						for (const role of guildRoles) {
 | 
				
			||||||
 | 
							if (role.name === "@everyone") {
 | 
				
			||||||
 | 
								allowed |= BigInt(role.permissions)
 | 
				
			||||||
 | 
								everyoneID = role.id
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (userRoles.includes(role.id)) {
 | 
				
			||||||
 | 
								allowed |= BigInt(role.permissions)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (channelOverwrites) {
 | 
				
			||||||
 | 
							/** @type {((overwrite: Required<DiscordTypes.APIGuildChannel>["permission_overwrites"][0]) => any)[]} */
 | 
				
			||||||
 | 
							const actions = [
 | 
				
			||||||
 | 
								// Channel @everyone deny
 | 
				
			||||||
 | 
								overwrite => overwrite.id === everyoneID && (allowed &= ~BigInt(overwrite.deny)),
 | 
				
			||||||
 | 
								// Channel @everyone allow
 | 
				
			||||||
 | 
								overwrite => overwrite.id === everyoneID && (allowed |= BigInt(overwrite.allow)),
 | 
				
			||||||
 | 
								// Role deny
 | 
				
			||||||
 | 
								overwrite => userRoles.includes(overwrite.id) && (allowed &= ~BigInt(overwrite.deny)),
 | 
				
			||||||
 | 
								// Role allow
 | 
				
			||||||
 | 
								overwrite => userRoles.includes(overwrite.id) && (allowed |= ~BigInt(overwrite.allow)),
 | 
				
			||||||
 | 
								// User deny
 | 
				
			||||||
 | 
								overwrite => overwrite.id === userID && (allowed &= ~BigInt(overwrite.deny)),
 | 
				
			||||||
 | 
								// User allow
 | 
				
			||||||
 | 
								overwrite => overwrite.id === userID && (allowed |= BigInt(overwrite.allow))
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
							for (let i = 0; i < actions.length; i++) {
 | 
				
			||||||
 | 
								for (const overwrite of channelOverwrites) {
 | 
				
			||||||
 | 
									actions[i](overwrite)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return allowed
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports.getPermissions = getPermissions
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue