Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting the pastebin line limit in room state #1301

Merged
merged 14 commits into from
May 7, 2021
Merged
6 changes: 6 additions & 0 deletions src/bridge/IrcBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { spawnMetricsWorker } from "../workers/MetricsWorker";
import { getBridgeVersion } from "../util/PackageInfo";
import { globalAgent as gAHTTP } from "http";
import { globalAgent as gAHTTPS } from "https";
import { RoomConfig } from "./RoomConfig";

const log = getLogger("IrcBridge");
const DEFAULT_PORT = 8090;
Expand All @@ -64,6 +65,7 @@ export class IrcBridge {
public readonly ircHandler: IrcHandler;
public readonly publicitySyncer: PublicitySyncer;
public readonly activityTracker: MatrixActivityTracker|null = null;
public readonly roomConfigs: RoomConfig;
private clientPool!: ClientPool; // This gets defined in the `run` function
private ircServers: IrcServer[] = [];
private memberListSyncers: {[domain: string]: MemberListSyncer} = {};
Expand Down Expand Up @@ -206,6 +208,7 @@ export class IrcBridge {
homeserverToken,
httpMaxSizeBytes: (this.config.advanced || { }).maxTxnSize || TXN_SIZE_DEFAULT,
});
this.roomConfigs = new RoomConfig(this.bridge);
}

public async onConfigChanged(newConfig: BridgeConfig) {
Expand Down Expand Up @@ -942,6 +945,9 @@ export class IrcBridge {
else if (event.type === "m.room.topic" && event.state_key === "") {
await this.matrixHandler.onMessage(request, event as unknown as MatrixMessageEvent);
}
else if (event.type === RoomConfig.STATE_EVENT_TYPE && typeof event.state_key === 'string') {
this.roomConfigs.invalidateConfig(event.room_id, event.state_key);
}
else if (event.type === "m.room.member" && event.state_key) {
if (!event.content || !event.content.membership) {
return BridgeRequestErr.ERR_NOT_MAPPED;
Expand Down
3 changes: 2 additions & 1 deletion src/bridge/MatrixHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,8 @@ export class MatrixHandler {

// Generate an array of individual messages that would be sent
const potentialMessages = ircClient.getSplitMessages(ircRoom.channel, ircAction.text);
const lineLimit = ircRoom.server.getLineLimit();
const roomLineLimit = await this.ircBridge.roomConfigs.getLineLimit(event.room_id, ircRoom);
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved
const lineLimit = roomLineLimit === null ? ircRoom.server.getLineLimit() : roomLineLimit;

if (potentialMessages.length <= lineLimit) {
await this.ircBridge.sendIrcAction(ircRoom, ircClient, ircAction);
Expand Down
59 changes: 59 additions & 0 deletions src/bridge/RoomConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Bridge } from "matrix-appservice-bridge";
import { IrcRoom } from "../models/IrcRoom";
import QuickLRU from "quick-lru";
import getLogger from "../logging";

interface RoomConfigContent {
lineLimit?: number;
}

const MAX_CACHE_SIZE = 512;

const log = getLogger("RoomConfig");
export class RoomConfig {
public static readonly STATE_EVENT_TYPE = 'org.matrix.appservice-irc.room-config';
private cache = new QuickLRU<string, RoomConfigContent|undefined>({maxSize: MAX_CACHE_SIZE});
constructor(private bridge: Bridge) { }

private async getRoomState(roomId: string, ircRoom?: IrcRoom): Promise<RoomConfigContent|null> {
const cacheKey = `${roomId}:${ircRoom?.getId() || 'global'}`;
let keyedConfig = this.cache.get(cacheKey);
if (keyedConfig) {
return keyedConfig;
}
const intent = this.bridge.getIntent();
keyedConfig = ircRoom && await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, ircRoom.getId(), true);
if (!keyedConfig) {
// Fall back to an empty key
keyedConfig = await intent.getStateEvent(roomId, RoomConfig.STATE_EVENT_TYPE, '', true);
}
log.debug(`Stored new config for ${cacheKey}:`, keyedConfig || 'No config set');
this.cache.set(cacheKey, keyedConfig || undefined);
return keyedConfig as RoomConfigContent|null;
}

/**
* Invalidate the cache for a room. Provide the key
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved
* @param roomId The Matrix roomId
* @param stateKey The state event's key.
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved
*/
public invalidateConfig(roomId: string, stateKey = 'global') {
log.info(`Invalidating config for ${roomId}:${stateKey}`);
this.cache.delete(`${roomId}:${stateKey}`)
}

/**
* Get the per-room configuration for the paste bin limit for a room.
* @param roomId The Matrix roomId
* @param ircRoom The IRC roomId. Optional.
* @returns The number of lines required for a pastebin. `null` means no limit set in the room.
*/
public async getLineLimit(roomId: string, ircRoom?: IrcRoom) {
const roomState = await this.getRoomState(roomId, ircRoom);
if (typeof roomState?.lineLimit !== 'number' || roomState.lineLimit > 0) {
// A missing line limit or an invalid one is considered invalid.
return null;
}
return roomState.lineLimit;
}
}