diff --git a/src/Conference.ts b/src/Conference.ts
index 70d07c1..8f78b21 100644
--- a/src/Conference.ts
+++ b/src/Conference.ts
@@ -855,8 +855,8 @@ export class Conference {
const until = Date.now() + inNextMinutes * 60000;
const upcomingTalks: ITalk[] = [];
- for (const t of Object.values(this.talks)) {
- const talk = await t.getDefinition();
+ // Use this.backend.talks because we care about physical talks here too.
+ for (const talk of this.backend.talks.values()) {
const talkEventTime = lambda(talk);
// If null is returned then the talk does not have this event, so don't return it as upcoming.
if (talkEventTime === null) continue;
diff --git a/src/Scheduler.ts b/src/Scheduler.ts
index e53e8b0..40de5c7 100644
--- a/src/Scheduler.ts
+++ b/src/Scheduler.ts
@@ -401,7 +401,8 @@ export class Scheduler {
} else {
await this.client.sendHtmlText(confAud.roomId, `
The talk will end shortly
`);
}
- } else if (task.type === ScheduledTaskType.TalkStart1H && confTalk !== undefined) {
+ } else if (task.type === ScheduledTaskType.TalkStart1H) {
+ if (confTalk === undefined) return;
// This stage is skipped entirely for physical auditoriums' talks, because it only serves to nag
// TODO Do we need to ensure that coordinators have checked in?
@@ -445,11 +446,15 @@ export class Scheduler {
await this.client.sendHtmlText(confTalk.roomId, `Your talk ends in about 5 minutes
The next talk will start automatically after yours.
`);
}
await this.client.sendHtmlText(confAud.roomId, `This talk ends in about 5 minutes
` + (task.talk.qa_startTime !== null ? `Ask questions here for the speakers!
`: ''));
- } else if (task.type === ScheduledTaskType.TalkLivestreamEnd1M && confTalk !== undefined) {
+ } else if (task.type === ScheduledTaskType.TalkLivestreamEnd1M) {
+ if (confTalk === undefined) return;
await this.client.sendHtmlText(confTalk.roomId, `Your talk ends in about 1 minute!
The next talk will start automatically after yours. Wrap it up!
`);
} else if (task.type === ScheduledTaskType.TalkEnd1M) {
+ // It's a bit spammy for a physical talk.
+ if (confTalk === undefined) return;
await this.client.sendHtmlText(confAud.roomId, `This talk ends in about 1 minute!
`);
- } else if (task.type === ScheduledTaskType.TalkCheckin45M && confTalk !== undefined) {
+ } else if (task.type === ScheduledTaskType.TalkCheckin45M) {
+ if (confTalk === undefined) return;
// TODO This is skipped entirely for physical talks, but do we want to ensure coordinators are checked-in?
if (!task.talk.prerecorded) return;
@@ -486,7 +491,8 @@ export class Scheduler {
const resolved = (await resolveIdentifiers(this.client, userIds)).filter(p => p.mxid).map(p => p.mxid!);
await this.checkins.expectCheckinFrom(resolved);
}
- } else if (task.type === ScheduledTaskType.TalkCheckin30M && confTalk !== undefined) {
+ } else if (task.type === ScheduledTaskType.TalkCheckin30M) {
+ if (confTalk === undefined) return;
// TODO This is skipped entirely for physical talks, but do we want to ensure coordinators are checked-in?
if (!task.talk.prerecorded) return;
@@ -523,7 +529,8 @@ export class Scheduler {
const resolved = (await resolveIdentifiers(this.client, userIds)).filter(p => p.mxid).map(p => p.mxid!);
await this.checkins.expectCheckinFrom(resolved);
} // else no complaints
- } else if (task.type === ScheduledTaskType.TalkCheckin15M && confTalk !== undefined) {
+ } else if (task.type === ScheduledTaskType.TalkCheckin15M) {
+ if (confTalk === undefined) return;
// TODO This is skipped entirely for physical talks, but do we want to ensure coordinators are checked-in?
if (!task.talk.prerecorded) return;
diff --git a/src/commands/InviteCommand.ts b/src/commands/InviteCommand.ts
index 16bb623..37b5617 100644
--- a/src/commands/InviteCommand.ts
+++ b/src/commands/InviteCommand.ts
@@ -67,12 +67,13 @@ export class InviteCommand implements ICommand {
} else if (args[0] && args[0] === "coordinators-support") {
let people: IPerson[] = [];
for (const aud of this.conference.storedAuditoriums) {
- if (!(await aud.getId()).startsWith("D.")) {
+ // This hack was not wanted in 2023 or 2024.
+ // if (!(await aud.getId()).startsWith("D.")) {
// HACK: Only invite coordinators for D.* auditoriums.
// TODO: Make invitations for support rooms more configurable.
// https://github.com/matrix-org/this.conference-bot/issues/76
- continue;
- }
+ // continue;
+ // }
const inviteTargets = await this.conference.getInviteTargetsForAuditorium(aud, true);
people.push(...inviteTargets.filter(i => i.role === Role.Coordinator));
diff --git a/src/commands/actions/people.ts b/src/commands/actions/people.ts
index b42cd17..0542fe3 100644
--- a/src/commands/actions/people.ts
+++ b/src/commands/actions/people.ts
@@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import { LogService, MatrixClient } from "matrix-bot-sdk";
+import { LogLevel, LogService, MatrixClient } from "matrix-bot-sdk";
import { ResolvedPersonIdentifier, resolveIdentifiers } from "../../invites";
import { Auditorium } from "../../models/Auditorium";
import { Conference } from "../../Conference";
import { asyncFilter } from "../../utils";
import { InterestRoom } from "../../models/InterestRoom";
import { ConferenceMatrixClient } from "../../ConferenceMatrixClient";
+import { logMessage } from "../../LogProxy";
export interface IAction {
(client: MatrixClient, roomId: string, people: ResolvedPersonIdentifier[]): Promise;
@@ -60,9 +61,12 @@ export async function doAuditoriumResolveAction(
? await conference.getInviteTargetsForAuditorium(realAud)
: await conference.getModeratorsForAuditorium(realAud);
const resolvedAudPeople = audPeople.map(p => allPossiblePeople.find(b => p.id === b.person.id));
- if (resolvedAudPeople.some(p => !p)) throw new Error(`Failed to resolve all targets for auditorium ${audId}`);
+ if (resolvedAudPeople.some(p => !p)) {
+ logMessage(LogLevel.WARN, "people", `Failed to resolve all targets for auditorium ${audId}. Inviting others anyway.`, client);
+ }
- await action(client, realAud.roomId, resolvedAudPeople as ResolvedPersonIdentifier[]);
+ const resolvedAudPeopleOnly = resolvedAudPeople.filter(p => !!p);
+ await action(client, realAud.roomId, resolvedAudPeopleOnly as ResolvedPersonIdentifier[]);
if (!skipTalks) {
const talks = await asyncFilter(
@@ -80,10 +84,11 @@ export async function doAuditoriumResolveAction(
const unresolveable = talkPeople.filter(
p => allPossiblePeople.find(b => p.id === b.person.id) === undefined
)
- throw new Error(`Failed to resolve all targets for talk ${await talk.getId()}: ` + JSON.stringify(unresolveable));
+ logMessage(LogLevel.WARN, "people", `Failed to resolve all targets for talk ${await talk.getId()}: ` + JSON.stringify(unresolveable), client);
}
- await action(client, talk.roomId, resolvedTalkPeople as ResolvedPersonIdentifier[]);
+ const resolvedTalkPeopleOnly = resolvedTalkPeople.filter(p => !!p);
+ await action(client, talk.roomId, resolvedTalkPeopleOnly as ResolvedPersonIdentifier[]);
}
}
}
diff --git a/src/web.ts b/src/web.ts
index 691631f..dc91c6e 100644
--- a/src/web.ts
+++ b/src/web.ts
@@ -37,9 +37,16 @@ export function renderAuditoriumWidget(req: Request, res: Response, conference:
return res.sendStatus(404);
}
+ //let sid = audId.toLowerCase().replace(/[^a-z0-9]/g, '');
+
+ // HACK for FOSDEM 2023 and FOSDEM 2024: transform auditorium IDs to the livestream ID
+ // 1. 'K1.105A (Words)' -> 'k1.105a'
+ // 2. 'k1.105a' -> 'k1105a'
+ let sid = audId.toLowerCase().replace(/\s+\(.+\)$/, '').replace(/[^a-z0-9]/g, '');
+
const streamUrl = template(auditoriumUrl, {
id: audId.toLowerCase(),
- sId: audId.toLowerCase().replace(/[^a-z0-9]/g, ''),
+ sId: sid
});
return res.render('auditorium.liquid', {