This repository has been archived by the owner on Feb 9, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added getMods and getVips functionality
- Loading branch information
Showing
5 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { assert } from "chai"; | ||
import * as sinon from "sinon"; | ||
import { TimeoutError } from "../await/timeout-error"; | ||
import { ClientError, ConnectionError } from "../client/errors"; | ||
import { assertErrorChain, fakeConnection } from "../helpers.spec"; | ||
import { getMods, GetUsersError, getVips } from "./get-mods-vips"; | ||
|
||
describe("./operations/join", function () { | ||
describe("#getMods()", function () { | ||
it("sends the correct wire command", function () { | ||
sinon.useFakeTimers(); // prevent the promise timing out | ||
const { data, client } = fakeConnection(); | ||
getMods(client, "pajlada"); | ||
assert.deepEqual(data, ["PRIVMSG #pajlada :/mods\r\n"]); | ||
}); | ||
|
||
it("resolves on incoming no_mods", async function () { | ||
const { emitAndEnd, client, clientError } = fakeConnection(); | ||
|
||
const promise = getMods(client, "tmijs"); | ||
|
||
emitAndEnd( | ||
"@msg-id=no_mods :tmi.twitch.tv NOTICE #tmijs :There are no moderators of this channel." | ||
); | ||
|
||
assert.deepStrictEqual(await promise, []); | ||
await clientError; | ||
}); | ||
|
||
it("resolves on incoming room_mods", async function () { | ||
const { emitAndEnd, client, clientError } = fakeConnection(); | ||
|
||
const promise = getMods(client, "randers"); | ||
|
||
emitAndEnd( | ||
"@msg-id=room_mods :tmi.twitch.tv NOTICE #randers :The moderators of this channel are: test, abc, def" | ||
); | ||
|
||
assert.deepStrictEqual(await promise, ["test", "abc", "def"]); | ||
await clientError; | ||
}); | ||
|
||
it("resolves on incoming room_mods (just 1 mod)", async function () { | ||
const { emitAndEnd, client, clientError } = fakeConnection(); | ||
|
||
const promise = getMods(client, "randers"); | ||
|
||
emitAndEnd( | ||
"@msg-id=room_mods :tmi.twitch.tv NOTICE #randers :The moderators of this channel are: test" | ||
); | ||
|
||
assert.deepStrictEqual(await promise, ["test"]); | ||
await clientError; | ||
}); | ||
|
||
it("should fail on timeout of 2000 ms", async function () { | ||
// given | ||
sinon.useFakeTimers(); | ||
const { client, clientError } = fakeConnection(); | ||
|
||
// when | ||
const promise = getMods(client, "test"); | ||
|
||
// then | ||
sinon.clock.tick(2000); | ||
await assertErrorChain( | ||
promise, | ||
GetUsersError, | ||
"Failed to get mods of channel test: Timed out after waiting for res" + | ||
"ponse for 2000 milliseconds", | ||
TimeoutError, | ||
"Timed out after waiting for response for 2000 milliseconds" | ||
); | ||
|
||
await assertErrorChain( | ||
clientError, | ||
GetUsersError, | ||
"Failed to get mods of channel test: Timed out after waiting for res" + | ||
"ponse for 2000 milliseconds", | ||
TimeoutError, | ||
"Timed out after waiting for response for 2000 milliseconds" | ||
); | ||
}); | ||
}); | ||
|
||
describe("#getVips()", function () { | ||
it("sends the correct wire command", function () { | ||
sinon.useFakeTimers(); // prevent the promise timing out | ||
const { data, client } = fakeConnection(); | ||
getVips(client, "pajlada"); | ||
assert.deepEqual(data, ["PRIVMSG #pajlada :/vips\r\n"]); | ||
}); | ||
|
||
it("resolves on incoming no_vips", async function () { | ||
const { emitAndEnd, client, clientError } = fakeConnection(); | ||
|
||
const promise = getVips(client, "tmijs"); | ||
|
||
emitAndEnd( | ||
"@msg-id=no_vips :tmi.twitch.tv NOTICE #tmijs :This channel does not have any VIPs." | ||
); | ||
|
||
assert.deepStrictEqual(await promise, []); | ||
await clientError; | ||
}); | ||
|
||
it("resolves on incoming vips_success", async function () { | ||
const { emitAndEnd, client, clientError } = fakeConnection(); | ||
|
||
const promise = getVips(client, "randers"); | ||
|
||
emitAndEnd( | ||
"@msg-id=vips_success :tmi.twitch.tv NOTICE #randers :The VIPs of this channel are: eeya_, pajlada, pastorbruce, ragglefraggle, supervate, supibot." | ||
); | ||
|
||
assert.deepStrictEqual(await promise, [ | ||
"eeya_", | ||
"pajlada", | ||
"pastorbruce", | ||
"ragglefraggle", | ||
"supervate", | ||
"supibot", | ||
]); | ||
await clientError; | ||
}); | ||
|
||
it("resolves on incoming room_mods (just 1 mod)", async function () { | ||
const { emitAndEnd, client, clientError } = fakeConnection(); | ||
|
||
const promise = getVips(client, "randers"); | ||
|
||
emitAndEnd( | ||
"@msg-id=vips_success :tmi.twitch.tv NOTICE #randers :The VIPs of this channel are: supibot." | ||
); | ||
|
||
assert.deepStrictEqual(await promise, ["supibot"]); | ||
await clientError; | ||
}); | ||
|
||
it("should fail on timeout of 2000 ms", async function () { | ||
// given | ||
sinon.useFakeTimers(); | ||
const { client, clientError } = fakeConnection(); | ||
|
||
// when | ||
const promise = getVips(client, "test"); | ||
|
||
// then | ||
sinon.clock.tick(2000); | ||
await assertErrorChain( | ||
promise, | ||
GetUsersError, | ||
"Failed to get vips of channel test: Timed out after waiting for res" + | ||
"ponse for 2000 milliseconds", | ||
TimeoutError, | ||
"Timed out after waiting for response for 2000 milliseconds" | ||
); | ||
|
||
await assertErrorChain( | ||
clientError, | ||
GetUsersError, | ||
"Failed to get vips of channel test: Timed out after waiting for res" + | ||
"ponse for 2000 milliseconds", | ||
TimeoutError, | ||
"Timed out after waiting for response for 2000 milliseconds" | ||
); | ||
}); | ||
}); | ||
|
||
describe("GetUsersError", function () { | ||
it("should not be instanceof ConnectionError", function () { | ||
assert.notInstanceOf(new GetUsersError("test", "mods"), ConnectionError); | ||
}); | ||
it("should not be instanceof ClientError", function () { | ||
assert.notInstanceOf(new GetUsersError("test", "mods"), ClientError); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { awaitResponse } from "../await/await-response"; | ||
import { matchingNotice } from "../await/conditions"; | ||
import { SingleConnection } from "../client/connection"; | ||
import { MessageError } from "../client/errors"; | ||
import { NoticeMessage } from "../message/twitch-types/notice"; | ||
|
||
interface GetUsersConfig { | ||
command: "mods" | "vips"; | ||
msgIdError: string; | ||
msgIdNone: string; | ||
msgIdSome: string; | ||
someMessagePrefix: string; | ||
someMessageSuffix: string; | ||
} | ||
|
||
const getModsInfo: GetUsersConfig = { | ||
command: "mods", | ||
msgIdError: "usage_mods", | ||
msgIdNone: "no_mods", | ||
msgIdSome: "room_mods", | ||
someMessagePrefix: "The moderators of this channel are: ", | ||
someMessageSuffix: "", | ||
}; | ||
const getVipsInfo: GetUsersConfig = { | ||
command: "vips", | ||
msgIdError: "usage_vips", | ||
msgIdNone: "no_vips", | ||
msgIdSome: "vips_success", | ||
someMessagePrefix: "The VIPs of this channel are: ", | ||
someMessageSuffix: ".", | ||
}; | ||
|
||
export class GetUsersError extends MessageError { | ||
public channelName: string; | ||
public type: "mods" | "vips"; | ||
|
||
public constructor( | ||
channelName: string, | ||
type: "mods" | "vips", | ||
message?: string, | ||
cause?: Error | ||
) { | ||
super(message, cause); | ||
this.channelName = channelName; | ||
this.type = type; | ||
} | ||
} | ||
|
||
export async function getMods( | ||
conn: SingleConnection, | ||
channelName: string | ||
): Promise<string[]> { | ||
return await getModsOrVips(conn, channelName, getModsInfo); | ||
} | ||
|
||
export async function getVips( | ||
conn: SingleConnection, | ||
channelName: string | ||
): Promise<string[]> { | ||
return await getModsOrVips(conn, channelName, getVipsInfo); | ||
} | ||
|
||
async function getModsOrVips( | ||
conn: SingleConnection, | ||
channelName: string, | ||
config: GetUsersConfig | ||
): Promise<string[]> { | ||
conn.sendRaw(`PRIVMSG #${channelName} :/${config.command}`); | ||
|
||
const responseMsg = (await awaitResponse(conn, { | ||
success: matchingNotice(channelName, [config.msgIdNone, config.msgIdSome]), | ||
failure: matchingNotice(channelName, [config.msgIdError]), | ||
errorType: (msg, cause) => | ||
new GetUsersError(channelName, config.command, msg, cause), | ||
errorMessage: `Failed to get ${config.command} of channel ${channelName}`, | ||
})) as NoticeMessage; | ||
|
||
if (responseMsg.messageID === config.msgIdNone) { | ||
return []; | ||
} | ||
|
||
if (responseMsg.messageID === config.msgIdSome) { | ||
let text = responseMsg.messageText; | ||
|
||
if ( | ||
!text.startsWith(config.someMessagePrefix) || | ||
!text.endsWith(config.someMessageSuffix) | ||
) { | ||
throw new GetUsersError( | ||
channelName, | ||
config.command, | ||
`Failed to get ${config.command} of channel ${channelName}: Response message had unexpected format: ${responseMsg.rawSource}` | ||
); | ||
} | ||
|
||
// slice away the prefix and suffix | ||
text = text.slice( | ||
config.someMessagePrefix.length, | ||
text.length - config.someMessageSuffix.length | ||
); | ||
|
||
return text.split(", "); | ||
} | ||
|
||
throw new Error("unreachable"); | ||
} |