mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-10 01:00:28 +03:00
279 lines
8 KiB
JavaScript
279 lines
8 KiB
JavaScript
import { EventTimeline } from 'matrix-js-sdk';
|
|
import { getIdServer } from '../../util/matrixUtil';
|
|
|
|
/**
|
|
* https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L73
|
|
* @param {MatrixClient} mx Matrix client
|
|
* @param {string} roomId Id of room to add
|
|
* @param {string} userId User id to which dm || undefined to remove
|
|
* @returns {Promise} A promise
|
|
*/
|
|
function addRoomToMDirect(mx, roomId, userId) {
|
|
const mDirectsEvent = mx.getAccountData('m.direct');
|
|
let userIdToRoomIds = {};
|
|
|
|
if (typeof mDirectsEvent !== 'undefined') userIdToRoomIds = structuredClone(mDirectsEvent.getContent());
|
|
|
|
// remove it from the lists of any others users
|
|
// (it can only be a DM room for one person)
|
|
Object.keys(userIdToRoomIds).forEach((thisUserId) => {
|
|
const roomIds = userIdToRoomIds[thisUserId];
|
|
|
|
if (thisUserId !== userId) {
|
|
const indexOfRoomId = roomIds.indexOf(roomId);
|
|
if (indexOfRoomId > -1) {
|
|
roomIds.splice(indexOfRoomId, 1);
|
|
}
|
|
}
|
|
});
|
|
|
|
// now add it, if it's not already there
|
|
if (userId) {
|
|
const roomIds = userIdToRoomIds[userId] || [];
|
|
if (roomIds.indexOf(roomId) === -1) {
|
|
roomIds.push(roomId);
|
|
}
|
|
userIdToRoomIds[userId] = roomIds;
|
|
}
|
|
|
|
return mx.setAccountData('m.direct', userIdToRoomIds);
|
|
}
|
|
|
|
/**
|
|
* Given a room, estimate which of its members is likely to
|
|
* be the target if the room were a DM room and return that user.
|
|
* https://github.com/matrix-org/matrix-react-sdk/blob/1e6c6e9d800890c732d60429449bc280de01a647/src/Rooms.js#L117
|
|
*
|
|
* @param {Object} room Target room
|
|
* @param {string} myUserId User ID of the current user
|
|
* @returns {string} User ID of the user that the room is probably a DM with
|
|
*/
|
|
function guessDMRoomTargetId(room, myUserId) {
|
|
let oldestMemberTs;
|
|
let oldestMember;
|
|
|
|
// Pick the joined user who's been here longest (and isn't us),
|
|
room.getJoinedMembers().forEach((member) => {
|
|
if (member.userId === myUserId) return;
|
|
|
|
if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
|
|
oldestMember = member;
|
|
oldestMemberTs = member.events.member.getTs();
|
|
}
|
|
});
|
|
if (oldestMember) return oldestMember.userId;
|
|
|
|
// if there are no joined members other than us, use the oldest member
|
|
room.getLiveTimeline().getState(EventTimeline.FORWARDS)?.getMembers().forEach((member) => {
|
|
if (member.userId === myUserId) return;
|
|
|
|
if (typeof oldestMemberTs === 'undefined' || (member.events.member && member.events.member.getTs() < oldestMemberTs)) {
|
|
oldestMember = member;
|
|
oldestMemberTs = member.events.member.getTs();
|
|
}
|
|
});
|
|
|
|
if (typeof oldestMember === 'undefined') return myUserId;
|
|
return oldestMember.userId;
|
|
}
|
|
|
|
function convertToDm(mx, roomId) {
|
|
const room = mx.getRoom(roomId);
|
|
return addRoomToMDirect(mx, roomId, guessDMRoomTargetId(room, mx.getUserId()));
|
|
}
|
|
|
|
function convertToRoom(mx, roomId) {
|
|
return addRoomToMDirect(mx, roomId, undefined);
|
|
}
|
|
|
|
/**
|
|
* @param {MatrixClient} mx
|
|
* @param {string} roomId
|
|
* @param {boolean} isDM
|
|
* @param {string[]} via
|
|
*/
|
|
async function join(mx, roomIdOrAlias, isDM = false, via = undefined) {
|
|
try {
|
|
const resultRoom = await mx.joinRoom(roomIdOrAlias, { viaServers: via });
|
|
|
|
if (isDM) {
|
|
const targetUserId = guessDMRoomTargetId(mx.getRoom(resultRoom.roomId), mx.getUserId());
|
|
await addRoomToMDirect(mx, resultRoom.roomId, targetUserId);
|
|
}
|
|
return resultRoom.roomId;
|
|
} catch (e) {
|
|
throw new Error(e);
|
|
}
|
|
}
|
|
|
|
async function create(mx, options, isDM = false) {
|
|
try {
|
|
const result = await mx.createRoom(options);
|
|
if (isDM && typeof options.invite?.[0] === 'string') {
|
|
await addRoomToMDirect(mx, result.room_id, options.invite[0]);
|
|
}
|
|
return result;
|
|
} catch (e) {
|
|
const errcodes = ['M_UNKNOWN', 'M_BAD_JSON', 'M_ROOM_IN_USE', 'M_INVALID_ROOM_STATE', 'M_UNSUPPORTED_ROOM_VERSION'];
|
|
if (errcodes.includes(e.errcode)) {
|
|
throw new Error(e);
|
|
}
|
|
throw new Error('Something went wrong!');
|
|
}
|
|
}
|
|
|
|
async function createDM(mx, userIdOrIds, isEncrypted = true) {
|
|
const options = {
|
|
is_direct: true,
|
|
invite: Array.isArray(userIdOrIds) ? userIdOrIds : [userIdOrIds],
|
|
visibility: 'private',
|
|
preset: 'trusted_private_chat',
|
|
initial_state: [],
|
|
};
|
|
if (isEncrypted) {
|
|
options.initial_state.push({
|
|
type: 'm.room.encryption',
|
|
state_key: '',
|
|
content: {
|
|
algorithm: 'm.megolm.v1.aes-sha2',
|
|
},
|
|
});
|
|
}
|
|
|
|
const result = await create(mx, options, true);
|
|
return result;
|
|
}
|
|
|
|
async function createRoom(mx, opts) {
|
|
// joinRule: 'public' | 'invite' | 'restricted'
|
|
const { name, topic, joinRule } = opts;
|
|
const alias = opts.alias ?? undefined;
|
|
const parentId = opts.parentId ?? undefined;
|
|
const isSpace = opts.isSpace ?? false;
|
|
const isEncrypted = opts.isEncrypted ?? false;
|
|
const powerLevel = opts.powerLevel ?? undefined;
|
|
const blockFederation = opts.blockFederation ?? false;
|
|
|
|
const visibility = joinRule === 'public' ? 'public' : 'private';
|
|
const options = {
|
|
creation_content: undefined,
|
|
name,
|
|
topic,
|
|
visibility,
|
|
room_alias_name: alias,
|
|
initial_state: [],
|
|
power_level_content_override: undefined,
|
|
};
|
|
if (isSpace) {
|
|
options.creation_content = { type: 'm.space' };
|
|
}
|
|
if (blockFederation) {
|
|
options.creation_content = { 'm.federate': false };
|
|
}
|
|
if (isEncrypted) {
|
|
options.initial_state.push({
|
|
type: 'm.room.encryption',
|
|
state_key: '',
|
|
content: {
|
|
algorithm: 'm.megolm.v1.aes-sha2',
|
|
},
|
|
});
|
|
}
|
|
if (powerLevel) {
|
|
options.power_level_content_override = {
|
|
users: {
|
|
[mx.getUserId()]: powerLevel,
|
|
},
|
|
};
|
|
}
|
|
if (parentId) {
|
|
options.initial_state.push({
|
|
type: 'm.space.parent',
|
|
state_key: parentId,
|
|
content: {
|
|
canonical: true,
|
|
via: [getIdServer(mx.getUserId())],
|
|
},
|
|
});
|
|
}
|
|
if (parentId && joinRule === 'restricted') {
|
|
const caps = await mx.getCapabilities();
|
|
if (caps['m.room_versions'].available?.['9'] !== 'stable') {
|
|
throw new Error("ERROR: The server doesn't support restricted rooms");
|
|
}
|
|
if (Number(caps['m.room_versions'].default) < 9) {
|
|
options.room_version = '9';
|
|
}
|
|
options.initial_state.push({
|
|
type: 'm.room.join_rules',
|
|
content: {
|
|
join_rule: 'restricted',
|
|
allow: [{
|
|
type: 'm.room_membership',
|
|
room_id: parentId,
|
|
}],
|
|
},
|
|
});
|
|
}
|
|
|
|
const result = await create(mx, options);
|
|
|
|
if (parentId) {
|
|
await mx.sendStateEvent(parentId, 'm.space.child', {
|
|
auto_join: false,
|
|
suggested: false,
|
|
via: [getIdServer(mx.getUserId())],
|
|
}, result.room_id);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
async function ignore(mx, userIds) {
|
|
|
|
let ignoredUsers = mx.getIgnoredUsers().concat(userIds);
|
|
ignoredUsers = [...new Set(ignoredUsers)];
|
|
await mx.setIgnoredUsers(ignoredUsers);
|
|
}
|
|
|
|
async function unignore(mx, userIds) {
|
|
const ignoredUsers = mx.getIgnoredUsers();
|
|
await mx.setIgnoredUsers(ignoredUsers.filter((id) => !userIds.includes(id)));
|
|
}
|
|
|
|
async function setPowerLevel(mx, roomId, userId, powerLevel) {
|
|
const result = await mx.setPowerLevel(roomId, userId, powerLevel);
|
|
return result;
|
|
}
|
|
|
|
async function setMyRoomNick(mx, roomId, nick) {
|
|
const room = mx.getRoom(roomId);
|
|
const mEvent = room.getLiveTimeline().getState(EventTimeline.FORWARDS).getStateEvents('m.room.member', mx.getUserId());
|
|
const content = mEvent?.getContent();
|
|
if (!content) return;
|
|
await mx.sendStateEvent(roomId, 'm.room.member', {
|
|
...content,
|
|
displayname: nick,
|
|
}, mx.getUserId());
|
|
}
|
|
|
|
async function setMyRoomAvatar(mx, roomId, mxc) {
|
|
const room = mx.getRoom(roomId);
|
|
const mEvent = room.getLiveTimeline().getState(EventTimeline.FORWARDS).getStateEvents('m.room.member', mx.getUserId());
|
|
const content = mEvent?.getContent();
|
|
if (!content) return;
|
|
await mx.sendStateEvent(roomId, 'm.room.member', {
|
|
...content,
|
|
avatar_url: mxc,
|
|
}, mx.getUserId());
|
|
}
|
|
|
|
export {
|
|
convertToDm,
|
|
convertToRoom,
|
|
join,
|
|
createDM, createRoom,
|
|
ignore, unignore,
|
|
setPowerLevel,
|
|
setMyRoomNick, setMyRoomAvatar,
|
|
};
|