mx-puppet-bridge
Advanced tools
Comparing version 0.0.19 to 0.0.20
@@ -29,2 +29,3 @@ /// <reference types="node" /> | ||
getPartsFromMxid(mxid: string): IRemoteChan | null; | ||
maybeLeaveGhost(chanMxid: string, userMxid: string): Promise<void>; | ||
delete(data: IRemoteChan, keepUsers?: boolean): Promise<void>; | ||
@@ -31,0 +32,0 @@ deleteForMxid(mxid: string): Promise<void>; |
@@ -242,2 +242,43 @@ "use strict"; | ||
} | ||
maybeLeaveGhost(chanMxid, userMxid) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
log.info(`Maybe leaving ghost ${userMxid} from ${chanMxid}`); | ||
const ghosts = yield this.bridge.puppetStore.getGhostsInChan(chanMxid); | ||
if (!ghosts.includes(userMxid)) { | ||
log.verbose("Ghost not in room!"); | ||
return; // not in chan, nothing to do | ||
} | ||
if (ghosts.length === 1) { | ||
log.verbose("Ghost is the only one in the room!"); | ||
return; // we are the last ghost in the chan, we can't leave | ||
} | ||
const intent = this.bridge.AS.getIntentForUserId(userMxid); | ||
const client = intent.underlyingClient; | ||
const oldOp = yield this.chanStore.getChanOp(chanMxid); | ||
if (oldOp === userMxid) { | ||
// we need to get a new OP! | ||
log.verbose("We are the OP in the room, we need to pass on OP"); | ||
const newOp = ghosts.find((element) => element !== userMxid); | ||
if (!newOp) { | ||
log.verbose("Noone to pass OP to!"); | ||
return; // we can't make a new OP, sorry | ||
} | ||
log.verbose(`Giving OP to ${newOp}...`); | ||
try { | ||
// give the user OP | ||
const powerLevels = yield client.getRoomStateEvent(chanMxid, "m.room.power_levels", ""); | ||
powerLevels.users[newOp] = powerLevels.users[oldOp]; | ||
yield client.sendStateEvent(chanMxid, "m.room.power_levels", "", powerLevels); | ||
yield this.chanStore.setChanOp(chanMxid, newOp); | ||
} | ||
catch (err) { | ||
log.error("Couldn't set new chan OP", err); | ||
return; | ||
} | ||
} | ||
// and finally we passed all checks and can leave | ||
yield intent.leaveRoom(chanMxid); | ||
yield this.bridge.puppetStore.leaveGhostFromChan(userMxid, chanMxid); | ||
}); | ||
} | ||
delete(data, keepUsers = false) { | ||
@@ -306,3 +347,3 @@ return __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
yield intent.underlyingClient.leaveRoom(entry.mxid); | ||
yield intent.leaveRoom(entry.mxid); | ||
} | ||
@@ -309,0 +350,0 @@ catch (err) { |
@@ -130,9 +130,13 @@ "use strict"; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const row = yield this.db.Get("SELECT * FROM chan_op WHERE chan_mxid=$chan AND user_mxid=$user", { | ||
const row = yield this.db.Get("SELECT * FROM chan_op WHERE chan_mxid=$chan", { | ||
chan: chanMxid, | ||
user: userMxid, | ||
}); | ||
if (row) { | ||
// nothing to do, we are already one | ||
return; | ||
if (row.user_mxid === userMxid) { | ||
// nothing to do, we are already set | ||
return; | ||
} | ||
yield this.db.Run("DELETE FROM chan_op WHERE chan_mxid=$chan", { | ||
chan: chanMxid, | ||
}); | ||
} | ||
@@ -139,0 +143,0 @@ yield this.db.Run("INSERT INTO chan_op (chan_mxid, user_mxid) VALUES ($chan, $user)", { |
@@ -32,6 +32,8 @@ import { IDatabaseConnector } from "./connector"; | ||
delete(puppetId: number): Promise<void>; | ||
isGhostInChan(ghostMxid: string, chanMxid: string): Promise<boolean>; | ||
joinGhostToChan(ghostMxid: string, chanMxid: string): Promise<void>; | ||
getGhostsInChan(chan: string): Promise<string[]>; | ||
emptyGhostsInChan(chan: string): Promise<void>; | ||
leaveGhostFromChan(ghostMxid: string, chanMxid: string): Promise<void>; | ||
private getRow; | ||
} |
@@ -195,3 +195,3 @@ "use strict"; | ||
} | ||
joinGhostToChan(ghostMxid, chanMxid) { | ||
isGhostInChan(ghostMxid, chanMxid) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
@@ -202,4 +202,9 @@ const exists = yield this.db.Get("SELECT * FROM ghosts_joined_chans WHERE ghost_mxid = $ghostMxid AND chan_mxid = $chanMxid", { | ||
}); | ||
if (exists) { | ||
return; // nothing to do | ||
return exists ? true : false; | ||
}); | ||
} | ||
joinGhostToChan(ghostMxid, chanMxid) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (yield this.isGhostInChan(ghostMxid, chanMxid)) { | ||
return; | ||
} | ||
@@ -227,2 +232,11 @@ yield this.db.Run("INSERT INTO ghosts_joined_chans (ghost_mxid, chan_mxid) VALUES ($ghostMxid, $chanMxid)", { | ||
} | ||
leaveGhostFromChan(ghostMxid, chanMxid) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield this.db.Run("DELETE FROM ghosts_joined_chans " + | ||
"WHERE ghost_mxid = $g AND chan_mxid = $c", { | ||
g: ghostMxid, | ||
c: chanMxid, | ||
}); | ||
}); | ||
} | ||
getRow(row) { | ||
@@ -229,0 +243,0 @@ try { |
@@ -109,2 +109,3 @@ /// <reference types="node" /> | ||
private memberInfoCache; | ||
private delayedFunction; | ||
constructor(registrationPath: string, configPath: string, features: IPuppetBridgeFeatures); | ||
@@ -111,0 +112,0 @@ readConfig(): void; |
@@ -30,5 +30,7 @@ "use strict"; | ||
const typinghandler_1 = require("./typinghandler"); | ||
const delayedfunction_1 = require("./structures/delayedfunction"); | ||
const log = new log_1.Log("PuppetBridge"); | ||
// tslint:disable-next-line:no-magic-numbers | ||
// tslint:disable no-magic-numbers | ||
const PUPPET_INVITE_CACHE_LIFETIME = 1000 * 60 * 60 * 24; | ||
const GHOST_PUPPET_LEAVE_TIMEOUT = 1000 * 60 * 60; | ||
const DEFAULT_TYPING_TIMEOUT = 30000; | ||
@@ -43,2 +45,3 @@ class PuppetBridge extends events_1.EventEmitter { | ||
this.hooks = {}; | ||
this.delayedFunction = new delayedfunction_1.DelayedFunction(); | ||
} | ||
@@ -699,3 +702,4 @@ readConfig() { | ||
log.verbose(`Preparing send parameters`, params); | ||
const puppetMxid = yield this.provisioner.getMxid(params.chan.puppetId); | ||
const puppetData = yield this.provisioner.get(params.chan.puppetId); | ||
const puppetMxid = puppetData ? puppetData.puppetMxid : ""; | ||
const client = yield this.userSync.getClient(params.user); | ||
@@ -718,2 +722,8 @@ const userId = yield client.getUserId(); | ||
yield intent.ensureRegisteredAndJoined(mxid); | ||
if (puppetData && puppetData.userId === params.user.userId) { | ||
const delayedKey = `${userId}_${mxid}`; | ||
this.delayedFunction.set(delayedKey, () => __awaiter(this, void 0, void 0, function* () { | ||
yield this.chanSync.maybeLeaveGhost(mxid, userId); | ||
}), GHOST_PUPPET_LEAVE_TIMEOUT); | ||
} | ||
} | ||
@@ -955,5 +965,2 @@ // ensure our puppeted user is in the room | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (ghostId === this.appservice.botIntent.userId) { | ||
return; // we don't handle ghost user here | ||
} | ||
// we CAN'T check for if the room exists here, as if we create a new room | ||
@@ -963,2 +970,4 @@ // the m.room.member event triggers before the room is incerted into the store | ||
yield this.store.puppetStore.joinGhostToChan(ghostId, roomId); | ||
// maybe remove the bot user, if it is present | ||
yield this.chanSync.maybeLeaveGhost(roomId, this.appservice.botIntent.userId); | ||
}); | ||
@@ -1015,2 +1024,3 @@ } | ||
if (this.appservice.isNamespacedUser(userId)) { | ||
yield this.store.puppetStore.leaveGhostFromChan(userId, roomId); | ||
if (userId !== event.sender) { | ||
@@ -1017,0 +1027,0 @@ // puppet got kicked, unbridging room |
{ | ||
"name": "mx-puppet-bridge", | ||
"version": "0.0.19", | ||
"version": "0.0.20", | ||
"description": "Matrix Puppeting Bridge library", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -270,2 +270,45 @@ import { PuppetBridge } from "./puppetbridge"; | ||
public async maybeLeaveGhost(chanMxid: string, userMxid: string) { | ||
log.info(`Maybe leaving ghost ${userMxid} from ${chanMxid}`); | ||
const ghosts = await this.bridge.puppetStore.getGhostsInChan(chanMxid); | ||
if (!ghosts.includes(userMxid)) { | ||
log.verbose("Ghost not in room!"); | ||
return; // not in chan, nothing to do | ||
} | ||
if (ghosts.length === 1) { | ||
log.verbose("Ghost is the only one in the room!"); | ||
return; // we are the last ghost in the chan, we can't leave | ||
} | ||
const intent = this.bridge.AS.getIntentForUserId(userMxid); | ||
const client = intent.underlyingClient; | ||
const oldOp = await this.chanStore.getChanOp(chanMxid); | ||
if (oldOp === userMxid) { | ||
// we need to get a new OP! | ||
log.verbose("We are the OP in the room, we need to pass on OP"); | ||
const newOp = ghosts.find((element: string) => element !== userMxid); | ||
if (!newOp) { | ||
log.verbose("Noone to pass OP to!"); | ||
return; // we can't make a new OP, sorry | ||
} | ||
log.verbose(`Giving OP to ${newOp}...`); | ||
try { | ||
// give the user OP | ||
const powerLevels = await client.getRoomStateEvent( | ||
chanMxid, "m.room.power_levels", "", | ||
); | ||
powerLevels.users[newOp] = powerLevels.users[oldOp]; | ||
await client.sendStateEvent( | ||
chanMxid, "m.room.power_levels", "", powerLevels, | ||
); | ||
await this.chanStore.setChanOp(chanMxid, newOp); | ||
} catch (err) { | ||
log.error("Couldn't set new chan OP", err); | ||
return; | ||
} | ||
} | ||
// and finally we passed all checks and can leave | ||
await intent.leaveRoom(chanMxid); | ||
await this.bridge.puppetStore.leaveGhostFromChan(userMxid, chanMxid); | ||
} | ||
public async delete(data: IRemoteChan, keepUsers: boolean = false) { | ||
@@ -332,3 +375,3 @@ const chan = await this.maybeGet(data); | ||
try { | ||
await intent.underlyingClient.leaveRoom(entry.mxid); | ||
await intent.leaveRoom(entry.mxid); | ||
} catch (err) { | ||
@@ -335,0 +378,0 @@ log.warn("Failed to trigger client leave room", err); |
@@ -143,9 +143,13 @@ import { IDatabaseConnector, ISqlRow } from "./connector"; | ||
public async setChanOp(chanMxid: string, userMxid: string) { | ||
const row = await this.db.Get("SELECT * FROM chan_op WHERE chan_mxid=$chan AND user_mxid=$user", { | ||
const row = await this.db.Get("SELECT * FROM chan_op WHERE chan_mxid=$chan", { | ||
chan: chanMxid, | ||
user: userMxid, | ||
}); | ||
if (row) { | ||
// nothing to do, we are already one | ||
return; | ||
if ((row.user_mxid as string) === userMxid) { | ||
// nothing to do, we are already set | ||
return; | ||
} | ||
await this.db.Run("DELETE FROM chan_op WHERE chan_mxid=$chan", { | ||
chan: chanMxid, | ||
}); | ||
} | ||
@@ -152,0 +156,0 @@ await this.db.Run("INSERT INTO chan_op (chan_mxid, user_mxid) VALUES ($chan, $user)", { |
@@ -197,3 +197,3 @@ import { IDatabaseConnector, ISqlRow } from "./connector"; | ||
public async joinGhostToChan(ghostMxid: string, chanMxid: string) { | ||
public async isGhostInChan(ghostMxid: string, chanMxid: string): Promise<boolean> { | ||
const exists = await this.db.Get( | ||
@@ -205,4 +205,8 @@ "SELECT * FROM ghosts_joined_chans WHERE ghost_mxid = $ghostMxid AND chan_mxid = $chanMxid" | ||
}); | ||
if (exists) { | ||
return; // nothing to do | ||
return exists ? true : false; | ||
} | ||
public async joinGhostToChan(ghostMxid: string, chanMxid: string) { | ||
if (await this.isGhostInChan(ghostMxid, chanMxid)) { | ||
return; | ||
} | ||
@@ -228,2 +232,10 @@ await this.db.Run("INSERT INTO ghosts_joined_chans (ghost_mxid, chan_mxid) VALUES ($ghostMxid, $chanMxid)", { | ||
public async leaveGhostFromChan(ghostMxid: string, chanMxid: string) { | ||
await this.db.Run("DELETE FROM ghosts_joined_chans " + | ||
"WHERE ghost_mxid = $g AND chan_mxid = $c", { | ||
g: ghostMxid, | ||
c: chanMxid, | ||
}); | ||
} | ||
private getRow(row: ISqlRow): IPuppet | null { | ||
@@ -230,0 +242,0 @@ try { |
@@ -30,8 +30,11 @@ import * as fs from "fs"; | ||
import { TypingHandler } from "./typinghandler"; | ||
import { DelayedFunction } from "./structures/delayedfunction"; | ||
const log = new Log("PuppetBridge"); | ||
// tslint:disable-next-line:no-magic-numbers | ||
// tslint:disable no-magic-numbers | ||
const PUPPET_INVITE_CACHE_LIFETIME = 1000 * 60 * 60 * 24; | ||
const GHOST_PUPPET_LEAVE_TIMEOUT = 1000 * 60 * 60; | ||
const DEFAULT_TYPING_TIMEOUT = 30000; | ||
// tslint:enable no-magic-numbers | ||
@@ -153,2 +156,3 @@ interface ISendInfo { | ||
private memberInfoCache: { [roomId: string]: { [userId: string]: IMemberInfo } }; | ||
private delayedFunction: DelayedFunction; | ||
@@ -163,2 +167,3 @@ constructor( | ||
this.hooks = {} as IPuppetBridgeHooks; | ||
this.delayedFunction = new DelayedFunction(); | ||
} | ||
@@ -806,3 +811,4 @@ | ||
log.verbose(`Preparing send parameters`, params); | ||
const puppetMxid = await this.provisioner.getMxid(params.chan.puppetId); | ||
const puppetData = await this.provisioner.get(params.chan.puppetId); | ||
const puppetMxid = puppetData ? puppetData.puppetMxid : ""; | ||
const client = await this.userSync.getClient(params.user); | ||
@@ -825,2 +831,8 @@ const userId = await client.getUserId(); | ||
await intent.ensureRegisteredAndJoined(mxid); | ||
if (puppetData && puppetData.userId === params.user.userId) { | ||
const delayedKey = `${userId}_${mxid}`; | ||
this.delayedFunction.set(delayedKey, async () => { | ||
await this.chanSync.maybeLeaveGhost(mxid, userId); | ||
}, GHOST_PUPPET_LEAVE_TIMEOUT); | ||
} | ||
} | ||
@@ -1058,6 +1070,2 @@ | ||
private async handleGhostJoinEvent(roomId: string, ghostId: string) { | ||
if (ghostId === this.appservice.botIntent.userId) { | ||
return; // we don't handle ghost user here | ||
} | ||
// we CAN'T check for if the room exists here, as if we create a new room | ||
@@ -1068,2 +1076,5 @@ // the m.room.member event triggers before the room is incerted into the store | ||
await this.store.puppetStore.joinGhostToChan(ghostId, roomId); | ||
// maybe remove the bot user, if it is present | ||
await this.chanSync.maybeLeaveGhost(roomId, this.appservice.botIntent.userId); | ||
} | ||
@@ -1118,2 +1129,3 @@ | ||
if (this.appservice.isNamespacedUser(userId)) { | ||
await this.store.puppetStore.leaveGhostFromChan(userId, roomId); | ||
if (userId !== event.sender) { | ||
@@ -1120,0 +1132,0 @@ // puppet got kicked, unbridging room |
340700
97
8972