File size: 3,394 Bytes
5c2ed06
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
 * Minor activities
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Minor activities are representations of non-game activities that rooms
 * regularly use, such as polls and announcements. Rooms are limited to
 * one minor activity at a time.
 *
 * Minor activities keep track of users in the form of userids and IPs.
 * If a player votes for a poll under one IP, they cannot vote for the same
 * poll again.
 *
 * The user-tracking system is not implemented at the base level: Announcements
 * do not have a reason to keep track of users' IPs/IDs because they're just used
 * to broadcast a message to a room.
 *
 * @license MIT
 */

export interface MinorActivityData {
	readonly activityid: 'announcement' | 'poll';
	activityNumber?: number;
	source?: string;
	timeoutMins?: number;
	timerEnd?: number;
	question: string;
	supportHTML: boolean;
	multiPoll: boolean;
	pendingVotes?: { [userid: string]: number[] };
	voters?: { [k: string]: number[] };
	voterIps?: { [k: string]: number[] };
	totalVotes?: number;
	isQuiz?: boolean;
	answers: string[] | { name: string, votes: number, correct?: boolean }[];
}

// globally Rooms.MinorActivity
export abstract class MinorActivity {
	abstract activityid: ID;
	abstract name: string;

	timeout: NodeJS.Timeout | null;
	timeoutMins: number;
	timerEnd: number;
	roomid: RoomID;
	room: Room;
	supportHTML: boolean;
	constructor(room: Room) {
		this.timeout = null;
		this.timeoutMins = 0;
		this.timerEnd = 0;
		this.roomid = room.roomid;
		this.room = room;
		this.supportHTML = false;
	}

	setTimer(options: { timeoutMins?: number, timerEnd?: number }) {
		if (this.timeout) clearTimeout(this.timeout);

		this.timeoutMins = options.timeoutMins || 0;
		if (!this.timeoutMins) {
			this.timerEnd = 0;
			this.timeout = null;
			return;
		}

		const now = Date.now();
		this.timerEnd = options.timerEnd || now + this.timeoutMins * 60000;
		this.timeout = setTimeout(() => {
			const room = this.room;
			if (!room) return; // someone forgot to `.destroy()`

			this.end(room);
		}, this.timerEnd - now);
		this.save();
	}

	end(room: Room, MinorActivityClass?: new (...args: any[]) => any) {
		room.minorActivity?.destroy();
		if (room.minorActivityQueue?.length) {
			const pollData = room.minorActivityQueue.shift()!;
			if (!room.minorActivityQueue.length) room.clearMinorActivityQueue();
			if (!room.settings.minorActivityQueue?.length) {
				delete room.settings.minorActivityQueue;
				room.saveSettings();
			}

			if (pollData.activityid !== 'poll') throw new Error(`Unexpected Minor Activity (${pollData.activityid}) in queue`);

			room.add(`|c|~|/log ${room.tr`The queued poll was started.`}`).update();
			room.modlog({
				action: 'POLL',
				note: '(queued)',
			});

			if (!MinorActivityClass) {
				if (pollData.activityid === 'poll') {
					const { Poll } = require('./chat-plugins/poll');
					room.setMinorActivity(new Poll(room, pollData));
				}
			} else {
				room.setMinorActivity(new MinorActivityClass(room, pollData));
			}
		}
	}

	endTimer() {
		if (!this.timeout) return false;
		clearTimeout(this.timeout);
		this.timeoutMins = 0;
		this.timerEnd = 0;
		return true;
	}

	abstract destroy(): void;
	abstract display(): void;
	onConnect?(user: User, connection: Connection | null): void;
	onRename?(user: User, oldid: ID, joining: boolean): void;
	abstract toJSON(): AnyObject;
	abstract save(): void;
}