matrix-account-creator/index.js

198 lines
6.5 KiB
JavaScript

require("dotenv").config();
const { DISCORD_TOKEN, MATRIX_BASE_URL, MATRIX_SERVER_NAME, MATRIX_ADMIN_TOKEN } = process.env;
const colors = require("colors");
const Path = require("path")
const Discord = require("discord.js");
const Client = new Discord.Client({
intents: []
});
const {
REST,
Routes
} = require('discord.js');
const rest = new REST({
version: '10'
}).setToken(DISCORD_TOKEN);
var whoami;
var matrixHeaders = {
"Authorization": `Bearer ${MATRIX_ADMIN_TOKEN}`
};
Client.on("ready", async () => {
console.log(`${colors.cyan("[Info]")} Logged in as ${Client.user.displayName}`);
const commands = [
{
"name": "matrix-register",
"description": "Create an account on matrix.litenet.tel",
"type": 1,
"options": [
{
"name": "username",
"description": "@<username>:litenet.tel. Be warned, you can't change this once it's set.",
"type": Discord.ApplicationCommandOptionType.String,
"required": true
}
]
}
]
//Register commands
await (async () => {
try {
console.log(`${colors.cyan("[Info]")} Registering Commands...`)
//Global
await rest.put(Routes.applicationCommands(Client.user.id), { body: commands })
console.log(`${colors.cyan("[Info]")} Successfully registered commands.`);
} catch (error) {
console.error(error);
}
})();
fetch(`${MATRIX_BASE_URL}/_matrix/client/v3/account/whoami`, {
headers: matrixHeaders
}).then(async (response) => {
whoami = await response.json();
})
});
const usernameFilter = /^[a-z0-9._=\-\/+]+$/;
const formatUsername = (input) => {
if (!usernameFilter.test(input)) throw new Error("Username does not match allowed characters");
return `@${input}:${MATRIX_SERVER_NAME}`
}
function generatePassword(length) {
// Define the character set
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789._=-/+';
// Ensure the length is valid
if (length <= 0) {
throw new Error('Password length must be greater than 0');
}
let password = '';
// Generate the password
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
password += characters[randomIndex];
}
return password;
}
async function uploadMediaToMatrix(url, filename, mimetype) {
var data = await fetch(url);
const response = await fetch(`${MATRIX_BASE_URL}/_matrix/media/v3/upload?filename=${filename}`, {
method: "POST",
headers: {
"Authorization": `Bearer ${MATRIX_ADMIN_TOKEN}`,
"Content-Type": mimetype
},
body: await data.arrayBuffer()
});
const json = await response.json();
return json;
}
Client.on("interactionCreate", async (interaction) => {
if (!interaction.isCommand()) return;
switch (interaction.commandName) {
case "matrix-register":
const inputUsername = interaction.options.getString("username");
if (!usernameFilter.test(inputUsername)) return interaction.reply({ephemeral: true, content: "Your username contains invalid characters.\nYour username MUST contain only the characters `a-z`, `0-9`, `.`, `_`, `=`, `-`, `/`, and `+`."});
const mxid = formatUsername(inputUsername);
console.log(`${colors.cyan('[Info]')} ${interaction.user.displayName} (${interaction.user.id}) is trying to make account ${mxid}`)
// Check if the user exists
const userCheck = await fetch(`${MATRIX_BASE_URL}/_synapse/admin/v2/users/${mxid}`, {
headers: {
"Authorization": `Bearer ${MATRIX_ADMIN_TOKEN}`
}
});
if (userCheck.ok) return interaction.reply({ephemeral: true, content: "That username is taken! Please choose a different one."});
// Check if user already has a matrix account
const dataStoreCheck = await fetch(`${MATRIX_BASE_URL}/_matrix/client/v3/user/${whoami.user_id}/account_data/tel.litenet.discordids.${interaction.user.id}`,{
headers: matrixHeaders
})
if(dataStoreCheck.status !== 404) return interaction.reply({ephemeral: true, content: `You already have an account dingus! It's \`${(await dataStoreCheck.json()).matrix_id}\`\nIf you believe this is an error, please contact an admin!`});
const newPassword = generatePassword(32);
// Emma@Rory&: Upload avatar to matrix:
const avatar = interaction.user.displayAvatarURL({size: 4096});
var filename = Path.basename(avatar);
var mimetype;
switch (filename.split(".").pop()) {
case "png":
mimetype = "image/png";
break;
case "jpg":
case "jpeg":
mimetype = "image/jpeg";
break;
case "gif":
mimetype = "image/gif";
break;
case "webp":
mimetype = "image/webp";
break;
default:
mimetype = "image/png";
break;
}
let mxcUri = undefined;
await uploadMediaToMatrix(avatar, filename, mimetype).then((response) => {
mxcUri = response.content_uri;
}).catch((error) => {
console.error("Error while uploading discord avatar to matrix:", error);
});
// Create the user
const createUser = await fetch(`${MATRIX_BASE_URL}/_synapse/admin/v2/users/${mxid}`, {
method: "PUT",
headers: {
"Authorization": `Bearer ${MATRIX_ADMIN_TOKEN}`
},
body: JSON.stringify({
"password": newPassword,
"displayname": interaction.user.displayName,
avatar_url: mxcUri
})
});
if (!userCheck.status.toString() == "201") {
const createUserError = await createUser.json()
return interaction.reply({
ephemeral: true,
content: `An error has occured!\n${createUserError.errcode}: ${createUserError.error}`
})
}
// Send the user their credentials
interaction.user.send({content: `Your matrix account has been created!\nUsername: \`${mxid}\`\nPassword: ||\`${newPassword}\`||\nServer: ${MATRIX_SERVER_NAME}\n\nYou can find a list of Matrix clients [here!](https://matrix.org/ecosystem/clients/)`}).then(() => {
interaction.reply({ephemeral: true, content: "User created! Check your DMs!"});
}).catch((error) => {
interaction.reply({ephemeral: true, content: `Your matrix account has been created!\nUsername: \`${mxid}\`\nPassword: ||\`${newPassword}\`||\nServer: ${MATRIX_SERVER_NAME}\n\nYou can find a list of Matrix clients [here!](https://matrix.org/ecosystem/clients/)\n\nYour DMs were disabled, so we weren't able to send you these credentials!\n***MAKE SURE YOU SAVE THEM***`});
})
// Store the discord id and matrix id
fetch(`${MATRIX_BASE_URL}/_matrix/client/v3/user/${whoami.user_id}/account_data/tel.litenet.discordids.${interaction.user.id}`,{
headers: matrixHeaders,
body: JSON.stringify({
matrix_id: mxid,
discord_id: interaction.user.id
}),
method: "PUT"
})
break;
}
})
Client.login(DISCORD_TOKEN);