matrix-bot/index.js
2024-10-13 15:48:49 +02:00

206 lines
No EOL
6.3 KiB
JavaScript

import apfetch from "./auth-fetch.js";
import * as sdk from "matrix-js-sdk";
import { VerificationMethod } from "matrix-js-sdk/lib/types.js";
import { VerificationPhase, VerificationRequestEvent, VerifierEvent } from "matrix-js-sdk/lib/crypto-api.js";
import sdkExt from "./lib/ext.js";
import { resolve } from "node:path";
import fs from "node:fs";
import { LocalStorage } from 'node-localstorage';
import fetch from "node-fetch";
global.fetch = fetch;
import Olm from "@matrix-org/olm";
global.Olm = Olm;
import env from "dotenv";
env.config();
const localStorage = new LocalStorage("./data/localstorage");
const cryptoStore = new sdk.LocalStorageCryptoStore(localStorage);
const store = new sdk.MemoryStore({ localStorage });
var logger = {
trace: () => { },
debug: () => { },
info: () => { },
warn: () => { },
error: () => { },
}
const client = sdk.createClient({
baseUrl: process.env.BASE_URL,
accessToken: process.env.ACCESS_TOKEN,
deviceId: process.env.DEVICE_ID,
userId: process.env.USER_ID,
verificationMethods: [VerificationMethod.Sas],
logger: logger,
cryptoStore,
store
});
sdkExt(client);
apfetch(process.env.AP_FETCH_PORT);
var prefixes = [process.env.PREFIX];
client.initialized = false;
client.modules = [];
client.commands = [];
for (const file of fs.readdirSync(resolve("modules"))) {
try {
if (file.startsWith(".")) continue;
var module = (await import(resolve("modules", file))).default;
client.modules.push(module);
client.log("[load:modules]", `loaded ${module.name}`);
} catch (err) {
client.error("[load:modules]", `failed to load "${file}":\n`, err);
}
}
for (const file of fs.readdirSync(resolve("commands"))) {
try {
if (file.startsWith(".")) continue;
var command = (await import(resolve("commands", file))).default;
command.usage = process.env.PREFIX + command.command + (command.usage ? " " + command.usage : '');
client.commands.push(command);
client.log("[load:commands]", `loaded ${command.name}`);
} catch (err) {
client.error("[load:commands]", `failed to load "${file}":`, err);
}
}
function doModule(client, event) {
client.modules.forEach(m => {
if (!m) return;
var config = client.config.get(event.sender.roomId);
if (config.get(m.name) === false) return;
try {
m.hooks?.message(client, event);
} catch (err) {
client.log("[hook]", err);
}
});
}
function doCommand(client, event, cmd, args) {
var command;
command = client.commands.filter(c => c.command == cmd)[0];
if (!command)
return false;
if ((command.owner && event.sender.userId != process.env.OWNER_ID) || (command.minPwr && event.sender.powerLevel < command.minPwr && event.sender.userId != process.env.OWNER_ID)) {
var addl = command.minPwr ? `this command requires a power level of ${command.minPwr}\nyour power level is ${event.sender.powerLevel}` : "this command is owner-only.";
client.reply(event, addl, '<img src="mxc://nyx.ftp.sh/2kAXPyosdaz1M6Fv8t2V9SyxcJZEscdR" alt="nuh uh" /><br>' + addl.replaceAll("\n", "<br>"));
return true;
}
try {
command.execute(client, event, args);
} catch (err) {
client.log("[cmd]", err);
}
return true;
}
async function verifyCallback(request) {
console.log("### VERIFICATION ### Starting for " + request.otherUserId);
if (!request.initiatedByMe) {
await request.accept();
var verifier = await request.startVerification(VerificationMethod.Sas);
verifier.on(VerifierEvent.ShowSas, async function () {
console.log("### VERIFICATION ### Confirming");
await verifier.getShowSasCallbacks().confirm();
});
}
request.on(VerificationRequestEvent.Change, async function () {
switch (request.phase) {
case 3:
break;
case 4:
var verifier = await request.startVerification(VerificationMethod.Sas);
verifier.on(VerifierEvent.ShowSas, async function () {
console.log("### VERIFICATION ### Confirming");
await verifier.getShowSasCallbacks().confirm();
});
case 5:
console.log("### VERIFICATION ### Cancelled!");
case 6:
console.log("### VERIFICATION ### Done!");
break;
default:
console.log("### VERIFICATION ### " + request.phase);
break;
}
});
}
client.once("sync", async function (state, prevState, data) {
if (state != "PREPARED") return;
client.setGlobalErrorOnUnknownDevices(false);
client.name = client._store.users[client.credentials.userId].displayName;
client.log("[sdk:sync]", "connected:", client.name);
prefixes.push(client.name + ": ");
prefixes.push(client.name + " ");
client.initialized = true;
while (!await client.getCrypto().isCrossSigningReady()) { }
var status = await client.getCrypto().getDeviceVerificationStatus(client.getUserId(), client.getDeviceId());
if (!status || Object.keys(status).map(v => status[v]).includes(false)) {
const request = await client.getCrypto().requestOwnUserVerification();
verifyCallback(request);
}
});
client.on(sdk.CryptoEvent.VerificationRequestReceived, verifyCallback);
client.on(sdk.RoomEvent.Timeline, async function (event, room, toStartOfTimeline) {
await client.decryptEventIfNeeded(event);
if (!client.initialized || toStartOfTimeline || event.getType() !== "m.room.message" || event.sender.userId == client.credentials.userId) {
return;
}
var content = event.getContent();
if (content["m.relates_to"] !== undefined)
content.body = content.body.substring(content.body.indexOf("\n\n") + 2);
event.sender = client.getRoom(event.sender.roomId).getMember(event.getSender());
var content = content.body;
var isCommand = false;
for (const prefix of prefixes) {
if (content.startsWith(prefix)) {
isCommand = true;
content = content.substring(prefix.length);
break;
}
}
if (!isCommand) {
doModule(client, event);
} else {
var args = content.split(/\s/g);
var cmd = args.shift();
args = content.substring(cmd.length + 1);
doCommand(client, event, cmd, args)
}
});
client.on("Room.myMembership", function (room, membership, prevMembership) {
if (membership === "invite") {
client.joinRoom(room.roomId).then(function () {
client.log("[client:autojoin] joined %s", room.roomId);
});
}
});
await client.initCrypto();
await client.startClient();